mirror of
https://github.com/langgenius/dify.git
synced 2026-03-06 15:45:14 +00:00
Compare commits
2 Commits
refactor/r
...
hotfix/1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c34d05141e | ||
|
|
8080159eaf |
@@ -157,7 +157,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
|
|||||||
id=self._message_id,
|
id=self._message_id,
|
||||||
mode=self._conversation_mode,
|
mode=self._conversation_mode,
|
||||||
message_id=self._message_id,
|
message_id=self._message_id,
|
||||||
answer=cast(str, self._task_state.llm_result.message.content),
|
answer=self._task_state.llm_result.message.get_text_content(),
|
||||||
created_at=self._message_created_at,
|
created_at=self._message_created_at,
|
||||||
**extras,
|
**extras,
|
||||||
),
|
),
|
||||||
@@ -170,7 +170,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
|
|||||||
mode=self._conversation_mode,
|
mode=self._conversation_mode,
|
||||||
conversation_id=self._conversation_id,
|
conversation_id=self._conversation_id,
|
||||||
message_id=self._message_id,
|
message_id=self._message_id,
|
||||||
answer=cast(str, self._task_state.llm_result.message.content),
|
answer=self._task_state.llm_result.message.get_text_content(),
|
||||||
created_at=self._message_created_at,
|
created_at=self._message_created_at,
|
||||||
**extras,
|
**extras,
|
||||||
),
|
),
|
||||||
@@ -283,7 +283,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
|
|||||||
|
|
||||||
# handle output moderation
|
# handle output moderation
|
||||||
output_moderation_answer = self.handle_output_moderation_when_task_finished(
|
output_moderation_answer = self.handle_output_moderation_when_task_finished(
|
||||||
cast(str, self._task_state.llm_result.message.content)
|
self._task_state.llm_result.message.get_text_content()
|
||||||
)
|
)
|
||||||
if output_moderation_answer:
|
if output_moderation_answer:
|
||||||
self._task_state.llm_result.message.content = output_moderation_answer
|
self._task_state.llm_result.message.content = output_moderation_answer
|
||||||
@@ -397,7 +397,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
|
|||||||
message.message_unit_price = usage.prompt_unit_price
|
message.message_unit_price = usage.prompt_unit_price
|
||||||
message.message_price_unit = usage.prompt_price_unit
|
message.message_price_unit = usage.prompt_price_unit
|
||||||
message.answer = (
|
message.answer = (
|
||||||
PromptTemplateParser.remove_template_variables(cast(str, llm_result.message.content).strip())
|
PromptTemplateParser.remove_template_variables(llm_result.message.get_text_content().strip())
|
||||||
if llm_result.message.content
|
if llm_result.message.content
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -131,33 +131,54 @@ class AppGenerateService:
|
|||||||
elif app_model.mode == AppMode.ADVANCED_CHAT:
|
elif app_model.mode == AppMode.ADVANCED_CHAT:
|
||||||
workflow_id = args.get("workflow_id")
|
workflow_id = args.get("workflow_id")
|
||||||
workflow = cls._get_workflow(app_model, invoke_from, workflow_id)
|
workflow = cls._get_workflow(app_model, invoke_from, workflow_id)
|
||||||
with rate_limit_context(rate_limit, request_id):
|
|
||||||
payload = AppExecutionParams.new(
|
|
||||||
app_model=app_model,
|
|
||||||
workflow=workflow,
|
|
||||||
user=user,
|
|
||||||
args=args,
|
|
||||||
invoke_from=invoke_from,
|
|
||||||
streaming=streaming,
|
|
||||||
call_depth=0,
|
|
||||||
)
|
|
||||||
payload_json = payload.model_dump_json()
|
|
||||||
|
|
||||||
def on_subscribe():
|
if streaming:
|
||||||
workflow_based_app_execution_task.delay(payload_json)
|
# Streaming mode: subscribe to SSE and enqueue the execution on first subscriber
|
||||||
|
with rate_limit_context(rate_limit, request_id):
|
||||||
|
payload = AppExecutionParams.new(
|
||||||
|
app_model=app_model,
|
||||||
|
workflow=workflow,
|
||||||
|
user=user,
|
||||||
|
args=args,
|
||||||
|
invoke_from=invoke_from,
|
||||||
|
streaming=True,
|
||||||
|
call_depth=0,
|
||||||
|
)
|
||||||
|
payload_json = payload.model_dump_json()
|
||||||
|
|
||||||
on_subscribe = cls._build_streaming_task_on_subscribe(on_subscribe)
|
def on_subscribe():
|
||||||
generator = AdvancedChatAppGenerator()
|
workflow_based_app_execution_task.delay(payload_json)
|
||||||
return rate_limit.generate(
|
|
||||||
generator.convert_to_event_stream(
|
on_subscribe = cls._build_streaming_task_on_subscribe(on_subscribe)
|
||||||
generator.retrieve_events(
|
generator = AdvancedChatAppGenerator()
|
||||||
AppMode.ADVANCED_CHAT,
|
return rate_limit.generate(
|
||||||
payload.workflow_run_id,
|
generator.convert_to_event_stream(
|
||||||
on_subscribe=on_subscribe,
|
generator.retrieve_events(
|
||||||
|
AppMode.ADVANCED_CHAT,
|
||||||
|
payload.workflow_run_id,
|
||||||
|
on_subscribe=on_subscribe,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
request_id=request_id,
|
||||||
request_id=request_id,
|
)
|
||||||
)
|
else:
|
||||||
|
# Blocking mode: run synchronously and return JSON instead of SSE
|
||||||
|
# Keep behaviour consistent with WORKFLOW blocking branch.
|
||||||
|
advanced_generator = AdvancedChatAppGenerator()
|
||||||
|
return rate_limit.generate(
|
||||||
|
advanced_generator.convert_to_event_stream(
|
||||||
|
advanced_generator.generate(
|
||||||
|
app_model=app_model,
|
||||||
|
workflow=workflow,
|
||||||
|
user=user,
|
||||||
|
args=args,
|
||||||
|
invoke_from=invoke_from,
|
||||||
|
workflow_run_id=str(uuid.uuid4()),
|
||||||
|
streaming=False,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
request_id=request_id,
|
||||||
|
)
|
||||||
elif app_model.mode == AppMode.WORKFLOW:
|
elif app_model.mode == AppMode.WORKFLOW:
|
||||||
workflow_id = args.get("workflow_id")
|
workflow_id = args.get("workflow_id")
|
||||||
workflow = cls._get_workflow(app_model, invoke_from, workflow_id)
|
workflow = cls._get_workflow(app_model, invoke_from, workflow_id)
|
||||||
|
|||||||
@@ -63,3 +63,56 @@ def test_workflow_blocking_injects_pause_state_config(mocker, monkeypatch):
|
|||||||
pause_state_config = call_kwargs.get("pause_state_config")
|
pause_state_config = call_kwargs.get("pause_state_config")
|
||||||
assert pause_state_config is not None
|
assert pause_state_config is not None
|
||||||
assert pause_state_config.state_owner_user_id == "owner-id"
|
assert pause_state_config.state_owner_user_id == "owner-id"
|
||||||
|
|
||||||
|
|
||||||
|
def test_advanced_chat_blocking_returns_dict_and_does_not_use_event_retrieval(mocker, monkeypatch):
|
||||||
|
"""
|
||||||
|
Regression test: ADVANCED_CHAT in blocking mode should return a plain dict
|
||||||
|
(non-streaming), and must not go through the async retrieve_events path.
|
||||||
|
Keeps behavior consistent with WORKFLOW blocking branch.
|
||||||
|
"""
|
||||||
|
# Disable billing and stub RateLimit to a no-op that just passes values through
|
||||||
|
monkeypatch.setattr(app_generate_service_module.dify_config, "BILLING_ENABLED", False)
|
||||||
|
mocker.patch("services.app_generate_service.RateLimit", _DummyRateLimit)
|
||||||
|
|
||||||
|
# Arrange a fake workflow and wire AppGenerateService._get_workflow to return it
|
||||||
|
workflow = MagicMock()
|
||||||
|
workflow.id = "workflow-id"
|
||||||
|
mocker.patch.object(AppGenerateService, "_get_workflow", return_value=workflow)
|
||||||
|
|
||||||
|
# Spy on the streaming retrieval path to ensure it's NOT called
|
||||||
|
retrieve_spy = mocker.patch("services.app_generate_service.AdvancedChatAppGenerator.retrieve_events")
|
||||||
|
|
||||||
|
# Make AdvancedChatAppGenerator.generate return a plain dict when streaming=False
|
||||||
|
generate_spy = mocker.patch(
|
||||||
|
"services.app_generate_service.AdvancedChatAppGenerator.generate",
|
||||||
|
return_value={"result": "ok"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Minimal app model for ADVANCED_CHAT
|
||||||
|
app_model = MagicMock()
|
||||||
|
app_model.mode = AppMode.ADVANCED_CHAT
|
||||||
|
app_model.id = "app-id"
|
||||||
|
app_model.tenant_id = "tenant-id"
|
||||||
|
app_model.max_active_requests = 0
|
||||||
|
app_model.is_agent = False
|
||||||
|
|
||||||
|
user = MagicMock()
|
||||||
|
user.id = "user-id"
|
||||||
|
|
||||||
|
# Must include query and inputs for AdvancedChatAppGenerator
|
||||||
|
args = {"workflow_id": "wf-1", "query": "hello", "inputs": {}}
|
||||||
|
|
||||||
|
# Act: call service with streaming=False (blocking mode)
|
||||||
|
result = AppGenerateService.generate(
|
||||||
|
app_model=app_model,
|
||||||
|
user=user,
|
||||||
|
args=args,
|
||||||
|
invoke_from=MagicMock(),
|
||||||
|
streaming=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert: returns the dict from generate(), and did not call retrieve_events()
|
||||||
|
assert result == {"result": "ok"}
|
||||||
|
assert generate_spy.call_args.kwargs.get("streaming") is False
|
||||||
|
retrieve_spy.assert_not_called()
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export const OAUTH_AUTHORIZE_PENDING_KEY = 'oauth_authorize_pending'
|
|
||||||
export const REDIRECT_URL_KEY = 'oauth_redirect_url'
|
|
||||||
export const OAUTH_AUTHORIZE_PENDING_TTL = 60 * 3
|
|
||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
RiMailLine,
|
RiMailLine,
|
||||||
RiTranslate2,
|
RiTranslate2,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { useRouter, useSearchParams } from 'next/navigation'
|
import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
@@ -17,22 +16,10 @@ import Button from '@/app/components/base/button'
|
|||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import Toast from '@/app/components/base/toast'
|
import Toast from '@/app/components/base/toast'
|
||||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||||
|
import { setPostLoginRedirect } from '@/app/signin/utils/post-login-redirect'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { useIsLogin } from '@/service/use-common'
|
import { useIsLogin } from '@/service/use-common'
|
||||||
import { useAuthorizeOAuthApp, useOAuthAppInfo } from '@/service/use-oauth'
|
import { useAuthorizeOAuthApp, useOAuthAppInfo } from '@/service/use-oauth'
|
||||||
import {
|
|
||||||
OAUTH_AUTHORIZE_PENDING_KEY,
|
|
||||||
OAUTH_AUTHORIZE_PENDING_TTL,
|
|
||||||
REDIRECT_URL_KEY,
|
|
||||||
} from './constants'
|
|
||||||
|
|
||||||
function setItemWithExpiry(key: string, value: string, ttl: number) {
|
|
||||||
const item = {
|
|
||||||
value,
|
|
||||||
expiry: dayjs().add(ttl, 'seconds').unix(),
|
|
||||||
}
|
|
||||||
localStorage.setItem(key, JSON.stringify(item))
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildReturnUrl(pathname: string, search: string) {
|
function buildReturnUrl(pathname: string, search: string) {
|
||||||
try {
|
try {
|
||||||
@@ -86,8 +73,8 @@ export default function OAuthAuthorize() {
|
|||||||
const onLoginSwitchClick = () => {
|
const onLoginSwitchClick = () => {
|
||||||
try {
|
try {
|
||||||
const returnUrl = buildReturnUrl('/account/oauth/authorize', `?client_id=${encodeURIComponent(client_id)}&redirect_uri=${encodeURIComponent(redirect_uri)}`)
|
const returnUrl = buildReturnUrl('/account/oauth/authorize', `?client_id=${encodeURIComponent(client_id)}&redirect_uri=${encodeURIComponent(redirect_uri)}`)
|
||||||
setItemWithExpiry(OAUTH_AUTHORIZE_PENDING_KEY, returnUrl, OAUTH_AUTHORIZE_PENDING_TTL)
|
setPostLoginRedirect(returnUrl)
|
||||||
router.push(`/signin?${REDIRECT_URL_KEY}=${encodeURIComponent(returnUrl)}`)
|
router.push('/signin')
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
router.push('/signin')
|
router.push('/signin')
|
||||||
@@ -145,7 +132,7 @@ export default function OAuthAuthorize() {
|
|||||||
<div className="text-[var(--color-saas-dify-blue-inverted)]">{authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('unknownApp', { ns: 'oauth' })}</div>
|
<div className="text-[var(--color-saas-dify-blue-inverted)]">{authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('unknownApp', { ns: 'oauth' })}</div>
|
||||||
{!isLoggedIn && <div className="text-text-primary">{t('tips.notLoggedIn', { ns: 'oauth' })}</div>}
|
{!isLoggedIn && <div className="text-text-primary">{t('tips.notLoggedIn', { ns: 'oauth' })}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="body-md-regular text-text-secondary">{isLoggedIn ? `${authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('unknownApp', { ns: 'oauth' })} ${t('tips.loggedIn', { ns: 'oauth' })}` : t('tips.needLogin', { ns: 'oauth' })}</div>
|
<div className="text-text-secondary body-md-regular">{isLoggedIn ? `${authAppInfo?.app_label[language] || authAppInfo?.app_label?.en_US || t('unknownApp', { ns: 'oauth' })} ${t('tips.loggedIn', { ns: 'oauth' })}` : t('tips.needLogin', { ns: 'oauth' })}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoggedIn && userProfile && (
|
{isLoggedIn && userProfile && (
|
||||||
@@ -154,7 +141,7 @@ export default function OAuthAuthorize() {
|
|||||||
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={36} />
|
<Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={36} />
|
||||||
<div>
|
<div>
|
||||||
<div className="system-md-semi-bold text-text-secondary">{userProfile.name}</div>
|
<div className="system-md-semi-bold text-text-secondary">{userProfile.name}</div>
|
||||||
<div className="system-xs-regular text-text-tertiary">{userProfile.email}</div>
|
<div className="text-text-tertiary system-xs-regular">{userProfile.email}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="tertiary" size="small" onClick={onLoginSwitchClick}>{t('switchAccount', { ns: 'oauth' })}</Button>
|
<Button variant="tertiary" size="small" onClick={onLoginSwitchClick}>{t('switchAccount', { ns: 'oauth' })}</Button>
|
||||||
@@ -166,7 +153,7 @@ export default function OAuthAuthorize() {
|
|||||||
{authAppInfo!.scope.split(/\s+/).filter(Boolean).map((scope: string) => {
|
{authAppInfo!.scope.split(/\s+/).filter(Boolean).map((scope: string) => {
|
||||||
const Icon = SCOPE_INFO_MAP[scope]
|
const Icon = SCOPE_INFO_MAP[scope]
|
||||||
return (
|
return (
|
||||||
<div key={scope} className="body-sm-medium flex items-center gap-2 text-text-secondary">
|
<div key={scope} className="flex items-center gap-2 text-text-secondary body-sm-medium">
|
||||||
{Icon ? <Icon.icon className="h-4 w-4" /> : <RiAccountCircleLine className="h-4 w-4" />}
|
{Icon ? <Icon.icon className="h-4 w-4" /> : <RiAccountCircleLine className="h-4 w-4" />}
|
||||||
{Icon.label}
|
{Icon.label}
|
||||||
</div>
|
</div>
|
||||||
@@ -199,7 +186,7 @@ export default function OAuthAuthorize() {
|
|||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div className="system-xs-regular mt-3 text-text-tertiary">{t('tips.common', { ns: 'oauth' })}</div>
|
<div className="mt-3 text-text-tertiary system-xs-regular">{t('tips.common', { ns: 'oauth' })}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const AppInitializer = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
const redirectUrl = resolvePostLoginRedirect()
|
||||||
if (redirectUrl) {
|
if (redirectUrl) {
|
||||||
location.replace(redirectUrl)
|
location.replace(redirectUrl)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default function CheckCode() {
|
|||||||
router.replace(`/signin/invite-settings?${searchParams.toString()}`)
|
router.replace(`/signin/invite-settings?${searchParams.toString()}`)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
const redirectUrl = resolvePostLoginRedirect()
|
||||||
router.replace(redirectUrl || '/apps')
|
router.replace(redirectUrl || '/apps')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,8 +95,8 @@ export default function CheckCode() {
|
|||||||
<RiMailSendFill className="h-6 w-6 text-2xl text-text-accent-light-mode-only" />
|
<RiMailSendFill className="h-6 w-6 text-2xl text-text-accent-light-mode-only" />
|
||||||
</div>
|
</div>
|
||||||
<div className="pb-4 pt-2">
|
<div className="pb-4 pt-2">
|
||||||
<h2 className="title-4xl-semi-bold text-text-primary">{t('checkCode.checkYourEmail', { ns: 'login' })}</h2>
|
<h2 className="text-text-primary title-4xl-semi-bold">{t('checkCode.checkYourEmail', { ns: 'login' })}</h2>
|
||||||
<p className="body-md-regular mt-2 text-text-secondary">
|
<p className="mt-2 text-text-secondary body-md-regular">
|
||||||
<span>
|
<span>
|
||||||
{t('checkCode.tipsPrefix', { ns: 'login' })}
|
{t('checkCode.tipsPrefix', { ns: 'login' })}
|
||||||
<strong>{email}</strong>
|
<strong>{email}</strong>
|
||||||
@@ -107,7 +107,7 @@ export default function CheckCode() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('checkCode.verificationCode', { ns: 'login' })}</label>
|
<label htmlFor="code" className="mb-1 text-text-secondary system-md-semibold">{t('checkCode.verificationCode', { ns: 'login' })}</label>
|
||||||
<Input
|
<Input
|
||||||
ref={codeInputRef}
|
ref={codeInputRef}
|
||||||
id="code"
|
id="code"
|
||||||
@@ -127,7 +127,7 @@ export default function CheckCode() {
|
|||||||
<div className="inline-block rounded-full bg-background-default-dimmed p-1">
|
<div className="inline-block rounded-full bg-background-default-dimmed p-1">
|
||||||
<RiArrowLeftLine size={12} />
|
<RiArrowLeftLine size={12} />
|
||||||
</div>
|
</div>
|
||||||
<span className="system-xs-regular ml-2">{t('back', { ns: 'login' })}</span>
|
<span className="ml-2 system-xs-regular">{t('back', { ns: 'login' })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis
|
|||||||
router.replace(`/signin/invite-settings?${searchParams.toString()}`)
|
router.replace(`/signin/invite-settings?${searchParams.toString()}`)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
const redirectUrl = resolvePostLoginRedirect()
|
||||||
router.replace(redirectUrl || '/apps')
|
router.replace(redirectUrl || '/apps')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis
|
|||||||
return (
|
return (
|
||||||
<form onSubmit={noop}>
|
<form onSubmit={noop}>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">
|
<label htmlFor="email" className="my-2 text-text-secondary system-md-semibold">
|
||||||
{t('email', { ns: 'login' })}
|
{t('email', { ns: 'login' })}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
@@ -124,7 +124,7 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis
|
|||||||
|
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label htmlFor="password" className="my-2 flex items-center justify-between">
|
<label htmlFor="password" className="my-2 flex items-center justify-between">
|
||||||
<span className="system-md-semibold text-text-secondary">{t('password', { ns: 'login' })}</span>
|
<span className="text-text-secondary system-md-semibold">{t('password', { ns: 'login' })}</span>
|
||||||
<Link
|
<Link
|
||||||
href={`/reset-password?${searchParams.toString()}`}
|
href={`/reset-password?${searchParams.toString()}`}
|
||||||
className={`system-xs-regular ${isEmailSetup ? 'text-components-button-secondary-accent-text' : 'pointer-events-none text-components-button-secondary-accent-text-disabled'}`}
|
className={`system-xs-regular ${isEmailSetup ? 'text-components-button-secondary-accent-text' : 'pointer-events-none text-components-button-secondary-accent-text-disabled'}`}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function InviteSettingsPage() {
|
|||||||
if (res.result === 'success') {
|
if (res.result === 'success') {
|
||||||
// Tokens are now stored in cookies by the backend
|
// Tokens are now stored in cookies by the backend
|
||||||
await setLocaleOnClient(language, false)
|
await setLocaleOnClient(language, false)
|
||||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
const redirectUrl = resolvePostLoginRedirect()
|
||||||
router.replace(redirectUrl || '/apps')
|
router.replace(redirectUrl || '/apps')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ export default function InviteSettingsPage() {
|
|||||||
<div className="flex flex-col md:w-[400px]">
|
<div className="flex flex-col md:w-[400px]">
|
||||||
<div className="mx-auto w-full">
|
<div className="mx-auto w-full">
|
||||||
<div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle text-2xl font-bold shadow-lg">🤷♂️</div>
|
<div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle text-2xl font-bold shadow-lg">🤷♂️</div>
|
||||||
<h2 className="title-4xl-semi-bold text-text-primary">{t('invalid', { ns: 'login' })}</h2>
|
<h2 className="text-text-primary title-4xl-semi-bold">{t('invalid', { ns: 'login' })}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto mt-6 w-full">
|
<div className="mx-auto mt-6 w-full">
|
||||||
<Button variant="primary" className="w-full !text-sm">
|
<Button variant="primary" className="w-full !text-sm">
|
||||||
@@ -89,11 +89,11 @@ export default function InviteSettingsPage() {
|
|||||||
<RiAccountCircleLine className="h-6 w-6 text-2xl text-text-accent-light-mode-only" />
|
<RiAccountCircleLine className="h-6 w-6 text-2xl text-text-accent-light-mode-only" />
|
||||||
</div>
|
</div>
|
||||||
<div className="pb-4 pt-2">
|
<div className="pb-4 pt-2">
|
||||||
<h2 className="title-4xl-semi-bold text-text-primary">{t('setYourAccount', { ns: 'login' })}</h2>
|
<h2 className="text-text-primary title-4xl-semi-bold">{t('setYourAccount', { ns: 'login' })}</h2>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={noop}>
|
<form onSubmit={noop}>
|
||||||
<div className="mb-5">
|
<div className="mb-5">
|
||||||
<label htmlFor="name" className="system-md-semibold my-2 text-text-secondary">
|
<label htmlFor="name" className="my-2 text-text-secondary system-md-semibold">
|
||||||
{t('name', { ns: 'login' })}
|
{t('name', { ns: 'login' })}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
@@ -114,7 +114,7 @@ export default function InviteSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-5">
|
<div className="mb-5">
|
||||||
<label htmlFor="name" className="system-md-semibold my-2 text-text-secondary">
|
<label htmlFor="name" className="my-2 text-text-secondary system-md-semibold">
|
||||||
{t('interfaceLanguage', { ns: 'login' })}
|
{t('interfaceLanguage', { ns: 'login' })}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
@@ -129,7 +129,7 @@ export default function InviteSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
{/* timezone */}
|
{/* timezone */}
|
||||||
<div className="mb-5">
|
<div className="mb-5">
|
||||||
<label htmlFor="timezone" className="system-md-semibold text-text-secondary">
|
<label htmlFor="timezone" className="text-text-secondary system-md-semibold">
|
||||||
{t('timezone', { ns: 'login' })}
|
{t('timezone', { ns: 'login' })}
|
||||||
</label>
|
</label>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
@@ -153,11 +153,11 @@ export default function InviteSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{!systemFeatures.branding.enabled && (
|
{!systemFeatures.branding.enabled && (
|
||||||
<div className="system-xs-regular mt-2 block w-full text-text-tertiary">
|
<div className="mt-2 block w-full text-text-tertiary system-xs-regular">
|
||||||
{t('license.tip', { ns: 'login' })}
|
{t('license.tip', { ns: 'login' })}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
className="system-xs-medium text-text-accent-secondary"
|
className="text-text-accent-secondary system-xs-medium"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href={LICENSE_LINK}
|
href={LICENSE_LINK}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const NormalForm = () => {
|
|||||||
try {
|
try {
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
setIsRedirecting(true)
|
setIsRedirecting(true)
|
||||||
const redirectUrl = resolvePostLoginRedirect(searchParams)
|
const redirectUrl = resolvePostLoginRedirect()
|
||||||
router.replace(redirectUrl || '/apps')
|
router.replace(redirectUrl || '/apps')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -98,8 +98,8 @@ const NormalForm = () => {
|
|||||||
<RiContractLine className="h-5 w-5" />
|
<RiContractLine className="h-5 w-5" />
|
||||||
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
|
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
|
||||||
</div>
|
</div>
|
||||||
<p className="system-sm-medium text-text-primary">{t('licenseLost', { ns: 'login' })}</p>
|
<p className="text-text-primary system-sm-medium">{t('licenseLost', { ns: 'login' })}</p>
|
||||||
<p className="system-xs-regular mt-1 text-text-tertiary">{t('licenseLostTip', { ns: 'login' })}</p>
|
<p className="mt-1 text-text-tertiary system-xs-regular">{t('licenseLostTip', { ns: 'login' })}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,8 +114,8 @@ const NormalForm = () => {
|
|||||||
<RiContractLine className="h-5 w-5" />
|
<RiContractLine className="h-5 w-5" />
|
||||||
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
|
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
|
||||||
</div>
|
</div>
|
||||||
<p className="system-sm-medium text-text-primary">{t('licenseExpired', { ns: 'login' })}</p>
|
<p className="text-text-primary system-sm-medium">{t('licenseExpired', { ns: 'login' })}</p>
|
||||||
<p className="system-xs-regular mt-1 text-text-tertiary">{t('licenseExpiredTip', { ns: 'login' })}</p>
|
<p className="mt-1 text-text-tertiary system-xs-regular">{t('licenseExpiredTip', { ns: 'login' })}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,8 +130,8 @@ const NormalForm = () => {
|
|||||||
<RiContractLine className="h-5 w-5" />
|
<RiContractLine className="h-5 w-5" />
|
||||||
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
|
<RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" />
|
||||||
</div>
|
</div>
|
||||||
<p className="system-sm-medium text-text-primary">{t('licenseInactive', { ns: 'login' })}</p>
|
<p className="text-text-primary system-sm-medium">{t('licenseInactive', { ns: 'login' })}</p>
|
||||||
<p className="system-xs-regular mt-1 text-text-tertiary">{t('licenseInactiveTip', { ns: 'login' })}</p>
|
<p className="mt-1 text-text-tertiary system-xs-regular">{t('licenseInactiveTip', { ns: 'login' })}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,12 +144,12 @@ const NormalForm = () => {
|
|||||||
{isInviteLink
|
{isInviteLink
|
||||||
? (
|
? (
|
||||||
<div className="mx-auto w-full">
|
<div className="mx-auto w-full">
|
||||||
<h2 className="title-4xl-semi-bold text-text-primary">
|
<h2 className="text-text-primary title-4xl-semi-bold">
|
||||||
{t('join', { ns: 'login' })}
|
{t('join', { ns: 'login' })}
|
||||||
{workspaceName}
|
{workspaceName}
|
||||||
</h2>
|
</h2>
|
||||||
{!systemFeatures.branding.enabled && (
|
{!systemFeatures.branding.enabled && (
|
||||||
<p className="body-md-regular mt-2 text-text-tertiary">
|
<p className="mt-2 text-text-tertiary body-md-regular">
|
||||||
{t('joinTipStart', { ns: 'login' })}
|
{t('joinTipStart', { ns: 'login' })}
|
||||||
{workspaceName}
|
{workspaceName}
|
||||||
{t('joinTipEnd', { ns: 'login' })}
|
{t('joinTipEnd', { ns: 'login' })}
|
||||||
@@ -159,8 +159,8 @@ const NormalForm = () => {
|
|||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<div className="mx-auto w-full">
|
<div className="mx-auto w-full">
|
||||||
<h2 className="title-4xl-semi-bold text-text-primary">{systemFeatures.branding.enabled ? t('pageTitleForE', { ns: 'login' }) : t('pageTitle', { ns: 'login' })}</h2>
|
<h2 className="text-text-primary title-4xl-semi-bold">{systemFeatures.branding.enabled ? t('pageTitleForE', { ns: 'login' }) : t('pageTitle', { ns: 'login' })}</h2>
|
||||||
<p className="body-md-regular mt-2 text-text-tertiary">{t('welcome', { ns: 'login' })}</p>
|
<p className="mt-2 text-text-tertiary body-md-regular">{t('welcome', { ns: 'login' })}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -177,7 +177,7 @@ const NormalForm = () => {
|
|||||||
<div className="relative mt-6">
|
<div className="relative mt-6">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="h-px flex-1 bg-gradient-to-r from-background-gradient-mask-transparent to-divider-regular"></div>
|
<div className="h-px flex-1 bg-gradient-to-r from-background-gradient-mask-transparent to-divider-regular"></div>
|
||||||
<span className="system-xs-medium-uppercase px-3 text-text-tertiary">{t('or', { ns: 'login' })}</span>
|
<span className="px-3 text-text-tertiary system-xs-medium-uppercase">{t('or', { ns: 'login' })}</span>
|
||||||
<div className="h-px flex-1 bg-gradient-to-l from-background-gradient-mask-transparent to-divider-regular"></div>
|
<div className="h-px flex-1 bg-gradient-to-l from-background-gradient-mask-transparent to-divider-regular"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,7 +190,7 @@ const NormalForm = () => {
|
|||||||
<MailAndCodeAuth isInvite={isInviteLink} />
|
<MailAndCodeAuth isInvite={isInviteLink} />
|
||||||
{systemFeatures.enable_email_password_login && (
|
{systemFeatures.enable_email_password_login && (
|
||||||
<div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('password') }}>
|
<div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('password') }}>
|
||||||
<span className="system-xs-medium text-components-button-secondary-accent-text">{t('usePassword', { ns: 'login' })}</span>
|
<span className="text-components-button-secondary-accent-text system-xs-medium">{t('usePassword', { ns: 'login' })}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -200,7 +200,7 @@ const NormalForm = () => {
|
|||||||
<MailAndPasswordAuth isInvite={isInviteLink} isEmailSetup={systemFeatures.is_email_setup} allowRegistration={systemFeatures.is_allow_register} />
|
<MailAndPasswordAuth isInvite={isInviteLink} isEmailSetup={systemFeatures.is_email_setup} allowRegistration={systemFeatures.is_allow_register} />
|
||||||
{systemFeatures.enable_email_code_login && (
|
{systemFeatures.enable_email_code_login && (
|
||||||
<div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('code') }}>
|
<div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('code') }}>
|
||||||
<span className="system-xs-medium text-components-button-secondary-accent-text">{t('useVerificationCode', { ns: 'login' })}</span>
|
<span className="text-components-button-secondary-accent-text system-xs-medium">{t('useVerificationCode', { ns: 'login' })}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -227,8 +227,8 @@ const NormalForm = () => {
|
|||||||
<div className="shadows-shadow-lg mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow">
|
<div className="shadows-shadow-lg mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow">
|
||||||
<RiDoorLockLine className="h-5 w-5" />
|
<RiDoorLockLine className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
<p className="system-sm-medium text-text-primary">{t('noLoginMethod', { ns: 'login' })}</p>
|
<p className="text-text-primary system-sm-medium">{t('noLoginMethod', { ns: 'login' })}</p>
|
||||||
<p className="system-xs-regular mt-1 text-text-tertiary">{t('noLoginMethodTip', { ns: 'login' })}</p>
|
<p className="mt-1 text-text-tertiary system-xs-regular">{t('noLoginMethodTip', { ns: 'login' })}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative my-2 py-2">
|
<div className="relative my-2 py-2">
|
||||||
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
||||||
@@ -239,11 +239,11 @@ const NormalForm = () => {
|
|||||||
)}
|
)}
|
||||||
{!systemFeatures.branding.enabled && (
|
{!systemFeatures.branding.enabled && (
|
||||||
<>
|
<>
|
||||||
<div className="system-xs-regular mt-2 block w-full text-text-tertiary">
|
<div className="mt-2 block w-full text-text-tertiary system-xs-regular">
|
||||||
{t('tosDesc', { ns: 'login' })}
|
{t('tosDesc', { ns: 'login' })}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
className="system-xs-medium text-text-secondary hover:underline"
|
className="text-text-secondary system-xs-medium hover:underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://dify.ai/terms"
|
href="https://dify.ai/terms"
|
||||||
@@ -252,7 +252,7 @@ const NormalForm = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
&
|
&
|
||||||
<Link
|
<Link
|
||||||
className="system-xs-medium text-text-secondary hover:underline"
|
className="text-text-secondary system-xs-medium hover:underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://dify.ai/privacy"
|
href="https://dify.ai/privacy"
|
||||||
@@ -261,11 +261,11 @@ const NormalForm = () => {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{IS_CE_EDITION && (
|
{IS_CE_EDITION && (
|
||||||
<div className="w-hull system-xs-regular mt-2 block text-text-tertiary">
|
<div className="w-hull mt-2 block text-text-tertiary system-xs-regular">
|
||||||
{t('goToInit', { ns: 'login' })}
|
{t('goToInit', { ns: 'login' })}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
className="system-xs-medium text-text-secondary hover:underline"
|
className="text-text-secondary system-xs-medium hover:underline"
|
||||||
href="/install"
|
href="/install"
|
||||||
>
|
>
|
||||||
{t('setAdminAccount', { ns: 'login' })}
|
{t('setAdminAccount', { ns: 'login' })}
|
||||||
|
|||||||
@@ -1,37 +1,15 @@
|
|||||||
import type { ReadonlyURLSearchParams } from 'next/navigation'
|
let postLoginRedirect: string | null = null
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import { OAUTH_AUTHORIZE_PENDING_KEY, REDIRECT_URL_KEY } from '@/app/account/oauth/authorize/constants'
|
|
||||||
|
|
||||||
function getItemWithExpiry(key: string): string | null {
|
export const setPostLoginRedirect = (value: string | null) => {
|
||||||
const itemStr = localStorage.getItem(key)
|
postLoginRedirect = value
|
||||||
if (!itemStr)
|
|
||||||
return null
|
|
||||||
|
|
||||||
try {
|
|
||||||
const item = JSON.parse(itemStr)
|
|
||||||
localStorage.removeItem(key)
|
|
||||||
if (!item?.value)
|
|
||||||
return null
|
|
||||||
|
|
||||||
return dayjs().unix() > item.expiry ? null : item.value
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const resolvePostLoginRedirect = (searchParams: ReadonlyURLSearchParams) => {
|
export const resolvePostLoginRedirect = () => {
|
||||||
const redirectUrl = searchParams.get(REDIRECT_URL_KEY)
|
if (postLoginRedirect) {
|
||||||
if (redirectUrl) {
|
const redirectUrl = postLoginRedirect
|
||||||
try {
|
postLoginRedirect = null
|
||||||
localStorage.removeItem(OAUTH_AUTHORIZE_PENDING_KEY)
|
return redirectUrl
|
||||||
return decodeURIComponent(redirectUrl)
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
console.error('Failed to decode redirect URL:', e)
|
|
||||||
return redirectUrl
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return getItemWithExpiry(OAUTH_AUTHORIZE_PENDING_KEY)
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,9 +278,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/account/oauth/authorize/page.tsx": {
|
"app/account/oauth/authorize/page.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 4
|
|
||||||
},
|
|
||||||
"ts/no-explicit-any": {
|
"ts/no-explicit-any": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
@@ -8593,29 +8590,16 @@
|
|||||||
"count": 6
|
"count": 6
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/signin/check-code/page.tsx": {
|
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/signin/components/mail-and-code-auth.tsx": {
|
"app/signin/components/mail-and-code-auth.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/signin/components/mail-and-password-auth.tsx": {
|
"app/signin/components/mail-and-password-auth.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 2
|
|
||||||
},
|
|
||||||
"ts/no-explicit-any": {
|
"ts/no-explicit-any": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/signin/invite-settings/page.tsx": {
|
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 7
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/signin/layout.tsx": {
|
"app/signin/layout.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -8624,11 +8608,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app/signin/normal-form.tsx": {
|
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
|
||||||
"count": 20
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"app/signin/one-more-step.tsx": {
|
"app/signin/one-more-step.tsx": {
|
||||||
"tailwindcss/enforce-consistent-class-order": {
|
"tailwindcss/enforce-consistent-class-order": {
|
||||||
"count": 7
|
"count": 7
|
||||||
|
|||||||
Reference in New Issue
Block a user