2021-02-06 08:58:58 +08:00
|
|
|
#!/usr/local/bin/python3
|
|
|
|
|
# coding: utf-8
|
|
|
|
|
|
|
|
|
|
# YYeTsBot - server.py
|
|
|
|
|
# 2/5/21 21:02
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
__author__ = "Benny <benny.think@gmail.com>"
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import contextlib
|
2021-02-07 10:33:12 +08:00
|
|
|
import logging
|
2021-02-07 09:32:23 +08:00
|
|
|
|
|
|
|
|
import pymongo
|
2021-02-06 17:21:33 +08:00
|
|
|
from http import HTTPStatus
|
2021-02-06 08:58:58 +08:00
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
|
from tornado import web, ioloop, httpserver, gen, options
|
|
|
|
|
from tornado.log import enable_pretty_logging
|
2021-02-07 09:32:23 +08:00
|
|
|
|
2021-02-06 08:58:58 +08:00
|
|
|
from tornado.concurrent import run_on_executor
|
2021-02-07 09:32:23 +08:00
|
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
2021-02-06 08:58:58 +08:00
|
|
|
|
2021-02-07 10:33:12 +08:00
|
|
|
from crypto import decrypt
|
|
|
|
|
|
2021-02-06 08:58:58 +08:00
|
|
|
enable_pretty_logging()
|
2021-02-06 19:58:40 +08:00
|
|
|
|
|
|
|
|
mongo_host = os.getenv("mongo") or "localhost"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Mongo:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.client = pymongo.MongoClient(host=mongo_host, connect=False)
|
|
|
|
|
self.db = self.client["zimuzu"]
|
|
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
|
self.client.close()
|
2021-02-06 08:58:58 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class BaseHandler(web.RequestHandler):
|
2021-02-06 19:58:40 +08:00
|
|
|
mongo = Mongo()
|
|
|
|
|
|
2021-02-06 08:58:58 +08:00
|
|
|
def data_received(self, chunk):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class IndexHandler(BaseHandler):
|
|
|
|
|
@gen.coroutine
|
|
|
|
|
def get(self):
|
|
|
|
|
with open("index.html") as f:
|
|
|
|
|
html = f.read()
|
|
|
|
|
self.write(html)
|
|
|
|
|
|
|
|
|
|
|
2021-02-07 10:33:12 +08:00
|
|
|
def anti_crawler(self) -> bool:
|
|
|
|
|
cypertext = self.request.headers.get("ne1", "")
|
|
|
|
|
referer = self.request.headers.get("Referer")
|
|
|
|
|
param = self.get_query_argument("id")
|
2021-02-07 17:44:05 +08:00
|
|
|
uri = self.request.uri
|
|
|
|
|
logging.info("Verifying: Referer:[%s] ct:[%s], uri:[%s], id:[%s]", referer, cypertext, uri, param)
|
2021-02-07 10:33:12 +08:00
|
|
|
|
|
|
|
|
if (referer is None) or (param not in referer):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
passphrase = param
|
|
|
|
|
result = decrypt(cypertext, passphrase).decode('u8')
|
|
|
|
|
except Exception:
|
|
|
|
|
logging.error("Decrypt failed")
|
|
|
|
|
result = ""
|
|
|
|
|
|
|
|
|
|
if result != self.request.uri:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
2021-02-06 08:58:58 +08:00
|
|
|
class ResourceHandler(BaseHandler):
|
|
|
|
|
executor = ThreadPoolExecutor(50)
|
|
|
|
|
|
|
|
|
|
@run_on_executor()
|
|
|
|
|
def get_resource_data(self):
|
2021-02-07 10:33:12 +08:00
|
|
|
if anti_crawler(self):
|
|
|
|
|
# X-Real-IP
|
|
|
|
|
logging.info("%s@%s make you happy:-(", self.request.headers.get("user-agent"),
|
|
|
|
|
self.request.headers.get("X-Real-IP")
|
|
|
|
|
)
|
|
|
|
|
return {}
|
2021-02-06 08:58:58 +08:00
|
|
|
param = self.get_query_argument("id")
|
|
|
|
|
with contextlib.suppress(ValueError):
|
|
|
|
|
param = int(param)
|
2021-02-06 19:58:40 +08:00
|
|
|
data = self.mongo.db["yyets"].find_one_and_update(
|
2021-02-06 08:58:58 +08:00
|
|
|
{"data.info.id": param},
|
|
|
|
|
{'$inc': {'data.info.views': 1}},
|
|
|
|
|
{'_id': False})
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
@run_on_executor()
|
|
|
|
|
def search_resource(self):
|
|
|
|
|
param = self.get_query_argument("kw").lower()
|
|
|
|
|
projection = {'_id': False,
|
|
|
|
|
'data.info': True,
|
|
|
|
|
}
|
2021-02-07 18:48:00 +08:00
|
|
|
print(111,param)
|
2021-02-06 19:58:40 +08:00
|
|
|
data = self.mongo.db["yyets"].find({
|
2021-02-06 08:58:58 +08:00
|
|
|
"$or": [
|
2021-02-07 18:48:00 +08:00
|
|
|
{"data.info.cnname": {'$regex': f'.*{param}.*', "$options": "-i"}},
|
|
|
|
|
{"data.info.enname": {'$regex': f'.*{param}.*', "$options": "-i"}},
|
|
|
|
|
{"data.info.aliasname": {'$regex': f'.*{param}.*', "$options": "-i"}},
|
2021-02-06 08:58:58 +08:00
|
|
|
]},
|
|
|
|
|
projection
|
|
|
|
|
)
|
|
|
|
|
return dict(data=list(data))
|
|
|
|
|
|
|
|
|
|
@gen.coroutine
|
|
|
|
|
def get(self):
|
|
|
|
|
if self.get_query_argument("id", None):
|
|
|
|
|
resp = yield self.get_resource_data()
|
|
|
|
|
elif self.get_query_argument("kw", None):
|
|
|
|
|
resp = yield self.search_resource()
|
|
|
|
|
else:
|
|
|
|
|
resp = "error"
|
|
|
|
|
self.write(resp)
|
|
|
|
|
|
|
|
|
|
|
2021-02-06 11:35:28 +08:00
|
|
|
class TopHandler(BaseHandler):
|
|
|
|
|
executor = ThreadPoolExecutor(50)
|
|
|
|
|
|
|
|
|
|
@run_on_executor()
|
|
|
|
|
def get_top_resource(self):
|
|
|
|
|
top_type = self.get_query_argument("type", "all")
|
|
|
|
|
projection = {'_id': False,
|
|
|
|
|
'data.info': True,
|
|
|
|
|
}
|
|
|
|
|
if top_type == "all":
|
2021-02-06 19:58:40 +08:00
|
|
|
data = self.mongo.db["yyets"].find({}, projection).sort("data.info.views", pymongo.DESCENDING).limit(10)
|
2021-02-06 11:35:28 +08:00
|
|
|
else:
|
|
|
|
|
data = []
|
|
|
|
|
return dict(data=list(data))
|
|
|
|
|
|
|
|
|
|
@gen.coroutine
|
|
|
|
|
def get(self):
|
|
|
|
|
resp = yield self.get_top_resource()
|
|
|
|
|
self.write(resp)
|
|
|
|
|
|
|
|
|
|
|
2021-02-06 17:21:33 +08:00
|
|
|
class MetricsHandler(BaseHandler):
|
2021-02-06 08:58:58 +08:00
|
|
|
executor = ThreadPoolExecutor(50)
|
|
|
|
|
|
|
|
|
|
@run_on_executor()
|
2021-02-06 17:21:33 +08:00
|
|
|
def set_metrics(self):
|
2021-02-07 10:33:12 +08:00
|
|
|
self.mongo.db['metrics'].update_one(
|
2021-02-07 09:32:23 +08:00
|
|
|
{'type': "access"}, {'$inc': {'count': 1}},
|
|
|
|
|
upsert=True
|
|
|
|
|
)
|
|
|
|
|
# today
|
2021-02-07 10:33:12 +08:00
|
|
|
self.mongo.db['metrics'].update_one(
|
2021-02-07 09:32:23 +08:00
|
|
|
{'type': "today"}, {'$inc': {'count': 1}},
|
|
|
|
|
upsert=True
|
2021-02-06 17:21:33 +08:00
|
|
|
)
|
|
|
|
|
self.set_status(HTTPStatus.CREATED)
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
@run_on_executor()
|
|
|
|
|
def get_metrics(self):
|
2021-02-07 09:32:23 +08:00
|
|
|
result = self.mongo.db['metrics'].find({}, {'_id': False})
|
|
|
|
|
return dict(metrics=list(result))
|
2021-02-06 08:58:58 +08:00
|
|
|
|
|
|
|
|
@gen.coroutine
|
|
|
|
|
def get(self):
|
2021-02-06 17:21:33 +08:00
|
|
|
resp = yield self.get_metrics()
|
|
|
|
|
self.write(resp)
|
|
|
|
|
|
|
|
|
|
@gen.coroutine
|
|
|
|
|
def post(self):
|
|
|
|
|
resp = yield self.set_metrics()
|
2021-02-06 08:58:58 +08:00
|
|
|
self.write(resp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RunServer:
|
|
|
|
|
root_path = os.path.dirname(__file__)
|
|
|
|
|
static_path = os.path.join(root_path, '')
|
|
|
|
|
handlers = [
|
|
|
|
|
(r'/api/resource', ResourceHandler),
|
2021-02-06 11:35:28 +08:00
|
|
|
(r'/api/top', TopHandler),
|
2021-02-06 17:21:33 +08:00
|
|
|
(r'/api/metrics', MetricsHandler),
|
2021-02-06 08:58:58 +08:00
|
|
|
(r'/', IndexHandler),
|
|
|
|
|
(r'/(.*\.html|.*\.js|.*\.css|.*\.png|.*\.jpg|.*\.ico|.*\.gif|.*\.woff2)', web.StaticFileHandler,
|
|
|
|
|
{'path': static_path}),
|
|
|
|
|
]
|
|
|
|
|
|
2021-02-06 19:58:40 +08:00
|
|
|
application = web.Application(handlers, xheaders=True)
|
2021-02-06 08:58:58 +08:00
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def run_server(port, host, **kwargs):
|
|
|
|
|
tornado_server = httpserver.HTTPServer(RunServer.application, **kwargs)
|
|
|
|
|
tornado_server.bind(port, host)
|
2021-02-06 19:58:40 +08:00
|
|
|
tornado_server.start(0)
|
2021-02-06 08:58:58 +08:00
|
|
|
|
|
|
|
|
try:
|
2021-02-06 19:58:40 +08:00
|
|
|
print('Server is running on http://{}:{}'.format(host, port))
|
2021-02-06 08:58:58 +08:00
|
|
|
ioloop.IOLoop.instance().current().start()
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
ioloop.IOLoop.instance().stop()
|
|
|
|
|
print('"Ctrl+C" received, exiting.\n')
|
|
|
|
|
|
|
|
|
|
|
2021-02-07 09:32:23 +08:00
|
|
|
def reset_day():
|
|
|
|
|
m = Mongo()
|
|
|
|
|
m.db["metrics"].delete_one({"type": "today"})
|
|
|
|
|
|
|
|
|
|
|
2021-02-06 08:58:58 +08:00
|
|
|
if __name__ == "__main__":
|
2021-02-07 09:32:23 +08:00
|
|
|
scheduler = BackgroundScheduler()
|
|
|
|
|
scheduler.add_job(reset_day, 'cron', hour=0, minute=0)
|
|
|
|
|
scheduler.start()
|
2021-02-06 08:58:58 +08:00
|
|
|
options.define("p", default=8888, help="running port", type=int)
|
|
|
|
|
options.define("h", default='127.0.0.1', help="listen address", type=str)
|
|
|
|
|
options.parse_command_line()
|
|
|
|
|
p = options.options.p
|
|
|
|
|
h = options.options.h
|
|
|
|
|
RunServer.run_server(port=p, host=h)
|