添加工程文件
parent
9c036d7cb7
commit
2406ef5c95
@ -0,0 +1,226 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import os.path
|
||||
import sys
|
||||
import time
|
||||
|
||||
import redis
|
||||
import tornado.escape
|
||||
import tornado.ioloop
|
||||
import tornado.options
|
||||
import tornado.web
|
||||
# import tornado.websocket
|
||||
import torndb
|
||||
import importlib
|
||||
# from confluent_kafka import Producer
|
||||
# from rediscluster import StrictRedisCluster
|
||||
# from redis import sentinel
|
||||
from rediscluster import RedisCluster
|
||||
# from redis.sentinel import Sentinel
|
||||
# from tornado.options import define, options
|
||||
from tornado.options import options, define as _define, parse_command_line
|
||||
# from elasticsearch import Elasticsearch
|
||||
# from tornado_swagger import swagger
|
||||
|
||||
|
||||
|
||||
def define(name, default=None, type=None, help=None, metavar=None,
|
||||
multiple=False, group=None, callback=None):
|
||||
if name not in options._options:
|
||||
return _define(name, default, type, help, metavar,
|
||||
multiple, group, callback)
|
||||
|
||||
tornado.options.define = define
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
define("port", default=8888, help="run on the given port", type=int)
|
||||
define("debug", default=0)
|
||||
|
||||
_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
importlib.reload(sys)
|
||||
# sys.setdefaultencoding('utf-8')
|
||||
|
||||
try:
|
||||
import website
|
||||
except ImportError:
|
||||
print("app package import error")
|
||||
logging.info("app import error")
|
||||
|
||||
# sys.path.append(os.path.join(_ROOT, "../.."))
|
||||
sys.path.append(os.path.abspath(os.path.join(os.getcwd(), "..")))
|
||||
sys.path.append(os.path.abspath(os.path.join(os.getcwd(), "../..")))
|
||||
|
||||
from website import settings
|
||||
from website.db import app_engine
|
||||
from website.handler import APIErrorHandler
|
||||
from website.urls import handlers, page_handlers
|
||||
# from website.urls import handlers_v2
|
||||
# print(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
class Connection(torndb.Connection):
|
||||
def __init__(self,
|
||||
host,
|
||||
database,
|
||||
user=None,
|
||||
password=None,
|
||||
max_idle_time=7 * 3600,
|
||||
connect_timeout=500,
|
||||
time_zone="+0:00"):
|
||||
self.host = host
|
||||
self.database = database
|
||||
self.max_idle_time = float(max_idle_time)
|
||||
|
||||
args = dict(conv=torndb.CONVERSIONS,
|
||||
use_unicode=True,
|
||||
charset="utf8",
|
||||
db=database,
|
||||
init_command=('SET time_zone = "%s";' %
|
||||
time_zone),
|
||||
connect_timeout=connect_timeout,
|
||||
sql_mode="TRADITIONAL")
|
||||
if user is not None:
|
||||
args["user"] = user
|
||||
if password is not None:
|
||||
args["passwd"] = password
|
||||
|
||||
# We accept a path to a MySQL socket file or a host(:port) string
|
||||
if "/" in host:
|
||||
args["unix_socket"] = host
|
||||
else:
|
||||
self.socket = None
|
||||
pair = host.split(":")
|
||||
if len(pair) == 2:
|
||||
args["host"] = pair[0]
|
||||
args["port"] = int(pair[1])
|
||||
else:
|
||||
args["host"] = host
|
||||
args["port"] = 3306
|
||||
|
||||
self._db = None
|
||||
self._db_args = args
|
||||
self._last_use_time = time.time()
|
||||
try:
|
||||
self.reconnect()
|
||||
except Exception:
|
||||
logging.error("Cannot connect to MySQL on %s",
|
||||
self.host,
|
||||
exc_info=True)
|
||||
|
||||
|
||||
# class NoCacheStaticFileHandler(tornado.web.StaticFileHandler):
|
||||
# def set_extra_headers(self, path):
|
||||
# self.set_header("Cache-control", "no-cache")
|
||||
|
||||
|
||||
class Application(tornado.web.Application):
|
||||
def __init__(self):
|
||||
# from website.handlers import Model
|
||||
handlers_ = []
|
||||
for handler in handlers:
|
||||
handlers_.append(("%s%s" % (settings.api_prefix, handler[0]),
|
||||
handler[1]))
|
||||
|
||||
for handler in page_handlers:
|
||||
handlers_.append((handler[0], handler[1]))
|
||||
|
||||
# for handler in handlers_v2:
|
||||
# handlers_.append(("%s%s" % (settings.api_prefix_v2, handler[0]),
|
||||
# handler[1]))
|
||||
|
||||
# handlers_.append((r"/wap/s", tornado.web.RedirectHandler, dict(url=r"//wap/s.html")))
|
||||
handlers_.append((r".*", APIErrorHandler))
|
||||
# handlers_.append((r"/static/(.*)", NoCacheStaticFileHandler, {"path": os.path.join(_ROOT, "static")}))
|
||||
|
||||
settings_ = dict(
|
||||
debug=options.debug,
|
||||
# login_url="/login",
|
||||
login_url="",
|
||||
cookie_secret=settings.cookie_secret,
|
||||
template_path=os.path.join(_ROOT, "templates"),
|
||||
static_path=os.path.join(_ROOT, "static"),
|
||||
xsrf_cookies=False,
|
||||
autoescape=None,
|
||||
)
|
||||
|
||||
self.db_app = Connection(
|
||||
settings.mysql_app["host"],
|
||||
settings.mysql_app["database"],
|
||||
user=settings.mysql_app["user"],
|
||||
password=settings.mysql_app["password"],
|
||||
time_zone=settings.mysql_app["time_zone"])
|
||||
|
||||
self.app_mysql = app_engine
|
||||
|
||||
# if settings.redis_sentinel == 1:
|
||||
# rs = Sentinel(settings.redis_sentinel_nodes, socket_timeout=0.1)
|
||||
# self.r_app = rs.master_for(settings.redis_sentinel_master,
|
||||
# socket_timeout=0.1,
|
||||
# password=settings.redis_sentinel_pwd)
|
||||
if settings.redis_cluster == 1:
|
||||
self.r_app = RedisCluster(startup_nodes=settings.redis_app_cluster_notes, decode_responses=True,
|
||||
password=settings.redis_cluster_pwd)
|
||||
else:
|
||||
self.r_app = redis.Redis(*settings.redis_app, decode_responses=True)
|
||||
# self.r_app = redis.Redis(*settings.redis_app)
|
||||
|
||||
# self.kafka_producer = Producer(**settings.kafka_conf)
|
||||
# self.es = Elasticsearch(settings.es_nodes)
|
||||
|
||||
# Model.setup_dbs({"db_app": self.db_app,
|
||||
# "r_app": self.r_app
|
||||
# })
|
||||
tornado.web.Application.__init__(self, handlers_, **settings_)
|
||||
# swagger.Application.__init__(self, handlers_, **settings_)
|
||||
|
||||
|
||||
def sig_handler(signum, frame):
|
||||
tornado.ioloop.IOLoop.instance().stop()
|
||||
|
||||
|
||||
class PwdFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
try:
|
||||
print("##########")
|
||||
print("{}, {}".format(record.name, record.msg))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
tornado.options.parse_command_line()
|
||||
# options.parse_command_line()
|
||||
|
||||
formatter = logging.Formatter(
|
||||
'[%(levelname)1.1s %(asctime)s.%(msecs)d '
|
||||
'%(module)s:%(funcName)s:%(lineno)d] %(message)s',
|
||||
"%Y-%m-%d %H:%M:%S"
|
||||
) # creating own format
|
||||
for handler in logging.getLogger().handlers: # setting format for all handlers
|
||||
handler.setFormatter(formatter)
|
||||
# handler.addFilter(PwdFilter())
|
||||
|
||||
app = Application()
|
||||
app.listen(options.port)
|
||||
|
||||
# def ping():
|
||||
# try:
|
||||
# row = app.db_app.get("select id from user limit 1")
|
||||
# if row:
|
||||
# logging.info("db check ok")
|
||||
# except Exception as e:
|
||||
# logging.info(e)
|
||||
# logging.info("db connection err, reconnect")
|
||||
# app.db_app.reconnect()
|
||||
|
||||
logging.info("start app server...")
|
||||
|
||||
# tornado.ioloop.PeriodicCallback(ping, 600000).start()
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""静态配置"""
|
||||
|
||||
# 行业分类
|
||||
industry_map = {
|
||||
1001: u"IT服务",
|
||||
1002: u"制造业",
|
||||
1003: u"批发/零售",
|
||||
1004: u"生活服务",
|
||||
1005: u"文化/体育/娱乐业",
|
||||
1006: u"建筑/房地产",
|
||||
1007: u"教育",
|
||||
1008: u"运输/物流/仓储",
|
||||
1009: u"医疗",
|
||||
1010: u"政府",
|
||||
1011: u"金融",
|
||||
1012: u"能源/采矿",
|
||||
1013: u"农林渔牧",
|
||||
1014: u"其他行业",
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
from tornado import escape
|
||||
from tornado.web import HTTPError
|
||||
|
||||
# HTTP status code
|
||||
HTTP_OK = 200
|
||||
ERROR_BAD_REQUEST = 400
|
||||
ERROR_UNAUTHORIZED = 401
|
||||
ERROR_FORBIDDEN = 403
|
||||
ERROR_NOT_FOUND = 404
|
||||
ERROR_METHOD_NOT_ALLOWED = 405
|
||||
ERROR_INTERNAL_SERVER_ERROR = 500
|
||||
# Custom error code
|
||||
ERROR_WARNING = 1001
|
||||
ERROR_DEPRECATED = 1002
|
||||
ERROR_MAINTAINING = 1003
|
||||
ERROR_UNKNOWN_ERROR = 9999
|
||||
ERROR_LICENSE_NOT_ACTIVE = 9000
|
||||
ERROR_LICENSE_EXPIRE_ATALL = 9003
|
||||
|
||||
|
||||
|
||||
class HTTPAPIError(HTTPError):
|
||||
|
||||
"""API error handling exception
|
||||
|
||||
API server always returns formatted JSON to client even there is
|
||||
an internal server error.
|
||||
"""
|
||||
|
||||
def __init__(self, status_code=ERROR_UNKNOWN_ERROR, message=None,
|
||||
error=None, data=None, log_message=None, *args):
|
||||
assert isinstance(data, dict) or data is None
|
||||
message = message if message else ""
|
||||
assert isinstance(message, str)
|
||||
|
||||
super(HTTPAPIError, self).__init__(int(status_code),
|
||||
log_message, *args)
|
||||
|
||||
self.error = error if error else \
|
||||
_error_types.get(self.status_code, _unknow_error)
|
||||
self.message = message
|
||||
self.data = data if data is not None else {}
|
||||
|
||||
def __str__(self):
|
||||
err = {"meta": {"code": self.status_code, "error": self.error}}
|
||||
|
||||
if self.data:
|
||||
err["data"] = self.data
|
||||
|
||||
if self.message:
|
||||
err["meta"]["message"] = self.message
|
||||
|
||||
return escape.json_encode(err)
|
||||
|
||||
|
||||
# default errors
|
||||
_unknow_error = "unknow_error"
|
||||
_error_types = {400: "bad_request",
|
||||
401: "unauthorized",
|
||||
403: "forbidden",
|
||||
404: "not_found",
|
||||
405: "method_not_allowed",
|
||||
500: "internal_server_error",
|
||||
1001: "warning",
|
||||
1002: "deprecated",
|
||||
1003: "maintaining",
|
||||
9000: "license_not_active",
|
||||
9003: "license_expire_at_all",
|
||||
9999: _unknow_error}
|
@ -0,0 +1,556 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import ast
|
||||
import functools
|
||||
import hashlib
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
import urllib
|
||||
import json
|
||||
# import urlparse
|
||||
from urllib.parse import parse_qs, unquote
|
||||
# from urllib import unquote
|
||||
|
||||
import tornado
|
||||
from tornado import escape
|
||||
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
|
||||
from tornado.options import options
|
||||
# from tornado.web import RequestHandler as BaseRequestHandler, HTTPError, asynchronous
|
||||
from tornado.web import RequestHandler as BaseRequestHandler, HTTPError
|
||||
from torndb import Row
|
||||
|
||||
from website import errors
|
||||
from website import settings
|
||||
from website.service.license import get_license_status
|
||||
|
||||
if settings.enable_curl_async_http_client:
|
||||
AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
|
||||
else:
|
||||
AsyncHTTPClient.configure(None, max_clients=settings.max_clients)
|
||||
|
||||
REMOVE_SLASH_RE = re.compile(".+/$")
|
||||
|
||||
|
||||
def _callback_wrapper(callback):
|
||||
"""A wrapper to handling basic callback error"""
|
||||
|
||||
def _wrapper(response):
|
||||
if response.error:
|
||||
logging.error("call remote api err: %s" % response)
|
||||
if isinstance(response.error, tornado.httpclient.HTTPError):
|
||||
raise errors.HTTPAPIError(response.error.code, "网络连接失败")
|
||||
else:
|
||||
raise errors.HTTPAPIError(errors.ERROR_UNKNOWN_ERROR, "未知错误")
|
||||
else:
|
||||
callback(response)
|
||||
|
||||
return _wrapper
|
||||
|
||||
|
||||
class BaseHandler(BaseRequestHandler):
|
||||
# def __init__(self, application, request, **kwargs):
|
||||
# super(BaseHandler, self).__init__(application, request, **kwargs)
|
||||
# self.xsrf_form_html()
|
||||
|
||||
def set_default_headers(self):
|
||||
self.set_header("Access-Control-Allow-Origin", "*")
|
||||
self.set_header("Access-Control-Allow-Headers",
|
||||
"DNT,token,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,"
|
||||
"Content-Type")
|
||||
# self.set_header("Access-Control-Allow-Headers",
|
||||
# "DNT,web-token,app-token,Authorization,Accept,Origin,Keep-Alive,User-Agent,X-Mx-ReqToken,"
|
||||
# "X-Data-Type,X-Auth-Token,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range")
|
||||
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
|
||||
|
||||
# def _request_summary(self):
|
||||
#
|
||||
# return "%s %s %s(@%s)" % (self.request.method, self.request.uri, self.request.body.decode(),
|
||||
# self.request.remote_ip)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
# enable GET request when enable delegate get to post
|
||||
if settings.app_get_to_post:
|
||||
self.post(*args, **kwargs)
|
||||
else:
|
||||
raise HTTPError(405)
|
||||
|
||||
def render(self, template_name, **kwargs):
|
||||
if self.current_user:
|
||||
if 'username' not in kwargs:
|
||||
kwargs["username"] = self.current_user.name
|
||||
if 'current_userid' not in kwargs:
|
||||
kwargs["current_userid"] = self.current_user.id
|
||||
if 'role' not in kwargs:
|
||||
kwargs["role"] = self.current_user.role
|
||||
self.set_header("Cache-control", "no-cache")
|
||||
return super(BaseHandler, self).render(template_name, **kwargs)
|
||||
|
||||
@property
|
||||
def db_app(self):
|
||||
return self.application.db_app
|
||||
|
||||
@property
|
||||
def app_mysql(self):
|
||||
return self.application.app_mysql
|
||||
|
||||
@property
|
||||
def r_app(self):
|
||||
return self.application.r_app
|
||||
|
||||
@property
|
||||
def kafka_producer(self):
|
||||
return self.application.kafka_producer
|
||||
#
|
||||
@property
|
||||
def es(self):
|
||||
return self.application.es
|
||||
|
||||
# @property
|
||||
# def nsq(self):
|
||||
# return self.application.nsq
|
||||
|
||||
def _call_api(self, url, headers, body, callback, method, callback_wrapper=_callback_wrapper):
|
||||
start = 0
|
||||
if callback_wrapper:
|
||||
callback = callback_wrapper(callback)
|
||||
|
||||
try:
|
||||
start = time.time()
|
||||
AsyncHTTPClient().fetch(HTTPRequest(url=url,
|
||||
method=method,
|
||||
body=body,
|
||||
headers=headers,
|
||||
allow_nonstandard_methods=True,
|
||||
connect_timeout=settings.remote_connect_timeout,
|
||||
request_timeout=settings.remote_request_timeout,
|
||||
follow_redirects=False),
|
||||
callback)
|
||||
except tornado.httpclient.HTTPError:
|
||||
logging.error("requet from %s, take time: %s" % (url, (time.time() - start) * 1000))
|
||||
# if hasattr(x, "response") and x.response:
|
||||
# callback(x.response)
|
||||
# else:
|
||||
# logging.error("Tornado signalled HTTPError %s", x)
|
||||
# raise x
|
||||
|
||||
# @asynchronous
|
||||
async def call_api(self, url, headers=None, body=None, callback=None, method="POST"):
|
||||
if callback is None:
|
||||
callback = self.call_api_callback
|
||||
|
||||
if headers is None:
|
||||
headers = self.request.headers
|
||||
|
||||
if body is None:
|
||||
body = self.request.body
|
||||
else:
|
||||
# make sure it is a post request
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
self._call_api(url, headers, body, callback, method)
|
||||
|
||||
def get_current_user(self, token_body=None):
|
||||
# jid = self.get_secure_cookie(settings.cookie_key)
|
||||
token = self.request.headers.get("token")
|
||||
if token_body:
|
||||
token = token_body
|
||||
|
||||
if not token:
|
||||
return None
|
||||
token = unquote(token)
|
||||
jid = tornado.web.decode_signed_value(
|
||||
settings.cookie_secret,
|
||||
settings.secure_cookie_name,
|
||||
token
|
||||
)
|
||||
|
||||
jid = jid and str(jid, encoding="utf-8") or ""
|
||||
key = settings.session_key_prefix % jid
|
||||
user = self.r_app.get(key)
|
||||
if user:
|
||||
if "/user/info" in self.request.path:
|
||||
pass
|
||||
else:
|
||||
self.r_app.expire(key, settings.session_ttl)
|
||||
user = str(user, encoding='utf8') if isinstance(user, bytes) else user
|
||||
# return Row(ast.literal_eval(str(user, encoding="utf-8")))
|
||||
return Row(ast.literal_eval(user))
|
||||
else:
|
||||
return None
|
||||
|
||||
def md5compare(self, name):
|
||||
string = unquote(name)
|
||||
|
||||
num1 = string.split("|")[0]
|
||||
num2 = string.split("|")[1]
|
||||
num3 = string.split("|")[2]
|
||||
|
||||
num = num1 + num2
|
||||
md5string = hashlib.md5(num).hexdigest().upper()
|
||||
|
||||
if md5string == num3:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def tostr(self, src):
|
||||
return str(src, encoding='utf8') if isinstance(src, bytes) else src
|
||||
|
||||
def prepare(self):
|
||||
self.remove_slash()
|
||||
self.prepare_context()
|
||||
self.set_default_jsonbody()
|
||||
# self.traffic_threshold()
|
||||
|
||||
def set_default_jsonbody(self):
|
||||
if self.request.headers.get('Content-Type') == 'application/json;charset=UTF-8' and self.request.body:
|
||||
# logging.info(self.request.headers.get('Content-Type'))
|
||||
# if self.request.headers.get('Content-Type') == 'application/json; charset=UTF-8':
|
||||
json_body = tornado.escape.json_decode(self.request.body)
|
||||
for key, value in json_body.items():
|
||||
if value is not None:
|
||||
if type(value) is list:
|
||||
self.request.arguments.setdefault(key, []).extend(value)
|
||||
elif type(value) is dict:
|
||||
self.request.arguments[key] = value
|
||||
else:
|
||||
self.request.arguments.setdefault(key, []).extend([bytes(str(value), 'utf-8')])
|
||||
|
||||
def traffic_threshold(self):
|
||||
# if self.request.uri in ["/api/download"]:
|
||||
# return
|
||||
if not self.current_user:
|
||||
user_id = self.request.remote_ip
|
||||
else:
|
||||
user_id = self.current_user.id
|
||||
|
||||
freq_key = "API:FREQ:%s:%s" % (user_id, int(time.time()) / 10)
|
||||
send_count = self.r_app.incr(freq_key)
|
||||
if send_count > settings.api_count_in_ten_second:
|
||||
freq_key = "API:FREQ:%s:%s" % (user_id, int(time.time()) / 10 + 1)
|
||||
self.r_app.setex(freq_key, send_count, 10)
|
||||
raise errors.HTTPAPIError(
|
||||
errors.ERROR_METHOD_NOT_ALLOWED, "请勿频繁操作")
|
||||
if send_count == 1:
|
||||
self.r_app.expire(freq_key, 10)
|
||||
|
||||
def prepare_context(self):
|
||||
# self.nsq.pub(settings.nsq_topic_stats, escape.json_encode(self._create_stats_msg()))
|
||||
pass
|
||||
|
||||
def remove_slash(self):
|
||||
if self.request.method == "GET":
|
||||
if REMOVE_SLASH_RE.match(self.request.path):
|
||||
# remove trail slash in path
|
||||
uri = self.request.path.rstrip("/")
|
||||
if self.request.query:
|
||||
uri += "?" + self.request.query
|
||||
|
||||
self.redirect(uri)
|
||||
|
||||
# def get_json_argument(self, name, default=BaseRequestHandler._ARG_DEFAULT):
|
||||
def get_json_argument(self, name, default=object()):
|
||||
json_body = tornado.escape.json_decode(self.request.body)
|
||||
value = json_body.get(name, default)
|
||||
return escape.utf8(value) if isinstance(value, str) else value
|
||||
|
||||
def get_int_json_argument(self, name, default=0):
|
||||
try:
|
||||
return int(self.get_json_argument(name, default))
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def get_escaped_json_argument(self, name, default=None):
|
||||
if default is not None:
|
||||
return self.escape_string(self.get_json_argument(name, default))
|
||||
else:
|
||||
return self.get_json_argument(name, default)
|
||||
|
||||
# def get_argument(self, name, default=BaseRequestHandler._ARG_DEFAULT, strip=True):
|
||||
def get_argument(self, name, default=object(), strip=True):
|
||||
value = super(BaseHandler, self).get_argument(name, default, strip)
|
||||
return escape.utf8(value) if isinstance(value, str) else value
|
||||
|
||||
def get_int_argument(self, name, default=0):
|
||||
try:
|
||||
return int(self.get_argument(name, default))
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def get_float_argument(self, name, default=0.0):
|
||||
try:
|
||||
return float(self.get_argument(name, default))
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def get_uint_arg(self, name, default=0):
|
||||
try:
|
||||
return abs(int(self.get_argument(name, default)))
|
||||
except ValueError:
|
||||
return default
|
||||
|
||||
def unescape_string(self, s):
|
||||
return escape.xhtml_unescape(s)
|
||||
|
||||
def escape_string(self, s):
|
||||
return escape.xhtml_escape(s)
|
||||
|
||||
def get_escaped_argument(self, name, default=None):
|
||||
if default is not None:
|
||||
return self.escape_string(self.get_argument(name, default))
|
||||
else:
|
||||
return self.get_argument(name, default)
|
||||
|
||||
def get_page_url(self, page, form_id=None, tab=None):
|
||||
if form_id:
|
||||
return "javascript:goto_page('%s',%s);" % (form_id.strip(), page)
|
||||
path = self.request.path
|
||||
query = self.request.query
|
||||
# qdict = urlparse.parse_qs(query)
|
||||
qdict = parse_qs(query)
|
||||
for k, v in qdict.items():
|
||||
if isinstance(v, list):
|
||||
qdict[k] = v and v[0] or ''
|
||||
qdict['page'] = page
|
||||
if tab:
|
||||
qdict['tab'] = tab
|
||||
return path + '?' + urllib.urlencode(qdict)
|
||||
|
||||
def find_all(self, target, substring):
|
||||
current_pos = target.find(substring)
|
||||
while current_pos != -1:
|
||||
yield current_pos
|
||||
current_pos += len(substring)
|
||||
current_pos = target.find(substring, current_pos)
|
||||
|
||||
class WebHandler(BaseHandler):
|
||||
def finish(self, chunk=None, message=None):
|
||||
callback = escape.utf8(self.get_argument("callback", None))
|
||||
if callback:
|
||||
self.set_header("Content-Type", "application/x-javascript")
|
||||
|
||||
if isinstance(chunk, dict):
|
||||
chunk = escape.json_encode(chunk)
|
||||
|
||||
self._write_buffer = [callback, "(", chunk, ")"] if chunk else []
|
||||
super(WebHandler, self).finish()
|
||||
else:
|
||||
self.set_header("Cache-control", "no-cache")
|
||||
super(WebHandler, self).finish(chunk)
|
||||
|
||||
def write_error(self, status_code, **kwargs):
|
||||
try:
|
||||
exc_info = kwargs.pop('exc_info')
|
||||
e = exc_info[1]
|
||||
|
||||
if isinstance(e, errors.HTTPAPIError):
|
||||
pass
|
||||
elif isinstance(e, HTTPError):
|
||||
if e.status_code == 401:
|
||||
self.redirect("/", permanent=True)
|
||||
return
|
||||
e = errors.HTTPAPIError(e.status_code)
|
||||
else:
|
||||
e = errors.HTTPAPIError(errors.ERROR_INTERNAL_SERVER_ERROR)
|
||||
|
||||
exception = "".join([ln for ln
|
||||
in traceback.format_exception(*exc_info)])
|
||||
|
||||
if status_code == errors.ERROR_INTERNAL_SERVER_ERROR \
|
||||
and not options.debug:
|
||||
self.send_error_mail(exception)
|
||||
|
||||
if options.debug:
|
||||
e.data["exception"] = exception
|
||||
|
||||
self.clear()
|
||||
# always return 200 OK for Web errors
|
||||
self.set_status(errors.HTTP_OK)
|
||||
self.set_header("Content-Type", "application/json; charset=UTF-8")
|
||||
self.finish(str(e))
|
||||
except Exception:
|
||||
logging.error(traceback.format_exc())
|
||||
return super(WebHandler, self).write_error(status_code, **kwargs)
|
||||
|
||||
def send_error_mail(self, exception):
|
||||
pass
|
||||
|
||||
|
||||
class APIHandler(BaseHandler):
|
||||
def finish(self, chunk=None, message=None):
|
||||
if chunk is None:
|
||||
chunk = {}
|
||||
|
||||
if isinstance(chunk, dict):
|
||||
chunk = {"meta": {"code": errors.HTTP_OK}, "data": chunk}
|
||||
|
||||
if message:
|
||||
chunk["message"] = message
|
||||
callback = escape.utf8(self.get_argument("callback", None))
|
||||
if callback:
|
||||
self.set_header("Content-Type", "application/x-javascript")
|
||||
|
||||
if isinstance(chunk, dict):
|
||||
chunk = escape.json_encode(chunk)
|
||||
|
||||
self._write_buffer = [callback, "(", chunk, ")"] if chunk else []
|
||||
super(APIHandler, self).finish()
|
||||
else:
|
||||
self.set_header("Content-Type", "application/json; charset=UTF-8")
|
||||
super(APIHandler, self).finish(chunk)
|
||||
|
||||
def write_error(self, status_code, **kwargs):
|
||||
try:
|
||||
exc_info = kwargs.pop('exc_info')
|
||||
e = exc_info[1]
|
||||
|
||||
if isinstance(e, errors.HTTPAPIError):
|
||||
pass
|
||||
elif isinstance(e, HTTPError):
|
||||
e = errors.HTTPAPIError(e.status_code)
|
||||
else:
|
||||
e = errors.HTTPAPIError(errors.ERROR_INTERNAL_SERVER_ERROR)
|
||||
|
||||
exception = "".join([ln for ln
|
||||
in traceback.format_exception(*exc_info)])
|
||||
|
||||
if status_code == errors.ERROR_INTERNAL_SERVER_ERROR \
|
||||
and not options.debug:
|
||||
self.send_error_mail(exception)
|
||||
|
||||
if options.debug:
|
||||
e.data["exception"] = exception
|
||||
|
||||
self.clear()
|
||||
# always return 200 OK for API errors
|
||||
self.set_status(errors.HTTP_OK)
|
||||
self.set_header("Content-Type", "application/json; charset=UTF-8")
|
||||
self.finish(str(e))
|
||||
except Exception:
|
||||
logging.error(traceback.format_exc())
|
||||
return super(APIHandler, self).write_error(status_code, **kwargs)
|
||||
|
||||
def send_error_mail(self, exception):
|
||||
"""Override to implement custom error mail"""
|
||||
pass
|
||||
|
||||
|
||||
class ErrorHandler(BaseHandler):
|
||||
"""Default 404: Not Found handler."""
|
||||
|
||||
def prepare(self):
|
||||
super(ErrorHandler, self).prepare()
|
||||
raise HTTPError(errors.ERROR_NOT_FOUND)
|
||||
|
||||
|
||||
class APIErrorHandler(APIHandler):
|
||||
"""Default API 404: Not Found handler."""
|
||||
|
||||
def prepare(self):
|
||||
super(APIErrorHandler, self).prepare()
|
||||
raise errors.HTTPAPIError(errors.ERROR_NOT_FOUND)
|
||||
|
||||
|
||||
def authenticated(method):
|
||||
"""Decorate methods with this to require that the user be logged in.
|
||||
|
||||
Just raise 401
|
||||
or be avaliable
|
||||
"""
|
||||
|
||||
@functools.wraps(method)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if not self.current_user:
|
||||
# raise HTTPError(401)
|
||||
raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED, "登录失效")
|
||||
return method(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def authenticated_admin(method):
|
||||
@functools.wraps(method)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if int(self.current_user.role) != 1001:
|
||||
# raise HTTPError(403)
|
||||
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "permission denied")
|
||||
return method(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def operation_log(primary_module, secondary_module, operation_type, content, desc):
|
||||
"""
|
||||
Add logging to a function. level is the logging
|
||||
level, name is the logger name, and message is the
|
||||
log message. If name and message aren't specified,
|
||||
they default to the function's module and name.
|
||||
"""
|
||||
def decorate(func):
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
self.db_app.insert(
|
||||
"insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
|
||||
"values(%s, %s, %s, %s, %s, %s, %s)",
|
||||
self.current_user.name, self.request.headers["X-Forwarded-For"], primary_module, secondary_module, operation_type,
|
||||
content, desc
|
||||
)
|
||||
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
return decorate
|
||||
|
||||
def permission(codes):
|
||||
def decorate(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
rows = self.db_app.query(
|
||||
"select rp.permission from role_permission rp, user_role ur where rp.role=ur.role and ur.userid=%s",
|
||||
self.current_user.id
|
||||
)
|
||||
permissions = [item["permission"] for item in rows]
|
||||
for code in codes:
|
||||
if code not in permissions:
|
||||
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "permission denied")
|
||||
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
return decorate
|
||||
|
||||
def license_validate(codes):
|
||||
def decorate(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
license_cache = self.r_app.get("system:license")
|
||||
license_info = {}
|
||||
if license_cache:
|
||||
license_info = json.loads(self.tostr(license_cache))
|
||||
else:
|
||||
row = self.db_app.get("select syscode, expireat from license limit 1")
|
||||
if row:
|
||||
self.r_app.set("system:license", json.dumps({"syscode":row["syscode"], "expireat":row["expireat"]}))
|
||||
license_info = row
|
||||
|
||||
license_status = get_license_status(license_info)
|
||||
# logging.info("license status is : {}, need : {}".format(license_status, codes))
|
||||
if license_status not in codes:
|
||||
raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "系统License未授权")
|
||||
# if not license_info:
|
||||
# raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "License未授权")
|
||||
# expireat = int(license_info["expireat"])
|
||||
# local_time = int(time.time())
|
||||
# if local_time >= expireat:
|
||||
# raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "License授权过期")
|
||||
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
return decorate
|
||||
|
||||
def userlog(method):
|
||||
@functools.wraps(method)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
logging.info("[ip]:%s [user]:%s" % (self.request.remote_ip, self.current_user.id))
|
||||
return method(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
class Model(object):
|
||||
_dbs = {}
|
||||
|
||||
@classmethod
|
||||
def setup_dbs(cls, dbs):
|
||||
cls._dbs = dbs
|
||||
|
||||
@property
|
||||
def db_app(self):
|
||||
return self._dbs.get("db_app", None)
|
||||
|
||||
@property
|
||||
def r_app(self):
|
||||
return self._dbs.get("r_app", None)
|
@ -0,0 +1,253 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
|
||||
from sqlalchemy import text
|
||||
from website import errors
|
||||
from website import settings
|
||||
from website import consts
|
||||
from website import db
|
||||
from website.util import shortuuid, aes
|
||||
from website.service import enterprise
|
||||
from website.handler import APIHandler, authenticated
|
||||
|
||||
|
||||
class EntityIndexHandler(APIHandler):
|
||||
"""首页"""
|
||||
|
||||
@authenticated
|
||||
def post(self):
|
||||
pageNo = self.get_int_argument("pageNo", 1)
|
||||
pageSize = self.get_int_argument("pageSize", 10)
|
||||
name = self.tostr(self.get_escaped_argument("name", ""))
|
||||
|
||||
with self.app_mysql.connect() as conn:
|
||||
sql_text = "select id, name, industry, logo, create_at from enterprise "
|
||||
param = {}
|
||||
|
||||
count_sql_text = "select count(id) from enterprise "
|
||||
count_param = {}
|
||||
|
||||
if name:
|
||||
sql_text += "where name like :name"
|
||||
param["name"] = "%{}%".format(name)
|
||||
|
||||
count_sql_text += "where name like :name"
|
||||
count_param["name"] = "%{}%".format(name)
|
||||
|
||||
sql_text += " order by id desc limit :pageSize offset :offset"
|
||||
param["pageSize"] = pageSize
|
||||
param["offset"] = (pageNo - 1) * pageSize
|
||||
|
||||
cur = conn.execute(text(sql_text), param)
|
||||
result = db.to_json_list(cur)
|
||||
|
||||
count = conn.execute(text(count_sql_text), count_param).fetchone()[0]
|
||||
logging.info(count)
|
||||
logging.info(result)
|
||||
|
||||
data = []
|
||||
for item in result:
|
||||
modelCount = enterprise.get_enterprise_model_count(item["id"])
|
||||
deviceCount = enterprise.get_enterprise_device_count(item["id"])
|
||||
|
||||
data.append(
|
||||
{
|
||||
"id": item["id"],
|
||||
"name": item["name"],
|
||||
"industry": item["industry"],
|
||||
"modelCount": modelCount,
|
||||
"deviceCount": deviceCount,
|
||||
"logo": item["logo"],
|
||||
"createTime": str(item["create_at"])
|
||||
}
|
||||
)
|
||||
|
||||
self.finish({"count": count, "data": data})
|
||||
|
||||
|
||||
class EntityIndexBasecountHandler(APIHandler):
|
||||
"""首页基础统计书记"""
|
||||
|
||||
@authenticated
|
||||
def post(self):
|
||||
entity = enterprise.get_enterprise_entity_count(self.app_mysql)
|
||||
model = enterprise.get_enterprise_model_count()
|
||||
device = enterprise.get_enterprise_device_count()
|
||||
|
||||
|
||||
self.finish({"entity": entity, "model": model, "device": device})
|
||||
|
||||
|
||||
class EntityAddHandler(APIHandler):
|
||||
"""添加企业"""
|
||||
|
||||
@authenticated
|
||||
def post(self):
|
||||
name = self.tostr(self.get_escaped_argument("name", ""))
|
||||
province = self.get_escaped_argument("province", "")
|
||||
city = self.get_escaped_argument("city", "")
|
||||
addr = self.get_escaped_argument("addr", "")
|
||||
industry = self.get_int_argument("industry")
|
||||
contact = self.get_escaped_argument("contact", "")
|
||||
phone = self.get_escaped_argument("phone", "")
|
||||
summary = self.get_escaped_argument("summary", "")
|
||||
logo = self.get_escaped_argument("logo", "")
|
||||
|
||||
if not name or not province or not city or not addr or not industry or not contact or not phone or not summary:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
|
||||
|
||||
if industry not in consts.industry_map:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "清选择行业类型")
|
||||
|
||||
if logo and len(logo) * 0.75 / 1024 / 1024 > 1.2:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "Logo图标大小超出1M限制")
|
||||
|
||||
short_uid = shortuuid.ShortUUID().random(length=8)
|
||||
pwd = aes.encrypt(settings.enterprise_aes_key, short_uid)
|
||||
|
||||
with self.app_mysql.connect() as conn:
|
||||
conn.execute(
|
||||
text(
|
||||
"insert into enterprise(name, province, city, addr, industry, contact, phone, summary, logo, account, pwd) "
|
||||
"values(:name, :province, :city, :addr, :industry, :contact, :phone, :summary, :logo, :account, :pwd)"
|
||||
),
|
||||
{
|
||||
"name": name,
|
||||
"province": province,
|
||||
"city": city,
|
||||
"addr": addr,
|
||||
"industry": industry,
|
||||
"contact": contact,
|
||||
"phone": phone,
|
||||
"summary": summary,
|
||||
"logo": logo,
|
||||
"account": "admin",
|
||||
"pwd": pwd,
|
||||
}
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# self.db_app.insert(
|
||||
# "insert into enterprise(name, province, city, addr, industry, contact, phone, summary, logo, account, pwd) "
|
||||
# "values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
|
||||
# name, province, city, addr, industry, contact, phone, summary, logo, "admin", pwd,
|
||||
# )
|
||||
|
||||
self.finish()
|
||||
|
||||
|
||||
class EntityEditHandler(APIHandler):
|
||||
"""编辑企业"""
|
||||
|
||||
@authenticated
|
||||
def post(self):
|
||||
eid = self.get_int_argument("id")
|
||||
name = self.tostr(self.get_escaped_argument("name", ""))
|
||||
province = self.get_escaped_argument("province", "")
|
||||
city = self.get_escaped_argument("city", "")
|
||||
addr = self.get_escaped_argument("addr", "")
|
||||
industry = self.get_int_argument("industry")
|
||||
contact = self.get_escaped_argument("contact", "")
|
||||
phone = self.get_escaped_argument("phone", "")
|
||||
summary = self.get_escaped_argument("summary", "")
|
||||
logo = self.get_escaped_argument("logo", "")
|
||||
account = self.get_escaped_argument("account", "")
|
||||
|
||||
if not name or not province or not city or not addr or not industry or not contact or not phone or not summary:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "参数缺失")
|
||||
|
||||
if industry not in consts.industry_map:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "清选择行业类型")
|
||||
|
||||
if logo and len(logo) * 0.75 / 1024 / 1024 > 1.2:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "Logo图标大小超出1M限制")
|
||||
|
||||
with self.app_mysql.connect() as conn:
|
||||
conn.execute(
|
||||
text(
|
||||
# "insert into enterprise(name, province, city, addr, industry, contact, phone, summary, logo, account, pwd) "
|
||||
# "values(:name, :province, :city, :addr, :industry, :contact, :phone, :summary, :logo, :account, :pwd)"
|
||||
|
||||
"update enterprise set name=:name, province=:province, city=:city, addr=:addr, industry=:industry, contact"
|
||||
"=:contact, phone=:phone, summary=:summary, logo=:logo, account=:account where id=:id",
|
||||
),
|
||||
{
|
||||
"name": name,
|
||||
"province": province,
|
||||
"city": city,
|
||||
"addr": addr,
|
||||
"industry": industry,
|
||||
"contact": contact,
|
||||
"phone": phone,
|
||||
"summary": summary,
|
||||
"logo": logo,
|
||||
"account": account,
|
||||
}
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
self.finish()
|
||||
|
||||
|
||||
class EntityInfoHandler(APIHandler):
|
||||
"""企业信息"""
|
||||
|
||||
@authenticated
|
||||
def post(self):
|
||||
eid = self.get_int_argument("id")
|
||||
|
||||
row = {}
|
||||
with self.app_mysql.connect() as conn:
|
||||
cur = conn.execute(
|
||||
text("select * from enterprise where id=:id"), {"id": eid}
|
||||
)
|
||||
# keys = list(cur.keys())
|
||||
#
|
||||
# one = cur.fetchone()
|
||||
# row = dict(zip(keys, one))
|
||||
# logging.info(db.Row(itertools.zip_longest(keys, one)))
|
||||
|
||||
row = db.to_json(cur)
|
||||
|
||||
cur.close()
|
||||
|
||||
if not row:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请求失败")
|
||||
|
||||
data = {
|
||||
"name": row["name"],
|
||||
"province": row["province"],
|
||||
"city": row["city"],
|
||||
"industry": row["industry"],
|
||||
"contact": row["contact"],
|
||||
"phone": row["phone"],
|
||||
"summary": row["summary"],
|
||||
"logo": row["logo"],
|
||||
"createTime": str(row["create_at"]),
|
||||
"account": row["account"], # 企业账号
|
||||
}
|
||||
|
||||
self.finish(data)
|
||||
|
||||
|
||||
class EntityPwdcheckHandler(APIHandler):
|
||||
"""查看企业密码"""
|
||||
|
||||
@authenticated
|
||||
def post(self):
|
||||
eid = self.get_int_argument("id")
|
||||
|
||||
with self.app_mysql.connect() as conn:
|
||||
cur = conn.execute(text("select pwd from enterprise where id=:id"), {"id": eid})
|
||||
# row = cur.fetchone()
|
||||
logging.info(cur)
|
||||
row = db.to_json(cur)
|
||||
if not row:
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请求失败")
|
||||
pwd = row["pwd"]
|
||||
cur.close()
|
||||
|
||||
pwd_dcrypt = aes.decrypt(settings.enterprise_aes_key, pwd)
|
||||
|
||||
self.finish({"pwd": pwd_dcrypt})
|
@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from website.handlers.enterprise import handler
|
||||
|
||||
handlers = [
|
||||
("/enterprise/entity/index", handler.EntityIndexHandler),
|
||||
("/enterprise/entity/index/basecount", handler.EntityIndexBasecountHandler),
|
||||
("/enterprise/entity/add", handler.EntityAddHandler),
|
||||
("/enterprise/entity/edit", handler.EntityEditHandler),
|
||||
("/enterprise/entity/info", handler.EntityInfoHandler),
|
||||
("/enterprise/entity/pwdcheck", handler.EntityPwdcheckHandler),
|
||||
|
||||
]
|
||||
|
||||
page_handlers = [
|
||||
|
||||
]
|
@ -0,0 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from website.handlers.system import handler
|
||||
|
||||
handlers = [
|
||||
# ("/", handler.Handler),
|
||||
("/system/version", handler.VersionHandler),
|
||||
("/system/identifycode", handler.IdentifycodeHandler),
|
||||
("/system/license/upload", handler.LicenseUploadHandler),
|
||||
("/system/activate/info", handler.ActivateInfoHandler),
|
||||
("/system/info", handler.InfoHandler),
|
||||
]
|
||||
|
||||
page_handlers = [
|
||||
]
|
@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import json
|
||||
import base64
|
||||
import tornado.web
|
||||
import uuid
|
||||
import time
|
||||
import datetime
|
||||
import itertools
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
from website import errors
|
||||
from website import settings
|
||||
from website import db
|
||||
from website import consts
|
||||
from website.handler import APIHandler
|
||||
from website.util import aes
|
||||
from website.util.captcha import create_validate_code
|
||||
from website.service.license import get_license_status
|
||||
from website.util import shortuuid
|
||||
from sqlalchemy import text
|
||||
import tornado.escape
|
||||
|
||||
|
||||
class CaptchaHandler(APIHandler):
|
||||
def get(self):
|
||||
self.set_header("Content-Type", "image/png")
|
||||
image, image_str = create_validate_code()
|
||||
c = uuid.uuid4().hex
|
||||
token = self.create_signed_value("logc", c)
|
||||
self.r_app.set("logincaptcha:%s" % c, image_str, ex=120)
|
||||
|
||||
buffered = BytesIO()
|
||||
# 保存验证码图片
|
||||
image.save(buffered, 'png')
|
||||
img_b64 = base64.b64encode(buffered.getvalue())
|
||||
|
||||
# for line in buffered.getvalue():
|
||||
# self.write(line)
|
||||
# output.close()
|
||||
self.finish({"token": self.tostr(token), "captcha": self.tostr(img_b64)})
|
||||
|
||||
class LogoutHandler(APIHandler):
|
||||
def get(self):
|
||||
if self.current_user:
|
||||
# self.db_app.insert(
|
||||
# "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
|
||||
# "values(%s, %s, %s, %s, %s, %s, %s)",
|
||||
# self.current_user.name, self.request.remote_ip, "平台管理中心", "账号管理", "登出", "系统登出", "系统登出"
|
||||
# )
|
||||
|
||||
self.r_app.delete(settings.session_key_prefix % self.current_user.uuid)
|
||||
self.finish()
|
||||
|
||||
class LoginHandler(APIHandler):
|
||||
def post(self):
|
||||
suid = shortuuid.ShortUUID().random(10)
|
||||
logging.info(suid)
|
||||
|
||||
username = self.get_escaped_argument("username")
|
||||
password = self.get_escaped_argument("pwd")
|
||||
# captcha = self.get_escaped_argument("captcha", "")
|
||||
# captcha_token = self.get_escaped_argument("captcha_token", "")
|
||||
|
||||
# wrong_time_lock = self.r_app.get("pwd:wrong:time:%s:lock" % self.tostr(username))
|
||||
# if wrong_time_lock:
|
||||
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "账号处于冷却期,请稍后再试")
|
||||
# return
|
||||
|
||||
logging.info(self.request.arguments)
|
||||
logging.info(password)
|
||||
logging.info("#########################")
|
||||
|
||||
# if not captcha:
|
||||
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请输入验证码")
|
||||
# if not captcha_token:
|
||||
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "缺少参数")
|
||||
|
||||
# c = tornado.web.decode_signed_value(
|
||||
# settings.cookie_secret,
|
||||
# "logc",
|
||||
# self.tostr(captcha_token)
|
||||
# )
|
||||
# code = self.r_app.get("logincaptcha:%s" % self.tostr(c))
|
||||
# 清除校验码缓存
|
||||
# self.r_app.delete("logincaptcha:%s" % c)
|
||||
# if not code:
|
||||
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码已过期")
|
||||
# 判断验证码与缓存是否一致
|
||||
# if self.tostr(captcha).lower() != self.tostr(code).lower():
|
||||
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码错误")
|
||||
|
||||
|
||||
username = self.tostr(username)
|
||||
password = self.tostr(password)
|
||||
|
||||
pwd_enc = aes.encrypt(settings.pwd_aes_key, password)
|
||||
|
||||
row = {}
|
||||
|
||||
with self.app_mysql.connect() as conn:
|
||||
cur = conn.execute(
|
||||
text("select id, uid, available from sys_user where name=:name and pwd=:pwd"),
|
||||
{"name": username, "pwd": pwd_enc}
|
||||
)
|
||||
# keys = list(cur.keys())
|
||||
#
|
||||
# one = cur.fetchone()
|
||||
# row = dict(zip(keys, one))
|
||||
# logging.info(db.Row(itertools.zip_longest(keys, one)))
|
||||
|
||||
row = db.to_json(cur)
|
||||
|
||||
cur.close()
|
||||
# data = [dict(zip(keys, res)) for res in cur.fetchall()]
|
||||
|
||||
if not row:
|
||||
# wrong_time = self.r_app.get("pwd:wrong:time:%s" % username)
|
||||
# logging.info(wrong_time)
|
||||
# logging.info(settings.pwd_error_limit - 1)
|
||||
# if wrong_time and int(wrong_time) > settings.pwd_error_limit - 1:
|
||||
# self.r_app.set("pwd:wrong:time:%s:lock" % username, 1, ex=3600)
|
||||
# self.r_app.delete("pwd:wrong:time:%s" % username)
|
||||
# else:
|
||||
# self.r_app.incr("pwd:wrong:time:%s" % username)
|
||||
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "用户名或者密码错误")
|
||||
return
|
||||
if row["available"] == 0:
|
||||
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "当前用户被禁用")
|
||||
return
|
||||
|
||||
# row_role = self.db_app.get("select role from user_role where userid=%s", row["id"])
|
||||
# user_role = row_role["role"]
|
||||
|
||||
userId = row["id"]
|
||||
jsessionid = row["uid"]
|
||||
|
||||
# create sign value admin_login_sign
|
||||
secure_cookie = self.create_signed_value(settings.secure_cookie_name, str(jsessionid))
|
||||
|
||||
self.r_app.set(
|
||||
settings.session_key_prefix % jsessionid,
|
||||
json.dumps({
|
||||
"id": userId,
|
||||
"name": username,
|
||||
"uuid": row["uid"],
|
||||
# "role": user_role
|
||||
}),
|
||||
ex=settings.session_ttl
|
||||
)
|
||||
|
||||
# self.db_app.insert(
|
||||
# "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
|
||||
# "values(%s, %s, %s, %s, %s, %s, %s)",
|
||||
# username, self.request.remote_ip, "平台管理中心", "账号管理", "登录", "系统登录", "系统登录"
|
||||
# )
|
||||
|
||||
# license_row = self.db_app.get(
|
||||
# "select expireat from license limit 1"
|
||||
# )
|
||||
|
||||
# system_status = get_license_status(license_row)
|
||||
|
||||
render_data = {
|
||||
"token": str(secure_cookie, encoding="utf-8"),
|
||||
# "role": user_role,
|
||||
"username": username,
|
||||
# "system_status": system_status, # 9000/未激活, 9001/已激活, 9002/过期可查看, 9003/完全过期
|
||||
}
|
||||
|
||||
self.finish(render_data)
|
||||
|
||||
|
||||
class UserInfoHandler(APIHandler):
|
||||
def post(self):
|
||||
token = self.get_argument("token")
|
||||
user = self.get_current_user(token_body=self.tostr(token))
|
||||
if not user:
|
||||
raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED)
|
||||
|
||||
self.finish({"name": user.name, "role": user.role})
|
@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from website.handlers.user import handler
|
||||
|
||||
handlers = [
|
||||
# ("/user/list", handler.UserListHandler),
|
||||
# ("/captcha", handler.CaptchaHandler),
|
||||
# ("/bodyargument", handler.BodyHandler),
|
||||
# ("/user/info", handler.UserInfoHandler),
|
||||
("/login", handler.LoginHandler),
|
||||
("/logout", handler.LogoutHandler),
|
||||
]
|
||||
|
||||
page_handlers = [
|
||||
|
||||
]
|
@ -0,0 +1,31 @@
|
||||
from website.handler import BaseHandler
|
||||
from sqlalchemy import text
|
||||
|
||||
# 获取企业模型数量
|
||||
def get_enterprise_model_count(id):
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
# 获取企业设备数量
|
||||
def get_enterprise_device_count(id):
|
||||
return 0
|
||||
|
||||
|
||||
# 获取所有企业实体数量
|
||||
def get_enterprise_entity_count(engine):
|
||||
with engine.connect() as conn:
|
||||
count_sql_text = "select count(id) from enterprise "
|
||||
count = conn.execute(text(count_sql_text)).fetchone()
|
||||
if count:
|
||||
return count[0]
|
||||
|
||||
return 0
|
||||
|
||||
# 获取所有企业模型数量
|
||||
def get_enterprise_model_count():
|
||||
return 0
|
||||
|
||||
# 获取所有企业设备数量
|
||||
def get_enterprise_device_count():
|
||||
return 0
|
@ -0,0 +1,32 @@
|
||||
import time
|
||||
import datetime
|
||||
import logging
|
||||
from website import consts, settings
|
||||
|
||||
def get_license_status(license):
|
||||
status = consts.system_status_not_active
|
||||
# if not license:
|
||||
# pass
|
||||
if license:
|
||||
now = datetime.datetime.now()
|
||||
timestamp_now = int(now.timestamp())
|
||||
|
||||
expireat = int(license["expireat"])
|
||||
expireat_datetime = datetime.datetime.fromtimestamp(expireat)
|
||||
expireat_next30days = expireat_datetime + datetime.timedelta(days=30)
|
||||
expireat_next30days_timestamp = int(expireat_next30days.timestamp())
|
||||
|
||||
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
|
||||
|
||||
# logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expireat)))
|
||||
# logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expireat_next30days_timestamp)))
|
||||
# logging.info(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp_now)))
|
||||
|
||||
if timestamp_now >= expireat_next30days_timestamp:
|
||||
status = consts.system_status_expire_atall
|
||||
elif timestamp_now >= expireat and timestamp_now < expireat_next30days_timestamp:
|
||||
status = consts.system_status_expire_but_ok
|
||||
elif timestamp_now < expireat:
|
||||
status = consts.system_status_activated
|
||||
|
||||
return status
|
@ -0,0 +1,17 @@
|
||||
import os
|
||||
import sys
|
||||
import importlib
|
||||
|
||||
handlers = []
|
||||
# handlers_v2 = []
|
||||
page_handlers = []
|
||||
|
||||
handlers_path = os.path.join(os.getcwd(), "handlers")
|
||||
sys.path.append(handlers_path)
|
||||
|
||||
handlers_dir = os.listdir(handlers_path)
|
||||
for item in handlers_dir:
|
||||
if os.path.isdir(os.path.join(handlers_path, item)):
|
||||
hu = importlib.import_module("{}.url".format(item))
|
||||
handlers.extend(hu.handlers)
|
||||
page_handlers.extend(hu.page_handlers)
|
@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from M2Crypto import RSA
|
||||
from M2Crypto import BIO
|
||||
from binascii import a2b_hex, b2a_hex
|
||||
|
||||
def load_pub_key_string(string):
|
||||
bio = BIO.MemoryBuffer(string)
|
||||
return RSA.load_pub_key_bio(bio)
|
||||
|
||||
def block_data(texts, block_size):
|
||||
for i in range(0, len(texts), block_size):
|
||||
yield texts[i:i + block_size]
|
||||
|
||||
def decrypt(publick_key, texts):
|
||||
plaintext = b""
|
||||
block_size = 256
|
||||
|
||||
for text in block_data(a2b_hex(texts), block_size):
|
||||
current_text = publick_key.public_decrypt(text, RSA.pkcs1_padding)
|
||||
plaintext += current_text
|
||||
|
||||
return plaintext
|
@ -0,0 +1,137 @@
|
||||
"""Concise UUID generation."""
|
||||
|
||||
import math
|
||||
import secrets
|
||||
import uuid as _uu
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def int_to_string(
|
||||
number: int, alphabet: List[str], padding: Optional[int] = None
|
||||
) -> str:
|
||||
"""
|
||||
Convert a number to a string, using the given alphabet.
|
||||
|
||||
The output has the most significant digit first.
|
||||
"""
|
||||
output = ""
|
||||
alpha_len = len(alphabet)
|
||||
while number:
|
||||
number, digit = divmod(number, alpha_len)
|
||||
output += alphabet[digit]
|
||||
if padding:
|
||||
remainder = max(padding - len(output), 0)
|
||||
output = output + alphabet[0] * remainder
|
||||
return output[::-1]
|
||||
|
||||
|
||||
def string_to_int(string: str, alphabet: List[str]) -> int:
|
||||
"""
|
||||
Convert a string to a number, using the given alphabet.
|
||||
|
||||
The input is assumed to have the most significant digit first.
|
||||
"""
|
||||
number = 0
|
||||
alpha_len = len(alphabet)
|
||||
for char in string:
|
||||
number = number * alpha_len + alphabet.index(char)
|
||||
return number
|
||||
|
||||
|
||||
class ShortUUID(object):
|
||||
def __init__(self, alphabet: Optional[str] = None) -> None:
|
||||
if alphabet is None:
|
||||
alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" "abcdefghijkmnopqrstuvwxyz"
|
||||
|
||||
self.set_alphabet(alphabet)
|
||||
|
||||
@property
|
||||
def _length(self) -> int:
|
||||
"""Return the necessary length to fit the entire UUID given the current alphabet."""
|
||||
return int(math.ceil(math.log(2**128, self._alpha_len)))
|
||||
|
||||
def encode(self, uuid: _uu.UUID, pad_length: Optional[int] = None) -> str:
|
||||
"""
|
||||
Encode a UUID into a string (LSB first) according to the alphabet.
|
||||
|
||||
If leftmost (MSB) bits are 0, the string might be shorter.
|
||||
"""
|
||||
if not isinstance(uuid, _uu.UUID):
|
||||
raise ValueError("Input `uuid` must be a UUID object.")
|
||||
if pad_length is None:
|
||||
pad_length = self._length
|
||||
return int_to_string(uuid.int, self._alphabet, padding=pad_length)
|
||||
|
||||
def decode(self, string: str, legacy: bool = False) -> _uu.UUID:
|
||||
"""
|
||||
Decode a string according to the current alphabet into a UUID.
|
||||
|
||||
Raises ValueError when encountering illegal characters or a too-long string.
|
||||
|
||||
If string too short, fills leftmost (MSB) bits with 0.
|
||||
|
||||
Pass `legacy=True` if your UUID was encoded with a ShortUUID version prior to
|
||||
1.0.0.
|
||||
"""
|
||||
if not isinstance(string, str):
|
||||
raise ValueError("Input `string` must be a str.")
|
||||
if legacy:
|
||||
string = string[::-1]
|
||||
return _uu.UUID(int=string_to_int(string, self._alphabet))
|
||||
|
||||
def uuid(self, name: Optional[str] = None, pad_length: Optional[int] = None) -> str:
|
||||
"""
|
||||
Generate and return a UUID.
|
||||
|
||||
If the name parameter is provided, set the namespace to the provided
|
||||
name and generate a UUID.
|
||||
"""
|
||||
if pad_length is None:
|
||||
pad_length = self._length
|
||||
|
||||
# If no name is given, generate a random UUID.
|
||||
if name is None:
|
||||
u = _uu.uuid4()
|
||||
elif name.lower().startswith(("http://", "https://")):
|
||||
u = _uu.uuid5(_uu.NAMESPACE_URL, name)
|
||||
else:
|
||||
u = _uu.uuid5(_uu.NAMESPACE_DNS, name)
|
||||
return self.encode(u, pad_length)
|
||||
|
||||
def random(self, length: Optional[int] = None) -> str:
|
||||
"""Generate and return a cryptographically secure short random string of `length`."""
|
||||
if length is None:
|
||||
length = self._length
|
||||
|
||||
return "".join(secrets.choice(self._alphabet) for _ in range(length))
|
||||
|
||||
def get_alphabet(self) -> str:
|
||||
"""Return the current alphabet used for new UUIDs."""
|
||||
return "".join(self._alphabet)
|
||||
|
||||
def set_alphabet(self, alphabet: str) -> None:
|
||||
"""Set the alphabet to be used for new UUIDs."""
|
||||
# Turn the alphabet into a set and sort it to prevent duplicates
|
||||
# and ensure reproducibility.
|
||||
new_alphabet = list(sorted(set(alphabet)))
|
||||
if len(new_alphabet) > 1:
|
||||
self._alphabet = new_alphabet
|
||||
self._alpha_len = len(self._alphabet)
|
||||
else:
|
||||
raise ValueError("Alphabet with more than " "one unique symbols required.")
|
||||
|
||||
def encoded_length(self, num_bytes: int = 16) -> int:
|
||||
"""Return the string length of the shortened UUID."""
|
||||
factor = math.log(256) / math.log(self._alpha_len)
|
||||
return int(math.ceil(factor * num_bytes))
|
||||
|
||||
|
||||
# For backwards compatibility
|
||||
_global_instance = ShortUUID()
|
||||
encode = _global_instance.encode
|
||||
decode = _global_instance.decode
|
||||
uuid = _global_instance.uuid
|
||||
random = _global_instance.random
|
||||
get_alphabet = _global_instance.get_alphabet
|
||||
set_alphabet = _global_instance.set_alphabet
|
@ -0,0 +1,61 @@
|
||||
import subprocess
|
||||
import socket
|
||||
import hashlib
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
def get_cpu_id():
|
||||
p = subprocess.Popen(["dmidecode -t 4 | grep ID"],
|
||||
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
data = p.stdout
|
||||
lines = []
|
||||
while True:
|
||||
line = str(data.readline(), encoding="utf-8")
|
||||
if line == '\n':
|
||||
break
|
||||
if line:
|
||||
d = dict([line.strip().split(': ')])
|
||||
lines.append(d)
|
||||
else:
|
||||
break
|
||||
return lines
|
||||
|
||||
def get_board_serialnumber():
|
||||
p = subprocess.Popen(["dmidecode -t 2 | grep Serial"],
|
||||
shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
data = p.stdout
|
||||
lines = []
|
||||
while True:
|
||||
line = str(data.readline(), encoding="utf-8")
|
||||
if line == '\n':
|
||||
break
|
||||
if line:
|
||||
d = dict([line.strip().split(': ')])
|
||||
lines.append(d)
|
||||
else:
|
||||
break
|
||||
return lines
|
||||
|
||||
def get_identify_code():
|
||||
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
|
||||
mac_addr = ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
|
||||
|
||||
host_name = socket.getfqdn(socket.gethostname())
|
||||
cpu_ids = get_cpu_id()
|
||||
serialnumbers = get_board_serialnumber()
|
||||
|
||||
s = ""
|
||||
if mac_addr:
|
||||
s += mac_addr
|
||||
if host_name:
|
||||
s += host_name
|
||||
if cpu_ids:
|
||||
for cpu in cpu_ids:
|
||||
s += cpu["ID"]
|
||||
if serialnumbers:
|
||||
for number in serialnumbers:
|
||||
s += number["Serial Number"]
|
||||
logging.info(s)
|
||||
|
||||
code = hashlib.new('md5', s.encode("utf8")).hexdigest()
|
||||
return code
|
@ -0,0 +1,103 @@
|
||||
# -*- coding:utf8 -*-
|
||||
|
||||
import io
|
||||
|
||||
import pandas as pd
|
||||
import tornado.gen as gen
|
||||
import xlwt
|
||||
|
||||
|
||||
class Excel(object):
|
||||
@gen.coroutine
|
||||
def generate_excel(self, head, rows):
|
||||
"""
|
||||
head is a dict, eg: [(0, u"编号"), (1, u"地址")]
|
||||
rows is detail list, eg: [[0, "XXX"], ...]
|
||||
"""
|
||||
workbook = xlwt.Workbook(encoding='utf-8')
|
||||
worksheet = workbook.add_sheet("sheet1")
|
||||
row_num = 0
|
||||
# col_num = 0
|
||||
|
||||
for item in head:
|
||||
worksheet.write(row_num, head.index(item), item[1])
|
||||
|
||||
for row in rows:
|
||||
row_num += 1
|
||||
col_num = 0
|
||||
for col in row:
|
||||
worksheet.write(row_num, col_num, col)
|
||||
col_num += 1
|
||||
|
||||
sio = io.BytesIO()
|
||||
workbook.save(sio)
|
||||
|
||||
raise gen.Return(sio)
|
||||
|
||||
@gen.coroutine
|
||||
# def generate_excel_pd(self, index, data, columns):
|
||||
def generate_excel_pd(self, pd_data):
|
||||
"""
|
||||
pandas 构建图表
|
||||
"""
|
||||
|
||||
sio = io.StringIO()
|
||||
writer = pd.ExcelWriter(sio, engine='xlsxwriter')
|
||||
|
||||
for data in pd_data:
|
||||
df = pd.DataFrame(data=data["data"], index=data["index"], columns=data["columns"])
|
||||
sheet_name = data["sheet_name"]
|
||||
|
||||
df.to_excel(writer, sheet_name=sheet_name)
|
||||
|
||||
workbook = writer.book
|
||||
worksheet = writer.sheets[sheet_name]
|
||||
|
||||
chart = workbook.add_chart({'type': 'line'})
|
||||
|
||||
max_row = len(df) + 1
|
||||
|
||||
for i in range(len(data['columns'])):
|
||||
col = i + 1
|
||||
chart.add_series({
|
||||
# 'name': ['Sheet1', 0, col],
|
||||
'name': [sheet_name, 0, col],
|
||||
'categories': [sheet_name, 1, 0, max_row, 0],
|
||||
'values': [sheet_name, 1, col, max_row, col],
|
||||
'line': {'width': 1.00},
|
||||
})
|
||||
|
||||
chart.set_x_axis({'name': 'Date', 'date_axis': True})
|
||||
chart.set_y_axis({'name': 'Statistics', 'major_gridlines': {'visible': False}})
|
||||
|
||||
chart.set_legend({'position': 'top'})
|
||||
|
||||
worksheet.insert_chart('H2', chart)
|
||||
|
||||
# df = pd.DataFrame(data=data, index=index, columns=columns)
|
||||
|
||||
"""
|
||||
# ================ anothor method =================
|
||||
# workbook.save(sio)
|
||||
|
||||
io = StringIO.StringIO()
|
||||
|
||||
# Use a temp filename to keep pandas happy.
|
||||
writer = pd.ExcelWriter('temp.xls', engine='xlsxwriter')
|
||||
|
||||
# Set the filename/file handle in the xlsxwriter.workbook object.
|
||||
writer.book.filename = io
|
||||
#
|
||||
# Write the data frame to the StringIO object.
|
||||
df.to_excel(writer, sheet_name='Sheet1')
|
||||
writer.save()
|
||||
|
||||
xlsx_data = io.getvalue()
|
||||
# ================ anothor method =================
|
||||
"""
|
||||
|
||||
# sheet_name = 'Sheet1'
|
||||
|
||||
writer.save()
|
||||
|
||||
raise gen.Return(sio)
|
Loading…
Reference in New Issue