app.py 7.0 KB

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