├── LICENSE ├── README.md ├── app ├── DNStack.py ├── Session.py ├── Template.py ├── __init__.py └── db.py ├── config ├── __init__.py └── settings.py ├── handler ├── BaseHandler.py ├── __init__.py ├── domain.py ├── index.py ├── rndc_handler.py ├── system.py └── user.py ├── model ├── __init__.py └── models.py ├── modules ├── README.md ├── __init__.py └── rndc.py ├── run.py ├── static ├── bootstrap │ ├── css │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ └── bootstrap.min.js ├── css │ └── dnstack.css ├── font-awesome │ ├── css │ │ ├── font-awesome.css.map │ │ └── font-awesome.min.css │ └── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── img │ └── screenshot │ │ ├── dashboard.jpg │ │ ├── domain.jpg │ │ ├── group.jpg │ │ └── record.jpg ├── js │ ├── jquery.min.js │ └── xk-dnstack.min.js ├── justgage │ ├── justgage.js │ └── raphael-2.1.4.min.js └── layer │ └── skin │ └── default │ ├── icon-ext.png │ ├── icon.png │ ├── layer.css │ ├── loading-0.gif │ ├── loading-1.gif │ └── loading-2.gif └── view ├── domain ├── group.html ├── index.html ├── record.html └── state.html ├── index ├── blank.html ├── index.html └── sample.html ├── layout ├── footer.html ├── head.html ├── main.html ├── nav.html └── top.html ├── page └── error.html ├── system ├── settings.html └── state.html └── user ├── login.html ├── passwd.html └── profile.html /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) KK Studio 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DNStack 2 | ======= 3 | 4 | DNS Web Admin Based on ISC Bind 5 | 6 | Powered By [KK Studio](http://github.com/kkstu) 7 | 8 | Version: **1.0.0-Alpha** 9 | 10 | 11 | ## Overview 12 | 13 | #### Dashboard 14 | 15 | ![Dashboard](static/img/screenshot/dashboard.jpg) 16 | 17 | 18 | #### Domain Admin 19 | 20 | ![Domain](static/img/screenshot/domain.jpg) 21 | 22 | 23 | #### Record 24 | 25 | ![Record](static/img/screenshot/record.jpg) 26 | 27 | 28 | #### Group 29 | 30 | ![Group](static/img/screenshot/group.jpg) 31 | 32 | 33 | ## Dependency Component 34 | 35 | - [Bind](http://www.isc.org):9.9+ 36 | 37 | - [Torweb](https://github.com/kkstu/Torweb):1.0+ 38 | 39 | - [Tornado](http://www.tornadoweb.org/):4.0+ 40 | 41 | - [SQLAlchemy](http://www.sqlalchemy.org/):1.1.9 42 | 43 | - [Jinja2](http://jinja.pocoo.org/):2.9+ 44 | 45 | - [MySQL](http://www.percona.com/):Percona-Server 5.5/5.6 46 | 47 | - [MySQL-python](http://pypi.python.org/pypi/MySQL-python):1.2.5+ 48 | 49 | - [Redis-Py](https://github.com/andymccurdy/redis-py):2.10+ 50 | 51 | - [Python](http://www.python.org):2.6.x/2.7.x 52 | 53 | 54 | ## Deployment 55 | 56 | #### Install Bind 57 | 58 | ```shell 59 | # Remove Bind (System) 60 | yum remove bind* 61 | 62 | # Downlaod Bind 63 | wget https://ftp.isc.org/isc/bind9/9.11.0-P5/bind-9.11.0-P5.tar.gz 64 | 65 | # Decompress 66 | tar zxf bind-9.11.0-P5.tar.gz 67 | 68 | # Configure 69 | cd bind-9.11.0-P5 70 | ./configure --prefix=/usr/local/bind \ 71 | --enable-epoll \ 72 | --enable-threads=no \ 73 | --enable-largefile \ 74 | --enable-ipv6 \ 75 | --with-openssl=no \ 76 | --with-readline \ 77 | --with-dlz-mysql=/usr/local/mysql \ 78 | --with-python=/usr/local/python2.7/bin/python2.7 79 | 80 | # Install 81 | make -j 2 82 | make install 83 | 84 | ln -s /usr/local/bind/bin/dig /usr/bin 85 | ln -s /usr/local/bind/bin/nslookup /usr/bin 86 | ln -s /usr/local/bind/sbin/named /usr/sbin 87 | ln -s /usr/local/bind/sbin/rndc /usr/sbin 88 | ``` 89 | 90 | #### Configure Named 91 | 92 | Generate rndc.conf 93 | 94 | ```shell 95 | cd /usr/local/bind/etc 96 | ../sbin/rndc-confgen > rndc.conf 97 | ``` 98 | 99 | 100 | named.conf 101 | 102 | ``` 103 | key "rndc-key" { 104 | algorithm hmac-md5; 105 | secret "kzIcztY4+xH0Px2SrsZyGQ=="; 106 | }; 107 | 108 | controls { 109 | inet 0.0.0.0 port 953 110 | allow { any; } keys { "rndc-key"; }; 111 | }; 112 | 113 | logging { 114 | channel query_log { 115 | file "/usr/local/bind/var/log/query.log" versions 1024 size 100m; 116 | severity info; 117 | print-category no; 118 | print-severity no; 119 | print-time yes; 120 | }; 121 | category queries { query_log; }; 122 | }; 123 | 124 | options { 125 | listen-on port 53 { any; }; 126 | listen-on-v6 { none; }; 127 | directory "/usr/local/bind/var"; 128 | pid-file "run/named.pid"; 129 | dump-file "cache_dump.db"; 130 | statistics-file "named.stats"; 131 | memstatistics yes; 132 | memstatistics-file "named.memstats"; 133 | allow-query { any; }; 134 | forwarders { 114.114.114.114; 8.8.8.8; }; 135 | }; 136 | 137 | //statistics-channels { 138 | // inet 0.0.0.0 port 8053 allow { any; }; 139 | //}; 140 | 141 | include "/usr/local/bind/etc/local.zone.conf"; 142 | //include "/usr/local/bind/etc/zone.conf"; 143 | include "/usr/local/bind/etc/dlz_mysql.conf"; 144 | ``` 145 | 146 | dlz_mysql.conf 147 | 148 | ``` 149 | dlz "mysql zone" { 150 | database "mysql 151 | {host=127.0.0.1 dbname=dnstack ssl=false port=3306 user=test pass=test} 152 | {select zone from domain where status = 1 and zone = '$zone$' limit 1} 153 | {select ttl, type, mx_priority, case when lower(type)='txt' then concat('\"', data, '\"') when lower(type) = 'soa' then concat_ws(' ', data, resp_person, serial, refresh, retry, expire, minimum) else data end from record where status = 1 and zone = '$zone$' and host ='$record$'}"; 154 | }; 155 | ``` 156 | 157 | #### Start Named 158 | 159 | > named -c /usr/local/bind/etc/named.conf 160 | 161 | #### Control Named 162 | 163 | - Get Running Status 164 | 165 | > rndc status 166 | 167 | - Reload 168 | 169 | > rndc reload 170 | 171 | - Reload config file 172 | 173 | > rndc reconfig 174 | 175 | - Shutdown Name 176 | 177 | > rndc halt 178 | 179 | 180 | ## Configure Database 181 | 182 | Create a database 183 | 184 | > mysql> create database dnstack; 185 | 186 | Import SQL 187 | 188 | > mysql dnstack < [docs/data.sql](docs/data.sql) 189 | 190 | Config file config/settings.py 191 | 192 | ``` 193 | config = { 194 | 'db': { 195 | 'host': '127.0.0.1', 196 | 'port': 3306, 197 | 'db': 'dnstack', 198 | 'user': 'test', 199 | 'passwd': 'test', 200 | 'charset': 'utf8' 201 | }, 202 | 'redis': { 203 | 'host': '127.0.0.1', 204 | 'port': 6379, 205 | 'password': '', 206 | 'db': '0' 207 | }, 208 | ...... 209 | } 210 | ``` 211 | 212 | ## Startup 213 | 214 | > python run.py 215 | 216 | You can visit the site via http://YourIP:8888/ 217 | 218 | Specify Port: 219 | 220 | > python run.py --port=8081 221 | 222 | 223 | ## Get Support and Help 224 | 225 | To report an issue with DNStack. 226 | 227 | https://github.com/kkstu/DNStack/issues 228 | 229 | 230 | ## Contributors 231 | 232 | After the version **1.0-Stable** release. 233 | 234 | 235 | ## Technology Communications 236 | 237 | #### Wechat 238 | 239 | ![Python运维圈](https://github.com/luxiaok/SaltAdmin/raw/master/static/images/ops_circle_qrcode.jpg) 240 | 241 | #### QQ Group 242 | 243 | 459457262 244 | 245 | 246 | #### Development Team 247 | 248 | http://studio.luxiaok.com 249 | 250 | 251 | ## License 252 | 253 | This project is under the MIT License. See the [LICENSE](LICENSE) file for the full license text. 254 | -------------------------------------------------------------------------------- /app/DNStack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | import tornado 6 | import tornado.httpserver 7 | import tornado.ioloop 8 | import tornado.web 9 | import tornado.netutil 10 | import tornado.process 11 | import tornado.locale 12 | import platform 13 | import db 14 | from tornado.log import gen_log 15 | from handler.page import Page404Handler 16 | from config.settings import * 17 | from handler import route 18 | #from ui_modules import UIModules # Don't support Jinja2 19 | from Template import TemplateLoader # For Jinja2 20 | 21 | 22 | class App(tornado.web.Application): 23 | 24 | def __init__(self,handlers,conf,log): 25 | self.__version__ = conf['version'] 26 | self.log = log 27 | settings = conf['app_settings'] 28 | settings['default_handler_class'] = Page404Handler # 404 29 | # Don't Support for Jinja2 30 | #settings['ui_modules'] = UIModules 31 | #tornado.web.Application.__init__(self, handlers, **settings) 32 | # Support for Jinja2 33 | tpl_loader = TemplateLoader(settings['template_path'], False) 34 | tornado.web.Application.__init__(self, handlers, template_loader=tpl_loader.Loader(), **settings) 35 | #每10秒执行一次 36 | #tornado.ioloop.PeriodicCallback(self.test, 1 * 10 * 1000).start() 37 | # Init Database 38 | _db = db.DB(**conf['db']) 39 | self.db = _db.session 40 | #Init Redis 41 | R = db.Redis(**conf['redis']) 42 | self.redis = R.Connect() 43 | # Load Locale 44 | self.__load_locale(settings['default_lang']) 45 | 46 | 47 | #def test(self): 48 | # self.log.info('Test') 49 | 50 | # Load Locale 51 | def __load_locale(self,default_lang): 52 | tornado.locale.load_translations('locale') 53 | tornado.locale.set_default_locale(default_lang) 54 | 55 | class DNStack(): 56 | 57 | def __init__(self,processes=4): 58 | self.__version__ = '1.0.0-Alpha' 59 | self.host = config['host'] 60 | self.port = config['port'] 61 | self.urls = route 62 | self.config = config 63 | self.config['version'] = self.__version__ 64 | self.log = gen_log 65 | if platform.system() == "Linux": #根据操作系统类型来确定是否启用多线程 66 | self.processes = processes # 当processes>1时,PeriodicCallback定时任务会响相应的执行多次 67 | else: 68 | self.processes = 1 69 | self.log.info('DNStack %s' % self.__version__) # 启动时打印版本号 70 | self.log.info('Listen Port: %s' % self.port) 71 | 72 | 73 | # 单进程模式 74 | def run(self): 75 | app = App(self.urls, self.config, self.log) 76 | app.listen(self.port) 77 | tornado.ioloop.IOLoop.current().start() 78 | 79 | 80 | # 多线程模式 81 | def run_multi(self): 82 | http_sockets = tornado.netutil.bind_sockets(self.port, self.host) 83 | tornado.process.fork_processes(num_processes=self.processes) 84 | http_server = tornado.httpserver.HTTPServer(request_callback=App(self.urls,self.config,self.log), xheaders=True) 85 | http_server.add_sockets(http_sockets) 86 | tornado.ioloop.IOLoop.instance().start() 87 | -------------------------------------------------------------------------------- /app/Session.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | # Session Support For Tornado 5 | 6 | import hashlib 7 | import os 8 | import time 9 | import json 10 | 11 | 12 | class Session: 13 | 14 | 15 | def __init__(self,prefix='',session_id=None,expires=7200,redis=None): 16 | self.redis = redis 17 | self.expires = expires 18 | self.prefix = prefix 19 | if session_id: 20 | self.session_id = prefix + session_id 21 | self.data = self.get_data() 22 | if self.data: 23 | self.isGuest = False 24 | else: 25 | self.isGuest = True # Not Login 26 | else: 27 | self.session_id = None 28 | self.data = {} # Null Dict 29 | self.isGuest = True # Not Login 30 | 31 | 32 | # 生成SessionID 33 | def gen_session_id(self): 34 | sid = hashlib.sha1('%s%s' % (os.urandom(16), time.time())).hexdigest() 35 | self.session_id = self.prefix + sid 36 | return sid 37 | 38 | 39 | # 获取Session数据 40 | def get_data(self): 41 | session = self.redis.get(self.session_id) 42 | if not session: 43 | return None 44 | session = json.loads(session) # 字符串转字典 45 | return session 46 | 47 | 48 | # Get 49 | def get(self,name): 50 | if name and self.data: 51 | return self.data.get(name,None) 52 | else: 53 | return None 54 | 55 | 56 | # Set 57 | def set(self,name,value): 58 | self.data[name] = value 59 | 60 | 61 | def save(self): 62 | if not self.isGuest and self.session_id and self.data: 63 | self.redis.set(self.session_id,json.dumps(self.data),self.expires) 64 | 65 | 66 | # 销毁Session 67 | def remove(self): 68 | if self.session_id: # SessionID存在 69 | self.redis.delete(self.session_id) 70 | self.session_id = None 71 | self.data = None 72 | self.isGuest = True 73 | -------------------------------------------------------------------------------- /app/Template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | # Via https://pypi.python.org/pypi/tornado-jinja2/0.2.4 5 | 6 | import tornado.template 7 | import jinja2 8 | from jinja2 import Environment, FileSystemLoader 9 | 10 | #import sys 11 | #reload(sys) 12 | #sys.setdefaultencoding('utf8') 13 | 14 | 15 | class FixedTemplate(jinja2.Template): 16 | """ Subclass of jinja2.Template 17 | Override Template.generate method to adapt render_string method\ 18 | from tornado.RequestHandler 19 | """ 20 | def generate(self, **kwargs): 21 | return self.render(**kwargs) 22 | 23 | # Change The template class that returned by Environment.get_templte 24 | Environment.template_class = FixedTemplate 25 | 26 | 27 | class Jinja2Loader(tornado.template.BaseLoader): 28 | """ inherit form tornado.template.BaseLoader 29 | Implementing customized Template Loader of for tornado to generate 30 | Jinja2 template. 31 | 32 | A jinja2.environment.Environment object may be provided using 33 | `jinja2_environment` argument, it can also be set later using `jinja2_environment` 34 | property. Additional arguments are passed to tornado.template.BaseLoader. 35 | 36 | A very basic example for a loader that looks up templates on the file 37 | system could look like this:: 38 | 39 | jinja2_environment = jinja2.Environment() 40 | jinja2_environment.loader = jinja2.FileSystemLoader('/path/to/templates') 41 | loader = Jinja2Loader(jinja2_environment) 42 | """ 43 | 44 | def __init__(self, *args, **kwargs): 45 | # Get arguments with backward compatibility 46 | if args: 47 | arg = args[0] 48 | if isinstance(arg, Environment): 49 | jinja2_environment = arg 50 | root_directory = None 51 | else: 52 | jinja2_environment = None 53 | root_directory = args 54 | kwargs.pop('jinja2_environment', None) 55 | kwargs.pop('root_directory', None) 56 | else: 57 | jinja2_environment = kwargs.pop('jinja2_environment', None) 58 | root_directory = kwargs.pop('root_directory', None) 59 | 60 | if jinja2_environment: # Env provided 61 | self._jinja2_env = jinja2_environment 62 | elif root_directory: # Backward compatibility 63 | self._jinja2_env = Environment() 64 | self._jinja2_env.loader = FileSystemLoader(root_directory) 65 | else: # Set env later 66 | self._jinja2_env = None 67 | 68 | super(Jinja2Loader, self).__init__(**kwargs) 69 | 70 | @property 71 | def jinja2_environment(self): 72 | return self._jinja2_env 73 | 74 | @jinja2_environment.setter 75 | def jinja2_environment(self, env): 76 | if env is self._jinja2_env: 77 | return 78 | 79 | # Clear template cache 80 | with self.lock: 81 | self._jinja2_env = env 82 | self.templates = {} 83 | 84 | def resolve_path(self, name, parent_path=None): 85 | return name # Template searching should be handled by Jinja2's loader 86 | 87 | def _create_template(self, name): 88 | if self._jinja2_env is None: 89 | raise TypeError('no jinja2 environment for this loader specified') 90 | return self._jinja2_env.get_template(name) 91 | 92 | 93 | class TemplateLoader: 94 | 95 | def __init__(self,template_path,autoescape=False): 96 | # Create a instance of Jinja2Loader 97 | self.jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_path), autoescape=autoescape, trim_blocks=True) 98 | 99 | def Loader(self): 100 | return Jinja2Loader(self.jinja2_env) 101 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio -------------------------------------------------------------------------------- /app/db.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | import redis as PyRedis 6 | from sqlalchemy import create_engine 7 | from sqlalchemy.orm import sessionmaker, scoped_session 8 | 9 | class DB: 10 | 11 | def __init__(self,host='localhost',port=3306,db='mysql',user='root',passwd='',charset='utf-8'): 12 | db_uri = 'mysql+mysqldb://%s:%s@%s:%s/%s?charset=%s' % (user,passwd,host,port,db,charset) 13 | self.session = self.create_session(db_uri,charset) 14 | 15 | 16 | def create_session(self,db_uri,encoding='utf-8'): 17 | engine = create_engine(db_uri, encoding=encoding, echo=False, pool_recycle=60) 18 | return scoped_session(sessionmaker(bind=engine, autocommit=False)) 19 | 20 | 21 | def close(self): 22 | if self.session: 23 | self.session.remove() 24 | 25 | # Wrapper Redis 26 | class Redis(): 27 | 28 | def __init__(self,host,port=6379,db=0,password=''): 29 | self._host = host 30 | self._port = port 31 | self._db = db 32 | self._password = password 33 | 34 | def Connect(self): 35 | return PyRedis.Redis(self._host, self._port, self._db, self._password) -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio -------------------------------------------------------------------------------- /config/settings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | from tornado.options import define, options 6 | import tornado.options 7 | 8 | define("host", default='0.0.0.0', help="Listen on the given IP", type=str) 9 | define("port", default=8888, help="Run on the given port", type=int) 10 | 11 | tornado.options.parse_command_line() 12 | 13 | # Call options.*** should be after the parse_command_line() 14 | 15 | config = { 16 | 'db': { 17 | 'host': '127.0.0.1', 18 | 'port': 3306, 19 | 'db': 'dns', 20 | 'user': 'test', 21 | 'passwd': 'test', 22 | 'charset': 'utf8' 23 | }, 24 | 'redis': { 25 | 'host': '127.0.0.1', 26 | 'port': 6379, 27 | 'password': '', 28 | 'db': '0' 29 | }, 30 | 'host': options.host, 31 | 'port': options.port, 32 | 'app_settings': dict( 33 | template_path = 'view', 34 | static_path = 'static', 35 | static_url_prefix = '/static/', 36 | xsrf_cookies = False, 37 | cookie_secret = "db884468559f4c432bf1c1775f3dc9da", 38 | cookie_name = '_knsid', 39 | session_prefix = "_k_dns_session_", 40 | session_expires = 7200, 41 | login_url = "/user/login", 42 | default_lang = "en_US", 43 | debug = True, 44 | autoreload = True 45 | ) 46 | } -------------------------------------------------------------------------------- /handler/BaseHandler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | # 2017-04-13 5 | 6 | import tornado 7 | import time 8 | import hashlib 9 | from app.Session import Session 10 | from random import Random 11 | from model.models import Options 12 | 13 | class BaseHandler(tornado.web.RequestHandler): 14 | 15 | # 初始化函数 16 | def initialize(self): 17 | # 当前请求时间 18 | self.time = int(time.time()) 19 | # Session 20 | self.init_session() 21 | # Version 22 | self.app_version = self.application.__version__ 23 | # Current Route 24 | self.url = self.get_current_route() 25 | # Nav Style Class 26 | self.nav_active = {'/':'','/domain':'','/domain/group':'','/domain/record':'','/domain/state':'','/system/state':'','/settings':''} 27 | # Copyright Year 28 | self.copyright_year = self.format_time(self.time,'%Y') 29 | 30 | # 后面的方法如果重写on_finish方法,需要调用_on_finish 31 | def _on_finish(self): 32 | # 更新Session 33 | self.session.save() 34 | # 请求逻辑处理结束时关闭数据库连接,如果不关闭可能会造成MySQL Server has gone away 2006错误 35 | self.db.close() 36 | 37 | # 重载on_finish 38 | def on_finish(self): 39 | self._on_finish() 40 | 41 | # 重载write_error方法 42 | def write_error(self, status_code, **kwargs): 43 | title = "%s - %s" % (status_code, self._reason) 44 | if status_code == 404: # 捕获404 45 | self.render('page/error.html',title=title) 46 | elif status_code == 500: # 500可以正常捕获,404好像不行 47 | #print self.settings.get("serve_traceback") 48 | msg = '' 49 | if 'exc_info' in kwargs: 50 | for i in kwargs['exc_info']: 51 | #print type(i) 52 | msg += "

%s

" % str(i) 53 | self.render('page/error.html', title=title, code=status_code, msg=msg) 54 | else: 55 | self.render('page/error.html', title=title, code=status_code, msg=status_code) 56 | 57 | # Log Instance 58 | @property 59 | def log(self): 60 | return self.application.log 61 | 62 | # 获取当前路由 63 | def get_current_route(self): 64 | uri = self.request.uri.split('?') 65 | return uri[0] 66 | 67 | # 数据库 68 | @property 69 | def db(self): 70 | return self.application.db 71 | 72 | # Redis 73 | @property 74 | def redis(self): 75 | return self.application.redis 76 | 77 | # 返回Json 78 | def jsonReturn(self,data): 79 | self.set_header('Content-Type', 'application/json') 80 | self.write(data) 81 | 82 | # 格式化时间戳 83 | def format_time(self,timstamp=None,format='%Y-%m-%d %H:%M:%S'): 84 | return time.strftime(format, time.localtime(timstamp)) 85 | 86 | 87 | # 获取当前登录用户 88 | def get_current_user(self): 89 | if not self.session.isGuest and self.session.data: 90 | return self.session.data 91 | else: 92 | return None 93 | 94 | 95 | # Session初始化 96 | def init_session(self): 97 | prefix = self.settings.get('session_prefix') 98 | expires = self.settings.get('session_expires') 99 | self.cookie_name = self.settings.get('cookie_name') 100 | self.sid = self.get_secure_cookie(self.cookie_name) 101 | self.session = Session(prefix, self.sid, expires, self.redis) 102 | 103 | 104 | # MD5计算 105 | def md5(self,text): 106 | s = hashlib.md5() 107 | s.update(text) 108 | return s.hexdigest() 109 | 110 | 111 | # 生成指定长度的随机字符 112 | def random_str(self,N=8): 113 | str = '' 114 | chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789' 115 | length = len(chars) - 1 116 | random = Random() 117 | for i in range(N): 118 | str += chars[random.randint(0, length)] 119 | return str 120 | 121 | 122 | # 获取配置 123 | def get_options(self): 124 | _ops = self.db.query(Options).all() 125 | ops = {} 126 | for i in _ops: 127 | ops[i.name] = {'id':i.id,'name':i.name,'value':i.value,'default':i.default_value} 128 | return ops 129 | 130 | -------------------------------------------------------------------------------- /handler/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | import index 5 | import page 6 | import user 7 | import domain 8 | import system 9 | import rndc_handler 10 | 11 | route = [ 12 | (r'/',index.IndexHandler), 13 | (r'/blank',index.BlankHandler), 14 | (r'/sample',index.SampleHandler), 15 | (r'/user/login',user.LoginHandler), 16 | (r'/user/logout',user.LogoutHandler), 17 | (r'/user/profile',user.ProfileHandler), 18 | (r'/user/passwd',user.PasswdHandler), 19 | (r'/domain',domain.IndexHandler), 20 | (r'/domain/state',domain.StateDomainHandler), 21 | (r'/domain/create',domain.CreateDomainHandler), 22 | (r'/domain/update',domain.UpdateDomainHandler), 23 | (r'/domain/status',domain.StatusDomainHandler), 24 | (r'/domain/delete',domain.DeleteDomainHandler), 25 | (r'/domain/group',domain.GroupHandler), 26 | (r'/domain/record',domain.RecordHandler), 27 | (r'/domain/record/create',domain.CreateRecordHandler), 28 | (r'/domain/record/update',domain.UpdateRecordHandler), 29 | (r'/domain/record/status',domain.StatusRecordHandler), 30 | (r'/domain/record/delete',domain.DeleteRecordHandler), 31 | (r'/system/state',system.StateHandler), 32 | (r'/system/settings',system.SettingsHandler), 33 | (r'/rndc/status',rndc_handler.StatusHandler), 34 | (r'/rndc/reload',rndc_handler.ReloadHandler), 35 | (r'/rndc/reconfig',rndc_handler.ReconfigHandler), 36 | (r'/rndc/flush',rndc_handler.FlushHandler), 37 | (r'/page/404.html',page.Page404Handler), 38 | (r'/page/500.html',page.Page500Handler), 39 | (r'/page/error.html',page.PageErrorHandler), 40 | (r'/page/blank.html',page.BlankHandler), 41 | ] -------------------------------------------------------------------------------- /handler/domain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | # Domain Page 5 | 6 | from BaseHandler import BaseHandler 7 | from tornado.web import authenticated as Auth 8 | from model.models import Domain, Groups, Record 9 | 10 | 11 | class IndexHandler(BaseHandler): 12 | @Auth 13 | def get(self): 14 | page = int(self.get_argument('page', 1)) 15 | line = int(self.get_argument('line', 20)) 16 | offset = (page - 1) * line 17 | data = self.db.query(Domain).order_by(Domain.id.desc()).offset(offset).limit(line).all() 18 | grps = self.db.query(Groups).all() 19 | group = {} 20 | for i in grps: 21 | group[i.id] = i.name 22 | status = {1:u'已启用',2:u'暂停解析'} 23 | self.nav_active['/domain'] = 'active' 24 | self.render('domain/index.html',data=data,group=group,status=status) 25 | 26 | 27 | # 新增域名 28 | class CreateDomainHandler(BaseHandler): 29 | @Auth 30 | def post(self): 31 | domain = self.get_argument('domain',None) 32 | gid = self.get_argument('gid',None) 33 | comment = self.get_argument('comment',None) 34 | if not domain and not gid: 35 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 36 | chk = self.db.query(Domain).filter_by(zone=domain).first() 37 | if chk: 38 | return self.jsonReturn({'code': -2, 'msg': u'域名重复'}) 39 | # Check Group ID 40 | grp = self.db.query(Groups).filter_by(id=gid).first() 41 | if not grp: 42 | return self.jsonReturn({'code': -3, 'msg': u'分组不存在'}) 43 | d = Domain(zone=domain,gid=gid,comment=comment,create_time=self.time,update_time=self.time) 44 | self.db.add(d) 45 | self.db.commit() 46 | if d.id: 47 | self.db.query(Groups).filter_by(id=gid).update({'domain_count': Groups.domain_count+1}) 48 | self.db.commit() 49 | code = 0 50 | msg = u'成功添加域名' 51 | else: 52 | self.db.rollback() 53 | gid = 0 54 | code = -4 55 | msg = u'保存域名失败' 56 | return self.jsonReturn({'code': code, 'msg': msg, 'gid': gid}) 57 | 58 | 59 | # 编辑域名 60 | class UpdateDomainHandler(BaseHandler): 61 | @Auth 62 | def post(self): 63 | domain_id = self.get_argument('id',None) 64 | gid = self.get_argument('gid',None) 65 | comment = self.get_argument('comment',None) 66 | if not domain_id and not gid: 67 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 68 | data = self.db.query(Domain).filter_by(id=domain_id).first() 69 | if not data: 70 | return self.jsonReturn({'code': -2, 'msg': u'域名不存在'}) 71 | # Check Group ID 72 | group = self.db.query(Groups).filter_by(id=gid).first() 73 | if not group: 74 | return self.jsonReturn({'code': -3, 'msg': u'分组不存在'}) 75 | self.db.query(Domain).filter_by(id=domain_id).update({'gid': gid, 'comment': comment, 'update_time': self.time}) 76 | self.db.query(Groups).filter_by(id=data.gid).update({'domain_count': Groups.domain_count-1}) 77 | self.db.query(Groups).filter_by(id=gid).update({'domain_count': Groups.domain_count+1}) 78 | self.db.commit() 79 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 80 | 81 | 82 | # 域名状态管理 83 | class StatusDomainHandler(BaseHandler): 84 | @Auth 85 | def post(self): 86 | status = self.get_argument('status',None) # 1 or 2 87 | id = self.get_argument('id',None) 88 | if not id and status not in ['1','2']: 89 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 90 | data = self.db.query(Domain).filter_by(id=id).first() 91 | if not data: 92 | return self.jsonReturn({'code': -2, 'msg': u'域名不存在'}) 93 | self.db.query(Domain).filter_by(id=id).update({'status': status, 'update_time': self.time}) 94 | self.db.commit() 95 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 96 | 97 | 98 | # 删除域名:这里需要添加权限控制,风险操作!!! 99 | class DeleteDomainHandler(BaseHandler): 100 | @Auth 101 | def post(self): 102 | id = self.get_argument('id',None) 103 | if not id: 104 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 105 | data = self.db.query(Domain).filter_by(id=id).first() 106 | if not data: 107 | return self.jsonReturn({'code': -2, 'msg': u'域名不存在'}) 108 | self.db.query(Domain).filter_by(id=id).delete() # 删除域名 109 | self.db.query(Record).filter_by(zone=data.zone).delete() # 删除解析记录 110 | self.db.query(Groups).filter_by(id=data.gid).update({'domain_count': Groups.domain_count - 1}) # 更新分组数据统计 111 | self.db.commit() 112 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 113 | 114 | 115 | class GroupHandler(BaseHandler): 116 | @Auth 117 | def get(self): 118 | self.nav_active['/domain/group'] = 'active' 119 | data = self.db.query(Groups).order_by(Groups.id.desc()).all() 120 | self.render('domain/group.html',data=data) 121 | 122 | 123 | class RecordHandler(BaseHandler): 124 | @Auth 125 | def get(self): 126 | zone = self.get_argument('zone',None) 127 | if zone: 128 | data = self.db.query(Record).filter_by(zone=zone).order_by(Record.host,Record.type,Record.data).all() 129 | else: 130 | zone = '' 131 | data = [] 132 | status = {1: u'已启用', 2: u'暂停解析'} 133 | self.nav_active['/domain/record'] = 'active' 134 | self.render('domain/record.html',data=data,status=status,zone=zone) 135 | 136 | 137 | # 新增记录 138 | class CreateRecordHandler(BaseHandler): 139 | @Auth 140 | def post(self): 141 | host = self.get_argument('host', None) 142 | zone = self.get_argument('zone', None) 143 | type = self.get_argument('type', None) 144 | data = self.get_argument('data', None) 145 | ttl = self.get_argument('ttl') 146 | mx_priority = self.get_argument('mx_priority') 147 | comment = self.get_argument('comment') 148 | if not host or not zone or not type or not data: 149 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 150 | type = type.upper() 151 | if type not in ['A','AAAA','MX','NS','CNAME','TXT','PTR']: 152 | return self.jsonReturn({'code': -2, 'msg': u'解析类型错误'}) 153 | if host == '@' and type in ['NS','SOA']: 154 | return self.jsonReturn({'code': -3, 'msg': u'禁止添加该类型的解析'}) 155 | domain = self.db.query(Domain).filter_by(zone=zone).first() 156 | if not domain: 157 | return self.jsonReturn({'code': -4, 'msg': u'域名不存在'}) 158 | if not ttl: ttl = 600 159 | if not comment: comment = None 160 | if type in ['A','AAAA','NS','CNAME','TXT','PTR']: 161 | r = Record(host=host,zone=zone,type=type,data=data,ttl=ttl, comment=comment, create_time=self.time, update_time=self.time) 162 | else: # type = 'MX' 163 | if not mx_priority: 164 | mx_priority = 10 165 | r = Record(host=host, zone=zone, type=type, data=data, ttl=ttl, mx_priority=mx_priority, comment=comment, create_time=self.time,update_time=self.time) 166 | self.db.add(r) 167 | self.db.query(Domain).filter_by(id=domain.id).update({'record_count': Domain.record_count + 1}) # 更新记录统计 168 | self.db.commit() 169 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 170 | 171 | 172 | # 更新解析记录 173 | class UpdateRecordHandler(BaseHandler): 174 | @Auth 175 | def post(self): 176 | id = self.get_argument('id', None) 177 | host = self.get_argument('host', None) 178 | zone = self.get_argument('zone', None) 179 | type = self.get_argument('type', None) 180 | data = self.get_argument('data', None) 181 | ttl = self.get_argument('ttl') 182 | mx_priority = self.get_argument('mx_priority') 183 | comment = self.get_argument('comment') 184 | if not id or not host or not zone or not type or not data: 185 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 186 | type = type.upper() 187 | if type not in ['A','AAAA','MX','NS','CNAME','TXT','PTR']: 188 | return self.jsonReturn({'code': -2, 'msg': u'解析类型错误'}) 189 | if host == '@' and type in ['NS','SOA']: 190 | return self.jsonReturn({'code': -3, 'msg': u'禁止添加该类型的解析'}) 191 | domain = self.db.query(Domain).filter_by(zone=zone).first() 192 | if not domain: 193 | return self.jsonReturn({'code': -4, 'msg': u'域名不存在'}) 194 | record = self.db.query(Record).filter_by(id=id).first() 195 | if not record: 196 | return self.jsonReturn({'code': -5, 'msg': u'无效解析'}) 197 | if not ttl: ttl = 600 198 | if not comment: comment = None 199 | if type in ['A', 'AAAA', 'NS', 'CNAME', 'TXT', 'PTR']: 200 | self.db.query(Record).filter_by(id=id).update( 201 | {'host': host, 'type': type, 'data': data, 'ttl': ttl, 'comment': comment, 'update_time': self.time}) 202 | else: 203 | if not mx_priority: mx_priority = 10 204 | self.db.query(Record).filter_by(id=id).update( 205 | {'host': host, 'type': type, 'data': data, 'ttl': ttl, 'mx_priority': mx_priority, 'comment': comment, 'update_time': self.time}) 206 | self.db.commit() 207 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 208 | 209 | 210 | # 记录状态管理 211 | class StatusRecordHandler(BaseHandler): 212 | @Auth 213 | def post(self): 214 | id = self.get_argument('id', None) 215 | status = self.get_argument('status',None) # 1 or 2 216 | if not id and status not in ['1','2']: 217 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 218 | data = self.db.query(Record).filter_by(id=id).first() 219 | if not data: 220 | return self.jsonReturn({'code': -2, 'msg': u'解析不存在'}) 221 | self.db.query(Record).filter_by(id=id).update({'status': status, 'update_time': self.time}) 222 | self.db.commit() 223 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 224 | 225 | 226 | # 删除解析 227 | class DeleteRecordHandler(BaseHandler): 228 | @Auth 229 | def post(self): 230 | id = self.get_argument('id',None) 231 | if not id: 232 | return self.jsonReturn({'code': -1, 'msg': u'参数错误'}) 233 | data = self.db.query(Record).filter_by(id=id).first() 234 | if not data: 235 | return self.jsonReturn({'code': -2, 'msg': u'解析不存在'}) 236 | self.db.query(Record).filter_by(id=id).delete() # 删除解析记录 237 | self.db.query(Domain).filter_by(zone=data.zone).update({'record_count': Domain.record_count - 1}) # 更新解析统计 238 | self.db.commit() 239 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 240 | 241 | 242 | # 解析数据分析 243 | class StateDomainHandler(BaseHandler): 244 | @Auth 245 | def get(self): 246 | self.nav_active['/domain/state'] = 'active' 247 | self.render('domain/state.html') 248 | -------------------------------------------------------------------------------- /handler/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | # Index Page 5 | 6 | from BaseHandler import BaseHandler 7 | from tornado.web import authenticated as Auth 8 | from model.models import Domain, Groups, Record, User 9 | 10 | class IndexHandler(BaseHandler): 11 | 12 | @Auth 13 | def get(self): 14 | data = {} 15 | data['domain'] = self.db.query(Domain).count() 16 | data['group'] = self.db.query(Groups).count() 17 | data['record'] = self.db.query(Record).count() 18 | data['user'] = self.db.query(User).count() 19 | self.render('index/index.html',data=data) 20 | 21 | class BlankHandler(BaseHandler): 22 | # @Auth 23 | def get(self): 24 | self.render('index/blank.html') 25 | 26 | 27 | class SampleHandler(BaseHandler): 28 | # @Auth 29 | def get(self): 30 | self.render('index/sample.html') 31 | -------------------------------------------------------------------------------- /handler/rndc_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | from BaseHandler import BaseHandler 6 | from tornado.web import authenticated as Auth 7 | from modules.rndc import rndc 8 | 9 | 10 | class RndcBase(BaseHandler): 11 | 12 | def rndc(self): 13 | ops = self.get_options() 14 | r = rndc(ops['rndc_host']['value'], ops['rndc_port']['value'], ops['rndc_algo']['value'],ops['rndc_secret']['value']) 15 | return r 16 | 17 | 18 | class StatusHandler(RndcBase): 19 | 20 | @Auth 21 | def get(self): 22 | status = self.rndc().get_status_original() 23 | return self.jsonReturn({'code': 0, 'msg': 'Success', 'data': status}) 24 | 25 | 26 | class ReloadHandler(RndcBase): 27 | @Auth 28 | def get(self): 29 | result = self.rndc().reload() 30 | print result 31 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 32 | 33 | 34 | class ReconfigHandler(RndcBase): 35 | @Auth 36 | def get(self): 37 | result = self.rndc().reconfig() 38 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 39 | 40 | 41 | class FlushHandler(RndcBase): 42 | @Auth 43 | def get(self): 44 | result = self.rndc().flush() 45 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 46 | -------------------------------------------------------------------------------- /handler/system.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | from BaseHandler import BaseHandler 6 | from tornado.web import authenticated as Auth 7 | from model.models import Options 8 | from modules.rndc import rndc 9 | 10 | class StateHandler(BaseHandler): 11 | 12 | @Auth 13 | def get(self): 14 | self.nav_active['/system/state'] = 'active' 15 | ops = self.get_options() 16 | r = rndc(ops['rndc_host']['value'],ops['rndc_port']['value'],ops['rndc_algo']['value'],ops['rndc_secret']['value']) 17 | status = r.get_status() 18 | rndc_error = r.err_msg 19 | self.render('system/state.html',status=status,error=rndc_error) 20 | 21 | 22 | class SettingsHandler(BaseHandler): 23 | 24 | @Auth 25 | def get(self): 26 | self.nav_active['/settings'] = 'active' 27 | _data = self.db.query(Options).all() 28 | data = {} 29 | for i in _data: 30 | data[i.name] = {'id':i.id,'name':i.name,'value':i.value,'default':i.default_value} 31 | self.render('system/settings.html',data=data) 32 | 33 | 34 | @Auth 35 | def post(self): 36 | data = dict( 37 | primary_ns=self.get_argument('primary_ns') or None, 38 | second_ns = self.get_argument('second_ns') or None, 39 | resp_person = self.get_argument('resp_person') or None, 40 | rndc_host = self.get_argument('rndc_host') or None, 41 | rndc_port = self.get_argument('rndc_port') or None, 42 | rndc_algo = self.get_argument('rndc_algo') or None, 43 | rndc_secret = self.get_argument('rndc_secret') or None 44 | ) 45 | for name in data: 46 | self.db.query(Options).filter_by(name=name).update({'value':data[name],'update_time': self.time}) 47 | self.db.commit() 48 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 49 | 50 | 51 | -------------------------------------------------------------------------------- /handler/user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | from BaseHandler import BaseHandler 6 | from tornado.web import authenticated as Auth 7 | from model.models import User, or_, and_ 8 | 9 | class LoginHandler(BaseHandler): 10 | 11 | def get(self): 12 | if not self.session.isGuest: 13 | return self.redirect('/') # 已登录则跳转到首页 14 | next = self.get_argument("next", "/") 15 | self.render('user/login.html', next=next) 16 | 17 | def post(self): 18 | username = self.get_argument("username", None) 19 | password = self.get_argument("password", None) 20 | remember = self.get_argument("remember", "no") 21 | if not username or not password: 22 | return self.jsonReturn({'code':-1,'msg':u'参数错误'}) 23 | profile = self.db.query(User).filter(or_(User.username==username,User.email==username),and_(User.status=='1')).first() 24 | if profile: 25 | password_hash = self.md5(password+profile.password_key) 26 | if password_hash != profile.password: 27 | return self.jsonReturn({'code':-2,'msg':u'用户名或密码错误'}) 28 | session_data = { 29 | 'uid': profile.id, 30 | 'username': profile.username, 31 | 'email': profile.email, 32 | 'nickname': profile.nickname, 33 | 'login_time': profile.login_time, 34 | 'login_ip': profile.login_ip, 35 | 'login_location': profile.login_location, 36 | } 37 | self.create_session(session_data, remember) 38 | # 记录登录信息 39 | headers = self.request.headers 40 | login_ua = headers.get('User-Agent') 41 | login_ip = self.request.remote_ip 42 | login_data = { 43 | "login_count": int(profile.login_count) + 1, 44 | "login_time": self.time, 45 | "login_ua": login_ua, 46 | "login_ip": login_ip 47 | # "login_location": login_location 48 | } 49 | self.db.query(User).filter_by(id=profile.id).update(login_data) 50 | self.db.commit() 51 | # 跳转登录前的URL 52 | next_url = self.get_argument("next", "/") 53 | return self.jsonReturn({'code': 0, 'msg': u'Login Successful', 'next':next_url}) 54 | else: 55 | return self.jsonReturn({'code': -2, 'msg': u'用户名或密码错误'}) 56 | 57 | 58 | def create_session(self,data,remember): 59 | sid = self.session.gen_session_id() 60 | self.session.data = data 61 | self.session.isGuest = False 62 | #self.session.save() # Why don't save? See self._on_finish !! 63 | if remember == "yes": 64 | expires_days = 15 # Remember Session 15 days 65 | else: 66 | expires_days = None 67 | self.set_secure_cookie(self.cookie_name, sid, expires_days) 68 | 69 | 70 | # Sign Out 71 | class LogoutHandler(BaseHandler): 72 | def get(self): 73 | self.session.remove() 74 | self.clear_cookie(self.cookie_name) 75 | self.redirect(self.get_login_url()) 76 | 77 | 78 | # Profile 79 | class ProfileHandler(BaseHandler): 80 | @Auth 81 | def get(self): 82 | uid = self.session.get('uid') 83 | profile = self.db.query(User).filter_by(id=uid).first() 84 | self.nav_active['/settings'] = 'active' 85 | self.render('user/profile.html',profile=profile) 86 | 87 | @Auth 88 | def post(self): 89 | uid = self.session.get('uid') 90 | email = self.get_argument("email") or None 91 | phone = self.get_argument("phone") or None 92 | nickname = self.get_argument("nickname") or None 93 | dept = self.get_argument("dept") or None 94 | if not email: 95 | return self.jsonReturn({'code': -1, 'msg': u'Email不能为空'}) 96 | if not nickname: 97 | return self.jsonReturn({'code': -1, 'msg': u'姓名不能为空'}) 98 | chk = self.db.query(User).filter(User.email==email,User.id!=uid).first() 99 | if chk: 100 | return self.jsonReturn({'code': -2, 'msg': u'Email重复'}) 101 | self.db.query(User).filter_by(id=uid).update({'email':email, 'phone':phone, 'nickname':nickname, 'dept':dept, 'update_time': self.time}) 102 | self.db.commit() 103 | self.session.set('email',email) 104 | self.session.set('nickname',nickname) 105 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 106 | 107 | 108 | # Password 109 | class PasswdHandler(BaseHandler): 110 | @Auth 111 | def get(self): 112 | self.nav_active['/settings'] = 'active' 113 | self.render('user/passwd.html') 114 | 115 | @Auth 116 | def post(self): 117 | password_old = self.get_argument("password_old") or None 118 | password = self.get_argument("password") or None # New Password 119 | if not password_old or not password: 120 | return self.jsonReturn({'code': -1, 'msg': u'密码不能为空'}) 121 | uid = self.session.get('uid') 122 | profile = self.db.query(User).filter_by(id=uid).first() 123 | password_old_hash = self.md5(password_old + profile.password_key) 124 | if password_old_hash != profile.password: 125 | return self.jsonReturn({'code': -2, 'msg': u'原始密码错误'}) 126 | password_key = self.random_str(12) # 使用新的PasswordKey 127 | password_hash = self.md5(password + password_key) 128 | self.db.query(User).filter_by(id=uid).update({'password': password_hash, 'password_key': password_key,'update_time': self.time}) 129 | self.db.commit() 130 | return self.jsonReturn({'code': 0, 'msg': 'Success'}) 131 | -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio -------------------------------------------------------------------------------- /model/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | 5 | from sqlalchemy import Column, Integer, SmallInteger, VARCHAR, or_, and_ 6 | from sqlalchemy.ext.declarative import declarative_base 7 | 8 | Base = declarative_base() 9 | 10 | class User(Base): 11 | __tablename__ = 'users' 12 | 13 | id = Column(Integer,primary_key=True,autoincrement=True) 14 | username = Column(VARCHAR(32),nullable=False,unique=True) 15 | password = Column(VARCHAR(64),nullable=False) 16 | password_key = Column(VARCHAR(12),nullable=False,default='a1b2c3d4e5f6') 17 | email = Column(VARCHAR(32),nullable=False,unique=True) 18 | phone = Column(VARCHAR(32),nullable=True) 19 | nickname = Column(VARCHAR(32),nullable=True) 20 | gender = Column(SmallInteger,nullable=True) # 性别 21 | dept = Column(VARCHAR(32),nullable=True) # 部门 22 | role = Column(VARCHAR(32),nullable=True) 23 | lang = Column(VARCHAR(12),nullable=False,default='zh_CN') 24 | login_count = Column(Integer,nullable=False,default=0) 25 | login_time = Column(Integer,nullable=True) 26 | login_ua = Column(VARCHAR(600),nullable=True) 27 | login_ip = Column(VARCHAR(64),nullable=True) 28 | login_location = Column(VARCHAR(32),nullable=True) 29 | create_time = Column(Integer,nullable=True) 30 | update_time = Column(Integer,nullable=True) 31 | status = Column(SmallInteger,nullable=False,default=1) 32 | 33 | 34 | class Domain(Base): 35 | __tablename__ = 'domain' 36 | 37 | id = Column(Integer,primary_key=True,autoincrement=True) 38 | zone = Column(VARCHAR(128),nullable=False,unique=True) 39 | gid = Column(Integer,nullable=True) 40 | comment = Column(VARCHAR(256), nullable=True) 41 | record_count = Column(Integer,nullable=False,default=0) 42 | create_time = Column(Integer,nullable=True) 43 | update_time = Column(Integer,nullable=True) 44 | status = Column(SmallInteger, nullable=False, default=1) 45 | 46 | 47 | class Groups(Base): 48 | __tablename__ = 'groups' 49 | 50 | id = Column(Integer,primary_key=True,autoincrement=True) 51 | name = Column(VARCHAR(128),nullable=False,unique=True) 52 | domain_count = Column(Integer, nullable=False, default=0) 53 | 54 | 55 | class Record(Base): 56 | __tablename__ = 'record' 57 | 58 | id = Column(Integer,primary_key=True,autoincrement=True) 59 | zone = Column(VARCHAR(128), nullable=False) 60 | host = Column(VARCHAR(128), nullable=False) 61 | type = Column(VARCHAR(12), nullable=False) 62 | data = Column(VARCHAR(128), nullable=False) 63 | ttl = Column(Integer, nullable=False,default=600) 64 | mx_priority = Column(Integer, nullable=True) 65 | refresh = Column(Integer, nullable=True) 66 | retry = Column(Integer, nullable=True) 67 | expire = Column(Integer, nullable=True) 68 | minimum = Column(Integer, nullable=True) 69 | serial = Column(Integer, nullable=True) 70 | resp_person = Column(VARCHAR(64), nullable=True) 71 | primary_ns = Column(VARCHAR(64), nullable=True) 72 | comment = Column(VARCHAR(256), nullable=True) 73 | create_time = Column(Integer, nullable=True) 74 | update_time = Column(Integer, nullable=True) 75 | status = Column(SmallInteger, nullable=False, default=1) 76 | 77 | 78 | class Options(Base): 79 | __tablename__ = 'options' 80 | 81 | id = Column(Integer,primary_key=True,autoincrement=True) 82 | name = Column(VARCHAR(128), nullable=False,unique=True) 83 | value = Column(VARCHAR(500), nullable=True) 84 | default_value = Column(VARCHAR(500), nullable=True) 85 | category = Column(VARCHAR(32), nullable=False,default='default') 86 | update_time = Column(Integer, nullable=True) 87 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | Modules 2 | ======== 3 | 4 | - ISC Python Module (rndc) 5 | -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio -------------------------------------------------------------------------------- /modules/rndc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # Powered By KK Studio 4 | import socket 5 | 6 | class rndc: 7 | 8 | def __init__(self,host,port,algo,secret): 9 | self.err_msg = '' 10 | try: 11 | from isc.rndc import rndc as isc_rndc 12 | self.rndc = isc_rndc((host, int(port)), algo, secret) 13 | except ImportError: 14 | self.err_msg = 'Not Found ISC (RNDC) Module' 15 | print self.err_msg 16 | self.rndc = None 17 | except socket.error, e: # 网络层问题,rndc_host或者rndc_port错了 18 | self.err_msg = 'RNDC: Can not connect to DNS Server' 19 | print 'Socket Error' 20 | #print e # [Errno 111] Connection refused 21 | self.rndc = None 22 | except Exception, e: # Key错误时有出现该错误 23 | self.err_msg = 'RNDC Config Error' 24 | print type(e) # 25 | print e # Incorrect padding 26 | self.rndc = None 27 | 28 | 29 | def call(self,arg): 30 | if self.rndc: 31 | data = self.rndc.call(arg) 32 | else: 33 | data = {} 34 | return data 35 | 36 | 37 | def get_status(self): 38 | _st = self.call('status') 39 | if 'text' in _st: 40 | status = _st['text'].split('\n') 41 | return self.parse_status(status) 42 | else: 43 | return {} 44 | 45 | 46 | def get_status_original(self): 47 | _st = self.call('status') 48 | status = [] 49 | if 'text' in _st: 50 | _st = _st['text'].split('\n') 51 | for i in _st: 52 | status.append(str(i)) 53 | return status 54 | 55 | 56 | def parse_status(self,status): 57 | data = {'version':'','uptime':'','running':'Unknown'} 58 | for i in status: 59 | i = str(i) 60 | if 'version:' in i: 61 | v = i.split('version: ')[1] 62 | data['version'] = v.split(' ')[1] 63 | elif 'boot time: ' in i: 64 | data['uptime'] = i.split('boot time: ')[1] 65 | elif i == 'server is up and running': 66 | data['running'] = 'Running' 67 | return data 68 | 69 | 70 | def reload(self): 71 | ret = self.call('reload') 72 | return ret 73 | 74 | 75 | def reconfig(self): 76 | ret = self.call('reconfig') 77 | return ret 78 | 79 | 80 | def flush(self): 81 | ret = self.call('flush') 82 | return ret 83 | 84 | 85 | if __name__ == '__main__': 86 | host = '127.0.0.1' 87 | port = 953 88 | algo = 'md5' 89 | secret = 'kzIcztY4+xH0Px2SrsZyGQ==' 90 | r = rndc(host,port,algo,secret) 91 | print r.get_status() 92 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf8 -*- 3 | # Powered By KK Studio 4 | 5 | from app.DNStack import DNStack 6 | 7 | if __name__ == "__main__": 8 | app = DNStack() 9 | app.run() 10 | -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/css/dnstack.css: -------------------------------------------------------------------------------- 1 | /* 2 | * For Torweb CSS Style 3 | * Powered By KK Studio 4 | */ 5 | /* Global Styles */ 6 | body { 7 | margin-top: 100px; 8 | background-color: #222; 9 | font-family: '微软雅黑'; 10 | } 11 | 12 | @media(min-width:768px) { 13 | body { 14 | margin-top: 50px; 15 | } 16 | } 17 | 18 | #wrapper { 19 | padding-left: 0; 20 | } 21 | 22 | #page-wrapper { 23 | width: 100%; 24 | padding: 0; 25 | background-color: #fff; 26 | } 27 | 28 | .huge { 29 | font-size: 50px; 30 | line-height: normal; 31 | } 32 | 33 | @media(min-width:768px) { 34 | #wrapper { 35 | padding-left: 225px; 36 | } 37 | 38 | #page-wrapper { 39 | padding: 10px; 40 | } 41 | } 42 | 43 | /* Top Navigation */ 44 | 45 | .top-nav { 46 | padding: 0 15px; 47 | } 48 | 49 | .top-nav>li { 50 | display: inline-block; 51 | float: left; 52 | } 53 | 54 | .top-nav>li>a { 55 | padding-top: 15px; 56 | padding-bottom: 15px; 57 | line-height: 20px; 58 | color: #999; 59 | } 60 | 61 | .top-nav>li>a:hover, 62 | .top-nav>li>a:focus, 63 | .top-nav>.open>a, 64 | .top-nav>.open>a:hover, 65 | .top-nav>.open>a:focus { 66 | color: #fff; 67 | background-color: #000; 68 | } 69 | 70 | .top-nav>.open>.dropdown-menu { 71 | float: left; 72 | position: absolute; 73 | margin-top: 0; 74 | border: 1px solid rgba(0,0,0,.15); 75 | border-top-left-radius: 0; 76 | border-top-right-radius: 0; 77 | background-color: #fff; 78 | -webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175); 79 | box-shadow: 0 6px 12px rgba(0,0,0,.175); 80 | } 81 | 82 | .top-nav>.open>.dropdown-menu>li>a { 83 | white-space: normal; 84 | } 85 | 86 | ul.message-dropdown { 87 | padding: 0; 88 | max-height: 250px; 89 | overflow-x: hidden; 90 | overflow-y: auto; 91 | } 92 | 93 | li.message-preview { 94 | width: 275px; 95 | border-bottom: 1px solid rgba(0,0,0,.15); 96 | } 97 | 98 | li.message-preview>a { 99 | padding-top: 15px; 100 | padding-bottom: 15px; 101 | } 102 | 103 | li.message-footer { 104 | margin: 5px 0; 105 | } 106 | 107 | ul.alert-dropdown { 108 | width: 200px; 109 | } 110 | 111 | /* Side Navigation */ 112 | 113 | @media(min-width:768px) { 114 | .side-nav { 115 | position: fixed; 116 | top: 51px; 117 | left: 225px; 118 | width: 225px; 119 | margin-left: -225px; 120 | border: none; 121 | border-radius: 0; 122 | overflow-y: auto; 123 | background-color: #222; 124 | bottom: 0; 125 | overflow-x: hidden; 126 | padding-bottom: 40px; 127 | } 128 | 129 | .side-nav>li>a { 130 | width: 225px; 131 | } 132 | 133 | .side-nav li a:hover, 134 | .side-nav li a:focus { 135 | outline: none; 136 | background-color: #000 !important; 137 | } 138 | } 139 | 140 | .side-nav>li>ul { 141 | padding: 0; 142 | } 143 | 144 | .side-nav>li>ul>li>a { 145 | display: block; 146 | padding: 10px 15px 10px 38px; 147 | text-decoration: none; 148 | color: #999; 149 | } 150 | 151 | .side-nav>li>ul>li>a:hover { 152 | color: #fff; 153 | } 154 | 155 | /* Flot Chart Containers */ 156 | 157 | .flot-chart { 158 | display: block; 159 | height: 400px; 160 | } 161 | 162 | .flot-chart-content { 163 | width: 100%; 164 | height: 100%; 165 | } 166 | 167 | /* Custom Colored Panels */ 168 | 169 | .huge { 170 | font-size: 40px; 171 | } 172 | 173 | .panel-green { 174 | border-color: #5cb85c; 175 | } 176 | 177 | .panel-green > .panel-heading { 178 | border-color: #5cb85c; 179 | color: #fff; 180 | background-color: #5cb85c; 181 | } 182 | 183 | .panel-green > a { 184 | color: #5cb85c; 185 | } 186 | 187 | .panel-green > a:hover { 188 | color: #3d8b3d; 189 | } 190 | 191 | .panel-red { 192 | border-color: #d9534f; 193 | } 194 | 195 | .panel-red > .panel-heading { 196 | border-color: #d9534f; 197 | color: #fff; 198 | background-color: #d9534f; 199 | } 200 | 201 | .panel-red > a { 202 | color: #d9534f; 203 | } 204 | 205 | .panel-red > a:hover { 206 | color: #b52b27; 207 | } 208 | 209 | .panel-yellow { 210 | border-color: #f0ad4e; 211 | } 212 | 213 | .panel-yellow > .panel-heading { 214 | border-color: #f0ad4e; 215 | color: #fff; 216 | background-color: #f0ad4e; 217 | } 218 | 219 | .panel-yellow > a { 220 | color: #f0ad4e; 221 | } 222 | 223 | .panel-yellow > a:hover { 224 | color: #df8a13; 225 | } 226 | 227 | .container-fluid { 228 | min-height:556px; 229 | } 230 | 231 | .page-header { 232 | margin: 10px 0 10px; 233 | } 234 | 235 | .footer { 236 | padding: 8px 8px; 237 | color: #9d9d9d; 238 | } 239 | 240 | .tb-center th, .tb-center td { 241 | text-align: center; 242 | vertical-align: middle; 243 | } 244 | 245 | .page-title { 246 | font-size: 20px; 247 | } -------------------------------------------------------------------------------- /static/font-awesome/css/font-awesome.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "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", 4 | "sources": ["../scss/_path.scss","../scss/_core.scss","../scss/_larger.scss","../scss/_fixed-width.scss","../scss/_list.scss","../scss/_variables.scss","../scss/_bordered-pulled.scss","../scss/_animated.scss","../scss/_rotated-flipped.scss","../scss/_mixins.scss","../scss/_stacked.scss","../scss/_icons.scss"], 5 | "names": [], 6 | "file": "font-awesome.css" 7 | } 8 | -------------------------------------------------------------------------------- /static/font-awesome/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} 5 | -------------------------------------------------------------------------------- /static/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/img/screenshot/dashboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/img/screenshot/dashboard.jpg -------------------------------------------------------------------------------- /static/img/screenshot/domain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/img/screenshot/domain.jpg -------------------------------------------------------------------------------- /static/img/screenshot/group.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/img/screenshot/group.jpg -------------------------------------------------------------------------------- /static/img/screenshot/record.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/img/screenshot/record.jpg -------------------------------------------------------------------------------- /static/layer/skin/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/layer/skin/default/icon-ext.png -------------------------------------------------------------------------------- /static/layer/skin/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/layer/skin/default/icon.png -------------------------------------------------------------------------------- /static/layer/skin/default/layer.css: -------------------------------------------------------------------------------- 1 | .layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:ellipsis;white-space:nowrap}*html{background-image:url(about:blank);background-attachment:fixed}html #layuicss-skinlayercss{display:none;position:absolute;width:1989px}.layui-layer,.layui-layer-shade{position:fixed;_position:absolute;pointer-events:auto}.layui-layer-shade{top:0;left:0;width:100%;height:100%;_height:expression(document.body.offsetHeight+"px")}.layui-layer{-webkit-overflow-scrolling:touch;top:150px;left:0;margin:0;padding:0;background-color:#fff;-webkit-background-clip:content;box-shadow:1px 1px 50px rgba(0,0,0,.3)}.layui-layer-close{position:absolute}.layui-layer-content{position:relative}.layui-layer-border{border:1px solid #B2B2B2;border:1px solid rgba(0,0,0,.1);box-shadow:1px 1px 5px rgba(0,0,0,.2)}.layui-layer-load{background:url(loading-1.gif) center center no-repeat #eee}.layui-layer-ico{background:url(icon.png) no-repeat}.layui-layer-btn a,.layui-layer-dialog .layui-layer-ico,.layui-layer-setwin a{display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-move{display:none;position:fixed;*position:absolute;left:0;top:0;width:100%;height:100%;cursor:move;opacity:0;filter:alpha(opacity=0);background-color:#fff;z-index:2147483647}.layui-layer-resize{position:absolute;width:15px;height:15px;right:0;bottom:0;cursor:se-resize}.layui-layer{border-radius:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-duration:.3s;animation-duration:.3s}@-webkit-keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceIn{0%{opacity:0;-webkit-transform:scale(.5);-ms-transform:scale(.5);transform:scale(.5)}100%{opacity:1;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale(.1) translateY(-2000px);-ms-transform:scale(.1) translateY(-2000px);transform:scale(.1) translateY(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateY(60px);-ms-transform:scale(.475) translateY(60px);transform:scale(.475) translateY(60px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-01{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translateY(2000px);-ms-transform:translateY(2000px);transform:translateY(2000px)}100%{opacity:1;-webkit-transform:translateY(0);-ms-transform:translateY(0);transform:translateY(0)}}.layer-anim-02{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale(.1) translateX(-2000px);-ms-transform:scale(.1) translateX(-2000px);transform:scale(.1) translateX(-2000px);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}60%{opacity:1;-webkit-transform:scale(.475) translateX(48px);-ms-transform:scale(.475) translateX(48px);transform:scale(.475) translateX(48px);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}}.layer-anim-03{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translateX(-100%) rotate(-120deg);-ms-transform:translateX(-100%) rotate(-120deg);transform:translateX(-100%) rotate(-120deg)}100%{opacity:1;-webkit-transform:translateX(0) rotate(0);-ms-transform:translateX(0) rotate(0);transform:translateX(0) rotate(0)}}.layer-anim-04{-webkit-animation-name:rollIn;animation-name:rollIn}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.layer-anim-05{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes shake{0%,100%{-webkit-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);transform:translateX(10px)}}@keyframes shake{0%,100%{-webkit-transform:translateX(0);-ms-transform:translateX(0);transform:translateX(0)}10%,30%,50%,70%,90%{-webkit-transform:translateX(-10px);-ms-transform:translateX(-10px);transform:translateX(-10px)}20%,40%,60%,80%{-webkit-transform:translateX(10px);-ms-transform:translateX(10px);transform:translateX(10px)}}.layer-anim-06{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes bounceOut{100%{opacity:0;-webkit-transform:scale(.7);-ms-transform:scale(.7);transform:scale(.7)}30%{-webkit-transform:scale(1.05);-ms-transform:scale(1.05);transform:scale(1.05)}0%{-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}}.layer-anim-close{-webkit-animation-name:bounceOut;animation-name:bounceOut;-webkit-animation-duration:.2s;animation-duration:.2s}.layui-layer-title{padding:0 80px 0 20px;height:42px;line-height:42px;border-bottom:1px solid #eee;font-size:14px;color:#333;overflow:hidden;background-color:#F8F8F8;border-radius:2px 2px 0 0}.layui-layer-setwin{position:absolute;right:15px;*right:0;top:15px;font-size:0;line-height:initial}.layui-layer-setwin a{position:relative;width:16px;height:16px;margin-left:10px;font-size:12px;_overflow:hidden}.layui-layer-setwin .layui-layer-min cite{position:absolute;width:14px;height:2px;left:0;top:50%;margin-top:-1px;background-color:#2E2D3C;cursor:pointer;_overflow:hidden}.layui-layer-setwin .layui-layer-min:hover cite{background-color:#2D93CA}.layui-layer-setwin .layui-layer-max{background-position:-32px -40px}.layui-layer-setwin .layui-layer-max:hover{background-position:-16px -40px}.layui-layer-setwin .layui-layer-maxmin{background-position:-65px -40px}.layui-layer-setwin .layui-layer-maxmin:hover{background-position:-49px -40px}.layui-layer-setwin .layui-layer-close1{background-position:0 -40px;cursor:pointer}.layui-layer-setwin .layui-layer-close1:hover{opacity:.7}.layui-layer-setwin .layui-layer-close2{position:absolute;right:-28px;top:-28px;width:30px;height:30px;margin-left:0;background-position:-149px -31px;*right:-18px;_display:none}.layui-layer-setwin .layui-layer-close2:hover{background-position:-180px -31px}.layui-layer-btn{text-align:right;padding:0 10px 12px;pointer-events:auto;user-select:none;-webkit-user-select:none}.layui-layer-btn a{height:28px;line-height:28px;margin:0 6px;padding:0 15px;border:1px solid #dedede;background-color:#f1f1f1;color:#333;border-radius:2px;font-weight:400;cursor:pointer;text-decoration:none}.layui-layer-btn a:hover{opacity:.9;text-decoration:none}.layui-layer-btn a:active{opacity:.8}.layui-layer-btn .layui-layer-btn0{border-color:#4898d5;background-color:#2e8ded;color:#fff}.layui-layer-btn-l{text-align:left}.layui-layer-btn-c{text-align:center}.layui-layer-dialog{min-width:260px}.layui-layer-dialog .layui-layer-content{position:relative;padding:20px;line-height:24px;word-break:break-all;overflow:hidden;font-size:14px;overflow-x:hidden;overflow-y:auto}.layui-layer-dialog .layui-layer-content .layui-layer-ico{position:absolute;top:16px;left:15px;_left:-40px;width:30px;height:30px}.layui-layer-ico1{background-position:-30px 0}.layui-layer-ico2{background-position:-60px 0}.layui-layer-ico3{background-position:-90px 0}.layui-layer-ico4{background-position:-120px 0}.layui-layer-ico5{background-position:-150px 0}.layui-layer-ico6{background-position:-180px 0}.layui-layer-rim{border:6px solid #8D8D8D;border:6px solid rgba(0,0,0,.3);border-radius:5px;box-shadow:none}.layui-layer-msg{min-width:180px;border:1px solid #D3D4D3;box-shadow:none}.layui-layer-hui{min-width:100px;background-color:#000;filter:alpha(opacity=60);background-color:rgba(0,0,0,.6);color:#fff;border:none}.layui-layer-hui .layui-layer-content{padding:12px 25px;text-align:center}.layui-layer-dialog .layui-layer-padding{padding:20px 20px 20px 55px;text-align:left}.layui-layer-page .layui-layer-content{position:relative;overflow:auto}.layui-layer-iframe .layui-layer-btn,.layui-layer-page .layui-layer-btn{padding-top:10px}.layui-layer-nobg{background:0 0}.layui-layer-iframe iframe{display:block;width:100%}.layui-layer-loading{border-radius:100%;background:0 0;box-shadow:none;border:none}.layui-layer-loading .layui-layer-content{width:60px;height:24px;background:url(loading-0.gif) no-repeat}.layui-layer-loading .layui-layer-loading1{width:37px;height:37px;background:url(loading-1.gif) no-repeat}.layui-layer-ico16,.layui-layer-loading .layui-layer-loading2{width:32px;height:32px;background:url(loading-2.gif) no-repeat}.layui-layer-tips{background:0 0;box-shadow:none;border:none}.layui-layer-tips .layui-layer-content{position:relative;line-height:22px;min-width:12px;padding:5px 10px;font-size:12px;_float:left;border-radius:2px;box-shadow:1px 1px 3px rgba(0,0,0,.2);background-color:#000;color:#fff}.layui-layer-tips .layui-layer-close{right:-2px;top:-1px}.layui-layer-tips i.layui-layer-TipsG{position:absolute;width:0;height:0;border-width:8px;border-color:transparent;border-style:dashed;*overflow:hidden}.layui-layer-tips i.layui-layer-TipsB,.layui-layer-tips i.layui-layer-TipsT{left:5px;border-right-style:solid;border-right-color:#000}.layui-layer-tips i.layui-layer-TipsT{bottom:-8px}.layui-layer-tips i.layui-layer-TipsB{top:-8px}.layui-layer-tips i.layui-layer-TipsL,.layui-layer-tips i.layui-layer-TipsR{top:1px;border-bottom-style:solid;border-bottom-color:#000}.layui-layer-tips i.layui-layer-TipsR{left:-8px}.layui-layer-tips i.layui-layer-TipsL{right:-8px}.layui-layer-lan[type=dialog]{min-width:280px}.layui-layer-lan .layui-layer-title{background:#4476A7;color:#fff;border:none}.layui-layer-lan .layui-layer-btn{padding:10px;text-align:right;border-top:1px solid #E9E7E7}.layui-layer-lan .layui-layer-btn a{background:#BBB5B5;border:none}.layui-layer-lan .layui-layer-btn .layui-layer-btn1{background:#C9C5C5}.layui-layer-molv .layui-layer-title{background:#009f95;color:#fff;border:none}.layui-layer-molv .layui-layer-btn a{background:#009f95}.layui-layer-molv .layui-layer-btn .layui-layer-btn1{background:#92B8B1}.layui-layer-iconext{background:url(icon-ext.png) no-repeat}.layui-layer-prompt .layui-layer-input{display:block;width:220px;height:30px;margin:0 auto;line-height:30px;padding:0 5px;border:1px solid #ccc;box-shadow:1px 1px 5px rgba(0,0,0,.1) inset;color:#333}.layui-layer-prompt textarea.layui-layer-input{width:300px;height:100px;line-height:20px}.layui-layer-prompt .layui-layer-content{padding:20px}.layui-layer-prompt .layui-layer-btn{padding-top:0}.layui-layer-tab{box-shadow:1px 1px 50px rgba(0,0,0,.4)}.layui-layer-tab .layui-layer-title{padding-left:0;border-bottom:1px solid #ccc;background-color:#eee;overflow:visible}.layui-layer-tab .layui-layer-title span{position:relative;float:left;min-width:80px;max-width:260px;padding:0 20px;text-align:center;cursor:default;overflow:hidden}.layui-layer-tab .layui-layer-title span.layui-layer-tabnow{height:43px;border-left:1px solid #ccc;border-right:1px solid #ccc;background-color:#fff;z-index:10}.layui-layer-tab .layui-layer-title span:first-child{border-left:none}.layui-layer-tabmain{line-height:24px;clear:both}.layui-layer-tabmain .layui-layer-tabli{display:none}.layui-layer-tabmain .layui-layer-tabli.xubox_tab_layer{display:block}.xubox_tabclose{position:absolute;right:10px;top:5px;cursor:pointer}.layui-layer-photos{-webkit-animation-duration:1s;animation-duration:1s}.layui-layer-photos .layui-layer-content{overflow:hidden;text-align:center}.layui-layer-photos .layui-layer-phimg img{position:relative;width:100%;display:inline-block;*display:inline;*zoom:1;vertical-align:top}.layui-layer-imgbar,.layui-layer-imguide{display:none}.layui-layer-imgnext,.layui-layer-imgprev{position:absolute;top:50%;width:27px;_width:44px;height:44px;margin-top:-22px;outline:0;blr:expression(this.onFocus=this.blur())}.layui-layer-imgprev{left:10px;background-position:-5px -5px;_background-position:-70px -5px}.layui-layer-imgprev:hover{background-position:-33px -5px;_background-position:-120px -5px}.layui-layer-imgnext{right:10px;_right:8px;background-position:-5px -50px;_background-position:-70px -50px}.layui-layer-imgnext:hover{background-position:-33px -50px;_background-position:-120px -50px}.layui-layer-imgbar{position:absolute;left:0;bottom:0;width:100%;height:32px;line-height:32px;background-color:rgba(0,0,0,.8);background-color:#000\9;filter:Alpha(opacity=80);color:#fff;overflow:hidden;font-size:0}.layui-layer-imgtit *{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:12px}.layui-layer-imgtit a{max-width:65%;overflow:hidden;color:#fff}.layui-layer-imgtit a:hover{color:#fff;text-decoration:underline}.layui-layer-imgtit em{padding-left:10px;font-style:normal}@media screen and (max-width:1100px){.layui-layer-iframe{overflow-y:auto;-webkit-overflow-scrolling:touch}} -------------------------------------------------------------------------------- /static/layer/skin/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/layer/skin/default/loading-0.gif -------------------------------------------------------------------------------- /static/layer/skin/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/layer/skin/default/loading-1.gif -------------------------------------------------------------------------------- /static/layer/skin/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xkstudio/DNStack/b74423128b133e3a0cc4ea712ccefbbedec2d826/static/layer/skin/default/loading-2.gif -------------------------------------------------------------------------------- /view/domain/group.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 分组管理 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% for row in data %} 25 | 26 | 27 | 28 | 29 | 33 | 34 | {% endfor %} 35 | 36 |
分组名称域名统计操作
{{ row.name }}{{ row.domain_count }} 30 | 31 | 32 |
37 |
38 |
39 | 40 | 64 | {% endblock %} 65 | {% block footer %} 66 | 84 | {% endblock %} -------------------------------------------------------------------------------- /view/domain/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 域名管理 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {% for row in data %} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | {% endfor %} 47 | 48 |
域名分组创建时间记录统计状态管理
{{ row.zone }}{{ group[row.gid] }}{{ handler.format_time(row.create_time) }}{{ row.record_count }}{{ status[row.status] }} 36 | 解析 37 | {% if row.status == 1 %} 38 | 39 | {% else %} 40 | 41 | {% endif %} 42 | 43 | 44 |
49 |
50 |
51 | 52 | 93 | {% endblock %} 94 | {% block footer %} 95 | 221 | {% endblock %} -------------------------------------------------------------------------------- /view/domain/record.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 解析管理 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% for row in data %} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 54 | 55 | {% endfor %} 56 | 57 |
主机记录记录类型记录值MX优先级TTL更新时间状态操作
{{ row.host }}{{ row.type }}{{ row.data }}{{ row.mx_priority or '' }}{{ row.ttl }}{{ handler.format_time(row.update_time) }}{{ status[row.status] }} 42 | {% if row.host == '@' and row.type in ['NS','SOA'] %} 43 | -- 44 | {% else %} 45 | 46 | {% if row.status == 1 %} 47 | 48 | {% else %} 49 | 50 | {% endif %} 51 | 52 | {% endif %} 53 |
58 |
59 |
60 | 61 | 126 | {% endblock %} 127 | {% block footer %} 128 | 273 | {% endblock %} -------------------------------------------------------------------------------- /view/domain/state.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 域名解析数据统计分析 6 |
7 |
8 |
9 |
10 |
11 |

DNStack开源版暂不支持该功能。

12 |
13 |
14 | {% endblock %} -------------------------------------------------------------------------------- /view/index/blank.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | DNStack - Blank Page 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
#Col 1Col 2Col 3Action
12345
12345
12345
48 |
49 |
50 | {% endblock %} -------------------------------------------------------------------------------- /view/index/index.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block head %} 3 | 10 | {% endblock %} 11 | {% block content %} 12 |
13 |
14 | DNStack 15 |
16 |
17 |
18 |
19 |
20 |
21 | 22 | 欢迎您,{{ handler.session.get('nickname') }}!您上次登录时间为:{{ handler.format_time(handler.session.get('login_time')) }},登录IP:{{ handler.session.get('login_ip') }} 23 |
24 |
25 |
26 |
27 |

域 名

28 |
29 |
30 |
31 |

记 录

32 |
33 |
34 |
35 |

分 组

36 |
37 |
38 |
39 |

用 户

40 |
41 |
42 | {% endblock %} 43 | {% block footer %} 44 | 45 | 46 | 133 | {% endblock %} -------------------------------------------------------------------------------- /view/index/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | DNStack - Template - Sample 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 85 |
86 |
87 | 88 |
89 |
90 | 91 |

Template Sample

92 |
93 |
94 | 95 |
96 | 97 |
98 |
99 | 100 |
101 | 102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /view/layout/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/layout/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DNStack 8 | 9 | 10 | 11 | {% block head %}{% endblock %} 12 | -------------------------------------------------------------------------------- /view/layout/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | DNStack 10 | 11 | 12 | 13 | {% block head %}{% endblock %} 14 | 15 | 16 |
17 | 18 | 37 |
38 |
39 | {% block content %} 40 | 41 |
42 |
43 | 44 |

Hello,DNStack.

45 |
46 |
47 | 48 | {% endblock %} 49 |
50 | 51 |
52 |
53 | 54 |
55 | 56 |
57 | {% include "layout/footer.html" %} 58 | {% block footer %}{% endblock %} 59 | 60 | -------------------------------------------------------------------------------- /view/layout/nav.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/layout/top.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /view/page/error.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |

Error Page

5 |

{{ title }}

6 |
7 | {% endblock %} -------------------------------------------------------------------------------- /view/system/settings.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 系统设置 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 | 26 |
27 |
永久授权
28 |
29 |
30 | 31 |
32 | 33 |
34 |
英文句点结尾,例如:ns1.qq.com.
35 |
36 |
37 | 38 |
39 | 40 |
41 |
英文句点结尾,例如:ns2.qq.com.
42 |
43 |
44 | 45 |
46 | 47 |
48 |
不加'@'符号,例如:admin.qq.com.
49 |
50 |
51 | 52 |
53 | 54 |
55 |
56 |
57 | 58 |
59 | 60 |
61 |
62 |
63 | 64 |
65 | 66 |
67 |
68 |
69 | 70 |
71 | 72 |
73 |
74 |
75 | 76 |
77 | 78 |
79 |
80 |
81 |
82 |
83 | {% endblock %} 84 | {% block footer %} 85 | 132 | {% endblock %} -------------------------------------------------------------------------------- /view/system/state.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 系统管理 6 | 7 | 8 | 9 |
10 |
11 |
12 |
13 | {% if error %} 14 |
15 |
16 | 17 | {{ error }} 18 |
19 |
20 | {% endif %} 21 | 80 |
81 | {% endblock %} -------------------------------------------------------------------------------- /view/user/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include "layout/head.html" %} 4 | 5 |
6 |
7 |
8 |
9 |
登录DNStack
10 |
11 |
12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 |
24 | 27 | 忘记密码 28 |
29 |
30 |
31 | 35 |
36 |
37 |
38 |
39 | 没有帐号? 40 | 41 | 注册 42 | 43 | {{ handler.copyright_year }} (C) KK Studio 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 97 |
98 | {% include "layout/footer.html" %} 99 | 151 | 152 | -------------------------------------------------------------------------------- /view/user/passwd.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 修改密码 6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 |
38 |
39 | {% endblock %} 40 | {% block footer %} 41 | 96 | {% endblock %} -------------------------------------------------------------------------------- /view/user/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "layout/main.html" %} 2 | {% block content %} 3 |
4 |
5 | 个人设置 6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 | 40 |
41 |
42 |
43 | 44 |
45 | 46 |
47 |
48 |
49 | 50 |
51 | 52 |
53 |
54 |
55 | 56 |
57 | 58 |
59 |
60 |
61 |
62 |
63 | {% endblock %} 64 | {% block footer %} 65 | 109 | {% endblock %} --------------------------------------------------------------------------------