├── 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 | 
16 |
17 |
18 | #### Domain Admin
19 |
20 | 
21 |
22 |
23 | #### Record
24 |
25 | 
26 |
27 |
28 | #### Group
29 |
30 | 
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 | 
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 |
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 |
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 |
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 | Col 1 |
19 | Col 2 |
20 | Col 3 |
21 | Action |
22 |
23 |
24 |
25 |
26 | 1 |
27 | 2 |
28 | 3 |
29 | 4 |
30 | 5 |
31 |
32 |
33 | 1 |
34 | 2 |
35 | 3 |
36 | 4 |
37 | 5 |
38 |
39 |
40 | 1 |
41 | 2 |
42 | 3 |
43 | 4 |
44 | 5 |
45 |
46 |
47 |
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 |
29 |
33 |
37 |
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 |
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 |
55 |
56 |
57 | {% include "layout/footer.html" %}
58 | {% block footer %}{% endblock %}
59 |
60 |
--------------------------------------------------------------------------------
/view/layout/nav.html:
--------------------------------------------------------------------------------
1 |
2 | -
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 |
--------------------------------------------------------------------------------
/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 |
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 |
22 |
23 |
24 |
25 |
系统状态
26 |
27 |
50 |
51 |
52 |
53 |
54 |
55 |
管理操作
56 |
57 |
77 |
78 |
79 |
80 |
81 | {% endblock %}
--------------------------------------------------------------------------------
/view/user/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% include "layout/head.html" %}
4 |
5 |
98 | {% include "layout/footer.html" %}
99 |
151 |
152 |
--------------------------------------------------------------------------------
/view/user/passwd.html:
--------------------------------------------------------------------------------
1 | {% extends "layout/main.html" %}
2 | {% block content %}
3 |
9 |
39 | {% endblock %}
40 | {% block footer %}
41 |
96 | {% endblock %}
--------------------------------------------------------------------------------
/view/user/profile.html:
--------------------------------------------------------------------------------
1 | {% extends "layout/main.html" %}
2 | {% block content %}
3 |
9 |
63 | {% endblock %}
64 | {% block footer %}
65 |
109 | {% endblock %}
--------------------------------------------------------------------------------