No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

239 líneas
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. import MySQLdb
  7. from six import iteritems
  8. import logging
  9. from werkzeug.wrappers import Request
  10. from werkzeug.local import LocalManager
  11. from werkzeug.exceptions import HTTPException, NotFound
  12. from werkzeug.contrib.profiler import ProfilerMiddleware
  13. from werkzeug.wsgi import SharedDataMiddleware
  14. import frappe
  15. import frappe.handler
  16. import frappe.auth
  17. import frappe.api
  18. import frappe.async
  19. import frappe.utils.response
  20. import frappe.website.render
  21. from frappe.utils import get_site_name
  22. from frappe.middlewares import StaticDataMiddleware
  23. from frappe.utils.error import make_error_snapshot
  24. from frappe.core.doctype.communication.comment import update_comments_in_parent_after_request
  25. from frappe import _
  26. local_manager = LocalManager([frappe.local])
  27. _site = None
  28. _sites_path = os.environ.get("SITES_PATH", ".")
  29. class RequestContext(object):
  30. def __init__(self, environ):
  31. self.request = Request(environ)
  32. def __enter__(self):
  33. init_request(self.request)
  34. def __exit__(self, type, value, traceback):
  35. frappe.destroy()
  36. @Request.application
  37. def application(request):
  38. response = None
  39. try:
  40. rollback = True
  41. init_request(request)
  42. if frappe.local.form_dict.cmd:
  43. response = frappe.handler.handle()
  44. elif frappe.request.path.startswith("/api/"):
  45. if frappe.local.form_dict.data is None:
  46. frappe.local.form_dict.data = request.get_data()
  47. response = frappe.api.handle()
  48. elif frappe.request.path.startswith('/backups'):
  49. response = frappe.utils.response.download_backup(request.path)
  50. elif frappe.request.path.startswith('/private/files/'):
  51. response = frappe.utils.response.download_private_file(request.path)
  52. elif frappe.local.request.method in ('GET', 'HEAD', 'POST'):
  53. response = frappe.website.render.render()
  54. else:
  55. raise NotFound
  56. except HTTPException as e:
  57. return e
  58. except frappe.SessionStopped as e:
  59. response = frappe.utils.response.handle_session_stopped()
  60. except Exception as e:
  61. response = handle_exception(e)
  62. else:
  63. rollback = after_request(rollback)
  64. finally:
  65. if frappe.local.request.method in ("POST", "PUT") and frappe.db and rollback:
  66. frappe.db.rollback()
  67. # set cookies
  68. if response and hasattr(frappe.local, 'cookie_manager'):
  69. frappe.local.cookie_manager.flush_cookies(response=response)
  70. frappe.destroy()
  71. return response
  72. def init_request(request):
  73. frappe.local.request = request
  74. frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest"
  75. site = _site or request.headers.get('X-Frappe-Site-Name') or get_site_name(request.host)
  76. frappe.init(site=site, sites_path=_sites_path)
  77. if not (frappe.local.conf and frappe.local.conf.db_name):
  78. # site does not exist
  79. raise NotFound
  80. if frappe.local.conf.get('maintenance_mode'):
  81. raise frappe.SessionStopped
  82. make_form_dict(request)
  83. frappe.local.http_request = frappe.auth.HTTPRequest()
  84. def make_form_dict(request):
  85. frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \
  86. for k, v in iteritems(request.form or request.args) })
  87. if "_" in frappe.local.form_dict:
  88. # _ is passed by $.ajax so that the request is not cached by the browser. So, remove _ from form_dict
  89. frappe.local.form_dict.pop("_")
  90. def handle_exception(e):
  91. response = None
  92. http_status_code = getattr(e, "http_status_code", 500)
  93. return_as_message = False
  94. if frappe.local.is_ajax or 'application/json' in frappe.local.request.headers.get('Accept', ''):
  95. # handle ajax responses first
  96. # if the request is ajax, send back the trace or error message
  97. response = frappe.utils.response.report_error(http_status_code)
  98. elif (http_status_code==500
  99. and isinstance(e, MySQLdb.OperationalError)
  100. and e.args[0] in (1205, 1213)):
  101. # 1205 = lock wait timeout
  102. # 1213 = deadlock
  103. # code 409 represents conflict
  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')
  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. b'/assets': os.path.join(sites_path, 'assets'),
  162. })
  163. application = StaticDataMiddleware(application, {
  164. b'/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)