├── .gitignore ├── LICENSE ├── README.md ├── cert_manage ├── __init__.py ├── celery.py ├── context_processor.py ├── settings.py ├── tasks.py ├── urls.py ├── utils.py ├── views.py └── wsgi.py ├── certs ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── serializers.py ├── signals_handler.py ├── tasks.py ├── templates │ └── cert │ │ ├── _base_create_update.html │ │ ├── cert_create_update.html │ │ ├── cert_detail.html │ │ └── cert_list.html ├── tests.py ├── urls │ ├── __init__.py │ ├── api_urls.py │ └── views_urls.py ├── utils.py └── views.py ├── cms ├── config_example.py ├── data └── .gitkeep ├── i18n └── zh_CN │ └── LC_MESSAGES │ └── django.po ├── logs └── .gitkeep ├── manage.py ├── requirements ├── .gitkeep └── requirements.txt ├── run_server.py ├── static ├── css │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ ├── bootstrap.min.css.map │ ├── plugin │ │ ├── datatables │ │ │ ├── datatables.min.css │ │ │ └── datatables.min.css.bak │ │ ├── font-awesome │ │ │ ├── css │ │ │ │ ├── fa-brands.css │ │ │ │ ├── fa-brands.min.css │ │ │ │ ├── fa-regular.css │ │ │ │ ├── fa-regular.min.css │ │ │ │ ├── fa-solid.css │ │ │ │ ├── fa-solid.min.css │ │ │ │ ├── fontawesome-all.css │ │ │ │ ├── fontawesome-all.min.css │ │ │ │ ├── fontawesome.css │ │ │ │ └── fontawesome.min.css │ │ │ ├── less │ │ │ │ ├── _animated.less │ │ │ │ ├── _bordered-pulled.less │ │ │ │ ├── _core.less │ │ │ │ ├── _fixed-width.less │ │ │ │ ├── _icons.less │ │ │ │ ├── _larger.less │ │ │ │ ├── _list.less │ │ │ │ ├── _mixins.less │ │ │ │ ├── _rotated-flipped.less │ │ │ │ ├── _screen-reader.less │ │ │ │ ├── _stacked.less │ │ │ │ ├── _variables.less │ │ │ │ ├── fa-brands.less │ │ │ │ ├── fa-regular.less │ │ │ │ ├── fa-solid.less │ │ │ │ └── fontawesome.less │ │ │ ├── scss │ │ │ │ ├── _animated.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _screen-reader.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── fa-brands.scss │ │ │ │ ├── fa-regular.scss │ │ │ │ ├── fa-solid.scss │ │ │ │ └── fontawesome.scss │ │ │ └── webfonts │ │ │ │ ├── fa-brands-400.eot │ │ │ │ ├── fa-brands-400.svg │ │ │ │ ├── fa-brands-400.ttf │ │ │ │ ├── fa-brands-400.woff │ │ │ │ ├── fa-brands-400.woff2 │ │ │ │ ├── fa-regular-400.eot │ │ │ │ ├── fa-regular-400.svg │ │ │ │ ├── fa-regular-400.ttf │ │ │ │ ├── fa-regular-400.woff │ │ │ │ ├── fa-regular-400.woff2 │ │ │ │ ├── fa-solid-900.eot │ │ │ │ ├── fa-solid-900.svg │ │ │ │ ├── fa-solid-900.ttf │ │ │ │ ├── fa-solid-900.woff │ │ │ │ └── fa-solid-900.woff2 │ │ ├── select2 │ │ │ ├── select2-bootstrap.min.css │ │ │ └── select2.min.css │ │ ├── skins │ │ │ ├── _all-skins.css │ │ │ ├── _all-skins.min.css │ │ │ ├── skin-black-light.css │ │ │ ├── skin-black-light.min.css │ │ │ ├── skin-black.css │ │ │ ├── skin-black.min.css │ │ │ ├── skin-blue-light.css │ │ │ ├── skin-blue-light.min.css │ │ │ ├── skin-blue.css │ │ │ ├── skin-blue.min.css │ │ │ ├── skin-green-light.css │ │ │ ├── skin-green-light.min.css │ │ │ ├── skin-green.css │ │ │ ├── skin-green.min.css │ │ │ ├── skin-purple-light.css │ │ │ ├── skin-purple-light.min.css │ │ │ ├── skin-purple.css │ │ │ ├── skin-purple.min.css │ │ │ ├── skin-red-light.css │ │ │ ├── skin-red-light.min.css │ │ │ ├── skin-red.css │ │ │ ├── skin-red.min.css │ │ │ ├── skin-yellow-light.css │ │ │ ├── skin-yellow-light.min.css │ │ │ ├── skin-yellow.css │ │ │ └── skin-yellow.min.css │ │ ├── sweetalert2 │ │ │ ├── sweetalert2.css │ │ │ └── sweetalert2.min.css │ │ └── toastr │ │ │ └── toastr.min.css │ └── style.css └── js │ ├── bootstrap.min.js │ ├── cert.js │ ├── jquery-2.2.4.js │ └── plugin │ ├── datatables │ ├── datatables.min.js │ ├── i18n │ │ ├── English.lang │ │ └── zh-hans.json │ └── pdfmake.min.js.map │ ├── select2 │ ├── select2.full.min.js │ └── select2.i18n.cn.js │ ├── sweetalert2 │ ├── sweetalert2.all.js │ ├── sweetalert2.all.min.js │ ├── sweetalert2.js │ └── sweetalert2.min.js │ └── toastr │ └── toastr.min.js ├── support ├── .gitkeep └── supervisord │ └── cert_manage.ini ├── templates ├── _base.html ├── _copyright.html ├── _foot_js.html ├── _head_css_js.html ├── flash_message_standalone.html └── index.html ├── tmp └── .gitkeep └── users ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── authentication.py ├── forms.py ├── migrations ├── 0001_initial.py └── __init__.py ├── mixins.py ├── models ├── __init__.py ├── authentication.py └── user.py ├── permissions.py ├── serializers.py ├── signals.py ├── templates └── users │ └── login.html ├── tests.py ├── urls ├── __init__.py ├── api_urls.py └── views_urls.py ├── utils.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | .idea/ 18 | data/static/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | *.pid 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | config.py 60 | local_settings.py 61 | db.sqlite3 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | .env 90 | .venv 91 | env/ 92 | venv/ 93 | ENV/ 94 | env.bak/ 95 | venv.bak/ 96 | 97 | # Spyder project settings 98 | .spyderproject 99 | .spyproject 100 | 101 | # Rope project settings 102 | .ropeproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ericwinn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cert_manage 2 | 证书管理系统 3 | 4 | ![Total visitor](https://visitor-count-badge.herokuapp.com/total.svg?repo_id=cert_manage) 5 | ![Visitors in today](https://visitor-count-badge.herokuapp.com/today.svg?repo_id=cert_manage) 6 | [![Python3](https://img.shields.io/badge/python-3.6-green.svg?style=plastic)](https://www.python.org/) 7 | [![Django](https://img.shields.io/badge/django-2.1.11-brightgreen.svg?style=plastic)](https://www.djangoproject.com/) 8 | 9 | --- 10 | 11 | Cert_manage 是一个开源的证书管理系统,用于检测证书到期日期并提醒管理者,支持基于URL的在线检测和PEM文件离线检测,支持WEB SSL证书、APP开发者证书、APP推送证书等,使用MIT开源协议 12 | 13 | Cert_manage 使用 Python / Django 进行开发,遵循 Web 2.0 规范 14 | 15 | ![主页面](https://www.itnotebooks.com/wp-content/uploads/2019/09/主页面截图-1.png) 16 | 17 | 18 | # 安装文档 19 | 20 | ## 安装Python 3.6 21 | 22 | ~~~ 23 | 参考网上其它文档 24 | ~~~ 25 | 26 | ## 创建虚拟环境 27 | 28 | ~~~ 29 | $ python3 -m venv /opt/py3 30 | ~~~ 31 | 32 | ## 载入虚拟环境 33 | 34 | ~~~ 35 | $ source /opt/py3/bin/activate 36 | ~~~ 37 | 38 | ## 获取cert_mange代码 39 | 40 | ~~~ 41 | $ cd /opt/ 42 | $ git clone --depth=1 https://github.com/itnotebooks/cert_manage.git 43 | # 如果没有安装 git 请先安装 44 | ~~~ 45 | 46 | ## 安装依赖 47 | 48 | ~~~ 49 | $ cd /opt/cert_manage 50 | $ pip install -r requirements/requirements.txt 51 | ~~~ 52 | 53 | ## 修改配置文件 54 | 55 | ~~~ 56 | $ cd /opt/cert_manage 57 | $ cp config_example.py config.py 58 | $ vim config.py 59 | ~~~ 60 | 61 | ## 启动 cert_manage 62 | 63 | ~~~ 64 | $ cd /opt/cert_manage 65 | $ python run_server.py 66 | ~~~ 67 | 68 | ## 访问 69 | 70 | ~~~ 71 | http://127.0.0.1:8080 72 | 初始用户名:admin 73 | 初始密码:admin 74 | ~~~ 75 | -------------------------------------------------------------------------------- /cert_manage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/cert_manage/__init__.py -------------------------------------------------------------------------------- /cert_manage/celery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-4 上午7:01 6 | # @Version : 1.0 7 | # @File : celery 8 | # @Software : PyCharm 9 | 10 | import os 11 | 12 | from celery import Celery 13 | from django.conf import settings 14 | 15 | C_FORCE_ROOT = True 16 | 17 | # set the default Django settings module for the 'celery' program. 18 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cert_manage.settings') 19 | 20 | app = Celery('cert_manage', backend=settings.CELERY_BROKER_URL, broker=settings.CELERY_BROKER_URL) 21 | 22 | app.conf.timezone = 'Asia/Shanghai' 23 | 24 | app.conf.update( 25 | task_serializer='json', 26 | accept_content=['json', 'pickle'], 27 | result_serializer='json', 28 | timezone='Asia/Shanghai', 29 | enable_utc=True, 30 | ) 31 | 32 | FORKS = 60 33 | TIMEOUT = 180 34 | PERIOD_TASK = os.environ.get("PERIOD_TASK", "on") 35 | -------------------------------------------------------------------------------- /cert_manage/context_processor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | 4 | 5 | def cmdb_processor(request): 6 | context = {} 7 | 8 | # Setting default pk 9 | context.update( 10 | {'DEFAULT_PK': '00000000-0000-0000-0000-000000000000'} 11 | ) 12 | return context 13 | -------------------------------------------------------------------------------- /cert_manage/tasks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-5 下午5:26 6 | # @Version : 1.0 7 | # @File : tasks 8 | # @Software : PyCharm 9 | from .celery import app 10 | from django.core.mail import send_mail 11 | from django.conf import settings 12 | 13 | 14 | @app.task 15 | def send_mail_async(*args, **kwargs): 16 | """ Using celery to send email async 17 | 18 | You can use it as django send_mail function 19 | 20 | Example: 21 | send_mail_sync.delay(subject, message, from_mail, recipient_list, fail_silently=False, html_message=None) 22 | 23 | Also you can ignore the from_mail, unlike django send_mail, from_email is not a require args: 24 | 25 | Example: 26 | send_mail_sync.delay(subject, message, recipient_list, fail_silently=False, html_message=None) 27 | """ 28 | if len(args) == 3: 29 | args = list(args) 30 | args[0] = settings.EMAIL_SUBJECT_PREFIX + args[0] 31 | args.insert(2, settings.EMAIL_HOST_USER) 32 | args = tuple(args) 33 | 34 | try: 35 | send_mail(*args, **kwargs) 36 | except Exception as e: 37 | print("Sending mail error: {}".format(e)) 38 | -------------------------------------------------------------------------------- /cert_manage/urls.py: -------------------------------------------------------------------------------- 1 | """cert_manage URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | 17 | from django.contrib import admin 18 | from django.conf import settings 19 | from django.conf.urls import url, include 20 | from django.conf.urls.static import static 21 | from cert_manage.views import IndexView 22 | 23 | urlpatterns = [ 24 | url(r'^$', IndexView.as_view(), name='index'), 25 | url(r'^users/', include('users.urls.views_urls', namespace='users')), 26 | url(r'^certs/', include('certs.urls.views_urls', namespace='certs')), 27 | 28 | url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')), 29 | url(r'^api/certs/', include('certs.urls.api_urls', namespace='api-certs')), 30 | # External apps url 31 | url(r'^admin/', admin.site.urls), 32 | url(r'^captcha/', include('captcha.urls')), 33 | ] 34 | 35 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ 36 | + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 37 | -------------------------------------------------------------------------------- /cert_manage/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 下午4:29 6 | # @Version : 1.0 7 | # @File : utils 8 | # @Software : PyCharm 9 | import time 10 | import base64 11 | import calendar 12 | import hashlib 13 | import threading 14 | 15 | from datetime import datetime 16 | from email.utils import formatdate 17 | 18 | from django.utils.translation import ugettext as _ 19 | from django.contrib.auth.mixins import UserPassesTestMixin 20 | 21 | from cert_manage.tasks import send_mail_async 22 | 23 | 24 | class AdminUserRequiredMixin(UserPassesTestMixin): 25 | def test_func(self): 26 | if not self.request.user.is_authenticated: 27 | return False 28 | elif not self.request.user.is_superuser: 29 | self.raise_exception = True 30 | return False 31 | return True 32 | 33 | 34 | def content_md5(data): 35 | """计算data的MD5值,经过Base64编码并返回str类型。 36 | 37 | 返回值可以直接作为HTTP Content-Type头部的值 38 | """ 39 | if isinstance(data, str): 40 | data = hashlib.md5(data.encode('utf-8')) 41 | value = base64.b64encode(data.hexdigest().encode('utf-8')) 42 | return value.decode('utf-8') 43 | 44 | 45 | _STRPTIME_LOCK = threading.Lock() 46 | 47 | _GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT" 48 | _ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z" 49 | 50 | 51 | def to_unixtime(time_string, format_string): 52 | time_string = time_string.decode("ascii") 53 | with _STRPTIME_LOCK: 54 | return int(calendar.timegm(time.strptime(time_string, format_string))) 55 | 56 | 57 | def http_date(timeval=None): 58 | """返回符合HTTP标准的GMT时间字符串,用strftime的格式表示就是"%a, %d %b %Y %H:%M:%S GMT"。 59 | 但不能使用strftime,因为strftime的结果是和locale相关的。 60 | """ 61 | return formatdate(timeval, usegmt=True) 62 | 63 | 64 | def http_to_unixtime(time_string): 65 | """把HTTP Date格式的字符串转换为UNIX时间(自1970年1月1日UTC零点的秒数)。 66 | 67 | HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。 68 | """ 69 | return to_unixtime(time_string, _GMT_FORMAT) 70 | 71 | 72 | def iso8601_to_unixtime(time_string): 73 | """把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。""" 74 | return to_unixtime(time_string, _ISO8601_FORMAT) 75 | 76 | 77 | def make_signature(access_key_secret, date=None): 78 | if isinstance(date, bytes): 79 | date = bytes.decode(date) 80 | if isinstance(date, int): 81 | date_gmt = http_date(date) 82 | elif date is None: 83 | date_gmt = http_date(int(time.time())) 84 | else: 85 | date_gmt = date 86 | 87 | data = str(access_key_secret) + "\n" + date_gmt 88 | return content_md5(data) 89 | 90 | 91 | def encrypt_password(password, salt=None): 92 | from passlib.hash import sha512_crypt 93 | if password: 94 | return sha512_crypt.using(rounds=5000).hash(password, salt=salt) 95 | return None 96 | 97 | 98 | def get_object_or_none(model, **kwargs): 99 | try: 100 | obj = model.objects.get(**kwargs) 101 | except model.DoesNotExist: 102 | return None 103 | return obj 104 | 105 | 106 | def certs_messages_remaind_email(cert, cert_info): 107 | base_message = '还有Days天到期' 108 | if int(cert_info.get('remain_days')) == 0: 109 | message = '已到期' 110 | elif int(cert_info.get('remain_days')) < 0: 111 | message = '已过期' 112 | elif int(cert_info.get('remain_days')) in [90, 60, 45, 30, 15] or int(cert_info.get('remain_days')) <= 14: 113 | message = base_message.replace('Days', str(cert_info.get('remain_days'))) 114 | else: 115 | message = None 116 | 117 | if message: 118 | if len(cert_info.get('orther_domain')) > 0: 119 | orther_domain = [domain[1] for domain in cert_info.get('orther_domain')] 120 | else: 121 | orther_domain = cert_info.get('orther_domain') 122 | subject = _('重要通知:%(domain)s证书到期提醒(%(time)s)') % {'domain': cert_info.get('domain'), 123 | 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')} 124 | recipient_list = [user.email for user in cert.users.all()] 125 | message = _(""" 126 | 艾瑞巴蒂~
127 |
128 | %(domain)s证书%(message)s,避免影响业务的正常运行请及时更新。
129 | =========================================================
130 | 颁发机构: %(issued_by)s
131 | 证书类型: %(cert_type)s
132 | 域名信息: %(domain)s
133 | 备用域名: %(orther_domain)s
134 | 有效期至: %(notafter)s
135 | =========================================================
136 | """) % { 137 | 'domain': cert_info.get('domain'), 138 | 'message': message, 139 | 'issued_by': cert_info.get('issued_by'), 140 | 'cert_type': cert_info.get('cert_type'), 141 | 'orther_domain': orther_domain, 142 | 'notafter': cert_info.get('notafter'), 143 | } 144 | 145 | send_mail_async.delay(subject, message, recipient_list, html_message=message) 146 | -------------------------------------------------------------------------------- /cert_manage/views.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:03 6 | # @Version : 1.0 7 | # @File : views 8 | # @Software : PyCharm 9 | 10 | from django.views.generic import TemplateView 11 | from django.contrib.auth.mixins import LoginRequiredMixin 12 | 13 | 14 | # 首页 15 | class IndexView(LoginRequiredMixin, TemplateView): 16 | template_name = 'index.html' 17 | 18 | session_week = None 19 | session_month = None 20 | session_month_dates = [] 21 | session_month_dates_archive = [] 22 | 23 | def get(self, request, *args, **kwargs): 24 | return super(IndexView, self).get(request, *args, **kwargs) 25 | 26 | def get_context_data(self, **kwargs): 27 | return super(IndexView, self).get_context_data(**kwargs) 28 | -------------------------------------------------------------------------------- /cert_manage/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for cert_manage project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cert_manage.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /certs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/certs/__init__.py -------------------------------------------------------------------------------- /certs/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /certs/api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:22 6 | # @Version : 1.0 7 | # @File : api 8 | # @Software : PyCharm 9 | 10 | 11 | from users.mixins import IDInFilterMixin 12 | from rest_framework_bulk import BulkModelViewSet 13 | from rest_framework.pagination import LimitOffsetPagination 14 | 15 | from users.permissions import IsSuperUser 16 | from certs import serializers 17 | from certs.models import Certs 18 | 19 | 20 | class CertViewSet(IDInFilterMixin, BulkModelViewSet): 21 | """ 22 | 证书操作接口 23 | """ 24 | filter_fields = ('name', 'domain', 'orther_domain', 'organization_name', 'cert_type', 'comment') 25 | search_fields = filter_fields 26 | ordering_fields = ('domain',) 27 | queryset = Certs.objects.all() 28 | serializer_class = serializers.CertSerializer 29 | pagination_class = LimitOffsetPagination 30 | permission_classes = (IsSuperUser,) 31 | -------------------------------------------------------------------------------- /certs/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CertsConfig(AppConfig): 5 | name = 'certs' 6 | 7 | def ready(self): 8 | from . import signals_handler 9 | super().ready() 10 | -------------------------------------------------------------------------------- /certs/forms.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-5 上午9:01 6 | # @Version : 1.0 7 | # @File : forms 8 | # @Software : PyCharm 9 | 10 | 11 | from django import forms 12 | from django.utils.translation import ugettext_lazy as _ 13 | 14 | from certs.models import Certs 15 | from users.models import User 16 | 17 | 18 | class CertCreateUpdateForm(forms.ModelForm): 19 | is_domain = forms.BooleanField(initial=True, required=False, help_text=_('域名或证书文件'), 20 | label='is_domain') 21 | 22 | class Meta: 23 | model = Certs 24 | fields = [ 25 | 'name', 'method', 'domain_url', 'crt_file', 'key_file', 'users', 'comment' 26 | ] 27 | 28 | widgets = { 29 | 'domain_url': forms.TextInput( 30 | attrs={ 31 | 'placeholder': _('通用名称') 32 | } 33 | ), 34 | 'users': forms.SelectMultiple(attrs={ 35 | 'class': 'select2', 36 | 'data-placeholder': _('选择用户') 37 | }), 38 | } 39 | 40 | help_texts = { 41 | 'name': '* required', 42 | 'domain_url': 'eg: www.itnotebooks.com', 43 | } 44 | 45 | # 重写init方法 46 | def __init__(self, initial, *args, **kwargs): 47 | super(CertCreateUpdateForm, self).__init__(*args, **kwargs) 48 | if initial.get('pk'): 49 | cert = Certs.objects.get(id=initial.get('pk')) 50 | if int(cert.method) == 1: 51 | self.fields['is_domain'].initial = False 52 | 53 | # 重写save方法,处理多个字段的问题 54 | def save(self, commit=True): 55 | certs = super().save(commit=commit) 56 | if not self.cleaned_data.get('is_domain'): 57 | certs.method = 1 58 | certs.save() 59 | return certs 60 | -------------------------------------------------------------------------------- /certs/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1 on 2019-09-18 14:24 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import uuid 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Certs', 19 | fields=[ 20 | ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), 21 | ('name', models.CharField(max_length=45, verbose_name='名称')), 22 | ('domain', models.CharField(max_length=128, null=True, verbose_name='通用名称')), 23 | ('orther_domain', models.TextField(blank=True, max_length=4096, null=True, verbose_name='备用名称')), 24 | ('organization_name', models.CharField(blank=True, max_length=64, null=True, verbose_name='Organization Name')), 25 | ('serial_number', models.CharField(blank=True, max_length=64, null=True, verbose_name='Serial number')), 26 | ('issued_by', models.CharField(blank=True, max_length=64, null=True, verbose_name='Issued By')), 27 | ('cert_type', models.CharField(blank=True, max_length=4, null=True, verbose_name='证书类型')), 28 | ('method', models.BooleanField(choices=[(0, '域名'), (1, '证书')], default=0, verbose_name='Cert Detection Approach')), 29 | ('domain_url', models.CharField(blank=True, max_length=128, null=True, verbose_name='域名')), 30 | ('crt_file', models.TextField(blank=True, null=True, verbose_name='Crt File')), 31 | ('key_file', models.TextField(blank=True, null=True, verbose_name='Key File')), 32 | ('notbefore', models.CharField(blank=True, max_length=24, null=True, verbose_name='Not Before')), 33 | ('notafter', models.CharField(blank=True, max_length=24, null=True, verbose_name='Not After')), 34 | ('remain_days', models.IntegerField(blank=True, null=True, verbose_name='Remaining Days')), 35 | ('create_date', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), 36 | ('comment', models.TextField(blank=True, max_length=128, null=True, verbose_name='Comment')), 37 | ('users', models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Contact')), 38 | ], 39 | options={ 40 | 'db_table': 'certs', 41 | }, 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /certs/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/certs/migrations/__init__.py -------------------------------------------------------------------------------- /certs/models.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | from django.db import models 3 | from django.utils.translation import ugettext_lazy as _ 4 | from users.models import User 5 | 6 | 7 | # Create your models here. 8 | class Certs(models.Model): 9 | ''' 10 | 证书 11 | ''' 12 | CERT_METHOD_CHOICES = ( 13 | (0, _('域名')), 14 | (1, _('证书')), 15 | ) 16 | id = models.UUIDField(default=uuid.uuid4, primary_key=True) 17 | name = models.CharField(max_length=45, verbose_name=_("名称"), null=False) 18 | 19 | # 通用名称 20 | domain = models.CharField(max_length=128, verbose_name=_("通用名称"), null=True) 21 | # 备用名称 22 | orther_domain = models.TextField(max_length=4096, null=True, blank=True, verbose_name=_("备用名称")) 23 | organization_name = models.CharField(max_length=64, null=True, blank=True, verbose_name=_("Organization Name")) 24 | 25 | serial_number = models.CharField(max_length=64, null=True, blank=True, verbose_name=_("Serial number")) 26 | issued_by = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Issued By')) 27 | 28 | # 证书类型 29 | cert_type = models.CharField(max_length=4, null=True, blank=True, verbose_name=_("证书类型")) 30 | 31 | # 在线解析,还是离线解析 32 | method = models.BooleanField(default=0, choices=CERT_METHOD_CHOICES, verbose_name=_("Cert Detection Approach")) 33 | 34 | # 通过域名,在线解析 35 | domain_url = models.CharField(max_length=128, null=True, blank=True, verbose_name=_("域名")) 36 | 37 | # 通过证书文件离线解析 38 | crt_file = models.TextField(null=True, blank=True, verbose_name=_("Crt File")) 39 | key_file = models.TextField(null=True, blank=True, verbose_name=_("Key File")) 40 | 41 | # 起至日期 42 | notbefore = models.CharField(max_length=24, null=True, blank=True, verbose_name=_('Not Before')) 43 | notafter = models.CharField(max_length=24, null=True, blank=True, verbose_name=_('Not After')) 44 | 45 | # 剩余天数 46 | remain_days = models.IntegerField(null=True, blank=True, verbose_name=_('Remaining Days')) 47 | 48 | users = models.ManyToManyField(User, verbose_name=_("Contact")) 49 | # 创建时间 50 | create_date = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')) 51 | # 描述 52 | comment = models.TextField(max_length=128, null=True, blank=True, verbose_name=_('Comment')) 53 | 54 | def __str__(self): 55 | return self.name 56 | 57 | @property 58 | def get_method(self): 59 | return self.get_method_display 60 | 61 | @property 62 | def get_contact(self): 63 | contact_lisst = [user.name for user in self.users.all()] 64 | return ','.join(contact_lisst) 65 | 66 | @property 67 | def get_user_info(self): 68 | user_info = [] 69 | for u in self.users.all(): 70 | info = {'name': u.name, 71 | 'phone': u.phone, 72 | 'email': u.email 73 | } 74 | user_info.append(info) 75 | return user_info 76 | 77 | class Meta: 78 | db_table = "certs" 79 | -------------------------------------------------------------------------------- /certs/serializers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-5 上午9:08 6 | # @Version : 1.0 7 | # @File : serializers 8 | # @Software : PyCharm 9 | 10 | 11 | from rest_framework import serializers 12 | from rest_framework_bulk import BulkListSerializer 13 | 14 | from users.mixins import BulkSerializerMixin 15 | from certs.models import Certs 16 | from users.serializers import UserSerializer 17 | 18 | 19 | class CertSerializer(BulkSerializerMixin, serializers.ModelSerializer): 20 | """ 21 | 证书的数据结构 22 | """ 23 | 24 | class Meta: 25 | model = Certs 26 | list_serializer_class = BulkListSerializer 27 | fields = '__all__' 28 | validators = [] 29 | 30 | def get_field_names(self, declared_fields, info): 31 | fields = super().get_field_names(declared_fields, info) 32 | fields.extend([ 33 | 'get_method' 34 | ]) 35 | return fields 36 | -------------------------------------------------------------------------------- /certs/signals_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-5 下午5:37 6 | # @Version : 1.0 7 | # @File : signals_handler 8 | # @Software : PyCharm 9 | 10 | from django.db.models.signals import post_save 11 | from django.dispatch import receiver 12 | 13 | from certs.tasks import refresh_certs_messages_to_db 14 | 15 | from certs.models import Certs 16 | 17 | 18 | @receiver(post_save, sender=Certs) 19 | def certs_pre_create_or_update(sender, instance, **kwargs): 20 | refresh_certs_messages_to_db.delay(instance.id) 21 | return instance 22 | -------------------------------------------------------------------------------- /certs/tasks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-5 下午5:35 6 | # @Version : 1.0 7 | # @File : tasks 8 | # @Software : PyCharm 9 | from cert_manage.celery import app 10 | 11 | from cert_manage.utils import certs_messages_remaind_email 12 | from certs.models import Certs 13 | from certs.utils import load_certificate 14 | 15 | 16 | @app.task 17 | def refresh_certs_messages_to_db(cert_obj_id=None): 18 | ''' 19 | 此函数用于刷新证书的详细信息 20 | :return: 21 | ''' 22 | certs = Certs.objects.filter(id=cert_obj_id) if cert_obj_id else Certs.objects.all() 23 | 24 | for cert in certs: 25 | 26 | Certs._meta.auto_created = True 27 | try: 28 | if not cert.method: cert.method = 0 29 | if cert.method == 0: 30 | cert_info = load_certificate(cert.method, cert.domain_url) 31 | else: 32 | cert_info = load_certificate(cert.method, cert.crt_file) 33 | 34 | for k, v in cert_info.items(): 35 | setattr(cert, k, v) 36 | if k.startswith('remain_days'): 37 | if int(v) <= 90: 38 | certs_messages_remaind_email(cert, cert_info) 39 | cert.save() 40 | except Exception as e: 41 | print(e) 42 | finally: 43 | Certs._meta.auto_created = False 44 | return 0 45 | -------------------------------------------------------------------------------- /certs/templates/cert/_base_create_update.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load i18n %} 3 | {% load static %} 4 | {% load bootstrap3 %} 5 | {% block custom_head_css_js %} 6 | 7 | 8 | {% block custom_head_css_js_create %} {% endblock %} 9 | {% endblock %} 10 | 11 | {% block nav_status %}active{% endblock %} 12 | 13 | {% block content %} 14 |
15 |
16 |
17 |
18 |
19 |
{{ action }}
20 |
21 |
22 | 23 | {% if form.errors.all %} 24 |
25 | {{ form.errors.all }} 26 |
27 | {% endif %} 28 | {% block form %} 29 | {% endblock %} 30 |
31 |
32 |
33 |
34 |
35 | {% endblock %} 36 | 37 | -------------------------------------------------------------------------------- /certs/templates/cert/cert_create_update.html: -------------------------------------------------------------------------------- 1 | {% extends 'cert/_base_create_update.html' %} 2 | {% load static %} 3 | {% load bootstrap3 %} 4 | {% load i18n %} 5 | 6 | {% block form %} 7 |
8 | {% if form.non_field_errors %} 9 |
10 | {{ form.non_field_errors }} 11 |
12 | {% endif %} 13 | {% csrf_token %} 14 | {% bootstrap_field form.name layout="horizontal" %} 15 | 18 |
19 |
20 | 22 |
23 | {{ form.is_domain }} 24 |
25 | {{ form.is_domain.help_text }} 26 |
27 |
28 |
29 |
30 | 31 |
32 | {% bootstrap_field form.domain_url layout="horizontal" %} 33 |
34 | 35 |
36 | {% bootstrap_field form.crt_file layout="horizontal" %} 37 |
38 |
39 | {% bootstrap_field form.key_file layout="horizontal" %} 40 |
41 | {% bootstrap_field form.users layout="horizontal" %} 42 | {% bootstrap_field form.comment layout="horizontal" %} 43 | 44 |
45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 | {% endblock %} 53 | 54 | {% block custom_foot_js %} 55 | 84 | {% endblock %} -------------------------------------------------------------------------------- /certs/templates/cert/cert_detail.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% load static %} 3 | {% load i18n %} 4 | 5 | {% block nav_status %}active{% endblock %} 6 | {% block content %} 7 |
8 |
9 |
10 |
11 |
12 | 19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 75 | 76 | 77 | 78 | 80 | 81 | 82 | 83 | 85 | 86 | 87 | 88 | 91 | 92 | 93 | 94 |
{% trans 'Name' %}:{{ cert.name }}
{% trans 'Common Domain' %}:{{ cert.domain }}
{% trans 'Orther Domain' %}: 37 | {{ cert.orther_domain }}
{% trans 'Organization Name' %}: 44 | {{ cert.organization_name }}
{% trans 'Serial number' %}: 49 | {{ cert.serial_number }}
{% trans 'Issued By' %}: 54 | {{ cert.issued_by }}
{% trans 'SSL Cert Type' %}: 59 | {{ cert.cert_type }}
{% trans 'Cert Detection Approach' %}: 64 | {{ cert.get_method }}
{% trans 'Not Before' %}: 69 | {{ cert.notbefore }}
{% trans 'Not After' %}: 74 | {{ cert.notafter }}
{% trans 'Remaining Days' %}: 79 | {{ cert.remain_days }}
{% trans 'Contact' %}: 84 | {{ cert.get_contact }}
{% trans 'Comment' %}: 89 | {{ asset.comment|safe }} 90 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | 104 | {% endblock %} 105 | 106 | {% block custom_foot_js %} 107 | 112 | {% endblock %} 113 | -------------------------------------------------------------------------------- /certs/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /certs/urls/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:19 6 | # @Version : 1.0 7 | # @File : __init__.py 8 | # @Software : PyCharm -------------------------------------------------------------------------------- /certs/urls/api_urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:19 6 | # @Version : 1.0 7 | # @File : api_urls 8 | # @Software : PyCharm 9 | 10 | from certs import api 11 | 12 | from rest_framework_bulk.routes import BulkRouter 13 | 14 | app_name = 'certs' 15 | 16 | router = BulkRouter() 17 | 18 | # 证书 19 | router.register(r'v1/cert', api.CertViewSet, 'cert') 20 | 21 | urlpatterns = [] 22 | urlpatterns += router.urls 23 | -------------------------------------------------------------------------------- /certs/urls/views_urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:19 6 | # @Version : 1.0 7 | # @File : views_urls 8 | # @Software : PyCharm 9 | 10 | 11 | from django.conf.urls import url 12 | from certs import views 13 | 14 | app_name = 'certs' 15 | 16 | urlpatterns = [ 17 | # 证书 18 | url(r'^$', views.CertListView.as_view(), name='cert-index'), 19 | url(r'^create/$', views.CertCreateView.as_view(), name='cert-create'), 20 | url(r'^(?P[0-9a-zA-Z\-]{36})/update/$', views.CertUpdateView.as_view(), 21 | name='cert-update'), 22 | url(r'^(?P[0-9a-zA-Z\-]{36})/$', views.CertDetailView.as_view(), name='cert-detail'), 23 | ] 24 | -------------------------------------------------------------------------------- /certs/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-5 下午5:24 6 | # @Version : 1.0 7 | # @File : utils 8 | # @Software : PyCharm 9 | 10 | 11 | from datetime import datetime 12 | from urllib3.contrib import pyopenssl as reqs 13 | 14 | 15 | # 从域名或pem文件解析SSL证书,获取签发信息 16 | def load_certificate(method, obj): 17 | if method == 0: 18 | cert = reqs.ssl.get_server_certificate((obj, 443)) 19 | elif method == 1: 20 | cert = obj 21 | else: 22 | return {} 23 | 24 | try: 25 | x509 = reqs.OpenSSL.crypto.load_certificate(reqs.OpenSSL.crypto.FILETYPE_PEM, cert) 26 | notbefore = datetime.strptime(x509.get_notBefore().decode()[0:-1], '%Y%m%d%H%M%S') 27 | notafter = datetime.strptime(x509.get_notAfter().decode()[0:-1], '%Y%m%d%H%M%S') 28 | remain_days = notafter - datetime.now() 29 | organization_name = x509.get_subject().organizationName 30 | serial_number = x509.get_subject().serialNumber 31 | 32 | if serial_number: 33 | cert_type = 'EV' 34 | elif not organization_name: 35 | cert_type = 'DV' 36 | else: 37 | cert_type = 'OV' 38 | 39 | ret_json = { 40 | 'domain': x509.get_subject().CN, 41 | 'orther_domain': reqs.get_subj_alt_name(x509), 42 | 'organization_name': organization_name, 43 | 'serial_number': serial_number, 44 | 'issued_by': x509.get_issuer().CN, 45 | 'cert_type': cert_type, 46 | 'notbefore': notbefore.strftime('%Y-%m-%d %H:%M:%S'), 47 | 'notafter': notafter.strftime('%Y-%m-%d %H:%M:%S'), 48 | 'remain_days': remain_days.days, 49 | } 50 | except Exception as e: 51 | raise Exception(e) 52 | return ret_json 53 | -------------------------------------------------------------------------------- /certs/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.messages.views import SuccessMessageMixin 2 | from django.shortcuts import render 3 | from django.urls import reverse_lazy 4 | from django.views.generic import TemplateView, CreateView, UpdateView, DetailView 5 | 6 | from django.utils.translation import ugettext as _ 7 | 8 | from users.utils import create_success_msg 9 | from cert_manage.utils import AdminUserRequiredMixin 10 | 11 | from certs import forms 12 | from certs.models import Certs 13 | 14 | 15 | # Create your views here. 16 | 17 | 18 | class CertListView(AdminUserRequiredMixin, TemplateView): 19 | """ 20 | 列出所有证书 21 | """ 22 | template_name = 'cert/cert_list.html' 23 | 24 | def get_context_data(self, **kwargs): 25 | context = { 26 | 'app': _('Cert'), 27 | 'action': _('List') 28 | } 29 | kwargs.update(context) 30 | return super().get_context_data(**kwargs) 31 | 32 | 33 | class CertCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): 34 | ''' 35 | 创建证书 36 | ''' 37 | model = Certs 38 | form_class = forms.CertCreateUpdateForm 39 | template_name = 'cert/cert_create_update.html' 40 | 41 | def get_form(self, form_class=None): 42 | form = super().get_form(form_class=form_class) 43 | return form 44 | 45 | # 重写get_form_kwargs,在initial中传入参数pk 46 | def get_form_kwargs(self): 47 | kwargs = super(CertCreateView, self).get_form_kwargs() 48 | if hasattr(self, 'object'): 49 | kwargs.update({'instance': self.object}) 50 | kwargs['initial']['pk'] = False 51 | return kwargs 52 | 53 | def get_context_data(self, **kwargs): 54 | context = { 55 | 'app': _('Cert'), 56 | 'action': _('Create'), 57 | } 58 | kwargs.update(context) 59 | return super().get_context_data(**kwargs) 60 | 61 | def get_success_message(self, cleaned_data): 62 | return create_success_msg % ({"name": cleaned_data["name"]}) 63 | 64 | def get_success_url(self): 65 | return reverse_lazy('certs:cert-index') 66 | 67 | 68 | class CertUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): 69 | ''' 70 | 更新证书 71 | ''' 72 | model = Certs 73 | form_class = forms.CertCreateUpdateForm 74 | template_name = 'cert/cert_create_update.html' 75 | 76 | def get_form(self, form_class=None): 77 | form = super().get_form(form_class=form_class) 78 | return form 79 | 80 | # 重写get_form_kwargs,在initial中传入参数pk 81 | def get_form_kwargs(self): 82 | kwargs = super(CertUpdateView, self).get_form_kwargs() 83 | if hasattr(self, 'object'): 84 | kwargs.update({'instance': self.object}) 85 | kwargs['initial']['pk'] = self.kwargs.get('pk') 86 | return kwargs 87 | 88 | def get_context_data(self, **kwargs): 89 | context = { 90 | 'app': _('Cert'), 91 | 'action': _('Update'), 92 | } 93 | kwargs.update(context) 94 | return super().get_context_data(**kwargs) 95 | 96 | def get_success_message(self, cleaned_data): 97 | return create_success_msg % ({"name": cleaned_data["name"]}) 98 | 99 | def get_success_url(self): 100 | return reverse_lazy('certs:cert-index') 101 | 102 | 103 | class CertDetailView(AdminUserRequiredMixin, DetailView): 104 | ''' 105 | 证书详细信息 106 | ''' 107 | model = Certs 108 | context_object_name = 'cert' 109 | template_name = 'cert/cert_detail.html' 110 | 111 | def get_context_data(self, **kwargs): 112 | context = { 113 | 'app': _('Cert'), 114 | 'action': _('Detail'), 115 | } 116 | kwargs.update(context) 117 | return super().get_context_data(**kwargs) 118 | -------------------------------------------------------------------------------- /config_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-2 下午9:13 6 | # @Version : 1.0 7 | # @File : config 8 | # @Software : PyCharm 9 | 10 | import os 11 | 12 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 13 | 14 | 15 | class Config: 16 | SECRET_KEY = os.environ.get('SECRET_KEY') or 'l(=lclv!iupi^%v5ybysmkcv8&a@b9^4lj#=!379r#yh=o^tk_' 17 | 18 | # Django security setting. if your disable debug model, you should setting that 19 | ALLOWED_HOSTS = ['*'] 20 | 21 | # Development env open this, when error occur display the full process track, Production disable it 22 | DEBUG = True 23 | 24 | # DEBUG, INFO, WARNING, ERROR, CRITICAL can set. See https://docs.djangoproject.com/en/1.10/topics/logging/ 25 | LOG_LEVEL = 'DEBUG' 26 | LOG_DIR = os.path.join(BASE_DIR, 'logs') 27 | 28 | # Database setting, Support sqlite3, mysql, postgres .... 29 | # See https://docs.djangoproject.com/en/1.10/ref/settings/#databases 30 | 31 | # SQLite setting: 32 | DB_ENGINE = 'sqlite3' 33 | DB_NAME = os.path.join(BASE_DIR, 'data', 'db.sqlite3') 34 | 35 | # MySQL or postgres setting like: 36 | # DB_ENGINE = 'mysql' 37 | # DB_HOST = '127.0.0.1' 38 | # DB_PORT = 3306 39 | # DB_USER = 'root' 40 | # DB_PASSWORD = '' 41 | # DB_NAME = 'cert_manage' 42 | 43 | # When Django start it will bind this host and port 44 | # ./manage.py runserver 127.0.0.1:8080 45 | HTTP_BIND_HOST = '127.0.0.1' 46 | HTTP_LISTEN_PORT = 8080 47 | 48 | # Use Redis as broker for celery and web socket 49 | REDIS_HOST = '127.0.0.1' 50 | REDIS_PORT = 6379 51 | REDIS_PASSWORD = '' 52 | BROKER_URL = 'redis://%(password)s@%(host)s:%(port)s/3' % { 53 | 'password': REDIS_PASSWORD, 54 | 'host': REDIS_HOST, 55 | 'port': REDIS_PORT, 56 | } 57 | 58 | def __init__(self): 59 | pass 60 | 61 | def __getattr__(self, item): 62 | return None 63 | 64 | 65 | class DevelopmentConfig(Config): 66 | pass 67 | 68 | 69 | class TestConfig(Config): 70 | pass 71 | 72 | 73 | class ProductionConfig(Config): 74 | pass 75 | 76 | 77 | # Default using Config settings, you can write if/else for different env 78 | config = DevelopmentConfig() 79 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/data/.gitkeep -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/logs/.gitkeep -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cert_manage.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /requirements/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/requirements/.gitkeep -------------------------------------------------------------------------------- /requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | amqp==2.5.1 2 | anyjson==0.3.3 3 | asn1crypto==0.24.0 4 | billiard==3.6.1.0 5 | celery==4.3.0 6 | cffi==1.12.3 7 | cryptography==2.7 8 | Django==2.1.11 9 | django-bootstrap3==11.1.0 10 | django-celery-beat==1.5.0 11 | django-filter==2.2.0 12 | django-ranged-response==0.2.0 13 | django-redis-cache==2.0.0 14 | django-simple-captcha==0.5.12 15 | django-timezone-field==3.0 16 | djangorestframework==3.10.2 17 | djangorestframework-bulk==0.2.1 18 | gunicorn==19.9.0 19 | idna==2.8 20 | importlib-metadata==0.20 21 | kombu==4.6.4 22 | more-itertools==7.2.0 23 | passlib==1.7.1 24 | Pillow==6.1.0 25 | pycparser==2.19 26 | pyOpenSSL==19.0.0 27 | python-crontab==2.3.8 28 | python-dateutil==2.8.0 29 | pytz==2019.2 30 | redis==3.3.8 31 | six==1.12.0 32 | sqlparse==0.3.0 33 | urllib3==1.25.3 34 | vine==1.3.0 35 | zipp==0.6.0 36 | -------------------------------------------------------------------------------- /run_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 下午9:18 6 | # @Version : 1.0 7 | # @File : run_server 8 | # @Software : PyCharm 9 | 10 | import sys 11 | import subprocess 12 | 13 | if __name__ == '__main__': 14 | subprocess.call('python3 cms start all', shell=True, 15 | stdin=sys.stdin, stdout=sys.stdout) 16 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/css/fa-brands.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face { 6 | font-family: 'Font Awesome 5 Brands'; 7 | font-style: normal; 8 | font-weight: normal; 9 | src: url("../webfonts/fa-brands-400.eot"); 10 | src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); } 11 | 12 | .fab { 13 | font-family: 'Font Awesome 5 Brands'; } 14 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/css/fa-brands.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:Font Awesome\ 5 Brands;font-style:normal;font-weight:400;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:Font Awesome\ 5 Brands} -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/css/fa-regular.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face { 6 | font-family: 'Font Awesome 5 Free'; 7 | font-style: normal; 8 | font-weight: 400; 9 | src: url("../webfonts/fa-regular-400.eot"); 10 | src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); } 11 | 12 | .far { 13 | font-family: 'Font Awesome 5 Free'; 14 | font-weight: 400; } 15 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/css/fa-regular.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:400;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:Font Awesome\ 5 Free;font-weight:400} -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/css/fa-solid.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face { 6 | font-family: 'Font Awesome 5 Free'; 7 | font-style: normal; 8 | font-weight: 900; 9 | src: url("../webfonts/fa-solid-900.eot"); 10 | src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); } 11 | 12 | .fa, 13 | .fas { 14 | font-family: 'Font Awesome 5 Free'; 15 | font-weight: 900; } 16 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/css/fa-solid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:900;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:Font Awesome\ 5 Free;font-weight:900} -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | animation: fa-spin 2s infinite linear; 6 | } 7 | 8 | .@{fa-css-prefix}-pulse { 9 | animation: fa-spin 1s infinite steps(8); 10 | } 11 | 12 | @keyframes fa-spin { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 100% { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | border-radius: .1em; 6 | border: solid .08em @fa-border-color; 7 | padding: .2em .25em .15em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix}, .fas, .far, .fal, .fab { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}, .fas, .far, .fal, .fab { 5 | -moz-osx-font-smoothing: grayscale; 6 | -webkit-font-smoothing: antialiased; 7 | display: inline-block; 8 | font-style: normal; 9 | font-variant: normal; 10 | text-rendering: auto; 11 | line-height: 1; 12 | } 13 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | text-align: center; 5 | width: (20em / 16); 6 | } 7 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | .larger(@factor) when (@factor > 0) { 5 | .larger((@factor - 1)); 6 | 7 | .@{fa-css-prefix}-@{factor}x { 8 | font-size: (@factor * 1em); 9 | } 10 | } 11 | 12 | /* makes the font 33% larger relative to the icon container */ 13 | .@{fa-css-prefix}-lg { 14 | font-size: (4em / 3); 15 | line-height: (3em / 4); 16 | vertical-align: -.0667em; 17 | } 18 | 19 | .@{fa-css-prefix}-xs { 20 | font-size: .75em; 21 | } 22 | 23 | .@{fa-css-prefix}-sm { 24 | font-size: .875em; 25 | } 26 | 27 | .larger(10); 28 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | list-style-type: none; 6 | margin-left: @fa-li-width * 5/4; 7 | padding-left: 0; 8 | 9 | > li { position: relative; } 10 | } 11 | 12 | .@{fa-css-prefix}-li { 13 | left: -@fa-li-width; 14 | position: absolute; 15 | text-align: center; 16 | width: @fa-li-width; 17 | line-height: inherit; 18 | } 19 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | -moz-osx-font-smoothing: grayscale; 6 | -webkit-font-smoothing: antialiased; 7 | display: inline-block; 8 | font-style: normal; 9 | font-variant: normal; 10 | font-weight: normal; 11 | line-height: 1; 12 | vertical-align: -.125em; 13 | } 14 | 15 | .fa-icon-rotate(@degrees, @rotation) { 16 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 17 | transform: rotate(@degrees); 18 | } 19 | 20 | .fa-icon-flip(@horiz, @vert, @rotation) { 21 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 22 | transform: scale(@horiz, @vert); 23 | } 24 | 25 | 26 | // Only display content to screen readers. A la Bootstrap 4. 27 | // 28 | // See: http://a11yproject.com/posts/how-to-hide-content/ 29 | 30 | .sr-only() { 31 | border: 0; 32 | clip: rect(0,0,0,0); 33 | height: 1px; 34 | margin: -1px; 35 | overflow: hidden; 36 | padding: 0; 37 | position: absolute; 38 | width: 1px; 39 | } 40 | 41 | // Use in conjunction with .sr-only to only display content when it's focused. 42 | // 43 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 44 | // 45 | // Credit: HTML5 Boilerplate 46 | 47 | .sr-only-focusable() { 48 | &:active, 49 | &:focus { 50 | clip: auto; 51 | height: auto; 52 | margin: 0; 53 | overflow: visible; 54 | position: static; 55 | width: auto; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | .@{fa-css-prefix}-flip-horizontal.@{fa-css-prefix}-flip-vertical { .fa-icon-flip(-1, -1, 2); } 11 | 12 | // Hook for IE8-9 13 | // ------------------------- 14 | 15 | :root { 16 | .@{fa-css-prefix}-rotate-90, 17 | .@{fa-css-prefix}-rotate-180, 18 | .@{fa-css-prefix}-rotate-270, 19 | .@{fa-css-prefix}-flip-horizontal, 20 | .@{fa-css-prefix}-flip-vertical { 21 | filter: none; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/_stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | display: inline-block; 6 | height: 2em; 7 | line-height: 2em; 8 | position: relative; 9 | vertical-align: middle; 10 | width: 2em; 11 | } 12 | 13 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 14 | left: 0; 15 | position: absolute; 16 | text-align: center; 17 | width: 100%; 18 | } 19 | 20 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 21 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 22 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 23 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/fa-brands.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import "_variables.less"; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Brands'; 9 | font-style: normal; 10 | font-weight: normal; 11 | src: url('@{fa-font-path}/fa-brands-400.eot'); 12 | src: url('@{fa-font-path}/fa-brands-400.eot?#iefix') format('embedded-opentype'), 13 | url('@{fa-font-path}/fa-brands-400.woff2') format('woff2'), 14 | url('@{fa-font-path}/fa-brands-400.woff') format('woff'), 15 | url('@{fa-font-path}/fa-brands-400.ttf') format('truetype'), 16 | url('@{fa-font-path}/fa-brands-400.svg#fontawesome') format('svg'); 17 | } 18 | 19 | .fab { 20 | font-family: 'Font Awesome 5 Brands'; 21 | } 22 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/fa-regular.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import "_variables.less"; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Free'; 9 | font-style: normal; 10 | font-weight: 400; 11 | src: url('@{fa-font-path}/fa-regular-400.eot'); 12 | src: url('@{fa-font-path}/fa-regular-400.eot?#iefix') format('embedded-opentype'), 13 | url('@{fa-font-path}/fa-regular-400.woff2') format('woff2'), 14 | url('@{fa-font-path}/fa-regular-400.woff') format('woff'), 15 | url('@{fa-font-path}/fa-regular-400.ttf') format('truetype'), 16 | url('@{fa-font-path}/fa-regular-400.svg#fontawesome') format('svg'); 17 | } 18 | 19 | .far { 20 | font-family: 'Font Awesome 5 Free'; 21 | font-weight: 400; 22 | } 23 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/fa-solid.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import "_variables.less"; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Free'; 9 | font-style: normal; 10 | font-weight: 900; 11 | src: url('@{fa-font-path}/fa-solid-900.eot'); 12 | src: url('@{fa-font-path}/fa-solid-900.eot?#iefix') format('embedded-opentype'), 13 | url('@{fa-font-path}/fa-solid-900.woff2') format('woff2'), 14 | url('@{fa-font-path}/fa-solid-900.woff') format('woff'), 15 | url('@{fa-font-path}/fa-solid-900.ttf') format('truetype'), 16 | url('@{fa-font-path}/fa-solid-900.svg#fontawesome') format('svg'); 17 | } 18 | 19 | .fa, 20 | .fas { 21 | font-family: 'Font Awesome 5 Free'; 22 | font-weight: 900; 23 | } 24 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/less/fontawesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import "_variables.less"; 6 | @import "_mixins.less"; 7 | @import "_core.less"; 8 | @import "_larger.less"; 9 | @import "_fixed-width.less"; 10 | @import "_list.less"; 11 | @import "_bordered-pulled.less"; 12 | @import "_animated.less"; 13 | @import "_rotated-flipped.less"; 14 | @import "_stacked.less"; 15 | @import "_icons.less"; 16 | @import "_screen-reader.less"; 17 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | animation: fa-spin 2s infinite linear; 6 | } 7 | 8 | .#{$fa-css-prefix}-pulse { 9 | animation: fa-spin 1s infinite steps(8); 10 | } 11 | 12 | @keyframes fa-spin { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 17 | 100% { 18 | transform: rotate(360deg); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | border: solid .08em $fa-border-color; 6 | border-radius: .1em; 7 | padding: .2em .25em .15em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix}, 14 | .fas, 15 | .far, 16 | .fal, 17 | .fab { 18 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 19 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 20 | } 21 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}, 5 | .fas, 6 | .far, 7 | .fal, 8 | .fab { 9 | -moz-osx-font-smoothing: grayscale; 10 | -webkit-font-smoothing: antialiased; 11 | display: inline-block; 12 | font-style: normal; 13 | font-variant: normal; 14 | text-rendering: auto; 15 | line-height: 1; 16 | } 17 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | text-align: center; 5 | width: (20em / 16); 6 | } 7 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | // makes the font 33% larger relative to the icon container 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -.0667em; 9 | } 10 | 11 | .#{$fa-css-prefix}-xs { 12 | font-size: .75em; 13 | } 14 | 15 | .#{$fa-css-prefix}-sm { 16 | font-size: .875em; 17 | } 18 | 19 | @for $i from 1 through 10 { 20 | .#{$fa-css-prefix}-#{$i}x { 21 | font-size: $i * 1em; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | list-style-type: none; 6 | margin-left: $fa-li-width * 5/4; 7 | padding-left: 0; 8 | 9 | > li { position: relative; } 10 | } 11 | 12 | .#{$fa-css-prefix}-li { 13 | left: -$fa-li-width; 14 | position: absolute; 15 | text-align: center; 16 | width: $fa-li-width; 17 | line-height: inherit; 18 | } 19 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon { 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | display: inline-block; 8 | font-style: normal; 9 | font-variant: normal; 10 | font-weight: normal; 11 | line-height: 1; 12 | vertical-align: -.125em; 13 | } 14 | 15 | @mixin fa-icon-rotate($degrees, $rotation) { 16 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 17 | transform: rotate($degrees); 18 | } 19 | 20 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 21 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 22 | transform: scale($horiz, $vert); 23 | } 24 | 25 | 26 | // Only display content to screen readers. A la Bootstrap 4. 27 | // 28 | // See: http://a11yproject.com/posts/how-to-hide-content/ 29 | 30 | @mixin sr-only { 31 | border: 0; 32 | clip: rect(0, 0, 0, 0); 33 | height: 1px; 34 | margin: -1px; 35 | overflow: hidden; 36 | padding: 0; 37 | position: absolute; 38 | width: 1px; 39 | } 40 | 41 | // Use in conjunction with .sr-only to only display content when it's focused. 42 | // 43 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 44 | // 45 | // Credit: HTML5 Boilerplate 46 | 47 | @mixin sr-only-focusable { 48 | &:active, 49 | &:focus { 50 | clip: auto; 51 | height: auto; 52 | margin: 0; 53 | overflow: visible; 54 | position: static; 55 | width: auto; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | .#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(-1, -1, 2); } 11 | 12 | // Hook for IE8-9 13 | // ------------------------- 14 | 15 | :root { 16 | .#{$fa-css-prefix}-rotate-90, 17 | .#{$fa-css-prefix}-rotate-180, 18 | .#{$fa-css-prefix}-rotate-270, 19 | .#{$fa-css-prefix}-flip-horizontal, 20 | .#{$fa-css-prefix}-flip-vertical { 21 | filter: none; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only; } 5 | .sr-only-focusable { @include sr-only-focusable; } 6 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | display: inline-block; 6 | height: 2em; 7 | line-height: 2em; 8 | position: relative; 9 | vertical-align: middle; 10 | width: 2em; 11 | } 12 | 13 | .#{$fa-css-prefix}-stack-1x, 14 | .#{$fa-css-prefix}-stack-2x { 15 | left: 0; 16 | position: absolute; 17 | text-align: center; 18 | width: 100%; 19 | } 20 | 21 | .#{$fa-css-prefix}-stack-1x { 22 | line-height: inherit; 23 | } 24 | 25 | .#{$fa-css-prefix}-stack-2x { 26 | font-size: 2em; 27 | } 28 | 29 | .#{$fa-css-prefix}-inverse { 30 | color: $fa-inverse; 31 | } 32 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/fa-brands.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Brands'; 9 | font-style: normal; 10 | font-weight: normal; 11 | src: url('#{$fa-font-path}/fa-brands-400.eot'); 12 | src: url('#{$fa-font-path}/fa-brands-400.eot?#iefix') format('embedded-opentype'), 13 | url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), 14 | url('#{$fa-font-path}/fa-brands-400.woff') format('woff'), 15 | url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'), 16 | url('#{$fa-font-path}/fa-brands-400.svg#fontawesome') format('svg'); 17 | } 18 | 19 | .fab { 20 | font-family: 'Font Awesome 5 Brands'; 21 | } 22 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/fa-regular.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Free'; 9 | font-style: normal; 10 | font-weight: 400; 11 | src: url('#{$fa-font-path}/fa-regular-400.eot'); 12 | src: url('#{$fa-font-path}/fa-regular-400.eot?#iefix') format('embedded-opentype'), 13 | url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), 14 | url('#{$fa-font-path}/fa-regular-400.woff') format('woff'), 15 | url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'), 16 | url('#{$fa-font-path}/fa-regular-400.svg#fontawesome') format('svg'); 17 | } 18 | 19 | .far { 20 | font-family: 'Font Awesome 5 Free'; 21 | font-weight: 400; 22 | } 23 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/fa-solid.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Free'; 9 | font-style: normal; 10 | font-weight: 900; 11 | src: url('#{$fa-font-path}/fa-solid-900.eot'); 12 | src: url('#{$fa-font-path}/fa-solid-900.eot?#iefix') format('embedded-opentype'), 13 | url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), 14 | url('#{$fa-font-path}/fa-solid-900.woff') format('woff'), 15 | url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'), 16 | url('#{$fa-font-path}/fa-solid-900.svg#fontawesome') format('svg'); 17 | } 18 | 19 | .fa, 20 | .fas { 21 | font-family: 'Font Awesome 5 Free'; 22 | font-weight: 900; 23 | } 24 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/scss/fontawesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.0.13 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | @import 'mixins'; 7 | @import 'core'; 8 | @import 'larger'; 9 | @import 'fixed-width'; 10 | @import 'list'; 11 | @import 'bordered-pulled'; 12 | @import 'animated'; 13 | @import 'rotated-flipped'; 14 | @import 'stacked'; 15 | @import 'icons'; 16 | @import 'screen-reader'; 17 | -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /static/css/plugin/font-awesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/css/plugin/font-awesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-black-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Black 3 | * ----------- 4 | */ 5 | /* skin-black navbar */ 6 | .skin-black-light .main-header { 7 | border-bottom: 1px solid #d2d6de; 8 | } 9 | .skin-black-light .main-header .navbar-toggle { 10 | color: #333; 11 | } 12 | .skin-black-light .main-header .navbar-brand { 13 | color: #333; 14 | border-right: 1px solid #d2d6de; 15 | } 16 | .skin-black-light .main-header .navbar { 17 | background-color: #ffffff; 18 | } 19 | .skin-black-light .main-header .navbar .nav > li > a { 20 | color: #333333; 21 | } 22 | .skin-black-light .main-header .navbar .nav > li > a:hover, 23 | .skin-black-light .main-header .navbar .nav > li > a:active, 24 | .skin-black-light .main-header .navbar .nav > li > a:focus, 25 | .skin-black-light .main-header .navbar .nav .open > a, 26 | .skin-black-light .main-header .navbar .nav .open > a:hover, 27 | .skin-black-light .main-header .navbar .nav .open > a:focus, 28 | .skin-black-light .main-header .navbar .nav > .active > a { 29 | background: #ffffff; 30 | color: #999999; 31 | } 32 | .skin-black-light .main-header .navbar .sidebar-toggle { 33 | color: #333333; 34 | } 35 | .skin-black-light .main-header .navbar .sidebar-toggle:hover { 36 | color: #999999; 37 | background: #ffffff; 38 | } 39 | .skin-black-light .main-header .navbar > .sidebar-toggle { 40 | color: #333; 41 | border-right: 1px solid #d2d6de; 42 | } 43 | .skin-black-light .main-header .navbar .navbar-nav > li > a { 44 | border-right: 1px solid #d2d6de; 45 | } 46 | .skin-black-light .main-header .navbar .navbar-custom-menu .navbar-nav > li > a, 47 | .skin-black-light .main-header .navbar .navbar-right > li > a { 48 | border-left: 1px solid #d2d6de; 49 | border-right-width: 0; 50 | } 51 | .skin-black-light .main-header > .logo { 52 | background-color: #ffffff; 53 | color: #333333; 54 | border-bottom: 0 solid transparent; 55 | border-right: 1px solid #d2d6de; 56 | } 57 | .skin-black-light .main-header > .logo:hover { 58 | background-color: #fcfcfc; 59 | } 60 | @media (max-width: 767px) { 61 | .skin-black-light .main-header > .logo { 62 | background-color: #222222; 63 | color: #ffffff; 64 | border-bottom: 0 solid transparent; 65 | border-right: none; 66 | } 67 | .skin-black-light .main-header > .logo:hover { 68 | background-color: #1f1f1f; 69 | } 70 | } 71 | .skin-black-light .main-header li.user-header { 72 | background-color: #222; 73 | } 74 | .skin-black-light .content-header { 75 | background: transparent; 76 | box-shadow: none; 77 | } 78 | .skin-black-light .wrapper, 79 | .skin-black-light .main-sidebar, 80 | .skin-black-light .left-side { 81 | background-color: #f9fafc; 82 | } 83 | .skin-black-light .main-sidebar { 84 | border-right: 1px solid #d2d6de; 85 | } 86 | .skin-black-light .user-panel > .info, 87 | .skin-black-light .user-panel > .info > a { 88 | color: #444444; 89 | } 90 | .skin-black-light .sidebar-menu > li { 91 | -webkit-transition: border-left-color 0.3s ease; 92 | -o-transition: border-left-color 0.3s ease; 93 | transition: border-left-color 0.3s ease; 94 | } 95 | .skin-black-light .sidebar-menu > li.header { 96 | color: #848484; 97 | background: #f9fafc; 98 | } 99 | .skin-black-light .sidebar-menu > li > a { 100 | border-left: 3px solid transparent; 101 | font-weight: 600; 102 | } 103 | .skin-black-light .sidebar-menu > li:hover > a, 104 | .skin-black-light .sidebar-menu > li.active > a { 105 | color: #000000; 106 | background: #f4f4f5; 107 | } 108 | .skin-black-light .sidebar-menu > li.active { 109 | border-left-color: #ffffff; 110 | } 111 | .skin-black-light .sidebar-menu > li.active > a { 112 | font-weight: 600; 113 | } 114 | .skin-black-light .sidebar-menu > li > .treeview-menu { 115 | background: #f4f4f5; 116 | } 117 | .skin-black-light .sidebar a { 118 | color: #444444; 119 | } 120 | .skin-black-light .sidebar a:hover { 121 | text-decoration: none; 122 | } 123 | .skin-black-light .sidebar-menu .treeview-menu > li > a { 124 | color: #777777; 125 | } 126 | .skin-black-light .sidebar-menu .treeview-menu > li.active > a, 127 | .skin-black-light .sidebar-menu .treeview-menu > li > a:hover { 128 | color: #000000; 129 | } 130 | .skin-black-light .sidebar-menu .treeview-menu > li.active > a { 131 | font-weight: 600; 132 | } 133 | .skin-black-light .sidebar-form { 134 | border-radius: 3px; 135 | border: 1px solid #d2d6de; 136 | margin: 10px 10px; 137 | } 138 | .skin-black-light .sidebar-form input[type="text"], 139 | .skin-black-light .sidebar-form .btn { 140 | box-shadow: none; 141 | background-color: #fff; 142 | border: 1px solid transparent; 143 | height: 35px; 144 | } 145 | .skin-black-light .sidebar-form input[type="text"] { 146 | color: #666; 147 | border-top-left-radius: 2px; 148 | border-top-right-radius: 0; 149 | border-bottom-right-radius: 0; 150 | border-bottom-left-radius: 2px; 151 | } 152 | .skin-black-light .sidebar-form input[type="text"]:focus, 153 | .skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 154 | background-color: #fff; 155 | color: #666; 156 | } 157 | .skin-black-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 158 | border-left-color: #fff; 159 | } 160 | .skin-black-light .sidebar-form .btn { 161 | color: #999; 162 | border-top-left-radius: 0; 163 | border-top-right-radius: 2px; 164 | border-bottom-right-radius: 2px; 165 | border-bottom-left-radius: 0; 166 | } 167 | @media (min-width: 768px) { 168 | .skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { 169 | border-left: 1px solid #d2d6de; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-black-light.min.css: -------------------------------------------------------------------------------- 1 | .skin-black-light .main-header{border-bottom:1px solid #d2d6de}.skin-black-light .main-header .navbar-toggle{color:#333}.skin-black-light .main-header .navbar-brand{color:#333;border-right:1px solid #d2d6de}.skin-black-light .main-header .navbar{background-color:#fff}.skin-black-light .main-header .navbar .nav>li>a{color:#333}.skin-black-light .main-header .navbar .nav>li>a:hover,.skin-black-light .main-header .navbar .nav>li>a:active,.skin-black-light .main-header .navbar .nav>li>a:focus,.skin-black-light .main-header .navbar .nav .open>a,.skin-black-light .main-header .navbar .nav .open>a:hover,.skin-black-light .main-header .navbar .nav .open>a:focus,.skin-black-light .main-header .navbar .nav>.active>a{background:#fff;color:#999}.skin-black-light .main-header .navbar .sidebar-toggle{color:#333}.skin-black-light .main-header .navbar .sidebar-toggle:hover{color:#999;background:#fff}.skin-black-light .main-header .navbar>.sidebar-toggle{color:#333;border-right:1px solid #d2d6de}.skin-black-light .main-header .navbar .navbar-nav>li>a{border-right:1px solid #d2d6de}.skin-black-light .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-black-light .main-header .navbar .navbar-right>li>a{border-left:1px solid #d2d6de;border-right-width:0}.skin-black-light .main-header>.logo{background-color:#fff;color:#333;border-bottom:0 solid transparent;border-right:1px solid #d2d6de}.skin-black-light .main-header>.logo:hover{background-color:#fcfcfc}@media (max-width:767px){.skin-black-light .main-header>.logo{background-color:#222;color:#fff;border-bottom:0 solid transparent;border-right:none}.skin-black-light .main-header>.logo:hover{background-color:#1f1f1f}}.skin-black-light .main-header li.user-header{background-color:#222}.skin-black-light .content-header{background:transparent;box-shadow:none}.skin-black-light .wrapper,.skin-black-light .main-sidebar,.skin-black-light .left-side{background-color:#f9fafc}.skin-black-light .main-sidebar{border-right:1px solid #d2d6de}.skin-black-light .user-panel>.info,.skin-black-light .user-panel>.info>a{color:#444}.skin-black-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-black-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-black-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-black-light .sidebar-menu>li:hover>a,.skin-black-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-black-light .sidebar-menu>li.active{border-left-color:#fff}.skin-black-light .sidebar-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-black-light .sidebar a{color:#444}.skin-black-light .sidebar a:hover{text-decoration:none}.skin-black-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-black-light .sidebar-menu .treeview-menu>li.active>a,.skin-black-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-black-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-black-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-black-light .sidebar-form input[type="text"],.skin-black-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-black-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black-light .sidebar-form input[type="text"]:focus,.skin-black-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-black-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-black.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Black 3 | * ----------- 4 | */ 5 | /* skin-black navbar */ 6 | .skin-black .main-header { 7 | -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05); 8 | box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.05); 9 | } 10 | .skin-black .main-header .navbar-toggle { 11 | color: #333; 12 | } 13 | .skin-black .main-header .navbar-brand { 14 | color: #333; 15 | border-right: 1px solid #eee; 16 | } 17 | .skin-black .main-header .navbar { 18 | background-color: #ffffff; 19 | } 20 | .skin-black .main-header .navbar .nav > li > a { 21 | color: #333333; 22 | } 23 | .skin-black .main-header .navbar .nav > li > a:hover, 24 | .skin-black .main-header .navbar .nav > li > a:active, 25 | .skin-black .main-header .navbar .nav > li > a:focus, 26 | .skin-black .main-header .navbar .nav .open > a, 27 | .skin-black .main-header .navbar .nav .open > a:hover, 28 | .skin-black .main-header .navbar .nav .open > a:focus, 29 | .skin-black .main-header .navbar .nav > .active > a { 30 | background: #ffffff; 31 | color: #999999; 32 | } 33 | .skin-black .main-header .navbar .sidebar-toggle { 34 | color: #333333; 35 | } 36 | .skin-black .main-header .navbar .sidebar-toggle:hover { 37 | color: #999999; 38 | background: #ffffff; 39 | } 40 | .skin-black .main-header .navbar > .sidebar-toggle { 41 | color: #333; 42 | border-right: 1px solid #eee; 43 | } 44 | .skin-black .main-header .navbar .navbar-nav > li > a { 45 | border-right: 1px solid #eee; 46 | } 47 | .skin-black .main-header .navbar .navbar-custom-menu .navbar-nav > li > a, 48 | .skin-black .main-header .navbar .navbar-right > li > a { 49 | border-left: 1px solid #eee; 50 | border-right-width: 0; 51 | } 52 | .skin-black .main-header > .logo { 53 | background-color: #ffffff; 54 | color: #333333; 55 | border-bottom: 0 solid transparent; 56 | border-right: 1px solid #eee; 57 | } 58 | .skin-black .main-header > .logo:hover { 59 | background-color: #fcfcfc; 60 | } 61 | @media (max-width: 767px) { 62 | .skin-black .main-header > .logo { 63 | background-color: #222222; 64 | color: #ffffff; 65 | border-bottom: 0 solid transparent; 66 | border-right: none; 67 | } 68 | .skin-black .main-header > .logo:hover { 69 | background-color: #1f1f1f; 70 | } 71 | } 72 | .skin-black .main-header li.user-header { 73 | background-color: #222; 74 | } 75 | .skin-black .content-header { 76 | background: transparent; 77 | box-shadow: none; 78 | } 79 | .skin-black .wrapper, 80 | .skin-black .main-sidebar, 81 | .skin-black .left-side { 82 | background-color: #222d32; 83 | } 84 | .skin-black .user-panel > .info, 85 | .skin-black .user-panel > .info > a { 86 | color: #fff; 87 | } 88 | .skin-black .sidebar-menu > li.header { 89 | color: #4b646f; 90 | background: #1a2226; 91 | } 92 | .skin-black .sidebar-menu > li > a { 93 | border-left: 3px solid transparent; 94 | } 95 | .skin-black .sidebar-menu > li:hover > a, 96 | .skin-black .sidebar-menu > li.active > a, 97 | .skin-black .sidebar-menu > li.menu-open > a { 98 | color: #ffffff; 99 | background: #1e282c; 100 | } 101 | .skin-black .sidebar-menu > li.active > a { 102 | border-left-color: #ffffff; 103 | } 104 | .skin-black .sidebar-menu > li > .treeview-menu { 105 | margin: 0 1px; 106 | background: #2c3b41; 107 | } 108 | .skin-black .sidebar a { 109 | color: #b8c7ce; 110 | } 111 | .skin-black .sidebar a:hover { 112 | text-decoration: none; 113 | } 114 | .skin-black .sidebar-menu .treeview-menu > li > a { 115 | color: #8aa4af; 116 | } 117 | .skin-black .sidebar-menu .treeview-menu > li.active > a, 118 | .skin-black .sidebar-menu .treeview-menu > li > a:hover { 119 | color: #ffffff; 120 | } 121 | .skin-black .sidebar-form { 122 | border-radius: 3px; 123 | border: 1px solid #374850; 124 | margin: 10px 10px; 125 | } 126 | .skin-black .sidebar-form input[type="text"], 127 | .skin-black .sidebar-form .btn { 128 | box-shadow: none; 129 | background-color: #374850; 130 | border: 1px solid transparent; 131 | height: 35px; 132 | } 133 | .skin-black .sidebar-form input[type="text"] { 134 | color: #666; 135 | border-top-left-radius: 2px; 136 | border-top-right-radius: 0; 137 | border-bottom-right-radius: 0; 138 | border-bottom-left-radius: 2px; 139 | } 140 | .skin-black .sidebar-form input[type="text"]:focus, 141 | .skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 142 | background-color: #fff; 143 | color: #666; 144 | } 145 | .skin-black .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 146 | border-left-color: #fff; 147 | } 148 | .skin-black .sidebar-form .btn { 149 | color: #999; 150 | border-top-left-radius: 0; 151 | border-top-right-radius: 2px; 152 | border-bottom-right-radius: 2px; 153 | border-bottom-left-radius: 0; 154 | } 155 | .skin-black .pace .pace-progress { 156 | background: #222; 157 | } 158 | .skin-black .pace .pace-activity { 159 | border-top-color: #222; 160 | border-left-color: #222; 161 | } 162 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-black.min.css: -------------------------------------------------------------------------------- 1 | .skin-black .main-header{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.skin-black .main-header .navbar-toggle{color:#333}.skin-black .main-header .navbar-brand{color:#333;border-right:1px solid #eee}.skin-black .main-header .navbar{background-color:#fff}.skin-black .main-header .navbar .nav>li>a{color:#333}.skin-black .main-header .navbar .nav>li>a:hover,.skin-black .main-header .navbar .nav>li>a:active,.skin-black .main-header .navbar .nav>li>a:focus,.skin-black .main-header .navbar .nav .open>a,.skin-black .main-header .navbar .nav .open>a:hover,.skin-black .main-header .navbar .nav .open>a:focus,.skin-black .main-header .navbar .nav>.active>a{background:#fff;color:#999}.skin-black .main-header .navbar .sidebar-toggle{color:#333}.skin-black .main-header .navbar .sidebar-toggle:hover{color:#999;background:#fff}.skin-black .main-header .navbar>.sidebar-toggle{color:#333;border-right:1px solid #eee}.skin-black .main-header .navbar .navbar-nav>li>a{border-right:1px solid #eee}.skin-black .main-header .navbar .navbar-custom-menu .navbar-nav>li>a,.skin-black .main-header .navbar .navbar-right>li>a{border-left:1px solid #eee;border-right-width:0}.skin-black .main-header>.logo{background-color:#fff;color:#333;border-bottom:0 solid transparent;border-right:1px solid #eee}.skin-black .main-header>.logo:hover{background-color:#fcfcfc}@media (max-width:767px){.skin-black .main-header>.logo{background-color:#222;color:#fff;border-bottom:0 solid transparent;border-right:none}.skin-black .main-header>.logo:hover{background-color:#1f1f1f}}.skin-black .main-header li.user-header{background-color:#222}.skin-black .content-header{background:transparent;box-shadow:none}.skin-black .wrapper,.skin-black .main-sidebar,.skin-black .left-side{background-color:#222d32}.skin-black .user-panel>.info,.skin-black .user-panel>.info>a{color:#fff}.skin-black .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-black .sidebar-menu>li>a{border-left:3px solid transparent}.skin-black .sidebar-menu>li:hover>a,.skin-black .sidebar-menu>li.active>a,.skin-black .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-black .sidebar-menu>li.active>a{border-left-color:#fff}.skin-black .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-black .sidebar a{color:#b8c7ce}.skin-black .sidebar a:hover{text-decoration:none}.skin-black .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-black .sidebar-menu .treeview-menu>li.active>a,.skin-black .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-black .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-black .sidebar-form input[type="text"],.skin-black .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-black .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-black .sidebar-form input[type="text"]:focus,.skin-black .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-black .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-black .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-black .pace .pace-progress{background:#222}.skin-black .pace .pace-activity{border-top-color:#222;border-left-color:#222} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-blue-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Blue 3 | * ---------- 4 | */ 5 | .skin-blue-light .main-header .navbar { 6 | background-color: #3c8dbc; 7 | } 8 | .skin-blue-light .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-blue-light .main-header .navbar .nav > li > a:hover, 12 | .skin-blue-light .main-header .navbar .nav > li > a:active, 13 | .skin-blue-light .main-header .navbar .nav > li > a:focus, 14 | .skin-blue-light .main-header .navbar .nav .open > a, 15 | .skin-blue-light .main-header .navbar .nav .open > a:hover, 16 | .skin-blue-light .main-header .navbar .nav .open > a:focus, 17 | .skin-blue-light .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-blue-light .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-blue-light .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-blue-light .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-blue-light .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #367fa9; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-blue-light .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-blue-light .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-blue-light .main-header .navbar .dropdown-menu li a:hover { 42 | background: #367fa9; 43 | } 44 | } 45 | .skin-blue-light .main-header .logo { 46 | background-color: #3c8dbc; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-blue-light .main-header .logo:hover { 51 | background-color: #3b8ab8; 52 | } 53 | .skin-blue-light .main-header li.user-header { 54 | background-color: #3c8dbc; 55 | } 56 | .skin-blue-light .content-header { 57 | background: transparent; 58 | } 59 | .skin-blue-light .wrapper, 60 | .skin-blue-light .main-sidebar, 61 | .skin-blue-light .left-side { 62 | background-color: #f9fafc; 63 | } 64 | .skin-blue-light .main-sidebar { 65 | border-right: 1px solid #d2d6de; 66 | } 67 | .skin-blue-light .user-panel > .info, 68 | .skin-blue-light .user-panel > .info > a { 69 | color: #444444; 70 | } 71 | .skin-blue-light .sidebar-menu > li { 72 | -webkit-transition: border-left-color 0.3s ease; 73 | -o-transition: border-left-color 0.3s ease; 74 | transition: border-left-color 0.3s ease; 75 | } 76 | .skin-blue-light .sidebar-menu > li.header { 77 | color: #848484; 78 | background: #f9fafc; 79 | } 80 | .skin-blue-light .sidebar-menu > li > a { 81 | border-left: 3px solid transparent; 82 | font-weight: 600; 83 | } 84 | .skin-blue-light .sidebar-menu > li:hover > a, 85 | .skin-blue-light .sidebar-menu > li.active > a { 86 | color: #000000; 87 | background: #f4f4f5; 88 | } 89 | .skin-blue-light .sidebar-menu > li.active { 90 | border-left-color: #3c8dbc; 91 | } 92 | .skin-blue-light .sidebar-menu > li.active > a { 93 | font-weight: 600; 94 | } 95 | .skin-blue-light .sidebar-menu > li > .treeview-menu { 96 | background: #f4f4f5; 97 | } 98 | .skin-blue-light .sidebar a { 99 | color: #444444; 100 | } 101 | .skin-blue-light .sidebar a:hover { 102 | text-decoration: none; 103 | } 104 | .skin-blue-light .sidebar-menu .treeview-menu > li > a { 105 | color: #777777; 106 | } 107 | .skin-blue-light .sidebar-menu .treeview-menu > li.active > a, 108 | .skin-blue-light .sidebar-menu .treeview-menu > li > a:hover { 109 | color: #000000; 110 | } 111 | .skin-blue-light .sidebar-menu .treeview-menu > li.active > a { 112 | font-weight: 600; 113 | } 114 | .skin-blue-light .sidebar-form { 115 | border-radius: 3px; 116 | border: 1px solid #d2d6de; 117 | margin: 10px 10px; 118 | } 119 | .skin-blue-light .sidebar-form input[type="text"], 120 | .skin-blue-light .sidebar-form .btn { 121 | box-shadow: none; 122 | background-color: #fff; 123 | border: 1px solid transparent; 124 | height: 35px; 125 | } 126 | .skin-blue-light .sidebar-form input[type="text"] { 127 | color: #666; 128 | border-top-left-radius: 2px; 129 | border-top-right-radius: 0; 130 | border-bottom-right-radius: 0; 131 | border-bottom-left-radius: 2px; 132 | } 133 | .skin-blue-light .sidebar-form input[type="text"]:focus, 134 | .skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 135 | background-color: #fff; 136 | color: #666; 137 | } 138 | .skin-blue-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 139 | border-left-color: #fff; 140 | } 141 | .skin-blue-light .sidebar-form .btn { 142 | color: #999; 143 | border-top-left-radius: 0; 144 | border-top-right-radius: 2px; 145 | border-bottom-right-radius: 2px; 146 | border-bottom-left-radius: 0; 147 | } 148 | @media (min-width: 768px) { 149 | .skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { 150 | border-left: 1px solid #d2d6de; 151 | } 152 | } 153 | .skin-blue-light .main-footer { 154 | border-top-color: #d2d6de; 155 | } 156 | .skin-blue.layout-top-nav .main-header > .logo { 157 | background-color: #3c8dbc; 158 | color: #ffffff; 159 | border-bottom: 0 solid transparent; 160 | } 161 | .skin-blue.layout-top-nav .main-header > .logo:hover { 162 | background-color: #3b8ab8; 163 | } 164 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-blue-light.min.css: -------------------------------------------------------------------------------- 1 | .skin-blue-light .main-header .navbar{background-color:#3c8dbc}.skin-blue-light .main-header .navbar .nav>li>a{color:#fff}.skin-blue-light .main-header .navbar .nav>li>a:hover,.skin-blue-light .main-header .navbar .nav>li>a:active,.skin-blue-light .main-header .navbar .nav>li>a:focus,.skin-blue-light .main-header .navbar .nav .open>a,.skin-blue-light .main-header .navbar .nav .open>a:hover,.skin-blue-light .main-header .navbar .nav .open>a:focus,.skin-blue-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue-light .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue-light .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue-light .main-header .logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue-light .main-header .logo:hover{background-color:#3b8ab8}.skin-blue-light .main-header li.user-header{background-color:#3c8dbc}.skin-blue-light .content-header{background:transparent}.skin-blue-light .wrapper,.skin-blue-light .main-sidebar,.skin-blue-light .left-side{background-color:#f9fafc}.skin-blue-light .main-sidebar{border-right:1px solid #d2d6de}.skin-blue-light .user-panel>.info,.skin-blue-light .user-panel>.info>a{color:#444}.skin-blue-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-blue-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-blue-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-blue-light .sidebar-menu>li:hover>a,.skin-blue-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-blue-light .sidebar-menu>li.active{border-left-color:#3c8dbc}.skin-blue-light .sidebar-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-blue-light .sidebar a{color:#444}.skin-blue-light .sidebar a:hover{text-decoration:none}.skin-blue-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-blue-light .sidebar-menu .treeview-menu>li.active>a,.skin-blue-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-blue-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-blue-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-blue-light .sidebar-form input[type="text"],.skin-blue-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-blue-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue-light .sidebar-form input[type="text"]:focus,.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-blue-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}}.skin-blue-light .main-footer{border-top-color:#d2d6de}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-blue.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Blue 3 | * ---------- 4 | */ 5 | .skin-blue .main-header .navbar { 6 | background-color: #3c8dbc; 7 | } 8 | .skin-blue .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-blue .main-header .navbar .nav > li > a:hover, 12 | .skin-blue .main-header .navbar .nav > li > a:active, 13 | .skin-blue .main-header .navbar .nav > li > a:focus, 14 | .skin-blue .main-header .navbar .nav .open > a, 15 | .skin-blue .main-header .navbar .nav .open > a:hover, 16 | .skin-blue .main-header .navbar .nav .open > a:focus, 17 | .skin-blue .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-blue .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-blue .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-blue .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-blue .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #367fa9; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-blue .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-blue .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-blue .main-header .navbar .dropdown-menu li a:hover { 42 | background: #367fa9; 43 | } 44 | } 45 | .skin-blue .main-header .logo { 46 | background-color: #367fa9; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-blue .main-header .logo:hover { 51 | background-color: #357ca5; 52 | } 53 | .skin-blue .main-header li.user-header { 54 | background-color: #3c8dbc; 55 | } 56 | .skin-blue .content-header { 57 | background: transparent; 58 | } 59 | .skin-blue .wrapper, 60 | .skin-blue .main-sidebar, 61 | .skin-blue .left-side { 62 | background-color: #222d32; 63 | } 64 | .skin-blue .user-panel > .info, 65 | .skin-blue .user-panel > .info > a { 66 | color: #fff; 67 | } 68 | .skin-blue .sidebar-menu > li.header { 69 | color: #4b646f; 70 | background: #1a2226; 71 | } 72 | .skin-blue .sidebar-menu > li > a { 73 | border-left: 3px solid transparent; 74 | } 75 | .skin-blue .sidebar-menu > li:hover > a, 76 | .skin-blue .sidebar-menu > li.active > a, 77 | .skin-blue .sidebar-menu > li.menu-open > a { 78 | color: #ffffff; 79 | background: #1e282c; 80 | } 81 | .skin-blue .sidebar-menu > li.active > a { 82 | border-left-color: #3c8dbc; 83 | } 84 | .skin-blue .sidebar-menu > li > .treeview-menu { 85 | margin: 0 1px; 86 | background: #2c3b41; 87 | } 88 | .skin-blue .sidebar a { 89 | color: #b8c7ce; 90 | } 91 | .skin-blue .sidebar a:hover { 92 | text-decoration: none; 93 | } 94 | .skin-blue .sidebar-menu .treeview-menu > li > a { 95 | color: #8aa4af; 96 | } 97 | .skin-blue .sidebar-menu .treeview-menu > li.active > a, 98 | .skin-blue .sidebar-menu .treeview-menu > li > a:hover { 99 | color: #ffffff; 100 | } 101 | .skin-blue .sidebar-form { 102 | border-radius: 3px; 103 | border: 1px solid #374850; 104 | margin: 10px 10px; 105 | } 106 | .skin-blue .sidebar-form input[type="text"], 107 | .skin-blue .sidebar-form .btn { 108 | box-shadow: none; 109 | background-color: #374850; 110 | border: 1px solid transparent; 111 | height: 35px; 112 | } 113 | .skin-blue .sidebar-form input[type="text"] { 114 | color: #666; 115 | border-top-left-radius: 2px; 116 | border-top-right-radius: 0; 117 | border-bottom-right-radius: 0; 118 | border-bottom-left-radius: 2px; 119 | } 120 | .skin-blue .sidebar-form input[type="text"]:focus, 121 | .skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 122 | background-color: #fff; 123 | color: #666; 124 | } 125 | .skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 126 | border-left-color: #fff; 127 | } 128 | .skin-blue .sidebar-form .btn { 129 | color: #999; 130 | border-top-left-radius: 0; 131 | border-top-right-radius: 2px; 132 | border-bottom-right-radius: 2px; 133 | border-bottom-left-radius: 0; 134 | } 135 | .skin-blue.layout-top-nav .main-header > .logo { 136 | background-color: #3c8dbc; 137 | color: #ffffff; 138 | border-bottom: 0 solid transparent; 139 | } 140 | .skin-blue.layout-top-nav .main-header > .logo:hover { 141 | background-color: #3b8ab8; 142 | } 143 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-blue.min.css: -------------------------------------------------------------------------------- 1 | .skin-blue .main-header .navbar{background-color:#3c8dbc}.skin-blue .main-header .navbar .nav>li>a{color:#fff}.skin-blue .main-header .navbar .nav>li>a:hover,.skin-blue .main-header .navbar .nav>li>a:active,.skin-blue .main-header .navbar .nav>li>a:focus,.skin-blue .main-header .navbar .nav .open>a,.skin-blue .main-header .navbar .nav .open>a:hover,.skin-blue .main-header .navbar .nav .open>a:focus,.skin-blue .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-blue .main-header .navbar .sidebar-toggle{color:#fff}.skin-blue .main-header .navbar .sidebar-toggle:hover{background-color:#367fa9}@media (max-width:767px){.skin-blue .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-blue .main-header .navbar .dropdown-menu li a{color:#fff}.skin-blue .main-header .navbar .dropdown-menu li a:hover{background:#367fa9}}.skin-blue .main-header .logo{background-color:#367fa9;color:#fff;border-bottom:0 solid transparent}.skin-blue .main-header .logo:hover{background-color:#357ca5}.skin-blue .main-header li.user-header{background-color:#3c8dbc}.skin-blue .content-header{background:transparent}.skin-blue .wrapper,.skin-blue .main-sidebar,.skin-blue .left-side{background-color:#222d32}.skin-blue .user-panel>.info,.skin-blue .user-panel>.info>a{color:#fff}.skin-blue .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-blue .sidebar-menu>li>a{border-left:3px solid transparent}.skin-blue .sidebar-menu>li:hover>a,.skin-blue .sidebar-menu>li.active>a,.skin-blue .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-blue .sidebar-menu>li.active>a{border-left-color:#3c8dbc}.skin-blue .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-blue .sidebar a{color:#b8c7ce}.skin-blue .sidebar a:hover{text-decoration:none}.skin-blue .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-blue .sidebar-menu .treeview-menu>li.active>a,.skin-blue .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-blue .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-blue .sidebar-form input[type="text"],.skin-blue .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-blue .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-blue .sidebar-form input[type="text"]:focus,.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-blue .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-blue .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}.skin-blue.layout-top-nav .main-header>.logo{background-color:#3c8dbc;color:#fff;border-bottom:0 solid transparent}.skin-blue.layout-top-nav .main-header>.logo:hover{background-color:#3b8ab8} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-green-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Green 3 | * ----------- 4 | */ 5 | .skin-green-light .main-header .navbar { 6 | background-color: #00a65a; 7 | } 8 | .skin-green-light .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-green-light .main-header .navbar .nav > li > a:hover, 12 | .skin-green-light .main-header .navbar .nav > li > a:active, 13 | .skin-green-light .main-header .navbar .nav > li > a:focus, 14 | .skin-green-light .main-header .navbar .nav .open > a, 15 | .skin-green-light .main-header .navbar .nav .open > a:hover, 16 | .skin-green-light .main-header .navbar .nav .open > a:focus, 17 | .skin-green-light .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-green-light .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-green-light .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-green-light .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-green-light .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #008d4c; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-green-light .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-green-light .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-green-light .main-header .navbar .dropdown-menu li a:hover { 42 | background: #008d4c; 43 | } 44 | } 45 | .skin-green-light .main-header .logo { 46 | background-color: #00a65a; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-green-light .main-header .logo:hover { 51 | background-color: #00a157; 52 | } 53 | .skin-green-light .main-header li.user-header { 54 | background-color: #00a65a; 55 | } 56 | .skin-green-light .content-header { 57 | background: transparent; 58 | } 59 | .skin-green-light .wrapper, 60 | .skin-green-light .main-sidebar, 61 | .skin-green-light .left-side { 62 | background-color: #f9fafc; 63 | } 64 | .skin-green-light .main-sidebar { 65 | border-right: 1px solid #d2d6de; 66 | } 67 | .skin-green-light .user-panel > .info, 68 | .skin-green-light .user-panel > .info > a { 69 | color: #444444; 70 | } 71 | .skin-green-light .sidebar-menu > li { 72 | -webkit-transition: border-left-color 0.3s ease; 73 | -o-transition: border-left-color 0.3s ease; 74 | transition: border-left-color 0.3s ease; 75 | } 76 | .skin-green-light .sidebar-menu > li.header { 77 | color: #848484; 78 | background: #f9fafc; 79 | } 80 | .skin-green-light .sidebar-menu > li > a { 81 | border-left: 3px solid transparent; 82 | font-weight: 600; 83 | } 84 | .skin-green-light .sidebar-menu > li:hover > a, 85 | .skin-green-light .sidebar-menu > li.active > a { 86 | color: #000000; 87 | background: #f4f4f5; 88 | } 89 | .skin-green-light .sidebar-menu > li.active { 90 | border-left-color: #00a65a; 91 | } 92 | .skin-green-light .sidebar-menu > li.active > a { 93 | font-weight: 600; 94 | } 95 | .skin-green-light .sidebar-menu > li > .treeview-menu { 96 | background: #f4f4f5; 97 | } 98 | .skin-green-light .sidebar a { 99 | color: #444444; 100 | } 101 | .skin-green-light .sidebar a:hover { 102 | text-decoration: none; 103 | } 104 | .skin-green-light .sidebar-menu .treeview-menu > li > a { 105 | color: #777777; 106 | } 107 | .skin-green-light .sidebar-menu .treeview-menu > li.active > a, 108 | .skin-green-light .sidebar-menu .treeview-menu > li > a:hover { 109 | color: #000000; 110 | } 111 | .skin-green-light .sidebar-menu .treeview-menu > li.active > a { 112 | font-weight: 600; 113 | } 114 | .skin-green-light .sidebar-form { 115 | border-radius: 3px; 116 | border: 1px solid #d2d6de; 117 | margin: 10px 10px; 118 | } 119 | .skin-green-light .sidebar-form input[type="text"], 120 | .skin-green-light .sidebar-form .btn { 121 | box-shadow: none; 122 | background-color: #fff; 123 | border: 1px solid transparent; 124 | height: 35px; 125 | } 126 | .skin-green-light .sidebar-form input[type="text"] { 127 | color: #666; 128 | border-top-left-radius: 2px; 129 | border-top-right-radius: 0; 130 | border-bottom-right-radius: 0; 131 | border-bottom-left-radius: 2px; 132 | } 133 | .skin-green-light .sidebar-form input[type="text"]:focus, 134 | .skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 135 | background-color: #fff; 136 | color: #666; 137 | } 138 | .skin-green-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 139 | border-left-color: #fff; 140 | } 141 | .skin-green-light .sidebar-form .btn { 142 | color: #999; 143 | border-top-left-radius: 0; 144 | border-top-right-radius: 2px; 145 | border-bottom-right-radius: 2px; 146 | border-bottom-left-radius: 0; 147 | } 148 | @media (min-width: 768px) { 149 | .skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { 150 | border-left: 1px solid #d2d6de; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-green-light.min.css: -------------------------------------------------------------------------------- 1 | .skin-green-light .main-header .navbar{background-color:#00a65a}.skin-green-light .main-header .navbar .nav>li>a{color:#fff}.skin-green-light .main-header .navbar .nav>li>a:hover,.skin-green-light .main-header .navbar .nav>li>a:active,.skin-green-light .main-header .navbar .nav>li>a:focus,.skin-green-light .main-header .navbar .nav .open>a,.skin-green-light .main-header .navbar .nav .open>a:hover,.skin-green-light .main-header .navbar .nav .open>a:focus,.skin-green-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-green-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-green-light .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green-light .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green-light .main-header .logo{background-color:#00a65a;color:#fff;border-bottom:0 solid transparent}.skin-green-light .main-header .logo:hover{background-color:#00a157}.skin-green-light .main-header li.user-header{background-color:#00a65a}.skin-green-light .content-header{background:transparent}.skin-green-light .wrapper,.skin-green-light .main-sidebar,.skin-green-light .left-side{background-color:#f9fafc}.skin-green-light .main-sidebar{border-right:1px solid #d2d6de}.skin-green-light .user-panel>.info,.skin-green-light .user-panel>.info>a{color:#444}.skin-green-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-green-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-green-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-green-light .sidebar-menu>li:hover>a,.skin-green-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-green-light .sidebar-menu>li.active{border-left-color:#00a65a}.skin-green-light .sidebar-menu>li.active>a{font-weight:600}.skin-green-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-green-light .sidebar a{color:#444}.skin-green-light .sidebar a:hover{text-decoration:none}.skin-green-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-green-light .sidebar-menu .treeview-menu>li.active>a,.skin-green-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-green-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-green-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-green-light .sidebar-form input[type="text"],.skin-green-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-green-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green-light .sidebar-form input[type="text"]:focus,.skin-green-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-green-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-green.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Green 3 | * ----------- 4 | */ 5 | .skin-green .main-header .navbar { 6 | background-color: #00a65a; 7 | } 8 | .skin-green .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-green .main-header .navbar .nav > li > a:hover, 12 | .skin-green .main-header .navbar .nav > li > a:active, 13 | .skin-green .main-header .navbar .nav > li > a:focus, 14 | .skin-green .main-header .navbar .nav .open > a, 15 | .skin-green .main-header .navbar .nav .open > a:hover, 16 | .skin-green .main-header .navbar .nav .open > a:focus, 17 | .skin-green .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-green .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-green .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-green .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-green .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #008d4c; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-green .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-green .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-green .main-header .navbar .dropdown-menu li a:hover { 42 | background: #008d4c; 43 | } 44 | } 45 | .skin-green .main-header .logo { 46 | background-color: #008d4c; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-green .main-header .logo:hover { 51 | background-color: #008749; 52 | } 53 | .skin-green .main-header li.user-header { 54 | background-color: #00a65a; 55 | } 56 | .skin-green .content-header { 57 | background: transparent; 58 | } 59 | .skin-green .wrapper, 60 | .skin-green .main-sidebar, 61 | .skin-green .left-side { 62 | background-color: #222d32; 63 | } 64 | .skin-green .user-panel > .info, 65 | .skin-green .user-panel > .info > a { 66 | color: #fff; 67 | } 68 | .skin-green .sidebar-menu > li.header { 69 | color: #4b646f; 70 | background: #1a2226; 71 | } 72 | .skin-green .sidebar-menu > li > a { 73 | border-left: 3px solid transparent; 74 | } 75 | .skin-green .sidebar-menu > li:hover > a, 76 | .skin-green .sidebar-menu > li.active > a, 77 | .skin-green .sidebar-menu > li.menu-open > a { 78 | color: #ffffff; 79 | background: #1e282c; 80 | } 81 | .skin-green .sidebar-menu > li.active > a { 82 | border-left-color: #00a65a; 83 | } 84 | .skin-green .sidebar-menu > li > .treeview-menu { 85 | margin: 0 1px; 86 | background: #2c3b41; 87 | } 88 | .skin-green .sidebar a { 89 | color: #b8c7ce; 90 | } 91 | .skin-green .sidebar a:hover { 92 | text-decoration: none; 93 | } 94 | .skin-green .sidebar-menu .treeview-menu > li > a { 95 | color: #8aa4af; 96 | } 97 | .skin-green .sidebar-menu .treeview-menu > li.active > a, 98 | .skin-green .sidebar-menu .treeview-menu > li > a:hover { 99 | color: #ffffff; 100 | } 101 | .skin-green .sidebar-form { 102 | border-radius: 3px; 103 | border: 1px solid #374850; 104 | margin: 10px 10px; 105 | } 106 | .skin-green .sidebar-form input[type="text"], 107 | .skin-green .sidebar-form .btn { 108 | box-shadow: none; 109 | background-color: #374850; 110 | border: 1px solid transparent; 111 | height: 35px; 112 | } 113 | .skin-green .sidebar-form input[type="text"] { 114 | color: #666; 115 | border-top-left-radius: 2px; 116 | border-top-right-radius: 0; 117 | border-bottom-right-radius: 0; 118 | border-bottom-left-radius: 2px; 119 | } 120 | .skin-green .sidebar-form input[type="text"]:focus, 121 | .skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 122 | background-color: #fff; 123 | color: #666; 124 | } 125 | .skin-green .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 126 | border-left-color: #fff; 127 | } 128 | .skin-green .sidebar-form .btn { 129 | color: #999; 130 | border-top-left-radius: 0; 131 | border-top-right-radius: 2px; 132 | border-bottom-right-radius: 2px; 133 | border-bottom-left-radius: 0; 134 | } 135 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-green.min.css: -------------------------------------------------------------------------------- 1 | .skin-green .main-header .navbar{background-color:#00a65a}.skin-green .main-header .navbar .nav>li>a{color:#fff}.skin-green .main-header .navbar .nav>li>a:hover,.skin-green .main-header .navbar .nav>li>a:active,.skin-green .main-header .navbar .nav>li>a:focus,.skin-green .main-header .navbar .nav .open>a,.skin-green .main-header .navbar .nav .open>a:hover,.skin-green .main-header .navbar .nav .open>a:focus,.skin-green .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-green .main-header .navbar .sidebar-toggle{color:#fff}.skin-green .main-header .navbar .sidebar-toggle:hover{background-color:#008d4c}@media (max-width:767px){.skin-green .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-green .main-header .navbar .dropdown-menu li a{color:#fff}.skin-green .main-header .navbar .dropdown-menu li a:hover{background:#008d4c}}.skin-green .main-header .logo{background-color:#008d4c;color:#fff;border-bottom:0 solid transparent}.skin-green .main-header .logo:hover{background-color:#008749}.skin-green .main-header li.user-header{background-color:#00a65a}.skin-green .content-header{background:transparent}.skin-green .wrapper,.skin-green .main-sidebar,.skin-green .left-side{background-color:#222d32}.skin-green .user-panel>.info,.skin-green .user-panel>.info>a{color:#fff}.skin-green .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-green .sidebar-menu>li>a{border-left:3px solid transparent}.skin-green .sidebar-menu>li:hover>a,.skin-green .sidebar-menu>li.active>a,.skin-green .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-green .sidebar-menu>li.active>a{border-left-color:#00a65a}.skin-green .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-green .sidebar a{color:#b8c7ce}.skin-green .sidebar a:hover{text-decoration:none}.skin-green .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-green .sidebar-menu .treeview-menu>li.active>a,.skin-green .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-green .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-green .sidebar-form input[type="text"],.skin-green .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-green .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-green .sidebar-form input[type="text"]:focus,.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-green .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-green .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-purple-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Purple 3 | * ------------ 4 | */ 5 | .skin-purple-light .main-header .navbar { 6 | background-color: #605ca8; 7 | } 8 | .skin-purple-light .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-purple-light .main-header .navbar .nav > li > a:hover, 12 | .skin-purple-light .main-header .navbar .nav > li > a:active, 13 | .skin-purple-light .main-header .navbar .nav > li > a:focus, 14 | .skin-purple-light .main-header .navbar .nav .open > a, 15 | .skin-purple-light .main-header .navbar .nav .open > a:hover, 16 | .skin-purple-light .main-header .navbar .nav .open > a:focus, 17 | .skin-purple-light .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-purple-light .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-purple-light .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-purple-light .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-purple-light .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #555299; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-purple-light .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-purple-light .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-purple-light .main-header .navbar .dropdown-menu li a:hover { 42 | background: #555299; 43 | } 44 | } 45 | .skin-purple-light .main-header .logo { 46 | background-color: #605ca8; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-purple-light .main-header .logo:hover { 51 | background-color: #5d59a6; 52 | } 53 | .skin-purple-light .main-header li.user-header { 54 | background-color: #605ca8; 55 | } 56 | .skin-purple-light .content-header { 57 | background: transparent; 58 | } 59 | .skin-purple-light .wrapper, 60 | .skin-purple-light .main-sidebar, 61 | .skin-purple-light .left-side { 62 | background-color: #f9fafc; 63 | } 64 | .skin-purple-light .main-sidebar { 65 | border-right: 1px solid #d2d6de; 66 | } 67 | .skin-purple-light .user-panel > .info, 68 | .skin-purple-light .user-panel > .info > a { 69 | color: #444444; 70 | } 71 | .skin-purple-light .sidebar-menu > li { 72 | -webkit-transition: border-left-color 0.3s ease; 73 | -o-transition: border-left-color 0.3s ease; 74 | transition: border-left-color 0.3s ease; 75 | } 76 | .skin-purple-light .sidebar-menu > li.header { 77 | color: #848484; 78 | background: #f9fafc; 79 | } 80 | .skin-purple-light .sidebar-menu > li > a { 81 | border-left: 3px solid transparent; 82 | font-weight: 600; 83 | } 84 | .skin-purple-light .sidebar-menu > li:hover > a, 85 | .skin-purple-light .sidebar-menu > li.active > a { 86 | color: #000000; 87 | background: #f4f4f5; 88 | } 89 | .skin-purple-light .sidebar-menu > li.active { 90 | border-left-color: #605ca8; 91 | } 92 | .skin-purple-light .sidebar-menu > li.active > a { 93 | font-weight: 600; 94 | } 95 | .skin-purple-light .sidebar-menu > li > .treeview-menu { 96 | background: #f4f4f5; 97 | } 98 | .skin-purple-light .sidebar a { 99 | color: #444444; 100 | } 101 | .skin-purple-light .sidebar a:hover { 102 | text-decoration: none; 103 | } 104 | .skin-purple-light .sidebar-menu .treeview-menu > li > a { 105 | color: #777777; 106 | } 107 | .skin-purple-light .sidebar-menu .treeview-menu > li.active > a, 108 | .skin-purple-light .sidebar-menu .treeview-menu > li > a:hover { 109 | color: #000000; 110 | } 111 | .skin-purple-light .sidebar-menu .treeview-menu > li.active > a { 112 | font-weight: 600; 113 | } 114 | .skin-purple-light .sidebar-form { 115 | border-radius: 3px; 116 | border: 1px solid #d2d6de; 117 | margin: 10px 10px; 118 | } 119 | .skin-purple-light .sidebar-form input[type="text"], 120 | .skin-purple-light .sidebar-form .btn { 121 | box-shadow: none; 122 | background-color: #fff; 123 | border: 1px solid transparent; 124 | height: 35px; 125 | } 126 | .skin-purple-light .sidebar-form input[type="text"] { 127 | color: #666; 128 | border-top-left-radius: 2px; 129 | border-top-right-radius: 0; 130 | border-bottom-right-radius: 0; 131 | border-bottom-left-radius: 2px; 132 | } 133 | .skin-purple-light .sidebar-form input[type="text"]:focus, 134 | .skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 135 | background-color: #fff; 136 | color: #666; 137 | } 138 | .skin-purple-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 139 | border-left-color: #fff; 140 | } 141 | .skin-purple-light .sidebar-form .btn { 142 | color: #999; 143 | border-top-left-radius: 0; 144 | border-top-right-radius: 2px; 145 | border-bottom-right-radius: 2px; 146 | border-bottom-left-radius: 0; 147 | } 148 | @media (min-width: 768px) { 149 | .skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { 150 | border-left: 1px solid #d2d6de; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-purple-light.min.css: -------------------------------------------------------------------------------- 1 | .skin-purple-light .main-header .navbar{background-color:#605ca8}.skin-purple-light .main-header .navbar .nav>li>a{color:#fff}.skin-purple-light .main-header .navbar .nav>li>a:hover,.skin-purple-light .main-header .navbar .nav>li>a:active,.skin-purple-light .main-header .navbar .nav>li>a:focus,.skin-purple-light .main-header .navbar .nav .open>a,.skin-purple-light .main-header .navbar .nav .open>a:hover,.skin-purple-light .main-header .navbar .nav .open>a:focus,.skin-purple-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-purple-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-purple-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple-light .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-purple-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple-light .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple-light .main-header .logo{background-color:#605ca8;color:#fff;border-bottom:0 solid transparent}.skin-purple-light .main-header .logo:hover{background-color:#5d59a6}.skin-purple-light .main-header li.user-header{background-color:#605ca8}.skin-purple-light .content-header{background:transparent}.skin-purple-light .wrapper,.skin-purple-light .main-sidebar,.skin-purple-light .left-side{background-color:#f9fafc}.skin-purple-light .main-sidebar{border-right:1px solid #d2d6de}.skin-purple-light .user-panel>.info,.skin-purple-light .user-panel>.info>a{color:#444}.skin-purple-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-purple-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-purple-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-purple-light .sidebar-menu>li:hover>a,.skin-purple-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-purple-light .sidebar-menu>li.active{border-left-color:#605ca8}.skin-purple-light .sidebar-menu>li.active>a{font-weight:600}.skin-purple-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-purple-light .sidebar a{color:#444}.skin-purple-light .sidebar a:hover{text-decoration:none}.skin-purple-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-purple-light .sidebar-menu .treeview-menu>li.active>a,.skin-purple-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-purple-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-purple-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-purple-light .sidebar-form input[type="text"],.skin-purple-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-purple-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple-light .sidebar-form input[type="text"]:focus,.skin-purple-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-purple-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-purple.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Purple 3 | * ------------ 4 | */ 5 | .skin-purple .main-header .navbar { 6 | background-color: #605ca8; 7 | } 8 | .skin-purple .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-purple .main-header .navbar .nav > li > a:hover, 12 | .skin-purple .main-header .navbar .nav > li > a:active, 13 | .skin-purple .main-header .navbar .nav > li > a:focus, 14 | .skin-purple .main-header .navbar .nav .open > a, 15 | .skin-purple .main-header .navbar .nav .open > a:hover, 16 | .skin-purple .main-header .navbar .nav .open > a:focus, 17 | .skin-purple .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-purple .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-purple .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-purple .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-purple .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #555299; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-purple .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-purple .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-purple .main-header .navbar .dropdown-menu li a:hover { 42 | background: #555299; 43 | } 44 | } 45 | .skin-purple .main-header .logo { 46 | background-color: #555299; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-purple .main-header .logo:hover { 51 | background-color: #545096; 52 | } 53 | .skin-purple .main-header li.user-header { 54 | background-color: #605ca8; 55 | } 56 | .skin-purple .content-header { 57 | background: transparent; 58 | } 59 | .skin-purple .wrapper, 60 | .skin-purple .main-sidebar, 61 | .skin-purple .left-side { 62 | background-color: #222d32; 63 | } 64 | .skin-purple .user-panel > .info, 65 | .skin-purple .user-panel > .info > a { 66 | color: #fff; 67 | } 68 | .skin-purple .sidebar-menu > li.header { 69 | color: #4b646f; 70 | background: #1a2226; 71 | } 72 | .skin-purple .sidebar-menu > li > a { 73 | border-left: 3px solid transparent; 74 | } 75 | .skin-purple .sidebar-menu > li:hover > a, 76 | .skin-purple .sidebar-menu > li.active > a, 77 | .skin-purple .sidebar-menu > li.menu-open > a { 78 | color: #ffffff; 79 | background: #1e282c; 80 | } 81 | .skin-purple .sidebar-menu > li.active > a { 82 | border-left-color: #605ca8; 83 | } 84 | .skin-purple .sidebar-menu > li > .treeview-menu { 85 | margin: 0 1px; 86 | background: #2c3b41; 87 | } 88 | .skin-purple .sidebar a { 89 | color: #b8c7ce; 90 | } 91 | .skin-purple .sidebar a:hover { 92 | text-decoration: none; 93 | } 94 | .skin-purple .sidebar-menu .treeview-menu > li > a { 95 | color: #8aa4af; 96 | } 97 | .skin-purple .sidebar-menu .treeview-menu > li.active > a, 98 | .skin-purple .sidebar-menu .treeview-menu > li > a:hover { 99 | color: #ffffff; 100 | } 101 | .skin-purple .sidebar-form { 102 | border-radius: 3px; 103 | border: 1px solid #374850; 104 | margin: 10px 10px; 105 | } 106 | .skin-purple .sidebar-form input[type="text"], 107 | .skin-purple .sidebar-form .btn { 108 | box-shadow: none; 109 | background-color: #374850; 110 | border: 1px solid transparent; 111 | height: 35px; 112 | } 113 | .skin-purple .sidebar-form input[type="text"] { 114 | color: #666; 115 | border-top-left-radius: 2px; 116 | border-top-right-radius: 0; 117 | border-bottom-right-radius: 0; 118 | border-bottom-left-radius: 2px; 119 | } 120 | .skin-purple .sidebar-form input[type="text"]:focus, 121 | .skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 122 | background-color: #fff; 123 | color: #666; 124 | } 125 | .skin-purple .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 126 | border-left-color: #fff; 127 | } 128 | .skin-purple .sidebar-form .btn { 129 | color: #999; 130 | border-top-left-radius: 0; 131 | border-top-right-radius: 2px; 132 | border-bottom-right-radius: 2px; 133 | border-bottom-left-radius: 0; 134 | } 135 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-purple.min.css: -------------------------------------------------------------------------------- 1 | .skin-purple .main-header .navbar{background-color:#605ca8}.skin-purple .main-header .navbar .nav>li>a{color:#fff}.skin-purple .main-header .navbar .nav>li>a:hover,.skin-purple .main-header .navbar .nav>li>a:active,.skin-purple .main-header .navbar .nav>li>a:focus,.skin-purple .main-header .navbar .nav .open>a,.skin-purple .main-header .navbar .nav .open>a:hover,.skin-purple .main-header .navbar .nav .open>a:focus,.skin-purple .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-purple .main-header .navbar .sidebar-toggle{color:#fff}.skin-purple .main-header .navbar .sidebar-toggle:hover{background-color:#555299}@media (max-width:767px){.skin-purple .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-purple .main-header .navbar .dropdown-menu li a{color:#fff}.skin-purple .main-header .navbar .dropdown-menu li a:hover{background:#555299}}.skin-purple .main-header .logo{background-color:#555299;color:#fff;border-bottom:0 solid transparent}.skin-purple .main-header .logo:hover{background-color:#545096}.skin-purple .main-header li.user-header{background-color:#605ca8}.skin-purple .content-header{background:transparent}.skin-purple .wrapper,.skin-purple .main-sidebar,.skin-purple .left-side{background-color:#222d32}.skin-purple .user-panel>.info,.skin-purple .user-panel>.info>a{color:#fff}.skin-purple .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-purple .sidebar-menu>li>a{border-left:3px solid transparent}.skin-purple .sidebar-menu>li:hover>a,.skin-purple .sidebar-menu>li.active>a,.skin-purple .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-purple .sidebar-menu>li.active>a{border-left-color:#605ca8}.skin-purple .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-purple .sidebar a{color:#b8c7ce}.skin-purple .sidebar a:hover{text-decoration:none}.skin-purple .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-purple .sidebar-menu .treeview-menu>li.active>a,.skin-purple .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-purple .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-purple .sidebar-form input[type="text"],.skin-purple .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-purple .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-purple .sidebar-form input[type="text"]:focus,.skin-purple .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-purple .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-purple .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-red-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Red 3 | * --------- 4 | */ 5 | .skin-red-light .main-header .navbar { 6 | background-color: #dd4b39; 7 | } 8 | .skin-red-light .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-red-light .main-header .navbar .nav > li > a:hover, 12 | .skin-red-light .main-header .navbar .nav > li > a:active, 13 | .skin-red-light .main-header .navbar .nav > li > a:focus, 14 | .skin-red-light .main-header .navbar .nav .open > a, 15 | .skin-red-light .main-header .navbar .nav .open > a:hover, 16 | .skin-red-light .main-header .navbar .nav .open > a:focus, 17 | .skin-red-light .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-red-light .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-red-light .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-red-light .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-red-light .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #d73925; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-red-light .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-red-light .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-red-light .main-header .navbar .dropdown-menu li a:hover { 42 | background: #d73925; 43 | } 44 | } 45 | .skin-red-light .main-header .logo { 46 | background-color: #dd4b39; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-red-light .main-header .logo:hover { 51 | background-color: #dc4735; 52 | } 53 | .skin-red-light .main-header li.user-header { 54 | background-color: #dd4b39; 55 | } 56 | .skin-red-light .content-header { 57 | background: transparent; 58 | } 59 | .skin-red-light .wrapper, 60 | .skin-red-light .main-sidebar, 61 | .skin-red-light .left-side { 62 | background-color: #f9fafc; 63 | } 64 | .skin-red-light .main-sidebar { 65 | border-right: 1px solid #d2d6de; 66 | } 67 | .skin-red-light .user-panel > .info, 68 | .skin-red-light .user-panel > .info > a { 69 | color: #444444; 70 | } 71 | .skin-red-light .sidebar-menu > li { 72 | -webkit-transition: border-left-color 0.3s ease; 73 | -o-transition: border-left-color 0.3s ease; 74 | transition: border-left-color 0.3s ease; 75 | } 76 | .skin-red-light .sidebar-menu > li.header { 77 | color: #848484; 78 | background: #f9fafc; 79 | } 80 | .skin-red-light .sidebar-menu > li > a { 81 | border-left: 3px solid transparent; 82 | font-weight: 600; 83 | } 84 | .skin-red-light .sidebar-menu > li:hover > a, 85 | .skin-red-light .sidebar-menu > li.active > a { 86 | color: #000000; 87 | background: #f4f4f5; 88 | } 89 | .skin-red-light .sidebar-menu > li.active { 90 | border-left-color: #dd4b39; 91 | } 92 | .skin-red-light .sidebar-menu > li.active > a { 93 | font-weight: 600; 94 | } 95 | .skin-red-light .sidebar-menu > li > .treeview-menu { 96 | background: #f4f4f5; 97 | } 98 | .skin-red-light .sidebar a { 99 | color: #444444; 100 | } 101 | .skin-red-light .sidebar a:hover { 102 | text-decoration: none; 103 | } 104 | .skin-red-light .sidebar-menu .treeview-menu > li > a { 105 | color: #777777; 106 | } 107 | .skin-red-light .sidebar-menu .treeview-menu > li.active > a, 108 | .skin-red-light .sidebar-menu .treeview-menu > li > a:hover { 109 | color: #000000; 110 | } 111 | .skin-red-light .sidebar-menu .treeview-menu > li.active > a { 112 | font-weight: 600; 113 | } 114 | .skin-red-light .sidebar-form { 115 | border-radius: 3px; 116 | border: 1px solid #d2d6de; 117 | margin: 10px 10px; 118 | } 119 | .skin-red-light .sidebar-form input[type="text"], 120 | .skin-red-light .sidebar-form .btn { 121 | box-shadow: none; 122 | background-color: #fff; 123 | border: 1px solid transparent; 124 | height: 35px; 125 | } 126 | .skin-red-light .sidebar-form input[type="text"] { 127 | color: #666; 128 | border-top-left-radius: 2px; 129 | border-top-right-radius: 0; 130 | border-bottom-right-radius: 0; 131 | border-bottom-left-radius: 2px; 132 | } 133 | .skin-red-light .sidebar-form input[type="text"]:focus, 134 | .skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 135 | background-color: #fff; 136 | color: #666; 137 | } 138 | .skin-red-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 139 | border-left-color: #fff; 140 | } 141 | .skin-red-light .sidebar-form .btn { 142 | color: #999; 143 | border-top-left-radius: 0; 144 | border-top-right-radius: 2px; 145 | border-bottom-right-radius: 2px; 146 | border-bottom-left-radius: 0; 147 | } 148 | @media (min-width: 768px) { 149 | .skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { 150 | border-left: 1px solid #d2d6de; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-red-light.min.css: -------------------------------------------------------------------------------- 1 | .skin-red-light .main-header .navbar{background-color:#dd4b39}.skin-red-light .main-header .navbar .nav>li>a{color:#fff}.skin-red-light .main-header .navbar .nav>li>a:hover,.skin-red-light .main-header .navbar .nav>li>a:active,.skin-red-light .main-header .navbar .nav>li>a:focus,.skin-red-light .main-header .navbar .nav .open>a,.skin-red-light .main-header .navbar .nav .open>a:hover,.skin-red-light .main-header .navbar .nav .open>a:focus,.skin-red-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-red-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-red-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-red-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-red-light .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-red-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red-light .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red-light .main-header .logo{background-color:#dd4b39;color:#fff;border-bottom:0 solid transparent}.skin-red-light .main-header .logo:hover{background-color:#dc4735}.skin-red-light .main-header li.user-header{background-color:#dd4b39}.skin-red-light .content-header{background:transparent}.skin-red-light .wrapper,.skin-red-light .main-sidebar,.skin-red-light .left-side{background-color:#f9fafc}.skin-red-light .main-sidebar{border-right:1px solid #d2d6de}.skin-red-light .user-panel>.info,.skin-red-light .user-panel>.info>a{color:#444}.skin-red-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-red-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-red-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-red-light .sidebar-menu>li:hover>a,.skin-red-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-red-light .sidebar-menu>li.active{border-left-color:#dd4b39}.skin-red-light .sidebar-menu>li.active>a{font-weight:600}.skin-red-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-red-light .sidebar a{color:#444}.skin-red-light .sidebar a:hover{text-decoration:none}.skin-red-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-red-light .sidebar-menu .treeview-menu>li.active>a,.skin-red-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-red-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-red-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-red-light .sidebar-form input[type="text"],.skin-red-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-red-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red-light .sidebar-form input[type="text"]:focus,.skin-red-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-red-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-red.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Red 3 | * --------- 4 | */ 5 | .skin-red .main-header .navbar { 6 | background-color: #dd4b39; 7 | } 8 | .skin-red .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-red .main-header .navbar .nav > li > a:hover, 12 | .skin-red .main-header .navbar .nav > li > a:active, 13 | .skin-red .main-header .navbar .nav > li > a:focus, 14 | .skin-red .main-header .navbar .nav .open > a, 15 | .skin-red .main-header .navbar .nav .open > a:hover, 16 | .skin-red .main-header .navbar .nav .open > a:focus, 17 | .skin-red .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-red .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-red .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-red .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-red .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #d73925; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-red .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-red .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-red .main-header .navbar .dropdown-menu li a:hover { 42 | background: #d73925; 43 | } 44 | } 45 | .skin-red .main-header .logo { 46 | background-color: #d73925; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-red .main-header .logo:hover { 51 | background-color: #d33724; 52 | } 53 | .skin-red .main-header li.user-header { 54 | background-color: #dd4b39; 55 | } 56 | .skin-red .content-header { 57 | background: transparent; 58 | } 59 | .skin-red .wrapper, 60 | .skin-red .main-sidebar, 61 | .skin-red .left-side { 62 | background-color: #222d32; 63 | } 64 | .skin-red .user-panel > .info, 65 | .skin-red .user-panel > .info > a { 66 | color: #fff; 67 | } 68 | .skin-red .sidebar-menu > li.header { 69 | color: #4b646f; 70 | background: #1a2226; 71 | } 72 | .skin-red .sidebar-menu > li > a { 73 | border-left: 3px solid transparent; 74 | } 75 | .skin-red .sidebar-menu > li:hover > a, 76 | .skin-red .sidebar-menu > li.active > a, 77 | .skin-red .sidebar-menu > li.menu-open > a { 78 | color: #ffffff; 79 | background: #1e282c; 80 | } 81 | .skin-red .sidebar-menu > li.active > a { 82 | border-left-color: #dd4b39; 83 | } 84 | .skin-red .sidebar-menu > li > .treeview-menu { 85 | margin: 0 1px; 86 | background: #2c3b41; 87 | } 88 | .skin-red .sidebar a { 89 | color: #b8c7ce; 90 | } 91 | .skin-red .sidebar a:hover { 92 | text-decoration: none; 93 | } 94 | .skin-red .sidebar-menu .treeview-menu > li > a { 95 | color: #8aa4af; 96 | } 97 | .skin-red .sidebar-menu .treeview-menu > li.active > a, 98 | .skin-red .sidebar-menu .treeview-menu > li > a:hover { 99 | color: #ffffff; 100 | } 101 | .skin-red .sidebar-form { 102 | border-radius: 3px; 103 | border: 1px solid #374850; 104 | margin: 10px 10px; 105 | } 106 | .skin-red .sidebar-form input[type="text"], 107 | .skin-red .sidebar-form .btn { 108 | box-shadow: none; 109 | background-color: #374850; 110 | border: 1px solid transparent; 111 | height: 35px; 112 | } 113 | .skin-red .sidebar-form input[type="text"] { 114 | color: #666; 115 | border-top-left-radius: 2px; 116 | border-top-right-radius: 0; 117 | border-bottom-right-radius: 0; 118 | border-bottom-left-radius: 2px; 119 | } 120 | .skin-red .sidebar-form input[type="text"]:focus, 121 | .skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 122 | background-color: #fff; 123 | color: #666; 124 | } 125 | .skin-red .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 126 | border-left-color: #fff; 127 | } 128 | .skin-red .sidebar-form .btn { 129 | color: #999; 130 | border-top-left-radius: 0; 131 | border-top-right-radius: 2px; 132 | border-bottom-right-radius: 2px; 133 | border-bottom-left-radius: 0; 134 | } 135 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-red.min.css: -------------------------------------------------------------------------------- 1 | .skin-red .main-header .navbar{background-color:#dd4b39}.skin-red .main-header .navbar .nav>li>a{color:#fff}.skin-red .main-header .navbar .nav>li>a:hover,.skin-red .main-header .navbar .nav>li>a:active,.skin-red .main-header .navbar .nav>li>a:focus,.skin-red .main-header .navbar .nav .open>a,.skin-red .main-header .navbar .nav .open>a:hover,.skin-red .main-header .navbar .nav .open>a:focus,.skin-red .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-red .main-header .navbar .sidebar-toggle{color:#fff}.skin-red .main-header .navbar .sidebar-toggle:hover{background-color:#d73925}@media (max-width:767px){.skin-red .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-red .main-header .navbar .dropdown-menu li a{color:#fff}.skin-red .main-header .navbar .dropdown-menu li a:hover{background:#d73925}}.skin-red .main-header .logo{background-color:#d73925;color:#fff;border-bottom:0 solid transparent}.skin-red .main-header .logo:hover{background-color:#d33724}.skin-red .main-header li.user-header{background-color:#dd4b39}.skin-red .content-header{background:transparent}.skin-red .wrapper,.skin-red .main-sidebar,.skin-red .left-side{background-color:#222d32}.skin-red .user-panel>.info,.skin-red .user-panel>.info>a{color:#fff}.skin-red .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-red .sidebar-menu>li>a{border-left:3px solid transparent}.skin-red .sidebar-menu>li:hover>a,.skin-red .sidebar-menu>li.active>a,.skin-red .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-red .sidebar-menu>li.active>a{border-left-color:#dd4b39}.skin-red .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-red .sidebar a{color:#b8c7ce}.skin-red .sidebar a:hover{text-decoration:none}.skin-red .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-red .sidebar-menu .treeview-menu>li.active>a,.skin-red .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-red .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-red .sidebar-form input[type="text"],.skin-red .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-red .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-red .sidebar-form input[type="text"]:focus,.skin-red .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-red .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-red .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-yellow-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Yellow 3 | * ------------ 4 | */ 5 | .skin-yellow-light .main-header .navbar { 6 | background-color: #f39c12; 7 | } 8 | .skin-yellow-light .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-yellow-light .main-header .navbar .nav > li > a:hover, 12 | .skin-yellow-light .main-header .navbar .nav > li > a:active, 13 | .skin-yellow-light .main-header .navbar .nav > li > a:focus, 14 | .skin-yellow-light .main-header .navbar .nav .open > a, 15 | .skin-yellow-light .main-header .navbar .nav .open > a:hover, 16 | .skin-yellow-light .main-header .navbar .nav .open > a:focus, 17 | .skin-yellow-light .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-yellow-light .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-yellow-light .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-yellow-light .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-yellow-light .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #e08e0b; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-yellow-light .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-yellow-light .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-yellow-light .main-header .navbar .dropdown-menu li a:hover { 42 | background: #e08e0b; 43 | } 44 | } 45 | .skin-yellow-light .main-header .logo { 46 | background-color: #f39c12; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-yellow-light .main-header .logo:hover { 51 | background-color: #f39a0d; 52 | } 53 | .skin-yellow-light .main-header li.user-header { 54 | background-color: #f39c12; 55 | } 56 | .skin-yellow-light .content-header { 57 | background: transparent; 58 | } 59 | .skin-yellow-light .wrapper, 60 | .skin-yellow-light .main-sidebar, 61 | .skin-yellow-light .left-side { 62 | background-color: #f9fafc; 63 | } 64 | .skin-yellow-light .main-sidebar { 65 | border-right: 1px solid #d2d6de; 66 | } 67 | .skin-yellow-light .user-panel > .info, 68 | .skin-yellow-light .user-panel > .info > a { 69 | color: #444444; 70 | } 71 | .skin-yellow-light .sidebar-menu > li { 72 | -webkit-transition: border-left-color 0.3s ease; 73 | -o-transition: border-left-color 0.3s ease; 74 | transition: border-left-color 0.3s ease; 75 | } 76 | .skin-yellow-light .sidebar-menu > li.header { 77 | color: #848484; 78 | background: #f9fafc; 79 | } 80 | .skin-yellow-light .sidebar-menu > li > a { 81 | border-left: 3px solid transparent; 82 | font-weight: 600; 83 | } 84 | .skin-yellow-light .sidebar-menu > li:hover > a, 85 | .skin-yellow-light .sidebar-menu > li.active > a { 86 | color: #000000; 87 | background: #f4f4f5; 88 | } 89 | .skin-yellow-light .sidebar-menu > li.active { 90 | border-left-color: #f39c12; 91 | } 92 | .skin-yellow-light .sidebar-menu > li.active > a { 93 | font-weight: 600; 94 | } 95 | .skin-yellow-light .sidebar-menu > li > .treeview-menu { 96 | background: #f4f4f5; 97 | } 98 | .skin-yellow-light .sidebar a { 99 | color: #444444; 100 | } 101 | .skin-yellow-light .sidebar a:hover { 102 | text-decoration: none; 103 | } 104 | .skin-yellow-light .sidebar-menu .treeview-menu > li > a { 105 | color: #777777; 106 | } 107 | .skin-yellow-light .sidebar-menu .treeview-menu > li.active > a, 108 | .skin-yellow-light .sidebar-menu .treeview-menu > li > a:hover { 109 | color: #000000; 110 | } 111 | .skin-yellow-light .sidebar-menu .treeview-menu > li.active > a { 112 | font-weight: 600; 113 | } 114 | .skin-yellow-light .sidebar-form { 115 | border-radius: 3px; 116 | border: 1px solid #d2d6de; 117 | margin: 10px 10px; 118 | } 119 | .skin-yellow-light .sidebar-form input[type="text"], 120 | .skin-yellow-light .sidebar-form .btn { 121 | box-shadow: none; 122 | background-color: #fff; 123 | border: 1px solid transparent; 124 | height: 35px; 125 | } 126 | .skin-yellow-light .sidebar-form input[type="text"] { 127 | color: #666; 128 | border-top-left-radius: 2px; 129 | border-top-right-radius: 0; 130 | border-bottom-right-radius: 0; 131 | border-bottom-left-radius: 2px; 132 | } 133 | .skin-yellow-light .sidebar-form input[type="text"]:focus, 134 | .skin-yellow-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 135 | background-color: #fff; 136 | color: #666; 137 | } 138 | .skin-yellow-light .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 139 | border-left-color: #fff; 140 | } 141 | .skin-yellow-light .sidebar-form .btn { 142 | color: #999; 143 | border-top-left-radius: 0; 144 | border-top-right-radius: 2px; 145 | border-bottom-right-radius: 2px; 146 | border-bottom-left-radius: 0; 147 | } 148 | @media (min-width: 768px) { 149 | .skin-yellow-light.sidebar-mini.sidebar-collapse .sidebar-menu > li > .treeview-menu { 150 | border-left: 1px solid #d2d6de; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-yellow-light.min.css: -------------------------------------------------------------------------------- 1 | .skin-yellow-light .main-header .navbar{background-color:#f39c12}.skin-yellow-light .main-header .navbar .nav>li>a{color:#fff}.skin-yellow-light .main-header .navbar .nav>li>a:hover,.skin-yellow-light .main-header .navbar .nav>li>a:active,.skin-yellow-light .main-header .navbar .nav>li>a:focus,.skin-yellow-light .main-header .navbar .nav .open>a,.skin-yellow-light .main-header .navbar .nav .open>a:hover,.skin-yellow-light .main-header .navbar .nav .open>a:focus,.skin-yellow-light .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-yellow-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow-light .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-yellow-light .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow-light .main-header .navbar .sidebar-toggle:hover{background-color:#e08e0b}@media (max-width:767px){.skin-yellow-light .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-yellow-light .main-header .navbar .dropdown-menu li a{color:#fff}.skin-yellow-light .main-header .navbar .dropdown-menu li a:hover{background:#e08e0b}}.skin-yellow-light .main-header .logo{background-color:#f39c12;color:#fff;border-bottom:0 solid transparent}.skin-yellow-light .main-header .logo:hover{background-color:#f39a0d}.skin-yellow-light .main-header li.user-header{background-color:#f39c12}.skin-yellow-light .content-header{background:transparent}.skin-yellow-light .wrapper,.skin-yellow-light .main-sidebar,.skin-yellow-light .left-side{background-color:#f9fafc}.skin-yellow-light .main-sidebar{border-right:1px solid #d2d6de}.skin-yellow-light .user-panel>.info,.skin-yellow-light .user-panel>.info>a{color:#444}.skin-yellow-light .sidebar-menu>li{-webkit-transition:border-left-color .3s ease;-o-transition:border-left-color .3s ease;transition:border-left-color .3s ease}.skin-yellow-light .sidebar-menu>li.header{color:#848484;background:#f9fafc}.skin-yellow-light .sidebar-menu>li>a{border-left:3px solid transparent;font-weight:600}.skin-yellow-light .sidebar-menu>li:hover>a,.skin-yellow-light .sidebar-menu>li.active>a{color:#000;background:#f4f4f5}.skin-yellow-light .sidebar-menu>li.active{border-left-color:#f39c12}.skin-yellow-light .sidebar-menu>li.active>a{font-weight:600}.skin-yellow-light .sidebar-menu>li>.treeview-menu{background:#f4f4f5}.skin-yellow-light .sidebar a{color:#444}.skin-yellow-light .sidebar a:hover{text-decoration:none}.skin-yellow-light .sidebar-menu .treeview-menu>li>a{color:#777}.skin-yellow-light .sidebar-menu .treeview-menu>li.active>a,.skin-yellow-light .sidebar-menu .treeview-menu>li>a:hover{color:#000}.skin-yellow-light .sidebar-menu .treeview-menu>li.active>a{font-weight:600}.skin-yellow-light .sidebar-form{border-radius:3px;border:1px solid #d2d6de;margin:10px 10px}.skin-yellow-light .sidebar-form input[type="text"],.skin-yellow-light .sidebar-form .btn{box-shadow:none;background-color:#fff;border:1px solid transparent;height:35px}.skin-yellow-light .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-yellow-light .sidebar-form input[type="text"]:focus,.skin-yellow-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-yellow-light .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-yellow-light .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0}@media (min-width:768px){.skin-yellow-light.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{border-left:1px solid #d2d6de}} -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-yellow.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skin: Yellow 3 | * ------------ 4 | */ 5 | .skin-yellow .main-header .navbar { 6 | background-color: #f39c12; 7 | } 8 | .skin-yellow .main-header .navbar .nav > li > a { 9 | color: #ffffff; 10 | } 11 | .skin-yellow .main-header .navbar .nav > li > a:hover, 12 | .skin-yellow .main-header .navbar .nav > li > a:active, 13 | .skin-yellow .main-header .navbar .nav > li > a:focus, 14 | .skin-yellow .main-header .navbar .nav .open > a, 15 | .skin-yellow .main-header .navbar .nav .open > a:hover, 16 | .skin-yellow .main-header .navbar .nav .open > a:focus, 17 | .skin-yellow .main-header .navbar .nav > .active > a { 18 | background: rgba(0, 0, 0, 0.1); 19 | color: #f6f6f6; 20 | } 21 | .skin-yellow .main-header .navbar .sidebar-toggle { 22 | color: #ffffff; 23 | } 24 | .skin-yellow .main-header .navbar .sidebar-toggle:hover { 25 | color: #f6f6f6; 26 | background: rgba(0, 0, 0, 0.1); 27 | } 28 | .skin-yellow .main-header .navbar .sidebar-toggle { 29 | color: #fff; 30 | } 31 | .skin-yellow .main-header .navbar .sidebar-toggle:hover { 32 | background-color: #e08e0b; 33 | } 34 | @media (max-width: 767px) { 35 | .skin-yellow .main-header .navbar .dropdown-menu li.divider { 36 | background-color: rgba(255, 255, 255, 0.1); 37 | } 38 | .skin-yellow .main-header .navbar .dropdown-menu li a { 39 | color: #fff; 40 | } 41 | .skin-yellow .main-header .navbar .dropdown-menu li a:hover { 42 | background: #e08e0b; 43 | } 44 | } 45 | .skin-yellow .main-header .logo { 46 | background-color: #e08e0b; 47 | color: #ffffff; 48 | border-bottom: 0 solid transparent; 49 | } 50 | .skin-yellow .main-header .logo:hover { 51 | background-color: #db8b0b; 52 | } 53 | .skin-yellow .main-header li.user-header { 54 | background-color: #f39c12; 55 | } 56 | .skin-yellow .content-header { 57 | background: transparent; 58 | } 59 | .skin-yellow .wrapper, 60 | .skin-yellow .main-sidebar, 61 | .skin-yellow .left-side { 62 | background-color: #222d32; 63 | } 64 | .skin-yellow .user-panel > .info, 65 | .skin-yellow .user-panel > .info > a { 66 | color: #fff; 67 | } 68 | .skin-yellow .sidebar-menu > li.header { 69 | color: #4b646f; 70 | background: #1a2226; 71 | } 72 | .skin-yellow .sidebar-menu > li > a { 73 | border-left: 3px solid transparent; 74 | } 75 | .skin-yellow .sidebar-menu > li:hover > a, 76 | .skin-yellow .sidebar-menu > li.active > a, 77 | .skin-yellow .sidebar-menu > li.menu-open > a { 78 | color: #ffffff; 79 | background: #1e282c; 80 | } 81 | .skin-yellow .sidebar-menu > li.active > a { 82 | border-left-color: #f39c12; 83 | } 84 | .skin-yellow .sidebar-menu > li > .treeview-menu { 85 | margin: 0 1px; 86 | background: #2c3b41; 87 | } 88 | .skin-yellow .sidebar a { 89 | color: #b8c7ce; 90 | } 91 | .skin-yellow .sidebar a:hover { 92 | text-decoration: none; 93 | } 94 | .skin-yellow .sidebar-menu .treeview-menu > li > a { 95 | color: #8aa4af; 96 | } 97 | .skin-yellow .sidebar-menu .treeview-menu > li.active > a, 98 | .skin-yellow .sidebar-menu .treeview-menu > li > a:hover { 99 | color: #ffffff; 100 | } 101 | .skin-yellow .sidebar-form { 102 | border-radius: 3px; 103 | border: 1px solid #374850; 104 | margin: 10px 10px; 105 | } 106 | .skin-yellow .sidebar-form input[type="text"], 107 | .skin-yellow .sidebar-form .btn { 108 | box-shadow: none; 109 | background-color: #374850; 110 | border: 1px solid transparent; 111 | height: 35px; 112 | } 113 | .skin-yellow .sidebar-form input[type="text"] { 114 | color: #666; 115 | border-top-left-radius: 2px; 116 | border-top-right-radius: 0; 117 | border-bottom-right-radius: 0; 118 | border-bottom-left-radius: 2px; 119 | } 120 | .skin-yellow .sidebar-form input[type="text"]:focus, 121 | .skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 122 | background-color: #fff; 123 | color: #666; 124 | } 125 | .skin-yellow .sidebar-form input[type="text"]:focus + .input-group-btn .btn { 126 | border-left-color: #fff; 127 | } 128 | .skin-yellow .sidebar-form .btn { 129 | color: #999; 130 | border-top-left-radius: 0; 131 | border-top-right-radius: 2px; 132 | border-bottom-right-radius: 2px; 133 | border-bottom-left-radius: 0; 134 | } 135 | -------------------------------------------------------------------------------- /static/css/plugin/skins/skin-yellow.min.css: -------------------------------------------------------------------------------- 1 | .skin-yellow .main-header .navbar{background-color:#f39c12}.skin-yellow .main-header .navbar .nav>li>a{color:#fff}.skin-yellow .main-header .navbar .nav>li>a:hover,.skin-yellow .main-header .navbar .nav>li>a:active,.skin-yellow .main-header .navbar .nav>li>a:focus,.skin-yellow .main-header .navbar .nav .open>a,.skin-yellow .main-header .navbar .nav .open>a:hover,.skin-yellow .main-header .navbar .nav .open>a:focus,.skin-yellow .main-header .navbar .nav>.active>a{background:rgba(0,0,0,0.1);color:#f6f6f6}.skin-yellow .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow .main-header .navbar .sidebar-toggle:hover{color:#f6f6f6;background:rgba(0,0,0,0.1)}.skin-yellow .main-header .navbar .sidebar-toggle{color:#fff}.skin-yellow .main-header .navbar .sidebar-toggle:hover{background-color:#e08e0b}@media (max-width:767px){.skin-yellow .main-header .navbar .dropdown-menu li.divider{background-color:rgba(255,255,255,0.1)}.skin-yellow .main-header .navbar .dropdown-menu li a{color:#fff}.skin-yellow .main-header .navbar .dropdown-menu li a:hover{background:#e08e0b}}.skin-yellow .main-header .logo{background-color:#e08e0b;color:#fff;border-bottom:0 solid transparent}.skin-yellow .main-header .logo:hover{background-color:#db8b0b}.skin-yellow .main-header li.user-header{background-color:#f39c12}.skin-yellow .content-header{background:transparent}.skin-yellow .wrapper,.skin-yellow .main-sidebar,.skin-yellow .left-side{background-color:#222d32}.skin-yellow .user-panel>.info,.skin-yellow .user-panel>.info>a{color:#fff}.skin-yellow .sidebar-menu>li.header{color:#4b646f;background:#1a2226}.skin-yellow .sidebar-menu>li>a{border-left:3px solid transparent}.skin-yellow .sidebar-menu>li:hover>a,.skin-yellow .sidebar-menu>li.active>a,.skin-yellow .sidebar-menu>li.menu-open>a{color:#fff;background:#1e282c}.skin-yellow .sidebar-menu>li.active>a{border-left-color:#f39c12}.skin-yellow .sidebar-menu>li>.treeview-menu{margin:0 1px;background:#2c3b41}.skin-yellow .sidebar a{color:#b8c7ce}.skin-yellow .sidebar a:hover{text-decoration:none}.skin-yellow .sidebar-menu .treeview-menu>li>a{color:#8aa4af}.skin-yellow .sidebar-menu .treeview-menu>li.active>a,.skin-yellow .sidebar-menu .treeview-menu>li>a:hover{color:#fff}.skin-yellow .sidebar-form{border-radius:3px;border:1px solid #374850;margin:10px 10px}.skin-yellow .sidebar-form input[type="text"],.skin-yellow .sidebar-form .btn{box-shadow:none;background-color:#374850;border:1px solid transparent;height:35px}.skin-yellow .sidebar-form input[type="text"]{color:#666;border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px}.skin-yellow .sidebar-form input[type="text"]:focus,.skin-yellow .sidebar-form input[type="text"]:focus+.input-group-btn .btn{background-color:#fff;color:#666}.skin-yellow .sidebar-form input[type="text"]:focus+.input-group-btn .btn{border-left-color:#fff}.skin-yellow .sidebar-form .btn{color:#999;border-top-left-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;border-bottom-left-radius:0} -------------------------------------------------------------------------------- /static/js/plugin/datatables/i18n/English.lang: -------------------------------------------------------------------------------- 1 | /** 2 | * English - this is the default DataTables ships with 3 | * @name English 4 | * @anchor English 5 | * @author Allan Jardine 6 | */ 7 | 8 | { 9 | "sEmptyTable": "No data available in table", 10 | "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", 11 | "sInfoEmpty": "Showing 0 to 0 of 0 entries", 12 | "sInfoFiltered": "(filtered from _MAX_ total entries)", 13 | "sInfoPostFix": "", 14 | "sInfoThousands": ",", 15 | "sLengthMenu": "Show _MENU_ entries", 16 | "sLoadingRecords": "Loading...", 17 | "sProcessing": "Processing...", 18 | "sSearch": "Search:", 19 | "sZeroRecords": "No matching records found", 20 | "oPaginate": { 21 | "sFirst": "First", 22 | "sLast": "Last", 23 | "sNext": "Next", 24 | "sPrevious": "Previous" 25 | }, 26 | "oAria": { 27 | "sSortAscending": ": activate to sort column ascending", 28 | "sSortDescending": ": activate to sort column descending" 29 | } 30 | } -------------------------------------------------------------------------------- /static/js/plugin/datatables/i18n/zh-hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "sProcessing": "处理中...", 3 | "sLengthMenu": "显示 _MENU_ 项结果", 4 | "sZeroRecords": "没有匹配结果", 5 | "sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项", 6 | "sInfoEmpty": "显示第 0 至 0 项结果,共 0 项", 7 | "sInfoFiltered": "(由 _MAX_ 项结果过滤)", 8 | "sInfoPostFix": "", 9 | "sSearch": "搜索:", 10 | "sUrl": "", 11 | "sEmptyTable": "表中数据为空", 12 | "sLoadingRecords": "载入中...", 13 | "sInfoThousands": ",", 14 | "oPaginate": { 15 | "sFirst": "首页", 16 | "sPrevious": "上页", 17 | "sNext": "下页", 18 | "sLast": "末页" 19 | }, 20 | "oAria": { 21 | "sSortAscending": ": 以升序排列此列", 22 | "sSortDescending": ": 以降序排列此列" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /static/js/plugin/datatables/pdfmake.min.js.map: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/static/js/plugin/datatables/pdfmake.min.js.map -------------------------------------------------------------------------------- /static/js/plugin/select2/select2.i18n.cn.js: -------------------------------------------------------------------------------- 1 | /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ 2 | 3 | (function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="请删除"+t+"个字符";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="请再输入至少"+t+"个字符";return n},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(e){var t="最多只能选择"+e.maximum+"个项目";return t},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"}}}),{define:e.define,require:e.require}})(); -------------------------------------------------------------------------------- /static/js/plugin/toastr/toastr.min.js: -------------------------------------------------------------------------------- 1 | !function(e){e(["jquery"],function(e){return function(){function t(e,t,n){return g({type:O.error,iconClass:m().iconClasses.error,message:e,optionsOverride:n,title:t})}function n(t,n){return t||(t=m()),v=e("#"+t.containerId),v.length?v:(n&&(v=d(t)),v)}function o(e,t,n){return g({type:O.info,iconClass:m().iconClasses.info,message:e,optionsOverride:n,title:t})}function s(e){C=e}function i(e,t,n){return g({type:O.success,iconClass:m().iconClasses.success,message:e,optionsOverride:n,title:t})}function a(e,t,n){return g({type:O.warning,iconClass:m().iconClasses.warning,message:e,optionsOverride:n,title:t})}function r(e,t){var o=m();v||n(o),u(e,o,t)||l(o)}function c(t){var o=m();return v||n(o),t&&0===e(":focus",t).length?void h(t):void(v.children().length&&v.remove())}function l(t){for(var n=v.children(),o=n.length-1;o>=0;o--)u(e(n[o]),t)}function u(t,n,o){var s=!(!o||!o.force)&&o.force;return!(!t||!s&&0!==e(":focus",t).length)&&(t[n.hideMethod]({duration:n.hideDuration,easing:n.hideEasing,complete:function(){h(t)}}),!0)}function d(t){return v=e("
").attr("id",t.containerId).addClass(t.positionClass),v.appendTo(e(t.target)),v}function p(){return{tapToDismiss:!0,toastClass:"toast",containerId:"toast-container",debug:!1,showMethod:"fadeIn",showDuration:300,showEasing:"swing",onShown:void 0,hideMethod:"fadeOut",hideDuration:1e3,hideEasing:"swing",onHidden:void 0,closeMethod:!1,closeDuration:!1,closeEasing:!1,closeOnHover:!0,extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},iconClass:"toast-info",positionClass:"toast-top-right",timeOut:5e3,titleClass:"toast-title",messageClass:"toast-message",escapeHtml:!1,target:"body",closeHtml:'',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1}}function f(e){C&&C(e)}function g(t){function o(e){return null==e&&(e=""),e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function s(){c(),u(),d(),p(),g(),C(),l(),i()}function i(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}I.attr("aria-live",e)}function a(){E.closeOnHover&&I.hover(H,D),!E.onclick&&E.tapToDismiss&&I.click(b),E.closeButton&&j&&j.click(function(e){e.stopPropagation?e.stopPropagation():void 0!==e.cancelBubble&&e.cancelBubble!==!0&&(e.cancelBubble=!0),E.onCloseClick&&E.onCloseClick(e),b(!0)}),E.onclick&&I.click(function(e){E.onclick(e),b()})}function r(){I.hide(),I[E.showMethod]({duration:E.showDuration,easing:E.showEasing,complete:E.onShown}),E.timeOut>0&&(k=setTimeout(b,E.timeOut),F.maxHideTime=parseFloat(E.timeOut),F.hideEta=(new Date).getTime()+F.maxHideTime,E.progressBar&&(F.intervalId=setInterval(x,10)))}function c(){t.iconClass&&I.addClass(E.toastClass).addClass(y)}function l(){E.newestOnTop?v.prepend(I):v.append(I)}function u(){if(t.title){var e=t.title;E.escapeHtml&&(e=o(t.title)),M.append(e).addClass(E.titleClass),I.append(M)}}function d(){if(t.message){var e=t.message;E.escapeHtml&&(e=o(t.message)),B.append(e).addClass(E.messageClass),I.append(B)}}function p(){E.closeButton&&(j.addClass(E.closeClass).attr("role","button"),I.prepend(j))}function g(){E.progressBar&&(q.addClass(E.progressClass),I.prepend(q))}function C(){E.rtl&&I.addClass("rtl")}function O(e,t){if(e.preventDuplicates){if(t.message===w)return!0;w=t.message}return!1}function b(t){var n=t&&E.closeMethod!==!1?E.closeMethod:E.hideMethod,o=t&&E.closeDuration!==!1?E.closeDuration:E.hideDuration,s=t&&E.closeEasing!==!1?E.closeEasing:E.hideEasing;if(!e(":focus",I).length||t)return clearTimeout(F.intervalId),I[n]({duration:o,easing:s,complete:function(){h(I),clearTimeout(k),E.onHidden&&"hidden"!==P.state&&E.onHidden(),P.state="hidden",P.endTime=new Date,f(P)}})}function D(){(E.timeOut>0||E.extendedTimeOut>0)&&(k=setTimeout(b,E.extendedTimeOut),F.maxHideTime=parseFloat(E.extendedTimeOut),F.hideEta=(new Date).getTime()+F.maxHideTime)}function H(){clearTimeout(k),F.hideEta=0,I.stop(!0,!0)[E.showMethod]({duration:E.showDuration,easing:E.showEasing})}function x(){var e=(F.hideEta-(new Date).getTime())/F.maxHideTime*100;q.width(e+"%")}var E=m(),y=t.iconClass||E.iconClass;if("undefined"!=typeof t.optionsOverride&&(E=e.extend(E,t.optionsOverride),y=t.optionsOverride.iconClass||y),!O(E,t)){T++,v=n(E,!0);var k=null,I=e("
"),M=e("
"),B=e("
"),q=e("
"),j=e(E.closeHtml),F={intervalId:null,hideEta:null,maxHideTime:null},P={toastId:T,state:"visible",startTime:new Date,options:E,map:t};return s(),r(),a(),f(P),E.debug&&console&&console.log(P),I}}function m(){return e.extend({},p(),b.options)}function h(e){v||(v=n()),e.is(":visible")||(e.remove(),e=null,0===v.children().length&&(v.remove(),w=void 0))}var v,C,w,T=0,O={error:"error",info:"info",success:"success",warning:"warning"},b={clear:r,remove:c,error:t,getContainer:n,info:o,options:{},subscribe:s,success:i,version:"2.1.3",warning:a};return b}()})}("function"==typeof define&&define.amd?define:function(e,t){"undefined"!=typeof module&&module.exports?module.exports=t(require("jquery")):window.toastr=t(window.jQuery)}); 2 | -------------------------------------------------------------------------------- /support/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/support/.gitkeep -------------------------------------------------------------------------------- /support/supervisord/cert_manage.ini: -------------------------------------------------------------------------------- 1 | [program:cert_manage] 2 | directory=/opt/cert_manage 3 | environment=PATH="/opt/py3/bin:%(ENV_PATH)s" 4 | command=/opt/py3/bin/python run_server.py all 5 | pidfile=/opt/cert_manage/cert_manage.pid 6 | autostart=true 7 | autorestart=true 8 | stdout_logfile=/opt/cert_manage/logs/cert_manage.log 9 | stderr_logfile=/opt/cert_manage/logs/cert_manage_error.log -------------------------------------------------------------------------------- /templates/_base.html: -------------------------------------------------------------------------------- 1 | {% load static i18n %} 2 | 4 | 5 | 6 | {% load staticfiles %} 7 | 8 | 证书管理系统 9 | {% include '_head_css_js.html' %} 10 | {% block custom_head_css_js %} 11 | {% endblock %} 12 | 13 | 14 | 15 |
16 | 17 |
18 | 67 |
68 | 69 | {% block content %} 70 | {% endblock %} 71 |
72 | 73 | 74 | {% include '_foot_js.html' %} 75 | {% block custom_foot_js %} 76 | {% endblock %} 77 | -------------------------------------------------------------------------------- /templates/_copyright.html: -------------------------------------------------------------------------------- 1 | Copyright 上海XXX网络技术有限公司 © 2015-2019 -------------------------------------------------------------------------------- /templates/_foot_js.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /templates/_head_css_js.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /templates/flash_message_standalone.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 证书管理系统 10 | 11 | {% include '_head_css_js.html' %} 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
21 |

证书管理系统

22 |
23 | {% if errors %} 24 |

25 |

26 | {{ errors }} 27 |
28 |

29 | {% endif %} 30 | 31 | {% if messages %} 32 |

33 |

34 | {{ messages|safe }} 35 |
36 |

37 | {% endif %} 38 |
39 |
40 | 返回 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {% include '_copyright.html' %} 50 |
51 |
52 |
53 | 54 | 76 | 77 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends '_base.html' %} 2 | {% block content %} 3 |

欢迎登录证书管理系统

4 | {% endblock %} -------------------------------------------------------------------------------- /tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/tmp/.gitkeep -------------------------------------------------------------------------------- /users/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/users/__init__.py -------------------------------------------------------------------------------- /users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from users.models import User, UserGroup 4 | 5 | 6 | # Register your models here. 7 | 8 | class userExtAdmin(admin.ModelAdmin): 9 | list_display = ( 10 | 'name', 'username', 'email', 'phone', 'role', 'date_expired', 'is_active', 'date_joined') 11 | 12 | 13 | class userGroupExtAdmin(admin.ModelAdmin): 14 | list_display = ('name', 'date_created') 15 | 16 | 17 | admin.site.register(User, userExtAdmin) 18 | admin.site.register(UserGroup, userGroupExtAdmin) 19 | -------------------------------------------------------------------------------- /users/api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:16 6 | # @Version : 1.0 7 | # @File : api 8 | # @Software : PyCharm 9 | 10 | 11 | from rest_framework.pagination import LimitOffsetPagination 12 | from rest_framework_bulk import BulkModelViewSet 13 | 14 | from users.serializers import UserSerializer, UserGroupSerializer 15 | 16 | from users.models import User, UserGroup 17 | from users.permissions import IsSuperUser 18 | from users.mixins import IDInFilterMixin 19 | 20 | 21 | class UserViewSet(IDInFilterMixin, BulkModelViewSet): 22 | ''' 23 | 用户的操作接口 24 | ''' 25 | queryset = User.objects.all() 26 | serializer_class = UserSerializer 27 | pagination_class = LimitOffsetPagination 28 | permission_classes = (IsSuperUser,) 29 | filter_fields = ('username', 'email', 'name', 'phone', 'date_expired') 30 | search_fields = filter_fields 31 | 32 | 33 | class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet): 34 | ''' 35 | 用户组的操作接口 36 | ''' 37 | queryset = UserGroup.objects.all() 38 | serializer_class = UserGroupSerializer 39 | pagination_class = LimitOffsetPagination 40 | permission_classes = (IsSuperUser,) 41 | search_fields = ('name', 'comment') 42 | -------------------------------------------------------------------------------- /users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | name = 'users' 6 | 7 | def ready(self): 8 | super().ready() 9 | -------------------------------------------------------------------------------- /users/forms.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 下午2:53 6 | # @Version : 1.0 7 | # @File : forms 8 | # @Software : PyCharm 9 | 10 | 11 | from django import forms 12 | from captcha.fields import CaptchaField 13 | from django.contrib.auth.forms import AuthenticationForm 14 | from django.utils.translation import gettext_lazy as _ 15 | 16 | 17 | class UserLoginForm(AuthenticationForm): 18 | username = forms.CharField(label=_('Username'), max_length=100) 19 | password = forms.CharField( 20 | label=_('Password'), widget=forms.PasswordInput, 21 | max_length=128, strip=False 22 | ) 23 | 24 | 25 | class UserLoginCaptchaForm(AuthenticationForm): 26 | username = forms.CharField(label=_('Username'), max_length=100) 27 | password = forms.CharField( 28 | label=_('Password'), widget=forms.PasswordInput, 29 | max_length=128, strip=False 30 | ) 31 | captcha = CaptchaField() 32 | -------------------------------------------------------------------------------- /users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1 on 2019-09-18 14:24 2 | 3 | from django.conf import settings 4 | import django.contrib.auth.models 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | import django.utils.timezone 8 | import users.utils 9 | import uuid 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | initial = True 15 | 16 | dependencies = [ 17 | ('auth', '0009_alter_user_last_name_max_length'), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name='User', 23 | fields=[ 24 | ('password', models.CharField(max_length=128, verbose_name='password')), 25 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 26 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), 27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 28 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 29 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 30 | ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), 31 | ('username', models.CharField(max_length=128, unique=True, verbose_name='Username')), 32 | ('name', models.CharField(max_length=128, verbose_name='Name')), 33 | ('email', models.EmailField(max_length=128, unique=True, verbose_name='Email')), 34 | ('role', models.CharField(blank=True, choices=[('Admin', 'Administrator'), ('User', 'User')], default='User', max_length=36, verbose_name='Role')), 35 | ('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Phone')), 36 | ('otp_level', models.SmallIntegerField(choices=[(0, 'Disable'), (1, 'Enable'), (2, 'Force enable')], default=0, verbose_name='MFA')), 37 | ('date_expired', models.DateTimeField(blank=True, default=users.utils.date_expired_default, null=True, verbose_name='Date expired')), 38 | ('comment', models.TextField(blank=True, max_length=200, verbose_name='Comment')), 39 | ], 40 | options={ 41 | 'verbose_name': 'User', 42 | 'ordering': ['username'], 43 | }, 44 | managers=[ 45 | ('objects', django.contrib.auth.models.UserManager()), 46 | ], 47 | ), 48 | migrations.CreateModel( 49 | name='AccessKey', 50 | fields=[ 51 | ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='AccessKeyID')), 52 | ('secret', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='AccessKeySecret')), 53 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='access_key', to=settings.AUTH_USER_MODEL, verbose_name='User')), 54 | ], 55 | ), 56 | migrations.CreateModel( 57 | name='PrivateToken', 58 | fields=[ 59 | ('key', models.CharField(max_length=40, primary_key=True, serialize=False, verbose_name='Key')), 60 | ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), 61 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='auth_token', to=settings.AUTH_USER_MODEL, verbose_name='User')), 62 | ], 63 | options={ 64 | 'verbose_name': 'Private Token', 65 | }, 66 | ), 67 | migrations.CreateModel( 68 | name='UserGroup', 69 | fields=[ 70 | ('is_discard', models.BooleanField(default=False, verbose_name='is discard')), 71 | ('discard_time', models.DateTimeField(blank=True, null=True, verbose_name='discard time')), 72 | ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), 73 | ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), 74 | ('comment', models.TextField(blank=True, verbose_name='Comment')), 75 | ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), 76 | ('created_by', models.CharField(blank=True, max_length=100, null=True)), 77 | ], 78 | options={ 79 | 'verbose_name': 'User group', 80 | 'ordering': ['name'], 81 | }, 82 | ), 83 | migrations.AddField( 84 | model_name='user', 85 | name='groups', 86 | field=models.ManyToManyField(blank=True, related_name='users', to='users.UserGroup', verbose_name='User group'), 87 | ), 88 | migrations.AddField( 89 | model_name='user', 90 | name='user_permissions', 91 | field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), 92 | ), 93 | ] 94 | -------------------------------------------------------------------------------- /users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itnotebooks/cert_manage/965d62cbcbc06df4105b14847955eef30343ec7c/users/migrations/__init__.py -------------------------------------------------------------------------------- /users/mixins.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午6:49 6 | # @Version : 1.0 7 | # @File : mixins 8 | # @Software : PyCharm 9 | 10 | from django.db import models 11 | from django.utils import timezone 12 | from rest_framework.exceptions import ValidationError 13 | from django.utils.translation import ugettext_lazy as _ 14 | 15 | 16 | class NoDeleteQuerySet(models.query.QuerySet): 17 | 18 | def delete(self): 19 | return self.update(is_discard=True, discard_time=timezone.now()) 20 | 21 | 22 | class NoDeleteManager(models.Manager): 23 | 24 | def get_all(self): 25 | return NoDeleteQuerySet(self.model, using=self._db) 26 | 27 | def get_queryset(self): 28 | return NoDeleteQuerySet(self.model, using=self._db).filter(is_discard=False) 29 | 30 | def get_deleted(self): 31 | return NoDeleteQuerySet(self.model, using=self._db).filter(is_discard=True) 32 | 33 | 34 | class NoDeleteModelMixin(models.Model): 35 | is_discard = models.BooleanField(verbose_name=_("is discard"), default=False) 36 | discard_time = models.DateTimeField(verbose_name=_("discard time"), null=True, blank=True) 37 | 38 | objects = NoDeleteManager() 39 | 40 | class Meta: 41 | abstract = True 42 | 43 | def delete(self): 44 | self.is_discard = True 45 | self.discard_time = timezone.now() 46 | return self.save() 47 | 48 | 49 | class IDInFilterMixin(object): 50 | def filter_queryset(self, queryset): 51 | 52 | queryset = super(IDInFilterMixin, self).filter_queryset(queryset) 53 | id_list = self.request.query_params.get('id__in') 54 | if id_list: 55 | import json 56 | try: 57 | ids = json.loads(id_list) 58 | except Exception as e: 59 | return queryset 60 | if isinstance(ids, list): 61 | queryset = queryset.filter(id__in=ids) 62 | return queryset 63 | 64 | 65 | class BulkSerializerMixin(object): 66 | """ 67 | Become rest_framework_bulk not support uuid as a primary key 68 | so rewrite it. https://github.com/miki725/django-rest-framework-bulk/issues/66 69 | """ 70 | 71 | def to_internal_value(self, data): 72 | from rest_framework_bulk import BulkListSerializer 73 | 74 | try: 75 | ret = super(BulkSerializerMixin, self).to_internal_value(data) 76 | except ValidationError as e: 77 | error = {} 78 | error['msg'] = e.detail 79 | error['code'] = 10400 80 | raise ValidationError(error) 81 | 82 | id_attr = getattr(self.Meta, 'update_lookup_field', 'id') 83 | request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '') 84 | # add update_lookup_field field back to validated data 85 | # since super by default strips out read-only fields 86 | # hence id will no longer be present in validated_data 87 | if all((isinstance(self.root, BulkListSerializer), 88 | id_attr, 89 | request_method in ('PUT', 'PATCH'))): 90 | id_field = self.fields[id_attr] 91 | if data.get("id"): 92 | id_value = id_field.to_internal_value(data.get("id")) 93 | else: 94 | id_value = id_field.to_internal_value(data.get("pk")) 95 | ret[id_attr] = id_value 96 | return ret 97 | -------------------------------------------------------------------------------- /users/models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 下午4:24 6 | # @Version : 1.0 7 | # @File : __init__.py 8 | # @Software : PyCharm 9 | 10 | from .user import UserGroup, User 11 | from .authentication import * 12 | -------------------------------------------------------------------------------- /users/models/authentication.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 下午4:25 6 | # @Version : 1.0 7 | # @File : authentication 8 | # @Software : PyCharm 9 | 10 | 11 | import uuid 12 | from django.db import models 13 | from django.utils.translation import ugettext_lazy as _ 14 | from rest_framework import authentication 15 | from rest_framework.authtoken.models import Token 16 | 17 | from .user import User 18 | 19 | 20 | class AccessKey(models.Model): 21 | id = models.UUIDField(verbose_name='AccessKeyID', primary_key=True, 22 | default=uuid.uuid4, editable=False) 23 | secret = models.UUIDField(verbose_name='AccessKeySecret', 24 | default=uuid.uuid4, editable=False) 25 | user = models.ForeignKey(User, verbose_name='User', 26 | on_delete=models.CASCADE, related_name='access_key') 27 | 28 | def get_id(self): 29 | return str(self.id) 30 | 31 | def get_secret(self): 32 | return str(self.secret) 33 | 34 | def __str__(self): 35 | return str(self.id) 36 | 37 | 38 | class PrivateToken(Token): 39 | """Inherit from auth token, otherwise migration is boring""" 40 | 41 | class Meta: 42 | verbose_name = _('Private Token') 43 | -------------------------------------------------------------------------------- /users/permissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午9:25 6 | # @Version : 1.0 7 | # @File : permissions 8 | # @Software : PyCharm 9 | 10 | 11 | from rest_framework import permissions 12 | 13 | 14 | class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission): 15 | """Allows access to valid user, is active and not expired""" 16 | 17 | def has_permission(self, request, view): 18 | return super(IsValidUser, self).has_permission(request, view) \ 19 | and request.user.is_valid 20 | 21 | 22 | class IsSuperUser(IsValidUser): 23 | """Allows access only to superuser""" 24 | 25 | def has_permission(self, request, view): 26 | return super(IsSuperUser, self).has_permission(request, view) \ 27 | and request.user.is_superuser 28 | -------------------------------------------------------------------------------- /users/serializers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午9:37 6 | # @Version : 1.0 7 | # @File : serializers 8 | # @Software : PyCharm 9 | 10 | 11 | from rest_framework import serializers 12 | 13 | from rest_framework_bulk import BulkListSerializer 14 | from users.mixins import BulkSerializerMixin 15 | from users.models import User, UserGroup 16 | 17 | 18 | class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): 19 | class Meta: 20 | model = UserGroup 21 | list_serializer_class = BulkListSerializer 22 | fields = '__all__' 23 | 24 | 25 | class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): 26 | groups = UserGroupSerializer(many=True) 27 | 28 | class Meta: 29 | model = User 30 | list_serializer_class = BulkListSerializer 31 | fields = ['id', 'name', 'username', 'first_name', 'last_name', 'role', 'phone', 'groups', 'is_active', 'email', 32 | 'date_expired'] 33 | -------------------------------------------------------------------------------- /users/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | 4 | post_user_create = Signal(providing_args=('user',)) 5 | 6 | -------------------------------------------------------------------------------- /users/templates/users/login.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | {% load i18n %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 证书管理系统 10 | {% include '_head_css_js.html' %} 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 | 23 | {% trans 'Login' %} 25 |
26 |
27 | {% csrf_token %} 28 | {% if form.errors %} 29 | {% if 'captcha' in form.errors %} 30 |

{% trans 'Captcha invalid' %}

31 | {% else %} 32 |

{{ form.non_field_errors.as_text }}

33 | {% endif %} 34 | {% endif %} 35 |
36 | 39 |
40 |
41 | 43 |
44 |
45 | {{ form.captcha }} 46 |
47 | 48 | 49 |
50 |

51 |

52 |
53 |
54 |
55 |
56 |
57 |
58 | {% include '_copyright.html' %} 59 |
60 |
61 |
62 | 63 | 64 | -------------------------------------------------------------------------------- /users/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /users/urls/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:14 6 | # @Version : 1.0 7 | # @File : __init__.py 8 | # @Software : PyCharm 9 | -------------------------------------------------------------------------------- /users/urls/api_urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:14 6 | # @Version : 1.0 7 | # @File : api_urls 8 | # @Software : PyCharm 9 | 10 | 11 | from __future__ import absolute_import 12 | 13 | from django.conf.urls import url 14 | from rest_framework_bulk.routes import BulkRouter 15 | from users import api 16 | 17 | app_name = 'users' 18 | 19 | router = BulkRouter() 20 | router.register(r'v1/users', api.UserViewSet, 'user') 21 | router.register(r'v1/groups', api.UserGroupViewSet, 'user-group') 22 | 23 | urlpatterns = [] 24 | 25 | urlpatterns += router.urls 26 | -------------------------------------------------------------------------------- /users/urls/views_urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午7:15 6 | # @Version : 1.0 7 | # @File : views_urls 8 | # @Software : PyCharm 9 | 10 | from __future__ import absolute_import 11 | 12 | from django.conf.urls import url 13 | 14 | from users import views 15 | 16 | app_name = 'users' 17 | 18 | urlpatterns = [ 19 | # Login view 20 | url(r'^login$', views.UserLoginView.as_view(), name='login'), 21 | url(r'^logout$', views.UserLogoutView.as_view(), name='logout') 22 | ] 23 | -------------------------------------------------------------------------------- /users/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Author : Eric Winn 4 | # @Email : eng.eric.winn@gmail.com 5 | # @Time : 19-9-3 上午6:41 6 | # @Version : 1.0 7 | # @File : utils 8 | # @Software : PyCharm 9 | 10 | from django.conf import settings 11 | 12 | from django.utils.translation import ugettext as _ 13 | from django.http import Http404 14 | from django.urls import reverse 15 | from django.utils import timezone 16 | from django.core.cache import cache 17 | 18 | 19 | def date_expired_default(): 20 | try: 21 | years = int(settings.DEFAULT_EXPIRED_YEARS) 22 | except TypeError: 23 | years = 70 24 | return timezone.now() + timezone.timedelta(days=365 * years) 25 | 26 | 27 | def get_tmp_user_from_cache(request): 28 | if not request.session.session_key: 29 | return None 30 | user = cache.get(request.session.session_key + 'user') 31 | return user 32 | 33 | 34 | def set_tmp_user_to_cache(request, user): 35 | cache.set(request.session.session_key + 'user', user, 600) 36 | 37 | 38 | def get_user_or_tmp_user(request): 39 | user = request.user 40 | tmp_user = get_tmp_user_from_cache(request) 41 | if user.is_authenticated: 42 | return user 43 | elif tmp_user: 44 | return tmp_user 45 | else: 46 | raise Http404("Not found this user") 47 | 48 | 49 | def redirect_user_first_login_or_index(request, redirect_field_name): 50 | return request.POST.get( 51 | redirect_field_name, 52 | request.GET.get(redirect_field_name, reverse('index'))) 53 | 54 | 55 | def get_login_ip(request): 56 | x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',') 57 | if x_forwarded_for and x_forwarded_for[0]: 58 | login_ip = x_forwarded_for[0] 59 | else: 60 | login_ip = request.META.get('REMOTE_ADDR', '') 61 | return login_ip 62 | 63 | 64 | create_success_msg = _("%(name)s was created successfully") 65 | update_success_msg = _("%(name)s was updated successfully") 66 | 67 | 68 | def refresh_token(token, user, expiration=settings.TOKEN_EXPIRATION or 3600): 69 | cache.set(token, user.id, expiration) 70 | -------------------------------------------------------------------------------- /users/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from django.shortcuts import redirect 3 | from django.core.cache import cache 4 | from django.contrib.auth import login as auth_login, logout as auth_logout 5 | from django.urls import reverse 6 | 7 | from django.utils.decorators import method_decorator 8 | 9 | from django.utils.translation import ugettext as _ 10 | 11 | from django.views.decorators.cache import never_cache 12 | from django.views.decorators.csrf import csrf_protect 13 | from django.views.decorators.debug import sensitive_post_parameters 14 | from django.views.generic import FormView, TemplateView 15 | 16 | from users import forms 17 | from users.utils import redirect_user_first_login_or_index, set_tmp_user_to_cache, get_login_ip, get_user_or_tmp_user 18 | 19 | 20 | # Create your views here. 21 | 22 | @method_decorator(sensitive_post_parameters(), name='dispatch') 23 | @method_decorator(csrf_protect, name='dispatch') 24 | @method_decorator(never_cache, name='dispatch') 25 | class UserLoginView(FormView): 26 | template_name = 'users/login.html' 27 | form_class = forms.UserLoginForm 28 | form_class_captcha = forms.UserLoginCaptchaForm 29 | redirect_field_name = 'next' 30 | key_prefix = "_LOGIN_INVALID_{}" 31 | 32 | def get(self, request, *args, **kwargs): 33 | if request.user.is_staff: 34 | return redirect(self.get_success_url()) 35 | request.session.set_test_cookie() 36 | return super().get(request, *args, **kwargs) 37 | 38 | def form_valid(self, form): 39 | if not self.request.session.test_cookie_worked(): 40 | return HttpResponse(_("Please enable cookies and try again.")) 41 | 42 | set_tmp_user_to_cache(self.request, form.get_user()) 43 | return redirect(self.get_success_url()) 44 | 45 | def form_invalid(self, form): 46 | ip = get_login_ip(self.request) 47 | cache.set(self.key_prefix.format(ip), 1, 3600) 48 | old_form = form 49 | form = self.form_class_captcha(data=form.data) 50 | form._errors = old_form.errors 51 | return super().form_invalid(form) 52 | 53 | def get_form_class(self): 54 | ip = get_login_ip(self.request) 55 | if cache.get(self.key_prefix.format(ip)): 56 | return self.form_class_captcha 57 | else: 58 | return self.form_class 59 | 60 | def get_success_url(self): 61 | user = get_user_or_tmp_user(self.request) 62 | auth_login(self.request, user) 63 | return redirect_user_first_login_or_index(self.request, self.redirect_field_name) 64 | 65 | def get_context_data(self, **kwargs): 66 | context = { 67 | } 68 | kwargs.update(context) 69 | return super().get_context_data(**kwargs) 70 | 71 | 72 | @method_decorator(never_cache, name='dispatch') 73 | class UserLogoutView(TemplateView): 74 | template_name = 'flash_message_standalone.html' 75 | 76 | def get(self, request, *args, **kwargs): 77 | auth_logout(request) 78 | response = super().get(request, *args, **kwargs) 79 | return response 80 | 81 | def get_context_data(self, **kwargs): 82 | context = { 83 | 'title': _('Logout success'), 84 | 'messages': _('Logout success, return login page'), 85 | 'interval': 5, 86 | 'redirect_url': reverse('index'), 87 | 'auto_redirect': True, 88 | } 89 | kwargs.update(context) 90 | return super().get_context_data(**kwargs) 91 | --------------------------------------------------------------------------------