From 7311f1ba6d66490bcedda29ad582b62e7f3a3952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Mon, 13 Jul 2026 10:55:39 +0800 Subject: [PATCH] chore: sandbox plan should not execution special workflow (#38662) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/controllers/service_api/app/error.py | 6 + api/controllers/service_api/app/workflow.py | 14 ++ api/openapi/markdown/service-openapi.md | 2 +- .../service_api/app/test_workflow.py | 114 +++++++++++++++- .../components/develop/__tests__/md.spec.tsx | 9 ++ ...rkflow-version-api-upgrade-notice.spec.tsx | 129 ++++++++++++++++++ web/app/components/develop/md.tsx | 6 +- .../develop/template/template_workflow.en.mdx | 3 + .../develop/template/template_workflow.ja.mdx | 3 + .../develop/template/template_workflow.zh.mdx | 3 + .../workflow-version-api-upgrade-notice.tsx | 43 ++++++ web/i18n/ar-TN/billing.json | 2 + web/i18n/de-DE/billing.json | 2 + web/i18n/en-US/billing.json | 2 + web/i18n/es-ES/billing.json | 2 + web/i18n/fa-IR/billing.json | 2 + web/i18n/fr-FR/billing.json | 2 + web/i18n/hi-IN/billing.json | 2 + web/i18n/id-ID/billing.json | 2 + web/i18n/it-IT/billing.json | 2 + web/i18n/ja-JP/billing.json | 2 + web/i18n/ko-KR/billing.json | 2 + web/i18n/nl-NL/billing.json | 2 + web/i18n/pl-PL/billing.json | 2 + web/i18n/pt-BR/billing.json | 2 + web/i18n/ro-RO/billing.json | 2 + web/i18n/ru-RU/billing.json | 2 + web/i18n/sl-SI/billing.json | 2 + web/i18n/th-TH/billing.json | 2 + web/i18n/tr-TR/billing.json | 2 + web/i18n/uk-UA/billing.json | 2 + web/i18n/vi-VN/billing.json | 2 + web/i18n/zh-Hans/billing.json | 2 + web/i18n/zh-Hant/billing.json | 2 + 34 files changed, 374 insertions(+), 4 deletions(-) create mode 100644 web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx create mode 100644 web/app/components/develop/workflow-version-api-upgrade-notice.tsx diff --git a/api/controllers/service_api/app/error.py b/api/controllers/service_api/app/error.py index 710fc7878fb..970e1deb3e4 100644 --- a/api/controllers/service_api/app/error.py +++ b/api/controllers/service_api/app/error.py @@ -31,6 +31,12 @@ class NotWorkflowAppError(BaseHTTPException): code = 400 +class WorkflowVersionExecutionNotAllowedError(BaseHTTPException): + error_code = "workflow_version_execution_not_allowed" + description = "Workflow version execution is not available on your current plan. Please upgrade to a paid plan." + code = 403 + + class ConversationCompletedError(BaseHTTPException): error_code = "conversation_completed" description = "The conversation has ended. Please start a new conversation." diff --git a/api/controllers/service_api/app/workflow.py b/api/controllers/service_api/app/workflow.py index 3a307c7236b..1041c038d72 100644 --- a/api/controllers/service_api/app/workflow.py +++ b/api/controllers/service_api/app/workflow.py @@ -11,6 +11,7 @@ from pydantic.json_schema import SkipJsonSchema from sqlalchemy.orm import Session, sessionmaker from werkzeug.exceptions import BadRequest, InternalServerError, NotFound +from configs import dify_config from controllers.common.controller_schemas import WorkflowRunPayload as WorkflowRunPayloadBase from controllers.common.fields import GeneratedAppResponse, SimpleResultResponse from controllers.common.schema import ( @@ -27,6 +28,7 @@ from controllers.service_api.app.error import ( ProviderModelCurrentlyNotSupportError, ProviderNotInitializeError, ProviderQuotaExceededError, + WorkflowVersionExecutionNotAllowedError, ) from controllers.service_api.schema import ( expect_user_json, @@ -43,6 +45,7 @@ from core.errors.error import ( QuotaExceededError, ) from core.helper.trace_id_helper import get_external_trace_id, get_trace_session_id, omit_trace_session_id_from_payload +from enums.cloud_plan import CloudPlan from extensions.ext_database import db from extensions.ext_redis import redis_client from fields.base import ResponseModel @@ -56,6 +59,7 @@ from libs.helper import dump_response, to_timestamp from models.model import App, AppMode, EndUser from repositories.factory import DifyAPIRepositoryFactory from services.app_generate_service import AppGenerateService +from services.billing_service import BillingService from services.errors.app import IsDraftWorkflowError, WorkflowIdFormatError, WorkflowNotFoundError from services.errors.llm import InvokeRateLimitError from services.workflow_app_service import WorkflowAppService @@ -396,6 +400,10 @@ class WorkflowRunByIdApi(Resource): "- `completion_request_error` : Workflow execution request failed.\n" "- `invalid_param` : Required parameter missing or invalid." ), + 403: ( + "`workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the " + "current plan. Upgrade to a paid plan." + ), 404: "`not_found` : Workflow not found.", 429: ( "- `too_many_requests` : Too many concurrent requests for this app.\n" @@ -421,6 +429,7 @@ class WorkflowRunByIdApi(Resource): 200: "Workflow executed successfully", 400: "Bad request - invalid parameters or workflow issues", 401: "Unauthorized - invalid API token", + 403: "Forbidden - upgrade to a paid plan to execute a specific workflow version", 404: "Workflow not found", 429: "Rate limit exceeded", 500: "Internal server error", @@ -442,6 +451,11 @@ class WorkflowRunByIdApi(Resource): if app_mode != AppMode.WORKFLOW: raise NotWorkflowAppError() + if dify_config.BILLING_ENABLED: + billing_info = BillingService.get_info(app_model.tenant_id, exclude_vector_space=True) + if billing_info["enabled"] and billing_info["subscription"]["plan"] == CloudPlan.SANDBOX: + raise WorkflowVersionExecutionNotAllowedError() + payload = WorkflowRunPayload.model_validate(omit_trace_session_id_from_payload(service_api_ns.payload) or {}) args = payload.model_dump(exclude_none=True) trace_session_id = get_trace_session_id(request) diff --git a/api/openapi/markdown/service-openapi.md b/api/openapi/markdown/service-openapi.md index 32b9c388506..c442b7a0ff7 100644 --- a/api/openapi/markdown/service-openapi.md +++ b/api/openapi/markdown/service-openapi.md @@ -2235,7 +2235,7 @@ Execute a specific workflow version identified by its ID. Useful for running a p | 200 | Successful response. The content type and structure depend on the `response_mode` parameter in the request. - If `response_mode` is `blocking`, returns `application/json` with a `WorkflowBlockingResponse` object. - If `response_mode` is `streaming`, returns `text/event-stream` with a stream of `ChunkWorkflowEvent` objects. | **application/json**: [GeneratedAppResponse](#generatedappresponse)
**text/event-stream**: [GeneratedAppResponse](#generatedappresponse)
| | 400 | - `not_workflow_app` : App mode does not match the API route. - `bad_request` : Workflow is a draft or has an invalid ID format. - `provider_not_initialize` : No valid model provider credentials found. - `provider_quota_exceeded` : Model provider quota exhausted. - `model_currently_not_support` : Current model unavailable. - `completion_request_error` : Workflow execution request failed. - `invalid_param` : Required parameter missing or invalid. | | | 401 | Unauthorized - invalid API token | | -| 403 | Forbidden - token scope, app, dataset, or workspace access denied | | +| 403 | `workflow_version_execution_not_allowed` : Workflow version execution is unavailable on the current plan. Upgrade to a paid plan. | | | 404 | `not_found` : Workflow not found. | | | 429 | - `too_many_requests` : Too many concurrent requests for this app. - `rate_limit_error` : The upstream model provider rate limit was exceeded. | | | 500 | `internal_server_error` : Internal server error. | | diff --git a/api/tests/unit_tests/controllers/service_api/app/test_workflow.py b/api/tests/unit_tests/controllers/service_api/app/test_workflow.py index f40a3b7ae7b..f381bd3fbc4 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_workflow.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_workflow.py @@ -26,7 +26,7 @@ from flask import Flask from sqlalchemy.orm import sessionmaker from werkzeug.exceptions import BadRequest, NotFound -from controllers.service_api.app.error import NotWorkflowAppError +from controllers.service_api.app.error import NotWorkflowAppError, WorkflowVersionExecutionNotAllowedError from controllers.service_api.app.workflow import ( AppQueueManager, DifyAPIRepositoryFactory, @@ -42,11 +42,13 @@ from controllers.service_api.app.workflow import ( ) from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError from core.app.entities.app_invoke_entities import InvokeFrom +from enums.cloud_plan import CloudPlan from graphon.enums import WorkflowExecutionStatus from models.enums import CreatorUserRole, WorkflowRunTriggeredFrom from models.model import App, AppMode, EndUser from models.workflow import WorkflowAppLog, WorkflowAppLogCreatedFrom, WorkflowRun, WorkflowType from services.app_generate_service import AppGenerateService +from services.billing_service import BillingService from services.errors.app import IsDraftWorkflowError, WorkflowNotFoundError from services.errors.llm import InvokeRateLimitError from services.workflow_app_service import LogView, LogViewDetails, WorkflowAppService @@ -570,9 +572,117 @@ class TestWorkflowRunApi: with pytest.raises(InvokeRateLimitHttpError): handler(api, session=Mock(), app_model=app_model, end_user=end_user) + def test_sandbox_billing_does_not_gate_default_workflow_run( + self, app: Flask, monkeypatch: pytest.MonkeyPatch + ) -> None: + workflow_module = sys.modules["controllers.service_api.app.workflow"] + monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", True) + + billing_get_info = Mock(return_value={"enabled": True, "subscription": {"plan": CloudPlan.SANDBOX}}) + generate = Mock(return_value={"result": "ok"}) + monkeypatch.setattr(BillingService, "get_info", billing_get_info) + monkeypatch.setattr(AppGenerateService, "generate", generate) + + api = WorkflowRunApi() + handler = unwrap(api.post) + + with app.test_request_context("/workflows/run", method="POST", json={"inputs": {}}): + response = handler( + api, + session=Mock(), + app_model=_make_app_model(), + end_user=_make_end_user(), + ) + + assert response.get_json() == {"result": "ok"} + billing_get_info.assert_not_called() + generate.assert_called_once() + class TestWorkflowRunByIdApi: + def test_rejects_sandbox_plan_with_upgrade_error(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + workflow_module = sys.modules["controllers.service_api.app.workflow"] + monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", True) + + billing_get_info = Mock(return_value={"enabled": True, "subscription": {"plan": CloudPlan.SANDBOX}}) + generate = Mock() + monkeypatch.setattr(BillingService, "get_info", billing_get_info) + monkeypatch.setattr(AppGenerateService, "generate", generate) + + api = WorkflowRunByIdApi() + handler = unwrap(api.post) + app_model = _make_app_model() + + with app.test_request_context("/workflows/w1/run", method="POST", json={"inputs": {}}): + with pytest.raises(WorkflowVersionExecutionNotAllowedError) as exc_info: + handler( + api, + session=Mock(), + app_model=app_model, + end_user=_make_end_user(), + workflow_id="w1", + ) + + billing_get_info.assert_called_once_with(app_model.tenant_id, exclude_vector_space=True) + generate.assert_not_called() + assert exc_info.value.code == 403 + assert exc_info.value.error_code == "workflow_version_execution_not_allowed" + assert exc_info.value.description == ( + "Workflow version execution is not available on your current plan. Please upgrade to a paid plan." + ) + assert exc_info.value.data == { + "code": "workflow_version_execution_not_allowed", + "message": exc_info.value.description, + "status": 403, + } + + @pytest.mark.parametrize( + ("billing_config_enabled", "billing_enabled", "plan"), + [ + (False, True, CloudPlan.SANDBOX), + (True, False, CloudPlan.SANDBOX), + (True, True, CloudPlan.PROFESSIONAL), + ], + ) + def test_allows_execution_outside_enabled_sandbox_plan( + self, + app: Flask, + monkeypatch: pytest.MonkeyPatch, + billing_config_enabled: bool, + billing_enabled: bool, + plan: CloudPlan, + ) -> None: + workflow_module = sys.modules["controllers.service_api.app.workflow"] + monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", billing_config_enabled) + + billing_get_info = Mock(return_value={"enabled": billing_enabled, "subscription": {"plan": plan}}) + generate = Mock(return_value={"result": "ok"}) + monkeypatch.setattr(BillingService, "get_info", billing_get_info) + monkeypatch.setattr(AppGenerateService, "generate", generate) + + api = WorkflowRunByIdApi() + handler = unwrap(api.post) + app_model = _make_app_model() + + with app.test_request_context("/workflows/w1/run", method="POST", json={"inputs": {}}): + response = handler( + api, + session=Mock(), + app_model=app_model, + end_user=_make_end_user(), + workflow_id="w1", + ) + + assert response.get_json() == {"result": "ok"} + generate.assert_called_once() + if billing_config_enabled: + billing_get_info.assert_called_once_with(app_model.tenant_id, exclude_vector_space=True) + else: + billing_get_info.assert_not_called() + def test_not_found(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + workflow_module = sys.modules["controllers.service_api.app.workflow"] + monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", False) monkeypatch.setattr( AppGenerateService, "generate", @@ -589,6 +699,8 @@ class TestWorkflowRunByIdApi: handler(api, session=Mock(), app_model=app_model, end_user=end_user, workflow_id="w1") def test_draft_workflow(self, app: Flask, monkeypatch: pytest.MonkeyPatch) -> None: + workflow_module = sys.modules["controllers.service_api.app.workflow"] + monkeypatch.setattr(workflow_module.dify_config, "BILLING_ENABLED", False) monkeypatch.setattr( AppGenerateService, "generate", diff --git a/web/app/components/develop/__tests__/md.spec.tsx b/web/app/components/develop/__tests__/md.spec.tsx index 4e3144229d9..756ee34b56d 100644 --- a/web/app/components/develop/__tests__/md.spec.tsx +++ b/web/app/components/develop/__tests__/md.spec.tsx @@ -28,6 +28,15 @@ describe('md.tsx components', () => { expect(link).toHaveAttribute('href', '#get-messages') }) + it('should render a title action immediately after the title', () => { + render(Upgrade} />) + + const heading = screen.getByRole('heading', { level: 2 }) + expect(heading).toHaveClass('flex', 'items-center', 'gap-x-2') + expect(heading.children[0]).toBe(screen.getByRole('link', { name: 'Get Messages' })) + expect(heading.children[1]).toBe(screen.getByRole('button', { name: 'Upgrade' })) + }) + it('should render an anchor span with correct id', () => { const { container } = render() const anchor = container.querySelector('#get-messages') diff --git a/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx b/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx new file mode 100644 index 00000000000..161e01eb4b3 --- /dev/null +++ b/web/app/components/develop/__tests__/workflow-version-api-upgrade-notice.spec.tsx @@ -0,0 +1,129 @@ +import { fireEvent, render, screen, within } from '@testing-library/react' +import { Plan } from '@/app/components/billing/type' +import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice' + +let mockPlanType = Plan.professional +let mockEnableBilling = true +let mockIsFetchedPlan = true + +vi.mock('@/context/provider-context', () => ({ + useProviderContext: () => ({ + plan: { type: mockPlanType }, + enableBilling: mockEnableBilling, + isFetchedPlan: mockIsFetchedPlan, + }), +})) + +vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({ + PlanUpgradeModal: ({ + show, + title, + description, + onClose, + }: { + show: boolean + title: string + description: string + onClose: () => void + }) => + show ? ( +
+
{title}
+
{description}
+ +
+ ) : null, +})) + +vi.mock('@/app/components/billing/upgrade-btn', () => ({ + default: ({ + onClick, + isShort, + size, + loc, + className, + }: { + onClick: () => void + isShort: boolean + size: string + loc: string + className: string + }) => ( + + ), +})) + +describe('WorkflowVersionApiUpgradeNotice', () => { + beforeEach(() => { + mockPlanType = Plan.professional + mockEnableBilling = true + mockIsFetchedPlan = true + }) + + it('should not render before the plan is fetched', () => { + mockPlanType = Plan.sandbox + mockIsFetchedPlan = false + + render() + + expect( + screen.queryByRole('button', { name: 'billing.upgradeBtn.encourageShort' }), + ).not.toBeInTheDocument() + }) + + it('should not render when billing is disabled', () => { + mockPlanType = Plan.sandbox + mockEnableBilling = false + + render() + + expect( + screen.queryByRole('button', { name: 'billing.upgradeBtn.encourageShort' }), + ).not.toBeInTheDocument() + }) + + it('should not render for paid plans', () => { + render() + + expect( + screen.queryByRole('button', { name: 'billing.upgradeBtn.encourageShort' }), + ).not.toBeInTheDocument() + }) + + it('should show a small upgrade button and open the plan upgrade modal for sandbox plans', () => { + mockPlanType = Plan.sandbox + + render() + + expect(screen.queryByText('billing.upgrade.workflowVersionRun.title')).not.toBeInTheDocument() + expect( + screen.queryByText('billing.upgrade.workflowVersionRun.description'), + ).not.toBeInTheDocument() + + const upgradeButton = screen.getByRole('button', { name: 'billing.upgradeBtn.encourageShort' }) + expect(upgradeButton).toHaveAttribute('data-is-short', 'true') + expect(upgradeButton).toHaveAttribute('data-size', 'custom') + expect(upgradeButton).toHaveAttribute('data-loc', 'workflow-version-api-docs') + expect(upgradeButton).toHaveClass('h-5!', 'rounded-md!', 'px-1!') + + fireEvent.click(upgradeButton) + + const modal = screen.getByTestId('plan-upgrade-modal') + expect(modal).toBeInTheDocument() + expect(within(modal).getByText('billing.upgrade.workflowVersionRun.title')).toBeInTheDocument() + expect( + within(modal).getByText('billing.upgrade.workflowVersionRun.description'), + ).toBeInTheDocument() + }) +}) diff --git a/web/app/components/develop/md.tsx b/web/app/components/develop/md.tsx index e853f6f3fd9..58358f884f9 100644 --- a/web/app/components/develop/md.tsx +++ b/web/app/components/develop/md.tsx @@ -14,9 +14,10 @@ type IHeaderingProps = { method: 'PUT' | 'DELETE' | 'GET' | 'POST' | 'PATCH' title: string name: string + titleAction?: React.ReactNode } -export const Heading = function H2({ url, method, title, name }: IHeaderingProps) { +export const Heading = function H2({ url, method, title, name, titleAction }: IHeaderingProps) { let style = '' switch (method) { case 'PUT': @@ -52,10 +53,11 @@ export const Heading = function H2({ url, method, title, name }: IHeaderingProps {/* */} {url} -

+

{title} + {titleAction}

) diff --git a/web/app/components/develop/template/template_workflow.en.mdx b/web/app/components/develop/template/template_workflow.en.mdx index a5b2d651007..daff5d5f955 100644 --- a/web/app/components/develop/template/template_workflow.en.mdx +++ b/web/app/components/develop/template/template_workflow.en.mdx @@ -1,5 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx' # Workflow App API @@ -642,6 +643,7 @@ Workflow applications offers non-session support and is ideal for translation, a method='POST' title='Execute Specific Workflow' name='#Execute-Specific-Workflow' + titleAction={} /> @@ -823,6 +825,7 @@ Workflow applications offers non-session support and is ideal for translation, a - 400, `draft_workflow_error`, Cannot use draft workflow version - 400, `workflow_id_format_error`, Workflow ID format error, UUID format required - 400, `workflow_request_error`, Workflow execution failed + - 403, `workflow_version_execution_not_allowed`, Workflow version execution is not available on your current plan. Please upgrade to a paid plan. - 500, Internal service error diff --git a/web/app/components/develop/template/template_workflow.ja.mdx b/web/app/components/develop/template/template_workflow.ja.mdx index 881e5de7e7a..a9def5bb14e 100644 --- a/web/app/components/develop/template/template_workflow.ja.mdx +++ b/web/app/components/develop/template/template_workflow.ja.mdx @@ -1,5 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx' # ワークフローアプリ API @@ -642,6 +643,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' method='POST' title='特定バージョンのワークフローを実行' name='#Execute-Specific-Workflow' + titleAction={} /> @@ -818,6 +820,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - 400,`draft_workflow_error`,ドラフトワークフローバージョンを使用できません - 400,`workflow_id_format_error`,ワークフローID形式エラー、UUID形式が必要です - 400,`workflow_request_error`,ワークフロー実行に失敗しました + - 403,`workflow_version_execution_not_allowed`,現在のプランではワークフローバージョンを実行できません。有料プランにアップグレードしてください - 500,サービス内部異常 diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx index 602ff8779d4..3ed68af921d 100644 --- a/web/app/components/develop/template/template_workflow.zh.mdx +++ b/web/app/components/develop/template/template_workflow.zh.mdx @@ -1,5 +1,6 @@ import { CodeGroup } from '../code.tsx' import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' +import WorkflowVersionApiUpgradeNotice from '../workflow-version-api-upgrade-notice.tsx' # Workflow 应用 API @@ -632,6 +633,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 method='POST' title='执行指定版本 Workflow' name='#Execute-Specific-Workflow' + titleAction={} /> @@ -811,6 +813,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 - 400,`draft_workflow_error`,无法使用草稿工作流版本 - 400,`workflow_id_format_error`,工作流ID格式错误,需要UUID格式 - 400,`workflow_request_error`,workflow 执行失败 + - 403,`workflow_version_execution_not_allowed`,当前套餐不支持执行工作流版本,请升级至付费套餐 - 500,服务内部异常 diff --git a/web/app/components/develop/workflow-version-api-upgrade-notice.tsx b/web/app/components/develop/workflow-version-api-upgrade-notice.tsx new file mode 100644 index 00000000000..bdb799f9c54 --- /dev/null +++ b/web/app/components/develop/workflow-version-api-upgrade-notice.tsx @@ -0,0 +1,43 @@ +'use client' + +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' +import { Plan } from '@/app/components/billing/type' +import UpgradeBtn from '@/app/components/billing/upgrade-btn' +import { useProviderContext } from '@/context/provider-context' + +const WorkflowVersionApiUpgradeNotice = () => { + const { t } = useTranslation('billing') + const { plan, enableBilling, isFetchedPlan } = useProviderContext() + const [isPlanUpgradeModalOpen, setIsPlanUpgradeModalOpen] = useState(false) + + if (!isFetchedPlan || !enableBilling || plan.type !== Plan.sandbox) return null + + const title = t(($) => $['upgrade.workflowVersionRun.title']) + const description = t(($) => $['upgrade.workflowVersionRun.description']) + + return ( + <> + + setIsPlanUpgradeModalOpen(true)} + /> + + {isPlanUpgradeModalOpen && ( + setIsPlanUpgradeModalOpen(false)} + title={title} + description={description} + /> + )} + + ) +} + +export default WorkflowVersionApiUpgradeNotice diff --git a/web/i18n/ar-TN/billing.json b/web/i18n/ar-TN/billing.json index 98920cb4558..5caf893b963 100644 --- a/web/i18n/ar-TN/billing.json +++ b/web/i18n/ar-TN/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "قم بالترقية لتحميل عدة مستندات دفعة واحدة", "upgrade.workflowRestore.description": "استعادة إصدارات سير العمل غير متاحة في خطتك الحالية.", "upgrade.workflowRestore.title": "قم بالترقية لاستعادة إصدارات سير العمل", + "upgrade.workflowVersionRun.description": "تشغيل إصدار محدد من سير العمل باستخدام معرّف سير العمل غير متاح في خطتك الحالية.", + "upgrade.workflowVersionRun.title": "قم بالترقية لتشغيل إصدارات محددة من سير العمل", "upgradeBtn.encourage": "الترقية الآن", "upgradeBtn.encourageShort": "ترقية", "upgradeBtn.plain": "عرض الخطة", diff --git a/web/i18n/de-DE/billing.json b/web/i18n/de-DE/billing.json index 9e485081ad2..73ab56beb96 100644 --- a/web/i18n/de-DE/billing.json +++ b/web/i18n/de-DE/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Upgrade, um mehrere Dokumente gleichzeitig hochzuladen", "upgrade.workflowRestore.description": "Die Wiederherstellung von Workflow-Versionen ist in Ihrem aktuellen Tarif nicht verfügbar.", "upgrade.workflowRestore.title": "Upgraden, um Workflow-Versionen wiederherzustellen", + "upgrade.workflowVersionRun.description": "Das Ausführen einer bestimmten Workflow-Version über die Workflow-ID ist in Ihrem aktuellen Tarif nicht verfügbar.", + "upgrade.workflowVersionRun.title": "Upgraden, um bestimmte Workflow-Versionen auszuführen", "upgradeBtn.encourage": "Jetzt Upgraden", "upgradeBtn.encourageShort": "Upgraden", "upgradeBtn.plain": "Tarif Upgraden", diff --git a/web/i18n/en-US/billing.json b/web/i18n/en-US/billing.json index 68a6f212f14..c8b6e84621e 100644 --- a/web/i18n/en-US/billing.json +++ b/web/i18n/en-US/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Upgrade to upload multiple documents at once", "upgrade.workflowRestore.description": "Workflow version restoration is not available on your current plan.", "upgrade.workflowRestore.title": "Upgrade to restore workflow versions", + "upgrade.workflowVersionRun.description": "Running a specific workflow version by workflow ID is not available on your current plan.", + "upgrade.workflowVersionRun.title": "Upgrade to run specific workflow versions", "upgradeBtn.encourage": "Upgrade Now", "upgradeBtn.encourageShort": "Upgrade", "upgradeBtn.plain": "View Plan", diff --git a/web/i18n/es-ES/billing.json b/web/i18n/es-ES/billing.json index fa79bd8d8d5..c42d48ddd28 100644 --- a/web/i18n/es-ES/billing.json +++ b/web/i18n/es-ES/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Actualiza para subir varios documentos a la vez", "upgrade.workflowRestore.description": "La restauración de versiones del flujo de trabajo no está disponible en tu plan actual.", "upgrade.workflowRestore.title": "Actualiza para restaurar versiones del flujo de trabajo", + "upgrade.workflowVersionRun.description": "Ejecutar una versión específica del flujo de trabajo mediante su ID no está disponible en tu plan actual.", + "upgrade.workflowVersionRun.title": "Actualiza para ejecutar versiones específicas del flujo de trabajo", "upgradeBtn.encourage": "Actualizar Ahora", "upgradeBtn.encourageShort": "Actualizar", "upgradeBtn.plain": "Actualizar Plan", diff --git a/web/i18n/fa-IR/billing.json b/web/i18n/fa-IR/billing.json index 6e81d5edef8..17d3ec6dd07 100644 --- a/web/i18n/fa-IR/billing.json +++ b/web/i18n/fa-IR/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "ارتقا برای آپلود همزمان چندین سند", "upgrade.workflowRestore.description": "بازیابی نسخه‌های گردش‌کار در طرح فعلی شما در دسترس نیست.", "upgrade.workflowRestore.title": "برای بازیابی نسخه‌های گردش‌کار ارتقا دهید", + "upgrade.workflowVersionRun.description": "اجرای نسخه‌ای خاص از گردش‌کار با شناسه گردش‌کار در طرح فعلی شما در دسترس نیست.", + "upgrade.workflowVersionRun.title": "برای اجرای نسخه‌های خاص گردش‌کار ارتقا دهید", "upgradeBtn.encourage": "هم اکنون ارتقاء دهید", "upgradeBtn.encourageShort": "ارتقاء دهید", "upgradeBtn.plain": "ارتقاء طرح", diff --git a/web/i18n/fr-FR/billing.json b/web/i18n/fr-FR/billing.json index 724f26b69c8..96108c61828 100644 --- a/web/i18n/fr-FR/billing.json +++ b/web/i18n/fr-FR/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Passez à la version supérieure pour télécharger plusieurs documents à la fois", "upgrade.workflowRestore.description": "La restauration des versions du workflow n'est pas disponible avec votre plan actuel.", "upgrade.workflowRestore.title": "Mettez à niveau pour restaurer les versions du workflow", + "upgrade.workflowVersionRun.description": "L’exécution d’une version spécifique du workflow via son ID n’est pas disponible avec votre plan actuel.", + "upgrade.workflowVersionRun.title": "Mettez à niveau pour exécuter des versions spécifiques du workflow", "upgradeBtn.encourage": "Mettre à niveau maintenant", "upgradeBtn.encourageShort": "Mise à niveau", "upgradeBtn.plain": "Mettre à jour le plan", diff --git a/web/i18n/hi-IN/billing.json b/web/i18n/hi-IN/billing.json index db27c7cb954..68f4cc4dc04 100644 --- a/web/i18n/hi-IN/billing.json +++ b/web/i18n/hi-IN/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "एक बार में कई दस्तावेज़ अपलोड करने के लिए अपग्रेड करें", "upgrade.workflowRestore.description": "आपकी वर्तमान योजना में वर्कफ़्लो संस्करण पुनर्स्थापना उपलब्ध नहीं है।", "upgrade.workflowRestore.title": "वर्कफ़्लो संस्करणों को पुनर्स्थापित करने के लिए अपग्रेड करें", + "upgrade.workflowVersionRun.description": "वर्कफ़्लो ID के ज़रिए किसी विशिष्ट वर्कफ़्लो संस्करण को चलाना आपकी वर्तमान योजना में उपलब्ध नहीं है।", + "upgrade.workflowVersionRun.title": "विशिष्ट वर्कफ़्लो संस्करण चलाने के लिए अपग्रेड करें", "upgradeBtn.encourage": "अभी अपग्रेड करें", "upgradeBtn.encourageShort": "अपग्रेड करें", "upgradeBtn.plain": "योजना अपग्रेड करें", diff --git a/web/i18n/id-ID/billing.json b/web/i18n/id-ID/billing.json index f0b2b4a27a7..f5bb448fa29 100644 --- a/web/i18n/id-ID/billing.json +++ b/web/i18n/id-ID/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Tingkatkan untuk mengunggah beberapa dokumen sekaligus", "upgrade.workflowRestore.description": "Pemulihan versi workflow tidak tersedia di paket Anda saat ini.", "upgrade.workflowRestore.title": "Tingkatkan untuk memulihkan versi workflow", + "upgrade.workflowVersionRun.description": "Menjalankan versi workflow tertentu berdasarkan ID workflow tidak tersedia di paket Anda saat ini.", + "upgrade.workflowVersionRun.title": "Tingkatkan untuk menjalankan versi workflow tertentu", "upgradeBtn.encourage": "Tingkatkan Sekarang", "upgradeBtn.encourageShort": "Upgrade", "upgradeBtn.plain": "Lihat Paket", diff --git a/web/i18n/it-IT/billing.json b/web/i18n/it-IT/billing.json index d93a170ae2e..ec428b74175 100644 --- a/web/i18n/it-IT/billing.json +++ b/web/i18n/it-IT/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Aggiorna per caricare più documenti contemporaneamente", "upgrade.workflowRestore.description": "Il ripristino delle versioni del workflow non è disponibile nel tuo piano attuale.", "upgrade.workflowRestore.title": "Esegui l'upgrade per ripristinare le versioni del workflow", + "upgrade.workflowVersionRun.description": "L’esecuzione di una versione specifica del workflow tramite il relativo ID non è disponibile nel tuo piano attuale.", + "upgrade.workflowVersionRun.title": "Esegui l’upgrade per avviare versioni specifiche del workflow", "upgradeBtn.encourage": "Aggiorna Ora", "upgradeBtn.encourageShort": "Aggiorna", "upgradeBtn.plain": "Aggiorna Piano", diff --git a/web/i18n/ja-JP/billing.json b/web/i18n/ja-JP/billing.json index fc41e988675..a064e6a81ee 100644 --- a/web/i18n/ja-JP/billing.json +++ b/web/i18n/ja-JP/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "複数ドキュメントを一度にアップロードするにはアップグレード", "upgrade.workflowRestore.description": "現在のプランでは、ワークフローバージョンの復元は利用できません。", "upgrade.workflowRestore.title": "アップグレードしてワークフローバージョンを復元", + "upgrade.workflowVersionRun.description": "現在のプランでは、ワークフロー ID を指定した特定バージョンの実行は利用できません。", + "upgrade.workflowVersionRun.title": "アップグレードして特定のワークフローバージョンを実行", "upgradeBtn.encourage": "今すぐアップグレード", "upgradeBtn.encourageShort": "アップグレード", "upgradeBtn.plain": "プランを見る", diff --git a/web/i18n/ko-KR/billing.json b/web/i18n/ko-KR/billing.json index 6e9ff09b177..3304e1bc836 100644 --- a/web/i18n/ko-KR/billing.json +++ b/web/i18n/ko-KR/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "한 번에 여러 문서를 업로드하려면 업그레이드하세요", "upgrade.workflowRestore.description": "현재 플랜에서는 워크플로 버전 복원을 사용할 수 없습니다.", "upgrade.workflowRestore.title": "워크플로 버전을 복원하려면 업그레이드하세요", + "upgrade.workflowVersionRun.description": "현재 플랜에서는 워크플로 ID로 특정 워크플로 버전을 실행할 수 없습니다.", + "upgrade.workflowVersionRun.title": "특정 워크플로 버전을 실행하려면 업그레이드하세요", "upgradeBtn.encourage": "지금 업그레이드", "upgradeBtn.encourageShort": "업그레이드", "upgradeBtn.plain": "요금제 업그레이드", diff --git a/web/i18n/nl-NL/billing.json b/web/i18n/nl-NL/billing.json index 6fd899dd807..9dba9b7031e 100644 --- a/web/i18n/nl-NL/billing.json +++ b/web/i18n/nl-NL/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Upgrade to upload multiple documents at once", "upgrade.workflowRestore.description": "Het herstellen van workflowversies is niet beschikbaar in je huidige abonnement.", "upgrade.workflowRestore.title": "Upgrade om workflowversies te herstellen", + "upgrade.workflowVersionRun.description": "Een specifieke workflowversie uitvoeren via de workflow-ID is niet beschikbaar in je huidige abonnement.", + "upgrade.workflowVersionRun.title": "Upgrade om specifieke workflowversies uit te voeren", "upgradeBtn.encourage": "Upgrade Now", "upgradeBtn.encourageShort": "Upgrade", "upgradeBtn.plain": "View Plan", diff --git a/web/i18n/pl-PL/billing.json b/web/i18n/pl-PL/billing.json index a90381921e2..077fdccc47f 100644 --- a/web/i18n/pl-PL/billing.json +++ b/web/i18n/pl-PL/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Przejdź na wyższą wersję, aby przesyłać wiele dokumentów jednocześnie", "upgrade.workflowRestore.description": "Przywracanie wersji przepływu pracy nie jest dostępne w obecnym planie.", "upgrade.workflowRestore.title": "Ulepsz plan, aby przywracać wersje przepływu pracy", + "upgrade.workflowVersionRun.description": "Uruchamianie określonej wersji przepływu pracy za pomocą identyfikatora nie jest dostępne w obecnym planie.", + "upgrade.workflowVersionRun.title": "Ulepsz plan, aby uruchamiać określone wersje przepływu pracy", "upgradeBtn.encourage": "Ulepsz teraz", "upgradeBtn.encourageShort": "Ulepsz", "upgradeBtn.plain": "Ulepsz plan", diff --git a/web/i18n/pt-BR/billing.json b/web/i18n/pt-BR/billing.json index 26da124e8e7..fa40727ce66 100644 --- a/web/i18n/pt-BR/billing.json +++ b/web/i18n/pt-BR/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Atualize para enviar vários documentos de uma vez", "upgrade.workflowRestore.description": "A restauração de versões do fluxo de trabalho não está disponível no seu plano atual.", "upgrade.workflowRestore.title": "Faça upgrade para restaurar versões do fluxo de trabalho", + "upgrade.workflowVersionRun.description": "Executar uma versão específica do fluxo de trabalho pelo ID não está disponível no seu plano atual.", + "upgrade.workflowVersionRun.title": "Faça upgrade para executar versões específicas do fluxo de trabalho", "upgradeBtn.encourage": "Fazer Upgrade Agora", "upgradeBtn.encourageShort": "Upgrade", "upgradeBtn.plain": "Fazer Upgrade do Plano", diff --git a/web/i18n/ro-RO/billing.json b/web/i18n/ro-RO/billing.json index 258a7d1741f..c11423ca791 100644 --- a/web/i18n/ro-RO/billing.json +++ b/web/i18n/ro-RO/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Actualizează pentru a încărca mai multe documente odată", "upgrade.workflowRestore.description": "Restaurarea versiunilor fluxului de lucru nu este disponibilă în planul tău actual.", "upgrade.workflowRestore.title": "Fă upgrade pentru a restaura versiunile fluxului de lucru", + "upgrade.workflowVersionRun.description": "Rularea unei versiuni specifice a fluxului de lucru prin ID nu este disponibilă în planul tău actual.", + "upgrade.workflowVersionRun.title": "Fă upgrade pentru a rula versiuni specifice ale fluxului de lucru", "upgradeBtn.encourage": "Actualizează acum", "upgradeBtn.encourageShort": "Actualizează", "upgradeBtn.plain": "Actualizează planul", diff --git a/web/i18n/ru-RU/billing.json b/web/i18n/ru-RU/billing.json index c607db5df4d..6b3e44ef3b6 100644 --- a/web/i18n/ru-RU/billing.json +++ b/web/i18n/ru-RU/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Обновите версию, чтобы загружать несколько документов одновременно", "upgrade.workflowRestore.description": "Восстановление версий рабочего процесса недоступно в вашем текущем плане.", "upgrade.workflowRestore.title": "Обновите план, чтобы восстанавливать версии рабочего процесса", + "upgrade.workflowVersionRun.description": "Запуск определённой версии рабочего процесса по его идентификатору недоступен в вашем текущем плане.", + "upgrade.workflowVersionRun.title": "Обновите план, чтобы запускать определённые версии рабочего процесса", "upgradeBtn.encourage": "Обновить сейчас", "upgradeBtn.encourageShort": "Обновить", "upgradeBtn.plain": "Обновить тарифный план", diff --git a/web/i18n/sl-SI/billing.json b/web/i18n/sl-SI/billing.json index b94f44f5dd5..cb5c04f2fef 100644 --- a/web/i18n/sl-SI/billing.json +++ b/web/i18n/sl-SI/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Nadgradite za nalaganje več dokumentov hkrati", "upgrade.workflowRestore.description": "Obnovitev različic poteka dela v vašem trenutnem paketu ni na voljo.", "upgrade.workflowRestore.title": "Nadgradite za obnovitev različic poteka dela", + "upgrade.workflowVersionRun.description": "Zagon določene različice poteka dela prek njegovega ID-ja v vašem trenutnem paketu ni na voljo.", + "upgrade.workflowVersionRun.title": "Nadgradite za zagon določenih različic poteka dela", "upgradeBtn.encourage": "Nadgradi zdaj", "upgradeBtn.encourageShort": "Nadgradi", "upgradeBtn.plain": "Nadgradi načrt", diff --git a/web/i18n/th-TH/billing.json b/web/i18n/th-TH/billing.json index f33294b0f45..2d63c899db4 100644 --- a/web/i18n/th-TH/billing.json +++ b/web/i18n/th-TH/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "อัปเกรดเพื่ออัปโหลดเอกสารหลายฉบับพร้อมกัน", "upgrade.workflowRestore.description": "การกู้คืนเวอร์ชันเวิร์กโฟลว์ไม่พร้อมใช้งานในแผนปัจจุบันของคุณ", "upgrade.workflowRestore.title": "อัปเกรดเพื่อกู้คืนเวอร์ชันเวิร์กโฟลว์", + "upgrade.workflowVersionRun.description": "แผนปัจจุบันของคุณไม่รองรับการเรียกใช้เวอร์ชันเวิร์กโฟลว์ที่ระบุด้วย ID เวิร์กโฟลว์", + "upgrade.workflowVersionRun.title": "อัปเกรดเพื่อเรียกใช้เวอร์ชันเวิร์กโฟลว์ที่ระบุ", "upgradeBtn.encourage": "อัปเกรดเดี๋ยวนี้", "upgradeBtn.encourageShort": "อัพ เกรด", "upgradeBtn.plain": "แผนการอัปเกรด", diff --git a/web/i18n/tr-TR/billing.json b/web/i18n/tr-TR/billing.json index 8810a9a24b9..7032d1a3313 100644 --- a/web/i18n/tr-TR/billing.json +++ b/web/i18n/tr-TR/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Aynı anda birden fazla belge yüklemek için yükseltin", "upgrade.workflowRestore.description": "İş akışı sürümü geri yükleme mevcut planınızda kullanılamaz.", "upgrade.workflowRestore.title": "İş akışı sürümlerini geri yüklemek için yükseltin", + "upgrade.workflowVersionRun.description": "İş akışı kimliğiyle belirli bir iş akışı sürümünü çalıştırmak mevcut planınızda kullanılamaz.", + "upgrade.workflowVersionRun.title": "Belirli iş akışı sürümlerini çalıştırmak için yükseltin", "upgradeBtn.encourage": "Şimdi Yükselt", "upgradeBtn.encourageShort": "Yükselt", "upgradeBtn.plain": "Planı Yükselt", diff --git a/web/i18n/uk-UA/billing.json b/web/i18n/uk-UA/billing.json index 20838f04c75..855625fd12e 100644 --- a/web/i18n/uk-UA/billing.json +++ b/web/i18n/uk-UA/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Оновіть, щоб завантажувати кілька документів одночасно", "upgrade.workflowRestore.description": "Відновлення версій робочого процесу недоступне у вашому поточному плані.", "upgrade.workflowRestore.title": "Оновіть план, щоб відновлювати версії робочого процесу", + "upgrade.workflowVersionRun.description": "Запуск певної версії робочого процесу за його ідентифікатором недоступний у вашому поточному плані.", + "upgrade.workflowVersionRun.title": "Оновіть план, щоб запускати певні версії робочого процесу", "upgradeBtn.encourage": "Оновити зараз", "upgradeBtn.encourageShort": "Оновити", "upgradeBtn.plain": "Оновити план", diff --git a/web/i18n/vi-VN/billing.json b/web/i18n/vi-VN/billing.json index 484e7ffcbf0..a4551506595 100644 --- a/web/i18n/vi-VN/billing.json +++ b/web/i18n/vi-VN/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "Nâng cấp để tải lên nhiều tài liệu cùng lúc", "upgrade.workflowRestore.description": "Tính năng khôi phục phiên bản quy trình không khả dụng trong gói hiện tại của bạn.", "upgrade.workflowRestore.title": "Nâng cấp để khôi phục các phiên bản quy trình", + "upgrade.workflowVersionRun.description": "Gói hiện tại của bạn không hỗ trợ chạy một phiên bản quy trình cụ thể bằng ID quy trình.", + "upgrade.workflowVersionRun.title": "Nâng cấp để chạy các phiên bản quy trình cụ thể", "upgradeBtn.encourage": "Nâng cấp Ngay", "upgradeBtn.encourageShort": "Nâng cấp", "upgradeBtn.plain": "Nâng cấp Kế hoạch", diff --git a/web/i18n/zh-Hans/billing.json b/web/i18n/zh-Hans/billing.json index fb3dac1187a..f637fe00aee 100644 --- a/web/i18n/zh-Hans/billing.json +++ b/web/i18n/zh-Hans/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "升级以一次性上传多个文档", "upgrade.workflowRestore.description": "当前套餐不支持恢复工作流版本。", "upgrade.workflowRestore.title": "升级以恢复工作流版本", + "upgrade.workflowVersionRun.description": "当前套餐不支持通过工作流 ID 执行指定版本。", + "upgrade.workflowVersionRun.title": "升级以执行指定工作流版本", "upgradeBtn.encourage": "立即升级", "upgradeBtn.encourageShort": "升级", "upgradeBtn.plain": "查看套餐", diff --git a/web/i18n/zh-Hant/billing.json b/web/i18n/zh-Hant/billing.json index c3825ffdb85..49e16183160 100644 --- a/web/i18n/zh-Hant/billing.json +++ b/web/i18n/zh-Hant/billing.json @@ -166,6 +166,8 @@ "upgrade.uploadMultiplePages.title": "升級以一次上傳多個文件", "upgrade.workflowRestore.description": "目前方案不支援還原工作流程版本。", "upgrade.workflowRestore.title": "升級以還原工作流程版本", + "upgrade.workflowVersionRun.description": "目前方案不支援透過工作流程 ID 執行指定版本。", + "upgrade.workflowVersionRun.title": "升級以執行指定工作流程版本", "upgradeBtn.encourage": "立即升級", "upgradeBtn.encourageShort": "升級", "upgradeBtn.plain": "升級套餐",