mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-21 07:01:04 +08:00
### What problem does this PR solve? Closes #15191. RAGFlow shipped a Microsoft Teams connector stub (`common/data_source/teams_connector.py`) whose document-loading methods all returned `[]`, `Teams._generate()` was a `pass`, and Teams was commented out of the data-source settings UI. As a result there was no way to index Teams channel conversations into a knowledge base. This PR implements the connector end to end on top of Microsoft Graph (Office365-REST-Python-Client). It shares the MSAL client-credentials auth shape with the SharePoint connector. **Backend** - `common/data_source/teams_connector.py` - `load_credentials()` now builds the Graph client using an MSAL client-credentials **token callback** — the form `GraphClient` actually expects. (The previous stub passed a raw access-token string to `GraphClient(...)`, which is not how that client is driven.) Token acquisition is lazy, so credential loading performs no network call. - `validate_connector_settings()` lists teams via Graph. - `load_from_checkpoint()` is now a generator that pages teams → channels → messages, flattens each top-level post together with its replies into one blob-based `Document` (`extension` `.txt`/`.html`, `blob`, `size_bytes`, `doc_updated_at`). Incremental syncs are bounded by message `lastModifiedDateTime` (falling back to `createdDateTime`). Per-message errors surface as `ConnectorFailure` instead of aborting the run. - `retrieve_all_slim_docs_perm_sync()` yields id-only `SlimDocument` batches and the checkpoint helpers return proper `TeamsCheckpoint`s. - ACL → `ExternalAccess` mapping is intentionally left best-effort (`load_from_checkpoint_with_perm_sync` delegates to the standard load) because the sync pipeline does not currently persist `ExternalAccess`. - `rag/svr/sync_data_source.py` - Implemented `Teams._generate()` using the existing `CheckpointOutputWrapper` pattern (same shape as Confluence/Jira/Google Drive), supporting full reindex and incremental polling from `poll_range_start`. - `TeamsConnector` is already exported from `common/data_source/__init__.py`. **Frontend (`web/`)** - Enabled the `TEAMS` data-source enum and added its form fields (`tenant_id`, `client_id`, `client_secret`), default values, display metadata, and a Teams icon. - Added `teamsDescription` / `teamsTenantIdTip` to `en.ts` and `zh.ts`. **Tests** - `test/unit_test/data_source/test_teams_connector_unit.py`: mock-based unit tests covering credential loading (incomplete creds raise, happy path sets the Graph client, fetch-without-creds raises), post/reply flattening (incl. the HTML vs text extension), incremental `lastModifiedDateTime` filtering, and slim-doc listing. All 6 pass; `ruff check` is clean. ### Type of change - [x] New Feature (non-breaking change which adds functionality)
1917 lines
49 KiB
TypeScript
1917 lines
49 KiB
TypeScript
import { FormFieldConfig, FormFieldType } from '@/components/dynamic-form';
|
|
import { IconFontFill } from '@/components/icon-font';
|
|
import SvgIcon from '@/components/svg-icon';
|
|
import { t, TFunction } from 'i18next';
|
|
import { Mail, Rss } from 'lucide-react';
|
|
import { useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import BoxTokenField from '../component/box-token-field';
|
|
import GmailTokenField from '../component/gmail-token-field';
|
|
import GoogleDriveTokenField from '../component/google-drive-token-field';
|
|
import { IDataSourceInfoMap } from '../interface';
|
|
import { bitbucketConstant } from './bitbucket-constant';
|
|
import { confluenceConstant } from './confluence-constant';
|
|
import { jiraConstant } from './jira-constant';
|
|
import { S3Constant } from './s3-constant';
|
|
import { seafileConstant } from './seafile-constant';
|
|
|
|
export enum DataSourceKey {
|
|
CONFLUENCE = 'confluence',
|
|
NOTION = 'notion',
|
|
GOOGLE_DRIVE = 'google_drive',
|
|
GMAIL = 'gmail',
|
|
GOOGLE_CLOUD_STORAGE = 'google_cloud_storage',
|
|
OCI_STORAGE = 'oci_storage',
|
|
S3 = 's3',
|
|
R2 = 'r2',
|
|
JIRA = 'jira',
|
|
BOX = 'box',
|
|
DROPBOX = 'dropbox',
|
|
BITBUCKET = 'bitbucket',
|
|
GITLAB = 'gitlab',
|
|
GITHUB = 'github',
|
|
MOODLE = 'moodle',
|
|
DISCORD = 'discord',
|
|
ZENDESK = 'zendesk',
|
|
WEBDAV = 'webdav',
|
|
AIRTABLE = 'airtable',
|
|
ASANA = 'asana',
|
|
IMAP = 'imap',
|
|
DINGTALK_AI_TABLE = 'dingtalk_ai_table',
|
|
SEAFILE = 'seafile',
|
|
MYSQL = 'mysql',
|
|
POSTGRESQL = 'postgresql',
|
|
REST_API = 'rest_api',
|
|
RSS = 'rss',
|
|
TEAMS = 'teams',
|
|
SLACK = 'slack',
|
|
SHAREPOINT = 'sharepoint',
|
|
}
|
|
|
|
type DataSourceFeatureVisibility = {
|
|
syncDeletedFiles?: boolean;
|
|
};
|
|
|
|
type DataSourceFormValues = Record<string, any>;
|
|
|
|
export const DataSourceFeatureVisibilityMap: Partial<
|
|
Record<DataSourceKey, DataSourceFeatureVisibility>
|
|
> = {
|
|
[DataSourceKey.GITHUB]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.GITLAB]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.GOOGLE_DRIVE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.GMAIL]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.IMAP]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.CONFLUENCE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.BOX]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.DROPBOX]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.S3]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.R2]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.GOOGLE_CLOUD_STORAGE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.OCI_STORAGE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.NOTION]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.DISCORD]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.JIRA]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.BITBUCKET]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.AIRTABLE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.DINGTALK_AI_TABLE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.WEBDAV]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.ZENDESK]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.SEAFILE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.ASANA]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.RSS]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.MOODLE]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.TEAMS]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.SLACK]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.SHAREPOINT]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.MYSQL]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
[DataSourceKey.POSTGRESQL]: {
|
|
syncDeletedFiles: true,
|
|
},
|
|
};
|
|
|
|
const isDataSourceFeatureVisible = (
|
|
source?: DataSourceKey,
|
|
feature?: keyof DataSourceFeatureVisibility,
|
|
) => {
|
|
if (!source || !feature) {
|
|
return false;
|
|
}
|
|
|
|
return Boolean(DataSourceFeatureVisibilityMap[source]?.[feature]);
|
|
};
|
|
|
|
export const generateDataSourceInfo = (t: TFunction) => {
|
|
return {
|
|
[DataSourceKey.RSS]: {
|
|
name: 'RSS',
|
|
description: t(`setting.${DataSourceKey.RSS}Description`),
|
|
icon: <Rss className="text-text-primary" size={22} />,
|
|
},
|
|
[DataSourceKey.GOOGLE_CLOUD_STORAGE]: {
|
|
name: 'Google Cloud Storage',
|
|
description: t(
|
|
`setting.${DataSourceKey.GOOGLE_CLOUD_STORAGE}Description`,
|
|
),
|
|
icon: <SvgIcon name={'data-source/google-cloud-storage'} width={38} />,
|
|
},
|
|
[DataSourceKey.OCI_STORAGE]: {
|
|
name: 'Oracle Storage',
|
|
description: t(`setting.${DataSourceKey.OCI_STORAGE}Description`),
|
|
icon: <SvgIcon name={'data-source/oracle-storage'} width={38} />,
|
|
},
|
|
[DataSourceKey.R2]: {
|
|
name: 'R2',
|
|
description: t(`setting.${DataSourceKey.R2}Description`),
|
|
icon: <SvgIcon name={'data-source/r2'} width={38} />,
|
|
},
|
|
[DataSourceKey.S3]: {
|
|
name: 'S3',
|
|
description: t(`setting.${DataSourceKey.S3}Description`),
|
|
icon: <SvgIcon name={'data-source/s3'} width={38} />,
|
|
},
|
|
[DataSourceKey.NOTION]: {
|
|
name: 'Notion',
|
|
description: t(`setting.${DataSourceKey.NOTION}Description`),
|
|
icon: <SvgIcon name={'data-source/notion'} width={38} />,
|
|
},
|
|
[DataSourceKey.DISCORD]: {
|
|
name: 'Discord',
|
|
description: t(`setting.${DataSourceKey.DISCORD}Description`),
|
|
icon: <SvgIcon name={'data-source/discord'} width={38} />,
|
|
},
|
|
[DataSourceKey.CONFLUENCE]: {
|
|
name: 'Confluence',
|
|
description: t(`setting.${DataSourceKey.CONFLUENCE}Description`),
|
|
icon: <SvgIcon name={'data-source/confluence'} width={38} />,
|
|
},
|
|
[DataSourceKey.GOOGLE_DRIVE]: {
|
|
name: 'Google Drive',
|
|
description: t(`setting.${DataSourceKey.GOOGLE_DRIVE}Description`),
|
|
icon: <SvgIcon name={'data-source/google-drive'} width={38} />,
|
|
},
|
|
[DataSourceKey.GMAIL]: {
|
|
name: 'Gmail',
|
|
description: t(`setting.${DataSourceKey.GMAIL}Description`),
|
|
icon: <SvgIcon name={'data-source/gmail'} width={38} />,
|
|
},
|
|
[DataSourceKey.REST_API]: {
|
|
name: 'REST API',
|
|
description: t(`setting.${DataSourceKey.REST_API}Description`),
|
|
icon: <SvgIcon name={'data-source/rest-api'} width={38} />,
|
|
},
|
|
[DataSourceKey.MOODLE]: {
|
|
name: 'Moodle',
|
|
description: t(`setting.${DataSourceKey.MOODLE}Description`),
|
|
icon: <SvgIcon name={'data-source/moodle'} width={38} />,
|
|
},
|
|
[DataSourceKey.TEAMS]: {
|
|
name: 'Microsoft Teams',
|
|
description: t(`setting.${DataSourceKey.TEAMS}Description`),
|
|
icon: <SvgIcon name={'data-source/teams'} width={38} />,
|
|
},
|
|
[DataSourceKey.SLACK]: {
|
|
name: 'Slack',
|
|
description: t(`setting.${DataSourceKey.SLACK}Description`),
|
|
icon: <SvgIcon name={'data-source/slack'} width={38} />,
|
|
},
|
|
[DataSourceKey.SHAREPOINT]: {
|
|
name: 'SharePoint',
|
|
description: t(`setting.${DataSourceKey.SHAREPOINT}Description`),
|
|
icon: <SvgIcon name={'data-source/sharepoint'} width={38} />,
|
|
},
|
|
[DataSourceKey.JIRA]: {
|
|
name: 'Jira',
|
|
description: t(`setting.${DataSourceKey.JIRA}Description`),
|
|
icon: <SvgIcon name={'data-source/jira'} width={38} />,
|
|
},
|
|
[DataSourceKey.WEBDAV]: {
|
|
name: 'WebDAV',
|
|
description: t(`setting.${DataSourceKey.WEBDAV}Description`),
|
|
icon: <SvgIcon name={'data-source/webdav'} width={38} />,
|
|
},
|
|
[DataSourceKey.DROPBOX]: {
|
|
name: 'Dropbox',
|
|
description: t(`setting.${DataSourceKey.DROPBOX}Description`),
|
|
icon: <SvgIcon name={'data-source/dropbox'} width={38} />,
|
|
},
|
|
[DataSourceKey.BOX]: {
|
|
name: 'Box',
|
|
description: t(`setting.${DataSourceKey.BOX}Description`),
|
|
icon: <SvgIcon name={'data-source/box'} width={38} />,
|
|
},
|
|
[DataSourceKey.AIRTABLE]: {
|
|
name: 'Airtable',
|
|
description: t(`setting.${DataSourceKey.AIRTABLE}Description`),
|
|
icon: <SvgIcon name={'data-source/airtable'} width={38} />,
|
|
},
|
|
[DataSourceKey.DINGTALK_AI_TABLE]: {
|
|
name: 'Dingtalk AI Table',
|
|
description: t(`setting.dingtalkAITableDescription`),
|
|
icon: <SvgIcon name={'data-source/dingtalk-ai-table'} width={38} />,
|
|
},
|
|
[DataSourceKey.GITLAB]: {
|
|
name: 'GitLab',
|
|
description: t(`setting.${DataSourceKey.GITLAB}Description`),
|
|
icon: <SvgIcon name={'data-source/gitlab'} width={38} />,
|
|
},
|
|
[DataSourceKey.ASANA]: {
|
|
name: 'Asana',
|
|
description: t(`setting.${DataSourceKey.ASANA}Description`),
|
|
icon: <SvgIcon name={'data-source/asana'} width={38} />,
|
|
},
|
|
[DataSourceKey.GITHUB]: {
|
|
name: 'GitHub',
|
|
description: t(`setting.${DataSourceKey.GITHUB}Description`),
|
|
icon: (
|
|
<IconFontFill
|
|
// name="a-DiscordIconSVGVectorIcon"
|
|
name="GitHub"
|
|
className="text-text-primary size-6"
|
|
></IconFontFill>
|
|
),
|
|
},
|
|
[DataSourceKey.IMAP]: {
|
|
name: 'IMAP',
|
|
description: t(`setting.${DataSourceKey.IMAP}Description`),
|
|
icon: <Mail className="text-text-primary" size={22} />,
|
|
},
|
|
[DataSourceKey.BITBUCKET]: {
|
|
name: 'Bitbucket',
|
|
description: t(`setting.${DataSourceKey.BITBUCKET}Description`),
|
|
icon: <SvgIcon name={'data-source/bitbucket'} width={38} />,
|
|
},
|
|
[DataSourceKey.ZENDESK]: {
|
|
name: 'Zendesk',
|
|
description: t(`setting.${DataSourceKey.ZENDESK}Description`),
|
|
icon: <SvgIcon name={'data-source/zendesk'} width={38} />,
|
|
},
|
|
[DataSourceKey.SEAFILE]: {
|
|
name: 'SeaFile',
|
|
description: t(`setting.${DataSourceKey.SEAFILE}Description`),
|
|
icon: <SvgIcon name={'data-source/seafile'} width={38} />,
|
|
},
|
|
[DataSourceKey.MYSQL]: {
|
|
name: 'MySQL',
|
|
description: t(`setting.${DataSourceKey.MYSQL}Description`),
|
|
icon: <SvgIcon name={'data-source/mysql'} width={38} />,
|
|
},
|
|
[DataSourceKey.POSTGRESQL]: {
|
|
name: 'PostgreSQL',
|
|
description: t(`setting.${DataSourceKey.POSTGRESQL}Description`),
|
|
icon: <SvgIcon name={'data-source/postgresql'} width={38} />,
|
|
},
|
|
};
|
|
};
|
|
|
|
export const useDataSourceInfo = () => {
|
|
const { t } = useTranslation();
|
|
const [dataSourceInfo, setDataSourceInfo] = useState<IDataSourceInfoMap>(
|
|
generateDataSourceInfo(t) as IDataSourceInfoMap,
|
|
);
|
|
useEffect(() => {
|
|
setDataSourceInfo(generateDataSourceInfo(t));
|
|
}, [t]);
|
|
return { dataSourceInfo };
|
|
};
|
|
|
|
const isPlainObject = (value: unknown): value is DataSourceFormValues =>
|
|
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
|
|
export const mergeDataSourceFormValues = (
|
|
...values: Array<DataSourceFormValues | undefined>
|
|
): DataSourceFormValues =>
|
|
values.reduce<DataSourceFormValues>((result, current) => {
|
|
if (!current) {
|
|
return result;
|
|
}
|
|
|
|
const next = { ...result };
|
|
|
|
Object.entries(current).forEach(([key, value]) => {
|
|
if (isPlainObject(value) && isPlainObject(next[key])) {
|
|
next[key] = mergeDataSourceFormValues(next[key], value);
|
|
} else {
|
|
next[key] = value;
|
|
}
|
|
});
|
|
|
|
return next;
|
|
}, {});
|
|
|
|
export const DataSourceFormBaseFields = [
|
|
{
|
|
id: 'Id',
|
|
name: 'id',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
hidden: true,
|
|
},
|
|
{
|
|
label: 'Name',
|
|
name: 'name',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
tooltip: t('setting.connectorNameTip'),
|
|
},
|
|
{
|
|
label: 'Source',
|
|
name: 'source',
|
|
type: FormFieldType.Select,
|
|
required: true,
|
|
hidden: true,
|
|
options: Object.keys(DataSourceKey).map((item) => ({
|
|
label: item,
|
|
value: DataSourceKey[item as keyof typeof DataSourceKey],
|
|
})),
|
|
},
|
|
];
|
|
|
|
export const getCommonExtraFields = (
|
|
source?: DataSourceKey,
|
|
): FormFieldConfig[] => [
|
|
{
|
|
label: t('setting.syncDeletedFiles'),
|
|
name: 'config.sync_deleted_files',
|
|
type: FormFieldType.Checkbox,
|
|
required: false,
|
|
defaultValue: false,
|
|
shouldRender: () => isDataSourceFeatureVisible(source, 'syncDeletedFiles'),
|
|
},
|
|
];
|
|
|
|
export const getCommonExtraDefaultValues = () => ({
|
|
config: {
|
|
sync_deleted_files: false,
|
|
},
|
|
});
|
|
|
|
export const DataSourceFormFields = {
|
|
[DataSourceKey.RSS]: [
|
|
{
|
|
label: 'Feed URL',
|
|
name: 'config.feed_url',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'https://example.com/feed.xml',
|
|
},
|
|
{
|
|
label: 'Batch Size',
|
|
name: 'config.batch_size',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
validation: {
|
|
min: 1,
|
|
message: 'Batch Size must be at least 1',
|
|
},
|
|
},
|
|
],
|
|
[DataSourceKey.GOOGLE_CLOUD_STORAGE]: [
|
|
{
|
|
label: 'GCS Access Key ID',
|
|
name: 'config.credentials.access_key_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'GCS Secret Access Key',
|
|
name: 'config.credentials.secret_access_key',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Bucket Name',
|
|
name: 'config.bucket_name',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.OCI_STORAGE]: [
|
|
{
|
|
label: 'OCI Namespace',
|
|
name: 'config.credentials.namespace',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'OCI Region',
|
|
name: 'config.credentials.region',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'OCI Access Key ID',
|
|
name: 'config.credentials.access_key_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'OCI Secret Access Key',
|
|
name: 'config.credentials.secret_access_key',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Bucket Name',
|
|
name: 'config.bucket_name',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.R2]: [
|
|
{
|
|
label: 'R2 Account ID',
|
|
name: 'config.credentials.account_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'R2 Access Key ID',
|
|
name: 'config.credentials.r2_access_key_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'R2 Secret Access Key',
|
|
name: 'config.credentials.r2_secret_access_key',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Bucket Name',
|
|
name: 'config.bucket_name',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.S3]: S3Constant(t),
|
|
[DataSourceKey.NOTION]: [
|
|
{
|
|
label: 'Notion Integration Token',
|
|
name: 'config.credentials.notion_integration_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Root Page Id',
|
|
name: 'config.root_page_id',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
},
|
|
],
|
|
[DataSourceKey.DISCORD]: [
|
|
{
|
|
label: 'Discord Bot Token',
|
|
name: 'config.credentials.discord_bot_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Server IDs',
|
|
name: 'config.server_ids',
|
|
type: FormFieldType.Tag,
|
|
required: false,
|
|
},
|
|
{
|
|
label: 'Channels',
|
|
name: 'config.channels',
|
|
type: FormFieldType.Tag,
|
|
required: false,
|
|
},
|
|
],
|
|
|
|
[DataSourceKey.CONFLUENCE]: confluenceConstant(t),
|
|
[DataSourceKey.GOOGLE_DRIVE]: [
|
|
{
|
|
label: 'Primary Admin Email',
|
|
name: 'config.credentials.google_primary_admin',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'admin@example.com',
|
|
tooltip: t('setting.google_drivePrimaryAdminTip'),
|
|
},
|
|
{
|
|
label: 'OAuth Token JSON',
|
|
name: 'config.credentials.google_tokens',
|
|
type: FormFieldType.Textarea,
|
|
required: true,
|
|
render: (fieldProps: any) => (
|
|
<GoogleDriveTokenField
|
|
value={fieldProps.value}
|
|
onChange={fieldProps.onChange}
|
|
/>
|
|
),
|
|
tooltip: t('setting.google_driveTokenTip'),
|
|
},
|
|
{
|
|
label: 'My Drive Emails',
|
|
name: 'config.my_drive_emails',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'user1@example.com,user2@example.com',
|
|
tooltip: t('setting.google_driveMyDriveEmailsTip'),
|
|
},
|
|
{
|
|
label: 'Shared Folder URLs',
|
|
name: 'config.shared_folder_urls',
|
|
type: FormFieldType.Textarea,
|
|
required: true,
|
|
placeholder:
|
|
'https://drive.google.com/drive/folders/XXXXX,https://drive.google.com/drive/folders/YYYYY',
|
|
tooltip: t('setting.google_driveSharedFoldersTip'),
|
|
},
|
|
// The fields below are intentionally disabled for now. Uncomment them when we
|
|
// reintroduce shared drive controls or advanced impersonation options.
|
|
// {
|
|
// label: 'Shared Drive URLs',
|
|
// name: 'config.shared_drive_urls',
|
|
// type: FormFieldType.Text,
|
|
// required: false,
|
|
// placeholder:
|
|
// 'Optional: comma-separated shared drive links if you want to include them.',
|
|
// },
|
|
// {
|
|
// label: 'Specific User Emails',
|
|
// name: 'config.specific_user_emails',
|
|
// type: FormFieldType.Text,
|
|
// required: false,
|
|
// placeholder:
|
|
// 'Optional: comma-separated list of users to impersonate (overrides defaults).',
|
|
// },
|
|
// {
|
|
// label: 'Include My Drive',
|
|
// name: 'config.include_my_drives',
|
|
// type: FormFieldType.Checkbox,
|
|
// required: false,
|
|
// defaultValue: true,
|
|
// },
|
|
// {
|
|
// label: 'Include Shared Drives',
|
|
// name: 'config.include_shared_drives',
|
|
// type: FormFieldType.Checkbox,
|
|
// required: false,
|
|
// defaultValue: false,
|
|
// },
|
|
// {
|
|
// label: 'Include “Shared with me”',
|
|
// name: 'config.include_files_shared_with_me',
|
|
// type: FormFieldType.Checkbox,
|
|
// required: false,
|
|
// defaultValue: false,
|
|
// },
|
|
// {
|
|
// label: 'Allow Images',
|
|
// name: 'config.allow_images',
|
|
// type: FormFieldType.Checkbox,
|
|
// required: false,
|
|
// defaultValue: false,
|
|
// },
|
|
{
|
|
label: '',
|
|
name: 'config.credentials.authentication_method',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
hidden: true,
|
|
defaultValue: 'uploaded',
|
|
},
|
|
],
|
|
[DataSourceKey.GMAIL]: [
|
|
{
|
|
label: 'Primary Admin Email',
|
|
name: 'config.credentials.google_primary_admin',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'admin@example.com',
|
|
tooltip: t('setting.gmailPrimaryAdminTip'),
|
|
},
|
|
{
|
|
label: 'OAuth Token JSON',
|
|
name: 'config.credentials.google_tokens',
|
|
type: FormFieldType.Textarea,
|
|
required: true,
|
|
render: (fieldProps: any) => (
|
|
<GmailTokenField
|
|
value={fieldProps.value}
|
|
onChange={fieldProps.onChange}
|
|
/>
|
|
),
|
|
tooltip: t('setting.gmailTokenTip'),
|
|
},
|
|
{
|
|
label: '',
|
|
name: 'config.credentials.authentication_method',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
hidden: true,
|
|
defaultValue: 'uploaded',
|
|
},
|
|
],
|
|
[DataSourceKey.MOODLE]: [
|
|
{
|
|
label: 'Moodle URL',
|
|
name: 'config.moodle_url',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'https://moodle.example.com',
|
|
},
|
|
{
|
|
label: 'API Token',
|
|
name: 'config.credentials.moodle_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.TEAMS]: [
|
|
{
|
|
label: 'Tenant ID',
|
|
name: 'config.credentials.tenant_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
tooltip: t('setting.teamsTenantIdTip'),
|
|
},
|
|
{
|
|
label: 'Client ID',
|
|
name: 'config.credentials.client_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Client Secret',
|
|
name: 'config.credentials.client_secret',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.SLACK]: [
|
|
{
|
|
label: 'Slack Bot Token',
|
|
name: 'config.credentials.slack_bot_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
tooltip: t('setting.slackBotTokenTip'),
|
|
},
|
|
{
|
|
label: 'Channels',
|
|
name: 'config.channels',
|
|
type: FormFieldType.Tag,
|
|
required: false,
|
|
tooltip: t('setting.slackChannelsTip'),
|
|
},
|
|
],
|
|
[DataSourceKey.SHAREPOINT]: [
|
|
{
|
|
label: 'Site URL',
|
|
name: 'config.credentials.site_url',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'https://contoso.sharepoint.com/sites/MySite',
|
|
tooltip: t('setting.sharepointSiteUrlTip'),
|
|
},
|
|
{
|
|
label: 'Tenant ID',
|
|
name: 'config.credentials.tenant_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Client ID',
|
|
name: 'config.credentials.client_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Client Secret',
|
|
name: 'config.credentials.client_secret',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.JIRA]: jiraConstant(t),
|
|
[DataSourceKey.WEBDAV]: [
|
|
{
|
|
label: 'WebDAV Server URL',
|
|
name: 'config.base_url',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'https://webdav.example.com',
|
|
},
|
|
{
|
|
label: 'Username',
|
|
name: 'config.credentials.username',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Password',
|
|
name: 'config.credentials.password',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Remote Path',
|
|
name: 'config.remote_path',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: '/',
|
|
tooltip: t('setting.webdavRemotePathTip'),
|
|
},
|
|
],
|
|
[DataSourceKey.DROPBOX]: [
|
|
{
|
|
label: 'Access Token',
|
|
name: 'config.credentials.dropbox_access_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
tooltip: t('setting.dropboxAccessTokenTip'),
|
|
},
|
|
{
|
|
label: 'Batch Size',
|
|
name: 'config.batch_size',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
placeholder: 'Defaults to 2',
|
|
},
|
|
],
|
|
[DataSourceKey.BOX]: [
|
|
{
|
|
label: 'Box OAuth JSON',
|
|
name: 'config.credentials.box_tokens',
|
|
type: FormFieldType.Textarea,
|
|
required: true,
|
|
render: (fieldProps: any) => (
|
|
<BoxTokenField
|
|
value={fieldProps.value}
|
|
onChange={fieldProps.onChange}
|
|
placeholder='{ "client_id": "...", "client_secret": "...", "redirect_uri": "..." }'
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
label: 'Folder ID',
|
|
name: 'config.folder_id',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'Defaults root',
|
|
},
|
|
],
|
|
[DataSourceKey.AIRTABLE]: [
|
|
{
|
|
label: 'Access Token',
|
|
name: 'config.credentials.airtable_access_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Base ID',
|
|
name: 'config.base_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Table Name OR ID',
|
|
name: 'config.table_name_or_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.DINGTALK_AI_TABLE]: [
|
|
{
|
|
label: 'Access Token',
|
|
name: 'config.credentials.access_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Base ID',
|
|
name: 'config.table_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Operator ID',
|
|
name: 'config.operator_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
],
|
|
[DataSourceKey.GITLAB]: [
|
|
{
|
|
label: 'Project Owner',
|
|
name: 'config.project_owner',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Project Name',
|
|
name: 'config.project_name',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'GitLab Personal Access Token',
|
|
name: 'config.credentials.gitlab_access_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'GitLab URL',
|
|
name: 'config.gitlab_url',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'https://gitlab.com',
|
|
},
|
|
{
|
|
label: 'include Merge Requests',
|
|
name: 'config.include_mrs',
|
|
type: FormFieldType.Checkbox,
|
|
required: false,
|
|
defaultValue: true,
|
|
},
|
|
{
|
|
label: 'include Issues',
|
|
name: 'config.include_issues',
|
|
type: FormFieldType.Checkbox,
|
|
required: false,
|
|
defaultValue: true,
|
|
},
|
|
{
|
|
label: 'include Code Files',
|
|
name: 'config.include_code_files',
|
|
type: FormFieldType.Checkbox,
|
|
required: false,
|
|
defaultValue: true,
|
|
},
|
|
],
|
|
[DataSourceKey.ASANA]: [
|
|
{
|
|
label: 'API Token',
|
|
name: 'config.credentials.asana_api_token_secret',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Workspace ID',
|
|
name: 'config.asana_workspace_id',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Project IDs',
|
|
name: 'config.asana_project_ids',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
},
|
|
{
|
|
label: 'Team ID',
|
|
name: 'config.asana_team_id',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
},
|
|
],
|
|
[DataSourceKey.GITHUB]: [
|
|
{
|
|
label: 'Repository Owner',
|
|
name: 'config.repository_owner',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Repository Name',
|
|
name: 'config.repository_name',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'GitHub Access Token',
|
|
name: 'config.credentials.github_access_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Include Pull Requests',
|
|
name: 'config.include_pull_requests',
|
|
type: FormFieldType.Checkbox,
|
|
required: false,
|
|
defaultValue: true,
|
|
},
|
|
{
|
|
label: 'Include Issues',
|
|
name: 'config.include_issues',
|
|
type: FormFieldType.Checkbox,
|
|
required: false,
|
|
defaultValue: true,
|
|
},
|
|
],
|
|
[DataSourceKey.IMAP]: [
|
|
{
|
|
label: 'Username',
|
|
name: 'config.credentials.imap_username',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Password',
|
|
name: 'config.credentials.imap_password',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Host',
|
|
name: 'config.imap_host',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Port',
|
|
name: 'config.imap_port',
|
|
type: FormFieldType.Number,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Mailboxes',
|
|
name: 'config.imap_mailbox',
|
|
type: FormFieldType.Tag,
|
|
required: false,
|
|
},
|
|
{
|
|
label: 'Poll Range',
|
|
name: 'config.poll_range',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
},
|
|
],
|
|
[DataSourceKey.BITBUCKET]: bitbucketConstant(t),
|
|
[DataSourceKey.ZENDESK]: [
|
|
{
|
|
label: 'Zendesk Domain',
|
|
name: 'config.credentials.zendesk_subdomain',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Zendesk Email',
|
|
name: 'config.credentials.zendesk_email',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Zendesk Token',
|
|
name: 'config.credentials.zendesk_token',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Content',
|
|
name: 'config.zendesk_content_type',
|
|
type: FormFieldType.Segmented,
|
|
required: true,
|
|
options: [
|
|
{ label: 'Articles', value: 'articles' },
|
|
{ label: 'Tickets', value: 'tickets' },
|
|
],
|
|
},
|
|
],
|
|
[DataSourceKey.SEAFILE]: seafileConstant(t),
|
|
[DataSourceKey.MYSQL]: [
|
|
{
|
|
label: 'Host',
|
|
name: 'config.host',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'localhost',
|
|
},
|
|
{
|
|
label: 'Port',
|
|
name: 'config.port',
|
|
type: FormFieldType.Number,
|
|
required: true,
|
|
placeholder: '3306',
|
|
},
|
|
{
|
|
label: 'Database',
|
|
name: 'config.database',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Username',
|
|
name: 'config.credentials.username',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Password',
|
|
name: 'config.credentials.password',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'SQL Query',
|
|
name: 'config.query',
|
|
type: FormFieldType.Textarea,
|
|
required: false,
|
|
placeholder: 'Leave empty to load all tables',
|
|
tooltip: t('setting.mysqlQueryTip'),
|
|
},
|
|
{
|
|
label: 'Content Columns',
|
|
name: 'config.content_columns',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'title,description,content',
|
|
tooltip: t('setting.mysqlContentColumnsTip'),
|
|
},
|
|
{
|
|
label: 'Metadata Columns',
|
|
name: 'config.metadata_columns',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'id,category,status',
|
|
tooltip: t('setting.mysqlMetadataColumnsTip'),
|
|
},
|
|
{
|
|
label: 'ID Column',
|
|
name: 'config.id_column',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'id',
|
|
tooltip: t('setting.mysqlIdColumnTip'),
|
|
},
|
|
{
|
|
label: 'Timestamp Column',
|
|
name: 'config.timestamp_column',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'updated_at',
|
|
tooltip: t('setting.mysqlTimestampColumnTip'),
|
|
},
|
|
],
|
|
[DataSourceKey.POSTGRESQL]: [
|
|
{
|
|
label: 'Host',
|
|
name: 'config.host',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'localhost',
|
|
},
|
|
{
|
|
label: 'Port',
|
|
name: 'config.port',
|
|
type: FormFieldType.Number,
|
|
required: true,
|
|
placeholder: '5432',
|
|
},
|
|
{
|
|
label: 'Database',
|
|
name: 'config.database',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Username',
|
|
name: 'config.credentials.username',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'Password',
|
|
name: 'config.credentials.password',
|
|
type: FormFieldType.Password,
|
|
required: true,
|
|
},
|
|
{
|
|
label: 'SQL Query',
|
|
name: 'config.query',
|
|
type: FormFieldType.Textarea,
|
|
required: false,
|
|
placeholder: 'Leave empty to load all tables',
|
|
tooltip: t('setting.postgresqlQueryTip'),
|
|
},
|
|
{
|
|
label: 'Content Columns',
|
|
name: 'config.content_columns',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'title,description,content',
|
|
tooltip: t('setting.postgresqlContentColumnsTip'),
|
|
},
|
|
{
|
|
label: 'Metadata Columns',
|
|
name: 'config.metadata_columns',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'id,category,status',
|
|
tooltip: t('setting.postgresqlMetadataColumnsTip'),
|
|
},
|
|
{
|
|
label: 'ID Column',
|
|
name: 'config.id_column',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'id',
|
|
tooltip: t('setting.postgresqlIdColumnTip'),
|
|
},
|
|
{
|
|
label: 'Timestamp Column',
|
|
name: 'config.timestamp_column',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'updated_at',
|
|
tooltip: t('setting.postgresqlTimestampColumnTip'),
|
|
},
|
|
],
|
|
[DataSourceKey.REST_API]: [
|
|
// ── Essential fields ──────────────────────────────────────────────
|
|
{
|
|
label: 'Base URL',
|
|
name: 'config.url',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'https://api.example.com/v1/resources',
|
|
},
|
|
{
|
|
label: 'HTTP Method',
|
|
name: 'config.method',
|
|
type: FormFieldType.Select,
|
|
required: true,
|
|
options: [
|
|
{ label: 'GET', value: 'GET' },
|
|
{ label: 'POST', value: 'POST' },
|
|
],
|
|
defaultValue: 'GET',
|
|
},
|
|
{
|
|
label: 'Query Parameters',
|
|
name: 'config.query_params',
|
|
type: FormFieldType.Textarea,
|
|
required: false,
|
|
placeholder: `key=value\none_per_line=true`,
|
|
tooltip: t('setting.restApiQueryParamsTip'),
|
|
},
|
|
{
|
|
label: 'Items Path',
|
|
name: 'config.items_path',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: '$.items',
|
|
tooltip: t('setting.restApiItemsPathTip'),
|
|
},
|
|
{
|
|
label: 'ID Field',
|
|
name: 'config.id_field',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'id',
|
|
tooltip: t('setting.restApiIdFieldTip'),
|
|
},
|
|
{
|
|
label: 'Auth Type',
|
|
name: 'config.auth_type',
|
|
type: FormFieldType.Select,
|
|
required: true,
|
|
options: [
|
|
{ label: 'None', value: 'none' },
|
|
{ label: 'API Key (Header)', value: 'api_key_header' },
|
|
{ label: 'Bearer Token', value: 'bearer' },
|
|
{ label: 'Basic Auth', value: 'basic' },
|
|
],
|
|
defaultValue: 'none',
|
|
},
|
|
{
|
|
label: 'API Key Header Name',
|
|
name: 'config.auth_config.header_name',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'X-API-Key',
|
|
shouldRender: (values: any) =>
|
|
values?.config?.auth_type === 'api_key_header',
|
|
customValidate: (val: string, values: any) => {
|
|
if (
|
|
values?.config?.auth_type === 'api_key_header' &&
|
|
!(val != null && String(val).trim())
|
|
) {
|
|
return t('setting.restApiValidationApiKeyHeaderNameRequired');
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
label: 'API Key Value',
|
|
name: 'config.credentials.api_key',
|
|
type: FormFieldType.Password,
|
|
required: false,
|
|
shouldRender: (values: any) =>
|
|
values?.config?.auth_type === 'api_key_header',
|
|
customValidate: (val: string, values: any) => {
|
|
if (values?.config?.auth_type === 'api_key_header' && !val) {
|
|
return t('setting.restApiValidationApiKeyRequired');
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
label: 'Bearer Token',
|
|
name: 'config.credentials.token',
|
|
type: FormFieldType.Password,
|
|
required: false,
|
|
shouldRender: (values: any) => values?.config?.auth_type === 'bearer',
|
|
customValidate: (val: string, values: any) => {
|
|
if (values?.config?.auth_type === 'bearer' && !val) {
|
|
return t('setting.restApiValidationBearerTokenRequired');
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
label: 'Username',
|
|
name: 'config.credentials.username',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
shouldRender: (values: any) => values?.config?.auth_type === 'basic',
|
|
customValidate: (val: string, values: any) => {
|
|
if (
|
|
values?.config?.auth_type === 'basic' &&
|
|
!(val != null && String(val).trim())
|
|
) {
|
|
return t('setting.restApiValidationBasicUsernameRequired');
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
label: 'Password',
|
|
name: 'config.credentials.password',
|
|
type: FormFieldType.Password,
|
|
required: false,
|
|
shouldRender: (values: any) => values?.config?.auth_type === 'basic',
|
|
customValidate: (val: string, values: any) => {
|
|
if (values?.config?.auth_type === 'basic' && !val) {
|
|
return t('setting.restApiValidationBasicPasswordRequired');
|
|
}
|
|
return true;
|
|
},
|
|
},
|
|
{
|
|
label: 'Content Fields',
|
|
name: 'config.content_fields',
|
|
type: FormFieldType.Text,
|
|
required: true,
|
|
placeholder: 'title,body',
|
|
tooltip: t('setting.restApiContentFieldsTip'),
|
|
},
|
|
{
|
|
label: 'Metadata Fields',
|
|
name: 'config.metadata_fields',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'author,category',
|
|
tooltip: t('setting.restApiMetadataFieldsTip'),
|
|
},
|
|
{
|
|
label: 'Pagination Type',
|
|
name: 'config.pagination_type',
|
|
type: FormFieldType.Select,
|
|
required: true,
|
|
options: [
|
|
{ label: 'None', value: 'none' },
|
|
{ label: 'Page', value: 'page' },
|
|
{ label: 'Offset', value: 'offset' },
|
|
{ label: 'Cursor', value: 'cursor' },
|
|
],
|
|
defaultValue: 'none',
|
|
},
|
|
{
|
|
label: 'Start Page',
|
|
name: 'config.pagination_config.start_page',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
defaultValue: 1,
|
|
shouldRender: (values: any) => values?.config?.pagination_type === 'page',
|
|
},
|
|
{
|
|
label: 'Offset Param',
|
|
name: 'config.pagination_config.offset_param',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
defaultValue: 'offset',
|
|
shouldRender: (values: any) =>
|
|
values?.config?.pagination_type === 'offset',
|
|
},
|
|
{
|
|
label: 'Start Offset',
|
|
name: 'config.pagination_config.start_offset',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
defaultValue: 0,
|
|
shouldRender: (values: any) =>
|
|
values?.config?.pagination_type === 'offset',
|
|
},
|
|
{
|
|
label: 'Cursor Param',
|
|
name: 'config.pagination_config.cursor_param',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
defaultValue: 'cursor',
|
|
shouldRender: (values: any) =>
|
|
values?.config?.pagination_type === 'cursor',
|
|
},
|
|
{
|
|
label: 'Next Cursor JSONPath',
|
|
name: 'config.pagination_config.next_cursor_path',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: '$.next_cursor',
|
|
shouldRender: (values: any) =>
|
|
values?.config?.pagination_type === 'cursor',
|
|
tooltip: t('setting.restApiNextCursorPathTip'),
|
|
},
|
|
// ── Advanced settings toggle ──────────────────────────────────────
|
|
{
|
|
label: 'Advanced Settings',
|
|
name: 'config.show_advanced',
|
|
type: FormFieldType.Switch,
|
|
required: false,
|
|
defaultValue: false,
|
|
},
|
|
// ── Advanced fields (hidden until toggled) ────────────────────────
|
|
{
|
|
label: 'Custom Headers (JSON)',
|
|
name: 'config.headers',
|
|
type: FormFieldType.Textarea,
|
|
required: false,
|
|
placeholder: `{"X-Custom-Header": "value"}`,
|
|
tooltip: t('setting.restApiHeadersTip'),
|
|
shouldRender: (values: any) => !!values?.config?.show_advanced,
|
|
},
|
|
{
|
|
label: 'Limit Param',
|
|
name: 'config.pagination_config.limit_param',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'limit (leave empty if already in Query Parameters)',
|
|
shouldRender: (values: any) =>
|
|
!!values?.config?.show_advanced &&
|
|
values?.config?.pagination_type === 'offset',
|
|
},
|
|
{
|
|
label: 'Initial Cursor',
|
|
name: 'config.pagination_config.initial_cursor',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
shouldRender: (values: any) =>
|
|
!!values?.config?.show_advanced &&
|
|
values?.config?.pagination_type === 'cursor',
|
|
},
|
|
{
|
|
label: 'Max Pages',
|
|
name: 'config.max_pages',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
defaultValue: 1000,
|
|
shouldRender: (values: any) => !!values?.config?.show_advanced,
|
|
},
|
|
{
|
|
label: 'Request Delay (seconds)',
|
|
name: 'config.request_delay',
|
|
type: FormFieldType.Number,
|
|
required: false,
|
|
defaultValue: 0.5,
|
|
placeholder: '0.5',
|
|
tooltip: t('setting.restApiRequestDelayTip'),
|
|
shouldRender: (values: any) => !!values?.config?.show_advanced,
|
|
},
|
|
{
|
|
label: 'Poll Timestamp Field',
|
|
name: 'config.poll_timestamp_field',
|
|
type: FormFieldType.Text,
|
|
required: false,
|
|
placeholder: 'updated_at',
|
|
tooltip: t('setting.restApiPollTimestampFieldTip'),
|
|
shouldRender: (values: any) => !!values?.config?.show_advanced,
|
|
},
|
|
{
|
|
label: 'Request Body (POST) JSON',
|
|
name: 'config.request_body',
|
|
type: FormFieldType.Textarea,
|
|
required: false,
|
|
placeholder: `{"status": "published"}`,
|
|
tooltip: t('setting.restApiRequestBodyTip'),
|
|
shouldRender: (values: any) =>
|
|
!!values?.config?.show_advanced && values?.config?.method === 'POST',
|
|
},
|
|
],
|
|
};
|
|
|
|
export const DataSourceFormDefaultValues = {
|
|
[DataSourceKey.RSS]: {
|
|
name: '',
|
|
source: DataSourceKey.RSS,
|
|
config: {
|
|
feed_url: '',
|
|
batch_size: 2,
|
|
},
|
|
},
|
|
[DataSourceKey.S3]: {
|
|
name: '',
|
|
source: DataSourceKey.S3,
|
|
config: {
|
|
bucket_name: '',
|
|
bucket_type: 's3',
|
|
prefix: '',
|
|
credentials: {
|
|
aws_access_key_id: '',
|
|
aws_secret_access_key: '',
|
|
region: '',
|
|
authentication_method: 'access_key',
|
|
aws_role_arn: '',
|
|
endpoint_url: '',
|
|
addressing_style: 'virtual',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.R2]: {
|
|
name: '',
|
|
source: DataSourceKey.R2,
|
|
config: {
|
|
bucket_name: '',
|
|
credentials: {
|
|
account_id: '',
|
|
r2_access_key_id: '',
|
|
r2_secret_access_key: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.NOTION]: {
|
|
name: '',
|
|
source: DataSourceKey.NOTION,
|
|
config: {
|
|
root_page_id: '',
|
|
credentials: {
|
|
notion_integration_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.DISCORD]: {
|
|
name: '',
|
|
source: DataSourceKey.DISCORD,
|
|
config: {
|
|
server_ids: [],
|
|
channels: [],
|
|
credentials: {
|
|
discord_bot_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.CONFLUENCE]: {
|
|
name: '',
|
|
source: DataSourceKey.CONFLUENCE,
|
|
config: {
|
|
wiki_base: '',
|
|
is_cloud: true,
|
|
space: '',
|
|
page_id: '',
|
|
credentials: {
|
|
confluence_username: '',
|
|
confluence_access_token: '',
|
|
},
|
|
index_mode: 'everything',
|
|
},
|
|
},
|
|
[DataSourceKey.GOOGLE_DRIVE]: {
|
|
name: '',
|
|
source: DataSourceKey.GOOGLE_DRIVE,
|
|
config: {
|
|
include_shared_drives: false,
|
|
include_my_drives: true,
|
|
include_files_shared_with_me: false,
|
|
allow_images: false,
|
|
shared_drive_urls: '',
|
|
shared_folder_urls: '',
|
|
my_drive_emails: '',
|
|
specific_user_emails: '',
|
|
credentials: {
|
|
google_primary_admin: '',
|
|
google_tokens: '',
|
|
authentication_method: 'uploaded',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.GMAIL]: {
|
|
name: '',
|
|
source: DataSourceKey.GMAIL,
|
|
config: {
|
|
credentials: {
|
|
google_primary_admin: '',
|
|
google_tokens: '',
|
|
authentication_method: 'uploaded',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.GOOGLE_CLOUD_STORAGE]: {
|
|
name: '',
|
|
source: DataSourceKey.GOOGLE_CLOUD_STORAGE,
|
|
config: {
|
|
bucket_name: '',
|
|
credentials: {
|
|
access_key_id: '',
|
|
secret_access_key: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.OCI_STORAGE]: {
|
|
name: '',
|
|
source: DataSourceKey.OCI_STORAGE,
|
|
config: {
|
|
bucket_name: '',
|
|
credentials: {
|
|
namespace: '',
|
|
region: '',
|
|
access_key_id: '',
|
|
secret_access_key: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.MOODLE]: {
|
|
name: '',
|
|
source: DataSourceKey.MOODLE,
|
|
config: {
|
|
moodle_url: '',
|
|
credentials: {
|
|
moodle_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.TEAMS]: {
|
|
name: '',
|
|
source: DataSourceKey.TEAMS,
|
|
config: {
|
|
credentials: {
|
|
tenant_id: '',
|
|
client_id: '',
|
|
client_secret: '',
|
|
}
|
|
}
|
|
},
|
|
[DataSourceKey.SLACK]: {
|
|
name: '',
|
|
source: DataSourceKey.SLACK,
|
|
config: {
|
|
channels: [],
|
|
credentials: {
|
|
slack_bot_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.SHAREPOINT]: {
|
|
name: '',
|
|
source: DataSourceKey.SHAREPOINT,
|
|
config: {
|
|
credentials: {
|
|
site_url: '',
|
|
tenant_id: '',
|
|
client_id: '',
|
|
client_secret: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.JIRA]: {
|
|
name: '',
|
|
source: DataSourceKey.JIRA,
|
|
config: {
|
|
is_cloud: true,
|
|
base_url: '',
|
|
project_key: '',
|
|
jql_query: '',
|
|
batch_size: 2,
|
|
include_comments: true,
|
|
include_attachments: false,
|
|
attachment_size_limit: 10 * 1024 * 1024,
|
|
labels_to_skip: [],
|
|
comment_email_blacklist: [],
|
|
scoped_token: false,
|
|
credentials: {
|
|
jira_user_email: '',
|
|
jira_username: '',
|
|
jira_api_token: '',
|
|
jira_password: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.WEBDAV]: {
|
|
name: '',
|
|
source: DataSourceKey.WEBDAV,
|
|
config: {
|
|
base_url: '',
|
|
remote_path: '/',
|
|
credentials: {
|
|
username: '',
|
|
password: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.DROPBOX]: {
|
|
name: '',
|
|
source: DataSourceKey.DROPBOX,
|
|
config: {
|
|
batch_size: 2,
|
|
credentials: {
|
|
dropbox_access_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.BOX]: {
|
|
name: '',
|
|
source: DataSourceKey.BOX,
|
|
config: {
|
|
name: '',
|
|
folder_id: '0',
|
|
credentials: {
|
|
box_tokens: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.AIRTABLE]: {
|
|
name: '',
|
|
source: DataSourceKey.AIRTABLE,
|
|
config: {
|
|
name: '',
|
|
base_id: '',
|
|
table_name_or_id: '',
|
|
credentials: {
|
|
airtable_access_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.DINGTALK_AI_TABLE]: {
|
|
name: '',
|
|
source: DataSourceKey.DINGTALK_AI_TABLE,
|
|
config: {
|
|
table_id: '',
|
|
operator_id: '',
|
|
credentials: {
|
|
access_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.GITLAB]: {
|
|
name: '',
|
|
source: DataSourceKey.GITLAB,
|
|
config: {
|
|
project_owner: '',
|
|
project_name: '',
|
|
gitlab_url: 'https://gitlab.com',
|
|
include_mrs: true,
|
|
include_issues: true,
|
|
include_code_files: true,
|
|
credentials: {
|
|
gitlab_access_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.ASANA]: {
|
|
name: '',
|
|
source: DataSourceKey.ASANA,
|
|
config: {
|
|
name: '',
|
|
asana_workspace_id: '',
|
|
asana_project_ids: '',
|
|
asana_team_id: '',
|
|
credentials: {
|
|
asana_api_token_secret: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.GITHUB]: {
|
|
name: '',
|
|
source: DataSourceKey.GITHUB,
|
|
config: {
|
|
repository_owner: '',
|
|
repository_name: '',
|
|
include_pull_requests: true,
|
|
include_issues: true,
|
|
credentials: {
|
|
github_access_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.IMAP]: {
|
|
name: '',
|
|
source: DataSourceKey.IMAP,
|
|
config: {
|
|
name: '',
|
|
imap_host: '',
|
|
imap_port: 993,
|
|
imap_mailbox: [],
|
|
poll_range: 30,
|
|
credentials: {
|
|
imap_username: '',
|
|
imap_password: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.BITBUCKET]: {
|
|
name: '',
|
|
source: DataSourceKey.BITBUCKET,
|
|
config: {
|
|
workspace: '',
|
|
index_mode: 'workspace',
|
|
repository_slugs: '',
|
|
projects: '',
|
|
},
|
|
credentials: {
|
|
bitbucket_api_token: '',
|
|
},
|
|
},
|
|
[DataSourceKey.ZENDESK]: {
|
|
name: '',
|
|
source: DataSourceKey.ZENDESK,
|
|
config: {
|
|
name: '',
|
|
zendesk_content_type: 'articles',
|
|
credentials: {
|
|
zendesk_subdomain: '',
|
|
zendesk_email: '',
|
|
zendesk_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.SEAFILE]: {
|
|
name: '',
|
|
source: DataSourceKey.SEAFILE,
|
|
config: {
|
|
seafile_url: '',
|
|
sync_scope: 'account',
|
|
repo_id: '',
|
|
sync_path: '',
|
|
include_shared: true,
|
|
batch_size: 100,
|
|
credentials: {
|
|
seafile_token: '',
|
|
repo_token: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.MYSQL]: {
|
|
name: '',
|
|
source: DataSourceKey.MYSQL,
|
|
config: {
|
|
host: 'localhost',
|
|
port: 3306,
|
|
database: '',
|
|
query: '',
|
|
content_columns: '',
|
|
metadata_columns: '',
|
|
id_column: '',
|
|
timestamp_column: '',
|
|
credentials: {
|
|
username: '',
|
|
password: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.POSTGRESQL]: {
|
|
name: '',
|
|
source: DataSourceKey.POSTGRESQL,
|
|
config: {
|
|
host: 'localhost',
|
|
port: 5432,
|
|
database: '',
|
|
query: '',
|
|
content_columns: '',
|
|
metadata_columns: '',
|
|
id_column: '',
|
|
timestamp_column: '',
|
|
credentials: {
|
|
username: '',
|
|
password: '',
|
|
},
|
|
},
|
|
},
|
|
[DataSourceKey.REST_API]: {
|
|
name: '',
|
|
source: DataSourceKey.REST_API,
|
|
config: {
|
|
url: '',
|
|
method: 'GET',
|
|
query_params: '',
|
|
headers: '',
|
|
auth_type: 'none',
|
|
auth_config: {},
|
|
items_path: '',
|
|
id_field: '',
|
|
content_fields: '',
|
|
metadata_fields: '',
|
|
pagination_type: 'none',
|
|
pagination_config: {},
|
|
poll_timestamp_field: '',
|
|
request_body: '',
|
|
max_pages: 1000,
|
|
request_delay: 0.5,
|
|
show_advanced: false,
|
|
credentials: {
|
|
api_key: '',
|
|
token: '',
|
|
username: '',
|
|
password: '',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export const getDataSourceFieldsWithExtras = (
|
|
source?: DataSourceKey,
|
|
): FormFieldConfig[] => {
|
|
if (!source) {
|
|
return [];
|
|
}
|
|
|
|
const sourceFields =
|
|
DataSourceFormFields[source as keyof typeof DataSourceFormFields] || [];
|
|
const extraFields = getCommonExtraFields(source);
|
|
|
|
if (source !== DataSourceKey.JIRA) {
|
|
return [...sourceFields, ...extraFields];
|
|
}
|
|
|
|
const modeFieldIndex = sourceFields.findIndex(
|
|
(field) => field.name === 'config.is_cloud',
|
|
);
|
|
if (modeFieldIndex < 0) {
|
|
return [...sourceFields, ...extraFields];
|
|
}
|
|
|
|
const sharedFields = sourceFields.slice(0, modeFieldIndex);
|
|
const modeFields = sourceFields.slice(modeFieldIndex);
|
|
|
|
const sharedCheckboxFieldIndex = sharedFields.findIndex(
|
|
(field) => field.type === FormFieldType.Checkbox,
|
|
);
|
|
|
|
if (sharedCheckboxFieldIndex < 0) {
|
|
return [...sharedFields, ...extraFields, ...modeFields];
|
|
}
|
|
|
|
return [
|
|
...sharedFields.slice(0, sharedCheckboxFieldIndex),
|
|
...sharedFields.slice(sharedCheckboxFieldIndex),
|
|
...extraFields,
|
|
...modeFields,
|
|
];
|
|
};
|