Unverified Commit c9b0fe47 authored by zxhlyh's avatar zxhlyh Committed by GitHub

Fix/notion sync (#1258)

parent bcd744b6
......@@ -45,15 +45,34 @@ class OAuthDataSource(Resource):
if current_app.config.get('NOTION_INTEGRATION_TYPE') == 'internal':
internal_secret = current_app.config.get('NOTION_INTERNAL_SECRET')
oauth_provider.save_internal_access_token(internal_secret)
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?oauth_data_source=success')
return { 'data': '' }
else:
auth_url = oauth_provider.get_authorization_url()
return redirect(auth_url)
return { 'data': auth_url }, 200
class OAuthDataSourceCallback(Resource):
def get(self, provider: str):
OAUTH_DATASOURCE_PROVIDERS = get_oauth_providers()
with current_app.app_context():
oauth_provider = OAUTH_DATASOURCE_PROVIDERS.get(provider)
if not oauth_provider:
return {'error': 'Invalid provider'}, 400
if 'code' in request.args:
code = request.args.get('code')
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?type=notion&code={code}')
elif 'error' in request.args:
error = request.args.get('error')
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?type=notion&error={error}')
else:
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?type=notion&error=Access denied')
class OAuthDataSourceBinding(Resource):
def get(self, provider: str):
OAUTH_DATASOURCE_PROVIDERS = get_oauth_providers()
with current_app.app_context():
......@@ -69,12 +88,7 @@ class OAuthDataSourceCallback(Resource):
f"An error occurred during the OAuthCallback process with {provider}: {e.response.text}")
return {'error': 'OAuth data source process failed'}, 400
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?oauth_data_source=success')
elif 'error' in request.args:
error = request.args.get('error')
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?oauth_data_source={error}')
else:
return redirect(f'{current_app.config.get("CONSOLE_WEB_URL")}?oauth_data_source=access_denied')
return {'result': 'success'}, 200
class OAuthDataSourceSync(Resource):
......@@ -101,4 +115,5 @@ class OAuthDataSourceSync(Resource):
api.add_resource(OAuthDataSource, '/oauth/data-source/<string:provider>')
api.add_resource(OAuthDataSourceCallback, '/oauth/data-source/callback/<string:provider>')
api.add_resource(OAuthDataSourceBinding, '/oauth/data-source/binding/<string:provider>')
api.add_resource(OAuthDataSourceSync, '/oauth/data-source/<string:provider>/<uuid:binding_id>/sync')
import { useEffect, useState } from 'react'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import { PlusIcon } from '@heroicons/react/24/solid'
import cn from 'classnames'
import Indicator from '../../../indicator'
import Operate from './operate'
import s from './style.module.css'
import NotionIcon from '@/app/components/base/notion-icon'
import { apiPrefix } from '@/config'
import type { DataSourceNotion as TDataSourceNotion } from '@/models/common'
import { useAppContext } from '@/context/app-context'
import { fetchNotionConnection } from '@/service/common'
type DataSourceNotionProps = {
workspaces: TDataSourceNotion[]
......@@ -18,9 +19,30 @@ const DataSourceNotion = ({
}: DataSourceNotionProps) => {
const { t } = useTranslation()
const { isCurrentWorkspaceManager } = useAppContext()
const [canConnectNotion, setCanConnectNotion] = useState(false)
const { data } = useSWR(canConnectNotion ? '/oauth/data-source/notion' : null, fetchNotionConnection)
const connected = !!workspaces.length
const handleConnectNotion = () => {
if (!isCurrentWorkspaceManager)
return
setCanConnectNotion(true)
}
const handleAuthAgain = () => {
if (data?.data)
window.location.href = data.data
else
setCanConnectNotion(true)
}
useEffect(() => {
if (data?.data)
window.location.href = data.data
}, [data])
return (
<div className='mb-2 border-[0.5px] border-gray-200 bg-gray-50 rounded-xl'>
<div className='flex items-center px-3 py-[9px]'>
......@@ -40,26 +62,28 @@ const DataSourceNotion = ({
{
connected
? (
<Link
<div
className={
`flex items-center ml-3 px-3 h-7 bg-white border border-gray-200
rounded-md text-xs font-medium text-gray-700
${isCurrentWorkspaceManager ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
}
href={isCurrentWorkspaceManager ? `${apiPrefix}/oauth/data-source/notion` : '/'}>
onClick={handleConnectNotion}
>
{t('common.dataSource.connect')}
</Link>
</div>
)
: (
<Link
href={isCurrentWorkspaceManager ? `${apiPrefix}/oauth/data-source/notion` : '/' }
<div
className={
`flex items-center px-3 h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md
${isCurrentWorkspaceManager ? 'cursor-pointer' : 'grayscale opacity-50 cursor-default'}`
}>
}
onClick={handleConnectNotion}
>
<PlusIcon className='w-[14px] h-[14px] mr-[5px]' />
{t('common.dataSource.notion.addWorkspace')}
</Link>
</div>
)
}
</div>
......@@ -98,7 +122,7 @@ const DataSourceNotion = ({
}
</div>
<div className='mr-2 w-[1px] h-3 bg-gray-100' />
<Operate workspace={workspace} />
<Operate workspace={workspace} onAuthAgain={handleAuthAgain} />
</div>
))
}
......
'use client'
import { useTranslation } from 'react-i18next'
import { Fragment } from 'react'
import Link from 'next/link'
import { useSWRConfig } from 'swr'
import { EllipsisHorizontalIcon } from '@heroicons/react/24/solid'
import { Menu, Transition } from '@headlessui/react'
import { apiPrefix } from '@/config'
import { syncDataSourceNotion, updateDataSourceNotionAction } from '@/service/common'
import Toast from '@/app/components/base/toast'
import type { DataSourceNotion } from '@/models/common'
......@@ -15,9 +13,11 @@ import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
type OperateProps = {
workspace: DataSourceNotion
onAuthAgain: () => void
}
export default function Operate({
workspace,
onAuthAgain,
}: OperateProps) {
const itemClassName = `
flex px-3 py-2 hover:bg-gray-50 text-sm text-gray-700
......@@ -71,9 +71,10 @@ export default function Operate({
>
<div className="px-1 py-1">
<Menu.Item>
<Link
<div
className={itemClassName}
href={`${apiPrefix}/oauth/data-source/notion`}>
onClick={onAuthAgain}
>
<FilePlus02 className={itemIconClassName} />
<div>
<div className='leading-5'>{t('common.dataSource.notion.changeAuthorizedPages')}</div>
......@@ -81,7 +82,7 @@ export default function Operate({
{workspace.source_info.total} {t('common.dataSource.notion.pagesAuthorized')}
</div>
</div>
</Link>
</div>
</Menu.Item>
<Menu.Item>
<div className={itemClassName} onClick={handleSync}>
......
......@@ -7,7 +7,10 @@ import useSWR from 'swr'
import { useContext } from 'use-context-selector'
import I18n from '@/context/i18n'
import { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
import { fetchFreeQuotaVerify } from '@/service/common'
import {
fetchDataSourceNotionBinding,
fetchFreeQuotaVerify,
} from '@/service/common'
import type { ConfirmCommonProps } from '@/app/components/base/confirm/common'
import Confirm from '@/app/components/base/confirm/common'
......@@ -92,19 +95,56 @@ export const useCheckFreeQuota = () => {
: null
}
export const useCheckNotion = () => {
const router = useRouter()
const [confirm, setConfirm] = useState<ConfirmType | null>(null)
const [canBinding, setCanBinding] = useState(false)
const searchParams = useSearchParams()
const type = searchParams.get('type')
const notionCode = searchParams.get('code')
const notionError = searchParams.get('error')
const { data } = useSWR(
canBinding
? `/oauth/data-source/binding/notion?code=${notionCode}`
: null,
fetchDataSourceNotionBinding,
)
useEffect(() => {
if (data)
router.replace('/', { forceOptimisticNavigation: false })
}, [data, router])
useEffect(() => {
if (type === 'notion') {
if (notionError) {
setConfirm({
type: 'danger',
title: notionError,
})
}
else if (notionCode) {
setCanBinding(true)
}
}
}, [type, notionCode, notionError])
return confirm
}
export const CheckModal = () => {
const router = useRouter()
const { t } = useTranslation()
const [showPayStatusModal, setShowPayStatusModal] = useState(true)
const anthropicConfirmInfo = useAnthropicCheckPay()
const freeQuotaConfirmInfo = useCheckFreeQuota()
const notionConfirmInfo = useCheckNotion()
const handleCancelShowPayStatusModal = useCallback(() => {
setShowPayStatusModal(false)
router.replace('/', { forceOptimisticNavigation: false })
}, [router])
const confirmInfo = anthropicConfirmInfo || freeQuotaConfirmInfo
const confirmInfo = anthropicConfirmInfo || freeQuotaConfirmInfo || notionConfirmInfo
if (!confirmInfo || !showPayStatusModal)
return null
......
......@@ -188,3 +188,11 @@ export const fetchDocumentsLimit: Fetcher<DocumentsLimitResponse, string> = (url
export const fetchFreeQuotaVerify: Fetcher<{ result: string; flag: boolean; reason: string }, string> = (url) => {
return get(url) as Promise<{ result: string; flag: boolean; reason: string }>
}
export const fetchNotionConnection: Fetcher<{ data: string }, string> = (url) => {
return get(url) as Promise<{ data: string }>
}
export const fetchDataSourceNotionBinding: Fetcher<{ result: string }, string> = (url) => {
return get(url) as Promise<{ result: string }>
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment