app.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import os
  2. if not os.environ.get("DEBUG") or os.environ.get("DEBUG").lower() != 'true':
  3. from gevent import monkey
  4. monkey.patch_all()
  5. import grpc.experimental.gevent
  6. grpc.experimental.gevent.init_gevent()
  7. import json
  8. import logging
  9. import sys
  10. import threading
  11. import time
  12. import warnings
  13. from logging.handlers import RotatingFileHandler
  14. from flask import Flask, Response, request
  15. from flask_cors import CORS
  16. from werkzeug.exceptions import Unauthorized
  17. from commands import register_commands
  18. from config import Config
  19. # DO NOT REMOVE BELOW
  20. from events import event_handlers
  21. from extensions import (
  22. ext_celery,
  23. ext_code_based_extension,
  24. ext_compress,
  25. ext_database,
  26. ext_hosting_provider,
  27. ext_login,
  28. ext_mail,
  29. ext_migrate,
  30. ext_redis,
  31. ext_sentry,
  32. ext_storage,
  33. )
  34. from extensions.ext_database import db
  35. from extensions.ext_login import login_manager
  36. from libs.passport import PassportService
  37. from models import account, dataset, model, source, task, tool, tools, web
  38. from services.account_service import AccountService
  39. # DO NOT REMOVE ABOVE
  40. warnings.simplefilter("ignore", ResourceWarning)
  41. # fix windows platform
  42. if os.name == "nt":
  43. os.system('tzutil /s "UTC"')
  44. else:
  45. os.environ['TZ'] = 'UTC'
  46. time.tzset()
  47. class DifyApp(Flask):
  48. pass
  49. # -------------
  50. # Configuration
  51. # -------------
  52. config_type = os.getenv('EDITION', default='SELF_HOSTED') # ce edition first
  53. # ----------------------------
  54. # Application Factory Function
  55. # ----------------------------
  56. def create_app() -> Flask:
  57. app = DifyApp(__name__)
  58. app.config.from_object(Config())
  59. app.secret_key = app.config['SECRET_KEY']
  60. log_handlers = None
  61. log_file = app.config.get('LOG_FILE')
  62. if log_file:
  63. log_dir = os.path.dirname(log_file)
  64. os.makedirs(log_dir, exist_ok=True)
  65. log_handlers = [
  66. RotatingFileHandler(
  67. filename=log_file,
  68. maxBytes=1024 * 1024 * 1024,
  69. backupCount=5
  70. ),
  71. logging.StreamHandler(sys.stdout)
  72. ]
  73. logging.basicConfig(
  74. level=app.config.get('LOG_LEVEL'),
  75. format=app.config.get('LOG_FORMAT'),
  76. datefmt=app.config.get('LOG_DATEFORMAT'),
  77. handlers=log_handlers
  78. )
  79. initialize_extensions(app)
  80. register_blueprints(app)
  81. register_commands(app)
  82. return app
  83. def initialize_extensions(app):
  84. # Since the application instance is now created, pass it to each Flask
  85. # extension instance to bind it to the Flask application instance (app)
  86. ext_compress.init_app(app)
  87. ext_code_based_extension.init()
  88. ext_database.init_app(app)
  89. ext_migrate.init(app, db)
  90. ext_redis.init_app(app)
  91. ext_storage.init_app(app)
  92. ext_celery.init_app(app)
  93. ext_login.init_app(app)
  94. ext_mail.init_app(app)
  95. ext_hosting_provider.init_app(app)
  96. ext_sentry.init_app(app)
  97. # Flask-Login configuration
  98. @login_manager.request_loader
  99. def load_user_from_request(request_from_flask_login):
  100. """Load user based on the request."""
  101. if request.blueprint in ['console', 'inner_api']:
  102. # Check if the user_id contains a dot, indicating the old format
  103. auth_header = request.headers.get('Authorization', '')
  104. if not auth_header:
  105. auth_token = request.args.get('_token')
  106. if not auth_token:
  107. raise Unauthorized('Invalid Authorization token.')
  108. else:
  109. if ' ' not in auth_header:
  110. raise Unauthorized('Invalid Authorization header format. Expected \'Bearer <api-key>\' format.')
  111. auth_scheme, auth_token = auth_header.split(None, 1)
  112. auth_scheme = auth_scheme.lower()
  113. if auth_scheme != 'bearer':
  114. raise Unauthorized('Invalid Authorization header format. Expected \'Bearer <api-key>\' format.')
  115. decoded = PassportService().verify(auth_token)
  116. user_id = decoded.get('user_id')
  117. return AccountService.load_user(user_id)
  118. else:
  119. return None
  120. @login_manager.unauthorized_handler
  121. def unauthorized_handler():
  122. """Handle unauthorized requests."""
  123. return Response(json.dumps({
  124. 'code': 'unauthorized',
  125. 'message': "Unauthorized."
  126. }), status=401, content_type="application/json")
  127. # register blueprint routers
  128. def register_blueprints(app):
  129. from controllers.console import bp as console_app_bp
  130. from controllers.files import bp as files_bp
  131. from controllers.inner_api import bp as inner_api_bp
  132. from controllers.service_api import bp as service_api_bp
  133. from controllers.web import bp as web_bp
  134. CORS(service_api_bp,
  135. allow_headers=['Content-Type', 'Authorization', 'X-App-Code'],
  136. methods=['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'PATCH']
  137. )
  138. app.register_blueprint(service_api_bp)
  139. CORS(web_bp,
  140. resources={
  141. r"/*": {"origins": app.config['WEB_API_CORS_ALLOW_ORIGINS']}},
  142. supports_credentials=True,
  143. allow_headers=['Content-Type', 'Authorization', 'X-App-Code'],
  144. methods=['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'PATCH'],
  145. expose_headers=['X-Version', 'X-Env']
  146. )
  147. app.register_blueprint(web_bp)
  148. CORS(console_app_bp,
  149. resources={
  150. r"/*": {"origins": app.config['CONSOLE_CORS_ALLOW_ORIGINS']}},
  151. supports_credentials=True,
  152. allow_headers=['Content-Type', 'Authorization'],
  153. methods=['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'PATCH'],
  154. expose_headers=['X-Version', 'X-Env']
  155. )
  156. app.register_blueprint(console_app_bp)
  157. CORS(files_bp,
  158. allow_headers=['Content-Type'],
  159. methods=['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'PATCH']
  160. )
  161. app.register_blueprint(files_bp)
  162. app.register_blueprint(inner_api_bp)
  163. # create app
  164. app = create_app()
  165. celery = app.extensions["celery"]
  166. if app.config['TESTING']:
  167. print("App is running in TESTING mode")
  168. @app.after_request
  169. def after_request(response):
  170. """Add Version headers to the response."""
  171. response.set_cookie('remember_token', '', expires=0)
  172. response.headers.add('X-Version', app.config['CURRENT_VERSION'])
  173. response.headers.add('X-Env', app.config['DEPLOY_ENV'])
  174. return response
  175. @app.route('/health')
  176. def health():
  177. return Response(json.dumps({
  178. 'status': 'ok',
  179. 'version': app.config['CURRENT_VERSION']
  180. }), status=200, content_type="application/json")
  181. @app.route('/threads')
  182. def threads():
  183. num_threads = threading.active_count()
  184. threads = threading.enumerate()
  185. thread_list = []
  186. for thread in threads:
  187. thread_name = thread.name
  188. thread_id = thread.ident
  189. is_alive = thread.is_alive()
  190. thread_list.append({
  191. 'name': thread_name,
  192. 'id': thread_id,
  193. 'is_alive': is_alive
  194. })
  195. return {
  196. 'thread_num': num_threads,
  197. 'threads': thread_list
  198. }
  199. @app.route('/db-pool-stat')
  200. def pool_stat():
  201. engine = db.engine
  202. return {
  203. 'pool_size': engine.pool.size(),
  204. 'checked_in_connections': engine.pool.checkedin(),
  205. 'checked_out_connections': engine.pool.checkedout(),
  206. 'overflow_connections': engine.pool.overflow(),
  207. 'connection_timeout': engine.pool.timeout(),
  208. 'recycle_time': db.engine.pool._recycle
  209. }
  210. if __name__ == '__main__':
  211. app.run(host='0.0.0.0', port=5001)