This commit is contained in:
net909 2024-04-03 20:51:10 +08:00
parent 73806d5b35
commit 44defa48fa
106 changed files with 11456 additions and 0 deletions

19
.example.env Normal file
View File

@ -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

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/.idea
/.vscode
/vendor
*.log
.env

72
README.md Normal file
View File

@ -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
```
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
```
### 版权信息
版权所有Copyright © 2023~2024 by 消失的彩虹海(https://blog.cccyun.cn)
### 其他推荐
- [彩虹云主机 - 免备案CDN/虚拟主机](https://www.cccyun.net/)
- [小白云高防云服务器](https://www.xiaobaiyun.cn/aff/GMLPMFOV)

1
app/.htaccess Normal file
View File

@ -0,0 +1 @@
deny from all

22
app/AppService.php Normal file
View File

@ -0,0 +1,22 @@
<?php
declare (strict_types = 1);
namespace app;
use think\Service;
/**
* 应用服务类
*/
class AppService extends Service
{
public function register()
{
// 服务注册
}
public function boot()
{
// 服务启动
}
}

122
app/BaseController.php Normal file
View File

@ -0,0 +1,122 @@
<?php
declare (strict_types = 1);
namespace app;
use think\App;
use think\exception\ValidateException;
use think\Validate;
use think\facade\View;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
protected $clientip;
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->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');
}
}

58
app/ExceptionHandle.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace app;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
// 其他错误交给系统处理
return parent::render($request, $e);
}
}

8
app/Request.php Normal file
View File

@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

210
app/common.php Normal file
View File

@ -0,0 +1,210 @@
<?php
// 应用公共文件
use think\facade\Db;
function get_curl($url, $post=0, $referer=0, $cookie=0, $header=0, $ua=0, $nobody=0, $addheader=0)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$httpheader[] = "Accept: */*";
$httpheader[] = "Accept-Encoding: gzip,deflate,sdch";
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
$httpheader[] = "Connection: close";
if($addheader){
$httpheader = array_merge($httpheader, $addheader);
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
if ($post) {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
}
if ($header) {
curl_setopt($ch, CURLOPT_HEADER, true);
}
if ($cookie) {
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
}
if($referer){
curl_setopt($ch, CURLOPT_REFERER, $referer);
}
if ($ua) {
curl_setopt($ch, CURLOPT_USERAGENT, $ua);
}
else {
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0");
}
if ($nobody) {
curl_setopt($ch, CURLOPT_NOBODY, 1);
}
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$ret = curl_exec($ch);
curl_close($ch);
return $ret;
}
function real_ip($type=0){
$ip = $_SERVER['REMOTE_ADDR'];
if($type<=0 && isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
foreach ($matches[0] AS $xip) {
if (filter_var($xip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$ip = $xip;
break;
}
}
} elseif ($type<=0 && isset($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif ($type<=1 && isset($_SERVER['HTTP_CF_CONNECTING_IP']) && filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
} elseif ($type<=1 && isset($_SERVER['HTTP_X_REAL_IP']) && filter_var($_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
$ip = $_SERVER['HTTP_X_REAL_IP'];
}
return $ip;
}
function strexists($string, $find) {
return !(strpos($string, $find) === FALSE);
}
function dstrpos($string, $arr) {
if(empty($string)) return false;
foreach((array)$arr as $v) {
if(strpos($string, $v) !== false) {
return true;
}
}
return false;
}
function checkmobile() {
$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
$ualist = array('android', 'midp', 'nokia', 'mobile', 'iphone', 'ipod', 'blackberry', 'windows phone');
if((dstrpos($useragent, $ualist) || strexists($_SERVER['HTTP_ACCEPT'], "VND.WAP") || strexists($_SERVER['HTTP_VIA'],"wap")))
return true;
else
return false;
}
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
$ckey_length = 4;
$key = md5($key);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == 'DECODE') {
if(((int)substr($result, 0, 10) == 0 || (int)substr($result, 0, 10) - time() > 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;
}

115
app/controller/Auth.php Normal file
View File

@ -0,0 +1,115 @@
<?php
namespace app\controller;
use app\BaseController;
use Exception;
use think\facade\Db;
use think\facade\View;
use app\lib\DnsHelper;
class Auth extends BaseController
{
public function verifycode()
{
return captcha();
}
public function login(){
$login_limit_count = 5;//登录失败次数
$login_limit_file = app()->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']);
}
}

561
app/controller/Domain.php Normal file
View File

@ -0,0 +1,561 @@
<?php
namespace app\controller;
use app\BaseController;
use think\facade\Db;
use think\facade\View;
use think\facade\Request;
use app\lib\DnsHelper;
class Domain extends BaseController
{
public function account(){
if(!checkPermission(2)) return $this->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'].'&timestamp='.$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")]);
}
}

96
app/controller/Index.php Normal file
View File

@ -0,0 +1,96 @@
<?php
namespace app\controller;
use app\BaseController;
use Exception;
use think\facade\Db;
use think\facade\View;
use think\facade\Cache;
use app\lib\DnsHelper;
class Index extends BaseController
{
public function index()
{
if(request()->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();
}
}

View File

@ -0,0 +1,84 @@
<?php
namespace app\controller;
use PDO;
use Exception;
use app\BaseController;
use think\facade\View;
use think\facade\Cache;
class Install extends BaseController
{
public function index()
{
if (file_exists(app()->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();
}
}

164
app/controller/User.php Normal file
View File

@ -0,0 +1,164 @@
<?php
namespace app\controller;
use app\BaseController;
use think\facade\Db;
use think\facade\View;
use think\facade\Request;
class User extends BaseController
{
public function user(){
if(!checkPermission(2)) return $this->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]);
}
}

File diff suppressed because one or more lines are too long

17
app/event.php Normal file
View File

@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [
],
];

91
app/lib/DnsHelper.php Normal file
View File

@ -0,0 +1,91 @@
<?php
namespace app\lib;
use think\facade\Db;
class DnsHelper
{
public static $dns_config = [
'aliyun' => [
'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;
}
}

35
app/lib/DnsInterface.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace app\lib;
interface DnsInterface
{
function getError();
function check();
function getDomainList($KeyWord=null, $PageNumber=1, $PageSize=20);
function getDomainRecords($PageNumber=1, $PageSize=20, $KeyWord = null, $SubDomain = null, $Type = null, $Line = null, $Status = null);
function getSubDomainRecords($SubDomain, $PageNumber=1, $PageSize=20, $Type = null, $Line = null);
function getDomainRecordInfo($RecordId);
function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Remark = null);
function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Remark = null);
function updateDomainRecordRemark($RecordId, $Remark);
function deleteDomainRecord($RecordId);
function setDomainRecordStatus($RecordId, $Status);
function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null);
function getRecordLine();
function getMinTTL();
}

314
app/lib/dns/aliyun.php Normal file
View File

@ -0,0 +1,314 @@
<?php
namespace app\lib\dns;
use app\lib\DnsInterface;
class aliyun implements DnsInterface {
private $AccessKeyId;
private $AccessKeySecret;
private $Endpoint = 'alidns.aliyuncs.com'; //API接入域名
private $Version = '2015-01-09'; //API版本号
private $error;
private $domain;
private $domainid;
private $domainInfo;
function __construct($config){
$this->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);
}
}

330
app/lib/dns/baidu.php Normal file
View File

@ -0,0 +1,330 @@
<?php
namespace app\lib\dns;
use app\lib\DnsInterface;
class baidu implements DnsInterface {
private $AccessKeyId;
private $SecretAccessKey;
private $endpoint = "dns.baidubce.com";
private $service = "dnspod";
private $version = "2021-03-23";
private $error;
private $domain;
private $domainid;
function __construct($config){
$this->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);
}
}

237
app/lib/dns/cloudflare.php Normal file
View File

@ -0,0 +1,237 @@
<?php
namespace app\lib\dns;
use app\lib\DnsInterface;
class cloudflare implements DnsInterface {
private $Email;
private $ApiKey;
private $baseUrl = 'https://api.cloudflare.com/client/v4';
private $error;
private $domain;
private $domainid;
function __construct($config){
$this->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);
}
}

362
app/lib/dns/dnspod.php Normal file
View File

@ -0,0 +1,362 @@
<?php
namespace app\lib\dns;
use app\lib\DnsInterface;
class dnspod implements DnsInterface {
private $SecretId;
private $SecretKey;
private $endpoint = "dnspod.tencentcloudapi.com";
private $service = "dnspod";
private $version = "2021-03-23";
private $error;
private $domain;
private $domainid;
function __construct($config){
$this->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);
}
}

345
app/lib/dns/huawei.php Normal file
View File

@ -0,0 +1,345 @@
<?php
namespace app\lib\dns;
use app\lib\DnsInterface;
class huawei implements DnsInterface {
private $AccessKeyId;
private $SecretAccessKey;
private $endpoint = "dns.myhuaweicloud.com";
private $service = "dnspod";
private $version = "2021-03-23";
private $error;
private $domain;
private $domainid;
function __construct($config){
$this->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);
}
}

226
app/lib/dns/west.php Normal file
View File

@ -0,0 +1,226 @@
<?php
namespace app\lib\dns;
use app\lib\DnsInterface;
class west implements DnsInterface {
private $username;
private $api_password;
private $baseUrl = 'https://api.west.cn/api/v2';
private $error;
private $domain;
private $domainid;
function __construct($config){
$this->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);
}
}

12
app/middleware.php Normal file
View File

@ -0,0 +1,12 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
// \think\middleware\SessionInit::class
\app\middleware\LoadConfig::class,
\app\middleware\AuthUser::class
];

View File

@ -0,0 +1,40 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use think\facade\Db;
class AuthApi
{
public function handle($request, \Closure $next)
{
$uid = input('post.uid/d');
$timestamp = input('post.timestamp');
$sign = input('post.sign');
if(!$uid || empty($timestamp) || empty($sign)){
return json(['code'=>-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);
}
}

View File

@ -0,0 +1,52 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use think\facade\Db;
class AuthUser
{
public function handle($request, \Closure $next)
{
$islogin = false;
$cookie = cookie('user_token');
$user = null;
if($cookie){
$token=authcode($cookie, 'DECODE', env('app.sys_key'));
if($token){
list($type, $uid, $sid, $expiretime) = explode("\t", $token);
if($type == 'user'){
$user = Db::name('user')->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);
}
}

View File

@ -0,0 +1,19 @@
<?php
declare (strict_types=1);
namespace app\middleware;
class CheckLogin
{
public function handle($request, \Closure $next)
{
if (!request()->islogin) {
if ($request->isAjax() || !$request->isGet()) {
return json(['code'=>-1, 'msg'=>'未登录'])->code(401);
}
return redirect((string)url('/login'));
}
return $next($request);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare (strict_types = 1);
namespace app\middleware;
use think\facade\Db;
use think\facade\Config;
class LoadConfig
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
if (!file_exists(app()->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',
]);
}
}

View File

@ -0,0 +1,24 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use think\facade\View;
class RefererCheck
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
if(!checkRefererHost()){
return response('Access Denied', 403);
}
return $next($request);
}
}

View File

@ -0,0 +1,25 @@
<?php
declare (strict_types=1);
namespace app\middleware;
use think\facade\View;
class ViewOutput
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
View::assign('islogin', $request->islogin);
View::assign('user', $request->user);
View::assign('cdnpublic', '//lib.baomitu.com/');
View::assign('skin', getAdminSkin());
return $next($request);
}
}

9
app/provider.php Normal file
View File

@ -0,0 +1,9 @@
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
];

9
app/service.php Normal file
View File

@ -0,0 +1,9 @@
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
];

63
app/sql/install.sql Normal file
View File

@ -0,0 +1,63 @@
DROP TABLE IF EXISTS `dnsmgr_account`;
CREATE TABLE `dnsmgr_account` (
`id` int(11) unsigned NOT NULL auto_increment,
`type` varchar(20) NOT NULL,
`ak` varchar(256) DEFAULT NULL,
`sk` varchar(256) DEFAULT NULL,
`ext` varchar(256) DEFAULT NULL,
`remark` varchar(100) DEFAULT NULL,
`addtime` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `dnsmgr_domain`;
CREATE TABLE `dnsmgr_domain` (
`id` int(11) unsigned NOT NULL auto_increment,
`aid` int(11) unsigned NOT NULL,
`name` varchar(128) NOT NULL,
`thirdid` varchar(60) DEFAULT NULL,
`addtime` datetime DEFAULT NULL,
`is_hide` tinyint(1) NOT NULL DEFAULT '0',
`is_sso` tinyint(1) NOT NULL DEFAULT '0',
`recordcount` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `dnsmgr_user`;
CREATE TABLE `dnsmgr_user` (
`id` int(11) unsigned NOT NULL auto_increment,
`username` varchar(64) NOT NULL,
`password` varchar(80) NOT NULL,
`is_api` tinyint(1) NOT NULL DEFAULT '0',
`apikey` varchar(32) DEFAULT NULL,
`level` int(11) NOT NULL DEFAULT '0',
`regtime` datetime DEFAULT NULL,
`lasttime` datetime DEFAULT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1000;
DROP TABLE IF EXISTS `dnsmgr_permission`;
CREATE TABLE `dnsmgr_permission` (
`id` int(11) unsigned NOT NULL auto_increment,
`uid` int(11) unsigned NOT NULL,
`domain` varchar(128) NOT NULL,
`sub` varchar(80) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `dnsmgr_log`;
CREATE TABLE `dnsmgr_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) unsigned NOT NULL,
`action` varchar(40) NOT NULL,
`domain` varchar(128) NOT NULL DEFAULT '',
`data` varchar(500) DEFAULT NULL,
`addtime` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
KEY `domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

112
app/view/auth/login.html Normal file
View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>聚合DNS管理系统 - 登录</title>
<link href="{$cdnpublic}twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<link href="{$cdnpublic}font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="/static/css/app.min.css" rel="stylesheet">
<link href="/static/css/skins/{$skin}.css" rel="stylesheet">
<link href="/static/css/bootstrap-table.css" rel="stylesheet"/>
<script src="{$cdnpublic}jquery/3.6.4/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="{$cdnpublic}html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="{$cdnpublic}respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<style type="text/css">
body{color:#999;background-color:#f1f4fd;background-size:cover}
a{color:#444}
.login-screen{max-width:430px;padding:0;margin:100px auto 0 auto}
.login-screen .well{border-radius:3px;-webkit-box-shadow:0 0 30px rgba(0,0,0,.1);box-shadow:0 0 30px rgba(0,0,0,.1);background:#fff;border:none;padding:0}
@media (max-width:767px){.login-screen{padding:20px 0}
}
.profile-img-card{width:100px;height:100px;display:block;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;margin:-93px auto 30px;border:5px solid #fff}
.profile-name-card{text-align:center}
.login-head{background:#899fe1;border-radius:3px 3px 0 0}
.login-form{padding:40px 30px;position:relative;z-index:99}
#login-form{margin-top:20px}
#login-form .input-group{margin-bottom:15px}
#login-form .form-control{font-size:14px}
</style>
</head>
<body>
<div class="container">
<div class="login-wrapper">
<div class="login-screen">
<div class="well">
<div class="login-head">
<img src="/static/images/login-head.png" style="width:100%;"/>
</div>
<div class="login-form">
<img id="profile-img" class="profile-img-card" src="/static/images/user.png"/>
<p id="profile-name" class="profile-name-card"></p>
<form action="" id="login-form" onsubmit="return doLogin()">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-user" aria-hidden="true"></span></div>
<input type="text" class="form-control" placeholder="用户名" name="username" required="required"/>
</div>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="password" class="form-control" placeholder="密码" name="password" required="required"/>
</div>
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock" aria-hidden="true"></span></div>
<input type="text" class="form-control input-lg" placeholder="验证码" name="code" autocomplete="off" required="required"/>
<span class="input-group-addon" style="padding: 0">
<img id="verifycode" src="/verifycode" height="45" onclick="this.src='/verifycode?r='+Math.random();" title="点击更换验证码">
</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success btn-lg btn-block" id="submit" style="background:#708eea;">登 录</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
function doLogin(){
var username = $("input[name='username']").val();
var password = $("input[name='password']").val();
var code = $("input[name='code']").val();
if(username == ''){
layer.msg('用户名不能为空', {icon: 2});
return false;
}
if(password == ''){
layer.msg('密码不能为空', {icon: 2});
return false;
}
if(code == ''){
layer.msg('验证码不能为空', {icon: 2});
return false;
}
var ii = layer.load(2);
$.ajax({
type: "POST",
url: "/login",
data: $("#login-form").serialize(),
dataType: 'json',
success: function(data){
layer.close(ii);
if(data.code == 0){
layer.msg('登录成功,正在跳转到首页', {icon: 1,shade: 0.01,time: 15000});
window.location.href = '/';
}else{
if(data.vcode==1){
$("#verifycode").attr('src', '/verifycode?r='+Math.random())
}
layer.alert(data.msg, {icon: 2});
}
}
});
return false;
}
</script>
</body>
</html>

196
app/view/common/layout.html Normal file
View File

@ -0,0 +1,196 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>{block name="title"}标题{/block}</title>
<link href="{$cdnpublic}twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<link href="{$cdnpublic}font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="{$cdnpublic}select2/4.0.13/css/select2.min.css" rel="stylesheet"/>
<link href="/static/css/app.min.css" rel="stylesheet">
<link href="/static/css/skins/{$skin}.css" rel="stylesheet">
<link href="/static/css/bootstrap-table.css" rel="stylesheet"/>
<script src="{$cdnpublic}jquery/3.6.4/jquery.min.js"></script>
<!--[if lt IE 9]>
<script src="{$cdnpublic}html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="{$cdnpublic}respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<style>
.type-logo{width: 18px;margin-top: -2px;padding-right: 4px;}
</style>
</head>
<body class="hold-transition {$skin} sidebar-mini">
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
<a href="javascript:;" class="logo">
<!-- mini logo for sidebar mini 50x50 pixels -->
<span class="logo-mini"><i class="fa fa-cube"></i></span>
<!-- logo for regular state and mobile devices -->
<span class="logo-lg"></span>聚合DNS管理系统
</a>
<!-- Header Navbar: style can be found in header.less -->
<nav class="navbar navbar-static-top">
<!-- Sidebar toggle button-->
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
<span class="visible-xs-inline">菜单</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li>
<a href="/" title="平台首页"><i class="fa fa-home fa-fw"></i></a>
</li>
<li class="hidden-xs">
<a href="#" data-toggle="fullscreen" title="全屏"><i class="fa fa-arrows-alt fa-fw"></i></a>
</li>
<li class="hidden-xs">
<a href="#" data-toggle="control-sidebar" title="更换皮肤"><i class="fa fa-wrench fa-fw"></i></a>
</li>
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="/static/images/user.png" class="user-image" alt="User Image">
<span class="hidden-xs">{$user.username}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="/static/images/user.png" class="img-circle" alt="User Image">
<p>
{$user.username}
<small>{$user.regtime}</small>
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
{if request()->user['type'] eq 'user'}<div class="pull-left">
<a href="/setpwd" class="btn btn-primary"><i class="fa fa-lock"></i> 修改密码</a>
</div>{/if}
<div class="pull-right">
<a href="/logout" class="btn btn-danger"><i class="fa fa-sign-out"></i> 退出登录</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img src="/static/images/user.png" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>{$user.username}</p>
<i class="fa fa-circle text-success"></i> Online
</div>
</div>
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu" data-widget="tree">
<li class="header">功能导航</li>
{if request()->user['type'] eq 'user'}<li class="{:checkIfActive('index')}">
<a href="/"><i class="fa fa-home fa-fw"></i> <span>后台首页</span></a>
</li>{/if}
<li class="{:checkIfActive('domain,record,record_log')}">
<a href="/domain"><i class="fa fa-list-ul fa-fw"></i> <span>域名管理</span></a>
</li>
{if request()->user['level'] eq 2}<li class="{:checkIfActive('account')}">
<a href="/account"><i class="fa fa-lock fa-fw"></i> <span>域名账户</span></a>
</li>
<li class="{:checkIfActive('user')}">
<a href="/user"><i class="fa fa-user fa-fw"></i> <span>用户管理</span></a>
</li>{/if}
<li class="{:checkIfActive('log')}">
<a href="/log"><i class="fa fa-list fa-fw"></i> <span>操作日志</span></a>
</li>
{if request()->user['type'] eq 'user'}
<li class="">
<a href="https://www.showdoc.com.cn/dnsmgr/11058996709621562" target="_blank"><i class="fa fa-book fa-fw"></i> <span>接口文档</span></a>
</li>
{/if}
</ul>
</section>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
<!-- Main content -->
<section class="content">
{block name="main"}主内容{/block}
</section>
<!-- /.content -->
</div>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark" style="display: none;">
<!-- Tab panes -->
<div class="tab-content">
<!-- Home tab content -->
<div class="tab-pane active">
<h4 class="control-sidebar-heading">皮肤</h4>
<ul class="list-unstyled clearfix skin-list">
<li><a href="javascript:;" data-skin="skin-blue" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #4e73df;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Blue</p></li>
<li><a href="javascript:;" data-skin="skin-black" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black</p></li>
<li><a href="javascript:;" data-skin="skin-purple" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #605ca8;"></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Purple</p></li>
<li><a href="javascript:;" data-skin="skin-green" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-green-active"></span><span class="bg-green" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Green</p></li>
<li><a href="javascript:;" data-skin="skin-red" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-red-active"></span><span class="bg-red" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Red</p></li>
<li><a href="javascript:;" data-skin="skin-yellow" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 7px;" class="bg-yellow-active"></span><span class="bg-yellow" style="width: 80%; height: 7px;"></span></div><div><span style="width: 20%; height: 20px; background: #000;"></span><span style="width: 80%; height: 20px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-blue-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #4e73df;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Blue Light</p></li>
<li><a href="javascript:;" data-skin="skin-black-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #000;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Black Light</p></li>
<li><a href="javascript:;" data-skin="skin-purple-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px; background: #605ca8;"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Purple Light</p></li>
<li><a href="javascript:;" data-skin="skin-green-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-green"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Green Light</p></li>
<li><a href="javascript:;" data-skin="skin-red-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-red"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Red Light</p></li>
<li><a href="javascript:;" data-skin="skin-yellow-light" class="clearfix full-opacity-hover"><div><span style="width: 100%; height: 7px;" class="bg-yellow"></span></div><div><span style="width: 100%; height: 20px; background: #f9fafc;"></span></div></a><p class="text-center no-margin" style="font-size: 12px">Yellow Light</p></li>
<li><a href="javascript:;" data-skin="skin-black-blue" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #4e73df;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Blue</p></li>
<li><a href="javascript:;" data-skin="skin-black-purple" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #605ca8;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Purple</p></li>
<li><a href="javascript:;" data-skin="skin-black-green" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-green"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Green</p></li>
<li><a href="javascript:;" data-skin="skin-black-red" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-red"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Red</p></li>
<li><a href="javascript:;" data-skin="skin-black-yellow" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px;" class="bg-yellow"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Yellow</p></li>
<li><a href="javascript:;" data-skin="skin-black-pink" class="clearfix full-opacity-hover"><div><span style="width: 20%; height: 27px; background: #000;"><span style="width: 100%; height: 3px; margin-top:10px; background: #f5549f;"></span></span><span style="width: 80%; height: 27px; background: #f4f5f7;"></span></div></a><p class="text-center no-margin">Black Pink</p></li>
</ul>
</div>
<!-- /.tab-pane -->
</div>
</aside>
<!-- /.control-sidebar -->
<!-- Add the sidebar's background. This div must be placed
immediately after the control sidebar -->
<div class="control-sidebar-bg"></div>
</div>
<!-- ./wrapper -->
<script src="<?php echo $cdnpublic?>twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="<?php echo $cdnpublic?>fastclick/1.0.6/fastclick.min.js"></script>
<script src="/static/js/app.js"></script>
<script>
document.addEventListener('pointerdown', function(e) {
if (e.target.closest('table')) {
return;
}
startX = e.clientX;
// 如果触摸开始于屏幕左边缘的10%区域内
if (startX < window.innerWidth * 0.1) {
e.preventDefault(); // 尝试阻止默认行为
}
}, false);
</script>
{block name="script"}{/block}
</body>
</html>

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>温馨提示</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit"/>
<style type="text/css">
*{box-sizing:border-box;margin:0;padding:0;font-family:Lantinghei SC,Open Sans,Arial,Hiragino Sans GB,Microsoft YaHei,"微软雅黑",STHeiti,WenQuanYi Micro Hei,SimSun,sans-serif;-webkit-font-smoothing:antialiased}
body{padding:70px 0;background:#edf1f4;font-weight:400;font-size:1pc;-webkit-text-size-adjust:none;color:#333}
a{outline:0;color:#3498db;text-decoration:none;cursor:pointer}
.system-message{margin:20px 5%;padding:40px 20px;background:#fff;box-shadow:1px 1px 1px hsla(0,0%,39%,.1);text-align:center}
.system-message h1{margin:0;margin-bottom:9pt;color:#444;font-weight:400;font-size:40px}
.system-message .jump,.system-message .image{margin:20px 0;padding:0;padding:10px 0;font-weight:400}
.system-message .jump{font-size:14px}
.system-message .jump a{color:#333}
.system-message p{font-size:9pt;line-height:20px}
.system-message .btn{display:inline-block;margin-right:10px;width:138px;height:2pc;border:1px solid #44a0e8;border-radius:30px;color:#44a0e8;text-align:center;font-size:1pc;line-height:2pc;margin-bottom:5px;}
.success .btn{border-color:#69bf4e;color:#69bf4e}
.error .btn{border-color:#ff8992;color:#ff8992}
.info .btn{border-color:#3498db;color:#3498db}
.copyright p{width:100%;color:#919191;text-align:center;font-size:10px}
.system-message .btn-grey{border-color:#bbb;color:#bbb}
.clearfix:after{clear:both;display:block;visibility:hidden;height:0;content:"."}
@media (max-width:768px){body {padding:20px 0;}}
@media (max-width:480px){.system-message h1{font-size:30px;}}
</style>
</head>
<body>
<div class="system-message {$code}">
<div class="image">
<img src="/static/images/{$code}.svg" alt="" width="150" />
</div>
<h1>{$msg}</h1>
{if $url}
<p class="jump">
页面将在 <span id="wait">{$wait}</span> 秒后自动跳转
</p>
{/if}
<p class="clearfix">
<a href="javascript:history.go(-1);" class="btn btn-grey">返回上一页</a>
{if $url}
<a href="{$url}" class="btn btn-primary">立即跳转</a>
{/if}
</p>
</div>
<script type="text/javascript">
(function () {
var wait = document.getElementById('wait');
var interval = setInterval(function () {
var time = --wait.innerHTML;
if (time <= 0) {
location.href = "{$url}";
clearInterval(interval);
}
}, 1000);
})();
</script>
</body>
</html>

View File

@ -0,0 +1,234 @@
{extend name="common/layout" /}
{block name="title"}域名账户{/block}
{block name="main"}
<div class="modal" id="modal-store" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content animated flipInX">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">添加/修改域名账户</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="form-store">
<input type="hidden" name="action"/>
<input type="hidden" name="id"/>
<div class="form-group">
<label class="col-sm-3 control-label">所属平台</label>
<div class="col-sm-9">
<select name="type" class="form-control">
{foreach $dnsconfig as $k=>$v}
<option value="{$k}">{$v['name']}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" id="ak_name">AccessKey</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="ak" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" id="sk_name">SecretKey</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="sk" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right" id="ext_name">扩展字段</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="ext" placeholder="没有请勿填写">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">备注</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="remark" placeholder="备注选填">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="store" onclick="save()">保存</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="AccessKey或备注">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新域名账户列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="javascript:addframe()" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
var dnsconfig = {$dnsconfig|json_encode|raw};
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
$("#listTable").bootstrapTable({
url: '/account/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '所属平台',
formatter: function(value, row, index) {
return '<img src="/static/images/'+row.type+'.ico" class="type-logo"></img>'+value;
}
},
{
field: 'ak',
title: 'AccessKey'
},
{
field: 'remark',
title: '备注'
},
{
field: 'addtime',
title: '添加时间'
},
{
field: '',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="javascript:editframe('+row.id+')" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
return html;
}
},
],
})
$("select[name=type]").change(function(){
var type = $(this).val();
if(dnsconfig[type] == undefined) return;
$("#ak_name").html(dnsconfig[type].config.ak);
$("#sk_name").html(dnsconfig[type].config.sk);
if(dnsconfig[type].config.ext == undefined) dnsconfig[type].config.ext = '扩展字段';
else $("#ext_name").html(dnsconfig[type].config.ext);
});
})
function addframe(){
$("#modal-store").modal('show');
$("#modal-title").html("添加域名账户");
$("#form-store input[name=action]").val("add");
$("#form-store input[name=id]").val('');
$("#form-store input[name=ak]").val('');
$("#form-store input[name=sk]").val('');
$("#form-store input[name=ext]").val('');
$("#form-store input[name=remark]").val('');
$("select[name=type]").change();
}
function editframe(id){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/account/op/act/get',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("#modal-store").modal('show');
$("#modal-title").html("修改域名账户");
$("#form-store input[name=action]").val("edit");
$("#form-store input[name=id]").val(data.data.id);
$("#form-store select[name=type]").val(data.data.type);
$("#form-store input[name=ak]").val(data.data.ak);
$("#form-store input[name=sk]").val(data.data.sk);
$("#form-store input[name=ext]").val(data.data.ext);
$("#form-store input[name=remark]").val(data.data.remark);
$("select[name=type]").change();
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function save(){
if($("#form-store input[name=username]").val()==''){
layer.alert('请确保各项不能为空!');return false;
}
var act = $("#form-store input[name=action]").val();
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/account/op/act/'+act,
data : $("#form-store").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
$("#modal-store").modal('hide');
searchSubmit();
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function delItem(id) {
var confirmobj = layer.confirm('确定要删除此域名账户吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/account/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchSubmit();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
</script>
{/block}

361
app/view/domain/domain.html Normal file
View File

@ -0,0 +1,361 @@
{extend name="common/layout" /}
{block name="title"}域名管理{/block}
{block name="main"}
<div class="modal" id="modal-store" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content animated flipInX">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">添加域名</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="form-store">
<input type="hidden" name="recordcount"/>
<div class="form-group">
<label class="col-sm-3 control-label">域名账户</label>
<div class="col-sm-9">
<select name="aid" class="form-control">
{foreach $accounts as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">选择域名</label>
<div class="col-sm-9">
<select name="domain" id="domainList" class="form-control"></select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="store" onclick="saveAdd()">添加</button>
</div>
</div>
</div>
</div>
<div class="modal" id="modal-store2" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content animated flipInX">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">修改域名配置</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="form-store2">
<input type="hidden" name="id"/>
<div class="form-group">
<label class="col-sm-3 control-label">是否隐藏</label>
<div class="col-sm-9">
<div class="input-group">
<select name="is_hide" class="form-control">
<option value="0"></option>
<option value="1"></option>
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="隐藏后仅管理员可见,普通用户在列表不可见" data-original-title="说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">对接开关</label>
<div class="col-sm-9">
<div class="input-group">
<select name="is_sso" class="form-control">
<option value="0"></option>
<option value="1"></option>
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="对接开关开启后将允许通过API接口一键登录该域名独立管理面板" data-original-title="说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="store" onclick="saveEdit()">保存</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="域名">
</div>
<div class="form-group">
<select name="type" class="form-control"><option value="">所有平台</option>{foreach $types as $k=>$v}
<option value="{$k}">{$v}</option>
{/foreach}</select>
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新域名列表"><i class="fa fa-refresh"></i> 刷新</a>
{if request()->user['level'] eq 2}<a href="javascript:addframe()" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>{/if}
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/select2.min.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/i18n/zh-CN.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
var userLevel = "{:request()->user['level']}";
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
$("#listTable").bootstrapTable({
url: '/domain/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'typename',
title: '平台账户',
formatter: function(value, row, index) {
return '<span title="'+row.remark+'" data-toggle="tooltip" data-placement="right" title="Tooltip on right"><img src="/static/images/'+row.type+'.ico" class="type-logo"></img>'+value+'('+row.aid+')</span>';
}
},
{
field: 'name',
title: '域名',
formatter: function(value, row, index) {
return '<a href="/record/'+row.id+'" title="进入解析记录管理" onclick="loading()">'+value+'</a>';
}
},
{
field: 'recordcount',
title: '记录数'
},
{
field: 'addtime',
title: '添加时间'
},
{
field: 'is_hide',
title: '是否隐藏',
formatter: function(value, row, index) {
return value==1?'<font color="grey"></font>':'<font color="blue"></font>';
}
},
{
field: 'is_sso',
title: '对接开关',
formatter: function(value, row, index) {
return value==1?'<font color="green"></font>':'<font color="red"></font>';
}
},
{
field: '',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="/record/'+row.id+'" class="btn btn-success btn-xs" onclick="loading()">解析</a>';
if(userLevel == '2'){
html += ' <a href="javascript:editframe('+row.id+')" class="btn btn-info btn-xs">配置</a>';
html += ' <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
}
return html;
}
},
],
onLoadSuccess: function(data) {
$('[data-toggle="tooltip"]').tooltip()
}
})
$("#form-store select[name=aid]").change(function(){
getDomainList()
})
$('#domainList').on('select2:select', function (e) {
var data = e.params.data;
$("#form-store input[name=recordcount]").val(data.recordcount);
});
$('[data-toggle="popover"]').popover()
})
function addframe(){
$("#modal-store").modal('show');
var aid = $("#form-store select[name=aid]").val();
if(aid != ''){
getDomainList()
}
}
function saveAdd(){
var aid = $("#form-store select[name=aid]").val();
var select = $('#domainList').select2('data');
if(select.length == 0){
layer.alert('请选择域名!');return false;
}
var name = select[0].text;
var thirdid = select[0].id;
var recordcount = $("#form-store input[name=recordcount]").val();
if(aid=='' || thirdid==''){
layer.alert('请确保各项不能为空!');return false;
}
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/domain/op/act/add',
data : {aid: aid, thirdid: thirdid, name: name, recordcount: recordcount},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
$("#modal-store").modal('hide');
searchSubmit();
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function editframe(id){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/domain/op/act/get',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("#modal-store2").modal('show');
$("#form-store2 input[name=id]").val(data.data.id);
$("#form-store2 select[name=is_hide]").val(data.data.is_hide);
$("#form-store2 select[name=is_sso]").val(data.data.is_sso);
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function saveEdit(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/domain/op/act/edit',
data : $("#form-store2").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
$("#modal-store2").modal('hide');
searchSubmit();
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function delItem(id) {
var confirmobj = layer.confirm('确定要删除此域名吗?删除域名不会影响已添加的解析', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/domain/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchSubmit();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
function getDomainList(){
$('#domainList').empty();
$('#domainList').select2({
placeholder: '请选择域名',
language: "zh-CN",
ajax:{
url: '/domain/list',
type: "post",
dataType: 'json',
delay: 300,
data: function(params) {
var aid = $("#form-store select[name=aid]").val();
var data = {
aid: aid,
kw: params.term,
page: params.page || 1,
pagesize: 10
};
return data;
},
processResults: function (data, params) {
params.page = params.page || 1
if(data.code == 0){
var resultData = [];
for (var i = 0; i < data.data.list.length; i++) {
resultData.push({'id': data.data.list[i].DomainId, 'text': data.data.list[i].Domain, 'recordcount': data.data.list[i].RecordCount});
}
return {
results: resultData,
pagination: {
more: data.data.total > params.page * 10
}
};
}else{
layer.alert(data.msg, {icon: 2});
return {
results: []
};
}
}
},
cache:false
});
}
function loading(){
layer.load(2);
}
</script>
{/block}

47
app/view/domain/log.html Normal file
View File

@ -0,0 +1,47 @@
{extend name="common/layout" /}
{block name="title"}域名日志{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
$("#listTable").bootstrapTable({
url: '/record/log/{$domainId}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'time',
title: '操作时间'
},
{
field: 'data',
title: '操作行为'
}
],
})
})
</script>
{/block}

436
app/view/domain/record.html Normal file
View File

@ -0,0 +1,436 @@
{extend name="common/layout" /}
{block name="title"}解析管理 - {$domainName}{/block}
{block name="main"}
<style>
td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:360px;}
</style>
<div class="modal" id="modal-store" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content animated flipInX">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">添加/修改解析</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="form-store">
<input type="hidden" name="action"/>
<input type="hidden" name="recordid"/>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">主机记录</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="name" placeholder="填写域名前缀,支持多级" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">记录类型</label>
<div class="col-sm-9">
<select name="type" class="form-control">
<option value="A">A</option>
<option value="CNAME">CNAME</option>
<option value="AAAA">AAAA</option>
<option value="NS">NS</option>
<option value="MX">MX</option>
<option value="SRV">SRV</option>
<option value="TXT">TXT</option>
<option value="CAA">CAA</option>
{if $dnsconfig.redirect}<option value="REDIRECT_URL">显性URL</option>
<option value="FORWARD_URL">隐性URL</option>{/if}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">线路类型</label>
<div class="col-sm-9" id="line_list">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">记录值</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="value" placeholder="输入记录值" required>
</div>
</div>
<div class="form-group" style="display:none" id="mx_type">
<label class="col-sm-3 control-label no-padding-right">MX优先级</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="mx" value="10">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">TTL</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="ttl" value="600" placeholder="指解析结果在DNS服务器中的缓存时间" required min="{$minTTL}">
</div>
</div>
{if $dnsconfig.remark == 2}<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">备注</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="remark" placeholder="">
</div>
</div>{/if}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="store" onclick="save()">保存</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-default">
<div class="panel-heading">
<h3 class="panel-title">{if request()->user['type'] eq 'user'}<a href="/domain" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>{/if}{$domainName}</h3>
</div>
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="keyword" placeholder="输入关键字">
</div>
<div class="form-group">
<select name="status" class="form-control"><option value="">所有状态</option><option value="1">启用</option><option value="0">暂停</option></select>
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新解析记录列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="javascript:addframe()" class="btn btn-success"><i class="fa fa-plus"></i> 添加记录</a>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">批量操作 <span class="caret"></span></button>
<ul class="dropdown-menu"><li><a href="javascript:operation('open')">启用</a></li><li><a href="javascript:operation('pause')">暂停</a></li><li><a href="javascript:operation('delete')">删除</a></li></ul>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">日志 <span class="caret"></span></button>
<ul class="dropdown-menu"><li><a href="/log?domain={$domainName}">本站日志</a></li>{if $dnsconfig.log}<li><a href="/record/log/{$domainId}">域名日志</a></li>{/if}</ul>
</div>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
<script src="/static/js/bootstrapValidator.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
var recordLine = {$recordLine|json_encode|raw};
var dnsconfig = {$dnsconfig|json_encode|raw};
var defaultLine = recordLine[0].id;
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
$("#listTable").bootstrapTable({
url: '/record/data/{$domainId}',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
uniqueId: 'RecordId',
sidePagination: "{if $dnsconfig.type=='baidu'}client{else}server{/if}",
columns: [
{
field: '',
checkbox: true
},
{
field: 'RecordId',
visible: false,
title: '记录ID'
},
{
field: 'Name',
title: '主机记录'
},
{
field: 'Type',
title: '记录类型'
},
{
field: 'LineName',
title: '线路类型'
},
{
field: 'Value',
title: '记录值',
formatter: function(value, row, index) {
if(row.Type == 'MX') return value + ' | '+row.MX;
return value;
}
},
{
field: 'TTL',
title: 'TTL'
},
{
field: 'Remark',
visible: dnsconfig.remark > 0,
title: '备注'
},
{
field: 'UpdateTime',
visible: false,
title: '最后更新时间'
},
{
field: 'Status',
title: '状态',
formatter: function(value, row, index) {
if(value == '1'){
return '<font color="green"><i class="fa fa-check-circle"></i>启用</font>';
}else{
return '<font color="orange"><i class="fa fa-pause-circle"></i>暂停</font>';
}
}
},
{
field: '',
title: '操作',
formatter: function(value, row, index) {
if((row.Type == 'NS' || row.Type == 'SOA') && row.Name == '@') return '-';
var html = '<a href="javascript:editframe(\''+row.RecordId+'\')" class="btn btn-primary btn-xs">修改</a>&nbsp;&nbsp;';
if(dnsconfig.status){
if(row.Status == '1'){
html += '<a href="javascript:setStatus(\''+row.RecordId+'\', \'0\')" class="btn btn-warning btn-xs">暂停</a>&nbsp;&nbsp;';
}else{
html += '<a href="javascript:setStatus(\''+row.RecordId+'\', \'1\')" class="btn btn-success btn-xs">启用</a>&nbsp;&nbsp;';
}
}
html += '<a href="javascript:delItem(\''+row.RecordId+'\')" class="btn btn-danger btn-xs">删除</a>&nbsp;&nbsp;';
if(dnsconfig.remark == 1){
html += '<a href="javascript:setRemark(\''+row.RecordId+'\')" class="btn btn-info btn-xs">备注</a>';
}
return html;
}
},
],
});
$("select[name=type]").change(function(){
if($(this).val() == 'MX'){
$("#mx_type").show();
}else{
$("#mx_type").hide();
}
});
$("#form-store").bootstrapValidator();
})
function initLine(option){
option = option || '';
$("#line_list").empty();
$.each(recordLine, function(index, item){
if(item.parent == null){
option += '<option value="'+item.id+'">'+item.name+'</option>';
}
})
$("#line_list").append('<select name="line" class="form-control" onchange="changeLine(this)">'+option+'</select>');
}
function changeLine(obj){
var line = $(obj).val();
var flag = false;
$("#line_list").children().each(function(index, elem){
if(flag) $(elem).remove()
if(obj == elem){
flag = true;
}
})
if($(obj).find("option:selected").text() == '子集线路(非必填)') return;
var tempLine = recordLine.filter((x) => x.parent == line)
if(tempLine.length > 0){
var option = '<option value="'+line+'">子集线路(非必填)</option>';
$.each(tempLine, function(index, item){
option += '<option value="'+item.id+'">'+item.name+'</option>';
})
$("#line_list").append('<select name="line" class="form-control" onchange="changeLine(this)">'+option+'</select>');
}
}
function addframe(){
$("#modal-store").modal('show');
$("#modal-title").html("添加记录");
$("#form-store input[name=action]").val("add");
$("#form-store input[name=recordid]").val('');
$("#form-store input[name=name]").val('');
$("#form-store select[name=type]").val('A');
$("select[name=type]").change();
initLine();
$("#form-store input[name=value]").val('');
$("#form-store input[name=ttl]").val('600');
$("#form-store input[name=remark]").val('');
$("#form-store").data("bootstrapValidator").resetForm();
}
function editframe(recordid){
var row = $("#listTable").bootstrapTable('getRowByUniqueId', recordid);
$("#modal-store").modal('show');
$("#modal-title").html("修改记录");
$("#form-store input[name=action]").val("update");
$("#form-store input[name=recordid]").val(recordid);
$("#form-store input[name=name]").val(row.Name);
$("#form-store select[name=type]").val(row.Type);
$("select[name=type]").change();
initLine();
if($('#form-store select[name=line] option[value="'+row.Line+'"]').length > 0){
$("#form-store select[name=line]").val(row.Line);
$("#form-store select[name=line]").change();
}else{
initLine('<option value="'+row.Line+'">'+row.LineName+'</option>');
}
$("#form-store input[name=value]").val(row.Value);
$("#form-store input[name=mx]").val(row.MX);
$("#form-store input[name=ttl]").val(row.TTL);
$("#form-store input[name=remark]").val(row.Remark);
$("#form-store").data("bootstrapValidator").resetForm();
}
function save(){
$("#form-store").data("bootstrapValidator").validate();
if(!$("#form-store").data("bootstrapValidator").isValid()){
return;
}
var act = $("#form-store input[name=action]").val();
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/'+act+'/{$domainId}',
data : $("#form-store").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
$("#modal-store").modal('hide');
searchSubmit();
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function setStatus(recordid, status){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/status/{$domainId}',
data : {recordid: recordid, status: status},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
layer.msg(status=='1'?'开启成功':'暂停成功', {icon: 1, time:500});
searchSubmit();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}
function delItem(recordid) {
var confirmobj = layer.confirm('确定要删除此解析记录吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/delete/{$domainId}',
data : {recordid: recordid},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
layer.msg('删除成功', {icon: 1, time:800});
searchSubmit();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
function setRemark(recordid) {
var row = $("#listTable").bootstrapTable('getRowByUniqueId', recordid);
layer.open({
type: 1,
area: ['350px'],
closeBtn: 2,
title: '编辑备注',
content: '<div style="padding:15px"><div class="form-group"><input class="form-control" type="text" name="remark" value="'+(row.Remark==null?'':row.Remark)+'" autocomplete="off" placeholder="备注信息"></div></div>',
btn: ['确认', '取消'],
yes: function(){
var remark = $("input[name='remark']").val();
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/record/remark/{$domainId}',
data : {recordid:recordid, remark:remark},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
layer.msg('保存成功', {icon: 1, time:800});
searchSubmit();
}else{
layer.alert(data.msg, {icon:2});
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
});
}
function operation(action){
var rows = $("#listTable").bootstrapTable('getSelections');
if(rows.length == 0){
layer.msg('请选择要操作的记录');
return;
}
var ids = [];
$.each(rows, function(index, item){
ids.push(item.RecordId);
})
var confirmobj = layer.confirm('确定要'+(action=='open'?'启用':(action=='pause'?'暂停':'删除'))+'所选记录吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/record/batch/{$domainId}',
data : {action: action, recordids: ids},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
layer.alert(data.msg, {icon: 1});
searchSubmit();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
</script>
{/block}

23
app/view/index/error.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>抱歉,出错了</title>
<link href="//res.wx.qq.com/open/libs/weui/0.4.3/weui.css" rel="stylesheet">
<style>.page{position:absolute;top:0;right:0;bottom:0;left:0;overflow-y:auto;-webkit-overflow-scrolling:touch;box-sizing:border-box}</style>
</head>
<body>
<div class="weui_msg">
<div class="weui_icon_area"><i class="weui_icon_info weui_icon_msg"></i></div>
<div class="weui_text_area">
<h4 class="weui_msg_title">{$errmsg}</h4>
</div>
</div>
<script>
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
},{ passive: false });
</script>
</body>
</html>

175
app/view/index/index.html Normal file
View File

@ -0,0 +1,175 @@
{extend name="common/layout" /}
{block name="title"}聚合DNS管理系统{/block}
{block name="main"}
<style>
.table>tbody>tr>td{white-space: normal;}
.query-title {
background-color:#f5fafe;
word-break: keep-all;
}
.query-result{
word-break: break-all;
}
</style>
<div class="row">
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-aqua">
<div class="inner">
<h3 id="count1">0</h3>
<p>域名数量</p>
</div>
<div class="icon">
<i class="fa fa-list-ul"></i>
</div>
<a href="/domain" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-green">
<div class="inner">
<h3 id="count2">0</h3>
<p>用户数量</p>
</div>
<div class="icon">
<i class="fa fa-users"></i>
</div>
<a href="/user" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-yellow">
<div class="inner">
<h3 id="count3">0</h3>
<p>解析数量</p>
</div>
<div class="icon">
<i class="fa fa-globe"></i>
</div>
<a href="#" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-md-3 col-xs-6">
<!-- small box -->
<div class="small-box bg-red">
<div class="inner">
<h3 id="count4">0</h3>
<p>平台数量</p>
</div>
<div class="icon">
<i class="fa fa-connectdevelop"></i>
</div>
<a href="#" class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-md-7 col-sm-12">
<div id="browser-notice"></div>
<div class="box box-primary">
<div class="box-header with-border">
<i class="fa fa-cloud"></i>
<h3 class="box-title">服务器信息</h3>
</div>
<table class="table table-bordered">
<tbody>
<tr>
<td class="query-title">框架版本</td>
<td class="query-result">{$info.framework_version}</td>
</tr>
<tr>
<td class="query-title">PHP版本</td>
<td class="query-result">{$info.php_version}</td>
</tr>
<tr>
<td class="query-title">MySQL版本</td>
<td class="query-result">{$info.mysql_version}</td>
</tr>
<tr>
<td class="query-title">WEB软件</td>
<td class="query-result">{$info.software}</td>
</tr>
<tr>
<td class="query-title">操作系统</td>
<td class="query-result">{$info.os}</td>
</tr>
<tr>
<td class="query-title">服务器时间</td>
<td class="query-result">{$info.date}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-5 col-sm-12">
<div class="box box-default">
<div class="box-header with-border">
<i class="fa fa-volume-up"></i>
<h3 class="box-title">版本信息</h3>
</div>
<ul class="list-group text-dark" id="checkupdate"></ul>
</div>
<button class="btn btn-default btn-block" onclick="cleancache()"><i class="fa fa-trash"></i>清理缓存</button><br/>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
$(document).ready(function(){
$.ajax({
type : "POST",
url : "/",
data : {do: 'stat'},
dataType : 'json',
success : function(data) {
$('#count1').html(data.domains);
$('#count2').html(data.users);
$('#count3').html(data.records);
$('#count4').html(data.types);
$.ajax({
url: '{$checkupdate}',
type: 'get',
dataType: 'jsonp',
jsonpCallback: 'callback'
}).done(function(data){
$("#checkupdate").html(data.msg);
})
}
})
})
function cleancache(){
var ii = layer.load(2);
$.ajax({
type : 'GET',
url : '/cleancache',
dataType : 'json',
success : function(data) {
layer.close(ii);
layer.msg('清理缓存成功', {icon: 1});
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
}
</script>
<script>
function speedModeNotice(){
var ua = window.navigator.userAgent;
if(ua.indexOf('Windows NT')>-1 && ua.indexOf('Trident/')>-1){
var html = "<div class=\"panel panel-default\"><div class=\"panel-body\">当前浏览器是兼容模式,为确保后台功能正常使用,请切换到<b style='color:#51b72f'>极速模式</b><br>操作方法点击浏览器地址栏右侧的IE符号<b style='color:#51b72f;'><i class='fa fa-internet-explorer fa-fw'></i></b>→选择“<b style='color:#51b72f;'><i class='fa fa-flash fa-fw'></i></b><b style='color:#51b72f;'>极速模式</b></div></div>";
$("#browser-notice").html(html)
}
}
speedModeNotice();
</script>
{/block}

View File

@ -0,0 +1,60 @@
{extend name="common/layout" /}
{block name="title"}修改密码{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">修改密码</h3></div>
<div class="panel-body">
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
<div class="form-group">
<label>旧密码:</label>
<input type="password" name="oldpwd" value="" class="form-control" placeholder="请输入当前的密码" required/>
</div>
<div class="form-group">
<label>新密码:</label>
<input type="password" name="newpwd" value="" class="form-control" placeholder="" required/>
</div>
<div class="form-group">
<label>重输密码:</label>
<input type="password" name="newpwd2" value="" class="form-control" placeholder="" required/>
</div>
<div class="form-group text-center">
<input type="submit" name="submit" value="确定" class="btn btn-success btn-block"/>
</div>
</form>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script>
function saveAccount(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/setpwd',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert('密码修改成功!请重新登录。', {
icon: 1,
closeBtn: false
}, function(){
window.location.reload()
});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
</script>
{/block}

266
app/view/install/index.html Normal file
View File

@ -0,0 +1,266 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>聚合DNS管理系统 - 安装程序</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="renderer" content="webkit">
<style>
body {
background: #f1f6fd;
margin: 0;
padding: 0;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body, input, button {
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, 'Microsoft Yahei', Arial, sans-serif;
font-size: 14px;
color: #7E96B3;
}
.container {
max-width: 480px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
a {
color: #4e73df;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
h1 {
margin-top: 0;
margin-bottom: 10px;
}
h2 {
font-size: 28px;
font-weight: normal;
color: #3C5675;
margin-bottom: 0;
margin-top: 0;
}
form {
margin-top: 40px;
}
.form-group {
margin-bottom: 20px;
}
.form-group .form-field:first-child input {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.form-group .form-field:last-child input {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
.form-field input {
background: #fff;
margin: 0 0 2px;
border: 2px solid transparent;
transition: background 0.2s, border-color 0.2s, color 0.2s;
width: 100%;
padding: 15px 15px 15px 180px;
box-sizing: border-box;
}
.form-field input:focus {
border-color: #4e73df;
background: #fff;
color: #444;
outline: none;
}
.form-field label {
float: left;
width: 160px;
text-align: right;
margin-right: -160px;
position: relative;
margin-top: 15px;
font-size: 14px;
pointer-events: none;
opacity: 0.7;
}
button, .btn {
background: #3C5675;
color: #fff;
border: 0;
font-weight: bold;
border-radius: 4px;
cursor: pointer;
padding: 15px 30px;
-webkit-appearance: none;
}
button[disabled] {
opacity: 0.5;
}
.form-buttons {
height: 52px;
line-height: 52px;
}
.form-buttons .btn {
margin-right: 5px;
}
#error, .error, #success, .success, #warmtips, .warmtips {
background: #D83E3E;
color: #fff;
padding: 15px 20px;
border-radius: 4px;
margin-bottom: 20px;
}
#success {
background: #3C5675;
}
#error a, .error a {
color: white;
text-decoration: underline;
}
#warmtips {
background: #fff;
font-size: 14px;
color: #3C5675;
border: 2px solid #4e73df;
text-align: left;
}
</style>
</head>
<body>
<div class="container">
<h1>
<svg t="1671271578323" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2750" data-spm-anchor-id="a313x.7781069.0.i0" width="20%" height="100%"><path d="M571.904 682.496h57.344V388.096h-59.904v183.296L452.096 390.144v-2.048H392.704v294.4h59.904V497.152l117.248 183.296v2.048h2.048zM200.704 320c34.304-55.296 82.944-100.352 140.8-130.048-12.8 36.352-27.648 85.504-38.4 142.848H399.36c8.704-57.856 23.552-119.296 45.056-179.2H440.32c23.552-4.096 47.104-6.144 72.704-6.144s49.152 2.048 72.704 8.704c21.504 59.904 34.304 123.904 45.056 179.2h95.744c-10.752-57.344-25.6-104.448-38.4-140.8 55.296 29.696 102.4 74.752 136.704 128 0 4.096 2.048 6.656 2.048 10.752H947.2c-70.144-170.496-239.104-290.304-435.2-290.304C313.344 42.496 144.896 162.304 74.752 332.8h121.856c-0.512-4.096 2.048-10.752 4.096-12.8z m780.8 185.856h-177.152V448h170.496c-2.048-21.504-6.144-42.496-10.752-57.344h-217.6v177.152h177.152v59.904h-177.152v55.296h204.8c19.456-49.152 25.6-98.304 27.648-142.848v25.6l2.56-59.904zM275.456 623.104V448S271.36 388.096 215.552 388.096H59.904c-16.896 66.048-31.744 181.248 10.752 294.4h144.896c2.048 0 59.904 0 59.904-59.392z m-57.856 2.048H100.352v-179.2h117.248v179.2z m471.552 202.752c10.752-27.648 16.896-57.344 23.552-82.944h-93.696c-14.848 68.096-31.744 113.152-36.352 125.952-23.552 4.096-47.104 6.656-72.704 6.656-23.552 0-45.056-2.048-66.048-6.656-2.048-4.096-21.504-49.152-36.352-125.952H313.344c6.144 29.696 14.848 57.344 25.6 87.552-42.496-21.504-78.848-53.248-108.544-87.552H102.4c80.896 140.8 232.448 236.544 409.6 236.544 175.104 0 328.704-96.256 409.6-236.544h-132.096c-27.648 33.792-59.904 61.44-100.352 82.944z" fill="#2B85FB" p-id="2751"></path></svg>
</svg>
</h1>
<h2>聚合DNS管理系统 - 安装程序</h2>
<div>
<form method="post">
<div id="error" style="display:none"></div>
<div id="success" style="display:none"></div>
<div class="form-group">
<div class="form-field">
<label>MySQL 数据库地址</label>
<input type="text" name="mysql_host" value="localhost" required="">
</div>
<div class="form-field">
<label>MySQL 数据库端口</label>
<input type="number" name="mysql_port" value="3306">
</div>
<div class="form-field">
<label>MySQL 用户名</label>
<input type="text" name="mysql_user" value="" required="">
</div>
<div class="form-field">
<label>MySQL 密码</label>
<input type="text" name="mysql_pwd" value="" required="">
</div>
<div class="form-field">
<label>MySQL 数据库名</label>
<input type="text" name="mysql_name" value="" required="">
</div>
<div class="form-field">
<label>MySQL 数据表前缀</label>
<input type="text" name="mysql_prefix" value="dnsmgr_">
</div>
</div>
<div class="form-group">
<div class="form-field">
<label>管理员用户名</label>
<input type="text" name="admin_username" value="admin" required=""/>
</div>
<div class="form-field">
<label>管理员密码</label>
<input type="text" name="admin_password" value="123456" required="">
</div>
</div>
<div class="form-buttons">
<!--@formatter:off-->
<button type="submit" >点击安装</button>
<!--@formatter:on-->
</div>
</form>
</div>
</div>
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
<script>
$(function () {
$('form').on('submit', function (e) {
e.preventDefault();
var form = this;
var $error = $("#error");
var $success = $("#success");
var $button = $(this).find('button')
.text("安装中...")
.prop('disabled', true);
$.ajax({
url: "",
type: "POST",
dataType: "json",
data: $(this).serialize(),
success: function (ret) {
if (ret.code == 1) {
$error.hide();
$(".form-group", form).remove();
$button.remove();
$("#success").text(ret.msg).show();
$("#warmtips").show();
$buttons = $(".form-buttons", form);
$('<a class="btn" href="/" style="background:#4e73df">进入后台</a>').appendTo($buttons);
} else {
$error.show().text(ret.msg);
$button.prop('disabled', false).text("点击安装");
$("html,body").animate({
scrollTop: 0
}, 500);
}
},
error: function (xhr) {
$error.show().text(xhr.responseText);
$button.prop('disabled', false).text("点击安装");
$("html,body").animate({
scrollTop: 0
}, 500);
}
});
return false;
});
});
</script>
</body>
</html>

77
app/view/user/log.html Normal file
View File

@ -0,0 +1,77 @@
{extend name="common/layout" /}
{block name="title"}操作日志{/block}
{block name="main"}
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
{if request()->user['level'] eq 2}<input type="text" class="form-control" name="uid" placeholder="UID">{/if}
<input type="text" class="form-control" name="domain" placeholder="域名">
<input type="text" class="form-control" name="kw" placeholder="操作类型/操作详情">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新日志列表"><i class="fa fa-refresh"></i> 刷新</a>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
$("#listTable").bootstrapTable({
url: '/log/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'ID'
},
{
field: 'uid',
title: 'UID',
formatter: function(value, row, index) {
return value>0?'<a href="/user?kw='+value+'" target="_blank">'+value+'</a>':'管理员';
}
},
{
field: 'domain',
title: '域名'
},
{
field: 'action',
title: '操作类型'
},
{
field: 'data',
title: '操作详情'
},
{
field: 'addtime',
title: '时间'
}
],
})
})
</script>
{/block}

338
app/view/user/user.html Normal file
View File

@ -0,0 +1,338 @@
{extend name="common/layout" /}
{block name="title"}用户管理{/block}
{block name="main"}
<div class="modal" id="modal-store" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content animated flipInX">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">&times;</span><span
class="sr-only">Close</span></button>
<h4 class="modal-title" id="modal-title">用户修改/添加</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="form-store">
<input type="hidden" name="action"/>
<input type="hidden" name="id"/>
<div class="form-group">
<label class="col-sm-3 control-label no-padding-right">用户名</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="username" required>
</div>
</div>
<div class="form-group" style="display:none" id="password_input">
<label class="col-sm-3 control-label no-padding-right">密码</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" name="password" autocomplete="off">
<a class="input-group-addon" id="create_password" href="javascript:">随机生成</a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">API接口</label>
<div class="col-sm-9">
<select name="is_api" class="form-control">
<option value="0">关闭</option>
<option value="1">开启</option>
</select>
</div>
</div>
<div class="form-group" style="display:none" id="apikey_input">
<label class="col-sm-3 control-label no-padding-right">API接口密钥</label>
<div class="col-sm-9">
<div class="input-group">
<input type="text" class="form-control" name="apikey" autocomplete="off" readonly>
<a class="input-group-addon" id="create_apikey" href="javascript:">生成密钥</a>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">用户等级</label>
<div class="col-sm-9">
<div class="input-group">
<select name="level" class="form-control">
<option value="1">普通用户</option>
<option value="2">管理员</option>
</select>
<a tabindex="0" class="input-group-addon" role="button" data-toggle="popover" data-trigger="focus" title="" data-placement="bottom" data-content="普通用户只能管理指定的域名解析,管理员拥有和你相同的权限" data-original-title="用户权限说明"><span class="glyphicon glyphicon-info-sign"></span></a>
</div>
</div>
</div>
<div class="form-group" style="display:none" id="permission_input">
<label class="col-sm-3 control-label">域名权限</label>
<div class="col-sm-9">
<select class="form-control select2" id="permission" name="permission[]" multiple="multiple" placeholder="留空">
{foreach $domains as $v}
<option value="{$v}">{$v}</option>
{/foreach}
</select>
</div>
</div>
<div class="form-group" style="display:none" id="repwd_input">
<label class="col-sm-3 control-label no-padding-right">重置密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="repwd" autocomplete="off" placeholder="不重置密码请留空">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="store" onclick="save()">保存</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 center-block" style="float: none;">
<div class="panel panel-default panel-intro">
<div class="panel-body">
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
<div class="form-group">
<label>搜索</label>
<input type="text" class="form-control" name="kw" placeholder="UID或用户名">
</div>
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
<a href="javascript:searchClear()" class="btn btn-default" title="刷新域名列表"><i class="fa fa-refresh"></i> 刷新</a>
<a href="javascript:addframe()" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
</form>
<table id="listTable">
</table>
</div>
</div>
</div>
</div>
{/block}
{block name="script"}
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
<script src="{$cdnpublic}bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/select2.min.js"></script>
<script src="{$cdnpublic}select2/4.0.13/js/i18n/zh-CN.min.js"></script>
<script src="/static/js/custom.js"></script>
<script>
$(document).ready(function(){
updateToolbar();
const defaultPageSize = 15;
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
$("#listTable").bootstrapTable({
url: '/user/data',
pageNumber: pageNumber,
pageSize: pageSize,
classes: 'table table-striped table-hover table-bordered',
columns: [
{
field: 'id',
title: 'UID'
},
{
field: 'username',
title: '用户名'
},
{
field: 'level',
title: '用户等级',
formatter: function(value, row, index) {
switch(value){
case 1: return '<font color="blue">普通用户</font>';break;
case 2: return '<font color="orange">管理员</font>';break;
}
}
},
{
field: 'is_api',
title: 'API接口',
formatter: function(value, row, index) {
switch(value){
case 0: return '<font color="grey">关闭</font>';break;
case 1: return '<font color="green">开启</font>';break;
}
}
},
{
field: 'regtime',
title: '添加时间'
},
{
field: 'lasttime',
title: '上次登录时间'
},
{
field: 'status',
title: '状态',
formatter: function(value, row, index) {
switch(value){
case 0: return '<a href="javascript:setStatus('+row.id+',1)"><font color=red><i class="fa fa-times-circle"></i>封禁</font></a>';break;
case 1: return '<a href="javascript:setStatus('+row.id+',0)"><font color=green><i class="fa fa-check-circle"></i>正常</font></a>';break;
}
}
},
{
field: '',
title: '操作',
formatter: function(value, row, index) {
var html = '<a href="javascript:editframe('+row.id+')" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
return html;
}
},
],
})
})
function addframe(){
$("#modal-store").modal('show');
$("#modal-title").html("添加用户");
$("#form-store input[name=action]").val("add");
$("#form-store input[name=id]").val('');
$("#form-store input[name=username]").val('');
$("#form-store input[name=password]").val('');
$("#form-store select[name=is_api]").val(0);
$("#form-store select[name=level]").val(1);
$("#password_input").show();
$("#repwd_input").hide();
$("#create_apikey").click();
$('#permission').val(null).trigger("change");
$("select[name=is_api]").change();
$("select[name=level]").change();
}
function editframe(id){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/get',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("#modal-store").modal('show');
$("#modal-title").html("修改用户");
$("#form-store input[name=action]").val("edit");
$("#form-store input[name=id]").val(data.data.id);
$("#form-store input[name=username]").val(data.data.username);
$("#form-store select[name=is_api]").val(data.data.is_api);
$("#form-store input[name=apikey]").val(data.data.apikey);
$("#form-store select[name=level]").val(data.data.level);
$("#form-store input[name=repwd]").val('');
$("#password_input").hide();
$("#repwd_input").show();
$('#permission').val(null).trigger("change");
if(data.data.permission != null && data.data.permission.length > 0){
$('#permission').val(data.data.permission).trigger('change');
}
$("select[name=is_api]").change();
$("select[name=level]").change();
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function save(){
if($("#form-store input[name=username]").val()==''){
layer.alert('请确保各项不能为空!');return false;
}
var act = $("#form-store input[name=action]").val();
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/'+act,
data : $("#form-store").serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.alert(data.msg,{
icon: 1,
closeBtn: false
}, function(){
layer.closeAll();
$("#modal-store").modal('hide');
searchSubmit();
});
}else{
layer.alert(data.msg, {icon: 2})
}
}
});
}
function setStatus(id,status) {
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/set',
data : {id:id, status:status},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
searchSubmit();
}else{
layer.msg(data.msg, {icon:2, time:1500});
}
}
});
}
function delItem(id) {
var confirmobj = layer.confirm('确定要删除此用户吗?', {
btn: ['确定','取消']
}, function(){
var ii = layer.load(2);
$.ajax({
type : 'POST',
url : '/user/op/act/del',
data : {id: id},
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.closeAll();
searchSubmit();
}else{
layer.alert(data.msg, {icon: 2});
}
}
});
}, function(){
layer.close(confirmobj);
});
}
var CreatePassword = function (len)
{
var str = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
var pass = '';
for (var i = 0; i < len; i++ )
pass += str.charAt(Math.floor( Math.random() * str.length));
return pass;
}
$(document).ready(function(){
$("select[name=is_api]").change(function(){
if($(this).val() == 1){
$("#apikey_input").show();
}else{
$("#apikey_input").hide();
}
});
$("select[name=level]").change(function(){
if($(this).val() == 2){
$("#permission_input").hide();
}else{
$("#permission_input").show();
$('#permission').select2({placeholder: '请选择该用户可管理解析的域名'});
}
});
$("#create_password").click(function(){
$("input[name='password']").val(CreatePassword(12));
});
$("#create_apikey").click(function(){
$("input[name='apikey']").val(CreatePassword(16));
});
$('[data-toggle="popover"]').popover()
})
</script>
{/block}

50
composer.json Normal file
View File

@ -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"
]
}
}

34
config/app.php Normal file
View File

@ -0,0 +1,34 @@
<?php
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
return [
// 应用地址
'app_host' => 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',
];

29
config/cache.php Normal file
View File

@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
return [
// 默认缓存驱动
'default' => env('cache.driver', 'file'),
// 缓存连接方式配置
'stores' => [
'file' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => '',
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
// 缓存标签前缀
'tag_prefix' => 'tag:',
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => [],
],
// 更多的缓存连接
],
];

39
config/captcha.php Normal file
View File

@ -0,0 +1,39 @@
<?php
// +----------------------------------------------------------------------
// | Captcha配置文件
// +----------------------------------------------------------------------
return [
//验证码位数
'length' => 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,
// ...
//],
];

9
config/console.php Normal file
View File

@ -0,0 +1,9 @@
<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
// 指令定义
'commands' => [
],
];

20
config/cookie.php Normal file
View File

@ -0,0 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
return [
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => false,
// 是否使用 setcookie
'setcookie' => true,
// samesite 设置,支持 'strict' 'lax'
'samesite' => '',
];

63
config/database.php Normal file
View File

@ -0,0 +1,63 @@
<?php
return [
// 默认使用的数据库连接配置
'default' => 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,
],
// 更多的数据库配置信息
],
];

24
config/filesystem.php Normal file
View File

@ -0,0 +1,24 @@
<?php
return [
// 默认磁盘
'default' => 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',
],
// 更多的磁盘配置信息
],
];

27
config/lang.php Normal file
View File

@ -0,0 +1,27 @@
<?php
// +----------------------------------------------------------------------
// | 多语言设置
// +----------------------------------------------------------------------
return [
// 默认语言
'default_lang' => 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,
];

45
config/log.php Normal file
View File

@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
// 默认日志记录通道
'default' => 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,
],
// 其它日志通道配置
],
];

8
config/middleware.php Normal file
View File

@ -0,0 +1,8 @@
<?php
// 中间件配置
return [
// 别名或分组
'alias' => [],
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
'priority' => [],
];

45
config/route.php Normal file
View File

@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
return [
// pathinfo分隔符
'pathinfo_depr' => '/',
// 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',
];

19
config/session.php Normal file
View File

@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
return [
// session name
'name' => 'PHPSESSID',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// 驱动方式 支持file cache
'type' => 'file',
// 存储连接标识 当type使用cache的时候有效
'store' => null,
// 过期时间
'expire' => 1440,
// 前缀
'prefix' => '',
];

10
config/trace.php Normal file
View File

@ -0,0 +1,10 @@
<?php
// +----------------------------------------------------------------------
// | Trace设置 开启调试模式后有效
// +----------------------------------------------------------------------
return [
// 内置Html和Console两种方式 支持扩展
'type' => 'Html',
// 读取的日志通道名
'channel' => '',
];

25
config/view.php Normal file
View File

@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
return [
// 模板引擎类型使用Think
'type' => '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' => '}',
];

8
public/.htaccess Normal file
View File

@ -0,0 +1,8 @@
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

24
public/index.php Normal file
View File

@ -0,0 +1,24 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// [ 应用入口文件 ]
namespace think;
require __DIR__ . '/../vendor/autoload.php';
// 执行HTTP应用并响应
$http = (new App())->http;
$response = $http->run();
$response->send();
$http->end($response);

2
public/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Disallow: /

19
public/router.php Normal file
View File

@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id$
if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
return false;
} else {
$_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
require __DIR__ . "/index.php";
}

10
public/static/css/app.min.css vendored Normal file

File diff suppressed because one or more lines are too long

7
public/static/css/bootstrap-table.css vendored Normal file

File diff suppressed because one or more lines are too long

1
public/static/css/select2.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1000px" height="1000px" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.7.1 (28215) - http://www.bohemiancoding.com/sketch -->
<title>Group 3</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-3">
<path d="M811.766606,296.607029 C725.516404,296.607029 655.657643,226.748269 655.657643,140.498066 C655.657643,101.665961 670.390426,66.541445 693.904339,39.2223769 C634.095092,13.9522389 568.43176,0 499.548681,0 C223.626089,0 0,223.723657 0,499.54868 C0,775.471271 223.626089,999.097361 499.548681,999.097361 C775.471273,999.097361 999.097362,775.471272 999.097361,499.54868 C999.097361,405.980871 972.851541,318.65742 928.067783,243.725117 C899.480329,275.922591 858.209022,296.607028 811.766606,296.607029 Z" id="Shape" fill="#FFC4C8"></path>
<path d="M238.675291,767.256916 C232.468717,780.47076 238.148444,796.222193 251.355655,802.404131 C264.478533,808.562379 280.221438,802.931927 286.455491,789.773041 C289.099207,784.249664 352.329513,654.941609 499.098414,654.941609 C659.240351,654.941609 708.869257,784.171015 710.929271,789.665018 C715.974126,803.3318 731.081212,810.308748 744.787792,805.392762 C758.446046,800.396232 765.522488,785.331786 760.580917,771.641316 C759.949837,769.980228 697.138357,602.112764 499.098413,602.112764 C318.202829,602.112764 241.820271,760.519703 238.675291,767.256916" id="Shape" fill="#FF8992"></path>
<g id="Group" transform="translate(267.388813, 340.579181)" fill="#FF8992">
<path d="M67.8347682,135.646149 C105.298876,135.646149 135.669536,105.280725 135.669536,67.8230747 C135.669536,30.3654249 105.298876,0 67.8347682,0 C30.3706602,0 0,30.3654249 0,67.8230747 C0,105.280725 30.3706602,135.646149 67.8347682,135.646149 Z" id="Oval-1"></path>
<ellipse id="Oval-1-Copy" cx="396.70349" cy="67.8230747" rx="67.8347682" ry="67.8230747"></ellipse>
</g>
<g id="Group-2" transform="translate(671.314741, 0.000000)">
<path d="M140.582972,0 C63.016331,0 0.0849062373,62.9314257 0.0849052614,140.498066 C0.0849042856,218.064706 63.016331,280.996132 140.582972,280.996133 C218.149613,280.996134 281.081038,218.064707 281.081038,140.498066 C281.081038,62.9314248 218.149612,0 140.582972,0 L140.582972,0 Z" id="Shape" fill="#FF8992"></path>
<path d="M140.87809,227.034331 C132.934307,227.034331 126.494024,220.60824 126.494024,212.68075 C126.494024,204.752278 132.934307,198.325203 140.87809,198.325203 C148.821872,198.325203 155.262155,204.752278 155.262155,212.68075 C155.262155,220.60824 148.821872,227.034331 140.87809,227.034331 L140.87809,227.034331 Z" id="Path" fill="#FFFFFF"></path>
<path d="M140.87809,179.186222 C132.934307,179.186222 126.494024,172.760131 126.494024,164.831658 L126.494024,69.1354403 C126.494024,61.2079508 132.934307,54.7808765 140.87809,54.7808765 C148.821872,54.7808765 155.262155,61.2079508 155.262155,69.1354403 L155.262155,164.831658 C155.262155,172.760131 148.821872,179.186222 140.87809,179.186222 Z" id="Path" fill="#FFFFFF"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1000px" height="1000px" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.7.1 (28215) - http://www.bohemiancoding.com/sketch -->
<title>Group 3</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-3" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-3">
<path d="M811.766606,296.607029 C725.516404,296.607029 655.657643,226.748269 655.657643,140.498066 C655.657643,101.665961 670.390426,66.541445 693.904339,39.2223769 C634.095092,13.9522389 568.43176,0 499.548681,0 C223.626089,0 0,223.723657 0,499.54868 C0,775.471271 223.626089,999.097361 499.548681,999.097361 C775.471273,999.097361 999.097362,775.471272 999.097361,499.54868 C999.097361,405.980871 972.851541,318.65742 928.067783,243.725117 C899.480329,275.922591 858.209022,296.607028 811.766606,296.607029 Z" id="Shape" fill="#A5DBFF"></path>
<g id="Group-2" transform="translate(671.314741, 0.000000)">
<path d="M140.582972,0 C63.016331,0 0.0849062373,62.9314257 0.0849052614,140.498066 C0.0849042856,218.064706 63.016331,280.996132 140.582972,280.996133 C218.149613,280.996134 281.081038,218.064707 281.081038,140.498066 C281.081038,62.9314248 218.149612,0 140.582972,0 L140.582972,0 Z" id="Shape" fill="#5EB7F3"></path>
<path d="M140.87809,227.034331 C132.934307,227.034331 126.494024,220.60824 126.494024,212.68075 C126.494024,204.752278 132.934307,198.325203 140.87809,198.325203 C148.821872,198.325203 155.262155,204.752278 155.262155,212.68075 C155.262155,220.60824 148.821872,227.034331 140.87809,227.034331 L140.87809,227.034331 Z" id="Path" fill="#FFFFFF"></path>
<path d="M140.87809,179.186222 C132.934307,179.186222 126.494024,172.760131 126.494024,164.831658 L126.494024,69.1354403 C126.494024,61.2079508 132.934307,54.7808765 140.87809,54.7808765 C148.821872,54.7808765 155.262155,61.2079508 155.262155,69.1354403 L155.262155,164.831658 C155.262155,172.760131 148.821872,179.186222 140.87809,179.186222 Z" id="Path" fill="#FFFFFF"></path>
</g>
<path d="M325.314744,308.474056 C318.121595,301.280319 306.453832,301.280319 299.267743,308.474056 C292.071653,315.668382 292.071653,327.33085 299.267743,334.527528 L325.314744,360.574529 C332.510834,367.769443 344.168596,367.769443 351.365275,360.574529 C358.558424,353.384322 358.558424,341.723619 351.365275,334.527528 L325.314744,308.474056 L325.314744,308.474056 L325.314744,308.474056 Z M675.316427,308.474056 L649.26825,334.527528 C642.073924,341.723619 642.073924,353.384322 649.26825,360.574529 C656.454339,367.769443 668.11916,367.769443 675.316427,360.574529 L701.362251,334.527528 C708.560107,327.331438 708.560107,315.668971 701.362251,308.474056 C694.170867,301.283849 682.510753,301.283849 675.316427,308.474056 L675.316427,308.474056 L675.316427,308.474056 Z M279.264353,454.262547 L242.422039,454.262547 C232.249148,454.262547 224,462.509931 224,472.682821 C224,482.858653 232.24856,491.10486 242.422039,491.10486 L279.264353,491.10486 C289.437243,491.10486 297.68345,482.858653 297.68345,472.682821 C297.68345,462.510519 289.437243,454.262547 279.264353,454.262547 L279.264353,454.262547 L279.264353,454.262547 Z M758.20972,454.262547 L721.367995,454.262547 C711.195693,454.262547 702.946544,462.509931 702.946544,472.682821 C702.946544,482.858653 711.195693,491.10486 721.367995,491.10486 L758.20972,491.10486 C768.382022,491.10486 776.634113,482.858653 776.634113,472.682821 C776.634113,462.510519 768.382023,454.262547 758.20972,454.262547 L758.20972,454.262547 L758.20972,454.262547 Z M481.895605,242.421451 L481.895605,279.262588 C481.895605,289.436655 490.140635,297.681685 500.313526,297.681685 C510.486417,297.681685 518.737919,289.436655 518.737919,279.262588 L518.737919,242.421451 C518.737919,232.24856 510.486417,224 500.313526,224 C490.140635,224 481.895605,232.24856 481.895605,242.421451 L481.895605,242.421451 L481.895605,242.421451 Z M371.368077,500.314703 C371.368077,429.100938 429.09682,371.368077 500.313526,371.368077 C571.533174,371.368077 629.26427,429.100938 629.26427,500.314703 C629.26427,571.530822 571.532586,629.265447 500.313526,629.265447 C429.09682,629.266036 371.368077,571.530822 371.368077,500.314703 L371.368077,500.314703 L371.368077,500.314703 Z M334.527528,500.314703 C334.527528,591.885428 408.749862,666.112467 500.313526,666.112467 C591.881897,666.112467 666.104819,591.885427 666.104819,500.314703 C666.104819,408.748685 591.881897,334.52694 500.313526,334.52694 C408.749273,334.527528 334.527528,408.749273 334.527528,500.314703 L334.527528,500.314703 L334.527528,500.314703 Z M463.473566,758.215603 C463.473566,768.381434 471.722126,776.637054 481.895605,776.637054 L518.737919,776.637054 C528.911398,776.637054 537.159958,768.382022 537.159958,758.215603 C537.159958,748.036241 528.911398,739.793564 518.737919,739.793564 L481.895605,739.793564 C471.722715,739.793564 463.473566,748.036241 463.473566,758.215603 L463.473566,758.215603 L463.473566,758.215603 Z M426.633018,702.953604 C426.633018,713.118259 434.879225,721.373878 445.052115,721.373878 L555.579056,721.373878 C565.751947,721.373878 574.000507,713.118847 574.000507,702.953604 C574.000507,692.771889 565.751947,684.528624 555.579056,684.528624 L445.052115,684.528624 C434.878636,684.528035 426.633018,692.771889 426.633018,702.953604 L426.633018,702.953604 L426.633018,702.953604 Z" id="Shape" fill="#5EB7F3"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1000px" height="1000px" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.7.1 (28215) - http://www.bohemiancoding.com/sketch -->
<title>Group 2</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group-2">
<path d="M928.424049,243.818679 C899.825622,276.028512 858.538471,296.72089 812.078227,296.720891 C725.794915,296.720891 655.909337,226.835313 655.909337,140.552 C655.909337,101.704989 670.647776,66.5669889 694.170715,39.2374336 C634.338509,13.9575949 568.649969,5.68434189e-14 499.740447,5.68434189e-14 C223.711934,5.68434189e-14 0,223.80954 0,499.740447 C0,775.768959 223.711934,999.480894 499.740447,999.480894 C775.768961,999.480894 999.480895,775.76896 999.480894,499.740447 C999.480894,406.136718 973.225,318.779746 928.424049,243.818679 L928.424049,243.818679 Z" id="Shape" fill="#E3FCD9"></path>
<path d="M812.078227,0.195211166 C734.481809,0.195211166 671.526226,63.150795 671.526225,140.747211 C671.526224,218.343628 734.481809,281.299212 812.078227,281.299213 C889.674644,281.299214 952.630227,218.343629 952.630227,140.747211 C952.630227,63.150794 889.674643,0.195211166 812.078227,0.195211166 L812.078227,0.195211166 Z M894.35971,102.193017 L796.949365,199.603362 C791.385848,205.166879 782.308531,205.166879 776.842621,199.603362 L729.796742,152.557484 C724.233226,146.993968 724.233226,137.916651 729.796742,132.450739 C735.360259,126.887223 744.437576,126.887223 749.903487,132.450739 L781.527688,164.074939 C784.455854,167.003106 789.238526,167.003106 792.166693,164.074939 L874.15536,82.0862722 C879.718877,76.5227555 888.796193,76.5227555 894.262105,82.0862722 C899.923227,87.5521829 899.923227,96.6294997 894.35971,102.193017 L894.35971,102.193017 Z" id="Shape" fill="#9AD388"></path>
<g id="Group" transform="translate(222.536413, 340.709923)" fill="#9AD388">
<ellipse id="Oval-1" cx="112.815854" cy="67.8491107" rx="67.8608086" ry="67.8491107"></ellipse>
<ellipse id="Oval-1-Copy" cx="441.810822" cy="67.8491107" rx="67.8608086" ry="67.8491107"></ellipse>
<path d="M2.6939078,297.641001 C-3.83805327,283.709963 2.13943932,267.103606 16.0390529,260.586137 C29.8499119,254.093644 46.4181552,260.029694 52.9790356,273.902791 C55.7613514,279.725955 122.306574,416.052327 276.769992,416.052327 C445.307541,416.052327 497.538297,279.808873 499.706308,274.016677 C505.015645,259.608118 520.914737,252.25249 535.339899,257.435296 C549.714202,262.703017 557.161634,278.5851 551.960996,293.018633 C551.29683,294.769878 485.192391,471.748507 276.769991,471.748507 C86.3907615,471.748507 6.0037667,304.743882 2.6939078,297.641001 Z" id="Shape"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More