app.py 7.2 KB

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