diff --git a/.example.env b/.example.env
new file mode 100644
index 0000000..1a50053
--- /dev/null
+++ b/.example.env
@@ -0,0 +1,19 @@
+APP_DEBUG = false
+
+[APP]
+DEFAULT_TIMEZONE = Asia/Shanghai
+SYS_KEY = {syskey}
+
+[DATABASE]
+TYPE = mysql
+HOSTNAME = {dbhost}
+DATABASE = {dbname}
+USERNAME = {dbuser}
+PASSWORD = {dbpwd}
+HOSTPORT = {dbport}
+CHARSET = utf8mb4
+PREFIX = {dbprefix}
+DEBUG = false
+
+[LANG]
+default_lang = zh-cn
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d465120
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/.idea
+/.vscode
+/vendor
+*.log
+.env
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..46f486e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,72 @@
+## 聚合DNS管理系统
+
+聚合DNS管理系统可以实现在一个网站内管理多个平台的域名解析,目前已支持的域名平台有:
+
+- 阿里云
+- 腾讯云
+- 华为云
+- 西部数码
+- CloudFlare
+
+本系统支持多用户,每个用户可分配不同的域名解析权限;支持API接口,支持获取域名独立DNS控制面板登录链接,方便各种IDC系统对接。
+
+### 演示截图
+
+添加域名账户
+
+![](https://p0.meituan.net/csc/090508cdc7aaabd185ba9c76a8c099f9283946.png)
+
+域名管理列表
+
+![](https://p0.meituan.net/csc/60bf3f607d40f30f152ad1f6ee3be098357839.png)
+
+域名DNS解析管理,支持解析批量操作
+
+![](https://p0.meituan.net/csc/f99c599d4febced404c88672dd50d62c212895.png)
+
+用户管理添加用户,支持为用户开启API接口
+
+![](https://p0.meituan.net/csc/d1bd90bedca9b6cbc5da40286bdb5cd5228438.png)
+
+### 部署方法
+
+* 运行环境要求PHP7.4+,MySQL5.6+
+* 设置网站运行目录为`public`
+* 设置伪静态为`ThinkPHP`
+* 访问网站,会自动跳转到安装页面,根据提示安装完成
+* 访问首页登录控制面板
+
+##### 伪静态规则
+
+* Nginx
+
+```
+location / {
+ if (!-e $request_filename){
+ rewrite ^(.*)$ /index.php?s=$1 last; break;
+ }
+}
+```
+
+* Apache
+
+```
+
+ Options +FollowSymlinks -Multiviews
+ RewriteEngine On
+
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+
+```
+
+### 版权信息
+
+版权所有Copyright © 2023~2024 by 消失的彩虹海(https://blog.cccyun.cn)
+
+### 其他推荐
+
+- [彩虹云主机 - 免备案CDN/虚拟主机](https://www.cccyun.net/)
+- [小白云高防云服务器](https://www.xiaobaiyun.cn/aff/GMLPMFOV)
+
diff --git a/app/.htaccess b/app/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/app/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/app/AppService.php b/app/AppService.php
new file mode 100644
index 0000000..96556e8
--- /dev/null
+++ b/app/AppService.php
@@ -0,0 +1,22 @@
+app = $app;
+ $this->request = $this->app->request;
+
+ // 控制器初始化
+ $this->initialize();
+ }
+
+ // 初始化
+ protected function initialize()
+ {
+ $this->clientip = real_ip(env('app.ip_type', 0));
+ }
+
+ /**
+ * 验证数据
+ * @access protected
+ * @param array $data 数据
+ * @param string|array $validate 验证器名或者验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return array|string|true
+ * @throws ValidateException
+ */
+ protected function validate(array $data, $validate, array $message = [], bool $batch = false)
+ {
+ if (is_array($validate)) {
+ $v = new Validate();
+ $v->rule($validate);
+ } else {
+ if (strpos($validate, '.')) {
+ // 支持场景
+ [$validate, $scene] = explode('.', $validate);
+ }
+ $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+ $v = new $class();
+ if (!empty($scene)) {
+ $v->scene($scene);
+ }
+ }
+
+ $v->message($message);
+
+ // 是否批量验证
+ if ($batch || $this->batchValidate) {
+ $v->batch(true);
+ }
+
+ return $v->failException(true)->check($data);
+ }
+
+
+ protected function alert($code, $msg = '', $url = null, $wait = 3)
+ {
+ if ($url) {
+ $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : (string)$this->app->route->buildUrl($url);
+ }
+ if(empty($msg)) $msg = '未知错误';
+
+ if (request()->isApi) {
+ return json(['code'=>$code=='success'?0:-1, 'msg'=>$msg]);
+ }
+ if (request()->isAjax()) {
+ return json(['code'=>$code=='success'?0:-1, 'msg'=>$msg, 'url'=>$url]);
+ }
+
+ View::assign([
+ 'code' => $code,
+ 'msg' => $msg,
+ 'url' => $url,
+ 'wait' => $wait,
+ ]);
+ return View::fetch(app()->getBasePath().'view/dispatch_jump.html');
+ }
+}
diff --git a/app/ExceptionHandle.php b/app/ExceptionHandle.php
new file mode 100644
index 0000000..453d126
--- /dev/null
+++ b/app/ExceptionHandle.php
@@ -0,0 +1,58 @@
+ 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
+ return substr($result, 26);
+ } else {
+ return '';
+ }
+ } else {
+ return $keyc.base64_encode($result);
+ }
+}
+
+function random($length, $numeric = 0) {
+ $seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
+ $seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed));
+ $hash = '';
+ $max = strlen($seed) - 1;
+ for($i = 0; $i < $length; $i++) {
+ $hash .= $seed[mt_rand(0, $max)];
+ }
+ return $hash;
+}
+
+function checkDomain($domain){
+ if(empty($domain) || !preg_match('/^[-$a-z0-9_*.]{2,512}$/i', $domain) || (stripos($domain, '.') === false) || substr($domain, -1) == '.' || substr($domain, 0 ,1) == '.' || substr($domain, 0 ,1) == '*' && substr($domain, 1 ,1) != '.' || substr_count($domain, '*')>1 || strpos($domain, '*')>0 || strlen($domain)<4) return false;
+ return true;
+}
+
+function getSubstr($str, $leftStr, $rightStr)
+{
+ $left = strpos($str, $leftStr);
+ $start = $left+strlen($leftStr);
+ $right = strpos($str, $rightStr, $start);
+ if($left < 0) return '';
+ if($right>0){
+ return substr($str, $start, $right-$start);
+ }else{
+ return substr($str, $start);
+ }
+}
+
+function checkRefererHost(){
+ if(!request()->header('referer'))return false;
+ $url_arr = parse_url(request()->header('referer'));
+ $http_host = request()->header('host');
+ if(strpos($http_host,':'))$http_host = substr($http_host, 0, strpos($http_host, ':'));
+ return $url_arr['host'] === $http_host;
+}
+
+function checkIfActive($string) {
+ $array=explode(',',$string);
+ $action = request()->action();
+ if (in_array($action,$array)){
+ return 'active';
+ }else
+ return null;
+}
+
+function getSid() {
+ return md5(uniqid(mt_rand(), true) . microtime());
+}
+function getMd5Pwd($pwd, $salt=null) {
+ return md5(md5($pwd) . md5('1277180438'.$salt));
+}
+
+function isNullOrEmpty($str){
+ return $str === null || $str === '';
+}
+
+function checkPermission($type, $domain = null){
+ $user = request()->user;
+ if(empty($user)) return false;
+ if($user['level'] == 2) return true;
+ if($type == 1 && $user['level'] == 1 || $type == 0 && $user['level'] >= 0){
+ if($domain == null) return true;
+ if(in_array($domain, $user['permission'])){
+ return true;
+ }
+ }
+ return false;
+}
+
+function getAdminSkin(){
+ $skin = cookie('admin_skin');
+ if(empty($skin)){
+ $skin = cache('admin_skin');
+ }
+ if(empty($skin)){
+ $skin = 'skin-black-blue';
+ }
+ return $skin;
+}
\ No newline at end of file
diff --git a/app/controller/Auth.php b/app/controller/Auth.php
new file mode 100644
index 0000000..2341ea7
--- /dev/null
+++ b/app/controller/Auth.php
@@ -0,0 +1,115 @@
+getRuntimePath().'@login.lock';
+
+ if(request()->islogin){
+ return redirect('/');
+ }
+
+ if(request()->isAjax()){
+ $username = input('post.username',null,'trim');
+ $password = input('post.password',null,'trim');
+ $code = input('post.code',null,'trim');
+
+ if(empty($username) || empty($password)){
+ return json(['code'=>-1, 'msg'=>'用户名或密码不能为空']);
+ }
+ if(!captcha_check($code)){
+ return json(['code'=>-1, 'msg'=>'验证码错误', 'vcode'=>1]);
+ }
+ if (file_exists($login_limit_file)) {
+ $login_limit = unserialize(file_get_contents($login_limit_file));
+ if ($login_limit['count'] >= $login_limit_count && $login_limit['time'] > time() - 86400) {
+ exit(json_encode(['code' => -1, 'msg' => '多次登录失败,暂时禁止登录。可删除/runtime/@login.lock文件解除限制', 'vcode'=>1]));
+ }
+ }
+ $user = Db::name('user')->where('username', $username)->find();
+ if($user && password_verify($password, $user['password'])){
+ if($user['status'] == 0) return json(['code'=>-1, 'msg'=>'此用户已被封禁', 'vcode'=>1]);
+ Db::name('log')->insert(['uid' => $user['id'], 'action' => '登录后台', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s")]);
+ DB::name('user')->where('id', $user['id'])->update(['lasttime' => date("Y-m-d H:i:s")]);
+ $session = md5($user['id'].$user['password']);
+ $expiretime = time()+2562000;
+ $token = authcode("user\t{$user['id']}\t{$session}\t{$expiretime}", 'ENCODE', env('app.sys_key'));
+ cookie('user_token', $token, ['expire' => $expiretime, 'httponly' => true]);
+ return json(['code'=>0]);
+ }else{
+ if($user){
+ Db::name('log')->insert(['uid' => $user['id'], 'action' => '登录失败', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s")]);
+ }
+ if (!file_exists($login_limit_file)) {
+ $login_limit = ['count' => 0, 'time' => 0];
+ }
+ $login_limit['count']++;
+ $login_limit['time'] = time();
+ file_put_contents($login_limit_file, serialize($login_limit));
+ $retry_times = $login_limit_count - $login_limit['count'];
+ if ($retry_times == 0) {
+ return json(['code' => -1, 'msg' => '多次登录失败,暂时禁止登录。可删除/runtime/@login.lock文件解除限制', 'vcode' => 1]);
+ } else {
+ return json(['code' => -1, 'msg' => '用户名或密码错误,你还可以尝试' . $retry_times . '次', 'vcode' => 1]);
+ }
+ }
+ }
+
+ return view();
+ }
+
+ public function logout()
+ {
+ cookie('user_token', null);
+ return redirect('/login');
+ }
+
+ public function quicklogin()
+ {
+ $domain = input('get.domain',null,'trim');
+ $timestamp = input('get.timestamp',null,'trim');
+ $token = input('get.token',null,'trim');
+ $sign = input('get.sign',null,'trim');
+ if(empty($domain) || empty($timestamp) || empty($token) || empty($sign)){
+ return $this->alert('error', '参数错误');
+ }
+ if($timestamp < time()-300 || $timestamp > time()+300){
+ return $this->alert('error', '时间戳无效');
+ }
+ if(md5(env('app.sys_key').$domain.$timestamp.$token.env('app.sys_key')) !== $sign){
+ return $this->alert('error', '签名错误');
+ }
+ if($token != cache('quicklogin_'.$domain)){
+ return $this->alert('error', 'Token无效');
+ }
+ $row = Db::name('domain')->where('name', $domain)->find();
+ if(!$row){
+ return $this->alert('error', '该域名不存在');
+ }
+ if(!$row['is_sso']){
+ return $this->alert('error', '该域名不支持快捷登录');
+ }
+
+ Db::name('log')->insert(['uid' => 0, 'action' => '域名快捷登录', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s"), 'domain' => $domain]);
+
+ $session = md5($row['id'].$row['name']);
+ $expiretime = time()+2562000;
+ $token = authcode("domain\t{$row['id']}\t{$session}\t{$expiretime}", 'ENCODE', env('app.sys_key'));
+ cookie('user_token', $token, ['expire' => $expiretime, 'httponly' => true]);
+ return redirect('/record/'.$row['id']);
+ }
+}
diff --git a/app/controller/Domain.php b/app/controller/Domain.php
new file mode 100644
index 0000000..0a79ab7
--- /dev/null
+++ b/app/controller/Domain.php
@@ -0,0 +1,561 @@
+alert('error', '无权限');
+ View::assign('dnsconfig', DnsHelper::$dns_config);
+ return view();
+ }
+
+ public function account_data(){
+ if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]);
+ $kw = input('post.kw', null, 'trim');
+ $offset = input('post.offset/d');
+ $limit = input('post.limit/d');
+
+ $select = Db::name('account');
+ if(!empty($kw)){
+ $select->whereLike('ak|remark', '%'.$kw.'%');
+ }
+ $total = $select->count();
+ $rows = $select->order('id','desc')->limit($offset, $limit)->select();
+
+ $list = [];
+ foreach($rows as $row){
+ $row['typename'] = DnsHelper::$dns_config[$row['type']]['name'];
+ $list[] = $row;
+ }
+
+ return json(['total'=>$total, 'rows'=>$list]);
+ }
+
+ public function account_op(){
+ if(!checkPermission(2)) return $this->alert('error', '无权限');
+ $act = input('param.act');
+ if($act == 'get'){
+ $id = input('post.id/d');
+ $row = Db::name('account')->where('id', $id)->find();
+ if(!$row) return json(['code'=>-1, 'msg'=>'域名账户不存在']);
+ return json(['code'=>0, 'data'=>$row]);
+ }elseif($act == 'add'){
+ $type = input('post.type');
+ $ak = input('post.ak', null, 'trim');
+ $sk = input('post.sk', null, 'trim');
+ $ext = input('post.ext', null, 'trim');
+ $remark = input('post.remark', null, 'trim');
+ if(empty($ak) || empty($sk)) return json(['code'=>-1, 'msg'=>'AccessKey和SecretKey不能为空']);
+ if(Db::name('account')->where('type', $type)->where('ak', $ak)->find()){
+ return json(['code'=>-1, 'msg'=>'域名账户已存在']);
+ }
+ Db::startTrans();
+ $id = Db::name('account')->insertGetId([
+ 'type' => $type,
+ 'ak' => $ak,
+ 'sk' => $sk,
+ 'ext' => $ext,
+ 'remark' => $remark,
+ 'addtime' => date('Y-m-d H:i:s'),
+ ]);
+ $dns = DnsHelper::getModel($id);
+ if($dns){
+ if($dns->check()){
+ Db::commit();
+ return json(['code'=>0, 'msg'=>'添加域名账户成功!']);
+ }else{
+ Db::rollback();
+ return json(['code'=>-1, 'msg'=>'验证域名账户失败,'.$dns->getError()]);
+ }
+ }else{
+ Db::rollback();
+ return json(['code'=>-1, 'msg'=>'DNS模块('.$type.')不存在']);
+ }
+
+ }elseif($act == 'edit'){
+ $id = input('post.id/d');
+ $row = Db::name('account')->where('id', $id)->find();
+ if(!$row) return json(['code'=>-1, 'msg'=>'域名账户不存在']);
+ $type = input('post.type');
+ $ak = input('post.ak', null, 'trim');
+ $sk = input('post.sk', null, 'trim');
+ $ext = input('post.ext', null, 'trim');
+ $remark = input('post.remark', null, 'trim');
+ if(empty($ak) || empty($sk)) return json(['code'=>-1, 'msg'=>'AccessKey和SecretKey不能为空']);
+ if(Db::name('account')->where('type', $type)->where('ak', $ak)->where('id', '<>', $id)->find()){
+ return json(['code'=>-1, 'msg'=>'域名账户已存在']);
+ }
+ Db::startTrans();
+ Db::name('account')->where('id', $id)->update([
+ 'type' => $type,
+ 'ak' => $ak,
+ 'sk' => $sk,
+ 'ext' => $ext,
+ 'remark' => $remark,
+ ]);
+ $dns = DnsHelper::getModel($id);
+ if($dns){
+ if($dns->check()){
+ Db::commit();
+ return json(['code'=>0, 'msg'=>'修改域名账户成功!']);
+ }else{
+ Db::rollback();
+ return json(['code'=>-1, 'msg'=>'验证域名账户失败,'.$dns->getError()]);
+ }
+ }else{
+ Db::rollback();
+ return json(['code'=>-1, 'msg'=>'DNS模块('.$type.')不存在']);
+ }
+ }elseif($act == 'del'){
+ $id = input('post.id/d');
+ $dcount = DB::name('domain')->where('aid', $id)->count();
+ if($dcount > 0) return json(['code'=>-1, 'msg'=>'该域名账户下存在域名,无法删除']);
+ Db::name('account')->where('id', $id)->delete();
+ return json(['code'=>0]);
+ }
+ return json(['code'=>-3]);
+ }
+
+
+ public function domain(){
+ if(request()->user['type'] == 'domain'){
+ return redirect('/record/'.request()->user['id']);
+ }
+ $list = Db::name('account')->select();
+ $accounts = [];
+ $types = [];
+ foreach($list as $row){
+ $accounts[$row['id']] = $row['id'].'_'.DnsHelper::$dns_config[$row['type']]['name'];
+ if(!array_key_exists($row['type'], $types)){
+ $types[$row['type']] = DnsHelper::$dns_config[$row['type']]['name'];
+ }
+ if(!empty($row['remark'])){
+ $accounts[$row['id']] .= '('.$row['remark'].')';
+ }
+ }
+ View::assign('accounts', $accounts);
+ View::assign('types', $types);
+ return view();
+ }
+
+ public function domain_data(){
+ if(!checkPermission(1)) return json(['total'=>0, 'rows'=>[]]);
+ $kw = input('post.kw', null, 'trim');
+ $type = input('post.type', null, 'trim');
+ $offset = input('post.offset/d', 0);
+ $limit = input('post.limit/d', 10);
+
+ $select = Db::name('domain')->alias('A')->join('account B','A.aid = B.id');
+ if(!empty($kw)){
+ $select->whereLike('name', '%'.$kw.'%');
+ }
+ if(!empty($type)){
+ $select->whereLike('B.type', $type);
+ }
+ if(request()->user['level'] == 1){
+ $select->where('is_hide', 0)->where('A.name', 'in', request()->user['permission']);
+ }
+ $total = $select->count();
+ $rows = $select->fieldRaw('A.*,B.type,B.remark')->order('A.id','desc')->limit($offset, $limit)->select();
+
+ $list = [];
+ foreach($rows as $row){
+ $row['typename'] = DnsHelper::$dns_config[$row['type']]['name'];
+ $list[] = $row;
+ }
+
+ return json(['total'=>$total, 'rows'=>$list]);
+ }
+
+ public function domain_op(){
+ if(!checkPermission(1)) return $this->alert('error', '无权限');
+ $act = input('param.act');
+ if($act == 'get'){
+ $id = input('post.id/d');
+ $row = Db::name('domain')->where('id', $id)->find();
+ if(!$row) return json(['code'=>-1, 'msg'=>'域名不存在']);
+ return json(['code'=>0, 'data'=>$row]);
+ }elseif($act == 'add'){
+ if(!checkPermission(2)) return $this->alert('error', '无权限');
+ $aid = input('post.aid/d');
+ $name = input('post.name', null, 'trim');
+ $thirdid = input('post.thirdid', null, 'trim');
+ $recordcount = input('post.recordcount/d', 0);
+ if(empty($name) || empty($thirdid)) return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ if(Db::name('domain')->where('aid', $aid)->where('name', $name)->find()){
+ return json(['code'=>-1, 'msg'=>'域名已存在']);
+ }
+ Db::name('domain')->insert([
+ 'aid' => $aid,
+ 'name' => $name,
+ 'thirdid' => $thirdid,
+ 'addtime' => date('Y-m-d H:i:s'),
+ 'is_hide' => 0,
+ 'is_sso' => 1,
+ 'recordcount' => $recordcount,
+ ]);
+ return json(['code'=>0, 'msg'=>'添加域名成功!']);
+ }elseif($act == 'edit'){
+ if(!checkPermission(2)) return $this->alert('error', '无权限');
+ $id = input('post.id/d');
+ $row = Db::name('domain')->where('id', $id)->find();
+ if(!$row) return json(['code'=>-1, 'msg'=>'域名不存在']);
+ $is_hide = input('post.is_hide/d');
+ $is_sso = input('post.is_sso/d');
+ Db::name('domain')->where('id', $id)->update([
+ 'is_hide' => $is_hide,
+ 'is_sso' => $is_sso,
+ ]);
+ return json(['code'=>0, 'msg'=>'修改域名配置成功!']);
+ }elseif($act == 'del'){
+ if(!checkPermission(2)) return $this->alert('error', '无权限');
+ $id = input('post.id/d');
+ Db::name('domain')->where('id', $id)->delete();
+ return json(['code'=>0]);
+ }
+ return json(['code'=>-3]);
+ }
+
+ public function domain_list(){
+ if(!checkPermission(2)) return $this->alert('error', '无权限');
+ $aid = input('post.aid/d');
+ $kw = input('post.kw', null, 'trim');
+ $page = input('?post.page') ? input('post.page/d') : 1;
+ $pagesize = input('?post.pagesize') ? input('post.pagesize/d') : 10;
+ $dns = DnsHelper::getModel($aid);
+ $list = $dns->getDomainList($kw, $page, $pagesize);
+ if(!$list) return json(['code'=>-1, 'msg'=>'获取域名列表失败,'.$dns->getError()]);
+ return json(['code'=>0, 'data'=>$list]);
+ }
+
+ //获取解析线路和最小TTL
+ private function get_line_and_ttl($drow){
+ $recordLine = cache('record_line_'.$drow['id']);
+ $minTTL = cache('min_ttl_'.$drow['id']);
+ if(empty($recordLine)){
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ if(!$dns) return $this->alert('error', 'DNS模块不存在');
+ $recordLine = $dns->getRecordLine($drow['name']);
+ if(!$recordLine) return $this->alert('error', '获取解析线路列表失败,'.$dns->getError());
+ cache('record_line_'.$drow['id'], $recordLine, 604800);
+ $minTTL = $dns->getMinTTL($drow['name']);
+ if($minTTL){
+ cache('min_ttl_'.$drow['id'], $minTTL, 604800);
+ }
+ }
+ return [$recordLine, $minTTL];
+ }
+
+ public function domain_info(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return $this->alert('error', '域名不存在');
+ }
+ $dnstype = Db::name('account')->where('id', $drow['aid'])->value('type');
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ list($recordLine, $minTTL) = $this->get_line_and_ttl($drow);
+
+ $recordLineArr = [];
+ foreach($recordLine as $key=>$item){
+ $recordLineArr[] = ['id'=>$key, 'name'=>$item['name'], 'parent'=>$item['parent']];
+ }
+
+ $dnsconfig = DnsHelper::$dns_config[$dnstype];
+ $dnsconfig['type'] = $dnstype;
+
+ $drow['config'] = $dnsconfig;
+ $drow['recordLine'] = $recordLineArr;
+ $drow['minTTL'] = $minTTL?$minTTL:1;
+ if(input('?post.loginurl') && input('post.loginurl') == '1'){
+ $token = getSid();
+ cache('quicklogin_'.$drow['name'], $token, 3600);
+ $timestamp = time();
+ $sign = md5(env('app.sys_key').$drow['name'].$timestamp.$token.env('app.sys_key'));
+ $drow['loginurl'] = request()->root(true).'/quicklogin?domain='.$drow['name'].'×tamp='.$timestamp.'&token='.$token.'&sign='.$sign;
+ }
+
+ return json(['code'=>0, 'data'=>$drow]);
+ }
+
+ public function record(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return $this->alert('error', '域名不存在');
+ }
+ $dnstype = Db::name('account')->where('id', $drow['aid'])->value('type');
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ list($recordLine, $minTTL) = $this->get_line_and_ttl($drow);
+
+ $recordLineArr = [];
+ foreach($recordLine as $key=>$item){
+ $recordLineArr[] = ['id'=>$key, 'name'=>$item['name'], 'parent'=>$item['parent']];
+ }
+
+ $dnsconfig = DnsHelper::$dns_config[$dnstype];
+ $dnsconfig['type'] = $dnstype;
+
+ View::assign('domainId', $id);
+ View::assign('domainName', $drow['name']);
+ View::assign('recordLine', $recordLineArr);
+ View::assign('minTTL', $minTTL?$minTTL:1);
+ View::assign('dnsconfig', $dnsconfig);
+ return view();
+ }
+
+ public function record_data(){
+ $id = input('param.id/d');
+ $keyword = input('post.keyword', null, 'trim');
+ $subdomain = input('post.subdomain', null, 'trim');
+ $type = input('post.type', null, 'trim');
+ $line = input('post.line', null, 'trim');
+ $status = input('post.status', null, 'trim');
+ $offset = input('post.offset/d');
+ $limit = input('post.limit/d');
+ if($limit == 0){
+ $page = 1;
+ }else{
+ $page = $offset/$limit + 1;
+ }
+
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['total'=>0, 'rows'=>[]]);
+ }
+ if(!checkPermission(0, $drow['name'])) return json(['total'=>0, 'rows'=>[]]);
+
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ $domainRecords = $dns->getDomainRecords($page, $limit, $keyword, $subdomain, $type, $line, $status);
+ if(!$domainRecords) return json(['total'=>0, 'rows'=>[]]);
+
+ if($domainRecords['total'] != $drow['recordcount']){
+ Db::name('domain')->where('id', $id)->update(['recordcount'=>$domainRecords['total']]);
+ }
+
+ $recordLine = cache('record_line_'.$id);
+
+ $list = [];
+ foreach($domainRecords['list'] as $row){
+ $row['LineName'] = isset($recordLine[$row['Line']]) ? $recordLine[$row['Line']]['name'] : $row['Line'];
+ $list[] = $row;
+ }
+
+ return json(['total'=>$domainRecords['total'], 'rows'=>$list]);
+ }
+
+ public function record_add(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['code'=>-1, 'msg'=>'域名不存在']);
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ $name = input('post.name', null, 'trim');
+ $type = input('post.type', null, 'trim');
+ $value = input('post.value', null, 'trim');
+ $line = input('post.line', null, 'trim');
+ $ttl = input('post.ttl/d', 600);
+ $mx = input('post.mx/d', 1);
+ $remark = input('post.remark', null, 'trim');
+
+ if(empty($name) || empty($type) || empty($value)){
+ return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ }
+
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ $recordid = $dns->addDomainRecord($name, $type, $value, $line, $ttl, $mx, $remark);
+ if($recordid){
+ $this->add_log($drow['name'], '添加解析', $type.'记录 '.$name.' '.$value.' (线路:'.$line.' TTL:'.$ttl.')');
+ return json(['code'=>0, 'msg'=>'添加解析记录成功!']);
+ }else{
+ return json(['code'=>-1, 'msg'=>'添加解析记录失败,'.$dns->getError()]);
+ }
+ }
+
+ public function record_update(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['code'=>-1, 'msg'=>'域名不存在']);
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ $recordid = input('post.recordid', null, 'trim');
+ $name = input('post.name', null, 'trim');
+ $type = input('post.type', null, 'trim');
+ $value = input('post.value', null, 'trim');
+ $line = input('post.line', null, 'trim');
+ $ttl = input('post.ttl/d', 600);
+ $mx = input('post.mx/d', 1);
+ $remark = input('post.remark', null, 'trim');
+
+ if(empty($recordid) || empty($name) || empty($type) || empty($value)){
+ return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ }
+
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ $recordid = $dns->updateDomainRecord($recordid, $name, $type, $value, $line, $ttl, $mx, $remark);
+ if($recordid){
+ $this->add_log($drow['name'], '修改解析', $type.'记录 '.$name.' '.$value.' (线路:'.$line.' TTL:'.$ttl.')');
+ return json(['code'=>0, 'msg'=>'修改解析记录成功!']);
+ }else{
+ return json(['code'=>-1, 'msg'=>'修改解析记录失败,'.$dns->getError()]);
+ }
+ }
+
+ public function record_delete(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['code'=>-1, 'msg'=>'域名不存在']);
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ $recordid = input('post.recordid', null, 'trim');
+
+ if(empty($recordid)){
+ return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ }
+
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ if($dns->deleteDomainRecord($recordid)){
+ $this->add_log($drow['name'], '删除解析', '记录ID:'.$recordid);
+ return json(['code'=>0, 'msg'=>'删除解析记录成功!']);
+ }else{
+ return json(['code'=>-1, 'msg'=>'删除解析记录失败,'.$dns->getError()]);
+ }
+ }
+
+ public function record_status(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['code'=>-1, 'msg'=>'域名不存在']);
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ $recordid = input('post.recordid', null, 'trim');
+ $status = input('post.status', null, 'trim');
+
+ if(empty($recordid)){
+ return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ }
+
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ if($dns->setDomainRecordStatus($recordid, $status)){
+ $action = $status == '1' ? '启用解析' : '暂停解析';
+ $this->add_log($drow['name'], $action, '记录ID:'.$recordid);
+ return json(['code'=>0, 'msg'=>'操作成功!']);
+ }else{
+ return json(['code'=>-1, 'msg'=>'操作失败,'.$dns->getError()]);
+ }
+ }
+
+ public function record_remark(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['code'=>-1, 'msg'=>'域名不存在']);
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ $recordid = input('post.recordid', null, 'trim');
+ $remark = input('post.remark', null, 'trim');
+
+ if(empty($recordid)){
+ return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ }
+ if(empty($remark)) $remark = null;
+
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ if($dns->updateDomainRecordRemark($recordid, $remark)){
+ return json(['code'=>0, 'msg'=>'操作成功!']);
+ }else{
+ return json(['code'=>-1, 'msg'=>'操作失败,'.$dns->getError()]);
+ }
+ }
+
+ public function record_batch(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return json(['code'=>-1, 'msg'=>'域名不存在']);
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ $recordids = input('post.recordids', null, 'trim');
+ $action = input('post.action', null, 'trim');
+
+ if(empty($recordids) || empty($action)){
+ return json(['code'=>-1, 'msg'=>'参数不能为空']);
+ }
+
+ $success = 0;
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ if($action == 'open'){
+ foreach($recordids as $recordid){
+ if($dns->setDomainRecordStatus($recordid, '1')){
+ $this->add_log($drow['name'], '启用解析', '记录ID:'.$recordid);
+ $success++;
+ }
+ }
+ $msg = '成功启用'.$success.'条解析记录';
+ }else if($action == 'pause'){
+ foreach($recordids as $recordid){
+ if($dns->setDomainRecordStatus($recordid, '0')){
+ $this->add_log($drow['name'], '暂停解析', '记录ID:'.$recordid);
+ $success++;
+ }
+ }
+ $msg = '成功暂停'.$success.'条解析记录';
+ }else if($action == 'delete'){
+ foreach($recordids as $recordid){
+ if($dns->deleteDomainRecord($recordid)){
+ $this->add_log($drow['name'], '删除解析', '记录ID:'.$recordid);
+ $success++;
+ }
+ }
+ $msg = '成功删除'.$success.'条解析记录';
+ }
+ return json(['code'=>0, 'msg'=>$msg]);
+ }
+
+ public function record_log(){
+ $id = input('param.id/d');
+ $drow = Db::name('domain')->where('id', $id)->find();
+ if(!$drow){
+ return $this->alert('error', '域名不存在');
+ }
+ if(!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
+
+ if(request()->isPost()){
+ $offset = input('post.offset/d');
+ $limit = input('post.limit/d');
+ $page = $offset/$limit + 1;
+ $dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
+ $domainRecords = $dns->getDomainRecordLog($page, $limit);
+ if(!$domainRecords) return json(['total'=>0, 'rows'=>[]]);
+ return json(['total'=>$domainRecords['total'], 'rows'=>$domainRecords['list']]);
+ }
+
+ View::assign('domainId', $id);
+ View::assign('domainName', $drow['name']);
+ return view('log');
+ }
+
+ private function add_log($domain, $action, $data){
+ Db::name('log')->insert(['uid' => request()->user['id'], 'domain' => $domain, 'action' => $action, 'data' => $data, 'addtime' => date("Y-m-d H:i:s")]);
+ }
+}
\ No newline at end of file
diff --git a/app/controller/Index.php b/app/controller/Index.php
new file mode 100644
index 0000000..bf4a387
--- /dev/null
+++ b/app/controller/Index.php
@@ -0,0 +1,96 @@
+user['type'] == 'domain'){
+ return redirect('/record/'.request()->user['id']);
+ }
+ if(request()->isAjax()){
+ if(input('post.do') == 'stat'){
+ $stat = ['domains'=>0, 'users'=>0, 'records'=>0, 'types'=>count(DnsHelper::$dns_config)];
+ if(request()->user['level'] == 2){
+ $stat['domains'] = Db::name('domain')->count();
+ $stat['users'] = Db::name('user')->count();
+ $stat['records'] = Db::name('domain')->sum('recordcount');
+ }else{
+ $stat['domains'] = Db::name('domain')->where('name', 'in', request()->user['permission'])->count();
+ $stat['users'] = 1;
+ $stat['records'] = Db::name('domain')->where('name', 'in', request()->user['permission'])->sum('recordcount');
+ }
+ return json($stat);
+ }
+ return json(['code'=>-3]);
+ }
+
+ $tmp = 'version()';
+ $mysqlVersion = Db::query("select version()")[0][$tmp];
+ $info = [
+ 'framework_version' => app()::VERSION,
+ 'php_version' => PHP_VERSION,
+ 'mysql_version' => $mysqlVersion,
+ 'software' => $_SERVER['SERVER_SOFTWARE'],
+ 'os' => php_uname(),
+ 'date' => date("Y-m-d H:i:s"),
+ ];
+ View::assign('info', $info);
+ View::assign('checkupdate', '//auth.cccyun.cc/app/dnsmgr.php?ver='.config('app.version'));
+ return view();
+ }
+
+ public function changeskin(){
+ $skin = input('post.skin');
+ if(request()->user['level'] == 2){
+ if(cookie('admin_skin')){
+ cookie('admin_skin', null);
+ }
+ cache('admin_skin', $skin);
+ }else{
+ cookie('admin_skin', $skin);
+ }
+ return json(['code'=>0,'msg'=>'succ']);
+ }
+
+ public function cleancache(){
+ if(!checkPermission(1)) return $this->alert('error', '无权限');
+ Cache::clear();
+ return json(['code'=>0,'msg'=>'succ']);
+ }
+
+ public function doc(){
+ if(!checkPermission(1)) return $this->alert('error', '无权限');
+ View::assign('siteurl', request()->root(true));
+ return view();
+ }
+
+ public function setpwd(){
+ if(!checkPermission(1)) return $this->alert('error', '无权限');
+ if(request()->isPost()){
+ $oldpwd = input('post.oldpwd');
+ $newpwd = input('post.newpwd');
+ $newpwd2 = input('post.newpwd2');
+ if(empty($oldpwd) || empty($newpwd) || empty($newpwd2)){
+ return json(['code'=>-1, 'msg'=>'密码不能为空']);
+ }
+ if($newpwd != $newpwd2){
+ return json(['code'=>-1, 'msg'=>'两次输入的密码不一致']);
+ }
+ if(!password_verify($oldpwd, request()->user['password'])){
+ return json(['code'=>-1, 'msg'=>'原密码错误']);
+ }
+ Db::name('user')->where('id', request()->user['id'])->update(['password'=>password_hash($newpwd, PASSWORD_DEFAULT)]);
+ return json(['code'=>0, 'msg'=>'succ']);
+ }
+ return view();
+ }
+
+}
diff --git a/app/controller/Install.php b/app/controller/Install.php
new file mode 100644
index 0000000..3f39265
--- /dev/null
+++ b/app/controller/Install.php
@@ -0,0 +1,84 @@
+getRootPath().'.env')){
+ return '当前已经安装成功,如果需要重新安装,请手动删除根目录.env文件';
+ }
+ if(request()->isPost()){
+ $mysql_host = input('post.mysql_host', null, 'trim');
+ $mysql_port = intval(input('post.mysql_port', '3306'));
+ $mysql_user = input('post.mysql_user', null, 'trim');
+ $mysql_pwd = input('post.mysql_pwd', null, 'trim');
+ $mysql_name = input('post.mysql_name', null, 'trim');
+ $mysql_prefix = input('post.mysql_prefix', 'cloud_', 'trim');
+ $admin_username = input('post.admin_username', null, 'trim');
+ $admin_password = input('post.admin_password', null, 'trim');
+
+ if(!$mysql_host || !$mysql_user || !$mysql_pwd || !$mysql_name || !$admin_username || !$admin_password){
+ return json(['code'=>0, 'msg'=>'必填项不能为空']);
+ }
+
+ $configdata = file_get_contents(app()->getRootPath().'.example.env');
+ $configdata = str_replace(['{syskey}','{dbhost}','{dbname}','{dbuser}','{dbpwd}','{dbport}','{dbprefix}'], [random(16), $mysql_host, $mysql_name, $mysql_user, $mysql_pwd, $mysql_port, $mysql_prefix], $configdata);
+
+ try{
+ $DB=new PDO("mysql:host=".$mysql_host.";dbname=".$mysql_name.";port=".$mysql_port,$mysql_user,$mysql_pwd);
+ }catch(Exception $e){
+ if($e->getCode() == 2002){
+ $errorMsg='连接数据库失败:数据库地址填写错误!';
+ }elseif($e->getCode() == 1045){
+ $errorMsg='连接数据库失败:数据库用户名或密码填写错误!';
+ }elseif($e->getCode() == 1049){
+ $errorMsg='连接数据库失败:数据库名不存在!';
+ }else{
+ $errorMsg='连接数据库失败:'.$e->getMessage();
+ }
+ return json(['code'=>0, 'msg'=>$errorMsg]);
+ }
+ $DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
+ $DB->exec("set sql_mode = ''");
+ $DB->exec("set names utf8");
+
+ $sqls=file_get_contents(app()->getAppPath().'sql/install.sql');
+ $sqls=explode(';', $sqls);
+
+ $password = password_hash($admin_password, PASSWORD_DEFAULT);
+ $sqls[]="INSERT INTO `".$mysql_prefix."user` (`username`,`password`,`level`,`regtime`,`lasttime`,`status`) VALUES ('".addslashes($admin_username)."', '$password', 2, NOW(), NOW(), 1)";
+
+ $success=0;$error=0;$errorMsg=null;
+ foreach ($sqls as $value) {
+ $value=trim($value);
+ if(empty($value))continue;
+ $value = str_replace('dnsmgr_',$mysql_prefix,$value);
+ if($DB->exec($value)===false){
+ $error++;
+ $dberror=$DB->errorInfo();
+ $errorMsg.=$dberror[2]."\n";
+ }else{
+ $success++;
+ }
+ }
+ if(empty($errorMsg)){
+ if(!file_put_contents(app()->getRootPath().'.env', $configdata)){
+ return json(['code'=>0, 'msg'=>'保存失败,请确保网站根目录有写入权限']);
+ }
+ Cache::clear();
+ return json(['code'=>1, 'msg'=>'安装完成!成功执行SQL语句'.$success.'条']);
+ }else{
+ return json(['code'=>0, 'msg'=>$errorMsg]);
+ }
+ }
+ return view();
+ }
+
+}
diff --git a/app/controller/User.php b/app/controller/User.php
new file mode 100644
index 0000000..99b8ab2
--- /dev/null
+++ b/app/controller/User.php
@@ -0,0 +1,164 @@
+alert('error', '无权限');
+ $list = Db::name('domain')->select();
+ $domains = [];
+ foreach($list as $row){
+ $domains[] = $row['name'];
+ }
+ View::assign('domains', $domains);
+ return view();
+ }
+
+ public function user_data(){
+ if(!checkPermission(2)) return json(['total'=>0, 'rows'=>[]]);
+ $kw = input('post.kw', null, 'trim');
+ $offset = input('post.offset/d');
+ $limit = input('post.limit/d');
+
+ $select = Db::name('user');
+ if(!empty($kw)){
+ $select->whereLike('id|username', $kw);
+ }
+ $total = $select->count();
+ $rows = $select->order('id','desc')->limit($offset, $limit)->select();
+
+ return json(['total'=>$total, 'rows'=>$rows]);
+ }
+
+ public function user_op(){
+ if(!checkPermission(2)) return $this->alert('error', '无权限');
+ $act = input('param.act');
+ if($act == 'get'){
+ $id = input('post.id/d');
+ $row = Db::name('user')->where('id', $id)->find();
+ if(!$row) return json(['code'=>-1, 'msg'=>'用户不存在']);
+ $row['permission'] = Db::name('permission')->where('uid', $id)->column('domain');
+ return json(['code'=>0, 'data'=>$row]);
+ }elseif($act == 'add'){
+ $username = input('post.username', null, 'trim');
+ $password = input('post.password', null, 'trim');
+ $is_api = input('post.is_api/d');
+ $apikey = input('post.apikey', null, 'trim');
+ $level = input('post.level/d');
+ if(empty($username) || empty($password)) return json(['code'=>-1, 'msg'=>'用户名或密码不能为空']);
+ if($is_api ==1 && empty($apikey)) return json(['code'=>-1, 'msg'=>'API密钥不能为空']);
+ if(Db::name('user')->where('username', $username)->find()){
+ return json(['code'=>-1, 'msg'=>'用户名已存在']);
+ }
+ $uid = Db::name('user')->insertGetId([
+ 'username' => $username,
+ 'password' => password_hash($password, PASSWORD_DEFAULT),
+ 'is_api' => $is_api,
+ 'apikey' => $apikey,
+ 'level' => $level,
+ 'regtime' => date('Y-m-d H:i:s'),
+ 'status' => 1,
+ ]);
+ if($level == 1){
+ $permission = input('post.permission/a');
+ if(!empty($permission)){
+ $data = [];
+ foreach($permission as $domain){
+ $data[] = ['uid'=>$uid, 'domain'=>$domain];
+ }
+ Db::name('permission')->insertAll($data);
+ }
+ }
+ return json(['code'=>0, 'msg'=>'添加用户成功!']);
+ }elseif($act == 'edit'){
+ $id = input('post.id/d');
+ $row = Db::name('user')->where('id', $id)->find();
+ if(!$row) return json(['code'=>-1, 'msg'=>'用户不存在']);
+ $username = input('post.username', null, 'trim');
+ $is_api = input('post.is_api/d');
+ $apikey = input('post.apikey', null, 'trim');
+ $level = input('post.level/d');
+ $repwd = input('post.repwd', null, 'trim');
+ if(empty($username)) return json(['code'=>-1, 'msg'=>'用户名不能为空']);
+ if($is_api ==1 && empty($apikey)) return json(['code'=>-1, 'msg'=>'API密钥不能为空']);
+ if(Db::name('user')->where('username', $username)->where('id', '<>', $id)->find()){
+ return json(['code'=>-1, 'msg'=>'用户名已存在']);
+ }
+ if($level == 1 && ($id == 1000 || $id == request()->user['id'])) $level = 2;
+ Db::name('user')->where('id', $id)->update([
+ 'username' => $username,
+ 'is_api' => $is_api,
+ 'apikey' => $apikey,
+ 'level' => $level,
+ ]);
+ Db::name('permission')->where(['uid'=>$id])->delete();
+ if($level == 1){
+ $permission = input('post.permission/a');
+ if(!empty($permission)){
+ $data = [];
+ foreach($permission as $domain){
+ $data[] = ['uid'=>$id, 'domain'=>$domain];
+ }
+ Db::name('permission')->insertAll($data);
+ }
+ }
+ if(!empty($repwd)){
+ Db::name('user')->where('id', $id)->update(['password'=>password_hash($repwd, PASSWORD_DEFAULT)]);
+ }
+ return json(['code'=>0, 'msg'=>'修改用户成功!']);
+ }elseif($act == 'set'){
+ $id = input('post.id/d');
+ $status = input('post.status/d');
+ if($id == 1000) return json(['code'=>-1, 'msg'=>'此用户无法修改状态']);
+ if($id == request()->user['id']) return json(['code'=>-1, 'msg'=>'当前登录用户无法修改状态']);
+ Db::name('user')->where('id', $id)->update(['status'=>$status]);
+ return json(['code'=>0]);
+ }elseif($act == 'del'){
+ $id = input('post.id/d');
+ if($id == 1000) return json(['code'=>-1, 'msg'=>'此用户无法删除']);
+ if($id == request()->user['id']) return json(['code'=>-1, 'msg'=>'当前登录用户无法删除']);
+ Db::name('user')->where('id', $id)->delete();
+ return json(['code'=>0]);
+ }
+ return json(['code'=>-3]);
+ }
+
+ public function log(){
+ return view();
+ }
+
+ public function log_data(){
+ $uid = input('post.uid', null, 'trim');
+ $kw = input('post.kw', null, 'trim');
+ $domain = input('post.domain', null, 'trim');
+ $offset = input('post.offset/d');
+ $limit = input('post.limit/d');
+
+ $select = Db::name('log');
+ if(request()->user['type'] == 'domain'){
+ $select->where('domain', request()->user['name']);
+ }elseif(request()->user['level'] == 1){
+ $select->where('uid', request()->user['id']);
+ }elseif(!empty($uid)){
+ $select->where('uid', $uid);
+ }
+ if(!empty($kw)){
+ $select->whereLike('action|data', '%'.$kw.'%');
+ }
+ if(!empty($domain)){
+ $select->where('domain', $domain);
+ }
+ $total = $select->count();
+ $rows = $select->order('id','desc')->limit($offset, $limit)->select();
+
+ return json(['total'=>$total, 'rows'=>$rows]);
+ }
+
+}
\ No newline at end of file
diff --git a/app/data/huawei_line.json b/app/data/huawei_line.json
new file mode 100644
index 0000000..941f3b6
--- /dev/null
+++ b/app/data/huawei_line.json
@@ -0,0 +1 @@
+{"default_view":{"name":"全网默认","parent":null},"Dianxin":{"name":"电信","parent":null},"Dianxin_Huabei":{"name":"电信_华北地区","parent":"Dianxin"},"Dianxin_Beijing":{"name":"电信_北京","parent":"Dianxin_Huabei"},"Dianxin_Tianjin":{"name":"电信_天津","parent":"Dianxin_Huabei"},"Dianxin_Hebei":{"name":"电信_河北","parent":"Dianxin_Huabei"},"Dianxin_Shanxi":{"name":"电信_山西","parent":"Dianxin_Huabei"},"Dianxin_Neimenggu":{"name":"电信_内蒙古","parent":"Dianxin_Huabei"},"Dianxin_Dongbei":{"name":"电信_东北地区","parent":"Dianxin"},"Dianxin_Liaoning":{"name":"电信_辽宁","parent":"Dianxin_Dongbei"},"Dianxin_Jilin":{"name":"电信_吉林","parent":"Dianxin_Dongbei"},"Dianxin_Heilongjiang":{"name":"电信_黑龙江","parent":"Dianxin_Dongbei"},"Dianxin_Xibei":{"name":"电信_西北地区","parent":"Dianxin"},"Dianxin_Shaanxi":{"name":"电信_陕西","parent":"Dianxin_Xibei"},"Dianxin_Gansu":{"name":"电信_甘肃","parent":"Dianxin_Xibei"},"Dianxin_Qinghai":{"name":"电信_青海","parent":"Dianxin_Xibei"},"Dianxin_Ningxia":{"name":"电信_宁夏","parent":"Dianxin_Xibei"},"Dianxin_Xinjiang":{"name":"电信_新疆","parent":"Dianxin_Xibei"},"Dianxin_Huazhong":{"name":"电信_华中地区","parent":"Dianxin"},"Dianxin_Henan":{"name":"电信_河南","parent":"Dianxin_Huazhong"},"Dianxin_Hubei":{"name":"电信_湖北","parent":"Dianxin_Huazhong"},"Dianxin_Hunan":{"name":"电信_湖南","parent":"Dianxin_Huazhong"},"Dianxin_Huadong":{"name":"电信_华东地区","parent":"Dianxin"},"Dianxin_Shanghai":{"name":"电信_上海","parent":"Dianxin_Huadong"},"Dianxin_Jiangsu":{"name":"电信_江苏","parent":"Dianxin_Huadong"},"Dianxin_Zhejiang":{"name":"电信_浙江","parent":"Dianxin_Huadong"},"Dianxin_Anhui":{"name":"电信_安徽","parent":"Dianxin_Huadong"},"Dianxin_Fujian":{"name":"电信_福建","parent":"Dianxin_Huadong"},"Dianxin_Jiangxi":{"name":"电信_江西","parent":"Dianxin_Huadong"},"Dianxin_Shandong":{"name":"电信_山东","parent":"Dianxin_Huadong"},"Dianxin_Huanan":{"name":"电信_华南地区","parent":"Dianxin"},"Dianxin_Guangdong":{"name":"电信_广东","parent":"Dianxin_Huanan"},"Dianxin_Hainan":{"name":"电信_海南","parent":"Dianxin_Huanan"},"Dianxin_Guangxi":{"name":"电信_广西","parent":"Dianxin_Huanan"},"Dianxin_Xinan":{"name":"电信_西南地区","parent":"Dianxin"},"Dianxin_Chongqing":{"name":"电信_重庆","parent":"Dianxin_Xinan"},"Dianxin_Sichuan":{"name":"电信_四川","parent":"Dianxin_Xinan"},"Dianxin_Guizhou":{"name":"电信_贵州","parent":"Dianxin_Xinan"},"Dianxin_Yunnan":{"name":"电信_云南","parent":"Dianxin_Xinan"},"Dianxin_Xizang":{"name":"电信_西藏","parent":"Dianxin_Xinan"},"Yidong":{"name":"移动","parent":null},"Yidong_Huabei":{"name":"移动_华北地区","parent":"Yidong"},"Yidong_Beijing":{"name":"移动_北京","parent":"Yidong_Huabei"},"Yidong_Tianjin":{"name":"移动_天津","parent":"Yidong_Huabei"},"Yidong_Hebei":{"name":"移动_河北","parent":"Yidong_Huabei"},"Yidong_Shanxi":{"name":"移动_山西","parent":"Yidong_Huabei"},"Yidong_Neimenggu":{"name":"移动_内蒙古","parent":"Yidong_Huabei"},"Yidong_Dongbei":{"name":"移动_东北地区","parent":"Yidong"},"Yidong_Liaoning":{"name":"移动_辽宁","parent":"Yidong_Dongbei"},"Yidong_Jilin":{"name":"移动_吉林","parent":"Yidong_Dongbei"},"Yidong_Heilongjiang":{"name":"移动_黑龙江","parent":"Yidong_Dongbei"},"Yidong_Xibei":{"name":"移动_西北地区","parent":"Yidong"},"Yidong_Shaanxi":{"name":"移动_陕西","parent":"Yidong_Xibei"},"Yidong_Gansu":{"name":"移动_甘肃","parent":"Yidong_Xibei"},"Yidong_Qinghai":{"name":"移动_青海","parent":"Yidong_Xibei"},"Yidong_Ningxia":{"name":"移动_宁夏","parent":"Yidong_Xibei"},"Yidong_Xinjiang":{"name":"移动_新疆","parent":"Yidong_Xibei"},"Yidong_Huazhong":{"name":"移动_华中地区","parent":"Yidong"},"Yidong_Henan":{"name":"移动_河南","parent":"Yidong_Huazhong"},"Yidong_Hubei":{"name":"移动_湖北","parent":"Yidong_Huazhong"},"Yidong_Hunan":{"name":"移动_湖南","parent":"Yidong_Huazhong"},"Yidong_Huadong":{"name":"移动_华东地区","parent":"Yidong"},"Yidong_Shanghai":{"name":"移动_上海","parent":"Yidong_Huadong"},"Yidong_Jiangsu":{"name":"移动_江苏","parent":"Yidong_Huadong"},"Yidong_Zhejiang":{"name":"移动_浙江","parent":"Yidong_Huadong"},"Yidong_Anhui":{"name":"移动_安徽","parent":"Yidong_Huadong"},"Yidong_Fujian":{"name":"移动_福建","parent":"Yidong_Huadong"},"Yidong_Jiangxi":{"name":"移动_江西","parent":"Yidong_Huadong"},"Yidong_Shandong":{"name":"移动_山东","parent":"Yidong_Huadong"},"Yidong_Huanan":{"name":"移动_华南地区","parent":"Yidong"},"Yidong_Guangdong":{"name":"移动_广东","parent":"Yidong_Huanan"},"Yidong_Hainan":{"name":"移动_海南","parent":"Yidong_Huanan"},"Yidong_Guangxi":{"name":"移动_广西","parent":"Yidong_Huanan"},"Yidong_Xinan":{"name":"移动_西南地区","parent":"Yidong"},"Yidong_Chongqing":{"name":"移动_重庆","parent":"Yidong_Xinan"},"Yidong_Sichuan":{"name":"移动_四川","parent":"Yidong_Xinan"},"Yidong_Guizhou":{"name":"移动_贵州","parent":"Yidong_Xinan"},"Yidong_Yunnan":{"name":"移动_云南","parent":"Yidong_Xinan"},"Yidong_Xizang":{"name":"移动_西藏","parent":"Yidong_Xinan"},"Liantong":{"name":"联通","parent":null},"Liantong_Huabei":{"name":"联通_华北地区","parent":"Liantong"},"Liantong_Beijing":{"name":"联通_北京","parent":"Liantong_Huabei"},"Liantong_Tianjin":{"name":"联通_天津","parent":"Liantong_Huabei"},"Liantong_Hebei":{"name":"联通_河北","parent":"Liantong_Huabei"},"Liantong_Shanxi":{"name":"联通_山西","parent":"Liantong_Huabei"},"Liantong_Neimenggu":{"name":"联通_内蒙古","parent":"Liantong_Huabei"},"Liantong_Dongbei":{"name":"联通_东北地区","parent":"Liantong"},"Liantong_Liaoning":{"name":"联通_辽宁","parent":"Liantong_Dongbei"},"Liantong_Jilin":{"name":"联通_吉林","parent":"Liantong_Dongbei"},"Liantong_Heilongjiang":{"name":"联通_黑龙江","parent":"Liantong_Dongbei"},"Liantong_Xibei":{"name":"联通_西北地区","parent":"Liantong"},"Liantong_Shaanxi":{"name":"联通_陕西","parent":"Liantong_Xibei"},"Liantong_Gansu":{"name":"联通_甘肃","parent":"Liantong_Xibei"},"Liantong_Qinghai":{"name":"联通_青海","parent":"Liantong_Xibei"},"Liantong_Ningxia":{"name":"联通_宁夏","parent":"Liantong_Xibei"},"Liantong_Xinjiang":{"name":"联通_新疆","parent":"Liantong_Xibei"},"Liantong_Huazhong":{"name":"联通_华中地区","parent":"Liantong"},"Liantong_Henan":{"name":"联通_河南","parent":"Liantong_Huazhong"},"Liantong_Hubei":{"name":"联通_湖北","parent":"Liantong_Huazhong"},"Liantong_Hunan":{"name":"联通_湖南","parent":"Liantong_Huazhong"},"Liantong_Huadong":{"name":"联通_华东地区","parent":"Liantong"},"Liantong_Shanghai":{"name":"联通_上海","parent":"Liantong_Huadong"},"Liantong_Jiangsu":{"name":"联通_江苏","parent":"Liantong_Huadong"},"Liantong_Zhejiang":{"name":"联通_浙江","parent":"Liantong_Huadong"},"Liantong_Anhui":{"name":"联通_安徽","parent":"Liantong_Huadong"},"Liantong_Fujian":{"name":"联通_福建","parent":"Liantong_Huadong"},"Liantong_Jiangxi":{"name":"联通_江西","parent":"Liantong_Huadong"},"Liantong_Shandong":{"name":"联通_山东","parent":"Liantong_Huadong"},"Liantong_Huanan":{"name":"联通_华南地区","parent":"Liantong"},"Liantong_Guangdong":{"name":"联通_广东","parent":"Liantong_Huanan"},"Liantong_Hainan":{"name":"联通_海南","parent":"Liantong_Huanan"},"Liantong_Guangxi":{"name":"联通_广西","parent":"Liantong_Huanan"},"Liantong_Xinan":{"name":"联通_西南地区","parent":"Liantong"},"Liantong_Chongqing":{"name":"联通_重庆","parent":"Liantong_Xinan"},"Liantong_Sichuan":{"name":"联通_四川","parent":"Liantong_Xinan"},"Liantong_Guizhou":{"name":"联通_贵州","parent":"Liantong_Xinan"},"Liantong_Yunnan":{"name":"联通_云南","parent":"Liantong_Xinan"},"Liantong_Xizang":{"name":"联通_西藏","parent":"Liantong_Xinan"},"Jiaoyuwang":{"name":"教育网","parent":null},"Tietong":{"name":"铁通","parent":null},"Pengboshi":{"name":"鹏博士","parent":null},"Pengboshi_Huabei":{"name":"鹏博士_华北地区","parent":"Pengboshi"},"Pengboshi_Beijing":{"name":"鹏博士_北京","parent":"Pengboshi_Huabei"},"Pengboshi_Tianjin":{"name":"鹏博士_天津","parent":"Pengboshi_Huabei"},"Pengboshi_Hebei":{"name":"鹏博士_河北","parent":"Pengboshi_Huabei"},"Pengboshi_Shanxi":{"name":"鹏博士_山西","parent":"Pengboshi_Huabei"},"Pengboshi_Neimenggu":{"name":"鹏博士_内蒙古","parent":"Pengboshi_Huabei"},"Pengboshi_Dongbei":{"name":"鹏博士_东北地区","parent":"Pengboshi"},"Pengboshi_Liaoning":{"name":"鹏博士_辽宁","parent":"Pengboshi_Dongbei"},"Pengboshi_Jilin":{"name":"鹏博士_吉林","parent":"Pengboshi_Dongbei"},"Pengboshi_Heilongjiang":{"name":"鹏博士_黑龙江","parent":"Pengboshi_Dongbei"},"Pengboshi_Xibei":{"name":"鹏博士_西北地区","parent":"Pengboshi"},"Pengboshi_Shaanxi":{"name":"鹏博士_陕西","parent":"Pengboshi_Xibei"},"Pengboshi_Gansu":{"name":"鹏博士_甘肃","parent":"Pengboshi_Xibei"},"Pengboshi_Qinghai":{"name":"鹏博士_青海","parent":"Pengboshi_Xibei"},"Pengboshi_Ningxia":{"name":"鹏博士_宁夏","parent":"Pengboshi_Xibei"},"Pengboshi_Xinjiang":{"name":"鹏博士_新疆","parent":"Pengboshi_Xibei"},"Pengboshi_Huazhong":{"name":"鹏博士_华中地区","parent":"Pengboshi"},"Pengboshi_Henan":{"name":"鹏博士_河南","parent":"Pengboshi_Huazhong"},"Pengboshi_Hubei":{"name":"鹏博士_湖北","parent":"Pengboshi_Huazhong"},"Pengboshi_Hunan":{"name":"鹏博士_湖南","parent":"Pengboshi_Huazhong"},"Pengboshi_Huadong":{"name":"鹏博士_华东地区","parent":"Pengboshi"},"Pengboshi_Shanghai":{"name":"鹏博士_上海","parent":"Pengboshi_Huadong"},"Pengboshi_Jiangsu":{"name":"鹏博士_江苏","parent":"Pengboshi_Huadong"},"Pengboshi_Zhejiang":{"name":"鹏博士_浙江","parent":"Pengboshi_Huadong"},"Pengboshi_Anhui":{"name":"鹏博士_安徽","parent":"Pengboshi_Huadong"},"Pengboshi_Fujian":{"name":"鹏博士_福建","parent":"Pengboshi_Huadong"},"Pengboshi_Jiangxi":{"name":"鹏博士_江西","parent":"Pengboshi_Huadong"},"Pengboshi_Shandong":{"name":"鹏博士_山东","parent":"Pengboshi_Huadong"},"Pengboshi_Huanan":{"name":"鹏博士_华南地区","parent":"Pengboshi"},"Pengboshi_Guangdong":{"name":"鹏博士_广东","parent":"Pengboshi_Huanan"},"Pengboshi_Hainan":{"name":"鹏博士_海南","parent":"Pengboshi_Huanan"},"Pengboshi_Guangxi":{"name":"鹏博士_广西","parent":"Pengboshi_Huanan"},"Pengboshi_Xinan":{"name":"鹏博士_西南地区","parent":"Pengboshi"},"Pengboshi_Chongqing":{"name":"鹏博士_重庆","parent":"Pengboshi_Xinan"},"Pengboshi_Sichuan":{"name":"鹏博士_四川","parent":"Pengboshi_Xinan"},"Pengboshi_Guizhou":{"name":"鹏博士_贵州","parent":"Pengboshi_Xinan"},"Pengboshi_Yunnan":{"name":"鹏博士_云南","parent":"Pengboshi_Xinan"},"Pengboshi_Xizang":{"name":"鹏博士_西藏","parent":"Pengboshi_Xinan"},"CN":{"name":"中国大陆","parent":null},"Huabei":{"name":"中国大陆_华北地区","parent":"CN"},"Beijing":{"name":"中国大陆_北京","parent":"Huabei"},"Tianjin":{"name":"中国大陆_天津","parent":"Huabei"},"Hebei":{"name":"中国大陆_河北","parent":"Huabei"},"Shanxi":{"name":"中国大陆_山西","parent":"Huabei"},"Neimenggu":{"name":"中国大陆_内蒙古","parent":"Huabei"},"Dongbei":{"name":"中国大陆_东北地区","parent":"CN"},"Liaoning":{"name":"中国大陆_辽宁","parent":"Dongbei"},"Jilin":{"name":"中国大陆_吉林","parent":"Dongbei"},"Heilongjiang":{"name":"中国大陆_黑龙江","parent":"Dongbei"},"Xibei":{"name":"中国大陆_西北地区","parent":"CN"},"Shaanxi":{"name":"中国大陆_陕西","parent":"Xibei"},"Gansu":{"name":"中国大陆_甘肃","parent":"Xibei"},"Qinghai":{"name":"中国大陆_青海","parent":"Xibei"},"Ningxia":{"name":"中国大陆_宁夏","parent":"Xibei"},"Xinjiang":{"name":"中国大陆_新疆","parent":"Xibei"},"Huazhong":{"name":"中国大陆_华中地区","parent":"CN"},"Henan":{"name":"中国大陆_河南","parent":"Huazhong"},"Hubei":{"name":"中国大陆_湖北","parent":"Huazhong"},"Hunan":{"name":"中国大陆_湖南","parent":"Huazhong"},"Huadong":{"name":"中国大陆_华东地区","parent":"CN"},"Shanghai":{"name":"中国大陆_上海","parent":"Huadong"},"Jiangsu":{"name":"中国大陆_江苏","parent":"Huadong"},"Zhejiang":{"name":"中国大陆_浙江","parent":"Huadong"},"Anhui":{"name":"中国大陆_安徽","parent":"Huadong"},"Fujian":{"name":"中国大陆_福建","parent":"Huadong"},"Jiangxi":{"name":"中国大陆_江西","parent":"Huadong"},"Shandong":{"name":"中国大陆_山东","parent":"Huadong"},"Huanan":{"name":"中国大陆_华南地区","parent":"CN"},"Guangdong":{"name":"中国大陆_广东","parent":"Huanan"},"Hainan":{"name":"中国大陆_海南","parent":"Huanan"},"Guangxi":{"name":"中国大陆_广西","parent":"Huanan"},"Xinan":{"name":"中国大陆_西南地区","parent":"CN"},"Chongqing":{"name":"中国大陆_重庆","parent":"Xinan"},"Sichuan":{"name":"中国大陆_四川","parent":"Xinan"},"Guizhou":{"name":"中国大陆_贵州","parent":"Xinan"},"Yunnan":{"name":"中国大陆_云南","parent":"Xinan"},"Xizang":{"name":"中国大陆_西藏","parent":"Xinan"},"Abroad":{"name":"全球","parent":null},"AP":{"name":"全球_亚太地区","parent":"Abroad"},"TW":{"name":"全球_中国台湾","parent":"AP"},"HK":{"name":"全球_中国香港","parent":"AP"},"MO":{"name":"全球_中国澳门","parent":"AP"},"JP":{"name":"全球_日本","parent":"AP"},"KR":{"name":"全球_韩国","parent":"AP"},"IN":{"name":"全球_印度","parent":"AP"},"TR":{"name":"全球_土耳其","parent":"AP"},"ID":{"name":"全球_印度尼西亚","parent":"AP"},"VN":{"name":"全球_越南","parent":"AP"},"SG":{"name":"全球_新加坡","parent":"AP"},"TH":{"name":"全球_泰国","parent":"AP"},"MY":{"name":"全球_马来西亚","parent":"AP"},"BD":{"name":"全球_孟加拉","parent":"AP"},"AE":{"name":"全球_阿联酋","parent":"AP"},"AM":{"name":"全球_亚美尼亚","parent":"AP"},"AZ":{"name":"全球_阿塞拜疆","parent":"AP"},"BH":{"name":"全球_巴林","parent":"AP"},"BN":{"name":"全球_文莱","parent":"AP"},"BT":{"name":"全球_不丹","parent":"AP"},"CX":{"name":"全球_圣诞岛","parent":"AP"},"GE":{"name":"全球_格鲁吉亚","parent":"AP"},"IQ":{"name":"全球_伊拉克","parent":"AP"},"JO":{"name":"全球_约旦","parent":"AP"},"KG":{"name":"全球_吉尔吉斯斯坦","parent":"AP"},"KH":{"name":"全球_柬埔寨","parent":"AP"},"KW":{"name":"全球_科威特","parent":"AP"},"KZ":{"name":"全球_哈萨克斯坦","parent":"AP"},"LB":{"name":"全球_黎巴嫩","parent":"AP"},"LK":{"name":"全球_斯里兰卡","parent":"AP"},"MM":{"name":"全球_缅甸","parent":"AP"},"MN":{"name":"全球_蒙古","parent":"AP"},"MV":{"name":"全球_马尔代夫","parent":"AP"},"NP":{"name":"全球_尼泊尔","parent":"AP"},"OM":{"name":"全球_阿曼","parent":"AP"},"PH":{"name":"全球_菲律宾","parent":"AP"},"PK":{"name":"全球_巴基斯坦","parent":"AP"},"PS":{"name":"全球_巴勒斯坦","parent":"AP"},"QA":{"name":"全球_卡塔尔","parent":"AP"},"SA":{"name":"全球_沙特阿拉伯","parent":"AP"},"TJ":{"name":"全球_塔吉克斯坦","parent":"AP"},"TL":{"name":"全球_东帝汶","parent":"AP"},"TM":{"name":"全球_土库曼斯坦","parent":"AP"},"UZ":{"name":"全球_乌兹别克斯坦","parent":"AP"},"YE":{"name":"全球_也门","parent":"AP"},"CY":{"name":"全球_塞浦路斯","parent":"AP"},"IL":{"name":"全球_以色列","parent":"AP"},"AS":{"name":"全球_美属萨摩亚","parent":"AP"},"CK":{"name":"全球_库克群岛","parent":"AP"},"FM":{"name":"全球_密克罗尼西亚","parent":"AP"},"GU":{"name":"全球_关岛","parent":"AP"},"KI":{"name":"全球_基里巴斯","parent":"AP"},"MH":{"name":"全球_马绍尔群岛","parent":"AP"},"MP":{"name":"全球_北马里亚纳群岛","parent":"AP"},"NC":{"name":"全球_新喀里多尼亚","parent":"AP"},"NF":{"name":"全球_诺福克岛","parent":"AP"},"NR":{"name":"全球_瑙鲁","parent":"AP"},"PF":{"name":"全球_法属波利尼西亚","parent":"AP"},"PG":{"name":"全球_巴布亚新几内亚","parent":"AP"},"PW":{"name":"全球_帕劳","parent":"AP"},"SB":{"name":"全球_所罗门群岛","parent":"AP"},"TK":{"name":"全球_托克劳群岛","parent":"AP"},"TO":{"name":"全球_汤加","parent":"AP"},"TV":{"name":"全球_图瓦卢","parent":"AP"},"VU":{"name":"全球_瓦努阿图","parent":"AP"},"WS":{"name":"全球_萨摩亚","parent":"AP"},"AFG":{"name":"全球_阿富汗","parent":"AP"},"LAO":{"name":"全球_老挝","parent":"AP"},"IR":{"name":"全球_伊朗","parent":"AP"},"SY":{"name":"全球_叙利亚","parent":"AP"},"OA":{"name":"全球_大洋洲","parent":"Abroad"},"AU":{"name":"全球_澳大利亚","parent":"OA"},"NZ":{"name":"全球_新西兰","parent":"OA"},"WF":{"name":"全球_瓦利斯和富图纳","parent":"OA"},"FJ":{"name":"全球_斐济群岛","parent":"OA"},"NU":{"name":"全球_纽埃","parent":"OA"},"EU":{"name":"全球_欧洲","parent":"Abroad"},"GB":{"name":"全球_英国","parent":"EU"},"DE":{"name":"全球_德国","parent":"EU"},"FR":{"name":"全球_法国","parent":"EU"},"IT":{"name":"全球_意大利","parent":"EU"},"RU":{"name":"全球_俄罗斯","parent":"EU"},"ES":{"name":"全球_西班牙","parent":"EU"},"UA":{"name":"全球_乌克兰","parent":"EU"},"NL":{"name":"全球_荷兰","parent":"EU"},"SE":{"name":"全球_瑞典","parent":"EU"},"PL":{"name":"全球_波兰","parent":"EU"},"IO":{"name":"全球_英属印度洋领地","parent":"EU"},"BY":{"name":"全球_白俄罗斯","parent":"EU"},"AD":{"name":"全球_安道尔","parent":"EU"},"AL":{"name":"全球_阿尔巴尼亚","parent":"EU"},"AT":{"name":"全球_奥地利","parent":"EU"},"AX":{"name":"全球_奥兰群岛","parent":"EU"},"BE":{"name":"全球_比利时","parent":"EU"},"BG":{"name":"全球_保加利亚","parent":"EU"},"CH":{"name":"全球_瑞士","parent":"EU"},"CZ":{"name":"全球_捷克","parent":"EU"},"DK":{"name":"全球_丹麦","parent":"EU"},"EE":{"name":"全球_爱沙尼亚","parent":"EU"},"FI":{"name":"全球_芬兰","parent":"EU"},"FO":{"name":"全球_法罗群岛","parent":"EU"},"GG":{"name":"全球_根西岛","parent":"EU"},"GI":{"name":"全球_直布罗陀","parent":"EU"},"GR":{"name":"全球_希腊","parent":"EU"},"HR":{"name":"全球_克罗地亚","parent":"EU"},"HU":{"name":"全球_匈牙利","parent":"EU"},"IE":{"name":"全球_爱尔兰","parent":"EU"},"IM":{"name":"全球_马恩岛","parent":"EU"},"IS":{"name":"全球_冰岛","parent":"EU"},"JE":{"name":"全球_泽西岛","parent":"EU"},"LI":{"name":"全球_列支敦士登","parent":"EU"},"LT":{"name":"全球_立陶宛","parent":"EU"},"LU":{"name":"全球_卢森堡","parent":"EU"},"LV":{"name":"全球_拉脱维亚","parent":"EU"},"MC":{"name":"全球_摩纳哥","parent":"EU"},"MD":{"name":"全球_摩尔多瓦","parent":"EU"},"ME":{"name":"全球_黑山","parent":"EU"},"MK":{"name":"全球_北马其顿","parent":"EU"},"MT":{"name":"全球_马耳他","parent":"EU"},"NO":{"name":"全球_挪威","parent":"EU"},"PT":{"name":"全球_葡萄牙","parent":"EU"},"RO":{"name":"全球_罗马尼亚","parent":"EU"},"RS":{"name":"全球_塞尔维亚","parent":"EU"},"SI":{"name":"全球_斯洛文尼亚","parent":"EU"},"SK":{"name":"全球_斯洛伐克","parent":"EU"},"SM":{"name":"全球_圣马力诺","parent":"EU"},"VA":{"name":"全球_梵蒂冈","parent":"EU"},"XK":{"name":"全球_科索沃","parent":"EU"},"SJ":{"name":"全球_斯瓦尔巴群岛和扬马延岛","parent":"EU"},"BQ":{"name":"全球_荷兰加勒比","parent":"EU"},"NA":{"name":"全球_北美洲","parent":"Abroad"},"US":{"name":"全球_美国","parent":"NA"},"CA":{"name":"全球_加拿大","parent":"NA"},"MX":{"name":"全球_墨西哥","parent":"NA"},"AG":{"name":"全球_安提瓜和巴布达","parent":"NA"},"BB":{"name":"全球_巴巴多斯","parent":"NA"},"BS":{"name":"全球_巴哈马","parent":"NA"},"BZ":{"name":"全球_伯利兹","parent":"NA"},"CR":{"name":"全球_哥斯达黎加","parent":"NA"},"DM":{"name":"全球_多米尼克","parent":"NA"},"DO":{"name":"全球_多米尼加","parent":"NA"},"GD":{"name":"全球_格林纳达","parent":"NA"},"GT":{"name":"全球_危地马拉","parent":"NA"},"HN":{"name":"全球_洪都拉斯","parent":"NA"},"HT":{"name":"全球_海地","parent":"NA"},"JM":{"name":"全球_牙买加","parent":"NA"},"KN":{"name":"全球_圣基茨和尼维斯","parent":"NA"},"KY":{"name":"全球_开曼群岛","parent":"NA"},"LC":{"name":"全球_圣卢西亚","parent":"NA"},"NI":{"name":"全球_尼加拉瓜","parent":"NA"},"PA":{"name":"全球_巴拿马","parent":"NA"},"PR":{"name":"全球_波多黎各","parent":"NA"},"SV":{"name":"全球_萨尔瓦多","parent":"NA"},"TC":{"name":"全球_特克斯和凯科斯群岛","parent":"NA"},"TT":{"name":"全球_特立尼达和多巴哥","parent":"NA"},"VG":{"name":"全球_英属维尔京群岛","parent":"NA"},"VI":{"name":"全球_美属维尔京群岛","parent":"NA"},"VC":{"name":"全球_圣文森特和格林纳丁斯","parent":"NA"},"MF":{"name":"全球_法属圣马丁","parent":"NA"},"PM":{"name":"全球_圣皮艾尔和密克隆群岛","parent":"NA"},"CU":{"name":"全球_古巴","parent":"NA"},"GL":{"name":"全球_格陵兰岛","parent":"NA"},"MQ":{"name":"全球_法属马提尼克群岛-马提尼克","parent":"NA"},"SX":{"name":"全球_荷属圣马丁","parent":"NA"},"LA":{"name":"全球_南美洲","parent":"Abroad"},"BR":{"name":"全球_巴西","parent":"LA"},"AR":{"name":"全球_阿根廷","parent":"LA"},"AI":{"name":"全球_安圭拉","parent":"LA"},"AW":{"name":"全球_阿鲁巴","parent":"LA"},"BL":{"name":"全球_圣巴泰勒米岛","parent":"LA"},"BM":{"name":"全球_百慕大","parent":"LA"},"GP":{"name":"全球_瓜德罗普","parent":"LA"},"MS":{"name":"全球_蒙特塞拉特岛","parent":"LA"},"BO":{"name":"全球_玻利维亚","parent":"LA"},"CL":{"name":"全球_智利","parent":"LA"},"CO":{"name":"全球_哥伦比亚","parent":"LA"},"CW":{"name":"全球_库拉索","parent":"LA"},"EC":{"name":"全球_厄瓜多尔","parent":"LA"},"GF":{"name":"全球_法属圭亚那","parent":"LA"},"GY":{"name":"全球_圭亚那","parent":"LA"},"PE":{"name":"全球_秘鲁","parent":"LA"},"PY":{"name":"全球_巴拉圭","parent":"LA"},"SR":{"name":"全球_苏里南","parent":"LA"},"UY":{"name":"全球_乌拉圭","parent":"LA"},"VE":{"name":"全球_委内瑞拉","parent":"LA"},"AF":{"name":"全球_非洲","parent":"Abroad"},"ZA":{"name":"全球_南非","parent":"AF"},"EG":{"name":"全球_埃及","parent":"AF"},"AO":{"name":"全球_安哥拉","parent":"AF"},"BF":{"name":"全球_布基纳法索","parent":"AF"},"BI":{"name":"全球_布隆迪","parent":"AF"},"BJ":{"name":"全球_贝宁","parent":"AF"},"BW":{"name":"全球_博茨瓦纳","parent":"AF"},"CD":{"name":"全球_刚果金","parent":"AF"},"CF":{"name":"全球_中非","parent":"AF"},"CG":{"name":"全球_刚果布","parent":"AF"},"CI":{"name":"全球_科特迪瓦","parent":"AF"},"CM":{"name":"全球_喀麦隆","parent":"AF"},"CV":{"name":"全球_佛得角","parent":"AF"},"DJ":{"name":"全球_吉布提","parent":"AF"},"DZ":{"name":"全球_阿尔及利亚","parent":"AF"},"ER":{"name":"全球_厄立特里亚","parent":"AF"},"ET":{"name":"全球_埃塞俄比亚","parent":"AF"},"GA":{"name":"全球_加蓬","parent":"AF"},"GH":{"name":"全球_加纳","parent":"AF"},"GM":{"name":"全球_冈比亚","parent":"AF"},"GN":{"name":"全球_几内亚","parent":"AF"},"GQ":{"name":"全球_赤道几内亚","parent":"AF"},"GW":{"name":"全球_几内亚比绍","parent":"AF"},"KE":{"name":"全球_肯尼亚","parent":"AF"},"KM":{"name":"全球_科摩罗","parent":"AF"},"LR":{"name":"全球_利比里亚","parent":"AF"},"LS":{"name":"全球_莱索托","parent":"AF"},"LY":{"name":"全球_利比亚","parent":"AF"},"MA":{"name":"全球_摩洛哥","parent":"AF"},"MG":{"name":"全球_马达加斯加","parent":"AF"},"ML":{"name":"全球_马里","parent":"AF"},"MR":{"name":"全球_毛里塔尼亚","parent":"AF"},"MU":{"name":"全球_毛里求斯","parent":"AF"},"MW":{"name":"全球_马拉维","parent":"AF"},"MZ":{"name":"全球_莫桑比克","parent":"AF"},"NE":{"name":"全球_尼日尔","parent":"AF"},"NG":{"name":"全球_尼日利亚","parent":"AF"},"RE":{"name":"全球_留尼汪","parent":"AF"},"RW":{"name":"全球_卢旺达","parent":"AF"},"SC":{"name":"全球_塞舌尔","parent":"AF"},"SL":{"name":"全球_塞拉利昂","parent":"AF"},"SN":{"name":"全球_塞内加尔","parent":"AF"},"SO":{"name":"全球_索马里","parent":"AF"},"SS":{"name":"全球_南苏丹","parent":"AF"},"ST":{"name":"全球_圣多美和普林西比","parent":"AF"},"SZ":{"name":"全球_斯威士兰","parent":"AF"},"TD":{"name":"全球_乍得","parent":"AF"},"TG":{"name":"全球_多哥","parent":"AF"},"TN":{"name":"全球_突尼斯","parent":"AF"},"TZ":{"name":"全球_坦桑尼亚","parent":"AF"},"UG":{"name":"全球_乌干达","parent":"AF"},"YT":{"name":"全球_马约特","parent":"AF"},"ZM":{"name":"全球_赞比亚","parent":"AF"},"ZW":{"name":"全球_津巴布韦","parent":"AF"},"NAM":{"name":"全球_纳米比亚","parent":"AF"},"SD":{"name":"全球_苏丹","parent":"AF"},"AQ":{"name":"全球_南极洲","parent":"AQ"}}
\ No newline at end of file
diff --git a/app/event.php b/app/event.php
new file mode 100644
index 0000000..e9851bb
--- /dev/null
+++ b/app/event.php
@@ -0,0 +1,17 @@
+ [
+ ],
+
+ 'listen' => [
+ 'AppInit' => [],
+ 'HttpRun' => [],
+ 'HttpEnd' => [],
+ 'LogLevel' => [],
+ 'LogWrite' => [],
+ ],
+
+ 'subscribe' => [
+ ],
+];
diff --git a/app/lib/DnsHelper.php b/app/lib/DnsHelper.php
new file mode 100644
index 0000000..48dd544
--- /dev/null
+++ b/app/lib/DnsHelper.php
@@ -0,0 +1,91 @@
+ [
+ 'name' => '阿里云',
+ 'config' => [
+ 'ak' => 'AccessKeyId',
+ 'sk' => 'AccessKeySecret'
+ ],
+ 'remark' => 1, //是否支持备注,1单独设置备注,2和记录一起设置
+ 'status' => true, //是否支持启用暂停
+ 'redirect' => true, //是否支持域名转发
+ 'log' => true, //是否支持查看日志
+ ],
+ 'dnspod' => [
+ 'name' => '腾讯云',
+ 'config' => [
+ 'ak' => 'SecretId',
+ 'sk' => 'SecretKey'
+ ],
+ 'remark' => 1,
+ 'status' => true,
+ 'redirect' => true,
+ 'log' => true,
+ ],
+ 'huawei' => [
+ 'name' => '华为云',
+ 'config' => [
+ 'ak' => 'AccessKeyId',
+ 'sk' => 'SecretAccessKey'
+ ],
+ 'remark' => 2,
+ 'status' => true,
+ 'redirect' => false,
+ 'log' => false,
+ ],
+ 'west' => [
+ 'name' => '西部数码',
+ 'config' => [
+ 'ak' => '用户名',
+ 'sk' => 'API密码'
+ ],
+ 'remark' => 0,
+ 'status' => false,
+ 'redirect' => false,
+ 'log' => false,
+ ],
+ 'cloudflare' => [
+ 'name' => 'Cloudflare',
+ 'config' => [
+ 'ak' => '邮箱地址',
+ 'sk' => 'API密钥'
+ ],
+ 'remark' => 2,
+ 'status' => false,
+ 'redirect' => false,
+ 'log' => false,
+ ],
+ ];
+
+ public static function getList()
+ {
+ return self::$dns_config;
+ }
+
+ private static function getConfig($aid){
+ $account = Db::name('account')->where('id', $aid)->find();
+ if(!$account) return false;
+ return $account;
+ }
+
+ public static function getModel($aid, $domain = null, $domainid = null)
+ {
+ $config = self::getConfig($aid);
+ if(!$config) return false;
+ $dnstype = $config['type'];
+ $class = "\\app\\lib\\dns\\{$dnstype}";
+ if(class_exists($class)){
+ $config['domain'] = $domain;
+ $config['domainid'] = $domainid;
+ $model = new $class($config);
+ return $model;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/app/lib/DnsInterface.php b/app/lib/DnsInterface.php
new file mode 100644
index 0000000..0cc915f
--- /dev/null
+++ b/app/lib/DnsInterface.php
@@ -0,0 +1,35 @@
+AccessKeyId = $config['ak'];
+ $this->AccessKeySecret = $config['sk'];
+ $this->domain = $config['domain'];
+ }
+
+ public function getError(){
+ return $this->error;
+ }
+
+ public function check(){
+ if($this->getDomainList() != false){
+ return true;
+ }
+ return false;
+ }
+
+ //获取域名列表
+ public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
+ $param = ['Action' => 'DescribeDomains', 'KeyWord' => $KeyWord, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize];
+ $data = $this->request($param, true);
+ if($data){
+ $list = [];
+ foreach($data['Domains']['Domain'] as $row){
+ $list[] = [
+ 'DomainId' => $row['DomainId'],
+ 'Domain' => $row['DomainName'],
+ 'RecordCount' => $row['RecordCount'],
+ ];
+ }
+ return ['total' => $data['TotalCount'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录列表
+ public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
+ $param = ['Action' => 'DescribeDomainRecords', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize];
+ if(!empty($SubDomain) || !empty($Type) || !empty($Line)){
+ $param += ['SearchMode' => 'ADVANCED', 'RRKeyWord' => $SubDomain, 'ValueKeyWord' => $KeyWord, 'Type' => $Type, 'Line' => $Line, 'ValueKeyWord' => $KeyWord];
+ }elseif(!empty($KeyWord)){
+ $param += ['KeyWord' => $KeyWord];
+ }
+ if(!isNullOrEmpty($Status)){
+ $Status = $Status == '1' ? 'Enable' : 'Disable';
+ $param += ['Status' => $Status];
+ }
+ $data = $this->request($param, true);
+ if($data){
+ $list = [];
+ foreach($data['DomainRecords']['Record'] as $row){
+ $list[] = [
+ 'RecordId' => $row['RecordId'],
+ 'Domain' => $row['DomainName'],
+ 'Name' => $row['RR'],
+ 'Type' => $row['Type'],
+ 'Value' => $row['Value'],
+ 'Line' => $row['Line'],
+ 'TTL' => $row['TTL'],
+ 'MX' => isset($row['Priority']) ? $row['Priority'] : null,
+ 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0',
+ 'Weight' => isset($row['Weight']) ? $row['Weight'] : null,
+ 'Remark' => isset($row['Remark']) ? $row['Remark'] : null,
+ 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $row['UpdateTimestamp']/1000) : null,
+ ];
+ }
+ return ['total' => $data['TotalCount'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取子域名解析记录列表
+ public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
+ $param = ['Action' => 'DescribeSubDomainRecords', 'SubDomain' => $SubDomain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'Type' => $Type, 'Line' => $Line];
+ $data = $this->request($param, true);
+ if($data){
+ $list = [];
+ foreach($data['DomainRecords']['Record'] as $row){
+ $list[] = [
+ 'RecordId' => $row['RecordId'],
+ 'Domain' => $row['DomainName'],
+ 'Name' => $row['RR'],
+ 'Type' => $row['Type'],
+ 'Value' => $row['Value'],
+ 'Line' => $row['Line'],
+ 'TTL' => $row['TTL'],
+ 'MX' => isset($row['Priority']) ? $row['Priority'] : null,
+ 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0',
+ 'Weight' => isset($row['Weight']) ? $row['Weight'] : null,
+ 'Remark' => isset($row['Remark']) ? $row['Remark'] : null,
+ 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $row['UpdateTimestamp']/1000) : null,
+ ];
+ }
+ return ['total' => $data['TotalCount'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录详细信息
+ public function getDomainRecordInfo($RecordId){
+ $param = ['Action' => 'DescribeDomainRecordInfo', 'RecordId' => $RecordId];
+ $data = $this->request($param, true);
+ if($data){
+ return [
+ 'RecordId' => $data['RecordId'],
+ 'Domain' => $data['DomainName'],
+ 'Name' => $data['RR'],
+ 'Type' => $data['Type'],
+ 'Value' => $data['Value'],
+ 'Line' => $data['Line'],
+ 'TTL' => $data['TTL'],
+ 'MX' => isset($data['Priority']) ? $data['Priority'] : null,
+ 'Status' => $data['Status'] == 'ENABLE' ? '1' : '0',
+ 'Weight' => isset($data['Weight']) ? $data['Weight'] : null,
+ 'Remark' => isset($data['Remark']) ? $data['Remark'] : null,
+ 'UpdateTime' => isset($row['UpdateTimestamp']) ? date('Y-m-d H:i:s', $data['UpdateTimestamp']/1000) : null,
+ ];
+ }
+ return false;
+ }
+
+ //添加解析记录
+ public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Remark = null){
+ $param = ['Action' => 'AddDomainRecord', 'DomainName' => $this->domain, 'RR' => $Name, 'Type' => $Type, 'Value' => $Value, 'Line' => $this->convertLineCode($Line), 'TTL' => intval($TTL)];
+ if($MX){
+ $params['Priority'] = intval($MX);
+ }
+ $data = $this->request($param, true);
+ if($data){
+ return $data['RecordId'];
+ }
+ return false;
+ }
+
+ //修改解析记录
+ public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = null, $Remark = null){
+ $param = ['Action' => 'UpdateDomainRecord', 'RecordId' => $RecordId, 'RR' => $Name, 'Type' => $Type, 'Value' => $Value, 'Line' => $this->convertLineCode($Line), 'TTL' => intval($TTL)];
+ if($MX){
+ $params['Priority'] = intval($MX);
+ }
+ return $this->request($param);
+ }
+
+ //修改解析记录备注
+ public function updateDomainRecordRemark($RecordId, $Remark){
+ $param = ['Action' => 'UpdateDomainRecordRemark', 'RecordId' => $RecordId, 'Remark' => $Remark];
+ return $this->request($param);
+ }
+
+ //删除解析记录
+ public function deleteDomainRecord($RecordId){
+ $param = ['Action' => 'DeleteDomainRecord', 'RecordId' => $RecordId];
+ return $this->request($param);
+ }
+
+ //删除子域名的解析记录
+ public function deleteSubDomainRecords($SubDomain){
+ $param = ['Action' => 'DeleteSubDomainRecords', 'DomainName' => $this->domain, 'RR' => $SubDomain];
+ return $this->request($param);
+ }
+
+ //设置解析记录状态
+ public function setDomainRecordStatus($RecordId, $Status){
+ $Status = $Status == '1' ? 'Enable' : 'Disable';
+ $param = ['Action' => 'SetDomainRecordStatus', 'RecordId' => $RecordId, 'Status' => $Status];
+ return $this->request($param);
+ }
+
+ //获取解析记录操作日志
+ public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){
+ $param = ['Action' => 'DescribeRecordLogs', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'KeyWord' => $KeyWord, 'StartDate' => $StartDate, 'endDate' => $endDate, 'Lang' => 'zh'];
+ $data = $this->request($param, true);
+ if($data){
+ $list = [];
+ foreach($data['RecordLogs']['RecordLog'] as $row){
+ $list[] = ['time'=>date('Y-m-d H:i:s', intval($row['ActionTimestamp']/1000)), 'data'=>$row['Message']];
+ }
+ return ['total' => $data['TotalCount'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析线路列表
+ public function getRecordLine(){
+ $data = $this->getDomainInfo();
+ if($data){
+ $list = [];
+ foreach($data['RecordLines']['RecordLine'] as $row){
+ $list[$row['LineCode']] = ['name'=>$row['LineDisplayName'], 'parent'=>isset($row['FatherCode']) ? $row['FatherCode'] : null];
+ }
+ return $list;
+ }
+ return false;
+ }
+
+ //获取域名信息
+ public function getDomainInfo(){
+ if(!empty($this->domainInfo)) return $this->domainInfo;
+ $param = ['Action' => 'DescribeDomainInfo', 'DomainName' => $this->domain, 'NeedDetailAttributes' => 'true'];
+ $data = $this->request($param, true);
+ if($data){
+ $this->domainInfo = $data;
+ return $data;
+ }
+ return false;
+ }
+
+ //获取域名最低TTL
+ public function getMinTTL(){
+ $data = $this->getDomainInfo();
+ if($data){
+ return $data['MinTtl'];
+ }
+ return false;
+ }
+
+ private function convertLineCode($line){
+ $convert_dict = ['0'=>'default', '10=1'=>'unicom', '10=0'=>'telecom', '10=3'=>'mobile', '10=2'=>'edu', '3=0'=>'oversea', '10=22'=>'btvn', '80=0'=>'search', '7=0'=>'internal'];
+ if(array_key_exists($line, $convert_dict)){
+ return $convert_dict[$line];
+ }
+ return $line;
+ }
+
+
+ private function aliyunSignature($parameters, $accessKeySecret, $method)
+ {
+ ksort($parameters);
+ $canonicalizedQueryString = '';
+ foreach ($parameters as $key => $value) {
+ if($value === null) continue;
+ $canonicalizedQueryString .= '&' . $this->percentEncode($key). '=' . $this->percentEncode($value);
+ }
+ $stringToSign = $method . '&%2F&' . $this->percentEncode(substr($canonicalizedQueryString, 1));
+ $signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret."&", true));
+
+ return $signature;
+ }
+ private function percentEncode($str)
+ {
+ $search = ['+', '*', '%7E'];
+ $replace = ['%20', '%2A', '~'];
+ return str_replace($search, $replace, urlencode($str));
+ }
+ private function request($param, $returnData=false){
+ if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false;
+ $result = $this->request_do($param, $returnData);
+ if(!$returnData && $result!==true){
+ usleep(50000);
+ $result = $this->request_do($param, $returnData);
+ }
+ return $result;
+ }
+ private function request_do($param, $returnData=false){
+ if(empty($this->AccessKeyId)||empty($this->AccessKeySecret))return false;
+ $url='https://'.$this->Endpoint.'/';
+ $data=array(
+ 'Format' => 'JSON',
+ 'Version' => $this->Version,
+ 'AccessKeyId' => $this->AccessKeyId,
+ 'SignatureMethod' => 'HMAC-SHA1',
+ 'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
+ 'SignatureVersion' => '1.0',
+ 'SignatureNonce' => random(8));
+ $data=array_merge($data, $param);
+ $data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, 'POST');
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
+ $response = curl_exec($ch);
+ $errno = curl_errno($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if ($errno) {
+ $this->setError('Curl error: ' . curl_error($ch));
+ }
+ curl_close($ch);
+ if ($errno) return false;
+
+ $arr = json_decode($response,true);
+ if($httpCode==200){
+ return $returnData ? $arr : true;
+ }elseif($arr){
+ $this->setError($arr['Message']);
+ return false;
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function setError($message){
+ $this->error = $message;
+ //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
+ }
+}
diff --git a/app/lib/dns/baidu.php b/app/lib/dns/baidu.php
new file mode 100644
index 0000000..aa16920
--- /dev/null
+++ b/app/lib/dns/baidu.php
@@ -0,0 +1,330 @@
+AccessKeyId = $config['ak'];
+ $this->SecretAccessKey = $config['sk'];
+ $this->domain = $config['domain'];
+ $this->domainid = $config['domainid'];
+ }
+
+ public function getError(){
+ return $this->error;
+ }
+
+ public function check(){
+ if($this->getDomainList() != false){
+ return true;
+ }
+ return false;
+ }
+
+ //获取域名列表
+ public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
+ $query = ['name' => $KeyWord];
+ $data = $this->send_reuqest('GET', '/v1/dns/zone', $query);
+ if($data){
+ $list = [];
+ foreach($data['zones'] as $row){
+ $list[] = [
+ 'DomainId' => $row['id'],
+ 'Domain' => rtrim($row['name'], '.'),
+ 'RecordCount' => 0,
+ ];
+ }
+ return ['total' => count($list), 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录列表
+ public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
+ $marker = cookie('baidu_record_marker');
+ $query = ['rr' => $KeyWord];
+ if(!isNullOrEmpty(($SubDomain))){
+ $param['rr'] = $SubDomain;
+ }
+ $data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query);
+ if($data){
+ $list = [];
+ foreach($data['records'] as $row){
+ $list[] = [
+ 'RecordId' => $row['id'],
+ 'Domain' => $this->domain,
+ 'Name' => $row['rr'],
+ 'Type' => $row['type'],
+ 'Value' => $row['value'],
+ 'Line' => $row['line'],
+ 'TTL' => $row['ttl'],
+ 'MX' => $row['priority'],
+ 'Status' => $row['status'] == 'running' ? '1' : '0',
+ 'Weight' => null,
+ 'Remark' => $row['description'],
+ 'UpdateTime' => null,
+ ];
+ }
+ return ['total' => count($list), 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取子域名解析记录列表
+ public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
+ $domain_arr = explode('.', $SubDomain);
+ $domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
+ $subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
+ if($subdomain == '')$subdomain='@';
+ return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
+ }
+
+ //获取解析记录详细信息
+ public function getDomainRecordInfo($RecordId){
+ $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
+ if($data){
+ return [
+ 'RecordId' => $data['id'],
+ 'Domain' => rtrim($data['zone_name'], '.'),
+ 'Name' => str_replace('.'.$data['zone_name'], '', $data['name']),
+ 'Type' => $data['type'],
+ 'Value' => $data['records'],
+ 'Line' => $data['line'],
+ 'TTL' => $data['ttl'],
+ 'MX' => $data['weight'],
+ 'Status' => $data['status'] == 'ACTIVE' ? '1' : '0',
+ 'Weight' => $data['weight'],
+ 'Remark' => $data['description'],
+ 'UpdateTime' => $data['updated_at'],
+ ];
+ }
+ return false;
+ }
+
+ //添加解析记录
+ public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark];
+ if($Type == 'MX')$param['priority'] = intval($MX);
+ $query = ['clientToken' => getSid()];
+ return $this->send_reuqest('POST', '/v1/dns/zone/'.$this->domain.'/record', $query, $params);
+ }
+
+ //修改解析记录
+ public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark];
+ if($Type == 'MX')$param['priority'] = intval($MX);
+ $query = ['clientToken' => getSid()];
+ return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query, $params);
+ }
+
+ //修改解析记录备注
+ public function updateDomainRecordRemark($RecordId, $Remark){
+ return false;
+ }
+
+ //删除解析记录
+ public function deleteDomainRecord($RecordId){
+ $query = ['clientToken' => getSid()];
+ return $this->send_reuqest('DELETE', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query);
+ }
+
+ //设置解析记录状态
+ public function setDomainRecordStatus($RecordId, $Status){
+ $Status = $Status == '1' ? 'enable' : 'disable';
+ $query = [$Status => '', 'clientToken' => getSid()];
+ return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query);
+ }
+
+ //获取解析记录操作日志
+ public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){
+ return false;
+ }
+
+ //获取解析线路列表
+ public function getRecordLine(){
+ return [
+ 'default'=>['name'=>'默认', 'parent'=>null],
+ 'ct'=>['name'=>'电信', 'parent'=>null],
+ 'cnc'=>['name'=>'联通', 'parent'=>null],
+ 'cmnet'=>['name'=>'移动', 'parent'=>null],
+ 'edu'=>['name'=>'教育网', 'parent'=>null],
+ 'search'=>['name'=>'搜索引擎(百度)', 'parent'=>null],
+ ];
+ }
+
+ //获取域名概览信息
+ public function getDomainInfo(){
+ $res = $this->getDomainList($this->domain);
+ if($res && !empty($res['list'])){
+ return $res['list'][0];
+ }
+ return false;
+ }
+
+ //获取域名最低TTL
+ public function getMinTTL(){
+ return false;
+ }
+
+ private function convertType($type){
+ return $type;
+ }
+
+ private function send_reuqest($method, $path, $query = null, $params = null){
+ if(!empty($query)){
+ $query = array_filter($query, function($a){ return $a!==null;});
+ }
+ if(!empty($params)){
+ $params = array_filter($params, function($a){ return $a!==null;});
+ }
+
+ $time = time();
+ $date = gmdate("Y-m-d\TH:i:s\Z", $time);
+ $body = !empty($params) ? json_encode($params) : '';
+ $headers = [
+ 'Host' => $this->endpoint,
+ 'x-bce-date' => $date,
+ ];
+ if($body){
+ $headers['Content-Type'] = 'application/json';
+ }
+
+ $authorization = $this->generateSign($method, $path, $query, $headers, $time);
+ $headers['Authorization'] = $authorization;
+
+ $url = 'https://'.$this->endpoint.$path;
+ if(!empty($query)){
+ $url .= '?'.http_build_query($query);
+ }
+ $header = [];
+ foreach($headers as $key => $value){
+ $header[] = $key.': '.$value;
+ }
+ return $this->curl($method, $url, $body, $header);
+ }
+
+ private function generateSign($method, $path, $query, $headers, $time){
+ $algorithm = "bce-auth-v1";
+
+ // step 1: build canonical request string
+ $httpRequestMethod = $method;
+ $canonicalUri = $this->getCanonicalUri($path);
+ $canonicalQueryString = $this->getCanonicalQueryString($query);
+ [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
+ $canonicalRequest = $httpRequestMethod."\n"
+ .$canonicalUri."\n"
+ .$canonicalQueryString."\n"
+ .$canonicalHeaders;
+
+ // step 2: calculate signing key
+ $date = gmdate("Y-m-d\TH:i:s\Z", $time);
+ $expirationInSeconds = 1800;
+ $authString = $algorithm . '/' . $this->AccessKeyId . '/' . $date . '/' . $expirationInSeconds;
+ $signingKey = hash_hmac('sha256', $authString, $this->SecretAccessKey);
+
+ // step 3: sign string
+ $signature = hash_hmac("sha256", $canonicalRequest, $signingKey);
+
+ // step 4: build authorization
+ $authorization = $authString . '/' . $signedHeaders . "/" . $signature;
+
+ return $authorization;
+ }
+
+ private function escape($str)
+ {
+ $search = ['+', '*', '%7E'];
+ $replace = ['%20', '%2A', '~'];
+ return str_replace($search, $replace, urlencode($str));
+ }
+
+ private function getCanonicalUri($path)
+ {
+ if(empty($path)) return '/';
+ $uri = str_replace('%2F', '/', $this->escape($path));
+ if(substr($uri, 0, 1) !== '/') $uri = '/'.$uri;
+ return $uri;
+ }
+
+ private function getCanonicalQueryString($parameters)
+ {
+ if(empty($parameters)) return '';
+ ksort($parameters);
+ $canonicalQueryString = '';
+ foreach ($parameters as $key => $value) {
+ if($key == 'authorization') continue;
+ $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value);
+ }
+ return substr($canonicalQueryString, 1);
+ }
+
+ private function getCanonicalHeaders($oldheaders){
+ $headers = array();
+ foreach ($oldheaders as $key => $value) {
+ $headers[strtolower($key)] = trim($value);
+ }
+ ksort($headers);
+
+ $canonicalHeaders = '';
+ $signedHeaders = '';
+ foreach ($headers as $key => $value) {
+ $canonicalHeaders .= $this->escape($key) . ':' . $this->escape($value) . "\n";
+ $signedHeaders .= $key . ';';
+ }
+ $canonicalHeaders = substr($canonicalHeaders, 0, -1);
+ $signedHeaders = substr($signedHeaders, 0, -1);
+ return [$canonicalHeaders, $signedHeaders];
+ }
+
+ private function curl($method, $url, $body, $header){
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+ if(!empty($body)){
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ }
+ $response = curl_exec($ch);
+ $errno = curl_errno($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if ($errno) {
+ $this->setError('Curl error: ' . curl_error($ch));
+ }
+ curl_close($ch);
+ if ($errno) return false;
+
+ if(empty($response) && $httpCode == 200){
+ return true;
+ }
+ $arr=json_decode($response,true);
+ if($arr){
+ if(isset($arr['code']) && isset($arr['message'])){
+ $this->setError($arr['message']);
+ return false;
+ }else{
+ return $arr;
+ }
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function setError($message){
+ $this->error = $message;
+ //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
+ }
+}
\ No newline at end of file
diff --git a/app/lib/dns/cloudflare.php b/app/lib/dns/cloudflare.php
new file mode 100644
index 0000000..b9dc942
--- /dev/null
+++ b/app/lib/dns/cloudflare.php
@@ -0,0 +1,237 @@
+Email = $config['ak'];
+ $this->ApiKey = $config['sk'];
+ $this->domain = $config['domain'];
+ $this->domainid = $config['domainid'];
+ }
+
+ public function getError(){
+ return $this->error;
+ }
+
+ public function check(){
+ if($this->getDomainList() != false){
+ return true;
+ }
+ return false;
+ }
+
+ //获取域名列表
+ public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
+ $param = ['page' => $PageNumber, 'per_page' => $PageSize, 'name' => $KeyWord];
+ $data = $this->send_reuqest('GET', '/zones', $param);
+ if($data){
+ $list = [];
+ foreach($data['result'] as $row){
+ $list[] = [
+ 'DomainId' => $row['id'],
+ 'Domain' => $row['name'],
+ 'RecordCount' => 0,
+ ];
+ }
+ return ['total' => $data['result_info']['total_count'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录列表
+ public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
+ $param = ['name' => $SubDomain, 'type' => $Type, 'search' => $KeyWord, 'page' => $PageNumber, 'per_page' => $PageSize];
+ if(!isNullOrEmpty($Line)){
+ $param['proxied'] = $Line == '1' ? 'true' : 'false';
+ }
+ $data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records', $param);
+ if($data){
+ $list = [];
+ foreach($data['result'] as $row){
+ $list[] = [
+ 'RecordId' => $row['id'],
+ 'Domain' => $row['zone_name'],
+ 'Name' => str_replace('.'.$row['zone_name'], '', $row['name']),
+ 'Type' => $row['type'],
+ 'Value' => $row['content'],
+ 'Line' => $row['proxied'] ? '1' : '0',
+ 'TTL' => $row['ttl'],
+ 'MX' => isset($row['priority']) ? $row['priority'] : null,
+ 'Status' => $row['locked'] ? '0' : '1',
+ 'Weight' => null,
+ 'Remark' => $row['comment'],
+ 'UpdateTime' => $row['modified_on'],
+ ];
+ }
+ return ['total' => $data['result_info']['total_count'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取子域名解析记录列表
+ public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
+ $domain_arr = explode('.', $SubDomain);
+ $domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
+ $subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
+ if($subdomain == '')$subdomain='@';
+ return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
+ }
+
+ //获取解析记录详细信息
+ public function getDomainRecordInfo($RecordId){
+ $data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
+ if($data){
+ return [
+ 'RecordId' => $data['result']['id'],
+ 'Domain' => $data['result']['zone_name'],
+ 'Name' => str_replace('.'.$data['result']['zone_name'], '', $data['result']['name']),
+ 'Type' => $data['result']['type'],
+ 'Value' => $data['result']['content'],
+ 'Line' => $data['result']['proxied'] ? '1' : '0',
+ 'TTL' => $data['result']['ttl'],
+ 'MX' => isset($data['result']['priority']) ? $data['result']['priority'] : null,
+ 'Status' => $data['result']['locked'] ? '0' : '1',
+ 'Weight' => null,
+ 'Remark' => $data['result']['comment'],
+ 'UpdateTime' => $data['result']['modified_on'],
+ ];
+ }
+ return false;
+ }
+
+ //添加解析记录
+ public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $param = ['name' => $Name, 'type' => $this->convertType($Type), 'content' => $Value, 'proxied' => $Line=='1', 'ttl' => intval($TTL), 'comment' => $Remark];
+ if($Type == 'MX')$param['priority'] = intval($MX);
+ $data = $this->send_reuqest('POST', '/zones/'.$this->domainid.'/dns_records', $param);
+ return is_array($data) ? $data['result']['id'] : false;
+ }
+
+ //修改解析记录
+ public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $param = ['name' => $Name, 'type' => $this->convertType($Type), 'content' => $Value, 'proxied' => $Line=='1', 'ttl' => intval($TTL), 'comment' => $Remark];
+ if($Type == 'MX')$param['priority'] = intval($MX);
+ $data = $this->send_reuqest('PATCH', '/zones/'.$this->domainid.'/dns_records/'.$RecordId, $param);
+ return is_array($data);
+ }
+
+ //修改解析记录备注
+ public function updateDomainRecordRemark($RecordId, $Remark){
+ return false;
+ }
+
+ //删除解析记录
+ public function deleteDomainRecord($RecordId){
+ $data = $this->send_reuqest('DELETE', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
+ return is_array($data);
+ }
+
+ //设置解析记录状态
+ public function setDomainRecordStatus($RecordId, $Status){
+ return false;
+ }
+
+ //获取解析记录操作日志
+ public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){
+ return false;
+ }
+
+ //获取解析线路列表
+ public function getRecordLine(){
+ return ['0'=>['name'=>'仅DNS', 'parent'=>null], '1'=>['name'=>'已代理', 'parent'=>null]];
+ }
+
+ //获取域名信息
+ public function getDomainInfo(){
+ $data = $this->send_reuqest('GET', '/zones/'.$this->domainid);
+ if($data){
+ return $data['result'];
+ }
+ return false;
+ }
+
+ //获取域名最低TTL
+ public function getMinTTL(){
+ return false;
+ }
+
+ private function convertType($type){
+ $convert_dict = ['REDIRECT_URL'=>'URI', 'FORWARD_URL'=>'URI'];
+ if(array_key_exists($type, $convert_dict)){
+ return $convert_dict[$type];
+ }
+ return $type;
+ }
+
+ private function send_reuqest($method, $path, $params = null){
+ $url = $this->baseUrl . $path;
+
+ $headers = [
+ 'X-Auth-Email: '.$this->Email,
+ 'X-Auth-Key: '.$this->ApiKey,
+ ];
+
+ $body = '';
+ if ($method == 'GET' || $method == 'DELETE') {
+ if ($params) {
+ $url .= '?' . http_build_query($params);
+ }
+ } else {
+ $body = json_encode($params);
+ $headers[] = 'Content-Type: application/json';
+ }
+
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ if ($method == 'POST') {
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ } elseif ($method == 'PUT') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ } elseif ($method == 'PATCH') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ } elseif ($method == 'DELETE') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
+ }
+ $response = curl_exec($ch);
+ $errno = curl_errno($ch);
+ if ($errno) {
+ $this->setError('Curl error: ' . curl_error($ch));
+ }
+ curl_close($ch);
+ if ($errno) return false;
+
+ $arr=json_decode($response,true);
+ if($arr){
+ if($arr['success']){
+ return $arr;
+ }else{
+ $this->setError(isset($arr['errors'][0])?$arr['errors'][0]['message']:'未知错误');
+ return false;
+ }
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function setError($message){
+ $this->error = $message;
+ //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
+ }
+}
\ No newline at end of file
diff --git a/app/lib/dns/dnspod.php b/app/lib/dns/dnspod.php
new file mode 100644
index 0000000..02f9c2e
--- /dev/null
+++ b/app/lib/dns/dnspod.php
@@ -0,0 +1,362 @@
+SecretId = $config['ak'];
+ $this->SecretKey = $config['sk'];
+ $this->domain = $config['domain'];
+ }
+
+ public function getError(){
+ return $this->error;
+ }
+
+ public function check(){
+ if($this->getAccountInfo() != false){
+ return true;
+ }
+ return false;
+ }
+
+ //获取域名列表
+ public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
+ $action = 'DescribeDomainList';
+ $offset = ($PageNumber-1)*$PageSize;
+ $param = ['Offset' => $offset, 'Limit' => $PageSize, 'Keyword' => $KeyWord];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ $list = [];
+ foreach($data['DomainList'] as $row){
+ $list[] = [
+ 'DomainId' => $row['DomainId'],
+ 'Domain' => $row['Name'],
+ 'RecordCount' => $row['RecordCount'],
+ ];
+ }
+ return ['total' => $data['DomainCountInfo']['DomainTotal'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录列表
+ public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
+ $offset = ($PageNumber-1)*$PageSize;
+ if(!isNullOrEmpty($Status)){
+ $action = 'DescribeRecordFilterList';
+ $Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
+ $param = ['Domain' => $this->domain, 'Subdomain' => $SubDomain, 'Keyword' => $KeyWord, 'Offset' => $offset, 'Limit' => $PageSize, 'RecordStatus' => [$Status]];
+ if(!isNullOrEmpty($Type)) $param['RecordType'] = [$Type];
+ if(!isNullOrEmpty($Line)) $param['RecordLine'] = [$Line];
+ }else{
+ $action = 'DescribeRecordList';
+ $param = ['Domain' => $this->domain, 'Subdomain' => $SubDomain, 'RecordType' => $Type, 'RecordLineId' => $Line, 'Keyword' => $KeyWord, 'Offset' => $offset, 'Limit' => $PageSize];
+ }
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ $list = [];
+ foreach($data['RecordList'] as $row){
+ //if($row['Name'] == '@' && $row['Type'] == 'NS') continue;
+ $list[] = [
+ 'RecordId' => $row['RecordId'],
+ 'Domain' => $this->domain,
+ 'Name' => $row['Name'],
+ 'Type' => $row['Type'],
+ 'Value' => $row['Value'],
+ 'Line' => $row['LineId'],
+ 'TTL' => $row['TTL'],
+ 'MX' => $row['MX'],
+ 'Status' => $row['Status'] == 'ENABLE' ? '1' : '0',
+ 'Weight' => $row['Weight'],
+ 'Remark' => $row['Remark'],
+ 'UpdateTime' => $row['UpdatedOn'],
+ ];
+ }
+ return ['total' => $data['RecordCountInfo']['TotalCount'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取子域名解析记录列表
+ public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
+ $domain_arr = explode('.', $SubDomain);
+ $domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
+ $subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
+ if($subdomain == '')$subdomain='@';
+ return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
+ }
+
+ //获取解析记录详细信息
+ public function getDomainRecordInfo($RecordId){
+ $action = 'DescribeRecord';
+ $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ return [
+ 'RecordId' => $data['RecordInfo']['Id'],
+ 'Domain' => $this->domain,
+ 'Name' => $data['RecordInfo']['SubDomain'],
+ 'Type' => $data['RecordInfo']['RecordType'],
+ 'Value' => $data['RecordInfo']['Value'],
+ 'Line' => $data['RecordInfo']['RecordLineId'],
+ 'TTL' => $data['RecordInfo']['TTL'],
+ 'MX' => $data['RecordInfo']['MX'],
+ 'Status' => $data['RecordInfo']['Enabled'] == 1 ? '1' : '0',
+ 'Weight' => $data['RecordInfo']['Weight'],
+ 'Remark' => $data['RecordInfo']['Remark'],
+ 'UpdateTime' => $data['RecordInfo']['UpdatedOn'],
+ ];
+ }
+ return false;
+ }
+
+ //添加解析记录
+ public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $action = 'CreateRecord';
+ $param = ['Domain' => $this->domain, 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine'=>$Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL)];
+ if($Type == 'MX')$param['MX'] = intval($MX);
+ $data = $this->send_reuqest($action, $param);
+ return is_array($data) ? $data['RecordId'] : false;
+ }
+
+ //修改解析记录
+ public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $action = 'ModifyRecord';
+ $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine'=>$Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL)];
+ if($Type == 'MX')$param['MX'] = intval($MX);
+ $data = $this->send_reuqest($action, $param);
+ return is_array($data);
+ }
+
+ //修改解析记录备注
+ public function updateDomainRecordRemark($RecordId, $Remark){
+ $action = 'ModifyRecordRemark';
+ $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Remark' => $Remark];
+ $data = $this->send_reuqest($action, $param);
+ return is_array($data);
+ }
+
+ //删除解析记录
+ public function deleteDomainRecord($RecordId){
+ $action = 'DeleteRecord';
+ $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)];
+ $data = $this->send_reuqest($action, $param);
+ return is_array($data);
+ }
+
+ //设置解析记录状态
+ public function setDomainRecordStatus($RecordId, $Status){
+ $Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
+ $action = 'ModifyRecordStatus';
+ $param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Status' => $Status];
+ $data = $this->send_reuqest($action, $param);
+ return is_array($data);
+ }
+
+ //获取解析记录操作日志
+ public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){
+ $action = 'DescribeDomainLogList';
+ $offset = ($PageNumber-1)*$PageSize;
+ $param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ $list = [];
+ foreach($data['LogList'] as $row){
+ $list[] = ['time'=>substr($row, 0, strpos($row,'(')), 'ip'=>substr($row, strpos($row,'(')+1, strpos($row,')')-strpos($row,'(')-1), 'data'=>substr($row, strpos($row,')')+1, strpos($row,' Uin:')-strpos($row,')')-1)];
+ }
+ return ['total' => $data['TotalCount'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析线路列表
+ public function getRecordLine(){
+ $action = 'DescribeRecordLineCategoryList';
+ $param = ['Domain' => $this->domain];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ $list = [];
+ $this->processLineList($list, $data['LineList'], null);
+ return $list;
+ }
+ return false;
+ }
+
+ private function processLineList(&$list, $line_list, $parent){
+ foreach($line_list as $row){
+ if(!isNullOrEmpty($row['LineId']) && $row['Useful'] && !isset($list[$row['LineId']])){
+ $list[$row['LineId']] = ['name'=>$row['LineName'], 'parent'=>$parent];
+ if($row['SubGroup']){
+ $this->processLineList($list, $row['SubGroup'], $row['LineId']);
+ }
+ }
+ }
+ }
+
+ //获取域名概览信息
+ public function getDomainInfo(){
+ $action = 'DescribeDomainPreview';
+ $param = ['Domain' => $this->domain];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ return $data['Domain'];
+ }
+ return false;
+ }
+
+ //获取域名权限
+ public function getDomainPurview(){
+ $action = 'DescribeDomainPurview';
+ $param = ['Domain' => $this->domain];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ return $data['PurviewList'];
+ }
+ return false;
+ }
+
+ //获取域名最低TTL
+ public function getMinTTL(){
+ $PurviewList = $this->getDomainPurview();
+ if($PurviewList){
+ foreach($PurviewList as $row){
+ if($row['Name'] == '记录 TTL 最低'){
+ return intval($row['Value']);
+ }
+ }
+ }
+ return false;
+ }
+
+ //获取用户信息
+ public function getAccountInfo(){
+ $action = 'DescribeUserDetail';
+ $param = [];
+ $data = $this->send_reuqest($action, $param);
+ if($data){
+ return $data['UserInfo'];
+ }
+ return false;
+ }
+
+ private function convertLineCode($line){
+ $convert_dict = ['default'=>'0', 'unicom'=>'10=1', 'telecom'=>'10=0', 'mobile'=>'10=3', 'edu'=>'10=2', 'oversea'=>'3=0', 'btvn'=>'10=22', 'search'=>'80=0', 'internal'=>'7=0'];
+ if(array_key_exists($line, $convert_dict)){
+ return $convert_dict[$line];
+ }
+ return $line;
+ }
+
+ private function convertType($type){
+ $convert_dict = ['REDIRECT_URL'=>'显性URL', 'FORWARD_URL'=>'隐性URL'];
+ if(array_key_exists($type, $convert_dict)){
+ return $convert_dict[$type];
+ }
+ return $type;
+ }
+
+
+ private function send_reuqest($action, $param){
+ $param = array_filter($param, function($a){ return $a!==null;});
+ if(!$param) $param = (object)[];
+ $payload = json_encode($param);
+ $time = time();
+ $authorization = $this->generateSign($payload, $time);
+ $header = [
+ 'Authorization: '.$authorization,
+ 'Content-Type: application/json; charset=utf-8',
+ 'X-TC-Action: '.$action,
+ 'X-TC-Timestamp: '.$time,
+ 'X-TC-Version: '.$this->version,
+ ];
+ return $this->curl_post($payload, $header);
+ }
+
+ private function generateSign($payload, $time){
+ $algorithm = "TC3-HMAC-SHA256";
+
+ // step 1: build canonical request string
+ $httpRequestMethod = "POST";
+ $canonicalUri = "/";
+ $canonicalQueryString = "";
+ $canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n";
+ $signedHeaders = "content-type;host";
+ $hashedRequestPayload = hash("SHA256", $payload);
+ $canonicalRequest = $httpRequestMethod."\n"
+ .$canonicalUri."\n"
+ .$canonicalQueryString."\n"
+ .$canonicalHeaders."\n"
+ .$signedHeaders."\n"
+ .$hashedRequestPayload;
+
+ // step 2: build string to sign
+ $date = gmdate("Y-m-d", $time);
+ $credentialScope = $date."/".$this->service."/tc3_request";
+ $hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
+ $stringToSign = $algorithm."\n"
+ .$time."\n"
+ .$credentialScope."\n"
+ .$hashedCanonicalRequest;
+
+ // step 3: sign string
+ $secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true);
+ $secretService = hash_hmac("SHA256", $this->service, $secretDate, true);
+ $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
+ $signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
+
+ // step 4: build authorization
+ $authorization = $algorithm
+ ." Credential=".$this->SecretId."/".$credentialScope
+ .", SignedHeaders=content-type;host, Signature=".$signature;
+
+ return $authorization;
+ }
+
+ private function curl_post($payload, $header){
+ $url = 'https://'.$this->endpoint.'/';
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_POST, 1);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
+ $response = curl_exec($ch);
+ $errno = curl_errno($ch);
+ if ($errno) {
+ $this->setError('Curl error: ' . curl_error($ch));
+ }
+ curl_close($ch);
+ if ($errno) return false;
+
+ $arr=json_decode($response,true);
+ if($arr){
+ if(isset($arr['Response']['Error'])){
+ $this->setError($arr['Response']['Error']['Message']);
+ return false;
+ }else{
+ return $arr['Response'];
+ }
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function setError($message){
+ $this->error = $message;
+ //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
+ }
+}
\ No newline at end of file
diff --git a/app/lib/dns/huawei.php b/app/lib/dns/huawei.php
new file mode 100644
index 0000000..9565d3f
--- /dev/null
+++ b/app/lib/dns/huawei.php
@@ -0,0 +1,345 @@
+AccessKeyId = $config['ak'];
+ $this->SecretAccessKey = $config['sk'];
+ $this->domain = $config['domain'];
+ $this->domainid = $config['domainid'];
+ }
+
+ public function getError(){
+ return $this->error;
+ }
+
+ public function check(){
+ if($this->getDomainList() != false){
+ return true;
+ }
+ return false;
+ }
+
+ //获取域名列表
+ public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
+ $offset = ($PageNumber-1)*$PageSize;
+ $query = ['offset' => $offset, 'limit' => $PageSize, 'name' => $KeyWord];
+ $data = $this->send_reuqest('GET', '/v2/zones', $query);
+ if($data){
+ $list = [];
+ foreach($data['zones'] as $row){
+ $list[] = [
+ 'DomainId' => $row['id'],
+ 'Domain' => rtrim($row['name'], '.'),
+ 'RecordCount' => $row['record_num'],
+ ];
+ }
+ return ['total' => $data['metadata']['total_count'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录列表
+ public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
+ $offset = ($PageNumber-1)*$PageSize;
+ $query = ['type' => $Type, 'line_id' => $Line, 'name' => $KeyWord, 'status' => $Status, 'offset' => $offset, 'limit' => $PageSize];
+ if(!isNullOrEmpty(($SubDomain))){
+ $param['name'] = $SubDomain;
+ }
+ $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets', $query);
+ if($data){
+ $list = [];
+ foreach($data['recordsets'] as $row){
+ if($row['name'] == $row['zone_name']) $row['name'] = '@';
+ $list[] = [
+ 'RecordId' => $row['id'],
+ 'Domain' => rtrim($row['zone_name'], '.'),
+ 'Name' => str_replace('.'.$row['zone_name'], '', $row['name']),
+ 'Type' => $row['type'],
+ 'Value' => $row['records'],
+ 'Line' => $row['line'],
+ 'TTL' => $row['ttl'],
+ 'MX' => $row['weight'],
+ 'Status' => $row['status'] == 'ACTIVE' ? '1' : '0',
+ 'Weight' => $row['weight'],
+ 'Remark' => $row['description'],
+ 'UpdateTime' => $row['updated_at'],
+ ];
+ }
+ return ['total' => $data['metadata']['total_count'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取子域名解析记录列表
+ public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
+ $domain_arr = explode('.', $SubDomain);
+ $domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
+ $subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
+ if($subdomain == '')$subdomain='@';
+ return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
+ }
+
+ //获取解析记录详细信息
+ public function getDomainRecordInfo($RecordId){
+ $data = $this->send_reuqest('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
+ if($data){
+ return [
+ 'RecordId' => $data['id'],
+ 'Domain' => rtrim($data['zone_name'], '.'),
+ 'Name' => str_replace('.'.$data['zone_name'], '', $data['name']),
+ 'Type' => $data['type'],
+ 'Value' => $data['records'],
+ 'Line' => $data['line'],
+ 'TTL' => $data['ttl'],
+ 'MX' => $data['weight'],
+ 'Status' => $data['status'] == 'ACTIVE' ? '1' : '0',
+ 'Weight' => $data['weight'],
+ 'Remark' => $data['description'],
+ 'UpdateTime' => $data['updated_at'],
+ ];
+ }
+ return false;
+ }
+
+ //添加解析记录
+ public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ if($Name == '@') $Name = '';
+ else $Name .= '.';
+ $Name .= $this->domain . '.';
+ $params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => [$Value], 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark];
+ if($Type == 'MX')$param['weight'] = intval($MX);
+ $data = $this->send_reuqest('POST', '/v2.1/zones/'.$this->domainid.'/recordsets', null, $params);
+ return is_array($data) ? $data['id'] : false;
+ }
+
+ //修改解析记录
+ public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ if($Name == '@') $Name = '';
+ else $Name .= '.';
+ $Name .= $this->domain . '.';
+ $params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => [$Value], 'line'=>$Line, 'ttl' => intval($TTL), 'description' => $Remark];
+ if($Type == 'MX')$param['weight'] = intval($MX);
+ $data = $this->send_reuqest('PUT', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId, null, $params);
+ return is_array($data);
+ }
+
+ //修改解析记录备注
+ public function updateDomainRecordRemark($RecordId, $Remark){
+ return false;
+ }
+
+ //删除解析记录
+ public function deleteDomainRecord($RecordId){
+ $data = $this->send_reuqest('DELETE', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
+ return is_array($data);
+ }
+
+ //设置解析记录状态
+ public function setDomainRecordStatus($RecordId, $Status){
+ $Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
+ $params = ['status' => $Status];
+ $data = $this->send_reuqest('PUT', '/v2.1/recordsets/'.$RecordId.'/statuses/set', null, $params);
+ return is_array($data);
+ }
+
+ //获取解析记录操作日志
+ public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){
+ return false;
+ }
+
+ //获取解析线路列表
+ public function getRecordLine(){
+ $file_path = app()->getBasePath().'data'.DIRECTORY_SEPARATOR.'huawei_line.json';
+ $content = file_get_contents($file_path);
+ $data = json_decode($content, true);
+ if($data){
+ return $data;
+ $list = [$data['DEFAULT']['id'] => ['name'=>$data['DEFAULT']['zh'], 'parent'=>null]];
+ $this->processLineList($list, $data['ISP'], null, 1, 1);
+ $this->processLineList($list, $data['REGION'], null, null, 1);
+ //file_put_contents($file_path, json_encode($list, JSON_UNESCAPED_UNICODE));
+ return $list;
+ }
+ return false;
+ }
+
+ private function processLineList(&$list, $line_list, $parent, $rootId = null, $rootName = null){
+ foreach($line_list as $row){
+ if($rootId && $rootId!==1){
+ $row['id'] = $rootId.'_'.$row['id'];
+ }
+ if($rootName && $rootName!==1){
+ $row['zh'] = $rootName.'_'.$row['zh'];
+ }
+ $list[$row['id']] = ['name'=>$row['zh'], 'parent'=>$parent];
+ if(isset($row['children']) && !empty($row['children'])){
+ $this->processLineList($list, $row['children'], $row['id'], $rootId === 1 ? $row['id'] : $rootId, $rootName === 1 ? $row['zh'] : $rootName);
+ }
+ }
+ }
+
+ //获取域名概览信息
+ public function getDomainInfo(){
+ return $this->send_reuqest('GET', '/v2/zones/'.$this->domainid);
+ }
+
+ //获取域名最低TTL
+ public function getMinTTL(){
+ return false;
+ }
+
+ private function convertType($type){
+ return $type;
+ }
+
+ private function send_reuqest($method, $path, $query = null, $params = null){
+ if(!empty($query)){
+ $query = array_filter($query, function($a){ return $a!==null;});
+ }
+ if(!empty($params)){
+ $params = array_filter($params, function($a){ return $a!==null;});
+ }
+
+ $time = time();
+ $date = gmdate("Ymd\THis\Z", $time);
+ $body = !empty($params) ? json_encode($params) : '';
+ $headers = [
+ 'Host' => $this->endpoint,
+ 'X-Sdk-Date' => $date,
+ ];
+ if($body){
+ $headers['Content-Type'] = 'application/json';
+ }
+
+ $authorization = $this->generateSign($method, $path, $query, $headers, $body, $time);
+ $headers['Authorization'] = $authorization;
+
+ $url = 'https://'.$this->endpoint.$path;
+ if(!empty($query)){
+ $url .= '?'.http_build_query($query);
+ }
+ $header = [];
+ foreach($headers as $key => $value){
+ $header[] = $key.': '.$value;
+ }
+ return $this->curl($method, $url, $body, $header);
+ }
+
+ private function generateSign($method, $path, $query, $headers, $body, $time){
+ $algorithm = "SDK-HMAC-SHA256";
+
+ // step 1: build canonical request string
+ $httpRequestMethod = $method;
+ $canonicalUri = $path;
+ if(substr($canonicalUri, -1) != "/") $canonicalUri .= "/";
+ $canonicalQueryString = $this->getCanonicalQueryString($query);
+ [$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
+ $hashedRequestPayload = hash("sha256", $body);
+ $canonicalRequest = $httpRequestMethod."\n"
+ .$canonicalUri."\n"
+ .$canonicalQueryString."\n"
+ .$canonicalHeaders."\n"
+ .$signedHeaders."\n"
+ .$hashedRequestPayload;
+
+ // step 2: build string to sign
+ $date = gmdate("Ymd\THis\Z", $time);
+ $hashedCanonicalRequest = hash("sha256", $canonicalRequest);
+ $stringToSign = $algorithm."\n"
+ .$date."\n"
+ .$hashedCanonicalRequest;
+
+ // step 3: sign string
+ $signature = hash_hmac("sha256", $stringToSign, $this->SecretAccessKey);
+
+ // step 4: build authorization
+ $authorization = $algorithm . ' Access=' . $this->AccessKeyId . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature;
+
+ return $authorization;
+ }
+
+ private function escape($str)
+ {
+ $search = ['+', '*', '%7E'];
+ $replace = ['%20', '%2A', '~'];
+ return str_replace($search, $replace, urlencode($str));
+ }
+
+ private function getCanonicalQueryString($parameters)
+ {
+ if(empty($parameters)) return '';
+ ksort($parameters);
+ $canonicalQueryString = '';
+ foreach ($parameters as $key => $value) {
+ $canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value);
+ }
+ return substr($canonicalQueryString, 1);
+ }
+
+ private function getCanonicalHeaders($oldheaders){
+ $headers = array();
+ foreach ($oldheaders as $key => $value) {
+ $headers[strtolower($key)] = trim($value);
+ }
+ ksort($headers);
+
+ $canonicalHeaders = '';
+ $signedHeaders = '';
+ foreach ($headers as $key => $value) {
+ $canonicalHeaders .= $key . ':' . $value . "\n";
+ $signedHeaders .= $key . ';';
+ }
+ $signedHeaders = substr($signedHeaders, 0, -1);
+ return [$canonicalHeaders, $signedHeaders];
+ }
+
+ private function curl($method, $url, $body, $header){
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+ if(!empty($body)){
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
+ }
+ $response = curl_exec($ch);
+ $errno = curl_errno($ch);
+ if ($errno) {
+ $this->setError('Curl error: ' . curl_error($ch));
+ }
+ curl_close($ch);
+ if ($errno) return false;
+
+ $arr=json_decode($response,true);
+ if($arr){
+ if(isset($arr['error_msg'])){
+ $this->setError($arr['error_msg']);
+ return false;
+ }else{
+ return $arr;
+ }
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function setError($message){
+ $this->error = $message;
+ //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
+ }
+}
\ No newline at end of file
diff --git a/app/lib/dns/west.php b/app/lib/dns/west.php
new file mode 100644
index 0000000..5b8c898
--- /dev/null
+++ b/app/lib/dns/west.php
@@ -0,0 +1,226 @@
+username = $config['ak'];
+ $this->api_password = $config['sk'];
+ $this->domain = $config['domain'];
+ }
+
+ public function getError(){
+ return $this->error;
+ }
+
+ public function check(){
+ if($this->getDomainList() != false){
+ return true;
+ }
+ return false;
+ }
+
+ //获取域名列表
+ public function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20){
+ $param = ['page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord];
+ $data = $this->execute('/domain/?act=getdomains', $param);
+ if($data){
+ $list = [];
+ foreach($data['items'] as $row){
+ $list[] = [
+ 'DomainId' => $row['domain'],
+ 'Domain' => $row['domain'],
+ 'RecordCount' => 0,
+ ];
+ }
+ return ['total' => $data['total'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取解析记录列表
+ public function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null){
+ $param = ['act' => 'dnsrec.list', 'domain' => $this->domain, 'record_type' => $Type, 'record_line' => $Line, 'hostname' => $KeyWord, 'pageno' => $PageNumber, 'limit' => $PageSize];
+ if(!isNullOrEmpty(($SubDomain))){
+ $param['hostname'] = $SubDomain;
+ }
+ $data = $this->execute2('/domain/dns/', $param);
+ if($data){
+ $list = [];
+ foreach($data['items'] as $row){
+ $list[] = [
+ 'RecordId' => $row['record_id'],
+ 'Domain' => $this->domain,
+ 'Name' => $row['hostname'],
+ 'Type' => $row['record_type'],
+ 'Value' => $row['record_value'],
+ 'Line' => $row['record_line'],
+ 'TTL' => $row['record_ttl'],
+ 'MX' => $row['record_mx'],
+ 'Status' => $row['pause'] == 1 ? '0' : '1',
+ 'Weight' => null,
+ 'Remark' => null,
+ 'UpdateTime' => null,
+ ];
+ }
+ return ['total' => $data['total'], 'list' => $list];
+ }
+ return false;
+ }
+
+ //获取子域名解析记录列表
+ public function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null){
+ $domain_arr = explode('.', $SubDomain);
+ $domain = $domain_arr[count($domain_arr)-2].'.'.$domain_arr[count($domain_arr)-1];
+ $subdomain = rtrim(str_replace($domain,'',$SubDomain),'.');
+ if($subdomain == '')$subdomain='@';
+ return $this->getDomainRecords($PageNumber, $PageSize, null, $subdomain, $Type, $Line);
+ }
+
+ //获取解析记录详细信息
+ public function getDomainRecordInfo($RecordId){
+ return false;
+ }
+
+ //添加解析记录
+ public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $param = ['act' => 'dnsrec.add', 'domain' => $this->domain, 'hostname' => $Name, 'record_type' => $this->convertType($Type), 'record_value' => $Value, 'record_level' => $MX, 'record_ttl' => intval($TTL), 'record_line' => $Line];
+ $data = $this->execute2('/domain/dns/', $param);
+ return is_array($data) ? $data['record_id'] : false;
+ }
+
+ //修改解析记录
+ public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Remark = null){
+ $param = ['act' => 'dnsrec.modify', 'domain' => $this->domain, 'record_id' => $RecordId, 'record_type' => $this->convertType($Type), 'record_value' => $Value, 'record_level' => $MX, 'record_ttl' => intval($TTL), 'record_line' => $Line];
+ $data = $this->execute2('/domain/dns/', $param);
+ return is_array($data);
+ }
+
+ //修改解析记录备注
+ public function updateDomainRecordRemark($RecordId, $Remark){
+ return false;
+ }
+
+ //删除解析记录
+ public function deleteDomainRecord($RecordId){
+ $param = ['act' => 'dnsrec.remove', 'domain' => $this->domain, 'record_id' => $RecordId];
+ $data = $this->execute2('/domain/dns/', $param);
+ return is_array($data);
+ }
+
+ //设置解析记录状态
+ public function setDomainRecordStatus($RecordId, $Status){
+ return false;
+ }
+
+ //获取解析记录操作日志
+ public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null){
+ return false;
+ }
+
+ //获取解析线路列表
+ public function getRecordLine(){
+ return [
+ ''=>['name'=>'默认', 'parent'=>null],
+ 'LTEL'=>['name'=>'电信', 'parent'=>null],
+ 'LCNC'=>['name'=>'联通', 'parent'=>null],
+ 'LMOB'=>['name'=>'移动', 'parent'=>null],
+ 'LEDU'=>['name'=>'教育网', 'parent'=>null],
+ 'LSEO'=>['name'=>'搜索引擎', 'parent'=>null],
+ 'LFOR'=>['name'=>'境外', 'parent'=>null],
+ ];
+ }
+
+ //获取域名信息
+ public function getDomainInfo(){
+ return false;
+ }
+
+ //获取域名最低TTL
+ public function getMinTTL(){
+ return false;
+ }
+
+ private function convertType($type){
+ return $type;
+ }
+
+ private function execute($path, $params){
+ $params['username'] = $this->username;
+ $params['time'] = $this->getMillisecond();
+ $params['token'] = md5($this->username.$this->api_password.$params['time']);
+ $response = $this->curl($path, $params);
+ $response = mb_convert_encoding($response, 'UTF-8', 'GBK');
+ $arr=json_decode($response,true);
+ if($arr){
+ if($arr['result'] == 200){
+ return $arr['data'];
+ }else{
+ $this->setError($arr['msg']);
+ return false;
+ }
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function execute2($path, $params){
+ $params['username'] = $this->username;
+ $params['apikey'] = md5($this->api_password);
+ $response = $this->curl($path, $params);
+ $response = mb_convert_encoding($response, 'UTF-8', 'GBK');
+ $arr=json_decode($response,true);
+ if($arr){
+ if($arr['code'] == 200){
+ return $arr['body'];
+ }else{
+ $this->setError($arr['msg']);
+ return false;
+ }
+ }else{
+ $this->setError('返回数据解析失败');
+ return false;
+ }
+ }
+
+ private function curl($path, $params = null){
+ $url = $this->baseUrl . $path;
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ if ($params) {
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
+ }
+ $response = curl_exec($ch);
+ $errno = curl_errno($ch);
+ if ($errno) {
+ $this->setError('Curl error: ' . curl_error($ch));
+ }
+ curl_close($ch);
+ if ($errno) return false;
+ return $response;
+ }
+
+ private function getMillisecond()
+ {
+ list($s1, $s2) = explode(' ', microtime());
+ return (float)sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
+ }
+
+ private function setError($message){
+ $this->error = $message;
+ //file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
+ }
+}
\ No newline at end of file
diff --git a/app/middleware.php b/app/middleware.php
new file mode 100644
index 0000000..378f4b9
--- /dev/null
+++ b/app/middleware.php
@@ -0,0 +1,12 @@
+-1, 'msg'=>'认证参数不能为空'])->code(403);
+ }
+ if($timestamp < time()-300 || $timestamp > time()+300){
+ return json(['code'=>-1, 'msg'=>'时间戳不合法'])->code(403);
+ }
+ $user = Db::name('user')->where('id', $uid)->find();
+ if(!$user) return json(['code'=>-1, 'msg'=>'用户不存在'])->code(403);
+ if($user['status'] == 0) return json(['code'=>-1, 'msg'=>'该用户已被封禁'])->code(403);
+ if($user['is_api'] == 0) return json(['code'=>-1, 'msg'=>'该用户未开启API权限'])->code(403);
+ if(md5($uid.$timestamp.$user['apikey']) !== $sign){
+ return json(['code'=>-1, 'msg'=>'签名错误'])->code(403);
+ }
+
+ $user['type'] = 'user';
+ $user['permission'] = [];
+ if($user['level'] == 1){
+ $user['permission'] = Db::name('permission')->where('uid', $uid)->column('domain');
+ }
+
+ $request->islogin = true;
+ $request->isApi = true;
+ $request->user = $user;
+ return $next($request);
+ }
+}
diff --git a/app/middleware/AuthUser.php b/app/middleware/AuthUser.php
new file mode 100644
index 0000000..d2f2c81
--- /dev/null
+++ b/app/middleware/AuthUser.php
@@ -0,0 +1,52 @@
+where('id', $uid)->find();
+ if($user && $user['status']==1){
+ $session=md5($user['id'].$user['password']);
+ if($session==$sid && $expiretime>time()) {
+ $islogin = true;
+ }
+ $user['type'] = 'user';
+ $user['permission'] = [];
+ if($user['level'] == 1){
+ $user['permission'] = Db::name('permission')->where('uid', $uid)->column('domain');
+ }
+ }
+ }elseif($type == 'domain'){
+ $user = Db::name('domain')->where('id', $uid)->find();
+ if($user && $user['is_sso']==1){
+ $session=md5($user['id'].$user['name']);
+ if($session==$sid && $expiretime>time()) {
+ $islogin = true;
+ }
+ $user['username'] = $user['name'];
+ $user['regtime'] = $user['addtime'];
+ $user['type'] = 'domain';
+ $user['level'] = 0;
+ $user['permission'] = [$user['name']];
+ }
+ }
+ }
+ }
+ $request->islogin = $islogin;
+ $request->user = $user;
+ return $next($request);
+ }
+}
diff --git a/app/middleware/CheckLogin.php b/app/middleware/CheckLogin.php
new file mode 100644
index 0000000..08016dc
--- /dev/null
+++ b/app/middleware/CheckLogin.php
@@ -0,0 +1,19 @@
+islogin) {
+ if ($request->isAjax() || !$request->isGet()) {
+ return json(['code'=>-1, 'msg'=>'未登录'])->code(401);
+ }
+ return redirect((string)url('/login'));
+ }
+ return $next($request);
+ }
+}
diff --git a/app/middleware/LoadConfig.php b/app/middleware/LoadConfig.php
new file mode 100644
index 0000000..0657b40
--- /dev/null
+++ b/app/middleware/LoadConfig.php
@@ -0,0 +1,39 @@
+getRootPath().'.env')){
+ if(strpos(request()->url(),'/install')===false){
+ return redirect((string)url('/install'))->header([
+ 'Cache-Control' => 'no-store, no-cache, must-revalidate',
+ 'Pragma' => 'no-cache',
+ ]);
+ }else{
+ return $next($request);
+ }
+ }
+ Config::set([], 'sys');
+
+ $request->isApi = false;
+
+ return $next($request)->header([
+ 'Cache-Control' => 'no-store, no-cache, must-revalidate',
+ 'Pragma' => 'no-cache',
+ ]);
+ }
+}
diff --git a/app/middleware/RefererCheck.php b/app/middleware/RefererCheck.php
new file mode 100644
index 0000000..7282761
--- /dev/null
+++ b/app/middleware/RefererCheck.php
@@ -0,0 +1,24 @@
+islogin);
+ View::assign('user', $request->user);
+ View::assign('cdnpublic', '//lib.baomitu.com/');
+ View::assign('skin', getAdminSkin());
+ return $next($request);
+ }
+}
diff --git a/app/provider.php b/app/provider.php
new file mode 100644
index 0000000..73d99fa
--- /dev/null
+++ b/app/provider.php
@@ -0,0 +1,9 @@
+ Request::class,
+ 'think\exception\Handle' => ExceptionHandle::class,
+];
diff --git a/app/service.php b/app/service.php
new file mode 100644
index 0000000..db1ee6a
--- /dev/null
+++ b/app/service.php
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+ 聚合DNS管理系统 - 登录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/view/common/layout.html b/app/view/common/layout.html
new file mode 100644
index 0000000..d25e4b0
--- /dev/null
+++ b/app/view/common/layout.html
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+ {block name="title"}标题{/block}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 聚合DNS管理系统
+
+
+
+
+
+
+
+
+
+
+
+{block name="main"}主内容{/block}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{block name="script"}{/block}
+
+
\ No newline at end of file
diff --git a/app/view/dispatch_jump.html b/app/view/dispatch_jump.html
new file mode 100644
index 0000000..7e7452b
--- /dev/null
+++ b/app/view/dispatch_jump.html
@@ -0,0 +1,60 @@
+
+
+
+
+ 温馨提示
+
+
+
+
+
+
+
+
+
+
{$msg}
+ {if $url}
+
+ 页面将在 {$wait} 秒后自动跳转
+
+ {/if}
+
+ 返回上一页
+ {if $url}
+ 立即跳转
+ {/if}
+
+
+
+
+
\ No newline at end of file
diff --git a/app/view/domain/account.html b/app/view/domain/account.html
new file mode 100644
index 0000000..1bacd31
--- /dev/null
+++ b/app/view/domain/account.html
@@ -0,0 +1,234 @@
+{extend name="common/layout" /}
+{block name="title"}域名账户{/block}
+{block name="main"}
+
+
+{/block}
+{block name="script"}
+
+
+
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/domain/domain.html b/app/view/domain/domain.html
new file mode 100644
index 0000000..db29a81
--- /dev/null
+++ b/app/view/domain/domain.html
@@ -0,0 +1,361 @@
+{extend name="common/layout" /}
+{block name="title"}域名管理{/block}
+{block name="main"}
+
+
+
+{/block}
+{block name="script"}
+
+
+
+
+
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/domain/log.html b/app/view/domain/log.html
new file mode 100644
index 0000000..3af7053
--- /dev/null
+++ b/app/view/domain/log.html
@@ -0,0 +1,47 @@
+{extend name="common/layout" /}
+{block name="title"}域名日志{/block}
+{block name="main"}
+
+
+{/block}
+{block name="script"}
+
+
+
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/domain/record.html b/app/view/domain/record.html
new file mode 100644
index 0000000..d24ba86
--- /dev/null
+++ b/app/view/domain/record.html
@@ -0,0 +1,436 @@
+{extend name="common/layout" /}
+{block name="title"}解析管理 - {$domainName}{/block}
+{block name="main"}
+
+
+
+
+
+
+
{if request()->user['type'] eq 'user'} 返回{/if}{$domainName}
+
+
+
+
+
+{/block}
+{block name="script"}
+
+
+
+
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/index/error.html b/app/view/index/error.html
new file mode 100644
index 0000000..4841d4b
--- /dev/null
+++ b/app/view/index/error.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+ 抱歉,出错了
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/view/index/index.html b/app/view/index/index.html
new file mode 100644
index 0000000..d4074f2
--- /dev/null
+++ b/app/view/index/index.html
@@ -0,0 +1,175 @@
+{extend name="common/layout" /}
+{block name="title"}聚合DNS管理系统{/block}
+{block name="main"}
+
+
+
+
+
+
+
+
+
+
+
+ 框架版本 |
+ {$info.framework_version} |
+
+
+ PHP版本 |
+ {$info.php_version} |
+
+
+ MySQL版本 |
+ {$info.mysql_version} |
+
+
+ WEB软件 |
+ {$info.software} |
+
+
+ 操作系统 |
+ {$info.os} |
+
+
+ 服务器时间 |
+ {$info.date} |
+
+
+
+
+
+
+
+{/block}
+{block name="script"}
+
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/index/setpwd.html b/app/view/index/setpwd.html
new file mode 100644
index 0000000..90d1601
--- /dev/null
+++ b/app/view/index/setpwd.html
@@ -0,0 +1,60 @@
+{extend name="common/layout" /}
+{block name="title"}修改密码{/block}
+{block name="main"}
+
+
+
+{/block}
+{block name="script"}
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/install/index.html b/app/view/install/index.html
new file mode 100644
index 0000000..3667912
--- /dev/null
+++ b/app/view/install/index.html
@@ -0,0 +1,266 @@
+
+
+
+
+
+
聚合DNS管理系统 - 安装程序
+
+
+
+
+
+
+
+
+
+
+
+
聚合DNS管理系统 - 安装程序
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/view/user/log.html b/app/view/user/log.html
new file mode 100644
index 0000000..f51738e
--- /dev/null
+++ b/app/view/user/log.html
@@ -0,0 +1,77 @@
+{extend name="common/layout" /}
+{block name="title"}操作日志{/block}
+{block name="main"}
+
+
+{/block}
+{block name="script"}
+
+
+
+
+
+{/block}
\ No newline at end of file
diff --git a/app/view/user/user.html b/app/view/user/user.html
new file mode 100644
index 0000000..eb6a5eb
--- /dev/null
+++ b/app/view/user/user.html
@@ -0,0 +1,338 @@
+{extend name="common/layout" /}
+{block name="title"}用户管理{/block}
+{block name="main"}
+
+
+
+{/block}
+{block name="script"}
+
+
+
+
+
+
+
+{/block}
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..c645bc1
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,50 @@
+{
+ "name": "topthink/think",
+ "description": "the new thinkphp framework",
+ "type": "project",
+ "keywords": [
+ "framework",
+ "thinkphp",
+ "ORM"
+ ],
+ "homepage": "https://www.thinkphp.cn/",
+ "license": "Apache-2.0",
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ },
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "require": {
+ "php": ">=7.2.5",
+ "topthink/framework": "^6.0.0",
+ "topthink/think-orm": "^2.0",
+ "topthink/think-view": "^1.0",
+ "cccyun/think-captcha": "^3.0"
+ },
+ "require-dev": {
+ "symfony/var-dumper": "^4.2",
+ "topthink/think-trace":"^1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "app\\": "app"
+ },
+ "psr-0": {
+ "": "extend/"
+ }
+ },
+ "config": {
+ "preferred-install": "dist"
+ },
+ "scripts": {
+ "post-autoload-dump": [
+ "@php think service:discover",
+ "@php think vendor:publish"
+ ]
+ }
+}
diff --git a/config/app.php b/config/app.php
new file mode 100644
index 0000000..8db0724
--- /dev/null
+++ b/config/app.php
@@ -0,0 +1,34 @@
+ env('app.host', ''),
+ // 应用的命名空间
+ 'app_namespace' => '',
+ // 是否启用路由
+ 'with_route' => true,
+ // 默认应用
+ 'default_app' => 'index',
+ // 默认时区
+ 'default_timezone' => 'Asia/Shanghai',
+
+ // 应用映射(自动多应用模式有效)
+ 'app_map' => [],
+ // 域名绑定(自动多应用模式有效)
+ 'domain_bind' => [],
+ // 禁止URL访问的应用列表(自动多应用模式有效)
+ 'deny_app_list' => [],
+
+ // 异常页面的模板文件
+ 'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
+
+ // 错误显示信息,非调试模式有效
+ 'error_message' => '页面错误!请稍后再试~',
+ // 显示错误信息
+ 'show_error_msg' => false,
+
+ 'version' => '1001',
+];
diff --git a/config/cache.php b/config/cache.php
new file mode 100644
index 0000000..a8d69d2
--- /dev/null
+++ b/config/cache.php
@@ -0,0 +1,29 @@
+ env('cache.driver', 'file'),
+
+ // 缓存连接方式配置
+ 'stores' => [
+ 'file' => [
+ // 驱动方式
+ 'type' => 'File',
+ // 缓存保存目录
+ 'path' => '',
+ // 缓存前缀
+ 'prefix' => '',
+ // 缓存有效期 0表示永久缓存
+ 'expire' => 0,
+ // 缓存标签前缀
+ 'tag_prefix' => 'tag:',
+ // 序列化机制 例如 ['serialize', 'unserialize']
+ 'serialize' => [],
+ ],
+ // 更多的缓存连接
+ ],
+];
diff --git a/config/captcha.php b/config/captcha.php
new file mode 100644
index 0000000..6ec80f6
--- /dev/null
+++ b/config/captcha.php
@@ -0,0 +1,39 @@
+ 4,
+ // 验证码字符集合
+ 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
+ // 验证码过期时间
+ 'expire' => 1800,
+ // 是否使用中文验证码
+ 'useZh' => false,
+ // 是否使用算术验证码
+ 'math' => false,
+ // 是否使用背景图
+ 'useImgBg' => false,
+ //验证码字符大小
+ 'fontSize' => 25,
+ // 是否使用混淆曲线
+ 'useCurve' => true,
+ //是否添加杂点
+ 'useNoise' => true,
+ // 验证码字体 不设置则随机
+ 'fontttf' => '',
+ //背景颜色
+ 'bg' => [243, 251, 254],
+ // 验证码图片高度
+ 'imageH' => 0,
+ // 验证码图片宽度
+ 'imageW' => 0,
+
+ // 添加额外的验证码设置
+ // verify => [
+ // 'length'=>4,
+ // ...
+ //],
+];
diff --git a/config/console.php b/config/console.php
new file mode 100644
index 0000000..a818a98
--- /dev/null
+++ b/config/console.php
@@ -0,0 +1,9 @@
+ [
+ ],
+];
diff --git a/config/cookie.php b/config/cookie.php
new file mode 100644
index 0000000..d3b3aab
--- /dev/null
+++ b/config/cookie.php
@@ -0,0 +1,20 @@
+ 0,
+ // cookie 保存路径
+ 'path' => '/',
+ // cookie 有效域名
+ 'domain' => '',
+ // cookie 启用安全传输
+ 'secure' => false,
+ // httponly设置
+ 'httponly' => false,
+ // 是否使用 setcookie
+ 'setcookie' => true,
+ // samesite 设置,支持 'strict' 'lax'
+ 'samesite' => '',
+];
diff --git a/config/database.php b/config/database.php
new file mode 100644
index 0000000..0cf1346
--- /dev/null
+++ b/config/database.php
@@ -0,0 +1,63 @@
+ env('database.driver', 'mysql'),
+
+ // 自定义时间查询规则
+ 'time_query_rule' => [],
+
+ // 自动写入时间戳字段
+ // true为自动识别类型 false关闭
+ // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
+ 'auto_timestamp' => true,
+
+ // 时间字段取出后的默认时间格式
+ 'datetime_format' => 'Y-m-d H:i:s',
+
+ // 时间字段配置 配置格式:create_time,update_time
+ 'datetime_field' => '',
+
+ // 数据库连接配置信息
+ 'connections' => [
+ 'mysql' => [
+ // 数据库类型
+ 'type' => env('database.type', 'mysql'),
+ // 服务器地址
+ 'hostname' => env('database.hostname', '127.0.0.1'),
+ // 数据库名
+ 'database' => env('database.database', ''),
+ // 用户名
+ 'username' => env('database.username', 'root'),
+ // 密码
+ 'password' => env('database.password', ''),
+ // 端口
+ 'hostport' => env('database.hostport', '3306'),
+ // 数据库连接参数
+ 'params' => [],
+ // 数据库编码默认采用utf8
+ 'charset' => env('database.charset', 'utf8'),
+ // 数据库表前缀
+ 'prefix' => env('database.prefix', ''),
+
+ // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+ 'deploy' => 0,
+ // 数据库读写是否分离 主从式有效
+ 'rw_separate' => false,
+ // 读写分离后 主服务器数量
+ 'master_num' => 1,
+ // 指定从服务器序号
+ 'slave_no' => '',
+ // 是否严格检查字段是否存在
+ 'fields_strict' => true,
+ // 是否需要断线重连
+ 'break_reconnect' => false,
+ // 监听SQL
+ 'trigger_sql' => env('app_debug', true),
+ // 开启字段缓存
+ 'fields_cache' => true,
+ ],
+
+ // 更多的数据库配置信息
+ ],
+];
diff --git a/config/filesystem.php b/config/filesystem.php
new file mode 100644
index 0000000..965297e
--- /dev/null
+++ b/config/filesystem.php
@@ -0,0 +1,24 @@
+ env('filesystem.driver', 'local'),
+ // 磁盘列表
+ 'disks' => [
+ 'local' => [
+ 'type' => 'local',
+ 'root' => app()->getRuntimePath() . 'storage',
+ ],
+ 'public' => [
+ // 磁盘类型
+ 'type' => 'local',
+ // 磁盘路径
+ 'root' => app()->getRootPath() . 'public/storage',
+ // 磁盘路径对应的外部URL路径
+ 'url' => '/storage',
+ // 可见性
+ 'visibility' => 'public',
+ ],
+ // 更多的磁盘配置信息
+ ],
+];
diff --git a/config/lang.php b/config/lang.php
new file mode 100644
index 0000000..59f320f
--- /dev/null
+++ b/config/lang.php
@@ -0,0 +1,27 @@
+ env('lang.default_lang', 'zh-cn'),
+ // 允许的语言列表
+ 'allow_lang_list' => [],
+ // 多语言自动侦测变量名
+ 'detect_var' => 'lang',
+ // 是否使用Cookie记录
+ 'use_cookie' => true,
+ // 多语言cookie变量
+ 'cookie_var' => 'think_lang',
+ // 多语言header变量
+ 'header_var' => 'think-lang',
+ // 扩展语言包
+ 'extend_list' => [],
+ // Accept-Language转义为对应语言包名称
+ 'accept_language' => [
+ 'zh-hans-cn' => 'zh-cn',
+ ],
+ // 是否支持语言分组
+ 'allow_group' => false,
+];
diff --git a/config/log.php b/config/log.php
new file mode 100644
index 0000000..ea24ff9
--- /dev/null
+++ b/config/log.php
@@ -0,0 +1,45 @@
+ env('log.channel', 'file'),
+ // 日志记录级别
+ 'level' => [],
+ // 日志类型记录的通道 ['error'=>'email',...]
+ 'type_channel' => [],
+ // 关闭全局日志写入
+ 'close' => false,
+ // 全局日志处理 支持闭包
+ 'processor' => null,
+
+ // 日志通道列表
+ 'channels' => [
+ 'file' => [
+ // 日志记录方式
+ 'type' => 'File',
+ // 日志保存目录
+ 'path' => '',
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => false,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ],
+ // 其它日志通道配置
+ ],
+
+];
diff --git a/config/middleware.php b/config/middleware.php
new file mode 100644
index 0000000..7e1972f
--- /dev/null
+++ b/config/middleware.php
@@ -0,0 +1,8 @@
+ [],
+ // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
+ 'priority' => [],
+];
diff --git a/config/route.php b/config/route.php
new file mode 100644
index 0000000..929e2b7
--- /dev/null
+++ b/config/route.php
@@ -0,0 +1,45 @@
+ '/',
+ // URL伪静态后缀
+ 'url_html_suffix' => '',
+ // URL普通方式参数 用于自动生成
+ 'url_common_param' => true,
+ // 是否开启路由延迟解析
+ 'url_lazy_route' => false,
+ // 是否强制使用路由
+ 'url_route_must' => true,
+ // 合并路由规则
+ 'route_rule_merge' => false,
+ // 路由是否完全匹配
+ 'route_complete_match' => false,
+ // 访问控制器层名称
+ 'controller_layer' => 'controller',
+ // 空控制器名
+ 'empty_controller' => 'Error',
+ // 是否使用控制器后缀
+ 'controller_suffix' => false,
+ // 默认的路由变量规则
+ 'default_route_pattern' => '[\w\.]+',
+ // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+ 'request_cache_key' => false,
+ // 请求缓存有效期
+ 'request_cache_expire' => null,
+ // 全局请求缓存排除规则
+ 'request_cache_except' => [],
+ // 默认控制器名
+ 'default_controller' => 'Index',
+ // 默认操作名
+ 'default_action' => 'index',
+ // 操作方法后缀
+ 'action_suffix' => '',
+ // 默认JSONP格式返回的处理方法
+ 'default_jsonp_handler' => 'jsonpReturn',
+ // 默认JSONP处理方法
+ 'var_jsonp_handler' => 'callback',
+];
diff --git a/config/session.php b/config/session.php
new file mode 100644
index 0000000..c1ef6e1
--- /dev/null
+++ b/config/session.php
@@ -0,0 +1,19 @@
+ 'PHPSESSID',
+ // SESSION_ID的提交变量,解决flash上传跨域
+ 'var_session_id' => '',
+ // 驱动方式 支持file cache
+ 'type' => 'file',
+ // 存储连接标识 当type使用cache的时候有效
+ 'store' => null,
+ // 过期时间
+ 'expire' => 1440,
+ // 前缀
+ 'prefix' => '',
+];
diff --git a/config/trace.php b/config/trace.php
new file mode 100644
index 0000000..fad2392
--- /dev/null
+++ b/config/trace.php
@@ -0,0 +1,10 @@
+ 'Html',
+ // 读取的日志通道名
+ 'channel' => '',
+];
diff --git a/config/view.php b/config/view.php
new file mode 100644
index 0000000..01259a0
--- /dev/null
+++ b/config/view.php
@@ -0,0 +1,25 @@
+ 'Think',
+ // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
+ 'auto_rule' => 1,
+ // 模板目录名
+ 'view_dir_name' => 'view',
+ // 模板后缀
+ 'view_suffix' => 'html',
+ // 模板文件名分隔符
+ 'view_depr' => DIRECTORY_SEPARATOR,
+ // 模板引擎普通标签开始标记
+ 'tpl_begin' => '{',
+ // 模板引擎普通标签结束标记
+ 'tpl_end' => '}',
+ // 标签库标签开始标记
+ 'taglib_begin' => '{',
+ // 标签库标签结束标记
+ 'taglib_end' => '}',
+];
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..0d0c2ae
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..e3c0fe9
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,24 @@
+
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+// 执行HTTP应用并响应
+$http = (new App())->http;
+
+$response = $http->run();
+
+$response->send();
+
+$http->end($response);
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..1f53798
--- /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..9b39a62
--- /dev/null
+++ b/public/router.php
@@ -0,0 +1,19 @@
+
+// +----------------------------------------------------------------------
+// $Id$
+
+if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
+ return false;
+} else {
+ $_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
+
+ require __DIR__ . "/index.php";
+}
diff --git a/public/static/css/app.min.css b/public/static/css/app.min.css
new file mode 100644
index 0000000..206b76f
--- /dev/null
+++ b/public/static/css/app.min.css
@@ -0,0 +1,10 @@
+/*!
+ * AdminLTE v2.4.18
+ *
+ * Author: Colorlib
+ * Support:
+ * Repository: git://github.com/ColorlibHQ/AdminLTE.git
+ * License: MIT
+ */
+html,body{height:100%}.layout-boxed html,.layout-boxed body{height:100%}body{font-family:'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:400;overflow-x:hidden;overflow-y:auto}.wrapper{height:100%;position:relative;overflow-x:hidden;overflow-y:auto}.wrapper:before,.wrapper:after{content:" ";display:table}.wrapper:after{clear:both}.layout-boxed .wrapper{max-width:1250px;margin:0 auto;min-height:100%;box-shadow:0 0 8px rgba(0,0,0,0.5);position:relative}.layout-boxed{background-color:#f9fafc}.content-wrapper,.main-footer{-webkit-transition:-webkit-transform .3s ease-in-out,margin .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,margin .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,margin .3s ease-in-out;transition:transform .3s ease-in-out,margin .3s ease-in-out;margin-left:230px;z-index:820}.layout-top-nav .content-wrapper,.layout-top-nav .main-footer{margin-left:0}@media (max-width:767px){.content-wrapper,.main-footer{margin-left:0}}@media (min-width:768px){.sidebar-collapse .content-wrapper,.sidebar-collapse .main-footer{margin-left:0}}@media (max-width:767px){.sidebar-open .content-wrapper,.sidebar-open .main-footer{-webkit-transform:translate(230px, 0);-ms-transform:translate(230px, 0);-o-transform:translate(230px, 0);transform:translate(230px, 0)}}.content-wrapper{min-height:calc(100vh - 101px);background-color:#ecf0f5;z-index:800}@media (max-width:767px){.content-wrapper{min-height:calc(100vh - 151px)}}.main-footer{background:#fff;padding:15px;color:#444;border-top:1px solid #d2d6de}.fixed .main-header,.fixed .main-sidebar,.fixed .left-side{position:fixed}.fixed .main-header{top:0;right:0;left:0}.fixed .content-wrapper,.fixed .right-side{padding-top:50px}@media (max-width:767px){.fixed .content-wrapper,.fixed .right-side{padding-top:100px}}.fixed.layout-boxed .wrapper{max-width:100%}.fixed .wrapper{overflow:hidden}.hold-transition .content-wrapper,.hold-transition .right-side,.hold-transition .main-footer,.hold-transition .main-sidebar,.hold-transition .left-side,.hold-transition .main-header .navbar,.hold-transition .main-header .logo,.hold-transition .menu-open .fa-angle-left{-webkit-transition:none;-o-transition:none;transition:none}.content{min-height:250px;padding:15px;margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Source Sans Pro',sans-serif}a{color:#3c8dbc}a:hover,a:active,a:focus{outline:none;text-decoration:none;color:#72afd2}.page-header{margin:10px 0 20px 0;font-size:22px}.page-header>small{color:#666;display:block;margin-top:5px}.main-header{position:relative;max-height:100px;z-index:1030}.main-header .navbar{-webkit-transition:margin-left .3s ease-in-out;-o-transition:margin-left .3s ease-in-out;transition:margin-left .3s ease-in-out;margin-bottom:0;margin-left:230px;border:none;min-height:50px;border-radius:0}.layout-top-nav .main-header .navbar{margin-left:0}.main-header #navbar-search-input.form-control{background:rgba(255,255,255,0.2);border-color:transparent}.main-header #navbar-search-input.form-control:focus,.main-header #navbar-search-input.form-control:active{border-color:rgba(0,0,0,0.1);background:rgba(255,255,255,0.9)}.main-header #navbar-search-input.form-control::-moz-placeholder{color:#ccc;opacity:1}.main-header #navbar-search-input.form-control:-ms-input-placeholder{color:#ccc}.main-header #navbar-search-input.form-control::-webkit-input-placeholder{color:#ccc}.main-header .navbar-custom-menu,.main-header .navbar-right{float:right}@media (max-width:991px){.main-header .navbar-custom-menu a,.main-header .navbar-right a{color:inherit;background:transparent}}@media (max-width:767px){.main-header .navbar-right{float:none}.navbar-collapse .main-header .navbar-right{margin:7.5px -15px}.main-header .navbar-right>li{color:inherit;border:0}}.main-header .sidebar-toggle{float:left;background-color:transparent;background-image:none;padding:15px 15px;font-family:fontAwesome}.main-header .sidebar-toggle:before{content:"\f0c9"}.main-header .sidebar-toggle:hover{color:#fff}.main-header .sidebar-toggle:focus,.main-header .sidebar-toggle:active{background:transparent}.main-header .sidebar-toggle.fa5{font-family:"Font Awesome\ 5 Free"}.main-header .sidebar-toggle.fa5:before{content:"\f0c9";font-weight:900}.main-header .sidebar-toggle .icon-bar{display:none}.main-header .navbar .nav>li.user>a>.fa,.main-header .navbar .nav>li.user>a>.glyphicon,.main-header .navbar .nav>li.user>a>.ion{margin-right:5px}.main-header .navbar .nav>li>a>.label{position:absolute;top:9px;right:7px;text-align:center;font-size:9px;padding:2px 3px;line-height:.9}.main-header .logo{-webkit-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;transition:width .3s ease-in-out;display:block;float:left;height:50px;font-size:20px;line-height:50px;text-align:center;width:230px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;padding:0 15px;font-weight:300;overflow:hidden}.main-header .logo img{padding:4px;object-fit:contain;margin:0 auto}.main-header .logo .logo-lg{display:block}.main-header .logo .logo-lg img{max-width:200px;max-height:50px}.main-header .logo .logo-lg .brandlogo-image{margin-top:8px;margin-right:10px;margin-left:-5px}.main-header .logo .logo-mini{display:none}.main-header .logo .logo-mini img{max-width:50px;max-height:50px}.main-header .logo .logo-mini .brandlogo-image{margin-top:8px;margin-right:10px;margin-left:10px}.main-header .logo .brandlogo-image{float:left;height:34px;width:auto}.main-header .navbar-brand{color:#fff}.content-header{position:relative;padding:15px 15px 0 15px}.content-header>h1{margin:0;font-size:24px}.content-header>h1>small{font-size:15px;display:inline-block;padding-left:4px;font-weight:300}.content-header>.breadcrumb{float:right;background:transparent;margin-top:0;margin-bottom:0;font-size:12px;padding:7px 5px;position:absolute;top:15px;right:10px;border-radius:2px}.content-header>.breadcrumb>li>a{color:#444;text-decoration:none;display:inline-block}.content-header>.breadcrumb>li>a>.fa,.content-header>.breadcrumb>li>a>.glyphicon,.content-header>.breadcrumb>li>a>.ion{margin-right:5px}.content-header>.breadcrumb>li+li:before{content:'>\00a0'}@media (max-width:991px){.content-header>.breadcrumb{position:relative;margin-top:5px;top:0;right:0;float:none;background:#d2d6de;padding-left:10px}.content-header>.breadcrumb li:before{color:#97a0b3}}.navbar-toggle{color:#fff;border:0;margin:0;padding:15px 15px}@media (max-width:991px){.navbar-custom-menu .navbar-nav>li{float:left}.navbar-custom-menu .navbar-nav{margin:0;float:left}.navbar-custom-menu .navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px}}@media (max-width:767px){.main-header{position:relative}.main-header .logo,.main-header .navbar{width:100%;float:none}.main-header .navbar{margin:0}.main-header .navbar-custom-menu{float:right}}@media (max-width:991px){.navbar-collapse.pull-left{float:none !important}.navbar-collapse.pull-left+.navbar-custom-menu{display:block;position:absolute;top:0;right:40px}}.main-sidebar{position:absolute;top:0;left:0;padding-top:50px;min-height:100%;width:230px;z-index:810;-webkit-transition:-webkit-transform .3s ease-in-out,width .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,width .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,width .3s ease-in-out;transition:transform .3s ease-in-out,width .3s ease-in-out}@media (max-width:767px){.main-sidebar{padding-top:100px}}@media (max-width:767px){.main-sidebar{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (min-width:768px){.sidebar-collapse .main-sidebar{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (max-width:767px){.sidebar-open .main-sidebar{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}}.sidebar{padding-bottom:10px}.sidebar-form input:focus{border-color:transparent}.user-panel{position:relative;width:100%;padding:10px;overflow:hidden}.user-panel:before,.user-panel:after{content:" ";display:table}.user-panel:after{clear:both}.user-panel>.image>img{width:100%;max-width:45px;height:auto}.user-panel>.info{padding:5px 5px 5px 15px;line-height:1;position:absolute;left:55px}.user-panel>.info>p{font-weight:600;margin-bottom:9px}.user-panel>.info>a{text-decoration:none;padding-right:5px;margin-top:3px;font-size:11px}.user-panel>.info>a>.fa,.user-panel>.info>a>.ion,.user-panel>.info>a>.glyphicon{margin-right:3px}.sidebar-menu{list-style:none;margin:0;padding:0}.sidebar-menu>li{position:relative;margin:0;padding:0}.sidebar-menu>li>a{padding:12px 5px 12px 15px;display:block}.sidebar-menu>li>a>.fa,.sidebar-menu>li>a>.glyphicon,.sidebar-menu>li>a>.ion{width:20px}.sidebar-menu>li .label,.sidebar-menu>li .badge{margin-right:5px}.sidebar-menu>li .badge{margin-top:3px}.sidebar-menu li.header{padding:10px 25px 10px 15px;font-size:12px}.sidebar-menu li>a>.fa-angle-left,.sidebar-menu li>a>.pull-right-container>.fa-angle-left{width:auto;height:auto;padding:0;margin-right:10px;-webkit-transition:transform .5s ease;-o-transition:transform .5s ease;transition:transform .5s ease}.sidebar-menu li>a>.fa-angle-left{position:absolute;top:50%;right:10px;margin-top:-8px}.sidebar-menu .menu-open>a>.fa-angle-left,.sidebar-menu .menu-open>a>.pull-right-container>.fa-angle-left{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.sidebar-menu .active>.treeview-menu{display:block}@media (min-width:768px){.sidebar-mini.sidebar-collapse .content-wrapper,.sidebar-mini.sidebar-collapse .right-side,.sidebar-mini.sidebar-collapse .main-footer{margin-left:50px !important;z-index:840}.sidebar-mini.sidebar-collapse .main-sidebar{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);width:50px !important;z-index:850}.sidebar-mini.sidebar-collapse .sidebar-menu>li{position:relative}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a{margin-right:0}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span{border-top-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:not(.treeview)>a>span{border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{padding-top:5px;padding-bottom:5px;border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .main-sidebar .user-panel>.info,.sidebar-mini.sidebar-collapse .sidebar-form,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span,.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>.pull-right,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span>.pull-right,.sidebar-mini.sidebar-collapse .sidebar-menu li.header{display:none !important;-webkit-transform:translateZ(0)}.sidebar-mini.sidebar-collapse .main-header .logo{width:50px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-mini{display:block;margin-left:-15px;margin-right:-15px;font-size:18px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-lg{display:none}.sidebar-mini.sidebar-collapse .main-header .navbar{margin-left:50px}}@media (min-width:768px){.sidebar-mini:not(.sidebar-mini-expand-feature).sidebar-collapse .sidebar-menu>li:hover>a>span:not(.pull-right),.sidebar-mini:not(.sidebar-mini-expand-feature).sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{display:block !important;position:absolute;width:180px;left:50px}.sidebar-mini:not(.sidebar-mini-expand-feature).sidebar-collapse .sidebar-menu>li:hover>a>span{top:0;margin-left:-3px;padding:12px 5px 12px 20px;background-color:inherit}.sidebar-mini:not(.sidebar-mini-expand-feature).sidebar-collapse .sidebar-menu>li:hover>a>.pull-right-container{position:relative !important;float:right;width:auto !important;left:180px !important;top:-22px !important;z-index:900}.sidebar-mini:not(.sidebar-mini-expand-feature).sidebar-collapse .sidebar-menu>li:hover>a>.pull-right-container>.label:not(:first-of-type){display:none}.sidebar-mini:not(.sidebar-mini-expand-feature).sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{top:44px;margin-left:0}}.sidebar-expanded-on-hover .main-footer,.sidebar-expanded-on-hover .content-wrapper{margin-left:50px}.sidebar-expanded-on-hover .main-sidebar{box-shadow:3px 0 8px rgba(0,0,0,0.125)}.sidebar-menu,.main-sidebar .user-panel,.sidebar-menu>li.header{white-space:nowrap;overflow:hidden}.sidebar-menu:hover{overflow:visible}.sidebar-form,.sidebar-menu>li.header{overflow:hidden;text-overflow:clip}.sidebar-menu li>a{position:relative}.sidebar-menu li>a>.pull-right-container{position:absolute;right:10px;top:50%;margin-top:-7px}.control-sidebar-bg{position:fixed;z-index:1000;bottom:0}.control-sidebar-bg,.control-sidebar{top:0;right:-230px;width:230px;-webkit-transition:right .3s ease-in-out;-o-transition:right .3s ease-in-out;transition:right .3s ease-in-out}.control-sidebar{position:absolute;padding-top:50px;z-index:1010}@media (max-width:767px){.control-sidebar{padding-top:100px}}.control-sidebar>.tab-content{padding:10px 15px}.control-sidebar.control-sidebar-open,.control-sidebar.control-sidebar-open+.control-sidebar-bg{right:0}.control-sidebar-hold-transition .control-sidebar-bg,.control-sidebar-hold-transition .control-sidebar,.control-sidebar-hold-transition .content-wrapper{transition:none}.control-sidebar-open .control-sidebar-bg,.control-sidebar-open .control-sidebar{right:0}@media (min-width:768px){.control-sidebar-open .content-wrapper,.control-sidebar-open .right-side,.control-sidebar-open .main-footer{margin-right:230px}}.fixed .control-sidebar{position:fixed;height:100%;overflow-y:auto;padding-bottom:50px}.nav-tabs.control-sidebar-tabs>li:first-of-type>a,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:hover,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:focus{border-left-width:0}.nav-tabs.control-sidebar-tabs>li>a{border-radius:0}.nav-tabs.control-sidebar-tabs>li>a,.nav-tabs.control-sidebar-tabs>li>a:hover{border-top:none;border-right:none;border-left:1px solid transparent;border-bottom:1px solid transparent}.nav-tabs.control-sidebar-tabs>li>a .icon{font-size:16px}.nav-tabs.control-sidebar-tabs>li.active>a,.nav-tabs.control-sidebar-tabs>li.active>a:hover,.nav-tabs.control-sidebar-tabs>li.active>a:focus,.nav-tabs.control-sidebar-tabs>li.active>a:active{border-top:none;border-right:none;border-bottom:none}@media (max-width:768px){.nav-tabs.control-sidebar-tabs{display:table}.nav-tabs.control-sidebar-tabs>li{display:table-cell}}.control-sidebar-heading{font-weight:400;font-size:16px;padding:10px 0;margin-bottom:10px}.control-sidebar-subheading{display:block;font-weight:400;font-size:14px}.control-sidebar-menu{list-style:none;padding:0;margin:0 -15px}.control-sidebar-menu>li>a{display:block;padding:10px 15px}.control-sidebar-menu>li>a:before,.control-sidebar-menu>li>a:after{content:" ";display:table}.control-sidebar-menu>li>a:after{clear:both}.control-sidebar-menu>li>a>.control-sidebar-subheading{margin-top:0}.control-sidebar-menu .menu-icon{float:left;width:35px;height:35px;border-radius:50%;text-align:center;line-height:35px}.control-sidebar-menu .menu-info{margin-left:45px;margin-top:3px}.control-sidebar-menu .menu-info>.control-sidebar-subheading{margin:0}.control-sidebar-menu .menu-info>p{margin:0;font-size:11px}.control-sidebar-menu .progress{margin:0}.control-sidebar-dark{color:#b8c7ce}.control-sidebar-dark,.control-sidebar-dark+.control-sidebar-bg{background:#222d32}.control-sidebar-dark .nav-tabs.control-sidebar-tabs{border-bottom:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a{background:#181f23;color:#b8c7ce}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#141a1d;border-bottom-color:#141a1d}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:active{background:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover{color:#fff}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#222d32;color:#fff}.control-sidebar-dark .control-sidebar-heading,.control-sidebar-dark .control-sidebar-subheading{color:#fff}.control-sidebar-dark .control-sidebar-menu>li>a:hover{background:#1e282c}.control-sidebar-dark .control-sidebar-menu>li>a .menu-info>p{color:#b8c7ce}.control-sidebar-light{color:#5e5e5e}.control-sidebar-light,.control-sidebar-light+.control-sidebar-bg{background:#f9fafc;border-left:1px solid #d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs{border-bottom:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a{background:#e8ecf4;color:#444}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#d2d6de;border-bottom-color:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:active{background:#eff1f7}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#f9fafc;color:#111}.control-sidebar-light .control-sidebar-heading,.control-sidebar-light .control-sidebar-subheading{color:#111}.control-sidebar-light .control-sidebar-menu{margin-left:-14px}.control-sidebar-light .control-sidebar-menu>li>a:hover{background:#f4f4f5}.control-sidebar-light .control-sidebar-menu>li>a .menu-info>p{color:#5e5e5e}.dropdown-menu{box-shadow:none;border-color:#eee}.dropdown-menu>li>a{color:#777}.dropdown-menu>li>a>.glyphicon,.dropdown-menu>li>a>.fa,.dropdown-menu>li>a>.ion{margin-right:10px}.dropdown-menu>li>a:hover{background-color:#e1e3e9;color:#333}.dropdown-menu>.divider{background-color:#eee}.navbar-nav>.notifications-menu>.dropdown-menu,.navbar-nav>.messages-menu>.dropdown-menu,.navbar-nav>.tasks-menu>.dropdown-menu{width:280px;padding:0 0 0 0;margin:0;top:100%}.navbar-nav>.notifications-menu>.dropdown-menu>li,.navbar-nav>.messages-menu>.dropdown-menu>li,.navbar-nav>.tasks-menu>.dropdown-menu>li{position:relative}.navbar-nav>.notifications-menu>.dropdown-menu>li.header,.navbar-nav>.messages-menu>.dropdown-menu>li.header,.navbar-nav>.tasks-menu>.dropdown-menu>li.header{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;background-color:#ffffff;padding:7px 10px;border-bottom:1px solid #f4f4f4;color:#444444;font-size:14px}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;font-size:12px;background-color:#fff;padding:7px 10px;border-bottom:1px solid #eeeeee;color:#444 !important;text-align:center}@media (max-width:991px){.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{background:#fff !important;color:#444 !important}}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a:hover{text-decoration:none;font-weight:normal}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu,.navbar-nav>.messages-menu>.dropdown-menu>li .menu,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu{max-height:200px;margin:0;padding:0;list-style:none;overflow-x:hidden}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{display:block;white-space:nowrap;border-bottom:1px solid #f4f4f4}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a:hover{background:#f4f4f4;text-decoration:none}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a{color:#444444;overflow:hidden;text-overflow:ellipsis;padding:10px}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.glyphicon,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.fa,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.ion{width:20px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a{margin:0;padding:10px 10px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>div>img{margin:auto 10px auto auto;width:40px;height:40px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4{padding:0;margin:0 0 0 45px;color:#444444;font-size:15px;position:relative}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4>small{color:#999999;font-size:10px;position:absolute;top:0;right:0}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>p{margin:0 0 0 45px;font-size:12px;color:#888888}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:before,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{content:" ";display:table}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{clear:both}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{padding:10px}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>h3{font-size:14px;padding:0;margin:0 0 10px 0;color:#666666}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>.progress{padding:0;margin:0}.navbar-nav>.user-menu>.dropdown-menu{border-top-right-radius:0;border-top-left-radius:0;padding:1px 0 0 0;border-top-width:0;width:280px}.navbar-nav>.user-menu>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header{height:175px;padding:10px;text-align:center}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>img{z-index:5;height:90px;width:90px;border:3px solid;border-color:transparent;border-color:rgba(255,255,255,0.2)}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p{z-index:5;color:#fff;color:rgba(255,255,255,0.8);font-size:17px;margin-top:10px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p>small{display:block;font-size:12px}.navbar-nav>.user-menu>.dropdown-menu>.user-body{padding:15px;border-bottom:1px solid #f4f4f4;border-top:1px solid #dddddd}.navbar-nav>.user-menu>.dropdown-menu>.user-body:before,.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-body a{color:#444 !important}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-body a{background:#fff !important;color:#444 !important}}.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background-color:#f9f9f9;padding:10px}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:before,.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default{color:#666666}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default:hover{background-color:#f9f9f9}}.navbar-nav>.user-menu .user-image{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px;margin-top:-2px}@media (max-width:767px){.navbar-nav>.user-menu .user-image{float:none;margin-right:0;margin-top:-8px;line-height:10px}}.open:not(.dropup)>.animated-dropdown-menu{backface-visibility:visible !important;-webkit-animation:flipInX .7s both;-o-animation:flipInX .7s both;animation:flipInX .7s both}@keyframes flipInX{0%{transform:perspective(400px) rotate3d(1, 0, 0, 90deg);transition-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotate3d(1, 0, 0, -20deg);transition-timing-function:ease-in}60%{transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{transform:perspective(400px)}}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 90deg);-webkit-transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -20deg);-webkit-transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{-webkit-transform:perspective(400px)}}.navbar-custom-menu>.navbar-nav>li{position:relative}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:0;left:auto}@media (max-width:991px){.navbar-custom-menu>.navbar-nav{float:right}.navbar-custom-menu>.navbar-nav>li{position:static}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:5%;left:auto;border:1px solid #ddd;background:#fff}}.form-control{border-radius:0;box-shadow:none;border-color:#d2d6de}.form-control:focus{border-color:#3c8dbc;box-shadow:none}.form-control::-moz-placeholder,.form-control:-ms-input-placeholder,.form-control::-webkit-input-placeholder{color:#bbb;opacity:1}.form-control:not(select){-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-group.has-success label{color:#00a65a}.form-group.has-success .form-control,.form-group.has-success .input-group-addon{border-color:#00a65a;box-shadow:none}.form-group.has-success .help-block{color:#00a65a}.form-group.has-warning label{color:#f39c12}.form-group.has-warning .form-control,.form-group.has-warning .input-group-addon{border-color:#f39c12;box-shadow:none}.form-group.has-warning .help-block{color:#f39c12}.form-group.has-error label{color:#dd4b39}.form-group.has-error .form-control,.form-group.has-error .input-group-addon{border-color:#dd4b39;box-shadow:none}.form-group.has-error .help-block{color:#dd4b39}.input-group .input-group-addon{border-radius:0;border-color:#d2d6de;background-color:#fff}.btn-group-vertical .btn.btn-flat:first-of-type,.btn-group-vertical .btn.btn-flat:last-of-type{border-radius:0}.icheck>label{padding-left:0}.form-control-feedback.fa{line-height:34px}.input-lg+.form-control-feedback.fa,.input-group-lg+.form-control-feedback.fa,.form-group-lg .form-control+.form-control-feedback.fa{line-height:46px}.input-sm+.form-control-feedback.fa,.input-group-sm+.form-control-feedback.fa,.form-group-sm .form-control+.form-control-feedback.fa{line-height:30px}.progress,.progress>.progress-bar{-webkit-box-shadow:none;box-shadow:none}.progress,.progress>.progress-bar,.progress .progress-bar,.progress>.progress-bar .progress-bar{border-radius:1px}.progress.sm,.progress-sm{height:10px}.progress.sm,.progress-sm,.progress.sm .progress-bar,.progress-sm .progress-bar{border-radius:1px}.progress.xs,.progress-xs{height:7px}.progress.xs,.progress-xs,.progress.xs .progress-bar,.progress-xs .progress-bar{border-radius:1px}.progress.xxs,.progress-xxs{height:3px}.progress.xxs,.progress-xxs,.progress.xxs .progress-bar,.progress-xxs .progress-bar{border-radius:1px}.progress.vertical{position:relative;width:30px;height:200px;display:inline-block;margin-right:10px}.progress.vertical>.progress-bar{width:100%;position:absolute;bottom:0}.progress.vertical.sm,.progress.vertical.progress-sm{width:20px}.progress.vertical.xs,.progress.vertical.progress-xs{width:10px}.progress.vertical.xxs,.progress.vertical.progress-xxs{width:3px}.progress-group .progress-text{font-weight:600}.progress-group .progress-number{float:right}.table tr>td .progress{margin:0}.progress-bar-light-blue,.progress-bar-primary{background-color:#3c8dbc}.progress-striped .progress-bar-light-blue,.progress-striped .progress-bar-primary{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-green,.progress-bar-success{background-color:#00a65a}.progress-striped .progress-bar-green,.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-aqua,.progress-bar-info{background-color:#00c0ef}.progress-striped .progress-bar-aqua,.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-yellow,.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-yellow,.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-red,.progress-bar-danger{background-color:#dd4b39}.progress-striped .progress-bar-red,.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.small-box{border-radius:2px;position:relative;display:block;margin-bottom:20px;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.small-box>.inner{padding:10px}.small-box>.small-box-footer{position:relative;text-align:center;padding:3px 0;color:#fff;color:rgba(255,255,255,0.8);display:block;z-index:10;background:rgba(0,0,0,0.1);text-decoration:none}.small-box>.small-box-footer:hover{color:#fff;background:rgba(0,0,0,0.15)}.small-box h3{font-size:38px;font-weight:bold;margin:0 0 10px 0;white-space:nowrap;padding:0}.small-box p{font-size:15px}.small-box p>small{display:block;color:#f9f9f9;font-size:13px;margin-top:5px}.small-box h3,.small-box p{z-index:5}.small-box .icon{-webkit-transition:all .3s linear;-o-transition:all .3s linear;transition:all .3s linear;position:absolute;top:-10px;right:10px;z-index:0;font-size:90px;color:rgba(0,0,0,0.15)}.small-box:hover{text-decoration:none;color:#f9f9f9}.small-box:hover .icon{font-size:95px}@media (max-width:767px){.small-box{text-align:center}.small-box .icon{display:none}.small-box p{font-size:12px}}.box{position:relative;border-radius:3px;background:#ffffff;border-top:3px solid #d2d6de;margin-bottom:20px;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.box.box-primary{border-top-color:#3c8dbc}.box.box-info{border-top-color:#00c0ef}.box.box-danger{border-top-color:#dd4b39}.box.box-warning{border-top-color:#f39c12}.box.box-success{border-top-color:#00a65a}.box.box-default{border-top-color:#d2d6de}.box.collapsed-box .box-body,.box.collapsed-box .box-footer{display:none}.box .nav-stacked>li{border-bottom:1px solid #f4f4f4;margin:0}.box .nav-stacked>li:last-of-type{border-bottom:none}.box.height-control .box-body{max-height:300px;overflow:auto}.box .border-right{border-right:1px solid #f4f4f4}.box .border-left{border-left:1px solid #f4f4f4}.box.box-solid{border-top:0}.box.box-solid>.box-header .btn.btn-default{background:transparent}.box.box-solid>.box-header .btn:hover,.box.box-solid>.box-header a:hover{background:rgba(0,0,0,0.1)}.box.box-solid.box-default{border:1px solid #d2d6de}.box.box-solid.box-default>.box-header{color:#444;background:#d2d6de;background-color:#d2d6de}.box.box-solid.box-default>.box-header a,.box.box-solid.box-default>.box-header .btn{color:#444}.box.box-solid.box-primary{border:1px solid #3c8dbc}.box.box-solid.box-primary>.box-header{color:#fff;background:#3c8dbc;background-color:#3c8dbc}.box.box-solid.box-primary>.box-header a,.box.box-solid.box-primary>.box-header .btn{color:#fff}.box.box-solid.box-info{border:1px solid #00c0ef}.box.box-solid.box-info>.box-header{color:#fff;background:#00c0ef;background-color:#00c0ef}.box.box-solid.box-info>.box-header a,.box.box-solid.box-info>.box-header .btn{color:#fff}.box.box-solid.box-danger{border:1px solid #dd4b39}.box.box-solid.box-danger>.box-header{color:#fff;background:#dd4b39;background-color:#dd4b39}.box.box-solid.box-danger>.box-header a,.box.box-solid.box-danger>.box-header .btn{color:#fff}.box.box-solid.box-warning{border:1px solid #f39c12}.box.box-solid.box-warning>.box-header{color:#fff;background:#f39c12;background-color:#f39c12}.box.box-solid.box-warning>.box-header a,.box.box-solid.box-warning>.box-header .btn{color:#fff}.box.box-solid.box-success{border:1px solid #00a65a}.box.box-solid.box-success>.box-header{color:#fff;background:#00a65a;background-color:#00a65a}.box.box-solid.box-success>.box-header a,.box.box-solid.box-success>.box-header .btn{color:#fff}.box.box-solid>.box-header>.box-tools .btn{border:0;box-shadow:none}.box.box-solid[class*='bg']>.box-header{color:#fff}.box .box-group>.box{margin-bottom:5px}.box .knob-label{text-align:center;color:#333;font-weight:100;font-size:12px;margin-bottom:.3em}.box>.overlay,.overlay-wrapper>.overlay,.box>.loading-img,.overlay-wrapper>.loading-img{position:absolute;top:0;left:0;width:100%;height:100%}.box .overlay,.overlay-wrapper .overlay{z-index:50;background:rgba(255,255,255,0.7);border-radius:3px}.box .overlay>.fa,.overlay-wrapper .overlay>.fa{position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px;color:#000;font-size:30px}.box .overlay.dark,.overlay-wrapper .overlay.dark{background:rgba(0,0,0,0.5)}.box-header:before,.box-body:before,.box-footer:before,.box-header:after,.box-body:after,.box-footer:after{content:" ";display:table}.box-header:after,.box-body:after,.box-footer:after{clear:both}.box-header{color:#444;display:block;padding:10px;position:relative}.box-header.with-border{border-bottom:1px solid #f4f4f4}.collapsed-box .box-header.with-border{border-bottom:none}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion,.box-header .box-title{display:inline-block;font-size:18px;margin:0;line-height:1}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion{margin-right:5px}.box-header>.box-tools{float:right;margin-top:-5px;margin-bottom:-5px}.box-header>.box-tools [data-toggle="tooltip"]{position:relative}.box-header>.box-tools.pull-right .dropdown-menu{right:0;left:auto}.box-header>.box-tools .dropdown-menu>li>a{color:#444 !important}.btn-box-tool{padding:5px;font-size:12px;background:transparent;color:#97a0b3}.open .btn-box-tool,.btn-box-tool:hover{color:#606c84}.btn-box-tool.btn:active{box-shadow:none}.box-body{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;padding:10px}.no-header .box-body{border-top-right-radius:3px;border-top-left-radius:3px}.box-body>.table{margin-bottom:0}.box-body .fc{margin-top:5px}.box-body .full-width-chart{margin:-19px}.box-body.no-padding .full-width-chart{margin:-9px}.box-body .box-pane{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:3px}.box-body .box-pane-right{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:0}.box-footer{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;border-top:1px solid #f4f4f4;padding:10px;background-color:#fff}.chart-legend{margin:10px 0}@media (max-width:991px){.chart-legend>li{float:left;margin-right:10px}}.box-comments{background:#f7f7f7}.box-comments .box-comment{padding:8px 0;border-bottom:1px solid #eee}.box-comments .box-comment:before,.box-comments .box-comment:after{content:" ";display:table}.box-comments .box-comment:after{clear:both}.box-comments .box-comment:last-of-type{border-bottom:0}.box-comments .box-comment:first-of-type{padding-top:0}.box-comments .box-comment img{float:left}.box-comments .comment-text{margin-left:40px;color:#555}.box-comments .username{color:#444;display:block;font-weight:600}.box-comments .text-muted{font-weight:400;font-size:12px}.info-box{display:block;min-height:90px;background:#fff;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:2px;margin-bottom:15px}.info-box small{font-size:14px}.info-box .progress{background:rgba(0,0,0,0.2);margin:5px -10px 5px -10px;height:2px}.info-box .progress,.info-box .progress .progress-bar{border-radius:0}.info-box .progress .progress-bar{background:#fff}.info-box-icon{border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px;display:block;float:left;height:90px;width:90px;text-align:center;font-size:45px;line-height:90px;background:rgba(0,0,0,0.2)}.info-box-icon>img{max-width:100%}.info-box-content{padding:5px 10px;margin-left:90px}.info-box-number{display:block;font-weight:bold;font-size:18px}.progress-description,.info-box-text{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.info-box-text{text-transform:uppercase}.info-box-more{display:block}.progress-description{margin:0}.timeline{position:relative;margin:0 0 30px 0;padding:0;list-style:none}.timeline:before{content:'';position:absolute;top:0;bottom:0;width:4px;background:#ddd;left:31px;margin:0;border-radius:2px}.timeline>li{position:relative;margin-right:10px;margin-bottom:15px}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-item{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;margin-top:0;background:#fff;color:#444;margin-left:60px;margin-right:15px;padding:0;position:relative}.timeline>li>.timeline-item>.time{color:#999;float:right;padding:10px;font-size:12px}.timeline>li>.timeline-item>.timeline-header{margin:0;color:#555;border-bottom:1px solid #f4f4f4;padding:10px;font-size:16px;line-height:1.1}.timeline>li>.timeline-item>.timeline-header>a{font-weight:600}.timeline>li>.timeline-item>.timeline-body,.timeline>li>.timeline-item>.timeline-footer{padding:10px}.timeline>li>.fa,.timeline>li>.glyphicon,.timeline>li>.ion{width:30px;height:30px;font-size:15px;line-height:30px;position:absolute;color:#666;background:#d2d6de;border-radius:50%;text-align:center;left:18px;top:0}.timeline>.time-label>span{font-weight:600;padding:5px;display:inline-block;background-color:#fff;border-radius:4px}.timeline-inverse>li>.timeline-item{background:#f0f0f0;border:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none}.timeline-inverse>li>.timeline-item>.timeline-header{border-bottom-color:#ddd}.btn{border-radius:3px;-webkit-box-shadow:none;box-shadow:none;border:1px solid transparent}.btn.uppercase{text-transform:uppercase}.btn.btn-flat{border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-width:1px}.btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:focus{outline:none}.btn.btn-file{position:relative;overflow:hidden}.btn.btn-file>input[type='file']{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;opacity:0;filter:alpha(opacity=0);outline:none;background:white;cursor:inherit;display:block}.btn-default{background-color:#f4f4f4;color:#444;border-color:#ddd}.btn-default:hover,.btn-default:active,.btn-default.hover{background-color:#e7e7e7}.btn-primary{background-color:#3c8dbc;border-color:#367fa9}.btn-primary:hover,.btn-primary:active,.btn-primary.hover{background-color:#367fa9}.btn-success{background-color:#00a65a;border-color:#008d4c}.btn-success:hover,.btn-success:active,.btn-success.hover{background-color:#008d4c}.btn-info{background-color:#00c0ef;border-color:#00acd6}.btn-info:hover,.btn-info:active,.btn-info.hover{background-color:#00acd6}.btn-danger{background-color:#dd4b39;border-color:#d73925}.btn-danger:hover,.btn-danger:active,.btn-danger.hover{background-color:#d73925}.btn-warning{background-color:#f39c12;border-color:#e08e0b}.btn-warning:hover,.btn-warning:active,.btn-warning.hover{background-color:#e08e0b}.btn-outline{border:1px solid #fff;background:transparent;color:#fff}.btn-outline:hover,.btn-outline:focus,.btn-outline:active{color:rgba(255,255,255,0.7);border-color:rgba(255,255,255,0.7)}.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn[class*='bg-']:hover{-webkit-box-shadow:inset 0 0 100px rgba(0,0,0,0.2);box-shadow:inset 0 0 100px rgba(0,0,0,0.2)}.btn-app{border-radius:3px;position:relative;padding:15px 5px;margin:0 0 10px 10px;min-width:80px;height:60px;text-align:center;color:#666;border:1px solid #ddd;background-color:#f4f4f4;font-size:12px}.btn-app>.fa,.btn-app>.glyphicon,.btn-app>.ion{font-size:20px;display:block}.btn-app:hover{background:#f4f4f4;color:#444;border-color:#aaa}.btn-app:active,.btn-app:focus{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-app>.badge{position:absolute;top:-3px;right:-10px;font-size:10px;font-weight:400}.callout{border-radius:3px;margin:0 0 20px 0;padding:15px 30px 15px 15px;border-left:5px solid #eee}.callout a{color:#fff;text-decoration:underline}.callout a:hover{color:#eee}.callout h4{margin-top:0;font-weight:600}.callout p:last-child{margin-bottom:0}.callout code,.callout .highlight{background-color:#fff}.callout.callout-danger{border-color:#c23321}.callout.callout-warning{border-color:#c87f0a}.callout.callout-info{border-color:#0097bc}.callout.callout-success{border-color:#00733e}.alert{border-radius:3px}.alert h4{font-weight:600}.alert .icon{margin-right:10px}.alert .close{color:#000;opacity:.2;filter:alpha(opacity=20)}.alert .close:hover{opacity:.5;filter:alpha(opacity=50)}.nav>li>a:hover,.nav>li>a:active,.nav>li>a:focus{color:#444;background:#f7f7f7}.nav-pills>li>a{border-radius:0;border-top:3px solid transparent;color:#444}.nav-pills>li>a>.fa,.nav-pills>li>a>.glyphicon,.nav-pills>li>a>.ion{margin-right:5px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{border-top-color:#3c8dbc}.nav-pills>li.active>a{font-weight:600}.nav-stacked>li>a{border-radius:0;border-top:0;border-left:3px solid transparent;color:#444}.nav-stacked>li.active>a,.nav-stacked>li.active>a:hover{background:transparent;color:#444;border-top:0;border-left-color:#3c8dbc}.nav-stacked>li.header{border-bottom:1px solid #ddd;color:#777;margin-bottom:10px;padding:5px 10px;text-transform:uppercase}.nav-tabs-custom{margin-bottom:20px;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px}.nav-tabs-custom>.nav-tabs{margin:0;border-bottom-color:#f4f4f4;border-top-right-radius:3px;border-top-left-radius:3px}.nav-tabs-custom>.nav-tabs>li{border-top:3px solid transparent;margin-bottom:-2px;margin-right:5px}.nav-tabs-custom>.nav-tabs>li.disabled>a{color:#777}.nav-tabs-custom>.nav-tabs>li>a{color:#444;border-radius:0}.nav-tabs-custom>.nav-tabs>li>a.text-muted{color:#999}.nav-tabs-custom>.nav-tabs>li>a,.nav-tabs-custom>.nav-tabs>li>a:hover{background:transparent;margin:0}.nav-tabs-custom>.nav-tabs>li>a:hover{color:#999}.nav-tabs-custom>.nav-tabs>li:not(.active)>a:hover,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:focus,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:active{border-color:transparent}.nav-tabs-custom>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom>.nav-tabs>li.active>a,.nav-tabs-custom>.nav-tabs>li.active:hover>a{background-color:#fff;color:#444}.nav-tabs-custom>.nav-tabs>li.active>a{border-top-color:transparent;border-left-color:#f4f4f4;border-right-color:#f4f4f4}.nav-tabs-custom>.nav-tabs>li:first-of-type{margin-left:0}.nav-tabs-custom>.nav-tabs>li:first-of-type.active>a{border-left-color:transparent}.nav-tabs-custom>.nav-tabs.pull-right{float:none !important}.nav-tabs-custom>.nav-tabs.pull-right>li{float:right}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type{margin-right:0}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type>a{border-left-width:1px}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type.active>a{border-left-color:#f4f4f4;border-right-color:transparent}.nav-tabs-custom>.nav-tabs>li.header{line-height:35px;padding:0 10px;font-size:20px;color:#444}.nav-tabs-custom>.nav-tabs>li.header>.fa,.nav-tabs-custom>.nav-tabs>li.header>.glyphicon,.nav-tabs-custom>.nav-tabs>li.header>.ion{margin-right:5px}.nav-tabs-custom>.tab-content{background:#fff;padding:10px;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.nav-tabs-custom .dropdown.open>a:active,.nav-tabs-custom .dropdown.open>a:focus{background:transparent;color:#999}.nav-tabs-custom.tab-primary>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom.tab-info>.nav-tabs>li.active{border-top-color:#00c0ef}.nav-tabs-custom.tab-danger>.nav-tabs>li.active{border-top-color:#dd4b39}.nav-tabs-custom.tab-warning>.nav-tabs>li.active{border-top-color:#f39c12}.nav-tabs-custom.tab-success>.nav-tabs>li.active{border-top-color:#00a65a}.nav-tabs-custom.tab-default>.nav-tabs>li.active{border-top-color:#d2d6de}.pagination>li>a{background:#fafafa;color:#666}.pagination.pagination-flat>li>a{border-radius:0 !important}.products-list{list-style:none;margin:0;padding:0}.products-list>.item{border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:10px 0;background:#fff}.products-list>.item:before,.products-list>.item:after{content:" ";display:table}.products-list>.item:after{clear:both}.products-list .product-img{float:left}.products-list .product-img img{width:50px;height:50px}.products-list .product-info{margin-left:60px}.products-list .product-title{font-weight:600}.products-list .product-description{display:block;color:#999;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.product-list-in-box>.item{-webkit-box-shadow:none;box-shadow:none;border-radius:0;border-bottom:1px solid #f4f4f4}.product-list-in-box>.item:last-of-type{border-bottom-width:0}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{border-top:1px solid #f4f4f4}.table>thead>tr>th{border-bottom:2px solid #f4f4f4}.table tr td .progress{margin-top:5px}.table-bordered{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table.no-border,.table.no-border td,.table.no-border th{border:0}table.text-center,table.text-center td,table.text-center th{text-align:center}.table.align th{text-align:left}.table.align td{text-align:right}.label-default{background-color:#d2d6de;color:#444}.carousel-control.left,.carousel-control.right{background-image:none}.carousel-control>.fa{font-size:40px;position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-20px}.modal{background:rgba(0,0,0,0.3)}.modal-content{border-radius:0;-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125);border:0}@media (min-width:768px){.modal-content{-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125)}}.modal-header{border-bottom-color:#f4f4f4}.modal-footer{border-top-color:#f4f4f4}.modal-primary .modal-header,.modal-primary .modal-footer{border-color:#307095}.modal-warning .modal-header,.modal-warning .modal-footer{border-color:#c87f0a}.modal-info .modal-header,.modal-info .modal-footer{border-color:#0097bc}.modal-success .modal-header,.modal-success .modal-footer{border-color:#00733e}.modal-danger .modal-header,.modal-danger .modal-footer{border-color:#c23321}.box-widget{border:none;position:relative}.widget-user .widget-user-header{padding:20px;height:120px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user .widget-user-username{margin-top:0;margin-bottom:5px;font-size:25px;font-weight:300;text-shadow:0 1px 1px rgba(0,0,0,0.2)}.widget-user .widget-user-desc{margin-top:0}.widget-user .widget-user-image{position:absolute;top:65px;left:50%;margin-left:-45px}.widget-user .widget-user-image>img{width:90px;height:auto;border:3px solid #fff}.widget-user .box-footer{padding-top:30px}.widget-user-2 .widget-user-header{padding:20px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user-2 .widget-user-username{margin-top:5px;margin-bottom:5px;font-size:25px;font-weight:300}.widget-user-2 .widget-user-desc{margin-top:0}.widget-user-2 .widget-user-username,.widget-user-2 .widget-user-desc{margin-left:75px}.widget-user-2 .widget-user-image>img{width:65px;height:auto;float:left}.treeview-menu{display:none;list-style:none;padding:0;margin:0;padding-left:5px}.treeview-menu .treeview-menu{padding-left:20px}.treeview-menu>li{margin:0}.treeview-menu>li>a{padding:5px 5px 5px 15px;display:block;font-size:14px}.treeview-menu>li>a>.fa,.treeview-menu>li>a>.glyphicon,.treeview-menu>li>a>.ion{width:20px}.treeview-menu>li>a>.pull-right-container>.fa-angle-left,.treeview-menu>li>a>.pull-right-container>.fa-angle-down,.treeview-menu>li>a>.fa-angle-left,.treeview-menu>li>a>.fa-angle-down{width:auto}.treeview>ul.treeview-menu{overflow:hidden;height:auto;padding-top:0px !important;padding-bottom:0px !important}.treeview.menu-open>ul.treeview-menu{overflow:visible;height:auto}.mailbox-messages>.table{margin:0}.mailbox-controls{padding:5px}.mailbox-controls.with-border{border-bottom:1px solid #f4f4f4}.mailbox-read-info{border-bottom:1px solid #f4f4f4;padding:10px}.mailbox-read-info h3{font-size:20px;margin:0}.mailbox-read-info h5{margin:0;padding:5px 0 0 0}.mailbox-read-time{color:#999;font-size:13px}.mailbox-read-message{padding:10px}.mailbox-attachments li{float:left;width:200px;border:1px solid #eee;margin-bottom:10px;margin-right:10px}.mailbox-attachment-name{font-weight:bold;color:#666}.mailbox-attachment-icon,.mailbox-attachment-info,.mailbox-attachment-size{display:block}.mailbox-attachment-info{padding:10px;background:#f4f4f4}.mailbox-attachment-size{color:#999;font-size:12px}.mailbox-attachment-icon{text-align:center;font-size:65px;color:#666;padding:20px 10px}.mailbox-attachment-icon.has-img{padding:0}.mailbox-attachment-icon.has-img>img{max-width:100%;height:auto}.lockscreen{background:#d2d6de}.lockscreen-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.lockscreen-logo a{color:#444}.lockscreen-wrapper{max-width:400px;margin:0 auto;margin-top:10%}.lockscreen .lockscreen-name{text-align:center;font-weight:600}.lockscreen-item{border-radius:4px;padding:0;background:#fff;position:relative;margin:10px auto 30px auto;width:290px}.lockscreen-image{border-radius:50%;position:absolute;left:-10px;top:-25px;background:#fff;padding:5px;z-index:10}.lockscreen-image>img{border-radius:50%;width:70px;height:70px}.lockscreen-credentials{margin-left:70px}.lockscreen-credentials .form-control{border:0}.lockscreen-credentials .btn{background-color:#fff;border:0;padding:0 10px}.lockscreen-footer{margin-top:10px}.login-logo,.register-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.login-logo a,.register-logo a{color:#444}.login-page,.register-page{height:auto;background:#d2d6de}.login-box,.register-box{width:360px;margin:7% auto}@media (max-width:768px){.login-box,.register-box{width:90%;margin-top:20px}}.login-box-body,.register-box-body{background:#fff;padding:20px;border-top:0;color:#666}.login-box-body .form-control-feedback,.register-box-body .form-control-feedback{color:#777}.login-box-msg,.register-box-msg{margin:0;text-align:center;padding:0 20px 20px 20px}.social-auth-links{margin:10px 0}.error-page{width:600px;margin:20px auto 0 auto}@media (max-width:991px){.error-page{width:100%}}.error-page>.headline{float:left;font-size:100px;font-weight:300}@media (max-width:991px){.error-page>.headline{float:none;text-align:center}}.error-page>.error-content{margin-left:190px;display:block}@media (max-width:991px){.error-page>.error-content{margin-left:0}}.error-page>.error-content>h3{font-weight:300;font-size:25px}@media (max-width:991px){.error-page>.error-content>h3{text-align:center}}.invoice{position:relative;background:#fff;border:1px solid #f4f4f4;padding:20px;margin:10px 25px}.invoice-title{margin-top:0}.profile-user-img{margin:0 auto;width:100px;padding:3px;border:3px solid #d2d6de}.profile-username{font-size:21px;margin-top:5px}.post{border-bottom:1px solid #d2d6de;margin-bottom:15px;padding-bottom:15px;color:#666}.post:last-of-type{border-bottom:0;margin-bottom:0;padding-bottom:0}.post .user-block{margin-bottom:15px}.fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)}.select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none}.select2-container--default.select2-container--focus .select2-selection--multiple,.select2-container--default .select2-search--dropdown .select2-search__field{border-color:#3c8dbc !important}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px}.box .datepicker-inline,.box .datepicker-inline .datepicker-days,.box .datepicker-inline>table,.box .datepicker-inline .datepicker-days>table{width:100%}.box .datepicker-inline td:hover,.box .datepicker-inline .datepicker-days td:hover,.box .datepicker-inline>table td:hover,.box .datepicker-inline .datepicker-days>table td:hover{background-color:rgba(255,255,255,0.3)}.box .datepicker-inline td.day.old,.box .datepicker-inline .datepicker-days td.day.old,.box .datepicker-inline>table td.day.old,.box .datepicker-inline .datepicker-days>table td.day.old,.box .datepicker-inline td.day.new,.box .datepicker-inline .datepicker-days td.day.new,.box .datepicker-inline>table td.day.new,.box .datepicker-inline .datepicker-days>table td.day.new{color:#777}.pad{padding:10px}.margin{margin:10px}.margin-bottom{margin-bottom:20px}.margin-bottom-none{margin-bottom:0}.margin-r-5{margin-right:5px}.inline{display:inline}.description-block{display:block;margin:10px 0;text-align:center}.description-block.margin-bottom{margin-bottom:25px}.description-block>.description-header{margin:0;padding:0;font-weight:600;font-size:16px}.description-block>.description-text{text-transform:uppercase}.bg-red,.bg-yellow,.bg-aqua,.bg-blue,.bg-light-blue,.bg-green,.bg-navy,.bg-teal,.bg-olive,.bg-lime,.bg-orange,.bg-fuchsia,.bg-purple,.bg-maroon,.bg-black,.bg-red-active,.bg-yellow-active,.bg-aqua-active,.bg-blue-active,.bg-light-blue-active,.bg-green-active,.bg-navy-active,.bg-teal-active,.bg-olive-active,.bg-lime-active,.bg-orange-active,.bg-fuchsia-active,.bg-purple-active,.bg-maroon-active,.bg-black-active,.callout.callout-danger,.callout.callout-warning,.callout.callout-info,.callout.callout-success,.label-danger,.label-info,.label-warning,.label-primary,.label-success,.modal-primary .modal-body,.modal-primary .modal-header,.modal-primary .modal-footer,.modal-warning .modal-body,.modal-warning .modal-header,.modal-warning .modal-footer,.modal-info .modal-body,.modal-info .modal-header,.modal-info .modal-footer,.modal-success .modal-body,.modal-success .modal-header,.modal-success .modal-footer,.modal-danger .modal-body,.modal-danger .modal-header,.modal-danger .modal-footer{color:#fff !important}.bg-gray{color:#000;background-color:#d2d6de !important}.bg-gray-light{background-color:#f7f7f7}.bg-black{background-color:#111 !important}.bg-red,.callout.callout-danger,.label-danger,.modal-danger .modal-body{background-color:#dd4b39 !important}.bg-yellow,.callout.callout-warning,.alert-warning,.label-warning,.modal-warning .modal-body{background-color:#f39c12 !important}.bg-aqua,.callout.callout-info,.label-info,.modal-info .modal-body{background-color:#00c0ef !important}.bg-blue{background-color:#0073b7 !important}.bg-light-blue,.label-primary,.modal-primary .modal-body{background-color:#3c8dbc !important}.bg-green,.callout.callout-success,.label-success,.modal-success .modal-body{background-color:#00a65a !important}.bg-navy{background-color:#001F3F !important}.bg-teal{background-color:#39CCCC !important}.bg-olive{background-color:#3D9970 !important}.bg-lime{background-color:#01FF70 !important}.bg-orange{background-color:#FF851B !important}.bg-fuchsia{background-color:#F012BE !important}.bg-purple{background-color:#605ca8 !important}.bg-maroon{background-color:#D81B60 !important}.bg-gray-active{color:#000;background-color:#b5bbc8 !important}.bg-black-active{background-color:#000 !important}.bg-red-active,.modal-danger .modal-header,.modal-danger .modal-footer{background-color:#d33724 !important}.bg-yellow-active,.modal-warning .modal-header,.modal-warning .modal-footer{background-color:#db8b0b !important}.bg-aqua-active,.modal-info .modal-header,.modal-info .modal-footer{background-color:#00a7d0 !important}.bg-blue-active{background-color:#005384 !important}.bg-light-blue-active,.modal-primary .modal-header,.modal-primary .modal-footer{background-color:#357ca5 !important}.bg-green-active,.modal-success .modal-header,.modal-success .modal-footer{background-color:#008d4c !important}.bg-navy-active{background-color:#001a35 !important}.bg-teal-active{background-color:#30bbbb !important}.bg-olive-active{background-color:#368763 !important}.bg-lime-active{background-color:#00e765 !important}.bg-orange-active{background-color:#ff7701 !important}.bg-fuchsia-active{background-color:#db0ead !important}.bg-purple-active{background-color:#555299 !important}.bg-maroon-active{background-color:#ca195a !important}[class^="bg-"].disabled{opacity:.65;filter:alpha(opacity=65)}.text-red{color:#dd4b39 !important}.text-yellow{color:#f39c12 !important}.text-aqua{color:#00c0ef !important}.text-blue{color:#0073b7 !important}.text-black{color:#111 !important}.text-light-blue{color:#3c8dbc !important}.text-green{color:#00a65a !important}.text-gray{color:#d2d6de !important}.text-navy{color:#001F3F !important}.text-teal{color:#39CCCC !important}.text-olive{color:#3D9970 !important}.text-lime{color:#01FF70 !important}.text-orange{color:#FF851B !important}.text-fuchsia{color:#F012BE !important}.text-purple{color:#605ca8 !important}.text-maroon{color:#D81B60 !important}.link-muted{color:#7a869d}.link-muted:hover,.link-muted:focus{color:#606c84}.link-black{color:#666}.link-black:hover,.link-black:focus{color:#999}.hide{display:none !important}.no-border{border:0 !important}.no-padding{padding:0 !important}.no-margin{margin:0 !important}.no-shadow{box-shadow:none !important}.list-unstyled,.chart-legend,.contacts-list,.users-list,.mailbox-attachments{list-style:none;margin:0;padding:0}.list-group-unbordered>.list-group-item{border-left:0;border-right:0;border-radius:0;padding-left:0;padding-right:0}.flat{border-radius:0 !important}.text-bold,.text-bold.table td,.text-bold.table th{font-weight:700}.text-sm{font-size:12px}.jqstooltip{padding:5px !important;width:auto !important;height:auto !important}.bg-teal-gradient{background:#39CCCC !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #39CCCC), color-stop(1, #7adddd)) !important;background:-ms-linear-gradient(bottom, #39CCCC, #7adddd) !important;background:-moz-linear-gradient(center bottom, #39CCCC 0%, #7adddd 100%) !important;background:-o-linear-gradient(#7adddd, #39CCCC) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39CCCC', GradientType=0) !important;color:#fff}.bg-light-blue-gradient{background:#3c8dbc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important;background:-ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important;background:-moz-linear-gradient(center bottom, #3c8dbc 0%, #67a8ce 100%) !important;background:-o-linear-gradient(#67a8ce, #3c8dbc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important;color:#fff}.bg-blue-gradient{background:#0073b7 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important;background:-ms-linear-gradient(bottom, #0073b7, #0089db) !important;background:-moz-linear-gradient(center bottom, #0073b7 0%, #0089db 100%) !important;background:-o-linear-gradient(#0089db, #0073b7) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important;color:#fff}.bg-aqua-gradient{background:#00c0ef !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important;background:-ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important;background:-moz-linear-gradient(center bottom, #00c0ef 0%, #14d1ff 100%) !important;background:-o-linear-gradient(#14d1ff, #00c0ef) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important;color:#fff}.bg-yellow-gradient{background:#f39c12 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important;background:-ms-linear-gradient(bottom, #f39c12, #f7bc60) !important;background:-moz-linear-gradient(center bottom, #f39c12 0%, #f7bc60 100%) !important;background:-o-linear-gradient(#f7bc60, #f39c12) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important;color:#fff}.bg-purple-gradient{background:#605ca8 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #605ca8), color-stop(1, #9491c4)) !important;background:-ms-linear-gradient(bottom, #605ca8, #9491c4) !important;background:-moz-linear-gradient(center bottom, #605ca8 0%, #9491c4 100%) !important;background:-o-linear-gradient(#9491c4, #605ca8) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9491c4', endColorstr='#605ca8', GradientType=0) !important;color:#fff}.bg-green-gradient{background:#00a65a !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important;background:-ms-linear-gradient(bottom, #00a65a, #00ca6d) !important;background:-moz-linear-gradient(center bottom, #00a65a 0%, #00ca6d 100%) !important;background:-o-linear-gradient(#00ca6d, #00a65a) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important;color:#fff}.bg-red-gradient{background:#dd4b39 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #dd4b39), color-stop(1, #e47365)) !important;background:-ms-linear-gradient(bottom, #dd4b39, #e47365) !important;background:-moz-linear-gradient(center bottom, #dd4b39 0%, #e47365 100%) !important;background:-o-linear-gradient(#e47365, #dd4b39) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e47365', endColorstr='#dd4b39', GradientType=0) !important;color:#fff}.bg-black-gradient{background:#111 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #111), color-stop(1, #2b2b2b)) !important;background:-ms-linear-gradient(bottom, #111, #2b2b2b) !important;background:-moz-linear-gradient(center bottom, #111 0%, #2b2b2b 100%) !important;background:-o-linear-gradient(#2b2b2b, #111) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2b2b2b', endColorstr='#111', GradientType=0) !important;color:#fff}.bg-maroon-gradient{background:#D81B60 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #D81B60), color-stop(1, #e73f7c)) !important;background:-ms-linear-gradient(bottom, #D81B60, #e73f7c) !important;background:-moz-linear-gradient(center bottom, #D81B60 0%, #e73f7c 100%) !important;background:-o-linear-gradient(#e73f7c, #D81B60) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e73f7c', endColorstr='#D81B60', GradientType=0) !important;color:#fff}.description-block .description-icon{font-size:16px}.no-pad-top{padding-top:0}.position-static{position:static !important}.list-header{font-size:15px;padding:10px 4px;font-weight:bold;color:#666}.list-seperator{height:1px;background:#f4f4f4;margin:15px 0 9px 0}.list-link>a{padding:4px;color:#777}.list-link>a:hover{color:#222}.font-light{font-weight:300}.user-block:before,.user-block:after{content:" ";display:table}.user-block:after{clear:both}.user-block img{width:40px;height:40px;float:left}.user-block .username,.user-block .description,.user-block .comment{display:block;margin-left:50px}.user-block .username{font-size:16px;font-weight:600}.user-block .description{color:#999;font-size:13px}.user-block.user-block-sm .username,.user-block.user-block-sm .description,.user-block.user-block-sm .comment{margin-left:40px}.user-block.user-block-sm .username{font-size:14px}.img-sm,.img-md,.img-lg,.box-comments .box-comment img,.user-block.user-block-sm img{float:left}.img-sm,.box-comments .box-comment img,.user-block.user-block-sm img{width:30px !important;height:30px !important}.img-sm+.img-push{margin-left:40px}.img-md{width:60px;height:60px}.img-md+.img-push{margin-left:70px}.img-lg{width:100px;height:100px}.img-lg+.img-push{margin-left:110px}.img-bordered{border:3px solid #d2d6de;padding:3px}.img-bordered-sm{border:2px solid #d2d6de;padding:2px}.attachment-block{border:1px solid #f4f4f4;padding:5px;margin-bottom:10px;background:#f7f7f7}.attachment-block .attachment-img{max-width:100px;max-height:100px;height:auto;float:left}.attachment-block .attachment-pushed{margin-left:110px}.attachment-block .attachment-heading{margin:0}.attachment-block .attachment-text{color:#555}.connectedSortable{min-height:100px}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sort-highlight{background:#f4f4f4;border:1px dashed #ddd;margin-bottom:10px}.full-opacity-hover{opacity:.65;filter:alpha(opacity=65)}.full-opacity-hover:hover{opacity:1;filter:alpha(opacity=100)}.chart{position:relative;overflow:hidden;width:100%}.chart svg,.chart canvas{width:100% !important}hr{border-top:1px solid #555}#red .slider-selection{background:#f56954}#blue .slider-selection{background:#3c8dbc}#green .slider-selection{background:#00a65a}#yellow .slider-selection{background:#f39c12}#aqua .slider-selection{background:#00c0ef}#purple .slider-selection{background:#932ab6}@media print{.no-print,.main-sidebar,.left-side,.main-header,.content-header{display:none !important}.content-wrapper,.right-side,.main-footer{margin-left:0 !important;min-height:0 !important;-webkit-transform:translate(0, 0) !important;-ms-transform:translate(0, 0) !important;-o-transform:translate(0, 0) !important;transform:translate(0, 0) !important}.fixed .content-wrapper,.fixed .right-side{padding-top:0 !important}.invoice{width:100%;border:0;margin:0;padding:0}.invoice-col{float:left;width:33.3333333%}.table-responsive{overflow:auto}.table-responsive>.table tr th,.table-responsive>.table tr td{white-space:normal !important}}
+.m-0{margin-top:0!important;margin-right:0!important;margin-bottom:0!important;margin-left:0!important}.mt-0{margin-top:0!important}.mr-0{margin-right:0!important}.mb-0{margin-bottom:0!important}.ml-0{margin-left:0!important}.mx-0{margin-left:0!important;margin-right:0!important}.my-0{margin-top:0!important;margin-bottom:0!important}.m-1{margin-top:5px!important;margin-right:5px!important;margin-bottom:5px!important;margin-left:5px!important}.mt-1{margin-top:5px!important}.mr-1{margin-right:5px!important}.mb-1{margin-bottom:5px!important}.ml-1{margin-left:5px!important}.mx-1{margin-left:5px!important;margin-right:5px!important}.my-1{margin-top:5px!important;margin-bottom:5px!important}.m-2{margin-top:10px!important;margin-right:10px!important;margin-bottom:10px!important;margin-left:10px!important}.mt-2{margin-top:10px!important}.mr-2{margin-right:10px!important}.mb-2{margin-bottom:10px!important}.ml-2{margin-left:10px!important}.mx-2{margin-left:10px!important;margin-right:10px!important}.my-2{margin-top:10px!important;margin-bottom:10px!important}.m-3{margin-top:15px!important;margin-right:15px!important;margin-bottom:15px!important;margin-left:15px!important}.mt-3{margin-top:15px!important}.mr-3{margin-right:15px!important}.mb-3{margin-bottom:15px!important}.ml-3{margin-left:15px!important}.mx-3{margin-left:15px!important;margin-right:15px!important}.my-3{margin-top:15px!important;margin-bottom:15px!important}.m-4{margin-top:20px!important;margin-right:20px!important;margin-bottom:20px!important;margin-left:20px!important}.mt-4{margin-top:20px!important}.mr-4{margin-right:20px!important}.mb-4{margin-bottom:20px!important}.ml-4{margin-left:20px!important}.mx-4{margin-left:20px!important;margin-right:20px!important}.my-4{margin-top:20px!important;margin-bottom:20px!important}.p-0{padding-top:0!important;padding-right:0!important;padding-bottom:0!important;padding-left:0!important}.pt-0{padding-top:0!important}.pr-0{padding-right:0!important}.pb-0{padding-bottom:0!important}.pl-0{padding-left:0!important}.px-0{padding-left:0!important;padding-right:0!important}.py-0{padding-top:0!important;padding-bottom:0!important}.p-1{padding-top:5px!important;padding-right:5px!important;padding-bottom:5px!important;padding-left:5px!important}.pt-1{padding-top:5px!important}.pr-1{padding-right:5px!important}.pb-1{padding-bottom:5px!important}.pl-1{padding-left:5px!important}.px-1{padding-left:5px!important;padding-right:5px!important}.py-1{padding-top:5px!important;padding-bottom:5px!important}.p-2{padding-top:10px!important;padding-right:10px!important;padding-bottom:10px!important;padding-left:10px!important}.pt-2{padding-top:10px!important}.pr-2{padding-right:10px!important}.pb-2{padding-bottom:10px!important}.pl-2{padding-left:10px!important}.px-2{padding-left:10px!important;padding-right:10px!important}.py-2{padding-top:10px!important;padding-bottom:10px!important}.p-3{padding-top:15px!important;padding-right:15px!important;padding-bottom:15px!important;padding-left:15px!important}.pt-3{padding-top:15px!important}.pr-3{padding-right:15px!important}.pb-3{padding-bottom:15px!important}.pl-3{padding-left:15px!important}.px-3{padding-left:15px!important;padding-right:15px!important}.py-3{padding-top:15px!important;padding-bottom:15px!important}.p-4{padding-top:20px!important;padding-right:20px!important;padding-bottom:20px!important;padding-left:20px!important}.pt-4{padding-top:20px!important}.pr-4{padding-right:20px!important}.pb-4{padding-bottom:20px!important}.pl-4{padding-left:20px!important}.px-4{padding-left:20px!important;padding-right:20px!important}.py-4{padding-top:20px!important;padding-bottom:20px!important}body{line-height:1.5715}.wrapper{height:100%}.content-wrapper{height:100%}.tab-addtabs .tab-pane{height:100%;width:100%}.row-between .col-xs-6+.col-xs-6:before{content:"-";position:absolute;left:-2%;top:6px}.navbar{height:50px;border-bottom:1px solid transparent;box-shadow:0 1px 4px rgba(0,21,41,.08)}.navbar .sidebar-toggle{line-height:1.42857143}.navbar .navbar-custom-menu{position:absolute;top:0;right:0;z-index:99;background:0 0}@media only screen and (min-width:481px){.row-flex{display:flex;flex-wrap:wrap}.row-flex>[class*=col-]{display:flex;flex-direction:column}.row-flex.row:after,.row-flex.row:before{display:flex}}@media (max-width:480px){.navbar-custom-menu ul li a{padding-left:10px;padding-right:10px}.navbar-nav>.user-menu .user-image{margin-top:-3px}}@media (max-width:767px){.wrapper .main-header .logo{border-bottom:0 solid transparent;position:absolute;top:0;z-index:1200;width:200px;left:50%;margin-left:-120px}.wrapper .main-header .navbar .dropdown-menu li>a{color:#333}.wrapper .main-header .navbar .dropdown-menu li>a:hover{background:#eee}.wrapper .main-header .navbar .dropdown-menu li.active>a{color:#fff}.wrapper .main-header .navbar .dropdown-menu li.active>a:hover{background:#222d32}.left-side,.main-sidebar{padding-top:50px}}@media (max-width:991px){.main-header .navbar-custom-menu a.btn-danger{color:#fff;background-color:#f75444}.main-header .navbar-custom-menu a.btn-primary{color:#fff;background-color:#444c69}}.sidebar-menu li.active>a>.fa-angle-left,.sidebar-menu li.active>a>.pull-right-container>.fa-angle-left{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}.sidebar-menu li.menu-open>a>.fa-angle-left,.sidebar-menu li.menu-open>a>.pull-right-container>.fa-angle-left{-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-o-transform:rotate(-90deg);-ms-transform:rotate(-90deg);transform:rotate(-90deg)}.sidebar-menu .treeview-menu>li{margin:4px 0 4px 0}.panel-intro{margin-bottom:0;border:none}.panel-intro>.panel-heading{padding:15px;padding-bottom:0;background:#e8edf0;border-color:#e8edf0;position:relative}.panel-intro>.panel-heading .panel-lead{margin-bottom:15px}.panel-intro>.panel-heading .panel-lead em{display:block;font-weight:700;font-style:normal}.panel-intro>.panel-heading .panel-title{height:25px;font-weight:400;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.panel-intro>.panel-heading .panel-control{height:42px;position:absolute;top:8px;right:8px}.panel-intro>.panel-heading .panel-control .fa{font-size:14px}.panel-intro>.panel-heading .nav-tabs{border-bottom:0;margin-bottom:0}.panel-intro>.panel-heading .nav-tabs>li>a{margin-right:4px;color:#95a5a6;background-color:#d8e0e6;border:1px solid #e8edf0;border-bottom-color:transparent}.panel-intro>.panel-heading .nav-tabs>li>a:focus,.panel-intro>.panel-heading .nav-tabs>li>a:hover{border:1px solid #e8edf0;color:#7b8a8b;background-color:#c9d4dc}.panel-intro>.panel-heading .nav-tabs>li.active>a,.panel-intro>.panel-heading .nav-tabs>li.active>a:focus,.panel-intro>.panel-heading .nav-tabs>li.active>a:hover{color:#7b8a8b;background-color:#fff;border-bottom-color:transparent;cursor:default}@media (max-width:768px){.panel-intro>.panel-heading .nav-tabs{white-space:nowrap;overflow-x:auto;overflow-y:hidden;margin-bottom:-1px}.panel-intro>.panel-heading .nav-tabs>li{display:inline-block;float:none}}.skin-list li{float:left;width:33.33333%;padding:5px}.skin-list li a{display:block;box-shadow:0 0 3px rgba(0,0,0,.4)}.skin-list li a span{display:block;float:left}.skin-list li.active a{opacity:1}.skin-list li.active p{color:#fff}.small-box .icon{font-size:80px}.sidebar-menu .treeview-menu:after,.sidebar-menu .treeview-menu:before{content:"";display:table}
diff --git a/public/static/css/bootstrap-table.css b/public/static/css/bootstrap-table.css
new file mode 100644
index 0000000..f7beca8
--- /dev/null
+++ b/public/static/css/bootstrap-table.css
@@ -0,0 +1,7 @@
+.bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.4286}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==")}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ")}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;width:auto!important;text-align:left!important}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value{width:100%!important;text-align:left!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:flex;justify-content:center;position:absolute;bottom:0;width:100%;max-width:100%;z-index:1000;transition:visibility 0s,opacity .15s ease-in-out;opacity:0;visibility:hidden}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open{visibility:visible;opacity:1}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:loading;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:"\2B05"}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:"\27A1"}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}.bootstrap-table.bootstrap4 .pagination-lg .page-link,.bootstrap-table.bootstrap5 .pagination-lg .page-link{padding:.5rem 1rem}.bootstrap-table.bootstrap5 .float-left{float:left}.bootstrap-table.bootstrap5 .float-right{float:right}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes loading{0%{opacity:0}50%{opacity:1}to{opacity:0}}
+.table-bottom-border{border-bottom: 2px solid #ddd;}
+@media screen and (max-width:767px){.fixed-table-body{border:1px solid #ddd}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{white-space:nowrap}.columns-right{display:none;}}
+.bootstrap-table.bootstrap3 .fixed-table-pagination>.pagination .page-jump-to{display:inline-block}.bootstrap-table.bootstrap3 .fixed-table-pagination>.pagination .input-group-btn{width:auto}.bootstrap-table .fixed-table-pagination>.pagination .page-jump-to input{width:70px;margin-left:5px;text-align:center;float:left}
+.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}
+.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}
+.bv-form .help-block{margin-bottom:0}.bv-form .tooltip-inner{text-align:left}.nav-tabs li.bv-tab-success>a{color:#3c763d}.nav-tabs li.bv-tab-error>a{color:#a94442}.bv-form .bv-icon-no-label{top:0}.bv-form .bv-icon-input-group{top:0;z-index:100}
diff --git a/public/static/css/select2.min.css b/public/static/css/select2.min.css
new file mode 100644
index 0000000..dc2315a
--- /dev/null
+++ b/public/static/css/select2.min.css
@@ -0,0 +1 @@
+.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
diff --git a/public/static/css/skins/skin-black-blue.css b/public/static/css/skins/skin-black-blue.css
new file mode 100644
index 0000000..d3042d0
--- /dev/null
+++ b/public/static/css/skins/skin-black-blue.css
@@ -0,0 +1,242 @@
+/*
+ * Skin: Black blue
+ * -----------
+ */
+.skin-black-blue .main-header {
+ background: #222d32;
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-blue .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black-blue .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black-blue .main-header .navbar .nav > li > a:hover,
+.skin-black-blue .main-header .navbar .nav > li > a:active,
+.skin-black-blue .main-header .navbar .nav > li > a:focus,
+.skin-black-blue .main-header .navbar .nav .open > a,
+.skin-black-blue .main-header .navbar .nav .open > a:hover,
+.skin-black-blue .main-header .navbar .nav .open > a:focus,
+.skin-black-blue .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #333;
+}
+.skin-black-blue .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #333;
+}
+.skin-black-blue .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black-blue .main-header .navbar .sidebar-toggle:hover {
+ color: #333;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black-blue .main-header .navbar .navbar-nav > li > a {
+ border-right: none;
+}
+.skin-black-blue .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-blue .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+@media (max-width: 767px) {
+ .skin-black-blue .main-header .navbar {
+ background-color: #181f23;
+ }
+ .skin-black-blue .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black-blue .main-header .navbar .nav > li > a:hover,
+ .skin-black-blue .main-header .navbar .nav > li > a:active,
+ .skin-black-blue .main-header .navbar .nav > li > a:focus,
+ .skin-black-blue .main-header .navbar .nav .open > a,
+ .skin-black-blue .main-header .navbar .nav .open > a:hover,
+ .skin-black-blue .main-header .navbar .nav .open > a:focus,
+ .skin-black-blue .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black-blue .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black-blue .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black-blue .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+}
+.skin-black-blue .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black-blue .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-blue .main-header .logo {
+ background-color: #181f23;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-blue .main-header .logo:hover {
+ background-color: #161d20;
+ }
+}
+.skin-black-blue .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-blue .main-header .nav-addtabs > li > a,
+.skin-black-blue .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black-blue .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-blue .wrapper,
+.skin-black-blue .main-sidebar,
+.skin-black-blue .left-side {
+ background-color: #222d32;
+}
+.skin-black-blue .user-panel > .info,
+.skin-black-blue .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black-blue .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black-blue .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black-blue .sidebar-menu > li:hover > a,
+.skin-black-blue .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black-blue .sidebar-menu > li > .treeview-menu {
+ background: #181f23;
+}
+.skin-black-blue .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black-blue .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-blue .treeview-menu > li > a {
+ color: #6c8c9b;
+}
+.skin-black-blue .treeview-menu > li.active > a,
+.skin-black-blue .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black-blue .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black-blue .sidebar-form input[type="text"],
+.skin-black-blue .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-blue .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-blue .sidebar-form input[type="text"]:focus,
+.skin-black-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-blue .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-blue .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-blue .treeview-menu > li > a {
+ padding-left: 18px;
+}
+.skin-black-blue .treeview-menu > li.active > a {
+ background-color: #4e73df;
+}
+.skin-black-blue .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #4e73df;
+ border-left-color: #4e73df;
+}
+.skin-black-blue .sidebar-menu > li:hover > a {
+ border-left-color: transparent;
+}
+.skin-black-blue .sidebar-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-blue .sidebar-menu li.treeview.active > a,
+.skin-black-blue .sidebar-menu li.treeview.treeview-open > a {
+ background-color: #181f23;
+ border-left-color: #181f23;
+}
+.skin-black-blue .sidebar-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-blue .sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-blue .sidebar-menu .treeview-menu .treeview-menu > li > a {
+ padding-left: 30px;
+}
+.skin-black-blue .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-blue.sidebar-collapse .sidebar-menu li:hover > a,
+.skin-black-blue.sidebar-collapse .sidebar-menu li.active > a {
+ color: #fff;
+ background: #4e73df;
+}
+.skin-black-blue.sidebar-collapse .sidebar-menu .treeview-menu li.active > a {
+ color: #fff;
+ background: #4e73df;
+}
+.skin-black-blue.sidebar-collapse .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+@media (max-width: 767px) {
+ .skin-black-blue.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black-blue.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #4e73df;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-blue.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black-green.css b/public/static/css/skins/skin-black-green.css
new file mode 100644
index 0000000..ba82498
--- /dev/null
+++ b/public/static/css/skins/skin-black-green.css
@@ -0,0 +1,242 @@
+/*
+ * Skin: Black green
+ * -----------
+ */
+.skin-black-green .main-header {
+ background: #222d32;
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-green .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black-green .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black-green .main-header .navbar .nav > li > a:hover,
+.skin-black-green .main-header .navbar .nav > li > a:active,
+.skin-black-green .main-header .navbar .nav > li > a:focus,
+.skin-black-green .main-header .navbar .nav .open > a,
+.skin-black-green .main-header .navbar .nav .open > a:hover,
+.skin-black-green .main-header .navbar .nav .open > a:focus,
+.skin-black-green .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #333;
+}
+.skin-black-green .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #333;
+}
+.skin-black-green .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black-green .main-header .navbar .sidebar-toggle:hover {
+ color: #333;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black-green .main-header .navbar .navbar-nav > li > a {
+ border-right: none;
+}
+.skin-black-green .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-green .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+@media (max-width: 767px) {
+ .skin-black-green .main-header .navbar {
+ background-color: #181f23;
+ }
+ .skin-black-green .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black-green .main-header .navbar .nav > li > a:hover,
+ .skin-black-green .main-header .navbar .nav > li > a:active,
+ .skin-black-green .main-header .navbar .nav > li > a:focus,
+ .skin-black-green .main-header .navbar .nav .open > a,
+ .skin-black-green .main-header .navbar .nav .open > a:hover,
+ .skin-black-green .main-header .navbar .nav .open > a:focus,
+ .skin-black-green .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black-green .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black-green .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black-green .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+}
+.skin-black-green .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black-green .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-green .main-header .logo {
+ background-color: #181f23;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-green .main-header .logo:hover {
+ background-color: #161d20;
+ }
+}
+.skin-black-green .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-green .main-header .nav-addtabs > li > a,
+.skin-black-green .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black-green .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-green .wrapper,
+.skin-black-green .main-sidebar,
+.skin-black-green .left-side {
+ background-color: #222d32;
+}
+.skin-black-green .user-panel > .info,
+.skin-black-green .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black-green .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black-green .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black-green .sidebar-menu > li:hover > a,
+.skin-black-green .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black-green .sidebar-menu > li > .treeview-menu {
+ background: #181f23;
+}
+.skin-black-green .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black-green .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-green .treeview-menu > li > a {
+ color: #6c8c9b;
+}
+.skin-black-green .treeview-menu > li.active > a,
+.skin-black-green .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black-green .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black-green .sidebar-form input[type="text"],
+.skin-black-green .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-green .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-green .sidebar-form input[type="text"]:focus,
+.skin-black-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-green .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-green .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-green .treeview-menu > li > a {
+ padding-left: 18px;
+}
+.skin-black-green .treeview-menu > li.active > a {
+ background-color: #18bc9c;
+}
+.skin-black-green .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #18bc9c;
+ border-left-color: #18bc9c;
+}
+.skin-black-green .sidebar-menu > li:hover > a {
+ border-left-color: transparent;
+}
+.skin-black-green .sidebar-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-green .sidebar-menu li.treeview.active > a,
+.skin-black-green .sidebar-menu li.treeview.treeview-open > a {
+ background-color: #181f23;
+ border-left-color: #181f23;
+}
+.skin-black-green .sidebar-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-green .sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-green .sidebar-menu .treeview-menu .treeview-menu > li > a {
+ padding-left: 30px;
+}
+.skin-black-green .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-green.sidebar-collapse .sidebar-menu li:hover > a,
+.skin-black-green.sidebar-collapse .sidebar-menu li.active > a {
+ color: #fff;
+ background: #18bc9c;
+}
+.skin-black-green.sidebar-collapse .sidebar-menu .treeview-menu li.active > a {
+ color: #fff;
+ background: #18bc9c;
+}
+.skin-black-green.sidebar-collapse .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+@media (max-width: 767px) {
+ .skin-black-green.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black-green.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #18bc9c;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-green.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black-light.css b/public/static/css/skins/skin-black-light.css
new file mode 100644
index 0000000..67dd410
--- /dev/null
+++ b/public/static/css/skins/skin-black-light.css
@@ -0,0 +1,192 @@
+/*
+ * Skin: Black light
+ * -----------
+ */
+.skin-black-light .main-header {
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-light .main-header .navbar-toggle {
+ color: #333;
+}
+.skin-black-light .main-header .navbar-brand {
+ color: #333;
+ border-right: 1px solid #eee;
+}
+.skin-black-light .main-header .navbar {
+ background-color: #222d32;
+}
+.skin-black-light .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-black-light .main-header .navbar .nav > li > a:hover,
+.skin-black-light .main-header .navbar .nav > li > a:active,
+.skin-black-light .main-header .navbar .nav > li > a:focus,
+.skin-black-light .main-header .navbar .nav .open > a,
+.skin-black-light .main-header .navbar .nav .open > a:hover,
+.skin-black-light .main-header .navbar .nav .open > a:focus,
+.skin-black-light .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.3);
+ color: #f6f6f6;
+}
+.skin-black-light .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-black-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-black-light .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.3);
+}
+.skin-black-light .main-header .navbar .navbar-nav > li > a {
+ color: #fff;
+}
+.skin-black-light .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-light .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+.skin-black-light .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-black-light .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-light .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-light .main-header .logo:hover {
+ background-color: #202a2f;
+ }
+}
+.skin-black-light .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-light .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-light .wrapper,
+.skin-black-light .main-sidebar,
+.skin-black-light .left-side {
+ background-color: #f9fafc;
+}
+.skin-black-light .content-wrapper,
+.skin-black-light .main-footer {
+ border-left: 1px solid #d2d6de;
+}
+.skin-black-light .user-panel > .info,
+.skin-black-light .user-panel > .info > a {
+ color: #444;
+}
+.skin-black-light .sidebar-menu > li {
+ -webkit-transition: border-left-color 0.3s ease;
+ -o-transition: border-left-color 0.3s ease;
+ transition: border-left-color 0.3s ease;
+}
+.skin-black-light .sidebar-menu > li.header {
+ color: #848484;
+ background: #f9fafc;
+}
+.skin-black-light .sidebar-menu > li:hover > a,
+.skin-black-light .sidebar-menu > li.active > a {
+ color: #000;
+ background: #f4f4f5;
+ border-left-color: #222d32;
+}
+.skin-black-light .sidebar-menu > li.active {
+ border-left-color: #222d32;
+}
+.skin-black-light .sidebar-menu > li > .treeview-menu {
+ background: #f4f4f5;
+}
+.skin-black-light .sidebar a {
+ color: #444;
+}
+.skin-black-light .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-light .treeview-menu > li > a {
+ color: #777;
+}
+.skin-black-light .treeview-menu > li.active > a,
+.skin-black-light .treeview-menu > li > a:hover {
+ color: #000;
+}
+.skin-black-light .treeview-menu > li.active > a {
+ font-weight: 600;
+}
+.skin-black-light .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #d2d6de;
+ margin: 10px 10px;
+}
+.skin-black-light .sidebar-form input[type="text"],
+.skin-black-light .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #fff;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-light .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-light .sidebar-form input[type="text"]:focus,
+.skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-light .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+@media (min-width: 768px) {
+ .skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ border-left: 1px solid #d2d6de;
+ }
+}
+.skin-black-light .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-black-light .main-sidebar {
+ -webkit-box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+ box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+}
+.skin-black-light .content-wrapper,
+.skin-black-light .main-footer {
+ border-left: none;
+}
+@media (max-width: 767px) {
+ .skin-black-light.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #eceff3;
+ color: #757575;
+ }
+ .skin-black-light.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #222d32;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-light.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black-pink.css b/public/static/css/skins/skin-black-pink.css
new file mode 100644
index 0000000..a9b2e5e
--- /dev/null
+++ b/public/static/css/skins/skin-black-pink.css
@@ -0,0 +1,242 @@
+/*
+ * Skin: Black pink
+ * -----------
+ */
+.skin-black-pink .main-header {
+ background: #222d32;
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-pink .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black-pink .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black-pink .main-header .navbar .nav > li > a:hover,
+.skin-black-pink .main-header .navbar .nav > li > a:active,
+.skin-black-pink .main-header .navbar .nav > li > a:focus,
+.skin-black-pink .main-header .navbar .nav .open > a,
+.skin-black-pink .main-header .navbar .nav .open > a:hover,
+.skin-black-pink .main-header .navbar .nav .open > a:focus,
+.skin-black-pink .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #333;
+}
+.skin-black-pink .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #333;
+}
+.skin-black-pink .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black-pink .main-header .navbar .sidebar-toggle:hover {
+ color: #333;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black-pink .main-header .navbar .navbar-nav > li > a {
+ border-right: none;
+}
+.skin-black-pink .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-pink .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+@media (max-width: 767px) {
+ .skin-black-pink .main-header .navbar {
+ background-color: #181f23;
+ }
+ .skin-black-pink .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black-pink .main-header .navbar .nav > li > a:hover,
+ .skin-black-pink .main-header .navbar .nav > li > a:active,
+ .skin-black-pink .main-header .navbar .nav > li > a:focus,
+ .skin-black-pink .main-header .navbar .nav .open > a,
+ .skin-black-pink .main-header .navbar .nav .open > a:hover,
+ .skin-black-pink .main-header .navbar .nav .open > a:focus,
+ .skin-black-pink .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black-pink .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black-pink .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black-pink .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+}
+.skin-black-pink .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black-pink .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-pink .main-header .logo {
+ background-color: #181f23;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-pink .main-header .logo:hover {
+ background-color: #161d20;
+ }
+}
+.skin-black-pink .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-pink .main-header .nav-addtabs > li > a,
+.skin-black-pink .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black-pink .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-pink .wrapper,
+.skin-black-pink .main-sidebar,
+.skin-black-pink .left-side {
+ background-color: #222d32;
+}
+.skin-black-pink .user-panel > .info,
+.skin-black-pink .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black-pink .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black-pink .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black-pink .sidebar-menu > li:hover > a,
+.skin-black-pink .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black-pink .sidebar-menu > li > .treeview-menu {
+ background: #181f23;
+}
+.skin-black-pink .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black-pink .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-pink .treeview-menu > li > a {
+ color: #6c8c9b;
+}
+.skin-black-pink .treeview-menu > li.active > a,
+.skin-black-pink .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black-pink .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black-pink .sidebar-form input[type="text"],
+.skin-black-pink .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-pink .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-pink .sidebar-form input[type="text"]:focus,
+.skin-black-pink .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-pink .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-pink .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-pink .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-pink .treeview-menu > li > a {
+ padding-left: 18px;
+}
+.skin-black-pink .treeview-menu > li.active > a {
+ background-color: #f5549f;
+}
+.skin-black-pink .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #f5549f;
+ border-left-color: #f5549f;
+}
+.skin-black-pink .sidebar-menu > li:hover > a {
+ border-left-color: transparent;
+}
+.skin-black-pink .sidebar-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-pink .sidebar-menu li.treeview.active > a,
+.skin-black-pink .sidebar-menu li.treeview.treeview-open > a {
+ background-color: #181f23;
+ border-left-color: #181f23;
+}
+.skin-black-pink .sidebar-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-pink .sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-pink .sidebar-menu .treeview-menu .treeview-menu > li > a {
+ padding-left: 30px;
+}
+.skin-black-pink .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-pink.sidebar-collapse .sidebar-menu li:hover > a,
+.skin-black-pink.sidebar-collapse .sidebar-menu li.active > a {
+ color: #fff;
+ background: #f5549f;
+}
+.skin-black-pink.sidebar-collapse .sidebar-menu .treeview-menu li.active > a {
+ color: #fff;
+ background: #f5549f;
+}
+.skin-black-pink.sidebar-collapse .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+@media (max-width: 767px) {
+ .skin-black-pink.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black-pink.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f5549f;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-pink.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black-purple.css b/public/static/css/skins/skin-black-purple.css
new file mode 100644
index 0000000..f81d581
--- /dev/null
+++ b/public/static/css/skins/skin-black-purple.css
@@ -0,0 +1,242 @@
+/*
+ * Skin: Black purple
+ * -----------
+ */
+.skin-black-purple .main-header {
+ background: #222d32;
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-purple .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black-purple .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black-purple .main-header .navbar .nav > li > a:hover,
+.skin-black-purple .main-header .navbar .nav > li > a:active,
+.skin-black-purple .main-header .navbar .nav > li > a:focus,
+.skin-black-purple .main-header .navbar .nav .open > a,
+.skin-black-purple .main-header .navbar .nav .open > a:hover,
+.skin-black-purple .main-header .navbar .nav .open > a:focus,
+.skin-black-purple .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #333;
+}
+.skin-black-purple .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #333;
+}
+.skin-black-purple .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black-purple .main-header .navbar .sidebar-toggle:hover {
+ color: #333;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black-purple .main-header .navbar .navbar-nav > li > a {
+ border-right: none;
+}
+.skin-black-purple .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-purple .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+@media (max-width: 767px) {
+ .skin-black-purple .main-header .navbar {
+ background-color: #181f23;
+ }
+ .skin-black-purple .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black-purple .main-header .navbar .nav > li > a:hover,
+ .skin-black-purple .main-header .navbar .nav > li > a:active,
+ .skin-black-purple .main-header .navbar .nav > li > a:focus,
+ .skin-black-purple .main-header .navbar .nav .open > a,
+ .skin-black-purple .main-header .navbar .nav .open > a:hover,
+ .skin-black-purple .main-header .navbar .nav .open > a:focus,
+ .skin-black-purple .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black-purple .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black-purple .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black-purple .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+}
+.skin-black-purple .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black-purple .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-purple .main-header .logo {
+ background-color: #181f23;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-purple .main-header .logo:hover {
+ background-color: #161d20;
+ }
+}
+.skin-black-purple .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-purple .main-header .nav-addtabs > li > a,
+.skin-black-purple .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black-purple .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-purple .wrapper,
+.skin-black-purple .main-sidebar,
+.skin-black-purple .left-side {
+ background-color: #222d32;
+}
+.skin-black-purple .user-panel > .info,
+.skin-black-purple .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black-purple .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black-purple .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black-purple .sidebar-menu > li:hover > a,
+.skin-black-purple .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black-purple .sidebar-menu > li > .treeview-menu {
+ background: #181f23;
+}
+.skin-black-purple .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black-purple .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-purple .treeview-menu > li > a {
+ color: #6c8c9b;
+}
+.skin-black-purple .treeview-menu > li.active > a,
+.skin-black-purple .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black-purple .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black-purple .sidebar-form input[type="text"],
+.skin-black-purple .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-purple .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-purple .sidebar-form input[type="text"]:focus,
+.skin-black-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-purple .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-purple .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-purple .treeview-menu > li > a {
+ padding-left: 18px;
+}
+.skin-black-purple .treeview-menu > li.active > a {
+ background-color: #605ca8;
+}
+.skin-black-purple .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #605ca8;
+ border-left-color: #605ca8;
+}
+.skin-black-purple .sidebar-menu > li:hover > a {
+ border-left-color: transparent;
+}
+.skin-black-purple .sidebar-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-purple .sidebar-menu li.treeview.active > a,
+.skin-black-purple .sidebar-menu li.treeview.treeview-open > a {
+ background-color: #181f23;
+ border-left-color: #181f23;
+}
+.skin-black-purple .sidebar-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-purple .sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-purple .sidebar-menu .treeview-menu .treeview-menu > li > a {
+ padding-left: 30px;
+}
+.skin-black-purple .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-purple.sidebar-collapse .sidebar-menu li:hover > a,
+.skin-black-purple.sidebar-collapse .sidebar-menu li.active > a {
+ color: #fff;
+ background: #605ca8;
+}
+.skin-black-purple.sidebar-collapse .sidebar-menu .treeview-menu li.active > a {
+ color: #fff;
+ background: #605ca8;
+}
+.skin-black-purple.sidebar-collapse .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+@media (max-width: 767px) {
+ .skin-black-purple.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black-purple.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #605ca8;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-purple.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black-red.css b/public/static/css/skins/skin-black-red.css
new file mode 100644
index 0000000..ef2ec20
--- /dev/null
+++ b/public/static/css/skins/skin-black-red.css
@@ -0,0 +1,242 @@
+/*
+ * Skin: Black red
+ * -----------
+ */
+.skin-black-red .main-header {
+ background: #222d32;
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-red .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black-red .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black-red .main-header .navbar .nav > li > a:hover,
+.skin-black-red .main-header .navbar .nav > li > a:active,
+.skin-black-red .main-header .navbar .nav > li > a:focus,
+.skin-black-red .main-header .navbar .nav .open > a,
+.skin-black-red .main-header .navbar .nav .open > a:hover,
+.skin-black-red .main-header .navbar .nav .open > a:focus,
+.skin-black-red .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #333;
+}
+.skin-black-red .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #333;
+}
+.skin-black-red .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black-red .main-header .navbar .sidebar-toggle:hover {
+ color: #333;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black-red .main-header .navbar .navbar-nav > li > a {
+ border-right: none;
+}
+.skin-black-red .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-red .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+@media (max-width: 767px) {
+ .skin-black-red .main-header .navbar {
+ background-color: #181f23;
+ }
+ .skin-black-red .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black-red .main-header .navbar .nav > li > a:hover,
+ .skin-black-red .main-header .navbar .nav > li > a:active,
+ .skin-black-red .main-header .navbar .nav > li > a:focus,
+ .skin-black-red .main-header .navbar .nav .open > a,
+ .skin-black-red .main-header .navbar .nav .open > a:hover,
+ .skin-black-red .main-header .navbar .nav .open > a:focus,
+ .skin-black-red .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black-red .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black-red .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black-red .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+}
+.skin-black-red .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black-red .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-red .main-header .logo {
+ background-color: #181f23;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-red .main-header .logo:hover {
+ background-color: #161d20;
+ }
+}
+.skin-black-red .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-red .main-header .nav-addtabs > li > a,
+.skin-black-red .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black-red .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-red .wrapper,
+.skin-black-red .main-sidebar,
+.skin-black-red .left-side {
+ background-color: #222d32;
+}
+.skin-black-red .user-panel > .info,
+.skin-black-red .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black-red .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black-red .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black-red .sidebar-menu > li:hover > a,
+.skin-black-red .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black-red .sidebar-menu > li > .treeview-menu {
+ background: #181f23;
+}
+.skin-black-red .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black-red .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-red .treeview-menu > li > a {
+ color: #6c8c9b;
+}
+.skin-black-red .treeview-menu > li.active > a,
+.skin-black-red .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black-red .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black-red .sidebar-form input[type="text"],
+.skin-black-red .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-red .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-red .sidebar-form input[type="text"]:focus,
+.skin-black-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-red .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-red .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-red .treeview-menu > li > a {
+ padding-left: 18px;
+}
+.skin-black-red .treeview-menu > li.active > a {
+ background-color: #f75444;
+}
+.skin-black-red .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #f75444;
+ border-left-color: #f75444;
+}
+.skin-black-red .sidebar-menu > li:hover > a {
+ border-left-color: transparent;
+}
+.skin-black-red .sidebar-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-red .sidebar-menu li.treeview.active > a,
+.skin-black-red .sidebar-menu li.treeview.treeview-open > a {
+ background-color: #181f23;
+ border-left-color: #181f23;
+}
+.skin-black-red .sidebar-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-red .sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-red .sidebar-menu .treeview-menu .treeview-menu > li > a {
+ padding-left: 30px;
+}
+.skin-black-red .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-red.sidebar-collapse .sidebar-menu li:hover > a,
+.skin-black-red.sidebar-collapse .sidebar-menu li.active > a {
+ color: #fff;
+ background: #f75444;
+}
+.skin-black-red.sidebar-collapse .sidebar-menu .treeview-menu li.active > a {
+ color: #fff;
+ background: #f75444;
+}
+.skin-black-red.sidebar-collapse .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+@media (max-width: 767px) {
+ .skin-black-red.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black-red.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f75444;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-red.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black-yellow.css b/public/static/css/skins/skin-black-yellow.css
new file mode 100644
index 0000000..0c02df8
--- /dev/null
+++ b/public/static/css/skins/skin-black-yellow.css
@@ -0,0 +1,242 @@
+/*
+ * Skin: Black yellow
+ * -----------
+ */
+.skin-black-yellow .main-header {
+ background: #222d32;
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black-yellow .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black-yellow .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black-yellow .main-header .navbar .nav > li > a:hover,
+.skin-black-yellow .main-header .navbar .nav > li > a:active,
+.skin-black-yellow .main-header .navbar .nav > li > a:focus,
+.skin-black-yellow .main-header .navbar .nav .open > a,
+.skin-black-yellow .main-header .navbar .nav .open > a:hover,
+.skin-black-yellow .main-header .navbar .nav .open > a:focus,
+.skin-black-yellow .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #333;
+}
+.skin-black-yellow .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #333;
+}
+.skin-black-yellow .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black-yellow .main-header .navbar .sidebar-toggle:hover {
+ color: #333;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black-yellow .main-header .navbar .navbar-nav > li > a {
+ border-right: none;
+}
+.skin-black-yellow .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black-yellow .main-header .navbar .navbar-right > li > a {
+ border-left: none;
+ border-right-width: 0;
+}
+@media (max-width: 767px) {
+ .skin-black-yellow .main-header .navbar {
+ background-color: #181f23;
+ }
+ .skin-black-yellow .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black-yellow .main-header .navbar .nav > li > a:hover,
+ .skin-black-yellow .main-header .navbar .nav > li > a:active,
+ .skin-black-yellow .main-header .navbar .nav > li > a:focus,
+ .skin-black-yellow .main-header .navbar .nav .open > a,
+ .skin-black-yellow .main-header .navbar .nav .open > a:hover,
+ .skin-black-yellow .main-header .navbar .nav .open > a:focus,
+ .skin-black-yellow .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black-yellow .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black-yellow .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black-yellow .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+}
+.skin-black-yellow .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black-yellow .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black-yellow .main-header .logo {
+ background-color: #181f23;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black-yellow .main-header .logo:hover {
+ background-color: #161d20;
+ }
+}
+.skin-black-yellow .main-header li.user-header {
+ background-color: #222d32;
+}
+.skin-black-yellow .main-header .nav-addtabs > li > a,
+.skin-black-yellow .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black-yellow .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black-yellow .wrapper,
+.skin-black-yellow .main-sidebar,
+.skin-black-yellow .left-side {
+ background-color: #222d32;
+}
+.skin-black-yellow .user-panel > .info,
+.skin-black-yellow .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black-yellow .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black-yellow .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black-yellow .sidebar-menu > li:hover > a,
+.skin-black-yellow .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black-yellow .sidebar-menu > li > .treeview-menu {
+ background: #181f23;
+}
+.skin-black-yellow .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black-yellow .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black-yellow .treeview-menu > li > a {
+ color: #6c8c9b;
+}
+.skin-black-yellow .treeview-menu > li.active > a,
+.skin-black-yellow .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black-yellow .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black-yellow .sidebar-form input[type="text"],
+.skin-black-yellow .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black-yellow .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black-yellow .sidebar-form input[type="text"]:focus,
+.skin-black-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black-yellow .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black-yellow .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black-yellow .treeview-menu > li > a {
+ padding-left: 18px;
+}
+.skin-black-yellow .treeview-menu > li.active > a {
+ background-color: #f39c12;
+}
+.skin-black-yellow .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #f39c12;
+ border-left-color: #f39c12;
+}
+.skin-black-yellow .sidebar-menu > li:hover > a {
+ border-left-color: transparent;
+}
+.skin-black-yellow .sidebar-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-yellow .sidebar-menu li.treeview.active > a,
+.skin-black-yellow .sidebar-menu li.treeview.treeview-open > a {
+ background-color: #181f23;
+ border-left-color: #181f23;
+}
+.skin-black-yellow .sidebar-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-yellow .sidebar-menu .treeview-menu .treeview-menu {
+ padding-left: 0;
+}
+.skin-black-yellow .sidebar-menu .treeview-menu .treeview-menu > li > a {
+ padding-left: 30px;
+}
+.skin-black-yellow .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+.skin-black-yellow.sidebar-collapse .sidebar-menu li:hover > a,
+.skin-black-yellow.sidebar-collapse .sidebar-menu li.active > a {
+ color: #fff;
+ background: #f39c12;
+}
+.skin-black-yellow.sidebar-collapse .sidebar-menu .treeview-menu li.active > a {
+ color: #fff;
+ background: #f39c12;
+}
+.skin-black-yellow.sidebar-collapse .sidebar-menu .treeview-menu li.treeview > a {
+ background: transparent;
+ border-left-color: transparent;
+}
+@media (max-width: 767px) {
+ .skin-black-yellow.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black-yellow.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f39c12;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-black-yellow.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-black.css b/public/static/css/skins/skin-black.css
new file mode 100644
index 0000000..ea95165
--- /dev/null
+++ b/public/static/css/skins/skin-black.css
@@ -0,0 +1,219 @@
+/*
+ * Skin: Black
+ * -----------
+ */
+.skin-black .main-header {
+ -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05);
+}
+.skin-black .main-header .navbar-toggle {
+ color: #333;
+}
+.skin-black .main-header .navbar-brand {
+ color: #333;
+ border-right: 1px solid #eee;
+}
+.skin-black .main-header .navbar {
+ background-color: #fff;
+}
+.skin-black .main-header .navbar .nav > li > a {
+ color: #666;
+}
+.skin-black .main-header .navbar .nav > li > a:hover,
+.skin-black .main-header .navbar .nav > li > a:active,
+.skin-black .main-header .navbar .nav > li > a:focus,
+.skin-black .main-header .navbar .nav .open > a,
+.skin-black .main-header .navbar .nav .open > a:hover,
+.skin-black .main-header .navbar .nav .open > a:focus,
+.skin-black .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #444;
+}
+.skin-black .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #444;
+}
+.skin-black .main-header .navbar .sidebar-toggle {
+ color: #666;
+}
+.skin-black .main-header .navbar .sidebar-toggle:hover {
+ color: #444;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-black .main-header .navbar > .sidebar-toggle {
+ color: #333;
+ border-right: 1px solid #eee;
+}
+.skin-black .main-header .navbar .navbar-nav > li > a {
+ border-right: 1px solid #eee;
+}
+.skin-black .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-black .main-header .navbar .navbar-right > li > a {
+ border-left: 1px solid #eee;
+ border-left: none;
+ border-right-width: 0;
+}
+.skin-black .main-header .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #222d32;
+}
+.skin-black .main-header .logo:hover {
+ background-color: #202a2f;
+}
+@media (max-width: 767px) {
+ .skin-black .main-header .logo {
+ background-color: #fff;
+ color: #222;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-black .main-header .logo:hover {
+ background-color: #fcfcfc;
+ }
+}
+.skin-black .main-header li.user-header {
+ background-color: #222;
+}
+.skin-black .main-header .nav-addtabs > li > a,
+.skin-black .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-black .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-black .wrapper,
+.skin-black .main-sidebar,
+.skin-black .left-side {
+ background-color: #222d32;
+}
+.skin-black .user-panel > .info,
+.skin-black .user-panel > .info > a {
+ color: #fff;
+}
+.skin-black .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-black .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-black .sidebar-menu > li:hover > a,
+.skin-black .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #fff;
+}
+.skin-black .sidebar-menu > li > .treeview-menu {
+ background: #1c2529;
+}
+.skin-black .sidebar a {
+ color: #b8c7ce;
+}
+.skin-black .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-black .treeview-menu > li > a {
+ color: #72919f;
+}
+.skin-black .treeview-menu > li.active > a,
+.skin-black .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-black .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-black .sidebar-form input[type="text"],
+.skin-black .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-black .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-black .sidebar-form input[type="text"]:focus,
+.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-black .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-black .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-black.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+@media (max-width: 767px) {
+ .skin-black.multiplenav .main-header .navbar {
+ background-color: #222d32;
+ }
+ .skin-black.multiplenav .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-black.multiplenav .main-header .navbar .nav > li > a:hover,
+ .skin-black.multiplenav .main-header .navbar .nav > li > a:active,
+ .skin-black.multiplenav .main-header .navbar .nav > li > a:focus,
+ .skin-black.multiplenav .main-header .navbar .nav .open > a,
+ .skin-black.multiplenav .main-header .navbar .nav .open > a:hover,
+ .skin-black.multiplenav .main-header .navbar .nav .open > a:focus,
+ .skin-black.multiplenav .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-black.multiplenav .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-black.multiplenav .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-black.multiplenav .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+ .skin-black.multiplenav .main-header > .logo {
+ background-color: #222d32;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ }
+ .skin-black.multiplenav .main-header > .logo:hover {
+ background-color: #202a2f;
+ }
+ .skin-black.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-black.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #fff;
+ color: #374850;
+ }
+}
+/*# sourceMappingURL=skin-black.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-blue-light.css b/public/static/css/skins/skin-blue-light.css
new file mode 100644
index 0000000..4fa2473
--- /dev/null
+++ b/public/static/css/skins/skin-blue-light.css
@@ -0,0 +1,184 @@
+/*
+ * Skin: Blue
+ * ----------
+ */
+.skin-blue-light .main-header {
+ background-color: #4e73df;
+}
+.skin-blue-light .main-header .navbar {
+ background-color: #4e73df;
+}
+.skin-blue-light .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-blue-light .main-header .navbar .nav > li > a:hover,
+.skin-blue-light .main-header .navbar .nav > li > a:active,
+.skin-blue-light .main-header .navbar .nav > li > a:focus,
+.skin-blue-light .main-header .navbar .nav .open > a,
+.skin-blue-light .main-header .navbar .nav .open > a:hover,
+.skin-blue-light .main-header .navbar .nav .open > a:focus,
+.skin-blue-light .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-blue-light .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-blue-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-blue-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-blue-light .main-header .navbar .sidebar-toggle:hover {
+ background-color: #3862db;
+}
+@media (max-width: 767px) {
+ .skin-blue-light .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-blue-light .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-blue-light .main-header .navbar .dropdown-menu li a:hover {
+ background: #3862db;
+ }
+}
+.skin-blue-light .main-header .logo {
+ background-color: #4e73df;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-blue-light .main-header .logo:hover {
+ background-color: #4a70de;
+}
+.skin-blue-light .main-header li.user-header {
+ background-color: #4e73df;
+}
+.skin-blue-light .content-header {
+ background: transparent;
+}
+.skin-blue-light .wrapper,
+.skin-blue-light .main-sidebar,
+.skin-blue-light .left-side {
+ background-color: #f9fafc;
+}
+.skin-blue-light .content-wrapper,
+.skin-blue-light .main-footer {
+ border-left: 1px solid #d2d6de;
+}
+.skin-blue-light .user-panel > .info,
+.skin-blue-light .user-panel > .info > a {
+ color: #444;
+}
+.skin-blue-light .sidebar-menu > li {
+ -webkit-transition: border-left-color 0.3s ease;
+ -o-transition: border-left-color 0.3s ease;
+ transition: border-left-color 0.3s ease;
+}
+.skin-blue-light .sidebar-menu > li.header {
+ color: #848484;
+ background: #f9fafc;
+}
+.skin-blue-light .sidebar-menu > li:hover > a,
+.skin-blue-light .sidebar-menu > li.active > a {
+ color: #000;
+ background: #f4f4f5;
+ border-left-color: #4e73df;
+}
+.skin-blue-light .sidebar-menu > li.active {
+ border-left-color: #4e73df;
+}
+.skin-blue-light .sidebar-menu > li > .treeview-menu {
+ background: #f4f4f5;
+}
+.skin-blue-light .sidebar a {
+ color: #444;
+}
+.skin-blue-light .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-blue-light .treeview-menu > li > a {
+ color: #777;
+}
+.skin-blue-light .treeview-menu > li.active > a,
+.skin-blue-light .treeview-menu > li > a:hover {
+ color: #000;
+}
+.skin-blue-light .treeview-menu > li.active > a {
+ font-weight: 600;
+}
+.skin-blue-light .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #d2d6de;
+ margin: 10px 10px;
+}
+.skin-blue-light .sidebar-form input[type="text"],
+.skin-blue-light .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #fff;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-blue-light .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-blue-light .sidebar-form input[type="text"]:focus,
+.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-blue-light .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+@media (min-width: 768px) {
+ .skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ border-left: 1px solid #d2d6de;
+ }
+}
+.skin-blue-light .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-blue-light .main-footer {
+ border-top-color: #d2d6de;
+}
+.skin-blue-light .main-sidebar {
+ -webkit-box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+ box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+}
+.skin-blue-light .content-wrapper,
+.skin-blue-light .main-footer {
+ border-left: none;
+}
+@media (max-width: 767px) {
+ .skin-blue-light.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #eceff3;
+ color: #757575;
+ }
+ .skin-blue-light.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #4e73df;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-blue-light.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-blue.css b/public/static/css/skins/skin-blue.css
new file mode 100644
index 0000000..1df808a
--- /dev/null
+++ b/public/static/css/skins/skin-blue.css
@@ -0,0 +1,230 @@
+/*
+ * Skin: Blue
+ * -----------
+ */
+.skin-blue .main-header .navbar-toggle {
+ color: #333;
+}
+.skin-blue .main-header .navbar-brand {
+ color: #333;
+ border-right: 1px solid #eee;
+}
+.skin-blue .main-header .navbar {
+ background-color: #fff;
+}
+.skin-blue .main-header .navbar .nav > li > a {
+ color: #444;
+}
+.skin-blue .main-header .navbar .nav > li > a:hover,
+.skin-blue .main-header .navbar .nav > li > a:active,
+.skin-blue .main-header .navbar .nav > li > a:focus,
+.skin-blue .main-header .navbar .nav .open > a,
+.skin-blue .main-header .navbar .nav .open > a:hover,
+.skin-blue .main-header .navbar .nav .open > a:focus,
+.skin-blue .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #4e73df;
+}
+.skin-blue .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #4e73df;
+}
+.skin-blue .main-header .navbar .sidebar-toggle {
+ color: #444;
+}
+.skin-blue .main-header .navbar .sidebar-toggle:hover {
+ color: #4e73df;
+ background: rgba(0, 0, 0, 0.02);
+}
+.skin-blue .main-header .navbar > .sidebar-toggle {
+ color: #333;
+ border-right: 1px solid #eee;
+}
+.skin-blue .main-header .navbar .navbar-nav > li > a {
+ border-right: 1px solid #eee;
+}
+.skin-blue .main-header .navbar .navbar-custom-menu .navbar-nav > li > a,
+.skin-blue .main-header .navbar .navbar-right > li > a {
+ border-left: 1px solid #eee;
+ border-left: none;
+ border-right-width: 0;
+}
+.skin-blue .main-header > .logo {
+ background-color: #4e73df;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #4e73df;
+ box-shadow: none;
+}
+.skin-blue .main-header > .logo:hover {
+ background-color: #4a70de;
+}
+@media (max-width: 767px) {
+ .skin-blue .main-header > .logo {
+ background-color: #fff;
+ color: #222;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-blue .main-header > .logo:hover {
+ background-color: #fcfcfc;
+ }
+}
+.skin-blue .main-header li.user-header {
+ background-color: #4e73df;
+}
+.skin-blue .main-header .nav-addtabs > li > a,
+.skin-blue .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-blue .content-header {
+ background: transparent;
+ box-shadow: none;
+}
+.skin-blue .wrapper,
+.skin-blue .main-sidebar,
+.skin-blue .left-side {
+ background-color: #4e73df;
+}
+.skin-blue .user-panel > .info,
+.skin-blue .user-panel > .info > a {
+ color: #fff;
+}
+.skin-blue .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-blue .sidebar-menu > li.header {
+ color: #a4b7ef;
+ background: #3d65dc;
+}
+.skin-blue .sidebar-menu > li:hover > a,
+.skin-blue .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #456cdd;
+ border-left-color: #fff;
+}
+.skin-blue .sidebar-menu > li > .treeview-menu {
+ background: #4169dd;
+}
+.skin-blue .sidebar a {
+ color: #ccd9ff;
+}
+.skin-blue .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-blue .treeview-menu > li > a {
+ color: #ccd9ff;
+}
+.skin-blue .treeview-menu > li.active > a,
+.skin-blue .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-blue .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #7995e7;
+ background-color: #7995e7;
+ margin: 10px 10px;
+}
+.skin-blue .sidebar-form input[type="text"],
+.skin-blue .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #7995e7;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-blue .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-blue .sidebar-form input[type="text"]:focus,
+.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-blue .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-blue .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-blue.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-blue .sidebar-form input[type="text"]::-moz-placeholder {
+ color: #fff;
+ opacity: 1;
+}
+.skin-blue .sidebar-form input[type="text"]:-ms-input-placeholder {
+ color: #fff;
+}
+.skin-blue .sidebar-form input[type="text"]::-webkit-input-placeholder {
+ color: #fff;
+}
+.skin-blue .sidebar-form input[type="text"],
+.skin-blue .sidebar-form .btn {
+ color: #fff;
+}
+@media (max-width: 767px) {
+ .skin-blue.multiplenav .main-header .navbar {
+ background-color: #4e73df;
+ }
+ .skin-blue.multiplenav .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-blue.multiplenav .main-header .navbar .nav > li > a:hover,
+ .skin-blue.multiplenav .main-header .navbar .nav > li > a:active,
+ .skin-blue.multiplenav .main-header .navbar .nav > li > a:focus,
+ .skin-blue.multiplenav .main-header .navbar .nav .open > a,
+ .skin-blue.multiplenav .main-header .navbar .nav .open > a:hover,
+ .skin-blue.multiplenav .main-header .navbar .nav .open > a:focus,
+ .skin-blue.multiplenav .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-blue.multiplenav .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-blue.multiplenav .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-blue.multiplenav .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+ .skin-blue.multiplenav .main-header > .logo {
+ background-color: #4e73df;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ }
+ .skin-blue.multiplenav .main-header > .logo:hover {
+ background-color: #4a70de;
+ }
+ .skin-blue.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #7995e7;
+ color: #fff;
+ }
+ .skin-blue.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #fff;
+ color: #7995e7;
+ }
+}
+/*# sourceMappingURL=skin-blue.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-green-light.css b/public/static/css/skins/skin-green-light.css
new file mode 100644
index 0000000..bc4c87e
--- /dev/null
+++ b/public/static/css/skins/skin-green-light.css
@@ -0,0 +1,181 @@
+/*
+ * Skin: Green
+ * -----------
+ */
+.skin-green-light .main-header {
+ background-color: #18bc9c;
+}
+.skin-green-light .main-header .navbar {
+ background-color: #18bc9c;
+}
+.skin-green-light .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-green-light .main-header .navbar .nav > li > a:hover,
+.skin-green-light .main-header .navbar .nav > li > a:active,
+.skin-green-light .main-header .navbar .nav > li > a:focus,
+.skin-green-light .main-header .navbar .nav .open > a,
+.skin-green-light .main-header .navbar .nav .open > a:hover,
+.skin-green-light .main-header .navbar .nav .open > a:focus,
+.skin-green-light .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-green-light .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-green-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-green-light .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-green-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-green-light .main-header .navbar .sidebar-toggle:hover {
+ background-color: #15a589;
+}
+@media (max-width: 767px) {
+ .skin-green-light .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-green-light .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-green-light .main-header .navbar .dropdown-menu li a:hover {
+ background: #15a589;
+ }
+}
+.skin-green-light .main-header .logo {
+ background-color: #18bc9c;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-green-light .main-header .logo:hover {
+ background-color: #17b798;
+}
+.skin-green-light .main-header li.user-header {
+ background-color: #18bc9c;
+}
+.skin-green-light .content-header {
+ background: transparent;
+}
+.skin-green-light .wrapper,
+.skin-green-light .main-sidebar,
+.skin-green-light .left-side {
+ background-color: #f9fafc;
+}
+.skin-green-light .content-wrapper,
+.skin-green-light .main-footer {
+ border-left: 1px solid #d2d6de;
+}
+.skin-green-light .user-panel > .info,
+.skin-green-light .user-panel > .info > a {
+ color: #444;
+}
+.skin-green-light .sidebar-menu > li {
+ -webkit-transition: border-left-color 0.3s ease;
+ -o-transition: border-left-color 0.3s ease;
+ transition: border-left-color 0.3s ease;
+}
+.skin-green-light .sidebar-menu > li.header {
+ color: #848484;
+ background: #f9fafc;
+}
+.skin-green-light .sidebar-menu > li:hover > a,
+.skin-green-light .sidebar-menu > li.active > a {
+ color: #000;
+ background: #f4f4f5;
+ border-left-color: #18bc9c;
+}
+.skin-green-light .sidebar-menu > li.active {
+ border-left-color: #18bc9c;
+}
+.skin-green-light .sidebar-menu > li > .treeview-menu {
+ background: #f4f4f5;
+}
+.skin-green-light .sidebar a {
+ color: #444;
+}
+.skin-green-light .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-green-light .treeview-menu > li > a {
+ color: #777;
+}
+.skin-green-light .treeview-menu > li.active > a,
+.skin-green-light .treeview-menu > li > a:hover {
+ color: #000;
+}
+.skin-green-light .treeview-menu > li.active > a {
+ font-weight: 600;
+}
+.skin-green-light .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #d2d6de;
+ margin: 10px 10px;
+}
+.skin-green-light .sidebar-form input[type="text"],
+.skin-green-light .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #fff;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-green-light .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-green-light .sidebar-form input[type="text"]:focus,
+.skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-green-light .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+@media (min-width: 768px) {
+ .skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ border-left: 1px solid #d2d6de;
+ }
+}
+.skin-green-light .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-green-light .main-sidebar {
+ -webkit-box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+ box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+}
+.skin-green-light .content-wrapper,
+.skin-green-light .main-footer {
+ border-left: none;
+}
+@media (max-width: 767px) {
+ .skin-green-light.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #eceff3;
+ color: #757575;
+ }
+ .skin-green-light.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #18bc9c;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-green-light.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-green.css b/public/static/css/skins/skin-green.css
new file mode 100644
index 0000000..aedf03c
--- /dev/null
+++ b/public/static/css/skins/skin-green.css
@@ -0,0 +1,175 @@
+/*
+ * Skin: Green
+ * -----------
+ */
+.skin-green .main-header {
+ background-color: #18bc9c;
+}
+.skin-green .main-header .navbar {
+ background-color: #18bc9c;
+}
+.skin-green .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-green .main-header .navbar .nav > li > a:hover,
+.skin-green .main-header .navbar .nav > li > a:active,
+.skin-green .main-header .navbar .nav > li > a:focus,
+.skin-green .main-header .navbar .nav .open > a,
+.skin-green .main-header .navbar .nav .open > a:hover,
+.skin-green .main-header .navbar .nav .open > a:focus,
+.skin-green .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-green .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-green .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-green .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-green .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-green .main-header .navbar .sidebar-toggle:hover {
+ background-color: #15a589;
+}
+@media (max-width: 767px) {
+ .skin-green .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-green .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-green .main-header .navbar .dropdown-menu li a:hover {
+ background: #15a589;
+ }
+}
+.skin-green .main-header .logo {
+ background-color: #15a589;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-green .main-header .logo:hover {
+ background-color: #15a185;
+}
+@media (max-width: 767px) {
+ .skin-green .main-header .logo {
+ background-color: #18bc9c;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-green .main-header .logo:hover {
+ background-color: #17b798;
+ }
+}
+.skin-green .main-header li.user-header {
+ background-color: #18bc9c;
+}
+.skin-green .content-header {
+ background: transparent;
+}
+.skin-green .wrapper,
+.skin-green .main-sidebar,
+.skin-green .left-side {
+ background-color: #222d32;
+}
+.skin-green .user-panel > .info,
+.skin-green .user-panel > .info > a {
+ color: #fff;
+}
+.skin-green .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-green .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-green .sidebar-menu > li:hover > a,
+.skin-green .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #18bc9c;
+}
+.skin-green .sidebar-menu > li > .treeview-menu {
+ background: #2c3b41;
+}
+.skin-green .sidebar a {
+ color: #b8c7ce;
+}
+.skin-green .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-green .treeview-menu > li > a {
+ color: #8aa4af;
+}
+.skin-green .treeview-menu > li.active > a,
+.skin-green .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-green .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-green .sidebar-form input[type="text"],
+.skin-green .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-green .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-green .sidebar-form input[type="text"]:focus,
+.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-green .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-green .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-green.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+@media (max-width: 767px) {
+ .skin-green.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-green.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #18bc9c;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-green.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-purple-light.css b/public/static/css/skins/skin-purple-light.css
new file mode 100644
index 0000000..f243e9b
--- /dev/null
+++ b/public/static/css/skins/skin-purple-light.css
@@ -0,0 +1,181 @@
+/*
+ * Skin: Purple
+ * ------------
+ */
+.skin-purple-light .main-header {
+ background-color: #605ca8;
+}
+.skin-purple-light .main-header .navbar {
+ background-color: #605ca8;
+}
+.skin-purple-light .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-purple-light .main-header .navbar .nav > li > a:hover,
+.skin-purple-light .main-header .navbar .nav > li > a:active,
+.skin-purple-light .main-header .navbar .nav > li > a:focus,
+.skin-purple-light .main-header .navbar .nav .open > a,
+.skin-purple-light .main-header .navbar .nav .open > a:hover,
+.skin-purple-light .main-header .navbar .nav .open > a:focus,
+.skin-purple-light .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-purple-light .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-purple-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-purple-light .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-purple-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-purple-light .main-header .navbar .sidebar-toggle:hover {
+ background-color: #555299;
+}
+@media (max-width: 767px) {
+ .skin-purple-light .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-purple-light .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-purple-light .main-header .navbar .dropdown-menu li a:hover {
+ background: #555299;
+ }
+}
+.skin-purple-light .main-header .logo {
+ background-color: #605ca8;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-purple-light .main-header .logo:hover {
+ background-color: #5d59a6;
+}
+.skin-purple-light .main-header li.user-header {
+ background-color: #605ca8;
+}
+.skin-purple-light .content-header {
+ background: transparent;
+}
+.skin-purple-light .wrapper,
+.skin-purple-light .main-sidebar,
+.skin-purple-light .left-side {
+ background-color: #f9fafc;
+}
+.skin-purple-light .content-wrapper,
+.skin-purple-light .main-footer {
+ border-left: 1px solid #d2d6de;
+}
+.skin-purple-light .user-panel > .info,
+.skin-purple-light .user-panel > .info > a {
+ color: #444;
+}
+.skin-purple-light .sidebar-menu > li {
+ -webkit-transition: border-left-color 0.3s ease;
+ -o-transition: border-left-color 0.3s ease;
+ transition: border-left-color 0.3s ease;
+}
+.skin-purple-light .sidebar-menu > li.header {
+ color: #848484;
+ background: #f9fafc;
+}
+.skin-purple-light .sidebar-menu > li:hover > a,
+.skin-purple-light .sidebar-menu > li.active > a {
+ color: #000;
+ background: #f4f4f5;
+ border-left-color: #605ca8;
+}
+.skin-purple-light .sidebar-menu > li.active {
+ border-left-color: #605ca8;
+}
+.skin-purple-light .sidebar-menu > li > .treeview-menu {
+ background: #f4f4f5;
+}
+.skin-purple-light .sidebar a {
+ color: #444;
+}
+.skin-purple-light .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-purple-light .treeview-menu > li > a {
+ color: #777;
+}
+.skin-purple-light .treeview-menu > li.active > a,
+.skin-purple-light .treeview-menu > li > a:hover {
+ color: #000;
+}
+.skin-purple-light .treeview-menu > li.active > a {
+ font-weight: 600;
+}
+.skin-purple-light .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #d2d6de;
+ margin: 10px 10px;
+}
+.skin-purple-light .sidebar-form input[type="text"],
+.skin-purple-light .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #fff;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-purple-light .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-purple-light .sidebar-form input[type="text"]:focus,
+.skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-purple-light .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+@media (min-width: 768px) {
+ .skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ border-left: 1px solid #d2d6de;
+ }
+}
+.skin-purple-light .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-purple-light .main-sidebar {
+ -webkit-box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+ box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+}
+.skin-purple-light .content-wrapper,
+.skin-purple-light .main-footer {
+ border-left: none;
+}
+@media (max-width: 767px) {
+ .skin-purple-light.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #eceff3;
+ color: #757575;
+ }
+ .skin-purple-light.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #605ca8;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-purple-light.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-purple.css b/public/static/css/skins/skin-purple.css
new file mode 100644
index 0000000..493e167
--- /dev/null
+++ b/public/static/css/skins/skin-purple.css
@@ -0,0 +1,220 @@
+/*
+ * Skin: Purple
+ * ------------
+ */
+.skin-purple .main-header .navbar {
+ background-color: #fff;
+}
+.skin-purple .main-header .navbar .nav > li > a {
+ color: #444;
+}
+.skin-purple .main-header .navbar .nav > li > a:hover,
+.skin-purple .main-header .navbar .nav > li > a:active,
+.skin-purple .main-header .navbar .nav > li > a:focus,
+.skin-purple .main-header .navbar .nav .open > a,
+.skin-purple .main-header .navbar .nav .open > a:hover,
+.skin-purple .main-header .navbar .nav .open > a:focus,
+.skin-purple .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.02);
+ color: #605ca8;
+}
+.skin-purple .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #605ca8;
+}
+.skin-purple .main-header .navbar .sidebar-toggle {
+ color: #444;
+}
+.skin-purple .main-header .navbar .sidebar-toggle:hover {
+ color: #605ca8;
+ background: rgba(0, 0, 0, 0.02);
+}
+@media (max-width: 767px) {
+ .skin-purple .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-purple .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-purple .main-header .navbar .dropdown-menu li a:hover {
+ background: #555299;
+ }
+}
+.skin-purple .main-header > .logo {
+ background-color: #605ca8;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: 1px solid #605ca8;
+ box-shadow: none;
+}
+.skin-purple .main-header > .logo:hover {
+ background-color: #5d59a6;
+}
+@media (max-width: 767px) {
+ .skin-purple .main-header > .logo {
+ background-color: #fff;
+ color: #222;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-purple .main-header > .logo:hover {
+ background-color: #fcfcfc;
+ }
+}
+.skin-purple .main-header li.user-header {
+ background-color: #605ca8;
+}
+.skin-purple .main-header .nav-addtabs > li > a,
+.skin-purple .main-header .nav-addtabs > li.active > a {
+ border-right-color: transparent;
+}
+.skin-purple .content-header {
+ background: transparent;
+}
+.skin-purple .wrapper,
+.skin-purple .main-sidebar,
+.skin-purple .left-side {
+ background-color: #605ca8;
+}
+.skin-purple .user-panel > .info,
+.skin-purple .user-panel > .info > a {
+ color: #fff;
+}
+.skin-purple .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-purple .sidebar-menu > li.header {
+ color: #a19fcb;
+ background: #57539c;
+}
+.skin-purple .sidebar-menu > li:hover > a,
+.skin-purple .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #5b57a3;
+ border-left-color: #fff;
+}
+.skin-purple .sidebar-menu > li > .treeview-menu {
+ background: #5955a0;
+}
+.skin-purple .sidebar a {
+ color: #c8c5ff;
+}
+.skin-purple .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-purple .treeview-menu > li > a {
+ color: #c8c5ff;
+}
+.skin-purple .treeview-menu > li.active > a,
+.skin-purple .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-purple .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #807dba;
+ background-color: #807dba;
+ margin: 10px 10px;
+}
+.skin-purple .sidebar-form input[type="text"],
+.skin-purple .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #807dba;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-purple .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-purple .sidebar-form input[type="text"]:focus,
+.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-purple .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-purple .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-purple.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-purple .sidebar-form input[type="text"]::-moz-placeholder {
+ color: #fff;
+ opacity: 1;
+}
+.skin-purple .sidebar-form input[type="text"]:-ms-input-placeholder {
+ color: #fff;
+}
+.skin-purple .sidebar-form input[type="text"]::-webkit-input-placeholder {
+ color: #fff;
+}
+.skin-purple .sidebar-form input[type="text"],
+.skin-purple .sidebar-form .btn {
+ color: #fff;
+}
+@media (max-width: 767px) {
+ .skin-purple.multiplenav .main-header .navbar {
+ background-color: #605ca8;
+ }
+ .skin-purple.multiplenav .main-header .navbar .nav > li > a {
+ color: #fff;
+ }
+ .skin-purple.multiplenav .main-header .navbar .nav > li > a:hover,
+ .skin-purple.multiplenav .main-header .navbar .nav > li > a:active,
+ .skin-purple.multiplenav .main-header .navbar .nav > li > a:focus,
+ .skin-purple.multiplenav .main-header .navbar .nav .open > a,
+ .skin-purple.multiplenav .main-header .navbar .nav .open > a:hover,
+ .skin-purple.multiplenav .main-header .navbar .nav .open > a:focus,
+ .skin-purple.multiplenav .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.1);
+ color: #f6f6f6;
+ }
+ .skin-purple.multiplenav .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+ }
+ .skin-purple.multiplenav .main-header .navbar .sidebar-toggle {
+ color: #fff;
+ }
+ .skin-purple.multiplenav .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.1);
+ }
+ .skin-purple.multiplenav .main-header > .logo {
+ background-color: #605ca8;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ }
+ .skin-purple.multiplenav .main-header > .logo:hover {
+ background-color: #5d59a6;
+ }
+ .skin-purple.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #807dba;
+ color: #fff;
+ }
+ .skin-purple.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #fff;
+ color: #807dba;
+ }
+}
+/*# sourceMappingURL=skin-purple.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-red-light.css b/public/static/css/skins/skin-red-light.css
new file mode 100644
index 0000000..ca8ab75
--- /dev/null
+++ b/public/static/css/skins/skin-red-light.css
@@ -0,0 +1,181 @@
+/*
+ * Skin: Red
+ * ---------
+ */
+.skin-red-light .main-header {
+ background-color: #f75444;
+}
+.skin-red-light .main-header .navbar {
+ background-color: #f75444;
+}
+.skin-red-light .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-red-light .main-header .navbar .nav > li > a:hover,
+.skin-red-light .main-header .navbar .nav > li > a:active,
+.skin-red-light .main-header .navbar .nav > li > a:focus,
+.skin-red-light .main-header .navbar .nav .open > a,
+.skin-red-light .main-header .navbar .nav .open > a:hover,
+.skin-red-light .main-header .navbar .nav .open > a:focus,
+.skin-red-light .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-red-light .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-red-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-red-light .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-red-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-red-light .main-header .navbar .sidebar-toggle:hover {
+ background-color: #f63e2c;
+}
+@media (max-width: 767px) {
+ .skin-red-light .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-red-light .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-red-light .main-header .navbar .dropdown-menu li a:hover {
+ background: #f63e2c;
+ }
+}
+.skin-red-light .main-header .logo {
+ background-color: #f75444;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-red-light .main-header .logo:hover {
+ background-color: #f7503f;
+}
+.skin-red-light .main-header li.user-header {
+ background-color: #f75444;
+}
+.skin-red-light .content-header {
+ background: transparent;
+}
+.skin-red-light .wrapper,
+.skin-red-light .main-sidebar,
+.skin-red-light .left-side {
+ background-color: #f9fafc;
+}
+.skin-red-light .content-wrapper,
+.skin-red-light .main-footer {
+ border-left: 1px solid #d2d6de;
+}
+.skin-red-light .user-panel > .info,
+.skin-red-light .user-panel > .info > a {
+ color: #444;
+}
+.skin-red-light .sidebar-menu > li {
+ -webkit-transition: border-left-color 0.3s ease;
+ -o-transition: border-left-color 0.3s ease;
+ transition: border-left-color 0.3s ease;
+}
+.skin-red-light .sidebar-menu > li.header {
+ color: #848484;
+ background: #f9fafc;
+}
+.skin-red-light .sidebar-menu > li:hover > a,
+.skin-red-light .sidebar-menu > li.active > a {
+ color: #000;
+ background: #f4f4f5;
+ border-left-color: #f75444;
+}
+.skin-red-light .sidebar-menu > li.active {
+ border-left-color: #f75444;
+}
+.skin-red-light .sidebar-menu > li > .treeview-menu {
+ background: #f4f4f5;
+}
+.skin-red-light .sidebar a {
+ color: #444;
+}
+.skin-red-light .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-red-light .treeview-menu > li > a {
+ color: #777;
+}
+.skin-red-light .treeview-menu > li.active > a,
+.skin-red-light .treeview-menu > li > a:hover {
+ color: #000;
+}
+.skin-red-light .treeview-menu > li.active > a {
+ font-weight: 600;
+}
+.skin-red-light .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #d2d6de;
+ margin: 10px 10px;
+}
+.skin-red-light .sidebar-form input[type="text"],
+.skin-red-light .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #fff;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-red-light .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-red-light .sidebar-form input[type="text"]:focus,
+.skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-red-light .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+@media (min-width: 768px) {
+ .skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ border-left: 1px solid #d2d6de;
+ }
+}
+.skin-red-light .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-red-light .main-sidebar {
+ -webkit-box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+ box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+}
+.skin-red-light .content-wrapper,
+.skin-red-light .main-footer {
+ border-left: none;
+}
+@media (max-width: 767px) {
+ .skin-red-light.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #eceff3;
+ color: #757575;
+ }
+ .skin-red-light.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f75444;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-red-light.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-red.css b/public/static/css/skins/skin-red.css
new file mode 100644
index 0000000..2182b4e
--- /dev/null
+++ b/public/static/css/skins/skin-red.css
@@ -0,0 +1,175 @@
+/*
+ * Skin: Red
+ * ---------
+ */
+.skin-red .main-header {
+ background-color: #f75444;
+}
+.skin-red .main-header .navbar {
+ background-color: #f75444;
+}
+.skin-red .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-red .main-header .navbar .nav > li > a:hover,
+.skin-red .main-header .navbar .nav > li > a:active,
+.skin-red .main-header .navbar .nav > li > a:focus,
+.skin-red .main-header .navbar .nav .open > a,
+.skin-red .main-header .navbar .nav .open > a:hover,
+.skin-red .main-header .navbar .nav .open > a:focus,
+.skin-red .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-red .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-red .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-red .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-red .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-red .main-header .navbar .sidebar-toggle:hover {
+ background-color: #f63e2c;
+}
+@media (max-width: 767px) {
+ .skin-red .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-red .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-red .main-header .navbar .dropdown-menu li a:hover {
+ background: #f63e2c;
+ }
+}
+.skin-red .main-header .logo {
+ background-color: #f63e2c;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-red .main-header .logo:hover {
+ background-color: #f63927;
+}
+@media (max-width: 767px) {
+ .skin-red .main-header .logo {
+ background-color: #f75444;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-red .main-header .logo:hover {
+ background-color: #f7503f;
+ }
+}
+.skin-red .main-header li.user-header {
+ background-color: #f75444;
+}
+.skin-red .content-header {
+ background: transparent;
+}
+.skin-red .wrapper,
+.skin-red .main-sidebar,
+.skin-red .left-side {
+ background-color: #222d32;
+}
+.skin-red .user-panel > .info,
+.skin-red .user-panel > .info > a {
+ color: #fff;
+}
+.skin-red .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-red .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-red .sidebar-menu > li:hover > a,
+.skin-red .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #f75444;
+}
+.skin-red .sidebar-menu > li > .treeview-menu {
+ background: #2c3b41;
+}
+.skin-red .sidebar a {
+ color: #b8c7ce;
+}
+.skin-red .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-red .treeview-menu > li > a {
+ color: #8aa4af;
+}
+.skin-red .treeview-menu > li.active > a,
+.skin-red .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-red .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-red .sidebar-form input[type="text"],
+.skin-red .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-red .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-red .sidebar-form input[type="text"]:focus,
+.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-red .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-red .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-red.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+@media (max-width: 767px) {
+ .skin-red.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-red.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f75444;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-red.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-yellow-light.css b/public/static/css/skins/skin-yellow-light.css
new file mode 100644
index 0000000..41d572f
--- /dev/null
+++ b/public/static/css/skins/skin-yellow-light.css
@@ -0,0 +1,181 @@
+/*
+ * Skin: Yellow light
+ * ------------
+ */
+.skin-yellow-light .main-header {
+ background-color: #f39c12;
+}
+.skin-yellow-light .main-header .navbar {
+ background-color: #f39c12;
+}
+.skin-yellow-light .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-yellow-light .main-header .navbar .nav > li > a:hover,
+.skin-yellow-light .main-header .navbar .nav > li > a:active,
+.skin-yellow-light .main-header .navbar .nav > li > a:focus,
+.skin-yellow-light .main-header .navbar .nav .open > a,
+.skin-yellow-light .main-header .navbar .nav .open > a:hover,
+.skin-yellow-light .main-header .navbar .nav .open > a:focus,
+.skin-yellow-light .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-yellow-light .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-yellow-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-yellow-light .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-yellow-light .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-yellow-light .main-header .navbar .sidebar-toggle:hover {
+ background-color: #e08e0b;
+}
+@media (max-width: 767px) {
+ .skin-yellow-light .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-yellow-light .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-yellow-light .main-header .navbar .dropdown-menu li a:hover {
+ background: #e08e0b;
+ }
+}
+.skin-yellow-light .main-header .logo {
+ background-color: #f39c12;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-yellow-light .main-header .logo:hover {
+ background-color: #f39a0d;
+}
+.skin-yellow-light .main-header li.user-header {
+ background-color: #f39c12;
+}
+.skin-yellow-light .content-header {
+ background: transparent;
+}
+.skin-yellow-light .wrapper,
+.skin-yellow-light .main-sidebar,
+.skin-yellow-light .left-side {
+ background-color: #f9fafc;
+}
+.skin-yellow-light .content-wrapper,
+.skin-yellow-light .main-footer {
+ border-left: 1px solid #d2d6de;
+}
+.skin-yellow-light .user-panel > .info,
+.skin-yellow-light .user-panel > .info > a {
+ color: #444;
+}
+.skin-yellow-light .sidebar-menu > li {
+ -webkit-transition: border-left-color 0.3s ease;
+ -o-transition: border-left-color 0.3s ease;
+ transition: border-left-color 0.3s ease;
+}
+.skin-yellow-light .sidebar-menu > li.header {
+ color: #848484;
+ background: #f9fafc;
+}
+.skin-yellow-light .sidebar-menu > li:hover > a,
+.skin-yellow-light .sidebar-menu > li.active > a {
+ color: #000;
+ background: #f4f4f5;
+ border-left-color: #f39c12;
+}
+.skin-yellow-light .sidebar-menu > li.active {
+ border-left-color: #f39c12;
+}
+.skin-yellow-light .sidebar-menu > li > .treeview-menu {
+ background: #f4f4f5;
+}
+.skin-yellow-light .sidebar a {
+ color: #444;
+}
+.skin-yellow-light .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-yellow-light .treeview-menu > li > a {
+ color: #777;
+}
+.skin-yellow-light .treeview-menu > li.active > a,
+.skin-yellow-light .treeview-menu > li > a:hover {
+ color: #000;
+}
+.skin-yellow-light .treeview-menu > li.active > a {
+ font-weight: 600;
+}
+.skin-yellow-light .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #d2d6de;
+ margin: 10px 10px;
+}
+.skin-yellow-light .sidebar-form input[type="text"],
+.skin-yellow-light .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #fff;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-yellow-light .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-yellow-light .sidebar-form input[type="text"]:focus,
+.skin-yellow-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-yellow-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-yellow-light .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+@media (min-width: 768px) {
+ .skin-yellow-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu {
+ border-left: 1px solid #d2d6de;
+ }
+}
+.skin-yellow-light .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-yellow-light.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+.skin-yellow-light .main-sidebar {
+ -webkit-box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+ box-shadow: 7px 0 14px rgba(0, 0, 0, 0.03);
+}
+.skin-yellow-light .content-wrapper,
+.skin-yellow-light .main-footer {
+ border-left: none;
+}
+@media (max-width: 767px) {
+ .skin-yellow-light.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #eceff3;
+ color: #757575;
+ }
+ .skin-yellow-light.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f39c12;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-yellow-light.css.map */
\ No newline at end of file
diff --git a/public/static/css/skins/skin-yellow.css b/public/static/css/skins/skin-yellow.css
new file mode 100644
index 0000000..fed08cd
--- /dev/null
+++ b/public/static/css/skins/skin-yellow.css
@@ -0,0 +1,175 @@
+/*
+ * Skin: Yellow
+ * ------------
+ */
+.skin-yellow .main-header {
+ background-color: #f39c12;
+}
+.skin-yellow .main-header .navbar {
+ background-color: #f39c12;
+}
+.skin-yellow .main-header .navbar .nav > li > a {
+ color: #fff;
+}
+.skin-yellow .main-header .navbar .nav > li > a:hover,
+.skin-yellow .main-header .navbar .nav > li > a:active,
+.skin-yellow .main-header .navbar .nav > li > a:focus,
+.skin-yellow .main-header .navbar .nav .open > a,
+.skin-yellow .main-header .navbar .nav .open > a:hover,
+.skin-yellow .main-header .navbar .nav .open > a:focus,
+.skin-yellow .main-header .navbar .nav > .active > a {
+ background: rgba(0, 0, 0, 0.05);
+ color: #f6f6f6;
+}
+.skin-yellow .main-header .navbar .nav-addtabs li > .close-tab {
+ color: #f6f6f6;
+}
+.skin-yellow .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-yellow .main-header .navbar .sidebar-toggle:hover {
+ color: #f6f6f6;
+ background: rgba(0, 0, 0, 0.05);
+}
+.skin-yellow .main-header .navbar .sidebar-toggle {
+ color: #fff;
+}
+.skin-yellow .main-header .navbar .sidebar-toggle:hover {
+ background-color: #e08e0b;
+}
+@media (max-width: 767px) {
+ .skin-yellow .main-header .navbar .dropdown-menu li.divider {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ .skin-yellow .main-header .navbar .dropdown-menu li a {
+ color: #fff;
+ }
+ .skin-yellow .main-header .navbar .dropdown-menu li a:hover {
+ background: #e08e0b;
+ }
+}
+.skin-yellow .main-header .logo {
+ background-color: #e08e0b;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+}
+.skin-yellow .main-header .logo:hover {
+ background-color: #db8b0b;
+}
+@media (max-width: 767px) {
+ .skin-yellow .main-header .logo {
+ background-color: #f39c12;
+ color: #fff;
+ border-bottom: 0 solid transparent;
+ border-right: none;
+ }
+ .skin-yellow .main-header .logo:hover {
+ background-color: #f39a0d;
+ }
+}
+.skin-yellow .main-header li.user-header {
+ background-color: #f39c12;
+}
+.skin-yellow .content-header {
+ background: transparent;
+}
+.skin-yellow .wrapper,
+.skin-yellow .main-sidebar,
+.skin-yellow .left-side {
+ background-color: #222d32;
+}
+.skin-yellow .user-panel > .info,
+.skin-yellow .user-panel > .info > a {
+ color: #fff;
+}
+.skin-yellow .sidebar-menu .treeview-menu {
+ padding-left: 3px;
+}
+.skin-yellow .sidebar-menu > li.header {
+ color: #4b646f;
+ background: #1a2226;
+}
+.skin-yellow .sidebar-menu > li:hover > a,
+.skin-yellow .sidebar-menu > li.active > a {
+ color: #fff;
+ background: #1e282c;
+ border-left-color: #f39c12;
+}
+.skin-yellow .sidebar-menu > li > .treeview-menu {
+ background: #2c3b41;
+}
+.skin-yellow .sidebar a {
+ color: #b8c7ce;
+}
+.skin-yellow .sidebar a:hover {
+ text-decoration: none;
+}
+.skin-yellow .treeview-menu > li > a {
+ color: #8aa4af;
+}
+.skin-yellow .treeview-menu > li.active > a,
+.skin-yellow .treeview-menu > li > a:hover {
+ color: #fff;
+}
+.skin-yellow .sidebar-form {
+ border-radius: 3px;
+ border: 1px solid #374850;
+ background-color: #374850;
+ margin: 10px 10px;
+}
+.skin-yellow .sidebar-form input[type="text"],
+.skin-yellow .sidebar-form .btn {
+ box-shadow: none;
+ background-color: #374850;
+ border: 1px solid transparent;
+ height: 35px;
+}
+.skin-yellow .sidebar-form input[type="text"] {
+ color: #666;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 2px;
+}
+.skin-yellow .sidebar-form input[type="text"]:focus,
+.skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ background-color: #fff;
+ color: #666;
+}
+.skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn {
+ background: #fff;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
+ border-left-color: #fff;
+}
+.skin-yellow .sidebar-form .btn {
+ color: #999;
+ border-top-left-radius: 0;
+ border-top-right-radius: 2px;
+ border-bottom-right-radius: 2px;
+ border-bottom-left-radius: 0;
+}
+.skin-yellow .sidebar-menu > li > a {
+ border-left: 3px solid transparent;
+ padding-left: 12px;
+}
+@media (min-width: 768px) {
+ .skin-yellow.sidebar-mini.sidebar-collapse .sidebar-menu > li:hover > a > span:not(.pull-right) {
+ margin-left: -3px;
+ }
+}
+@media (max-width: 767px) {
+ .skin-yellow.multiplenav .sidebar .mobilenav a.btn-app {
+ background: #374850;
+ color: #fff;
+ }
+ .skin-yellow.multiplenav .sidebar .mobilenav a.btn-app.active {
+ background: #f39c12;
+ color: #fff;
+ }
+}
+/*# sourceMappingURL=skin-yellow.css.map */
\ No newline at end of file
diff --git a/public/static/images/aliyun.ico b/public/static/images/aliyun.ico
new file mode 100644
index 0000000..a566910
Binary files /dev/null and b/public/static/images/aliyun.ico differ
diff --git a/public/static/images/baidu.ico b/public/static/images/baidu.ico
new file mode 100644
index 0000000..32070ff
Binary files /dev/null and b/public/static/images/baidu.ico differ
diff --git a/public/static/images/cloudflare.ico b/public/static/images/cloudflare.ico
new file mode 100644
index 0000000..3c17430
Binary files /dev/null and b/public/static/images/cloudflare.ico differ
diff --git a/public/static/images/dnspod.ico b/public/static/images/dnspod.ico
new file mode 100644
index 0000000..362fa8b
Binary files /dev/null and b/public/static/images/dnspod.ico differ
diff --git a/public/static/images/error.svg b/public/static/images/error.svg
new file mode 100644
index 0000000..d422a34
--- /dev/null
+++ b/public/static/images/error.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/public/static/images/huawei.ico b/public/static/images/huawei.ico
new file mode 100644
index 0000000..32085d8
Binary files /dev/null and b/public/static/images/huawei.ico differ
diff --git a/public/static/images/info.svg b/public/static/images/info.svg
new file mode 100644
index 0000000..fe91e8e
--- /dev/null
+++ b/public/static/images/info.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/public/static/images/login-head.png b/public/static/images/login-head.png
new file mode 100644
index 0000000..544958c
Binary files /dev/null and b/public/static/images/login-head.png differ
diff --git a/public/static/images/logo.png b/public/static/images/logo.png
new file mode 100644
index 0000000..f5157ac
Binary files /dev/null and b/public/static/images/logo.png differ
diff --git a/public/static/images/success.svg b/public/static/images/success.svg
new file mode 100644
index 0000000..1c2d586
--- /dev/null
+++ b/public/static/images/success.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/public/static/images/user.png b/public/static/images/user.png
new file mode 100644
index 0000000..2753596
Binary files /dev/null and b/public/static/images/user.png differ
diff --git a/public/static/images/west.ico b/public/static/images/west.ico
new file mode 100644
index 0000000..589df08
Binary files /dev/null and b/public/static/images/west.ico differ
diff --git a/public/static/js/app.js b/public/static/js/app.js
new file mode 100644
index 0000000..b57645b
--- /dev/null
+++ b/public/static/js/app.js
@@ -0,0 +1,738 @@
+/*! AdminLTE app.js
+* ================
+* Main JS application file for AdminLTE v2. This file
+* should be included in all pages. It controls some layout
+* options and implements exclusive AdminLTE plugins.
+*
+* @author Colorlib
+* @support
+* @version v2.4.18
+* @repository git://github.com/ColorlibHQ/AdminLTE.git
+* @license MIT
+*/
+
+// Make sure jQuery has been loaded
+if (typeof jQuery === 'undefined') {
+throw new Error('AdminLTE requires jQuery')
+}
+
+/* ControlSidebar()
+ * ===============
+ * Toggles the state of the control sidebar
+ *
+ * @Usage: $('#control-sidebar-trigger').controlSidebar(options)
+ * or add [data-toggle="control-sidebar"] to the trigger
+ * Pass any option as data-option="value"
+ */
++function ($) {
+ 'use strict';
+
+ var DataKey = 'lte.controlsidebar';
+
+ var Default = {
+ controlsidebarSlide: true
+ };
+
+ var Selector = {
+ sidebar: '.control-sidebar',
+ data : '[data-toggle="control-sidebar"]',
+ open : '.control-sidebar-open',
+ bg : '.control-sidebar-bg',
+ wrapper: '.wrapper',
+ content: '.content-wrapper',
+ boxed : '.layout-boxed'
+ };
+
+ var ClassName = {
+ open: 'control-sidebar-open',
+ transition: 'control-sidebar-hold-transition',
+ fixed: 'fixed'
+ };
+
+ var Event = {
+ collapsed: 'collapsed.controlsidebar',
+ expanded : 'expanded.controlsidebar'
+ };
+
+ // ControlSidebar Class Definition
+ // ===============================
+ var ControlSidebar = function (element, options) {
+ this.element = element;
+ this.options = options;
+ this.hasBindedResize = false;
+
+ this.init();
+ };
+
+ ControlSidebar.prototype.init = function () {
+ // Add click listener if the element hasn't been
+ // initialized using the data API
+ if (!$(this.element).is(Selector.data)) {
+ $(this).on('click', this.toggle);
+ }
+
+ this.fix();
+ $(window).resize(function () {
+ this.fix();
+ }.bind(this));
+ };
+
+ ControlSidebar.prototype.toggle = function (event) {
+ if (event) event.preventDefault();
+
+ this.fix();
+
+ if (!$(Selector.sidebar).is(Selector.open) && !$('body').is(Selector.open)) {
+ this.expand();
+ } else {
+ this.collapse();
+ }
+ };
+
+ ControlSidebar.prototype.expand = function () {
+ $(Selector.sidebar).show();
+ if (!this.options.controlsidebarSlide) {
+ $('body').addClass(ClassName.transition).addClass(ClassName.open).delay(50).queue(function(){
+ $('body').removeClass(ClassName.transition);
+ $(this).dequeue()
+ })
+ } else {
+ $(Selector.sidebar).addClass(ClassName.open);
+ }
+
+
+ $(this.element).trigger($.Event(Event.expanded));
+ };
+
+ ControlSidebar.prototype.collapse = function () {
+ if (!this.options.controlsidebarSlide) {
+ $('body').addClass(ClassName.transition).removeClass(ClassName.open).delay(50).queue(function(){
+ $('body').removeClass(ClassName.transition);
+ $(this).dequeue()
+ })
+ } else {
+ $(Selector.sidebar).removeClass(ClassName.open);
+ }
+ $(Selector.sidebar).fadeOut();
+ $(this.element).trigger($.Event(Event.collapsed));
+ };
+
+ ControlSidebar.prototype.fix = function () {
+ if ($('body').is(Selector.boxed)) {
+ this._fixForBoxed($(Selector.bg));
+ }
+ };
+
+ // Private
+
+ ControlSidebar.prototype._fixForBoxed = function (bg) {
+ bg.css({
+ position: 'absolute',
+ height : $(Selector.wrapper).height()
+ });
+ };
+
+ // Plugin Definition
+ // =================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data(DataKey);
+
+ if (!data) {
+ var options = $.extend({}, Default, $this.data(), typeof option == 'object' && option);
+ $this.data(DataKey, (data = new ControlSidebar($this, options)));
+ }
+
+ if (typeof option == 'string') data.toggle();
+ });
+ }
+
+ var old = $.fn.controlSidebar;
+
+ $.fn.controlSidebar = Plugin;
+ $.fn.controlSidebar.Constructor = ControlSidebar;
+
+ // No Conflict Mode
+ // ================
+ $.fn.controlSidebar.noConflict = function () {
+ $.fn.controlSidebar = old;
+ return this;
+ };
+
+ // ControlSidebar Data API
+ // =======================
+ $(document).on('click', Selector.data, function (event) {
+ if (event) event.preventDefault();
+ Plugin.call($(this), 'toggle');
+ });
+
+}(jQuery);
+
+
+/* PushMenu()
+ * ==========
+ * Adds the push menu functionality to the sidebar.
+ *
+ * @usage: $('.btn').pushMenu(options)
+ * or add [data-toggle="push-menu"] to any button
+ * Pass any option as data-option="value"
+ */
++function ($) {
+ 'use strict';
+
+ var DataKey = 'lte.pushmenu';
+
+ var Default = {
+ collapseScreenSize : 767,
+ expandOnHover : false,
+ expandTransitionDelay: 200
+ };
+
+ var Selector = {
+ collapsed : '.sidebar-collapse',
+ open : '.sidebar-open',
+ mainSidebar : '.main-sidebar',
+ contentWrapper: '.content-wrapper',
+ searchInput : '.sidebar-form .form-control',
+ button : '[data-toggle="push-menu"]',
+ mini : '.sidebar-mini',
+ expanded : '.sidebar-expanded-on-hover',
+ layoutFixed : '.fixed'
+ };
+
+ var ClassName = {
+ collapsed : 'sidebar-collapse',
+ open : 'sidebar-open',
+ mini : 'sidebar-mini',
+ expanded : 'sidebar-expanded-on-hover',
+ expandFeature: 'sidebar-mini-expand-feature',
+ layoutFixed : 'fixed'
+ };
+
+ var Event = {
+ expanded : 'expanded.pushMenu',
+ collapsed: 'collapsed.pushMenu'
+ };
+
+ // PushMenu Class Definition
+ // =========================
+ var PushMenu = function (options) {
+ this.options = options;
+ this.init();
+ };
+
+ PushMenu.prototype.init = function () {
+ if (this.options.expandOnHover
+ || ($('body').is(Selector.mini + Selector.layoutFixed))) {
+ this.expandOnHover();
+ $('body').addClass(ClassName.expandFeature);
+ }
+
+ $(Selector.contentWrapper).click(function () {
+ // Enable hide menu when clicking on the content-wrapper on small screens
+ if ($(window).width() <= this.options.collapseScreenSize && $('body').hasClass(ClassName.open)) {
+ this.close();
+ }
+ }.bind(this));
+
+ // __Fix for android devices
+ $(Selector.searchInput).click(function (e) {
+ e.stopPropagation();
+ });
+ };
+
+ PushMenu.prototype.toggle = function () {
+ var windowWidth = $(window).width();
+ var isOpen = !$('body').hasClass(ClassName.collapsed);
+
+ if (windowWidth <= this.options.collapseScreenSize) {
+ isOpen = $('body').hasClass(ClassName.open);
+ }
+
+ if (!isOpen) {
+ this.open();
+ } else {
+ this.close();
+ }
+ };
+
+ PushMenu.prototype.open = function () {
+ var windowWidth = $(window).width();
+
+ if (windowWidth > this.options.collapseScreenSize) {
+ $('body').removeClass(ClassName.collapsed)
+ .trigger($.Event(Event.expanded));
+ }
+ else {
+ $('body').addClass(ClassName.open)
+ .trigger($.Event(Event.expanded));
+ }
+ };
+
+ PushMenu.prototype.close = function () {
+ var windowWidth = $(window).width();
+ if (windowWidth > this.options.collapseScreenSize) {
+ $('body').addClass(ClassName.collapsed)
+ .trigger($.Event(Event.collapsed));
+ } else {
+ $('body').removeClass(ClassName.open + ' ' + ClassName.collapsed)
+ .trigger($.Event(Event.collapsed));
+ }
+ };
+
+ PushMenu.prototype.expandOnHover = function () {
+ $(Selector.mainSidebar).hover(function () {
+ if ($('body').is(Selector.mini + Selector.collapsed)
+ && $(window).width() > this.options.collapseScreenSize) {
+ this.expand();
+ }
+ }.bind(this), function () {
+ if ($('body').is(Selector.expanded)) {
+ this.collapse();
+ }
+ }.bind(this));
+ };
+
+ PushMenu.prototype.expand = function () {
+ setTimeout(function () {
+ $('body').removeClass(ClassName.collapsed)
+ .addClass(ClassName.expanded);
+ }, this.options.expandTransitionDelay);
+ };
+
+ PushMenu.prototype.collapse = function () {
+ setTimeout(function () {
+ $('body').removeClass(ClassName.expanded)
+ .addClass(ClassName.collapsed);
+ }, this.options.expandTransitionDelay);
+ };
+
+ // PushMenu Plugin Definition
+ // ==========================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data(DataKey);
+
+ if (!data) {
+ var options = $.extend({}, Default, $this.data(), typeof option == 'object' && option);
+ $this.data(DataKey, (data = new PushMenu(options)));
+ }
+
+ if (option === 'toggle') data.toggle();
+ });
+ }
+
+ var old = $.fn.pushMenu;
+
+ $.fn.pushMenu = Plugin;
+ $.fn.pushMenu.Constructor = PushMenu;
+
+ // No Conflict Mode
+ // ================
+ $.fn.pushMenu.noConflict = function () {
+ $.fn.pushMenu = old;
+ return this;
+ };
+
+ // Data API
+ // ========
+ $(document).on('click', Selector.button, function (e) {
+ e.preventDefault();
+ Plugin.call($(this), 'toggle');
+ });
+ $(window).on('load', function () {
+ Plugin.call($(Selector.button));
+ });
+}(jQuery);
+
+
+/* Tree()
+ * ======
+ * Converts a nested list into a multilevel
+ * tree view menu.
+ *
+ * @Usage: $('.my-menu').tree(options)
+ * or add [data-widget="tree"] to the ul element
+ * Pass any option as data-option="value"
+ */
++function ($) {
+ 'use strict';
+
+ var DataKey = 'lte.tree';
+
+ var Default = {
+ animationSpeed: 300,
+ accordion : true,
+ followLink : false,
+ trigger : '.treeview a'
+ };
+
+ var Selector = {
+ tree : '.tree',
+ treeview : '.treeview',
+ treeviewMenu: '.treeview-menu',
+ open : '.menu-open, .active',
+ li : 'li',
+ data : '[data-widget="tree"]',
+ active : '.active'
+ };
+
+ var ClassName = {
+ open: 'menu-open',
+ tree: 'tree'
+ };
+
+ var Event = {
+ collapsed: 'collapsed.tree',
+ expanded : 'expanded.tree'
+ };
+
+ // Tree Class Definition
+ // =====================
+ var Tree = function (element, options) {
+ this.element = element;
+ this.options = options;
+
+ $(this.element).addClass(ClassName.tree);
+
+ $(Selector.treeview + Selector.active, this.element).addClass(ClassName.open);
+
+ this._setUpListeners();
+ };
+
+ Tree.prototype.toggle = function (link, event) {
+ var treeviewMenu = link.next(Selector.treeviewMenu);
+ var parentLi = link.parent();
+ var isOpen = parentLi.hasClass(ClassName.open);
+
+ if (!parentLi.is(Selector.treeview)) {
+ return;
+ }
+
+ if (!this.options.followLink || link.attr('href') === '#') {
+ event.preventDefault();
+ }
+
+ if (isOpen) {
+ this.collapse(treeviewMenu, parentLi);
+ } else {
+ this.expand(treeviewMenu, parentLi);
+ }
+ };
+
+ Tree.prototype.expand = function (tree, parent) {
+ var expandedEvent = $.Event(Event.expanded);
+
+ if (this.options.accordion) {
+ var openMenuLi = parent.siblings(Selector.open);
+ var openTree = openMenuLi.children(Selector.treeviewMenu);
+ this.collapse(openTree, openMenuLi);
+ }
+
+ parent.addClass(ClassName.open);
+ tree.stop().slideDown(this.options.animationSpeed, function () {
+ $(this.element).trigger(expandedEvent);
+ parent.height('auto');
+ }.bind(this));
+ };
+
+ Tree.prototype.collapse = function (tree, parentLi) {
+ var collapsedEvent = $.Event(Event.collapsed);
+
+ //tree.find(Selector.open).removeClass(ClassName.open);
+ parentLi.removeClass(ClassName.open);
+ tree.stop().slideUp(this.options.animationSpeed, function () {
+ //tree.find(Selector.open + ' > ' + Selector.treeview).slideUp();
+ $(this.element).trigger(collapsedEvent);
+
+ // Collapse child items
+ parentLi.find(Selector.treeview).removeClass(ClassName.open).find(Selector.treeviewMenu).hide();
+ }.bind(this));
+ };
+
+ // Private
+
+ Tree.prototype._setUpListeners = function () {
+ var that = this;
+
+ $(this.element).on('click', this.options.trigger, function (event) {
+ that.toggle($(this), event);
+ });
+ };
+
+ // Plugin Definition
+ // =================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data(DataKey);
+
+ if (!data) {
+ var options = $.extend({}, Default, $this.data(), typeof option == 'object' && option);
+ $this.data(DataKey, new Tree($this, options));
+ }
+ });
+ }
+
+ var old = $.fn.tree;
+
+ $.fn.tree = Plugin;
+ $.fn.tree.Constructor = Tree;
+
+ // No Conflict Mode
+ // ================
+ $.fn.tree.noConflict = function () {
+ $.fn.tree = old;
+ return this;
+ };
+
+ // Tree Data API
+ // =============
+ $(window).on('load', function () {
+ $(Selector.data).each(function () {
+ Plugin.call($(this));
+ });
+ });
+
+}(jQuery);
+
+
+/* Layout()
+ * ========
+ * Implements AdminLTE layout.
+ * Fixes the layout height in case min-height fails.
+ *
+ * @usage activated automatically upon window load.
+ * Configure any options by passing data-option="value"
+ * to the body tag.
+ */
++function ($) {
+ 'use strict';
+
+ var DataKey = 'lte.layout';
+
+ var Default = {
+ slimscroll : true,
+ resetHeight: true
+ };
+
+ var Selector = {
+ wrapper : '.wrapper',
+ contentWrapper: '.content-wrapper',
+ layoutBoxed : '.layout-boxed',
+ mainFooter : '.main-footer',
+ mainHeader : '.main-header',
+ mainSidebar : '.main-sidebar',
+ slimScrollDiv : 'slimScrollDiv',
+ sidebar : '.sidebar',
+ controlSidebar: '.control-sidebar',
+ fixed : '.fixed',
+ sidebarMenu : '.sidebar-menu',
+ logo : '.main-header .logo'
+ };
+
+ var ClassName = {
+ fixed : 'fixed',
+ holdTransition: 'hold-transition'
+ };
+
+ var Layout = function (options) {
+ this.options = options;
+ this.bindedResize = false;
+ this.activate();
+ };
+
+ Layout.prototype.activate = function () {
+ this.fix();
+ this.fixSidebar();
+
+ $('body').removeClass(ClassName.holdTransition);
+
+ if (this.options.resetHeight) {
+ $('body, html, ' + Selector.wrapper).css({
+ 'height' : 'auto',
+ 'min-height': '100%'
+ });
+ }
+
+ if (!this.bindedResize) {
+ $(window).resize(function () {
+ this.fix();
+ this.fixSidebar();
+
+ $(Selector.logo + ', ' + Selector.sidebar).one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function () {
+ this.fix();
+ this.fixSidebar();
+ }.bind(this));
+ }.bind(this));
+
+ this.bindedResize = true;
+ }
+
+ $(Selector.sidebarMenu).on('expanded.tree', function () {
+ this.fix();
+ this.fixSidebar();
+ }.bind(this));
+
+ $(Selector.sidebarMenu).on('collapsed.tree', function () {
+ this.fix();
+ this.fixSidebar();
+ }.bind(this));
+ };
+
+ Layout.prototype.fix = function () {
+ // Remove overflow from .wrapper if layout-boxed exists
+ $(Selector.layoutBoxed + ' > ' + Selector.wrapper).css('overflow', 'hidden');
+
+ // Get window height and the wrapper height
+ var footerHeight = $(Selector.mainFooter).outerHeight() || 0;
+ var headerHeight = $(Selector.mainHeader).outerHeight() || 0;
+ var neg = headerHeight + footerHeight;
+ var windowHeight = $(window).height();
+ var sidebarHeight = $(Selector.sidebar).outerHeight() || 0;
+
+ // Set the min-height of the content and sidebar based on
+ // the height of the document.
+ if ($('body').hasClass(ClassName.fixed)) {
+ $(Selector.contentWrapper).css('min-height', windowHeight - footerHeight);
+ } else {
+ var postSetHeight;
+
+ if (windowHeight >= sidebarHeight + headerHeight) {
+ $(Selector.contentWrapper).css('min-height', windowHeight - neg);
+ postSetHeight = windowHeight - neg;
+ } else {
+ $(Selector.contentWrapper).css('min-height', sidebarHeight);
+ postSetHeight = sidebarHeight;
+ }
+
+ // Fix for the control sidebar height
+ var $controlSidebar = $(Selector.controlSidebar);
+ if (typeof $controlSidebar !== 'undefined') {
+ if ($controlSidebar.height() > postSetHeight)
+ $(Selector.contentWrapper).css('min-height', $controlSidebar.height());
+ }
+ }
+ };
+
+ Layout.prototype.fixSidebar = function () {
+ // Make sure the body tag has the .fixed class
+ if (!$('body').hasClass(ClassName.fixed)) {
+ if (typeof $.fn.slimScroll !== 'undefined') {
+ $(Selector.sidebar).slimScroll({ destroy: true }).height('auto');
+ }
+ return;
+ }
+
+ // Enable slimscroll for fixed layout
+ if (this.options.slimscroll) {
+ if (typeof $.fn.slimScroll !== 'undefined') {
+ // Destroy if it exists
+ // $(Selector.sidebar).slimScroll({ destroy: true }).height('auto')
+
+ // Add slimscroll
+ if ($(Selector.mainSidebar).find(Selector.slimScrollDiv).length === 0) {
+ $(Selector.sidebar).slimScroll({
+ height: ($(window).height() - $(Selector.mainHeader).height()) + 'px'
+ });
+ }
+ }
+ }
+ };
+
+ // Plugin Definition
+ // =================
+ function Plugin(option) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data(DataKey);
+
+ if (!data) {
+ var options = $.extend({}, Default, $this.data(), typeof option === 'object' && option);
+ $this.data(DataKey, (data = new Layout(options)));
+ }
+
+ if (typeof option === 'string') {
+ if (typeof data[option] === 'undefined') {
+ throw new Error('No method named ' + option);
+ }
+ data[option]();
+ }
+ });
+ }
+
+ var old = $.fn.layout;
+
+ $.fn.layout = Plugin;
+ $.fn.layout.Constuctor = Layout;
+
+ // No conflict mode
+ // ================
+ $.fn.layout.noConflict = function () {
+ $.fn.layout = old;
+ return this;
+ };
+
+ // Layout DATA-API
+ // ===============
+ $(window).on('load', function () {
+ Plugin.call($('body'));
+ });
+}(jQuery);
+
+//全屏事件
+$(document).on('click', "[data-toggle='fullscreen']", function () {
+ var doc = document.documentElement;
+ if ($(document.body).hasClass("full-screen")) {
+ $(document.body).removeClass("full-screen");
+ document.exitFullscreen ? document.exitFullscreen() : document.mozCancelFullScreen ? document.mozCancelFullScreen() : document.webkitExitFullscreen && document.webkitExitFullscreen();
+ } else {
+ $(document.body).addClass("full-screen");
+ doc.requestFullscreen ? doc.requestFullscreen() : doc.mozRequestFullScreen ? doc.mozRequestFullScreen() : doc.webkitRequestFullscreen ? doc.webkitRequestFullscreen() : doc.msRequestFullscreen && doc.msRequestFullscreen();
+ }
+});
+
+var my_skins = [
+ "skin-blue",
+ "skin-black",
+ "skin-red",
+ "skin-yellow",
+ "skin-purple",
+ "skin-green",
+ "skin-blue-light",
+ "skin-black-light",
+ "skin-red-light",
+ "skin-yellow-light",
+ "skin-purple-light",
+ "skin-green-light",
+ "skin-black-blue",
+ "skin-black-purple",
+ "skin-black-red",
+ "skin-black-green",
+ "skin-black-yellow",
+ "skin-black-pink",
+];
+
+// 皮肤切换
+$("[data-skin]").on('click', function (e) {
+ var skin = $(this).data('skin');
+ if (!$("body").hasClass(skin)) {
+ $("body").removeClass(my_skins.join(' ')).addClass(skin);
+ var cssfile = "/static/css/skins/" + skin + ".css";
+ $('head').append('');
+ $(".skin-list li.active").removeClass("active");
+ $(".skin-list li a[data-skin='" + skin + "']").parent().addClass("active");
+ $.ajax({
+ type : "POST",
+ url : "/changeskin",
+ data : {skin:skin},
+ dataType : 'json',
+ async: true,
+ success : function(data) {
+ }
+ });
+ }
+ return false;
+});
\ No newline at end of file
diff --git a/public/static/js/bootstrapValidator.min.js b/public/static/js/bootstrapValidator.min.js
new file mode 100644
index 0000000..4b4112a
--- /dev/null
+++ b/public/static/js/bootstrapValidator.min.js
@@ -0,0 +1 @@
+if("undefined"==typeof jQuery)throw new Error("BootstrapValidator requires jQuery");!function(){var t=window.jQuery.fn.jquery.split(" ")[0].split(".");if(+t[0]<2&&+t[1]<9||1==+t[0]&&9==+t[1]&&+t[2]<1)throw new Error("BootstrapValidator requires jQuery version 1.9.1 or higher")}(),function(V){function s(t,e){this.$form=V(t),this.options=V.extend({},V.fn.bootstrapValidator.DEFAULT_OPTIONS,e),this.$invalidFields=V([]),this.$submitButton=null,this.$hiddenButton=null,this.STATUS_NOT_VALIDATED="NOT_VALIDATED",this.STATUS_VALIDATING="VALIDATING",this.STATUS_INVALID="INVALID",this.STATUS_VALID="VALID",t=function(){for(var t=3,e=document.createElement("div"),a=e.all||[];e.innerHTML="\x3c!--[if gt IE "+ ++t+"]>
").attr("type","submit").prependTo(this.$form).addClass("bv-hidden-submit").css({display:"none",width:0,height:0}),this.$form.on("click.bv",'[type="submit"]',function(t){t.isDefaultPrevented()||(t=((t=V(t.target)).is('[type="submit"]')?t:t.parent('[type="submit"]')).eq(0),!i.options.submitButtons)||t.is(i.options.submitButtons)||t.is(i.$hiddenButton)||i.$form.off("submit.bv").submit()}),this.options.fields)this._initField(t);this.$form.trigger(V.Event(this.options.events.formInit),{bv:this,options:this.options}),this.options.onSuccess&&this.$form.on(this.options.events.formSuccess,function(t){V.fn.bootstrapValidator.helpers.call(i.options.onSuccess,[t])}),this.options.onError&&this.$form.on(this.options.events.formError,function(t){V.fn.bootstrapValidator.helpers.call(i.options.onError,[t])})},_parseOptions:function(t){var e,a,i,s,o,r,n,l,d,f=t.attr("name")||t.attr("data-bv-field"),u={};for(a in V.fn.bootstrapValidator.validators)if(e=V.fn.bootstrapValidator.validators[a],i="data-bv-"+a.toLowerCase(),s=t.attr(i)+"",(d="function"==typeof e.enableByHtml5?e.enableByHtml5(t):null)&&"false"!==s||!0!==d&&(""===s||"true"===s||i===s.toLowerCase()))for(l in e.html5Attributes=V.extend({},{message:"message",onerror:"onError",onsuccess:"onSuccess"},e.html5Attributes),u[a]=V.extend({},!0===d?{}:d,u[a]),e.html5Attributes)o=e.html5Attributes[l],r="data-bv-"+a.toLowerCase()+"-"+l,(n=t.attr(r))&&("true"===n||r===n.toLowerCase()?n=!0:"false"===n&&(n=!1),u[a][o]=n);var h={autoFocus:t.attr("data-bv-autofocus"),container:t.attr("data-bv-container"),excluded:t.attr("data-bv-excluded"),feedbackIcons:t.attr("data-bv-feedbackicons"),group:t.attr("data-bv-group"),message:t.attr("data-bv-message"),onError:t.attr("data-bv-onerror"),onStatus:t.attr("data-bv-onstatus"),onSuccess:t.attr("data-bv-onsuccess"),selector:t.attr("data-bv-selector"),threshold:t.attr("data-bv-threshold"),trigger:t.attr("data-bv-trigger"),verbose:t.attr("data-bv-verbose"),validators:u},p=V.isEmptyObject(h);return!V.isEmptyObject(u)||!p&&this.options.fields&&this.options.fields[f]?(h.validators=u,h):null},_initField:function(t){var e,a=V([]);switch(typeof t){case"object":t=(a=t).attr("data-bv-field");break;case"string":(a=this.getFieldElements(t)).attr("data-bv-field",t)}if(0!==a.length&&null!==this.options.fields[t]&&null!==this.options.fields[t].validators){for(e in this.options.fields[t].validators)V.fn.bootstrapValidator.validators[e]||delete this.options.fields[t].validators[e];null===this.options.fields[t].enabled&&(this.options.fields[t].enabled=!0);for(var i=this,s=a.length,o=a.attr("type"),r=1===s||"radio"===o||"checkbox"===o,n="radio"===o||"checkbox"===o||"file"===o||"SELECT"===a.eq(0).get(0).tagName?"change":this._changeEvent,n=(this.options.fields[t].trigger||this.options.trigger||n).split(" "),l=V.map(n,function(t){return t+".update.bv"}).join(" "),d=0;d").css("display","none").addClass("help-block").attr("data-bv-validator",e).attr("data-bv-for",t).attr("data-bv-result",this.STATUS_NOT_VALIDATED).html(this._getMessage(t,e)).appendTo(c),"function"==typeof V.fn.bootstrapValidator.validators[e].init&&V.fn.bootstrapValidator.validators[e].init(this,u,this.options.fields[t].validators[e]);!1!==this.options.fields[t].feedbackIcons&&"false"!==this.options.fields[t].feedbackIcons&&this.options.feedbackIcons&&this.options.feedbackIcons.validating&&this.options.feedbackIcons.invalid&&this.options.feedbackIcons.valid&&(!r||d===s-1)&&(p.addClass("has-feedback"),h=V("").css("display","none").addClass("form-control-feedback").attr("data-bv-icon-for",t).insertAfter(u),"checkbox"!==o&&"radio"!==o||((f=u.parent()).hasClass(o)?h.insertAfter(f):f.parent().hasClass(o)&&h.insertAfter(f.parent())),0===p.find("label").length&&h.addClass("bv-no-label"),0!==p.find(".input-group").length&&h.addClass("bv-icon-input-group").insertAfter(p.find(".input-group").eq(0)),r?d===s-1&&a.data("bv.icon",h):u.data("bv.icon",h),v)&&u.off("focus.container.bv").on("focus.container.bv",function(){switch(v){case"tooltip":V(this).data("bv.icon").tooltip("show");break;case"popover":V(this).data("bv.icon").popover("show")}}).off("blur.container.bv").on("blur.container.bv",function(){switch(v){case"tooltip":V(this).data("bv.icon").tooltip("hide");break;case"popover":V(this).data("bv.icon").popover("hide")}})}switch(a.on(this.options.events.fieldSuccess,function(t,e){var a=i.getOptions(e.field,null,"onSuccess");a&&V.fn.bootstrapValidator.helpers.call(a,[t,e])}).on(this.options.events.fieldError,function(t,e){var a=i.getOptions(e.field,null,"onError");a&&V.fn.bootstrapValidator.helpers.call(a,[t,e])}).on(this.options.events.fieldStatus,function(t,e){var a=i.getOptions(e.field,null,"onStatus");a&&V.fn.bootstrapValidator.helpers.call(a,[t,e])}).on(this.options.events.validatorError,function(t,e){var a=i.getOptions(e.field,e.validator,"onError");a&&V.fn.bootstrapValidator.helpers.call(a,[t,e])}).on(this.options.events.validatorSuccess,function(t,e){var a=i.getOptions(e.field,e.validator,"onSuccess");a&&V.fn.bootstrapValidator.helpers.call(a,[t,e])}),l=V.map(n,function(t){return t+".live.bv"}).join(" "),this.options.live){case"submitted":break;case"disabled":a.off(l);break;default:a.off(l).on(l,function(){i._exceedThreshold(V(this))&&i.validateField(V(this))})}a.trigger(V.Event(this.options.events.fieldInit),{bv:this,field:t,element:a})}},_getMessage:function(t,e){if(!(this.options.fields[t]&&V.fn.bootstrapValidator.validators[e]&&this.options.fields[t].validators&&this.options.fields[t].validators[e]))return"";var a=this.options.fields[t].validators[e];switch(!0){case!!a.message:return a.message;case!!this.options.fields[t].message:return this.options.fields[t].message;case!!V.fn.bootstrapValidator.i18n[e]:return V.fn.bootstrapValidator.i18n[e].default;default:return this.options.message}},_getMessageContainer:function(t,e){var a=t.parent();if(a.is(e))return a;var i=a.attr("class");if(i)for(var s=(i=i.split(" ")).length,o=0;o=e},_onError:function(t){if(!t.isDefaultPrevented()){if("submitted"===this.options.live){this.options.live="enabled";var e,a=this;for(e in this.options.fields)i=e,s=void 0,(i=a.getFieldElements(i)).length&&(s="radio"===(s=V(i[0]).attr("type"))||"checkbox"===s||"file"===s||"SELECT"===V(i[0]).get(0).tagName?"change":a._changeEvent,s=a.options.fields[e].trigger||a.options.trigger||s,s=V.map(s.split(" "),function(t){return t+".live.bv"}).join(" "),i.off(s).on(s,function(){a._exceedThreshold(V(this))&&a.validateField(V(this))}))}for(var i,s,o=0;o").attr("type","hidden").attr("data-bv-submit-hidden","").attr("name",this.$submitButton.attr("name")).val(this.$submitButton.val()).appendTo(this.$form),this.$form.off("submit.bv").submit()},getInvalidFields:function(){return this.$invalidFields},getSubmitButton:function(){return this.$submitButton},getMessages:function(t,e){var a=this,i=[],s=V([]);switch(!0){case t&&"object"==typeof t:s=t;break;case t&&"string"==typeof t:var o,r=this.getFieldElements(t);0parseInt(o,10))&&(t=!1),!0){case!!s&&!!o:r=n.fn.bootstrapValidator.helpers.format(a.message||n.fn.bootstrapValidator.i18n.choice.between,[parseInt(s,10),parseInt(o,10)]);break;case!!s:r=n.fn.bootstrapValidator.helpers.format(a.message||n.fn.bootstrapValidator.i18n.choice.less,parseInt(s,10));break;case!!o:r=n.fn.bootstrapValidator.helpers.format(a.message||n.fn.bootstrapValidator.i18n.choice.more,parseInt(o,10))}return{valid:t,message:r}}}}(window.jQuery),function(l){l.fn.bootstrapValidator.i18n.color=l.extend(l.fn.bootstrapValidator.i18n.color||{},{default:"Please enter a valid color"}),l.fn.bootstrapValidator.validators.color={SUPPORTED_TYPES:["hex","rgb","rgba","hsl","hsla","keyword"],KEYWORD_COLORS:["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkgrey","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkslategrey","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dimgrey","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","green","greenyellow","grey","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightgrey","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightslategrey","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","slategrey","snow","springgreen","steelblue","tan","teal","thistle","tomato","transparent","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"],validate:function(t,e,a){var i=e.val();if(""===i)return!0;var s=a.type||this.SUPPORTED_TYPES;l.isArray(s)||(s=s.replace(/s/g,"").split(","));for(var o,r=!1,n=0;n=m.getTime(),b=a.message||y.fn.bootstrapValidator.helpers.format(y.fn.bootstrapValidator.i18n.date.min,V);break;case A&&!V&&c:c=l.getTime()<=g.getTime(),b=a.message||y.fn.bootstrapValidator.helpers.format(y.fn.bootstrapValidator.i18n.date.max,A);break;case A&&V&&c:c=l.getTime()<=g.getTime()&&l.getTime()>=m.getTime(),b=a.message||y.fn.bootstrapValidator.helpers.format(y.fn.bootstrapValidator.i18n.date.range,[V,A])}return{valid:c,message:b}},_parseDate:function(t,e,a){var i=0,s=0,o=0,t=t.split(" "),r=t[0],t=1()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;if(!0!==a.multiple&&"true"!==a.multiple)return i.test(e);for(var a=a.separator||/[,;]/,s=this._splitEmailAddresses(e,a),o=0;oparseInt(a.maxFiles,10)||a.minFiles&&lparseInt(a.maxSize,10)||o&&-1===u.inArray(s.toLowerCase(),o)||n[f].type&&r&&-1===u.inArray(n[f].type.toLowerCase(),r))return!1;if(a.maxTotalSize&&d>parseInt(a.maxTotalSize,10)||a.minTotalSize&&dparseInt(o,10))&&(e=!1),!0){case!!s&&!!o:r=n.fn.bootstrapValidator.helpers.format(a.message||n.fn.bootstrapValidator.i18n.stringLength.between,[parseInt(s,10),parseInt(o,10)]);break;case!!s:r=n.fn.bootstrapValidator.helpers.format(a.message||n.fn.bootstrapValidator.i18n.stringLength.more,parseInt(s,10));break;case!!o:r=n.fn.bootstrapValidator.helpers.format(a.message||n.fn.bootstrapValidator.i18n.stringLength.less,parseInt(o,10))}return{valid:e,message:r}}}}(window.jQuery),function(t){t.fn.bootstrapValidator.i18n.uri=t.extend(t.fn.bootstrapValidator.i18n.uri||{},{default:"Please enter a valid URI"}),t.fn.bootstrapValidator.validators.uri={html5Attributes:{message:"message",allowlocal:"allowLocal",protocol:"protocol"},enableByHtml5:function(t){return"url"===t.attr("type")},validate:function(t,e,a){var i,e=e.val();return""===e||(i=!0===a.allowLocal||"true"===a.allowLocal,a=(a.protocol||"http, https, ftp").split(",").join("|").replace(/\s/g,""),new RegExp("^(?:(?:"+a+")://)(?:\\S+(?::\\S*)?@)?(?:"+(i?"":"(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})")+"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))"+(i?"?":"")+")(?::\\d{2,5})?(?:/[^\\s]*)?$","i").test(e))}}}(window.jQuery),function(o){o.fn.bootstrapValidator.i18n.uuid=o.extend(o.fn.bootstrapValidator.i18n.uuid||{},{default:"Please enter a valid UUID number",version:"Please enter a valid UUID version %s number"}),o.fn.bootstrapValidator.validators.uuid={html5Attributes:{message:"message",version:"version"},validate:function(t,e,a){var i,s,e=e.val();return""===e||{valid:null===(i={3:/^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,4:/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,5:/^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,all:/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i})[s=a.version?a.version+"":"all"]||i[s].test(e),message:a.version?o.fn.bootstrapValidator.helpers.format(a.message||o.fn.bootstrapValidator.i18n.uuid.version,a.version):a.message||o.fn.bootstrapValidator.i18n.uuid.default}}}}(window.jQuery),function(t){t.fn.bootstrapValidator.i18n=t.extend(!0,t.fn.bootstrapValidator.i18n,{base64:{default:"请输入有效的Base64编码"},between:{default:"请输入在 %s 和 %s 之间的数值",notInclusive:"请输入在 %s 和 %s 之间(不含两端)的数值"},callback:{default:"请输入有效的值"},choice:{default:"请输入有效的值",less:"请至少选中 %s 个选项",more:"最多只能选中 %s 个选项",between:"请选择 %s 至 %s 个选项"},color:{default:"请输入有效的颜色值"},date:{default:"请输入有效的日期",min:"请输入 %s 或之后的日期",max:"请输入 %s 或以前的日期",range:"请输入 %s 和 %s 之间的日期"},different:{default:"请输入不同的值"},digits:{default:"请输入有效的数字"},emailAddress:{default:"请输入有效的邮件地址"},file:{default:"请选择有效的文件"},greaterThan:{default:"请输入大于等于 %s 的数值",notInclusive:"请输入大于 %s 的数值"},hex:{default:"请输入有效的16进制数"},hexColor:{default:"请输入有效的16进制颜色值"},id:{default:"请输入有效的身份证号码"},identical:{default:"请输入相同的值"},integer:{default:"请输入有效的整数值"},ip:{default:"请输入有效的IP地址",ipv4:"请输入有效的IPv4地址",ipv6:"请输入有效的IPv6地址"},lessThan:{default:"请输入小于等于 %s 的数值",notInclusive:"请输入小于 %s 的数值"},mac:{default:"请输入有效的MAC物理地址"},notEmpty:{default:"请填写必填项目"},numeric:{default:"请输入有效的数值,允许小数"},phone:{default:"请输入有效的手机号码"},regexp:{default:"请输入符合正则表达式限制的值"},step:{default:"请输入在基础值上,增加 %s 的整数倍的数值"},stringCase:{default:"只能输入小写字母",upper:"只能输入大写字母"},stringLength:{default:"请输入符合长度限制的值",less:"最多只能输入 %s 个字符",more:"需要输入至少 %s 个字符",between:"请输入 %s 至 %s 个字符"},uri:{default:"请输入一个有效的URL地址"},uuid:{default:"请输入有效的UUID",version:"请输入版本 %s 的UUID"}})}(window.jQuery);
\ No newline at end of file
diff --git a/public/static/js/custom.js b/public/static/js/custom.js
new file mode 100644
index 0000000..59be541
--- /dev/null
+++ b/public/static/js/custom.js
@@ -0,0 +1,133 @@
+var location_url = window.location.href;
+var parameter_str = location_url.split('?')[1];
+if (parameter_str !== undefined) {
+ parameter_str = parameter_str.split('#')[0];
+ var $_GET = {};
+ var parameter_arr = parameter_str.split('&');
+ var tmp_arr;
+ for (var i = 0, len = parameter_arr.length; i <= len - 1; i++) {
+ tmp_arr = parameter_arr[i].split('=');
+ $_GET[tmp_arr[0]] = decodeURIComponent(tmp_arr[1]);
+ }
+ window.$_GET = $_GET;
+} else {
+ window.$_GET = [];
+}
+
+function searchSubmit(){
+ $('#listTable').bootstrapTable('refresh');
+ return false;
+}
+function searchClear(){
+ $('#searchToolbar').find('input[name]').each(function() {
+ $(this).val('');
+ });
+ $('#searchToolbar').find('select[name]').each(function() {
+ $(this).find('option:first').prop("selected", 'selected');
+ });
+ $('#listTable').bootstrapTable('refresh');
+}
+function updateToolbar(){
+ $('#searchToolbar').find(':input[name]').each(function() {
+ var name = $(this).attr('name');
+ if(typeof window.$_GET[name] != 'undefined')
+ $(this).val(window.$_GET[name]);
+ })
+}
+function updateQueryStr(obj){
+ var arr = [];
+ for (var p in obj){
+ if (obj.hasOwnProperty(p) && typeof obj[p] != 'undefined' && obj[p] != '') {
+ arr.push(p + "=" + encodeURIComponent(obj[p]));
+ }
+ }
+ history.replaceState({}, null, '?'+arr.join("&"));
+}
+
+if (typeof $.fn.bootstrapTable !== "undefined") {
+ $.fn.bootstrapTable.custom = {
+ method: 'post',
+ contentType: "application/x-www-form-urlencoded",
+ sortable: true,
+ pagination: true,
+ sidePagination: 'server',
+ pageNumber: 1,
+ pageSize: 20,
+ pageList: [10, 15, 20, 30, 50, 100],
+ loadingFontSize: '18px',
+ toolbar: '#searchToolbar',
+ showColumns: true,
+ minimumCountColumns: 2,
+ showToggle: true,
+ showFullscreen: true,
+ paginationPreText: '前页',
+ paginationNextText: '后页',
+ showJumpTo: true,
+ paginationLoop: false,
+ queryParamsType: '',
+ queryParams: function(params) {
+ $('#searchToolbar').find(':input[name]').each(function() {
+ params[$(this).attr('name')] = $(this).val()
+ })
+ updateQueryStr(params);
+ params.offset = params.pageSize * (params.pageNumber-1);
+ params.limit = params.pageSize;
+ return params;
+ },
+ formatLoadingMessage: function(){
+ return '';
+ },
+ formatShowingRows: function(t,n,r,e){
+ return '显示第 '+t+' 到第 '+n+' 条, 总共 '+r+' 条';
+ },
+ formatRecordsPerPage: function(t){
+ return '每页显示 '+t+' 条';
+ },
+ formatNoMatches: function(){
+ return '没有找到匹配的记录';
+ }
+ };
+ $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.custom);
+}
+
+function httpGet(url, callback){
+ $.ajax({
+ url: url,
+ type: 'get',
+ dataType: 'json',
+ success: function (res) {
+ callback(res)
+ },
+ error: function () {
+ if (typeof layer !== "undefined") {
+ layer.closeAll();
+ layer.msg('服务器错误');
+ }
+ }
+ });
+}
+
+function httpPost(url, data, callback){
+ $.ajax({
+ url: url,
+ type: 'post',
+ data: data,
+ dataType: 'json',
+ success: function (res) {
+ callback(res)
+ },
+ error: function () {
+ if (typeof layer !== "undefined") {
+ layer.closeAll();
+ layer.msg('服务器错误');
+ }
+ }
+ });
+}
+
+var isMobile = function(){
+ if( /Android|SymbianOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Windows Phone|Midp/i.test(navigator.userAgent)) {
+ return true;
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/route/app.php b/route/app.php
new file mode 100644
index 0000000..b32b677
--- /dev/null
+++ b/route/app.php
@@ -0,0 +1,78 @@
+
+// +----------------------------------------------------------------------
+use think\facade\Route;
+
+Route::pattern([
+ 'id' => '\d+',
+]);
+
+Route::any('/install', 'install/index')
+->middleware(\app\middleware\ViewOutput::class);
+
+Route::get('/verifycode', 'auth/verifycode')->middleware(\think\middleware\SessionInit::class)
+->middleware(\app\middleware\ViewOutput::class);
+Route::any('/login', 'auth/login')->middleware(\think\middleware\SessionInit::class)
+->middleware(\app\middleware\ViewOutput::class);
+Route::get('/logout', 'auth/logout');
+Route::any('/quicklogin', 'auth/quicklogin');
+
+Route::group(function () {
+ Route::any('/', 'index/index');
+ Route::post('/changeskin', 'index/changeskin');
+ Route::get('/cleancache', 'index/cleancache');
+ Route::any('/setpwd', 'index/setpwd');
+
+ Route::post('/user/data', 'user/user_data');
+ Route::post('/user/op', 'user/user_op');
+ Route::get('/user', 'user/user');
+
+ Route::post('/log/data', 'user/log_data');
+ Route::get('/log', 'user/log');
+
+ Route::post('/account/data', 'domain/account_data');
+ Route::post('/account/op', 'domain/account_op');
+ Route::get('/account', 'domain/account');
+
+ Route::post('/domain/data', 'domain/domain_data');
+ Route::post('/domain/op', 'domain/domain_op');
+ Route::post('/domain/list', 'domain/domain_list');
+ Route::get('/domain', 'domain/domain');
+
+ Route::post('/record/data/:id', 'domain/record_data');
+ Route::post('/record/add/:id', 'domain/record_add');
+ Route::post('/record/update/:id', 'domain/record_update');
+ Route::post('/record/delete/:id', 'domain/record_delete');
+ Route::post('/record/status/:id', 'domain/record_status');
+ Route::post('/record/remark/:id', 'domain/record_remark');
+ Route::post('/record/batch/:id', 'domain/record_batch');
+ Route::any('/record/log/:id', 'domain/record_log');
+ Route::get('/record/:id', 'domain/record');
+
+})->middleware(\app\middleware\CheckLogin::class)
+->middleware(\app\middleware\ViewOutput::class);
+
+Route::group('api', function () {
+ Route::post('/domain/:id', 'domain/domain_info');
+ Route::post('/domain', 'domain/domain_data');
+
+ Route::post('/record/data/:id', 'domain/record_data');
+ Route::post('/record/add/:id', 'domain/record_add');
+ Route::post('/record/update/:id', 'domain/record_update');
+ Route::post('/record/delete/:id', 'domain/record_delete');
+ Route::post('/record/status/:id', 'domain/record_status');
+ Route::post('/record/remark/:id', 'domain/record_remark');
+ Route::post('/record/batch/:id', 'domain/record_batch');
+
+})->middleware(\app\middleware\AuthApi::class);
+
+Route::miss(function() {
+ return response('404 Not Found')->code(404);
+});
diff --git a/runtime/.gitignore b/runtime/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/runtime/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/think b/think
new file mode 100644
index 0000000..2429d22
--- /dev/null
+++ b/think
@@ -0,0 +1,10 @@
+#!/usr/bin/env php
+console->run();
\ No newline at end of file