您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

241 行
6.6 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  3. # MIT License. See license.txt
  4. from __future__ import unicode_literals
  5. import os
  6. from six import iteritems
  7. import logging
  8. from werkzeug.wrappers import Request
  9. from werkzeug.local import LocalManager
  10. from werkzeug.exceptions import HTTPException, NotFound
  11. from werkzeug.contrib.profiler import ProfilerMiddleware
  12. from werkzeug.wsgi import SharedDataMiddleware
  13. import frappe
  14. import frappe.handler
  15. import frappe.auth
  16. import frappe.api
  17. import frappe.async
  18. import frappe.utils.response
  19. import frappe.website.render
  20. from frappe.utils import get_site_name
  21. from frappe.middlewares import StaticDataMiddleware
  22. from frappe.utils.error import make_error_snapshot
  23. from frappe.core.doctype.communication.comment import update_comments_in_parent_after_request
  24. from frappe import _
  25. # imports - third-party imports
  26. import pymysql
  27. from pymysql.constants import ER
  28. # imports - module imports
  29. local_manager = LocalManager([frappe.local])
  30. _site = None
  31. _sites_path = os.environ.get("SITES_PATH", ".")
  32. class RequestContext(object):
  33. def __init__(self, environ):
  34. self.request = Request(environ)
  35. def __enter__(self):
  36. init_request(self.request)
  37. def __exit__(self, type, value, traceback):
  38. frappe.destroy()
  39. @Request.application
  40. def application(request):
  41. response = None
  42. try:
  43. rollback = True
  44. init_request(request)
  45. if frappe.local.form_dict.cmd:
  46. response = frappe.handler.handle()
  47. elif frappe.request.path.startswith("/api/"):
  48. if frappe.local.form_dict.data is None:
  49. frappe.local.form_dict.data = request.get_data()
  50. response = frappe.api.handle()
  51. elif frappe.request.path.startswith('/backups'):
  52. response = frappe.utils.response.download_backup(request.path)
  53. elif frappe.request.path.startswith('/private/files/'):
  54. response = frappe.utils.response.download_private_file(request.path)
  55. elif frappe.local.request.method in ('GET', 'HEAD', 'POST'):
  56. response = frappe.website.render.render()
  57. else:
  58. raise NotFound
  59. except HTTPException as e:
  60. return e
  61. except frappe.SessionStopped as e:
  62. response = frappe.utils.response.handle_session_stopped()
  63. except Exception as e:
  64. response = handle_exception(e)
  65. else:
  66. rollback = after_request(rollback)
  67. finally:
  68. if frappe.local.request.method in ("POST", "PUT") and frappe.db and rollback:
  69. frappe.db.rollback()
  70. # set cookies
  71. if response and hasattr(frappe.local, 'cookie_manager'):
  72. frappe.local.cookie_manager.flush_cookies(response=response)
  73. frappe.destroy()
  74. return response
  75. def init_request(request):
  76. frappe.local.request = request
  77. frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest"
  78. site = _site or request.headers.get('X-Frappe-Site-Name') or get_site_name(request.host)
  79. frappe.init(site=site, sites_path=_sites_path)
  80. if not (frappe.local.conf and frappe.local.conf.db_name):
  81. # site does not exist
  82. raise NotFound
  83. if frappe.local.conf.get('maintenance_mode'):
  84. raise frappe.SessionStopped
  85. make_form_dict(request)
  86. frappe.local.http_request = frappe.auth.HTTPRequest()
  87. def make_form_dict(request):
  88. frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \
  89. for k, v in iteritems(request.form or request.args) })
  90. if "_" in frappe.local.form_dict:
  91. # _ is passed by $.ajax so that the request is not cached by the browser. So, remove _ from form_dict
  92. frappe.local.form_dict.pop("_")
  93. def handle_exception(e):
  94. response = None
  95. http_status_code = getattr(e, "http_status_code", 500)
  96. return_as_message = False
  97. if frappe.local.is_ajax or 'application/json' in frappe.get_request_header('Accept'):
  98. # handle ajax responses first
  99. # if the request is ajax, send back the trace or error message
  100. response = frappe.utils.response.report_error(http_status_code)
  101. elif (http_status_code==500
  102. and isinstance(e, pymysql.InternalError)
  103. and e.args[0] in (ER.LOCK_WAIT_TIMEOUT, ER.LOCK_DEADLOCK)):
  104. http_status_code = 508
  105. elif http_status_code==401:
  106. frappe.respond_as_web_page(_("Session Expired"),
  107. _("Your session has expired, please login again to continue."),
  108. http_status_code=http_status_code, indicator_color='red')
  109. return_as_message = True
  110. elif http_status_code==403:
  111. frappe.respond_as_web_page(_("Not Permitted"),
  112. _("You do not have enough permissions to complete the action"),
  113. http_status_code=http_status_code, indicator_color='red')
  114. return_as_message = True
  115. elif http_status_code==404:
  116. frappe.respond_as_web_page(_("Not Found"),
  117. _("The resource you are looking for is not available"),
  118. http_status_code=http_status_code, indicator_color='red')
  119. return_as_message = True
  120. else:
  121. traceback = "<pre>"+frappe.get_traceback()+"</pre>"
  122. if frappe.local.flags.disable_traceback:
  123. traceback = ""
  124. frappe.respond_as_web_page("Server Error",
  125. traceback, http_status_code=http_status_code,
  126. indicator_color='red', width=640)
  127. return_as_message = True
  128. if e.__class__ == frappe.AuthenticationError:
  129. if hasattr(frappe.local, "login_manager"):
  130. frappe.local.login_manager.clear_cookies()
  131. if http_status_code >= 500:
  132. frappe.logger().error('Request Error', exc_info=True)
  133. make_error_snapshot(e)
  134. if return_as_message:
  135. response = frappe.website.render.render("message",
  136. http_status_code=http_status_code)
  137. return response
  138. def after_request(rollback):
  139. if (frappe.local.request.method in ("POST", "PUT") or frappe.local.flags.commit) and frappe.db:
  140. if frappe.db.transaction_writes:
  141. frappe.db.commit()
  142. rollback = False
  143. # update session
  144. if getattr(frappe.local, "session_obj", None):
  145. updated_in_db = frappe.local.session_obj.update()
  146. if updated_in_db:
  147. frappe.db.commit()
  148. rollback = False
  149. update_comments_in_parent_after_request()
  150. return rollback
  151. application = local_manager.make_middleware(application)
  152. def serve(port=8000, profile=False, site=None, sites_path='.'):
  153. global application, _site, _sites_path
  154. _site = site
  155. _sites_path = sites_path
  156. from werkzeug.serving import run_simple
  157. if profile:
  158. application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls'))
  159. if not os.environ.get('NO_STATICS'):
  160. application = SharedDataMiddleware(application, {
  161. '/assets': os.path.join(sites_path, 'assets'),
  162. })
  163. application = StaticDataMiddleware(application, {
  164. '/files': os.path.abspath(sites_path)
  165. })
  166. application.debug = True
  167. application.config = {
  168. 'SERVER_NAME': 'localhost:8000'
  169. }
  170. in_test_env = os.environ.get('CI')
  171. if in_test_env:
  172. log = logging.getLogger('werkzeug')
  173. log.setLevel(logging.ERROR)
  174. run_simple('0.0.0.0', int(port), application,
  175. use_reloader=not in_test_env,
  176. use_debugger=not in_test_env,
  177. use_evalex=not in_test_env,
  178. threaded=True)