app.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. # -*- coding: utf-8 -*-
  2. """The app module, containing the app factory function."""
  3. import logging
  4. import sys
  5. import os
  6. from flask import Flask, render_template, current_app
  7. from flask_restful import Api
  8. from walle import commands
  9. from walle.api import access as AccessAPI
  10. from walle.api import api as BaseAPI
  11. from walle.api import deploy as DeployAPI
  12. from walle.api import environment as EnvironmentAPI
  13. from walle.api import general as GeneralAPI
  14. from walle.api import group as GroupAPI
  15. from walle.api import passport as PassportAPI
  16. from walle.api import project as ProjectAPI
  17. from walle.api import repo as RepoApi
  18. from walle.api import role as RoleAPI
  19. from walle.api import server as ServerAPI
  20. from walle.api import space as SpaceAPI
  21. from walle.api import task as TaskAPI
  22. from walle.api import user as UserAPI
  23. from walle.api.api import ApiResource
  24. from walle.config.settings_prod import ProdConfig
  25. from walle.model.user import UserModel
  26. from walle.service.code import Code
  27. from walle.service.error import WalleError
  28. from walle.service.extensions import bcrypt, csrf_protect, db, migrate
  29. from walle.service.extensions import login_manager, mail, permission, socketio
  30. from walle.service.websocket import WalleSocketIO
  31. def create_app(config_object=ProdConfig):
  32. """An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.
  33. :param config_object: The configuration object to use.
  34. """
  35. app = Flask(__name__.split('.')[0])
  36. app.config.from_object(config_object)
  37. register_extensions(app)
  38. register_blueprints(app)
  39. register_errorhandlers(app)
  40. register_shellcontext(app)
  41. register_commands(app)
  42. register_logging(app)
  43. @app.before_request
  44. def before_request():
  45. # TODO
  46. pass
  47. @app.teardown_request
  48. def shutdown_session(exception=None):
  49. # TODO
  50. from walle.model.database import db
  51. db.session.remove()
  52. @app.route('/api/websocket')
  53. def index():
  54. return render_template('socketio.html')
  55. # 单元测试不用开启 websocket
  56. if app.config.get('ENV') != 'test':
  57. register_socketio(app)
  58. try:
  59. reload(sys)
  60. sys.setdefaultencoding('utf-8')
  61. except NameError:
  62. pass
  63. return app
  64. def register_extensions(app):
  65. """Register Flask extensions."""
  66. bcrypt.init_app(app)
  67. db.init_app(app)
  68. csrf_protect.init_app(app)
  69. login_manager.session_protection = 'strong'
  70. @login_manager.user_loader
  71. def load_user(user_id):
  72. current_app.logger.info(user_id)
  73. return UserModel.query.get(user_id)
  74. @login_manager.unauthorized_handler
  75. def unauthorized():
  76. # TODO log
  77. return BaseAPI.ApiResource.json(code=Code.unlogin)
  78. login_manager.init_app(app)
  79. migrate.init_app(app, db)
  80. mail.init_app(app)
  81. permission.init_app(app)
  82. return app
  83. def register_blueprints(app):
  84. """Register Flask blueprints."""
  85. api = Api(app)
  86. api.add_resource(BaseAPI.Base, '/', endpoint='root')
  87. api.add_resource(GeneralAPI.GeneralAPI, '/api/general/<string:action>', endpoint='general')
  88. api.add_resource(SpaceAPI.SpaceAPI, '/api/space/', '/api/space/<int:space_id>', '/api/space/<int:space_id>/<string:action>', endpoint='space')
  89. api.add_resource(DeployAPI.DeployAPI, '/api/deploy/', '/api/deploy/<int:task_id>', endpoint='deploy')
  90. api.add_resource(AccessAPI.AccessAPI, '/api/access/', '/api/access/<int:access_id>', endpoint='access')
  91. api.add_resource(RoleAPI.RoleAPI, '/api/role/', endpoint='role')
  92. api.add_resource(GroupAPI.GroupAPI, '/api/group/', '/api/group/<int:group_id>', endpoint='group')
  93. api.add_resource(PassportAPI.PassportAPI, '/api/passport/', '/api/passport/<string:action>', endpoint='passport')
  94. api.add_resource(UserAPI.UserAPI, '/api/user/', '/api/user/<int:user_id>/<string:action>', '/api/user/<string:action>', '/api/user/<int:user_id>', endpoint='user')
  95. api.add_resource(ServerAPI.ServerAPI, '/api/server/', '/api/server/<int:id>', endpoint='server')
  96. api.add_resource(ProjectAPI.ProjectAPI, '/api/project/', '/api/project/<int:project_id>', '/api/project/<int:project_id>/<string:action>', endpoint='project')
  97. api.add_resource(RepoApi.RepoAPI, '/api/repo/<string:action>/', endpoint='repo')
  98. api.add_resource(TaskAPI.TaskAPI, '/api/task/', '/api/task/<int:task_id>', '/api/task/<int:task_id>/<string:action>', endpoint='task')
  99. api.add_resource(EnvironmentAPI.EnvironmentAPI, '/api/environment/', '/api/environment/<int:env_id>', endpoint='environment')
  100. return None
  101. def register_errorhandlers(app):
  102. """Register error handlers."""
  103. @app.errorhandler(WalleError)
  104. def render_error(error):
  105. # response 的 json 内容为自定义错误代码和错误信息
  106. app.logger.error(error, exc_info=1)
  107. return error.render_error()
  108. def register_shellcontext(app):
  109. """Register shell context objects."""
  110. def shell_context():
  111. """Shell context objects."""
  112. return {
  113. 'db': db,
  114. 'User': UserModel,
  115. }
  116. app.shell_context_processor(shell_context)
  117. def register_commands(app):
  118. """Register Click commands."""
  119. app.cli.add_command(commands.test)
  120. app.cli.add_command(commands.lint)
  121. app.cli.add_command(commands.clean)
  122. app.cli.add_command(commands.urls)
  123. def register_logging(app):
  124. # TODO https://blog.csdn.net/zwxiaoliu/article/details/80890136
  125. # email errors to the administrators
  126. import logging
  127. from logging.handlers import RotatingFileHandler
  128. # Formatter
  129. formatter = logging.Formatter(
  130. '%(asctime)s %(levelname)s %(pathname)s %(lineno)s %(module)s.%(funcName)s %(message)s')
  131. # log dir
  132. if not os.path.exists(app.config['LOG_PATH']):
  133. os.makedirs(app.config['LOG_PATH'])
  134. # FileHandler Info
  135. file_handler_info = RotatingFileHandler(filename=app.config['LOG_PATH_INFO'])
  136. file_handler_info.setFormatter(formatter)
  137. file_handler_info.setLevel(logging.INFO)
  138. info_filter = InfoFilter()
  139. file_handler_info.addFilter(info_filter)
  140. app.logger.addHandler(file_handler_info)
  141. # FileHandler Error
  142. file_handler_error = RotatingFileHandler(filename=app.config['LOG_PATH_ERROR'])
  143. file_handler_error.setFormatter(formatter)
  144. file_handler_error.setLevel(logging.ERROR)
  145. app.logger.addHandler(file_handler_error)
  146. def register_socketio(app):
  147. if len(sys.argv) > 1 and sys.argv[1] == 'db':
  148. return app
  149. socketio.init_app(app, async_mode='gevent')
  150. socketio.on_namespace(WalleSocketIO(namespace='/walle'))
  151. socketio.run(app, debug=True, host=app.config.get('HOST'), port=app.config.get('PORT'))
  152. return app
  153. class InfoFilter(logging.Filter):
  154. def filter(self, record):
  155. """only use INFO
  156. 筛选, 只需要 INFO 级别的log
  157. :param record:
  158. :return:
  159. """
  160. if logging.INFO <= record.levelno < logging.ERROR:
  161. # 已经是INFO级别了
  162. # 然后利用父类, 返回 1
  163. return 1
  164. else:
  165. return 0