box connector_app
This commit is contained in:
parent
58205f62e9
commit
d263989317
5 changed files with 309 additions and 57 deletions
|
|
@ -28,11 +28,12 @@ from api.db import InputType
|
||||||
from api.db.services.connector_service import ConnectorService, SyncLogsService
|
from api.db.services.connector_service import ConnectorService, SyncLogsService
|
||||||
from api.utils.api_utils import get_data_error_result, get_json_result, get_request_json, validate_request
|
from api.utils.api_utils import get_data_error_result, get_json_result, get_request_json, validate_request
|
||||||
from common.constants import RetCode, TaskStatus
|
from common.constants import RetCode, TaskStatus
|
||||||
from common.data_source.config import GOOGLE_DRIVE_WEB_OAUTH_REDIRECT_URI, GMAIL_WEB_OAUTH_REDIRECT_URI, DocumentSource
|
from common.data_source.config import GOOGLE_DRIVE_WEB_OAUTH_REDIRECT_URI, GMAIL_WEB_OAUTH_REDIRECT_URI, BOX_WEB_OAUTH_REDIRECT_URI, DocumentSource
|
||||||
from common.data_source.google_util.constant import GOOGLE_WEB_OAUTH_POPUP_TEMPLATE, GOOGLE_SCOPES
|
from common.data_source.google_util.constant import WEB_OAUTH_POPUP_TEMPLATE, GOOGLE_SCOPES
|
||||||
from common.misc_utils import get_uuid
|
from common.misc_utils import get_uuid
|
||||||
from rag.utils.redis_conn import REDIS_CONN
|
from rag.utils.redis_conn import REDIS_CONN
|
||||||
from api.apps import login_required, current_user
|
from api.apps import login_required, current_user
|
||||||
|
from box_sdk_gen import BoxOAuth, OAuthConfig, GetAuthorizeUrlOptions
|
||||||
|
|
||||||
|
|
||||||
@manager.route("/set", methods=["POST"]) # noqa: F821
|
@manager.route("/set", methods=["POST"]) # noqa: F821
|
||||||
|
|
@ -117,8 +118,6 @@ def rm_connector(connector_id):
|
||||||
return get_json_result(data=True)
|
return get_json_result(data=True)
|
||||||
|
|
||||||
|
|
||||||
GOOGLE_WEB_FLOW_STATE_PREFIX = "google_drive_web_flow_state"
|
|
||||||
GOOGLE_WEB_FLOW_RESULT_PREFIX = "google_drive_web_flow_result"
|
|
||||||
WEB_FLOW_TTL_SECS = 15 * 60
|
WEB_FLOW_TTL_SECS = 15 * 60
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -129,10 +128,7 @@ def _web_state_cache_key(flow_id: str, source_type: str | None = None) -> str:
|
||||||
When source_type == "gmail", a different prefix is used so that
|
When source_type == "gmail", a different prefix is used so that
|
||||||
Drive/Gmail flows don't clash in Redis.
|
Drive/Gmail flows don't clash in Redis.
|
||||||
"""
|
"""
|
||||||
if source_type == "gmail":
|
prefix = f"{source_type}_web_flow_state"
|
||||||
prefix = "gmail_web_flow_state"
|
|
||||||
else:
|
|
||||||
prefix = GOOGLE_WEB_FLOW_STATE_PREFIX
|
|
||||||
return f"{prefix}:{flow_id}"
|
return f"{prefix}:{flow_id}"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -141,10 +137,7 @@ def _web_result_cache_key(flow_id: str, source_type: str | None = None) -> str:
|
||||||
|
|
||||||
Mirrors _web_state_cache_key logic for result storage.
|
Mirrors _web_state_cache_key logic for result storage.
|
||||||
"""
|
"""
|
||||||
if source_type == "gmail":
|
prefix = f"{source_type}_web_flow_result"
|
||||||
prefix = "gmail_web_flow_result"
|
|
||||||
else:
|
|
||||||
prefix = GOOGLE_WEB_FLOW_RESULT_PREFIX
|
|
||||||
return f"{prefix}:{flow_id}"
|
return f"{prefix}:{flow_id}"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -180,7 +173,7 @@ async def _render_web_oauth_popup(flow_id: str, success: bool, message: str, sou
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# TODO(google-oauth): title/heading/message may need to reflect drive/gmail based on cached type
|
# TODO(google-oauth): title/heading/message may need to reflect drive/gmail based on cached type
|
||||||
html = GOOGLE_WEB_OAUTH_POPUP_TEMPLATE.format(
|
html = WEB_OAUTH_POPUP_TEMPLATE.format(
|
||||||
title=f"Google {source.capitalize()} Authorization",
|
title=f"Google {source.capitalize()} Authorization",
|
||||||
heading="Authorization complete" if success else "Authorization failed",
|
heading="Authorization complete" if success else "Authorization failed",
|
||||||
message=escaped_message,
|
message=escaped_message,
|
||||||
|
|
@ -204,8 +197,8 @@ async def start_google_web_oauth():
|
||||||
redirect_uri = GMAIL_WEB_OAUTH_REDIRECT_URI
|
redirect_uri = GMAIL_WEB_OAUTH_REDIRECT_URI
|
||||||
scopes = GOOGLE_SCOPES[DocumentSource.GMAIL]
|
scopes = GOOGLE_SCOPES[DocumentSource.GMAIL]
|
||||||
else:
|
else:
|
||||||
redirect_uri = GOOGLE_DRIVE_WEB_OAUTH_REDIRECT_URI if source == "google-drive" else GMAIL_WEB_OAUTH_REDIRECT_URI
|
redirect_uri = GOOGLE_DRIVE_WEB_OAUTH_REDIRECT_URI
|
||||||
scopes = GOOGLE_SCOPES[DocumentSource.GOOGLE_DRIVE if source == "google-drive" else DocumentSource.GMAIL]
|
scopes = GOOGLE_SCOPES[DocumentSource.GOOGLE_DRIVE]
|
||||||
|
|
||||||
if not redirect_uri:
|
if not redirect_uri:
|
||||||
return get_json_result(
|
return get_json_result(
|
||||||
|
|
@ -271,8 +264,6 @@ async def google_gmail_web_oauth_callback():
|
||||||
state_id = request.args.get("state")
|
state_id = request.args.get("state")
|
||||||
error = request.args.get("error")
|
error = request.args.get("error")
|
||||||
source = "gmail"
|
source = "gmail"
|
||||||
if source != 'gmail':
|
|
||||||
return await _render_web_oauth_popup("", False, "Invalid Google OAuth type.", source)
|
|
||||||
|
|
||||||
error_description = request.args.get("error_description") or error
|
error_description = request.args.get("error_description") or error
|
||||||
|
|
||||||
|
|
@ -313,9 +304,6 @@ async def google_gmail_web_oauth_callback():
|
||||||
"credentials": creds_json,
|
"credentials": creds_json,
|
||||||
}
|
}
|
||||||
REDIS_CONN.set_obj(_web_result_cache_key(state_id, source), result_payload, WEB_FLOW_TTL_SECS)
|
REDIS_CONN.set_obj(_web_result_cache_key(state_id, source), result_payload, WEB_FLOW_TTL_SECS)
|
||||||
|
|
||||||
print("\n\n", _web_result_cache_key(state_id, source), "\n\n")
|
|
||||||
|
|
||||||
REDIS_CONN.delete(_web_state_cache_key(state_id, source))
|
REDIS_CONN.delete(_web_state_cache_key(state_id, source))
|
||||||
|
|
||||||
return await _render_web_oauth_popup(state_id, True, "Authorization completed successfully.", source)
|
return await _render_web_oauth_popup(state_id, True, "Authorization completed successfully.", source)
|
||||||
|
|
@ -326,8 +314,6 @@ async def google_drive_web_oauth_callback():
|
||||||
state_id = request.args.get("state")
|
state_id = request.args.get("state")
|
||||||
error = request.args.get("error")
|
error = request.args.get("error")
|
||||||
source = "google-drive"
|
source = "google-drive"
|
||||||
if source not in ("google-drive", "gmail"):
|
|
||||||
return await _render_web_oauth_popup("", False, "Invalid Google OAuth type.", source)
|
|
||||||
|
|
||||||
error_description = request.args.get("error_description") or error
|
error_description = request.args.get("error_description") or error
|
||||||
|
|
||||||
|
|
@ -391,3 +377,97 @@ async def poll_google_web_result():
|
||||||
|
|
||||||
REDIS_CONN.delete(_web_result_cache_key(flow_id, source))
|
REDIS_CONN.delete(_web_result_cache_key(flow_id, source))
|
||||||
return get_json_result(data={"credentials": result.get("credentials")})
|
return get_json_result(data={"credentials": result.get("credentials")})
|
||||||
|
|
||||||
|
@manager.route("/box/oauth/web/start", methods=["POST"]) # noqa: F821
|
||||||
|
@login_required
|
||||||
|
@validate_request("")
|
||||||
|
async def start_box_web_oauth():
|
||||||
|
req = await get_request_json()
|
||||||
|
|
||||||
|
client_id = req.get("client_id")
|
||||||
|
client_secret = req.get("client_secret")
|
||||||
|
redirect_uri = req.get("redirect_uri", BOX_WEB_OAUTH_REDIRECT_URI)
|
||||||
|
|
||||||
|
if not client_id or not client_secret:
|
||||||
|
return get_json_result(code=RetCode.ARGUMENT_ERROR, message="Box client_id and client_secret are required.")
|
||||||
|
|
||||||
|
flow_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
box_auth = BoxOAuth(
|
||||||
|
oauth_config=OAuthConfig(
|
||||||
|
client_id=client_id,
|
||||||
|
client_secret=client_secret,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
auth_url = box_auth.get_authorize_url(
|
||||||
|
options=GetAuthorizeUrlOptions(
|
||||||
|
redirect_uri=redirect_uri,
|
||||||
|
state=flow_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
cache_payload = {
|
||||||
|
"user_id": current_user.id,
|
||||||
|
"auth_url": auth_url,
|
||||||
|
"client_id": client_id,
|
||||||
|
"clientsecret": client_secret,
|
||||||
|
"created_at": int(time.time()),
|
||||||
|
}
|
||||||
|
REDIS_CONN.set_obj(_web_state_cache_key(flow_id, "box"), cache_payload, WEB_FLOW_TTL_SECS)
|
||||||
|
return get_json_result(
|
||||||
|
data = {
|
||||||
|
"flow_id": flow_id,
|
||||||
|
"authorization_url": auth_url,
|
||||||
|
"expires_in": WEB_FLOW_TTL_SECS,}
|
||||||
|
)
|
||||||
|
|
||||||
|
@manager.route("/box/oauth/web/callback", methods=["GET"]) # noqa: F821
|
||||||
|
async def box_web_oauth_callback():
|
||||||
|
flow_id = request.args.get("state")
|
||||||
|
|
||||||
|
if not code or not flow_id:
|
||||||
|
return await _render_web_oauth_popup("", False, "Missing OAuth parameters.", "box")
|
||||||
|
|
||||||
|
cache_payload = REDIS_CONN.get_obj(_web_state_cache_key(flow_id, "box"))
|
||||||
|
if not cache_payload:
|
||||||
|
return get_json_result(code=RetCode.ARGUMENT_ERROR, message="Box OAuth session expired or invalid.")
|
||||||
|
|
||||||
|
error = request.args.get("error")
|
||||||
|
error_description = request.args.get("error_description") or error
|
||||||
|
if error:
|
||||||
|
REDIS_CONN.delete(_web_state_cache_key(flow_id, "box"))
|
||||||
|
return await _render_web_oauth_popup(flow_id, False, error_description or "Authorization failed.", "box")
|
||||||
|
|
||||||
|
code = request.args.get("code")
|
||||||
|
if not code:
|
||||||
|
return await _render_web_oauth_popup(flow_id, False, "Missing authorization code from Box.", "box")
|
||||||
|
|
||||||
|
result_payload = {
|
||||||
|
"user_id": cache_payload.get("user_id"),
|
||||||
|
"client_id": cache_payload.get("client_id"),
|
||||||
|
"client_secret": cache_payload.get("clientsecret"),
|
||||||
|
"code": code,
|
||||||
|
}
|
||||||
|
|
||||||
|
REDIS_CONN.set_obj(_web_result_cache_key(flow_id, "box"), result_payload, WEB_FLOW_TTL_SECS)
|
||||||
|
REDIS_CONN.delete(_web_state_cache_key(flow_id, "box"))
|
||||||
|
|
||||||
|
return await _render_web_oauth_popup(flow_id, True, "Authorization completed successfully.", "box")
|
||||||
|
|
||||||
|
@manager.route("/box/oauth/web/result", methods=["POST"]) # noqa: F821
|
||||||
|
@login_required
|
||||||
|
@validate_request("flow_id")
|
||||||
|
async def poll_box_web_result():
|
||||||
|
req = await get_request_json()
|
||||||
|
flow_id = req.get("flow_id")
|
||||||
|
|
||||||
|
cache_raw = REDIS_CONN.get_obj(_web_result_cache_key(flow_id, "box"))
|
||||||
|
if not cache_raw:
|
||||||
|
return get_json_result(code=RetCode.RUNNING, message="Authorization is still pending.")
|
||||||
|
|
||||||
|
if cache_raw.get("user_id") != current_user.id:
|
||||||
|
return get_json_result(code=RetCode.PERMISSION_ERROR, message="You are not allowed to access this authorization result.")
|
||||||
|
|
||||||
|
REDIS_CONN.delete(_web_result_cache_key(flow_id, "box"))
|
||||||
|
return get_json_result(data={"credentials": cache_raw})
|
||||||
|
|
@ -20,12 +20,7 @@ class BoxConnector(LoadConnector, PollConnector):
|
||||||
self.folder_id = folder_id
|
self.folder_id = folder_id
|
||||||
self.use_marker = use_marker
|
self.use_marker = use_marker
|
||||||
|
|
||||||
|
def load_credentials(self, auth: Any):
|
||||||
def load_credentials(self, credentials):
|
|
||||||
auth = credentials.get("auth")
|
|
||||||
if not auth:
|
|
||||||
raise ConnectorMissingCredentialError("Box auth is required")
|
|
||||||
|
|
||||||
self.box_client = BoxClient(auth=auth)
|
self.box_client = BoxClient(auth=auth)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ from common.data_source.utils import load_all_docs_from_checkpoint_connector
|
||||||
from common.log_utils import init_root_logger
|
from common.log_utils import init_root_logger
|
||||||
from common.signal_utils import start_tracemalloc_and_snapshot, stop_tracemalloc
|
from common.signal_utils import start_tracemalloc_and_snapshot, stop_tracemalloc
|
||||||
from common.versions import get_ragflow_version
|
from common.versions import get_ragflow_version
|
||||||
|
from box_sdk_gen import BoxClient, BoxOAuth, OAuthConfig, GetAuthorizeUrlOptions
|
||||||
|
|
||||||
MAX_CONCURRENT_TASKS = int(os.environ.get("MAX_CONCURRENT_TASKS", "5"))
|
MAX_CONCURRENT_TASKS = int(os.environ.get("MAX_CONCURRENT_TASKS", "5"))
|
||||||
task_limiter = trio.Semaphore(MAX_CONCURRENT_TASKS)
|
task_limiter = trio.Semaphore(MAX_CONCURRENT_TASKS)
|
||||||
|
|
@ -610,7 +611,16 @@ class BOX(SyncBase):
|
||||||
use_marker=self.conf.get("use_marker", False)
|
use_marker=self.conf.get("use_marker", False)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.connector.load_credentials(self.conf["credentials"])
|
credential = self.conf['credentials']
|
||||||
|
auth = BoxOAuth(
|
||||||
|
OAuthConfig(
|
||||||
|
client_id=credential['client_id'],
|
||||||
|
client_secret=credential['client_secret'],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
auth.get_tokens_authorization_code_grant(credential['code'])
|
||||||
|
|
||||||
|
self.connector.load_credentials(auth)
|
||||||
if task["reindex"] == "1" or not task["poll_range_start"]:
|
if task["reindex"] == "1" or not task["poll_range_start"]:
|
||||||
document_generator = self.connector.load_from_state()
|
document_generator = self.connector.load_from_state()
|
||||||
begin_info = "totally"
|
begin_info = "totally"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import message from '@/components/ui/message';
|
||||||
|
|
||||||
|
export type BoxTokenFieldProps = {
|
||||||
|
/** 存储在表单里的值,约定为 JSON 字符串 */
|
||||||
|
value?: string;
|
||||||
|
/** 表单回写,用 JSON 字符串承载 client_id / client_secret / redirect_uri */
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type BoxCredentials = {
|
||||||
|
client_id?: string;
|
||||||
|
client_secret?: string;
|
||||||
|
redirect_uri?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseBoxCredentials = (content?: string): BoxCredentials | null => {
|
||||||
|
if (!content) return null;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(content);
|
||||||
|
return {
|
||||||
|
client_id: parsed.client_id,
|
||||||
|
client_secret: parsed.client_secret,
|
||||||
|
redirect_uri: parsed.redirect_uri,
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const BoxTokenField = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder,
|
||||||
|
}: BoxTokenFieldProps) => {
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
const [clientId, setClientId] = useState('');
|
||||||
|
const [clientSecret, setClientSecret] = useState('');
|
||||||
|
const [redirectUri, setRedirectUri] = useState('');
|
||||||
|
|
||||||
|
const parsed = useMemo(() => parseBoxCredentials(value), [value]);
|
||||||
|
|
||||||
|
// 当外部 value 变化且弹窗关闭时,同步初始值
|
||||||
|
useEffect(() => {
|
||||||
|
if (!dialogOpen) {
|
||||||
|
setClientId(parsed?.client_id ?? '');
|
||||||
|
setClientSecret(parsed?.client_secret ?? '');
|
||||||
|
setRedirectUri(parsed?.redirect_uri ?? '');
|
||||||
|
}
|
||||||
|
}, [parsed, dialogOpen]);
|
||||||
|
|
||||||
|
const hasConfigured = useMemo(
|
||||||
|
() =>
|
||||||
|
Boolean(
|
||||||
|
parsed?.client_id && parsed?.client_secret && parsed?.redirect_uri,
|
||||||
|
),
|
||||||
|
[parsed],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOpenDialog = useCallback(() => {
|
||||||
|
setDialogOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleCloseDialog = useCallback(() => {
|
||||||
|
setDialogOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(() => {
|
||||||
|
if (!clientId.trim() || !clientSecret.trim() || !redirectUri.trim()) {
|
||||||
|
message.error(
|
||||||
|
'Please fill in Client ID, Client Secret, and Redirect URI.',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload: BoxCredentials = {
|
||||||
|
client_id: clientId.trim(),
|
||||||
|
client_secret: clientSecret.trim(),
|
||||||
|
redirect_uri: redirectUri.trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
onChange(JSON.stringify(payload));
|
||||||
|
message.success('Box credentials saved locally.');
|
||||||
|
setDialogOpen(false);
|
||||||
|
} catch {
|
||||||
|
message.error('Failed to save Box credentials.');
|
||||||
|
}
|
||||||
|
}, [clientId, clientSecret, redirectUri, onChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{hasConfigured && (
|
||||||
|
<div className="flex flex-wrap items-center gap-2 rounded-md border border-dashed border-muted-foreground/40 bg-muted/20 px-3 py-2 text-xs text-muted-foreground">
|
||||||
|
<span className="rounded-full bg-emerald-100 px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-emerald-700">
|
||||||
|
Configured
|
||||||
|
</span>
|
||||||
|
<p className="m-0">
|
||||||
|
Box OAuth credentials have been configured. You can update them at
|
||||||
|
any time.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button variant="outline" onClick={handleOpenDialog}>
|
||||||
|
{hasConfigured ? 'Edit Box credentials' : 'Configure Box credentials'}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={dialogOpen}
|
||||||
|
onOpenChange={(open) =>
|
||||||
|
!open ? handleCloseDialog() : setDialogOpen(true)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DialogContent
|
||||||
|
onPointerDownOutside={(e) => e.preventDefault()}
|
||||||
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
|
onEscapeKeyDown={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Configure Box OAuth credentials</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Enter your Box application's Client ID, Client Secret, and
|
||||||
|
Redirect URI. These values will be stored in the form field and
|
||||||
|
can be used later to start the OAuth flow.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-3 pt-2">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-sm font-medium">Client ID</label>
|
||||||
|
<Input
|
||||||
|
value={clientId}
|
||||||
|
placeholder={placeholder || 'Enter Box Client ID'}
|
||||||
|
onChange={(e) => setClientId(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-sm font-medium">Client Secret</label>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
value={clientSecret}
|
||||||
|
placeholder="Enter Box Client Secret"
|
||||||
|
onChange={(e) => setClientSecret(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="text-sm font-medium">Redirect URI</label>
|
||||||
|
<Input
|
||||||
|
value={redirectUri}
|
||||||
|
placeholder="https://example.com/box/oauth/callback"
|
||||||
|
onChange={(e) => setRedirectUri(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter className="pt-3">
|
||||||
|
<Button variant="ghost" onClick={handleCloseDialog}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSubmit}>Submit</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BoxTokenField;
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { FormFieldType } from '@/components/dynamic-form';
|
import { FormFieldType } from '@/components/dynamic-form';
|
||||||
import SvgIcon from '@/components/svg-icon';
|
import SvgIcon from '@/components/svg-icon';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
|
import BoxTokenField from './component/box-token-field';
|
||||||
import { ConfluenceIndexingModeField } from './component/confluence-token-field';
|
import { ConfluenceIndexingModeField } from './component/confluence-token-field';
|
||||||
import GmailTokenField from './component/gmail-token-field';
|
import GmailTokenField from './component/gmail-token-field';
|
||||||
import GoogleDriveTokenField from './component/google-drive-token-field';
|
import GoogleDriveTokenField from './component/google-drive-token-field';
|
||||||
|
|
@ -559,37 +560,24 @@ export const DataSourceFormFields = {
|
||||||
],
|
],
|
||||||
[DataSourceKey.BOX]: [
|
[DataSourceKey.BOX]: [
|
||||||
{
|
{
|
||||||
label: 'Client ID',
|
label: 'Box OAuth JSON',
|
||||||
name: 'config.credentials.box_client_id',
|
name: 'config.credentials.box_tokens',
|
||||||
type: FormFieldType.Text,
|
type: FormFieldType.Textarea,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
render: (fieldProps: any) => (
|
||||||
{
|
<BoxTokenField
|
||||||
label: 'Client Secret',
|
value={fieldProps.value}
|
||||||
name: 'config.credentials.box_client_secret',
|
onChange={fieldProps.onChange}
|
||||||
type: FormFieldType.Password,
|
placeholder='{ "client_id": "...", "client_secret": "...", "redirect_uri": "..." }'
|
||||||
required: true,
|
/>
|
||||||
},
|
),
|
||||||
{
|
|
||||||
label: 'Redirect URI',
|
|
||||||
name: 'config.credentials.box_redirect_uri',
|
|
||||||
type: FormFieldType.Text,
|
|
||||||
required: true,
|
|
||||||
placeholder: 'https://example.com/oauth2/callback',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Folder ID',
|
label: 'Folder ID',
|
||||||
name: 'config.folder_id',
|
name: 'config.folder_id',
|
||||||
type: FormFieldType.Text,
|
type: FormFieldType.Text,
|
||||||
required: false,
|
required: false,
|
||||||
placeholder: 'Defaults to root (0)',
|
placeholder: 'Defaults root',
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Index recursively',
|
|
||||||
name: 'config.index_recursively',
|
|
||||||
type: FormFieldType.Checkbox,
|
|
||||||
required: false,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
@ -733,12 +721,10 @@ export const DataSourceFormDefaultValues = {
|
||||||
source: DataSourceKey.BOX,
|
source: DataSourceKey.BOX,
|
||||||
config: {
|
config: {
|
||||||
name: '',
|
name: '',
|
||||||
folder_id: '0',
|
folder_id: '',
|
||||||
index_recursively: false,
|
index_recursively: false,
|
||||||
credentials: {
|
credentials: {
|
||||||
box_client_id: '',
|
box_tokens: '',
|
||||||
box_client_secret: '',
|
|
||||||
box_redirect_uri: '',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue