refactor: assembling the app features in modular way (#9129)

Signed-off-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
This commit is contained in:
Bowen Liang
2024-11-30 23:05:22 +08:00
committed by GitHub
parent 3bc4dc58d7
commit 9b46b02717
30 changed files with 396 additions and 318 deletions

View File

@@ -0,0 +1,65 @@
import json
import os
import threading
from flask import Response
from configs import dify_config
from dify_app import DifyApp
def init_app(app: DifyApp):
@app.after_request
def after_request(response):
"""Add Version headers to the response."""
response.headers.add("X-Version", dify_config.CURRENT_VERSION)
response.headers.add("X-Env", dify_config.DEPLOY_ENV)
return response
@app.route("/health")
def health():
return Response(
json.dumps({"pid": os.getpid(), "status": "ok", "version": dify_config.CURRENT_VERSION}),
status=200,
content_type="application/json",
)
@app.route("/threads")
def threads():
num_threads = threading.active_count()
threads = threading.enumerate()
thread_list = []
for thread in threads:
thread_name = thread.name
thread_id = thread.ident
is_alive = thread.is_alive()
thread_list.append(
{
"name": thread_name,
"id": thread_id,
"is_alive": is_alive,
}
)
return {
"pid": os.getpid(),
"thread_num": num_threads,
"threads": thread_list,
}
@app.route("/db-pool-stat")
def pool_stat():
from extensions.ext_database import db
engine = db.engine
return {
"pid": os.getpid(),
"pool_size": engine.pool.size(),
"checked_in_connections": engine.pool.checkedin(),
"checked_out_connections": engine.pool.checkedout(),
"overflow_connections": engine.pool.overflow(),
"connection_timeout": engine.pool.timeout(),
"recycle_time": db.engine.pool._recycle,
}

View File

@@ -0,0 +1,48 @@
from configs import dify_config
from dify_app import DifyApp
def init_app(app: DifyApp):
# register blueprint routers
from flask_cors import CORS
from controllers.console import bp as console_app_bp
from controllers.files import bp as files_bp
from controllers.inner_api import bp as inner_api_bp
from controllers.service_api import bp as service_api_bp
from controllers.web import bp as web_bp
CORS(
service_api_bp,
allow_headers=["Content-Type", "Authorization", "X-App-Code"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
)
app.register_blueprint(service_api_bp)
CORS(
web_bp,
resources={r"/*": {"origins": dify_config.WEB_API_CORS_ALLOW_ORIGINS}},
supports_credentials=True,
allow_headers=["Content-Type", "Authorization", "X-App-Code"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
expose_headers=["X-Version", "X-Env"],
)
app.register_blueprint(web_bp)
CORS(
console_app_bp,
resources={r"/*": {"origins": dify_config.CONSOLE_CORS_ALLOW_ORIGINS}},
supports_credentials=True,
allow_headers=["Content-Type", "Authorization"],
methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
expose_headers=["X-Version", "X-Env"],
)
app.register_blueprint(console_app_bp)
CORS(files_bp, allow_headers=["Content-Type"], methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"])
app.register_blueprint(files_bp)
app.register_blueprint(inner_api_bp)

View File

@@ -3,12 +3,12 @@ from datetime import timedelta
import pytz
from celery import Celery, Task
from celery.schedules import crontab
from flask import Flask
from configs import dify_config
from dify_app import DifyApp
def init_app(app: Flask) -> Celery:
def init_app(app: DifyApp) -> Celery:
class FlaskTask(Task):
def __call__(self, *args: object, **kwargs: object) -> object:
with app.app_context():

View File

@@ -1,7 +1,8 @@
from core.extension.extension import Extension
from dify_app import DifyApp
def init():
def init_app(app: DifyApp):
code_based_extension.init()

View File

@@ -0,0 +1,29 @@
from dify_app import DifyApp
def init_app(app: DifyApp):
from commands import (
add_qdrant_doc_id_index,
convert_to_agent_apps,
create_tenant,
fix_app_site_missing,
reset_email,
reset_encrypt_key_pair,
reset_password,
upgrade_db,
vdb_migrate,
)
cmds_to_register = [
reset_password,
reset_email,
reset_encrypt_key_pair,
vdb_migrate,
convert_to_agent_apps,
add_qdrant_doc_id_index,
create_tenant,
upgrade_db,
fix_app_site_missing,
]
for cmd in cmds_to_register:
app.cli.add_command(cmd)

View File

@@ -1,11 +1,13 @@
from flask import Flask
from configs import dify_config
from dify_app import DifyApp
def init_app(app: Flask):
if dify_config.API_COMPRESSION_ENABLED:
from flask_compress import Compress
def is_enabled() -> bool:
return dify_config.API_COMPRESSION_ENABLED
compress = Compress()
compress.init_app(app)
def init_app(app: DifyApp):
from flask_compress import Compress
compress = Compress()
compress.init_app(app)

View File

@@ -1,6 +1,8 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from dify_app import DifyApp
POSTGRES_INDEXES_NAMING_CONVENTION = {
"ix": "%(column_0_label)s_idx",
"uq": "%(table_name)s_%(column_0_name)s_key",
@@ -13,5 +15,5 @@ metadata = MetaData(naming_convention=POSTGRES_INDEXES_NAMING_CONVENTION)
db = SQLAlchemy(metadata=metadata)
def init_app(app):
def init_app(app: DifyApp):
db.init_app(app)

View File

@@ -1,9 +1,10 @@
from flask import Flask
from core.hosting_configuration import HostingConfiguration
hosting_configuration = HostingConfiguration()
def init_app(app: Flask):
from dify_app import DifyApp
def init_app(app: DifyApp):
hosting_configuration.init_app(app)

View File

@@ -0,0 +1,6 @@
from dify_app import DifyApp
def init_app(app: DifyApp):
from events import event_handlers # noqa: F401
from models import account, dataset, model, source, task, tool, tools, web # noqa: F401

View File

@@ -3,12 +3,11 @@ import os
import sys
from logging.handlers import RotatingFileHandler
from flask import Flask
from configs import dify_config
from dify_app import DifyApp
def init_app(app: Flask):
def init_app(app: DifyApp):
log_handlers = []
log_file = dify_config.LOG_FILE
if log_file:

View File

@@ -1,7 +1,62 @@
import json
import flask_login
from flask import Response, request
from flask_login import user_loaded_from_request, user_logged_in
from werkzeug.exceptions import Unauthorized
import contexts
from dify_app import DifyApp
from libs.passport import PassportService
from services.account_service import AccountService
login_manager = flask_login.LoginManager()
def init_app(app):
# Flask-Login configuration
@login_manager.request_loader
def load_user_from_request(request_from_flask_login):
"""Load user based on the request."""
if request.blueprint not in {"console", "inner_api"}:
return None
# Check if the user_id contains a dot, indicating the old format
auth_header = request.headers.get("Authorization", "")
if not auth_header:
auth_token = request.args.get("_token")
if not auth_token:
raise Unauthorized("Invalid Authorization token.")
else:
if " " not in auth_header:
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
auth_scheme, auth_token = auth_header.split(None, 1)
auth_scheme = auth_scheme.lower()
if auth_scheme != "bearer":
raise Unauthorized("Invalid Authorization header format. Expected 'Bearer <api-key>' format.")
decoded = PassportService().verify(auth_token)
user_id = decoded.get("user_id")
logged_in_account = AccountService.load_logged_in_account(account_id=user_id)
return logged_in_account
@user_logged_in.connect
@user_loaded_from_request.connect
def on_user_logged_in(_sender, user):
"""Called when a user logged in."""
if user:
contexts.tenant_id.set(user.current_tenant_id)
@login_manager.unauthorized_handler
def unauthorized_handler():
"""Handle unauthorized requests."""
return Response(
json.dumps({"code": "unauthorized", "message": "Unauthorized."}),
status=401,
content_type="application/json",
)
def init_app(app: DifyApp):
login_manager.init_app(app)

View File

@@ -1,10 +1,10 @@
import logging
from typing import Optional
import resend
from flask import Flask
from configs import dify_config
from dify_app import DifyApp
class Mail:
@@ -26,6 +26,8 @@ class Mail:
match mail_type:
case "resend":
import resend
api_key = dify_config.RESEND_API_KEY
if not api_key:
raise ValueError("RESEND_API_KEY is not set")
@@ -84,7 +86,11 @@ class Mail:
)
def init_app(app: Flask):
def is_enabled() -> bool:
return dify_config.MAIL_TYPE is not None and dify_config.MAIL_TYPE != ""
def init_app(app: DifyApp):
mail.init_app(app)

View File

@@ -1,5 +1,9 @@
import flask_migrate
from dify_app import DifyApp
def init(app, db):
def init_app(app: DifyApp):
import flask_migrate
from extensions.ext_database import db
flask_migrate.Migrate(app, db)

View File

@@ -1,9 +1,8 @@
from flask import Flask
from configs import dify_config
from dify_app import DifyApp
def init_app(app: Flask):
def init_app(app: DifyApp):
if dify_config.RESPECT_XFORWARD_HEADERS_ENABLED:
from werkzeug.middleware.proxy_fix import ProxyFix

View File

@@ -4,6 +4,7 @@ from redis.connection import Connection, SSLConnection
from redis.sentinel import Sentinel
from configs import dify_config
from dify_app import DifyApp
class RedisClientWrapper:
@@ -43,7 +44,7 @@ class RedisClientWrapper:
redis_client = RedisClientWrapper()
def init_app(app):
def init_app(app: DifyApp):
global redis_client
connection_class = Connection
if dify_config.REDIS_USE_SSL:

View File

@@ -1,25 +1,26 @@
import openai
import sentry_sdk
from langfuse import parse_error
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.flask import FlaskIntegration
from werkzeug.exceptions import HTTPException
from configs import dify_config
from core.model_runtime.errors.invoke import InvokeRateLimitError
from dify_app import DifyApp
def before_send(event, hint):
if "exc_info" in hint:
exc_type, exc_value, tb = hint["exc_info"]
if parse_error.defaultErrorResponse in str(exc_value):
return None
return event
def init_app(app):
def init_app(app: DifyApp):
if dify_config.SENTRY_DSN:
import openai
import sentry_sdk
from langfuse import parse_error
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.flask import FlaskIntegration
from werkzeug.exceptions import HTTPException
from core.model_runtime.errors.invoke import InvokeRateLimitError
def before_send(event, hint):
if "exc_info" in hint:
exc_type, exc_value, tb = hint["exc_info"]
if parse_error.defaultErrorResponse in str(exc_value):
return None
return event
sentry_sdk.init(
dsn=dify_config.SENTRY_DSN,
integrations=[FlaskIntegration(), CeleryIntegration()],

View File

@@ -0,0 +1,6 @@
from configs import dify_config
from dify_app import DifyApp
def init_app(app: DifyApp):
app.secret_key = dify_config.SECRET_KEY

View File

@@ -5,6 +5,7 @@ from typing import Union
from flask import Flask
from configs import dify_config
from dify_app import DifyApp
from extensions.storage.base_storage import BaseStorage
from extensions.storage.storage_type import StorageType
@@ -122,5 +123,5 @@ class Storage:
storage = Storage()
def init_app(app: Flask):
def init_app(app: DifyApp):
storage.init_app(app)

View File

@@ -0,0 +1,11 @@
import os
import time
from dify_app import DifyApp
def init_app(app: DifyApp):
os.environ["TZ"] = "UTC"
# windows platform not support tzset
if hasattr(time, "tzset"):
time.tzset()

View File

@@ -0,0 +1,7 @@
from dify_app import DifyApp
def init_app(app: DifyApp):
import warnings
warnings.simplefilter("ignore", ResourceWarning)