224 lines
9.6 KiB
Python
224 lines
9.6 KiB
Python
# coding=utf-8
|
||
|
||
from flask import Flask
|
||
import logging
|
||
import os
|
||
from concurrent_log_handler import ConcurrentRotatingFileHandler
|
||
from queue import Queue
|
||
from logging.handlers import QueueHandler, QueueListener
|
||
import click
|
||
|
||
from app.config import Config, proj_dir
|
||
from app.extensions import (db, migrate, login_manager, mail, csrf, bootstrap, session_id_manager,
|
||
socketio, dispatcher_scheduler)
|
||
from app import models
|
||
from app.template_global import (render_to_json, sort_by_order_in_module, sort_by_order_in_logic_controller,
|
||
can_show, is_forbidden, get_latest_reports, get_case_from_id, get_tool_from_id,
|
||
calc_percent, url_for_static)
|
||
from app.cores.ws import register_all_user_socket
|
||
|
||
|
||
def create_app(config_class=Config):
|
||
app = Flask(import_name=__name__)
|
||
app.config.from_object(obj=config_class)
|
||
|
||
register_logging(app)
|
||
register_extensions(app)
|
||
register_blueprints(app)
|
||
register_shell_context(app)
|
||
register_template_global(app)
|
||
register_template_context_processor(app)
|
||
register_before_first_request_funcs(app)
|
||
register_cli(app)
|
||
|
||
return app
|
||
|
||
|
||
def register_blueprints(app: Flask):
|
||
from app.routes.auth import bp as auth_bp
|
||
app.register_blueprint(blueprint=auth_bp, url_prefix='/auth')
|
||
from app.routes.main import bp as main_bp
|
||
app.register_blueprint(blueprint=main_bp)
|
||
from app.routes.case import bp as case_bp
|
||
app.register_blueprint(blueprint=case_bp, url_prefix='/case')
|
||
from app.routes.project import bp as project_bp
|
||
app.register_blueprint(blueprint=project_bp, url_prefix='/project')
|
||
from app.routes.module import bp as module_bp
|
||
app.register_blueprint(blueprint=module_bp, url_prefix='/module')
|
||
from app.routes.logic_controller import bp as logic_controller_bp
|
||
app.register_blueprint(blueprint=logic_controller_bp, url_prefix='/logic_controller')
|
||
from app.routes.scene import bp as scene_bp
|
||
app.register_blueprint(blueprint=scene_bp, url_prefix='/scene')
|
||
from app.routes.report import bp as report_bp
|
||
app.register_blueprint(blueprint=report_bp, url_prefix='/report')
|
||
from app.routes.setting import bp as settings_bp
|
||
app.register_blueprint(blueprint=settings_bp)
|
||
from app.routes.error import bp as errors_bp
|
||
app.register_blueprint(blueprint=errors_bp, url_prefix='/error')
|
||
from app.routes.ajax import bp as ajax_bp
|
||
app.register_blueprint(blueprint=ajax_bp, url_prefix='/ajax')
|
||
from app.utils import bp as util_bp
|
||
app.register_blueprint(blueprint=util_bp, url_prefix='/util')
|
||
|
||
|
||
def register_extensions(app: Flask):
|
||
db.init_app(app=app)
|
||
migrate.init_app(app=app, db=db)
|
||
login_manager.init_app(app=app)
|
||
mail.init_app(app=app)
|
||
csrf.init_app(app=app)
|
||
bootstrap.init_app(app=app)
|
||
socketio.init_app(app=app)
|
||
session_id_manager.init_app(app=app)
|
||
dispatcher_scheduler.init_app(app=app)
|
||
|
||
|
||
def register_shell_context(app: Flask):
|
||
from app.models import (db, User, EmailSetting, Case, HTTPCase, HTTPCaseParameter, HTTPCaseExpectation,
|
||
HTTPCaseFileUpload, Project, Module, Scene, Dispatcher, DispatcherDetail, Report,
|
||
ReportCaseData, ReportCaseExpectationData, SubElementInLogicController,
|
||
ReportToolData, ReportScriptToolData, ReportTimerToolData, ReportVariableDefinitionToolData,
|
||
ReportVariableDefinitionToolListData, Tool)
|
||
from app.email import send_email
|
||
from app.utils.util import get_project_id_from_current_element
|
||
|
||
@app.shell_context_processor
|
||
def make_shell_context():
|
||
return {
|
||
'db': db,
|
||
'User': User,
|
||
'send_email': send_email,
|
||
'EmailSetting': EmailSetting,
|
||
'Case': Case,
|
||
'HTTPCase': HTTPCase,
|
||
'HTTPCaseParameter': HTTPCaseParameter,
|
||
'HTTPCaseExpectation': HTTPCaseExpectation,
|
||
'HTTPCaseFileUpload': HTTPCaseFileUpload,
|
||
'Project': Project,
|
||
'Module': Module,
|
||
'Scene': Scene,
|
||
'Tool': Tool,
|
||
'Dispatcher': Dispatcher,
|
||
'DispatcherDetail': DispatcherDetail,
|
||
'SubElementInLogicController': SubElementInLogicController,
|
||
'ReportToolData': ReportToolData,
|
||
'ReportScriptToolData': ReportScriptToolData,
|
||
'ReportTimerToolData': ReportTimerToolData,
|
||
'ReportVariableDefinitionToolData': ReportVariableDefinitionToolData,
|
||
'ReportVariableDefinitionToolListData': ReportVariableDefinitionToolListData,
|
||
'get_project_id_from_current_element': get_project_id_from_current_element,
|
||
}
|
||
|
||
|
||
def register_logging(app: Flask):
|
||
# 日志记录
|
||
if not os.path.exists(os.path.join(proj_dir, 'logs')):
|
||
os.mkdir(os.path.join(proj_dir, 'logs'))
|
||
# 改用ConcurrentRotatingFileHandler避免出现多进程时出现文件争用情况
|
||
file_handler = ConcurrentRotatingFileHandler(filename=os.path.join(proj_dir, 'logs', 'webserver.log'),
|
||
maxBytes=1024 * 1024 * 10,
|
||
backupCount=10)
|
||
file_sys_handler = ConcurrentRotatingFileHandler(filename=os.path.join(proj_dir, 'logs', 'webserver_sys.log'),
|
||
maxBytes=1024 * 1024 * 10,
|
||
backupCount=10)
|
||
stream_handler = logging.StreamHandler()
|
||
formater = logging.Formatter(
|
||
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
|
||
)
|
||
file_handler.setFormatter(formater)
|
||
file_sys_handler.setFormatter(formater)
|
||
stream_handler.setFormatter(formater)
|
||
file_handler.setLevel(logging.INFO)
|
||
file_sys_handler.setLevel(logging.DEBUG)
|
||
stream_handler.setLevel(logging.DEBUG)
|
||
|
||
logger_queue = Queue(-1)
|
||
logger_queue_handler = QueueHandler(logger_queue)
|
||
logger_queue_listener = QueueListener(logger_queue,
|
||
file_sys_handler, file_handler, stream_handler,
|
||
respect_handler_level=True)
|
||
logger_queue_listener.start()
|
||
|
||
# app.logger
|
||
for handler in app.logger.handlers:
|
||
app.logger.removeHandler(handler)
|
||
app.logger.addHandler(logger_queue_handler)
|
||
app.logger.setLevel(logging.INFO)
|
||
app.logger.propagate = False # 记录消息将不会传递给当前记录器的祖先记录器的处理器
|
||
app.logger.info("WebServer 服务已启动")
|
||
|
||
# 框架默认logger指定输出handler(包含了http请求日志、定时任务job日志、socket日志)
|
||
logger = logging.getLogger()
|
||
logger.addHandler(logger_queue_handler)
|
||
logger.setLevel(logging.DEBUG)
|
||
|
||
|
||
def register_template_global(app: Flask):
|
||
# 注册jinja全局函数
|
||
app.add_template_global(render_to_json, 'render_to_json')
|
||
app.add_template_global(sort_by_order_in_module, 'sort_by_order_in_module')
|
||
app.add_template_global(can_show, 'can_show')
|
||
app.add_template_global(sort_by_order_in_logic_controller, 'sort_by_order_in_logic_controller')
|
||
app.add_template_global(get_latest_reports, 'get_latest_reports')
|
||
app.add_template_global(is_forbidden, 'is_forbidden')
|
||
app.add_template_global(get_case_from_id, 'get_case_from_id')
|
||
app.add_template_global(get_tool_from_id, 'get_tool_from_id')
|
||
app.add_template_global(calc_percent, 'calc_percent')
|
||
app.add_template_global(url_for_static, 'url_for_static')
|
||
|
||
|
||
def register_template_context_processor(app: Flask):
|
||
# 注册jinja模板上下文
|
||
@app.context_processor
|
||
def context_processor():
|
||
return {
|
||
'version': app.config['AAT_VERSION'],
|
||
}
|
||
|
||
|
||
def register_before_first_request_funcs(app: Flask):
|
||
# 在app实例拉起后,第一个请求处理前进行一些准备工作
|
||
# 注册所有用户socket
|
||
app.before_first_request_funcs.append(register_all_user_socket)
|
||
|
||
|
||
def register_cli(app: Flask):
|
||
"""注册命令"""
|
||
@app.cli.command(short_help='初始化当前项目.')
|
||
def init():
|
||
from flask_migrate import init, migrate, upgrade
|
||
click.echo('[flask init] 开始初始化数据库.')
|
||
click.echo('[flask init] init.')
|
||
init()
|
||
click.echo('[flask init] migrate.')
|
||
migrate(message='init db')
|
||
click.echo('[flask init] upgrade.')
|
||
upgrade()
|
||
click.echo('[flask init] add user admin.')
|
||
from app.models import User
|
||
User.add(username='admin', password='admin', email='admin@example.com')
|
||
click.echo('[flask init] 已完成数据库初始化.')
|
||
|
||
@app.cli.command(short_help='测试邮件服务.')
|
||
@click.option('--receiver', '-r', help='收件人邮箱.')
|
||
def email_test(receiver: str):
|
||
from app.email import send_email
|
||
import traceback
|
||
if receiver is None or receiver.strip() == '':
|
||
click.echo('[flask email-test] 请输入收件人邮箱 eg: flask email-test -r zhangsan@test.com')
|
||
return
|
||
click.echo('[flask email-test] 开始测试email服务.')
|
||
click.echo('[flask email-test] 收件人邮箱: %s.' % receiver)
|
||
try:
|
||
send_email(
|
||
subject="[ApiAutomationTest] 通知",
|
||
sender=app.config['ADMINS'][0],
|
||
recipients=[receiver],
|
||
text_body='您已收到来自ApiAutomationTest的邮件.',
|
||
async_send=False,
|
||
)
|
||
except Exception:
|
||
click.echo('[flask email-test] 邮件发送失败,失败原因: \n%s' % traceback.format_exc())
|
||
else:
|
||
click.echo('[flask email-test] 测试邮件已发送,请查看收件邮箱.')
|