| 
									
										
										
										
											2025-03-10 19:50:11 +08:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | from collections.abc import Callable | 
					
						
							| 
									
										
										
										
											2025-01-07 18:04:41 +08:00
										 |  |  | from datetime import UTC, datetime, timedelta | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | from enum import Enum | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | from functools import wraps | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | from typing import Optional | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  | from flask import current_app, request | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  | from flask_login import user_logged_in  # type: ignore | 
					
						
							|  |  |  | from flask_restful import Resource  # type: ignore | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | from pydantic import BaseModel | 
					
						
							| 
									
										
										
										
											2025-01-07 18:04:41 +08:00
										 |  |  | from sqlalchemy import select, update | 
					
						
							|  |  |  | from sqlalchemy.orm import Session | 
					
						
							| 
									
										
										
										
											2024-06-04 18:04:10 +08:00
										 |  |  | from werkzeug.exceptions import Forbidden, Unauthorized | 
					
						
							| 
									
										
										
										
											2024-02-06 13:21:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | from extensions.ext_database import db | 
					
						
							| 
									
										
										
										
											2025-03-10 19:50:11 +08:00
										 |  |  | from extensions.ext_redis import redis_client | 
					
						
							| 
									
										
										
										
											2023-10-08 05:21:32 -05:00
										 |  |  | from libs.login import _get_user | 
					
						
							| 
									
										
										
										
											2024-04-18 17:33:32 +08:00
										 |  |  | from models.account import Account, Tenant, TenantAccountJoin, TenantStatus | 
					
						
							| 
									
										
										
										
											2025-03-10 19:50:11 +08:00
										 |  |  | from models.dataset import RateLimitLog | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | from models.model import ApiToken, App, EndUser | 
					
						
							| 
									
										
										
										
											2023-12-20 15:37:57 +08:00
										 |  |  | from services.feature_service import FeatureService | 
					
						
							| 
									
										
										
										
											2024-01-12 12:34:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | class WhereisUserArg(Enum): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Enum for whereis_user_arg. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     QUERY = "query" | 
					
						
							|  |  |  |     JSON = "json" | 
					
						
							|  |  |  |     FORM = "form" | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FetchUserArg(BaseModel): | 
					
						
							|  |  |  |     fetch_from: WhereisUserArg | 
					
						
							|  |  |  |     required: bool = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def validate_app_token(view: Optional[Callable] = None, *, fetch_user_arg: Optional[FetchUserArg] = None): | 
					
						
							|  |  |  |     def decorator(view_func): | 
					
						
							|  |  |  |         @wraps(view_func) | 
					
						
							|  |  |  |         def decorated_view(*args, **kwargs): | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             api_token = validate_and_get_api_token("app") | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 20:32:44 +08:00
										 |  |  |             app_model = db.session.query(App).filter(App.id == api_token.app_id).first() | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |             if not app_model: | 
					
						
							| 
									
										
										
										
											2024-06-04 18:04:10 +08:00
										 |  |  |                 raise Forbidden("The app no longer exists.") | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             if app_model.status != "normal": | 
					
						
							| 
									
										
										
										
											2024-06-04 18:04:10 +08:00
										 |  |  |                 raise Forbidden("The app's status is abnormal.") | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if not app_model.enable_api: | 
					
						
							| 
									
										
										
										
											2024-06-04 18:04:10 +08:00
										 |  |  |                 raise Forbidden("The app's API service has been disabled.") | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 17:33:32 +08:00
										 |  |  |             tenant = db.session.query(Tenant).filter(Tenant.id == app_model.tenant_id).first() | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |             if tenant is None: | 
					
						
							|  |  |  |                 raise ValueError("Tenant does not exist.") | 
					
						
							| 
									
										
										
										
											2024-04-18 17:33:32 +08:00
										 |  |  |             if tenant.status == TenantStatus.ARCHIVE: | 
					
						
							| 
									
										
										
										
											2024-06-04 18:04:10 +08:00
										 |  |  |                 raise Forbidden("The workspace's status is archived.") | 
					
						
							| 
									
										
										
										
											2024-04-18 17:33:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-07 10:36:58 +08:00
										 |  |  |             tenant_account_join = ( | 
					
						
							|  |  |  |                 db.session.query(Tenant, TenantAccountJoin) | 
					
						
							|  |  |  |                 .filter(Tenant.id == api_token.tenant_id) | 
					
						
							|  |  |  |                 .filter(TenantAccountJoin.tenant_id == Tenant.id) | 
					
						
							|  |  |  |                 .filter(TenantAccountJoin.role.in_(["owner"])) | 
					
						
							|  |  |  |                 .filter(Tenant.status == TenantStatus.NORMAL) | 
					
						
							|  |  |  |                 .one_or_none() | 
					
						
							|  |  |  |             )  # TODO: only owner information is required, so only one is returned. | 
					
						
							|  |  |  |             if tenant_account_join: | 
					
						
							|  |  |  |                 tenant, ta = tenant_account_join | 
					
						
							| 
									
										
										
										
											2025-04-09 14:07:32 +08:00
										 |  |  |                 account = db.session.query(Account).filter(Account.id == ta.account_id).first() | 
					
						
							| 
									
										
										
										
											2025-04-07 10:36:58 +08:00
										 |  |  |                 # Login admin | 
					
						
							|  |  |  |                 if account: | 
					
						
							|  |  |  |                     account.current_tenant = tenant | 
					
						
							|  |  |  |                     current_app.login_manager._update_request_context_with_user(account)  # type: ignore | 
					
						
							|  |  |  |                     user_logged_in.send(current_app._get_current_object(), user=_get_user())  # type: ignore | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     raise Unauthorized("Tenant owner account does not exist.") | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 raise Unauthorized("Tenant does not exist.") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             kwargs["app_model"] = app_model | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:46:50 +08:00
										 |  |  |             if fetch_user_arg: | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |                 if fetch_user_arg.fetch_from == WhereisUserArg.QUERY: | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                     user_id = request.args.get("user") | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |                 elif fetch_user_arg.fetch_from == WhereisUserArg.JSON: | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                     user_id = request.get_json().get("user") | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |                 elif fetch_user_arg.fetch_from == WhereisUserArg.FORM: | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                     user_id = request.form.get("user") | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     # use default-user | 
					
						
							|  |  |  |                     user_id = None | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |                 if not user_id and fetch_user_arg.required: | 
					
						
							|  |  |  |                     raise ValueError("Arg user must be provided.") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:46:50 +08:00
										 |  |  |                 if user_id: | 
					
						
							|  |  |  |                     user_id = str(user_id) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                 kwargs["end_user"] = create_or_update_end_user_for_user_id(app_model, user_id) | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return view_func(*args, **kwargs) | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |         return decorated_view | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if view is None: | 
					
						
							|  |  |  |         return decorator | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return decorator(view) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  | def cloud_edition_billing_resource_check(resource: str, api_token_type: str): | 
					
						
							| 
									
										
										
										
											2023-12-05 16:53:55 +08:00
										 |  |  |     def interceptor(view): | 
					
						
							|  |  |  |         def decorated(*args, **kwargs): | 
					
						
							| 
									
										
										
										
											2023-12-20 15:37:57 +08:00
										 |  |  |             api_token = validate_and_get_api_token(api_token_type) | 
					
						
							|  |  |  |             features = FeatureService.get_features(api_token.tenant_id) | 
					
						
							| 
									
										
										
										
											2023-12-05 16:53:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-20 15:37:57 +08:00
										 |  |  |             if features.billing.enabled: | 
					
						
							|  |  |  |                 members = features.members | 
					
						
							|  |  |  |                 apps = features.apps | 
					
						
							|  |  |  |                 vector_space = features.vector_space | 
					
						
							| 
									
										
										
										
											2024-03-03 12:45:06 +08:00
										 |  |  |                 documents_upload_quota = features.documents_upload_quota | 
					
						
							| 
									
										
										
										
											2023-12-05 16:53:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                 if resource == "members" and 0 < members.limit <= members.size: | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  |                     raise Forbidden("The number of members has reached the limit of your subscription.") | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                 elif resource == "apps" and 0 < apps.limit <= apps.size: | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  |                     raise Forbidden("The number of apps has reached the limit of your subscription.") | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                 elif resource == "vector_space" and 0 < vector_space.limit <= vector_space.size: | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  |                     raise Forbidden("The capacity of the vector space has reached the limit of your subscription.") | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                 elif resource == "documents" and 0 < documents_upload_quota.limit <= documents_upload_quota.size: | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  |                     raise Forbidden("The number of documents has reached the limit of your subscription.") | 
					
						
							| 
									
										
										
										
											2023-12-05 16:53:55 +08:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     return view(*args, **kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return view(*args, **kwargs) | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-05 16:53:55 +08:00
										 |  |  |         return decorated | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-05 16:53:55 +08:00
										 |  |  |     return interceptor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  | def cloud_edition_billing_knowledge_limit_check(resource: str, api_token_type: str): | 
					
						
							| 
									
										
										
										
											2024-04-02 17:55:49 +08:00
										 |  |  |     def interceptor(view): | 
					
						
							|  |  |  |         @wraps(view) | 
					
						
							|  |  |  |         def decorated(*args, **kwargs): | 
					
						
							|  |  |  |             api_token = validate_and_get_api_token(api_token_type) | 
					
						
							|  |  |  |             features = FeatureService.get_features(api_token.tenant_id) | 
					
						
							|  |  |  |             if features.billing.enabled: | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |                 if resource == "add_segment": | 
					
						
							|  |  |  |                     if features.billing.subscription.plan == "sandbox": | 
					
						
							| 
									
										
										
										
											2024-08-28 18:55:47 +08:00
										 |  |  |                         raise Forbidden( | 
					
						
							|  |  |  |                             "To unlock this feature and elevate your Dify experience, please upgrade to a paid plan." | 
					
						
							|  |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2024-04-02 17:55:49 +08:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     return view(*args, **kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return view(*args, **kwargs) | 
					
						
							| 
									
										
										
										
											2025-03-10 19:50:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return decorated | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return interceptor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def cloud_edition_billing_rate_limit_check(resource: str, api_token_type: str): | 
					
						
							|  |  |  |     def interceptor(view): | 
					
						
							|  |  |  |         @wraps(view) | 
					
						
							|  |  |  |         def decorated(*args, **kwargs): | 
					
						
							|  |  |  |             api_token = validate_and_get_api_token(api_token_type) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if resource == "knowledge": | 
					
						
							|  |  |  |                 knowledge_rate_limit = FeatureService.get_knowledge_rate_limit(api_token.tenant_id) | 
					
						
							|  |  |  |                 if knowledge_rate_limit.enabled: | 
					
						
							|  |  |  |                     current_time = int(time.time() * 1000) | 
					
						
							|  |  |  |                     key = f"rate_limit_{api_token.tenant_id}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     redis_client.zadd(key, {current_time: current_time}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     redis_client.zremrangebyscore(key, 0, current_time - 60000) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     request_count = redis_client.zcard(key) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     if request_count > knowledge_rate_limit.limit: | 
					
						
							|  |  |  |                         # add ratelimit record | 
					
						
							|  |  |  |                         rate_limit_log = RateLimitLog( | 
					
						
							|  |  |  |                             tenant_id=api_token.tenant_id, | 
					
						
							|  |  |  |                             subscription_plan=knowledge_rate_limit.subscription_plan, | 
					
						
							|  |  |  |                             operation="knowledge", | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                         db.session.add(rate_limit_log) | 
					
						
							|  |  |  |                         db.session.commit() | 
					
						
							|  |  |  |                         raise Forbidden( | 
					
						
							|  |  |  |                             "Sorry, you have reached the knowledge base request rate limit of your subscription." | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |             return view(*args, **kwargs) | 
					
						
							| 
									
										
										
										
											2024-04-02 17:55:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return decorated | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return interceptor | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | def validate_dataset_token(view=None): | 
					
						
							|  |  |  |     def decorator(view): | 
					
						
							|  |  |  |         @wraps(view) | 
					
						
							|  |  |  |         def decorated(*args, **kwargs): | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             api_token = validate_and_get_api_token("dataset") | 
					
						
							|  |  |  |             tenant_account_join = ( | 
					
						
							|  |  |  |                 db.session.query(Tenant, TenantAccountJoin) | 
					
						
							|  |  |  |                 .filter(Tenant.id == api_token.tenant_id) | 
					
						
							|  |  |  |                 .filter(TenantAccountJoin.tenant_id == Tenant.id) | 
					
						
							|  |  |  |                 .filter(TenantAccountJoin.role.in_(["owner"])) | 
					
						
							|  |  |  |                 .filter(Tenant.status == TenantStatus.NORMAL) | 
					
						
							|  |  |  |                 .one_or_none() | 
					
						
							|  |  |  |             )  # TODO: only owner information is required, so only one is returned. | 
					
						
							| 
									
										
										
										
											2023-09-27 16:06:32 +08:00
										 |  |  |             if tenant_account_join: | 
					
						
							|  |  |  |                 tenant, ta = tenant_account_join | 
					
						
							| 
									
										
										
										
											2025-02-26 18:45:12 +08:00
										 |  |  |                 account = db.session.query(Account).filter(Account.id == ta.account_id).first() | 
					
						
							| 
									
										
										
										
											2023-09-27 16:06:32 +08:00
										 |  |  |                 # Login admin | 
					
						
							|  |  |  |                 if account: | 
					
						
							|  |  |  |                     account.current_tenant = tenant | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |                     current_app.login_manager._update_request_context_with_user(account)  # type: ignore | 
					
						
							|  |  |  |                     user_logged_in.send(current_app._get_current_object(), user=_get_user())  # type: ignore | 
					
						
							| 
									
										
										
										
											2023-09-27 16:06:32 +08:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2024-01-26 12:47:42 +08:00
										 |  |  |                     raise Unauthorized("Tenant owner account does not exist.") | 
					
						
							| 
									
										
										
										
											2023-09-27 16:06:32 +08:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-01-26 12:47:42 +08:00
										 |  |  |                 raise Unauthorized("Tenant does not exist.") | 
					
						
							| 
									
										
										
										
											2023-09-27 16:06:32 +08:00
										 |  |  |             return view(api_token.tenant_id, *args, **kwargs) | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |         return decorated | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if view: | 
					
						
							|  |  |  |         return decorator(view) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # if view is None, it means that the decorator is used without parentheses | 
					
						
							|  |  |  |     # use the decorator as a function for method_decorators | 
					
						
							|  |  |  |     return decorator | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-07 18:04:41 +08:00
										 |  |  | def validate_and_get_api_token(scope: str | None = None): | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Validate and get API token. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |     auth_header = request.headers.get("Authorization") | 
					
						
							|  |  |  |     if auth_header is None or " " not in auth_header: | 
					
						
							| 
									
										
										
										
											2023-08-18 20:32:44 +08:00
										 |  |  |         raise Unauthorized("Authorization header must be provided and start with 'Bearer'") | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auth_scheme, auth_token = auth_header.split(None, 1) | 
					
						
							|  |  |  |     auth_scheme = auth_scheme.lower() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |     if auth_scheme != "bearer": | 
					
						
							| 
									
										
										
										
											2023-08-18 20:32:44 +08:00
										 |  |  |         raise Unauthorized("Authorization scheme must be 'Bearer'") | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-07 18:04:41 +08:00
										 |  |  |     current_time = datetime.now(UTC).replace(tzinfo=None) | 
					
						
							|  |  |  |     cutoff_time = current_time - timedelta(minutes=1) | 
					
						
							|  |  |  |     with Session(db.engine, expire_on_commit=False) as session: | 
					
						
							|  |  |  |         update_stmt = ( | 
					
						
							|  |  |  |             update(ApiToken) | 
					
						
							| 
									
										
										
										
											2025-01-22 11:01:45 +08:00
										 |  |  |             .where( | 
					
						
							|  |  |  |                 ApiToken.token == auth_token, | 
					
						
							|  |  |  |                 (ApiToken.last_used_at.is_(None) | (ApiToken.last_used_at < cutoff_time)), | 
					
						
							|  |  |  |                 ApiToken.type == scope, | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2025-01-07 18:04:41 +08:00
										 |  |  |             .values(last_used_at=current_time) | 
					
						
							|  |  |  |             .returning(ApiToken) | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2025-01-07 18:04:41 +08:00
										 |  |  |         result = session.execute(update_stmt) | 
					
						
							|  |  |  |         api_token = result.scalar_one_or_none() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if not api_token: | 
					
						
							|  |  |  |             stmt = select(ApiToken).where(ApiToken.token == auth_token, ApiToken.type == scope) | 
					
						
							|  |  |  |             api_token = session.scalar(stmt) | 
					
						
							|  |  |  |             if not api_token: | 
					
						
							|  |  |  |                 raise Unauthorized("Access token is invalid") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             session.commit() | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return api_token | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | def create_or_update_end_user_for_user_id(app_model: App, user_id: Optional[str] = None) -> EndUser: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Create or update session terminal based on user ID. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not user_id: | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         user_id = "DEFAULT-USER" | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |     end_user = ( | 
					
						
							|  |  |  |         db.session.query(EndUser) | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |         .filter( | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             EndUser.tenant_id == app_model.tenant_id, | 
					
						
							|  |  |  |             EndUser.app_id == app_model.id, | 
					
						
							|  |  |  |             EndUser.session_id == user_id, | 
					
						
							|  |  |  |             EndUser.type == "service_api", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         .first() | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if end_user is None: | 
					
						
							|  |  |  |         end_user = EndUser( | 
					
						
							|  |  |  |             tenant_id=app_model.tenant_id, | 
					
						
							|  |  |  |             app_id=app_model.id, | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             type="service_api", | 
					
						
							| 
									
										
										
										
											2025-01-13 09:38:48 +08:00
										 |  |  |             is_anonymous=user_id == "DEFAULT-USER", | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |             session_id=user_id, | 
					
						
							| 
									
										
										
										
											2024-02-28 16:09:56 +08:00
										 |  |  |         ) | 
					
						
							|  |  |  |         db.session.add(end_user) | 
					
						
							|  |  |  |         db.session.commit() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return end_user | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DatasetApiResource(Resource): | 
					
						
							|  |  |  |     method_decorators = [validate_dataset_token] |