Compare commits

..

5 Commits

Author SHA1 Message Date
JzoNg 30393043b1 add error handle for email unavailable 10 months ago
JzoNg ae49a2bdd9 add error message of email unavailable 10 months ago
Yansong Zhang c271c65c85 fix ruff liner 10 months ago
Yansong Zhang a1de4fa428 check is email is freeze user email 10 months ago
Yansong Zhang 952bce4196 check is email is freeze user email 10 months ago

@ -1,7 +1,7 @@
![cover-v5-optimized](./images/GitHub_README_if.png)
<p align="center">
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast111</a>
📌 <a href="https://dify.ai/blog/introducing-dify-workflow-file-upload-a-demo-on-ai-podcast">Introducing Dify Workflow File Upload: Recreate Google NotebookLM Podcast</a>
</p>
<p align="center">

@ -511,6 +511,8 @@ class CheckEmailUnique(Resource):
parser = reqparse.RequestParser()
parser.add_argument("email", type=email, required=True, location="json")
args = parser.parse_args()
if AccountService.is_account_in_freeze(args["new_email"]):
raise AccountInFreezeError()
if not AccountService.check_email_unique(args["email"]):
raise EmailAlreadyInUseError()
return {"result": "success"}

@ -1011,9 +1011,7 @@ class ToolManager:
if variable is None:
raise ToolParameterError(f"Variable {tool_input.value} does not exist")
parameter_value = variable.value
elif tool_input.type == "constant":
parameter_value = tool_input.value
elif tool_input.type == "mixed":
elif tool_input.type in {"mixed", "constant"}:
segment_group = variable_pool.convert_template(str(tool_input.value))
parameter_value = segment_group.text
else:

@ -54,7 +54,7 @@ class ToolNodeData(BaseNodeData, ToolEntity):
for val in value:
if not isinstance(val, str):
raise ValueError("value must be a list of strings")
elif typ == "constant" and not isinstance(value, str | int | float | bool | dict):
elif typ == "constant" and not isinstance(value, str | int | float | bool):
raise ValueError("value must be a string, int, float, or bool")
return typ

@ -32,35 +32,6 @@ export type IAppDetailLayoutProps = {
appId: string
}
const useIframeHeader = () => {
const [show, setShow] = useState(true);
useEffect(() => {
// 监听父级指定操作
const handler = (event: MessageEvent) => {
// if (event.origin !== "https://gcgj.ngsk.tech:7001") return;
if (event.data.type === "HIDDEN") setShow(() => false);
};
// 初始化完成后提示iframe操作
window.parent.postMessage(
{
type: "SIDEBA",
},
"*"
);
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, []);
return {
show,
};
};
const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const {
children,
@ -85,7 +56,6 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
icon: NavIcon
selectedIcon: NavIcon
}>>([])
const { show } = useIframeHeader()
const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => {
const navs = [
@ -190,9 +160,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return (
<div className={cn(s.app, 'relative flex', 'overflow-hidden')}>
{show ? appDetail && (
{appDetail && (
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background as string} desc={appDetail.mode} navigation={navigation} />
): ''}
)}
<div className="grow overflow-hidden bg-components-panel-bg">
{children}
</div>

@ -1,5 +1,5 @@
'use client'
import React, { useEffect, useState } from 'react'
import React, { useState } from 'react'
import dayjs from 'dayjs'
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
import { useTranslation } from 'react-i18next'
@ -9,7 +9,6 @@ import type { Item } from '@/app/components/base/select'
import { SimpleSelect } from '@/app/components/base/select'
import { TIME_PERIOD_MAPPING } from '@/app/components/app/log/filter'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useRouter } from 'next/navigation'
dayjs.extend(quarterOfYear)
@ -22,44 +21,12 @@ export type IChartViewProps = {
headerRight: React.ReactNode
}
let state = 0
const useIframeRedirection = () => {
const router = useRouter()
useEffect(() => {
// 监听父级指定操作
const handler = (event: MessageEvent) => {
// if (event.origin !== "https://gcgj.ngsk.tech:7001") return;
if (
event.data.type === 'CHART_VIEW_REDIRECT'
&& event.data.redirectUrl
&& state < 3 // 防止无限重定向
) {
state++
router.replace(event.data.redirectUrl)
}
}
// 首页初始化完成后提示iframe操作
window.parent.postMessage(
{
type: 'CHART_VIEW',
},
'*',
)
window.addEventListener('message', handler)
return () => window.removeEventListener('message', handler)
}, [])
}
export default function ChartView({ appId, headerRight }: IChartViewProps) {
const { t } = useTranslation()
const appDetail = useAppStore(state => state.appDetail)
const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
const isWorkflow = appDetail?.mode === 'workflow'
const [period, setPeriod] = useState<PeriodParams>({ name: t('appLog.filter.period.last7days'), query: { start: today.subtract(7, 'day').startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } })
useIframeRedirection()
const onSelect = (item: Item) => {
if (item.value === -1) {

@ -1,5 +1,5 @@
'use client'
import type { FC, JSX } from 'react'
import type { FC } from 'react'
import React, { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'

@ -15,6 +15,8 @@ import {
verifyEmail,
} from '@/service/common'
import { noop } from 'lodash-es'
import { asyncRunSafe } from '@/utils'
import type { ResponseError } from '@/service/fetch'
type Props = {
show: boolean
@ -39,6 +41,7 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
const [time, setTime] = useState<number>(0)
const [stepToken, setStepToken] = useState<string>('')
const [newEmailExited, setNewEmailExited] = useState<boolean>(false)
const [unAvailableEmail, setUnAvailableEmail] = useState<boolean>(false)
const [isCheckingEmail, setIsCheckingEmail] = useState<boolean>(false)
const startCount = () => {
@ -124,9 +127,17 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
email,
})
setNewEmailExited(false)
setUnAvailableEmail(false)
}
catch {
setNewEmailExited(true)
catch (e: any) {
if (e.status === 400) {
const [, errRespData] = await asyncRunSafe<ResponseError>(e.json())
const { code } = errRespData || {}
if (code === 'email_already_in_use')
setNewEmailExited(true)
if (code === 'account_in_freeze')
setUnAvailableEmail(true)
}
}
finally {
setIsCheckingEmail(false)
@ -291,15 +302,18 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => {
placeholder={t('common.account.changeEmail.emailPlaceholder')}
value={mail}
onChange={e => handleNewEmailValueChange(e.target.value)}
destructive={newEmailExited}
destructive={newEmailExited || unAvailableEmail}
/>
{newEmailExited && (
<div className='body-xs-regular mt-1 py-0.5 text-text-destructive'>{t('common.account.changeEmail.existingEmail')}</div>
)}
{unAvailableEmail && (
<div className='body-xs-regular mt-1 py-0.5 text-text-destructive'>{t('common.account.changeEmail.unAvailableEmail')}</div>
)}
</div>
<div className='mt-3 space-y-2'>
<Button
disabled={!mail || newEmailExited || isCheckingEmail || !isValidEmail(mail)}
disabled={!mail || newEmailExited || unAvailableEmail || isCheckingEmail || !isValidEmail(mail)}
className='!w-full'
variant='primary'
onClick={sendCodeToNewEmail}

@ -1,4 +1,4 @@
import type { JSX } from 'react'
import type { ReactElement } from 'react'
import { cloneElement, useCallback } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -7,7 +7,7 @@ import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigge
import { RiMoreLine } from '@remixicon/react'
export type Operation = {
id: string; title: string; icon: JSX.Element; onClick: () => void
id: string; title: string; icon: ReactElement; onClick: () => void
}
const AppOperations = ({ operations, gap }: {
@ -47,7 +47,7 @@ const AppOperations = ({ operations, gap }: {
updatedEntries[id] = true
width += gap + childWidth
}
else {
else {
if (i === childrens.length - 1 && width + childWidth <= containerWidth)
updatedEntries[id] = true
else

@ -91,7 +91,7 @@ const PureSelect = ({
triggerPopupSameWidth={triggerPopupSameWidth}
>
<PortalToFollowElemTrigger
onClick={() => !disabled && handleOpenChange(!mergedOpen)}
onClick={() => handleOpenChange(!mergedOpen)}
asChild
>
<div
@ -116,7 +116,7 @@ const PureSelect = ({
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className={cn(
'z-[9999]',
'z-10',
popupWrapperClassName,
)}>
<div

@ -70,7 +70,7 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
const [isShowModifyRetrievalModal, setIsShowModifyRetrievalModal] = useState(false)
const [isShowRightPanel, { setTrue: showRightPanel, setFalse: hideRightPanel, set: setShowRightPanel }] = useBoolean(!isMobile)
const renderHitResults = (results: HitTesting[] | ExternalKnowledgeBaseHitTesting[]) => (
<div className='flex h-full flex-col rounded-tl-2xl bg-background-body px-4 py-3'>
<div className='flex h-full flex-col rounded-t-2xl bg-background-body px-4 py-3'>
<div className='mb-2 shrink-0 pl-2 font-semibold leading-6 text-text-primary'>
{t('datasetHitTesting.hit.title', { num: results.length })}
</div>
@ -93,7 +93,7 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
)
const renderEmptyState = () => (
<div className='flex h-full flex-col items-center justify-center rounded-tl-2xl bg-background-body px-4 py-3'>
<div className='flex h-full flex-col items-center justify-center rounded-t-2xl bg-background-body px-4 py-3'>
<div className={cn(docStyle.commonIcon, docStyle.targetIcon, '!h-14 !w-14 !bg-text-quaternary')} />
<div className='mt-3 text-[13px] text-text-quaternary'>
{t('datasetHitTesting.hit.emptyTip')}
@ -180,7 +180,7 @@ const HitTestingPage: FC<Props> = ({ datasetId }: Props) => {
<div className='flex flex-col pt-3'>
{/* {renderHitResults(generalResultData)} */}
{submitLoading
? <div className='flex h-full flex-col rounded-tl-2xl bg-background-body px-4 py-3'>
? <div className='flex h-full flex-col rounded-t-2xl bg-background-body px-4 py-3'>
<CardSkelton />
</div>
: (

@ -1,5 +1,5 @@
'use client'
import React, { useState, useEffect } from "react";
import React, { useState } from 'react'
import { usePathname } from 'next/navigation'
import s from './index.module.css'
import { useEventEmitterContextContext } from '@/context/event-emitter'
@ -9,33 +9,6 @@ type HeaderWrapperProps = {
children: React.ReactNode
}
const useIframeHeader = () => {
const [show, setShow] = useState(true);
useEffect(() => {
// 监听父级指定操作
const handler = (event: MessageEvent) => {
// if (event.origin !== "https://gcgj.ngsk.tech:7001") return;
if (event.data.type === "HIDDEN") setShow(() => false);
};
// 初始化完成后提示iframe操作
window.parent.postMessage(
{
type: "HEADER",
},
"*"
);
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, []);
return {
show,
};
};
const HeaderWrapper = ({
children,
}: HeaderWrapperProps) => {
@ -46,7 +19,6 @@ const HeaderWrapper = ({
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
const { eventEmitter } = useEventEmitterContextContext()
const { show } = useIframeHeader();
eventEmitter?.useSubscription((v: any) => {
if (v?.type === 'workflow-canvas-maximize')
@ -54,22 +26,15 @@ const HeaderWrapper = ({
})
return (
<>
{show ? (
<div
className={classNames(
"sticky left-0 right-0 top-0 z-[15] flex min-h-[56px] shrink-0 grow-0 basis-auto flex-col",
s.header,
isBordered ? "border-b border-divider-regular" : "",
hideHeader && inWorkflowCanvas && "hidden"
)}
>
{children}
</div>
) : (
""
)}
</>
);
<div className={classNames(
'sticky left-0 right-0 top-0 z-[15] flex min-h-[56px] shrink-0 grow-0 basis-auto flex-col',
s.header,
isBordered ? 'border-b border-divider-regular' : '',
hideHeader && inWorkflowCanvas && 'hidden',
)}
>
{children}
</div>
)
}
export default HeaderWrapper

@ -164,7 +164,7 @@ const FormInputItem: FC<Props> = ({
...value,
[variable]: {
...varInput,
value: newValue,
...newValue,
},
})
}
@ -242,7 +242,7 @@ const FormInputItem: FC<Props> = ({
<AppSelector
disabled={readOnly}
scope={scope || 'all'}
value={varInput?.value}
value={varInput as any}
onSelect={handleAppOrModelSelect}
/>
)}
@ -251,7 +251,7 @@ const FormInputItem: FC<Props> = ({
popupClassName='!w-[387px]'
isAdvancedMode
isInWorkflow
value={varInput?.value}
value={varInput}
setModel={handleAppOrModelSelect}
readonly={readOnly}
scope={scope}

@ -15,37 +15,6 @@ import Toast from '@/app/components/base/toast'
import { IS_CE_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
const useIframeToken = () => {
const router = useRouter();
useEffect(() => {
// 监听父级指定操作
const handler = (event: MessageEvent) => {
// if (event.origin !== "https://gcgj.ngsk.tech:7001") return;
if (
event.data.type === "LOGIN_AND_WORKFLOW_INTO" &&
event.data.tokens &&
event.data.workflowUrl
) {
localStorage.setItem("console_token", event.data.tokens.console_token);
localStorage.setItem("refresh_token", event.data.tokens.refresh_token);
router.replace(event.data.workflowUrl);
}
};
// 首页初始化完成后提示iframe操作
window.parent.postMessage(
{
type: "LOGIN_AND_WORKFLOW",
},
"*"
);
window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
}, []);
};
const NormalForm = () => {
const { t } = useTranslation()
const router = useRouter()
@ -60,7 +29,6 @@ const NormalForm = () => {
const [showORLine, setShowORLine] = useState(false)
const [allMethodsAreDisabled, setAllMethodsAreDisabled] = useState(false)
const [workspaceName, setWorkSpaceName] = useState('')
useIframeToken();
const isInviteLink = Boolean(invite_token && invite_token !== 'null')

@ -248,6 +248,7 @@ const translation = {
emailLabel: 'New email',
emailPlaceholder: 'Enter a new email',
existingEmail: 'A user with this email already exists.',
unAvailableEmail: 'This email is temporarily unavailable.',
sendVerifyCode: 'Send verification code',
continue: 'Continue',
changeTo: 'Change to {{email}}',

@ -249,6 +249,7 @@ const translation = {
emailLabel: '新しいメール',
emailPlaceholder: '新しいメールを入力してください',
existingEmail: 'このメールアドレスのユーザーは既に存在します',
unAvailableEmail: 'このメールアドレスは現在使用できません。',
sendVerifyCode: '確認コードを送信',
continue: '続行',
changeTo: '{{email}} に変更',

@ -248,6 +248,7 @@ const translation = {
emailLabel: '新邮箱',
emailPlaceholder: '输入新邮箱',
existingEmail: '该邮箱已存在',
unAvailableEmail: '该邮箱暂时无法使用。',
sendVerifyCode: '发送验证码',
continue: '继续',
changeTo: '更改为 {{email}}',

@ -43,25 +43,14 @@ const nextConfig = {
search: '',
})),
},
experimental: {},
experimental: {
},
// fix all before production. Now it slow the develop speed.
eslint: {
// Warning: This allows production builds to successfully complete even if
// your project has ESLint errors.
ignoreDuringBuilds: true,
dirs: [
'app',
'bin',
'config',
'context',
'hooks',
'i18n',
'models',
'service',
'test',
'types',
'utils',
],
dirs: ['app', 'bin', 'config', 'context', 'hooks', 'i18n', 'models', 'service', 'test', 'types', 'utils'],
},
typescript: {
// https://nextjs.org/docs/api-reference/next.config.js/ignoring-typescript-errors
@ -78,23 +67,6 @@ const nextConfig = {
]
},
output: 'standalone',
async headers() {
return [
{
source: '/(.*)', // 匹配所有路由
headers: [
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN', // 或 ALLOWALL更宽松
},
{
key: 'Content-Security-Policy',
value: 'frame-ancestors "self" *', // 允许所有
},
],
},
]
},
}
module.exports = withBundleAnalyzer(withMDX(nextConfig))

Loading…
Cancel
Save