Skip to content

Commit 1cce348

Browse files
committed
Merge branch 'main' into feat-mcp_tool_support_custom_headers
2 parents ac50b0d + 5d0a500 commit 1cce348

File tree

89 files changed

+2101
-764
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2101
-764
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ sdks/python-client/dify_client.egg-info
198198
!.vscode/launch.json.template
199199
!.vscode/README.md
200200
api/.vscode
201+
web/.vscode
201202
# vscode Code History Extension
202203
.history
203204

@@ -215,6 +216,13 @@ mise.toml
215216
# Next.js build output
216217
.next/
217218

219+
# PWA generated files
220+
web/public/sw.js
221+
web/public/sw.js.map
222+
web/public/workbox-*.js
223+
web/public/workbox-*.js.map
224+
web/public/fallback-*.js
225+
218226
# AI Assistant
219227
.roo/
220228
api/.env.backup

api/controllers/console/admin.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
from collections.abc import Callable
12
from functools import wraps
3+
from typing import ParamSpec, TypeVar
24

35
from flask import request
46
from flask_restx import Resource, reqparse
57
from sqlalchemy import select
68
from sqlalchemy.orm import Session
79
from werkzeug.exceptions import NotFound, Unauthorized
810

11+
P = ParamSpec("P")
12+
R = TypeVar("R")
913
from configs import dify_config
1014
from constants.languages import supported_language
1115
from controllers.console import api
@@ -14,9 +18,9 @@
1418
from models.model import App, InstalledApp, RecommendedApp
1519

1620

17-
def admin_required(view):
21+
def admin_required(view: Callable[P, R]):
1822
@wraps(view)
19-
def decorated(*args, **kwargs):
23+
def decorated(*args: P.args, **kwargs: P.kwargs):
2024
if not dify_config.ADMIN_API_KEY:
2125
raise Unauthorized("API key is invalid.")
2226

api/controllers/console/apikey.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def post(self, resource_id):
8787
custom="max_keys_exceeded",
8888
)
8989

90-
key = ApiToken.generate_api_key(self.token_prefix, 24)
90+
key = ApiToken.generate_api_key(self.token_prefix or "", 24)
9191
api_token = ApiToken()
9292
setattr(api_token, self.resource_id_field, resource_id)
9393
api_token.tenant_id = current_user.current_tenant_id

api/controllers/console/auth/oauth_server.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
from collections.abc import Callable
12
from functools import wraps
2-
from typing import cast
3+
from typing import Concatenate, ParamSpec, TypeVar, cast
34

45
import flask_login
56
from flask import jsonify, request
@@ -15,10 +16,14 @@
1516

1617
from .. import api
1718

19+
P = ParamSpec("P")
20+
R = TypeVar("R")
21+
T = TypeVar("T")
1822

19-
def oauth_server_client_id_required(view):
23+
24+
def oauth_server_client_id_required(view: Callable[Concatenate[T, OAuthProviderApp, P], R]):
2025
@wraps(view)
21-
def decorated(*args, **kwargs):
26+
def decorated(self: T, *args: P.args, **kwargs: P.kwargs):
2227
parser = reqparse.RequestParser()
2328
parser.add_argument("client_id", type=str, required=True, location="json")
2429
parsed_args = parser.parse_args()
@@ -30,18 +35,15 @@ def decorated(*args, **kwargs):
3035
if not oauth_provider_app:
3136
raise NotFound("client_id is invalid")
3237

33-
kwargs["oauth_provider_app"] = oauth_provider_app
34-
35-
return view(*args, **kwargs)
38+
return view(self, oauth_provider_app, *args, **kwargs)
3639

3740
return decorated
3841

3942

40-
def oauth_server_access_token_required(view):
43+
def oauth_server_access_token_required(view: Callable[Concatenate[T, OAuthProviderApp, Account, P], R]):
4144
@wraps(view)
42-
def decorated(*args, **kwargs):
43-
oauth_provider_app = kwargs.get("oauth_provider_app")
44-
if not oauth_provider_app or not isinstance(oauth_provider_app, OAuthProviderApp):
45+
def decorated(self: T, oauth_provider_app: OAuthProviderApp, *args: P.args, **kwargs: P.kwargs):
46+
if not isinstance(oauth_provider_app, OAuthProviderApp):
4547
raise BadRequest("Invalid oauth_provider_app")
4648

4749
authorization_header = request.headers.get("Authorization")
@@ -79,9 +81,7 @@ def decorated(*args, **kwargs):
7981
response.headers["WWW-Authenticate"] = "Bearer"
8082
return response
8183

82-
kwargs["account"] = account
83-
84-
return view(*args, **kwargs)
84+
return view(self, oauth_provider_app, account, *args, **kwargs)
8585

8686
return decorated
8787

api/controllers/console/billing/billing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from flask_login import current_user
21
from flask_restx import Resource, reqparse
32

43
from controllers.console import api
54
from controllers.console.wraps import account_initialization_required, only_edition_cloud, setup_required
6-
from libs.login import login_required
5+
from libs.login import current_user, login_required
6+
from models.model import Account
77
from services.billing_service import BillingService
88

99

@@ -17,9 +17,10 @@ def get(self):
1717
parser.add_argument("plan", type=str, required=True, location="args", choices=["professional", "team"])
1818
parser.add_argument("interval", type=str, required=True, location="args", choices=["month", "year"])
1919
args = parser.parse_args()
20+
assert isinstance(current_user, Account)
2021

2122
BillingService.is_tenant_owner_or_admin(current_user)
22-
23+
assert current_user.current_tenant_id is not None
2324
return BillingService.get_subscription(
2425
args["plan"], args["interval"], current_user.email, current_user.current_tenant_id
2526
)
@@ -31,7 +32,9 @@ class Invoices(Resource):
3132
@account_initialization_required
3233
@only_edition_cloud
3334
def get(self):
35+
assert isinstance(current_user, Account)
3436
BillingService.is_tenant_owner_or_admin(current_user)
37+
assert current_user.current_tenant_id is not None
3538
return BillingService.get_invoices(current_user.email, current_user.current_tenant_id)
3639

3740

api/controllers/console/datasets/datasets_document.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ def get(self, dataset_id, batch):
475475
data_source_info = document.data_source_info_dict
476476

477477
if document.data_source_type == "upload_file":
478+
if not data_source_info:
479+
continue
478480
file_id = data_source_info["upload_file_id"]
479481
file_detail = (
480482
db.session.query(UploadFile)
@@ -491,6 +493,8 @@ def get(self, dataset_id, batch):
491493
extract_settings.append(extract_setting)
492494

493495
elif document.data_source_type == "notion_import":
496+
if not data_source_info:
497+
continue
494498
extract_setting = ExtractSetting(
495499
datasource_type=DatasourceType.NOTION.value,
496500
notion_info={
@@ -503,6 +507,8 @@ def get(self, dataset_id, batch):
503507
)
504508
extract_settings.append(extract_setting)
505509
elif document.data_source_type == "website_crawl":
510+
if not data_source_info:
511+
continue
506512
extract_setting = ExtractSetting(
507513
datasource_type=DatasourceType.WEBSITE.value,
508514
website_info={

api/controllers/console/explore/parameter.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class ExploreAppMetaApi(InstalledAppResource):
4343
def get(self, installed_app: InstalledApp):
4444
"""Get app meta"""
4545
app_model = installed_app.app
46+
if not app_model:
47+
raise ValueError("App not found")
4648
return AppService().get_app_meta(app_model)
4749

4850

api/controllers/console/explore/workflow.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def post(self, installed_app: InstalledApp):
3535
Run workflow
3636
"""
3737
app_model = installed_app.app
38+
if not app_model:
39+
raise NotWorkflowAppError()
3840
app_mode = AppMode.value_of(app_model.mode)
3941
if app_mode != AppMode.WORKFLOW:
4042
raise NotWorkflowAppError()
@@ -73,6 +75,8 @@ def post(self, installed_app: InstalledApp, task_id: str):
7375
Stop workflow task
7476
"""
7577
app_model = installed_app.app
78+
if not app_model:
79+
raise NotWorkflowAppError()
7680
app_mode = AppMode.value_of(app_model.mode)
7781
if app_mode != AppMode.WORKFLOW:
7882
raise NotWorkflowAppError()

api/controllers/console/explore/wraps.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from collections.abc import Callable
12
from functools import wraps
3+
from typing import Concatenate, Optional, ParamSpec, TypeVar
24

35
from flask_login import current_user
46
from flask_restx import Resource
@@ -13,19 +15,15 @@
1315
from services.enterprise.enterprise_service import EnterpriseService
1416
from services.feature_service import FeatureService
1517

18+
P = ParamSpec("P")
19+
R = TypeVar("R")
20+
T = TypeVar("T")
1621

17-
def installed_app_required(view=None):
18-
def decorator(view):
19-
@wraps(view)
20-
def decorated(*args, **kwargs):
21-
if not kwargs.get("installed_app_id"):
22-
raise ValueError("missing installed_app_id in path parameters")
23-
24-
installed_app_id = kwargs.get("installed_app_id")
25-
installed_app_id = str(installed_app_id)
26-
27-
del kwargs["installed_app_id"]
2822

23+
def installed_app_required(view: Optional[Callable[Concatenate[InstalledApp, P], R]] = None):
24+
def decorator(view: Callable[Concatenate[InstalledApp, P], R]):
25+
@wraps(view)
26+
def decorated(installed_app_id: str, *args: P.args, **kwargs: P.kwargs):
2927
installed_app = (
3028
db.session.query(InstalledApp)
3129
.where(
@@ -52,10 +50,10 @@ def decorated(*args, **kwargs):
5250
return decorator
5351

5452

55-
def user_allowed_to_access_app(view=None):
56-
def decorator(view):
53+
def user_allowed_to_access_app(view: Optional[Callable[Concatenate[InstalledApp, P], R]] = None):
54+
def decorator(view: Callable[Concatenate[InstalledApp, P], R]):
5755
@wraps(view)
58-
def decorated(installed_app: InstalledApp, *args, **kwargs):
56+
def decorated(installed_app: InstalledApp, *args: P.args, **kwargs: P.kwargs):
5957
feature = FeatureService.get_system_features()
6058
if feature.webapp_auth.enabled:
6159
app_id = installed_app.app_id

api/controllers/console/workspace/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from collections.abc import Callable
12
from functools import wraps
3+
from typing import ParamSpec, TypeVar
24

35
from flask_login import current_user
46
from sqlalchemy.orm import Session
@@ -7,14 +9,17 @@
79
from extensions.ext_database import db
810
from models.account import TenantPluginPermission
911

12+
P = ParamSpec("P")
13+
R = TypeVar("R")
14+
1015

1116
def plugin_permission_required(
1217
install_required: bool = False,
1318
debug_required: bool = False,
1419
):
15-
def interceptor(view):
20+
def interceptor(view: Callable[P, R]):
1621
@wraps(view)
17-
def decorated(*args, **kwargs):
22+
def decorated(*args: P.args, **kwargs: P.kwargs):
1823
user = current_user
1924
tenant_id = user.current_tenant_id
2025

0 commit comments

Comments
 (0)