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; export const DataSourceFeatureVisibilityMap: Partial< Record > = { [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: , }, [DataSourceKey.GOOGLE_CLOUD_STORAGE]: { name: 'Google Cloud Storage', description: t( `setting.${DataSourceKey.GOOGLE_CLOUD_STORAGE}Description`, ), icon: , }, [DataSourceKey.OCI_STORAGE]: { name: 'Oracle Storage', description: t(`setting.${DataSourceKey.OCI_STORAGE}Description`), icon: , }, [DataSourceKey.R2]: { name: 'R2', description: t(`setting.${DataSourceKey.R2}Description`), icon: , }, [DataSourceKey.S3]: { name: 'S3', description: t(`setting.${DataSourceKey.S3}Description`), icon: , }, [DataSourceKey.NOTION]: { name: 'Notion', description: t(`setting.${DataSourceKey.NOTION}Description`), icon: , }, [DataSourceKey.DISCORD]: { name: 'Discord', description: t(`setting.${DataSourceKey.DISCORD}Description`), icon: , }, [DataSourceKey.CONFLUENCE]: { name: 'Confluence', description: t(`setting.${DataSourceKey.CONFLUENCE}Description`), icon: , }, [DataSourceKey.GOOGLE_DRIVE]: { name: 'Google Drive', description: t(`setting.${DataSourceKey.GOOGLE_DRIVE}Description`), icon: , }, [DataSourceKey.GMAIL]: { name: 'Gmail', description: t(`setting.${DataSourceKey.GMAIL}Description`), icon: , }, [DataSourceKey.REST_API]: { name: 'REST API', description: t(`setting.${DataSourceKey.REST_API}Description`), icon: , }, [DataSourceKey.MOODLE]: { name: 'Moodle', description: t(`setting.${DataSourceKey.MOODLE}Description`), icon: , }, [DataSourceKey.TEAMS]: { name: 'Microsoft Teams', description: t(`setting.${DataSourceKey.TEAMS}Description`), icon: , }, [DataSourceKey.SLACK]: { name: 'Slack', description: t(`setting.${DataSourceKey.SLACK}Description`), icon: , }, [DataSourceKey.SHAREPOINT]: { name: 'SharePoint', description: t(`setting.${DataSourceKey.SHAREPOINT}Description`), icon: , }, [DataSourceKey.JIRA]: { name: 'Jira', description: t(`setting.${DataSourceKey.JIRA}Description`), icon: , }, [DataSourceKey.WEBDAV]: { name: 'WebDAV', description: t(`setting.${DataSourceKey.WEBDAV}Description`), icon: , }, [DataSourceKey.DROPBOX]: { name: 'Dropbox', description: t(`setting.${DataSourceKey.DROPBOX}Description`), icon: , }, [DataSourceKey.BOX]: { name: 'Box', description: t(`setting.${DataSourceKey.BOX}Description`), icon: , }, [DataSourceKey.AIRTABLE]: { name: 'Airtable', description: t(`setting.${DataSourceKey.AIRTABLE}Description`), icon: , }, [DataSourceKey.DINGTALK_AI_TABLE]: { name: 'Dingtalk AI Table', description: t(`setting.dingtalkAITableDescription`), icon: , }, [DataSourceKey.GITLAB]: { name: 'GitLab', description: t(`setting.${DataSourceKey.GITLAB}Description`), icon: , }, [DataSourceKey.ASANA]: { name: 'Asana', description: t(`setting.${DataSourceKey.ASANA}Description`), icon: , }, [DataSourceKey.GITHUB]: { name: 'GitHub', description: t(`setting.${DataSourceKey.GITHUB}Description`), icon: ( ), }, [DataSourceKey.IMAP]: { name: 'IMAP', description: t(`setting.${DataSourceKey.IMAP}Description`), icon: , }, [DataSourceKey.BITBUCKET]: { name: 'Bitbucket', description: t(`setting.${DataSourceKey.BITBUCKET}Description`), icon: , }, [DataSourceKey.ZENDESK]: { name: 'Zendesk', description: t(`setting.${DataSourceKey.ZENDESK}Description`), icon: , }, [DataSourceKey.SEAFILE]: { name: 'SeaFile', description: t(`setting.${DataSourceKey.SEAFILE}Description`), icon: , }, [DataSourceKey.MYSQL]: { name: 'MySQL', description: t(`setting.${DataSourceKey.MYSQL}Description`), icon: , }, [DataSourceKey.POSTGRESQL]: { name: 'PostgreSQL', description: t(`setting.${DataSourceKey.POSTGRESQL}Description`), icon: , }, }; }; export const useDataSourceInfo = () => { const { t } = useTranslation(); const [dataSourceInfo, setDataSourceInfo] = useState( 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 => values.reduce((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) => ( ), 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) => ( ), 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) => ( ), }, { 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, ]; };