diff --git a/.github/workflows/stainless.yml b/.github/workflows/stainless.yml new file mode 100644 index 000000000..1214b165a --- /dev/null +++ b/.github/workflows/stainless.yml @@ -0,0 +1,17 @@ +name: Upload OpenAPI spec to Stainless + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + stainless: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: stainless-api/upload-openapi-spec-action@main + with: + stainless_api_key: ${{ secrets.STAINLESS_API_KEY }} + input_path: 'packages/api/swagger/swagger-spec.yaml' + project_name: 'panora2' \ No newline at end of file diff --git a/README.md b/README.md index 5be690014..430dd90cf 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Your customers expect all of their tools to work well together. Panora avoids yo Panora supports integration with the following objects across multiple platforms: +[Here is an extensive list of all integrations !](https://docs.panora.dev/integrations-catalog) + ### CRM | | Contacts | Deals | Notes | Engagements | Tasks | Users | Companies | @@ -90,7 +92,13 @@ Your favourite software is missing? [Ask the community to build a connector!](ht docker compose up ``` -Visit our [Quickstart Guide](https://docs.panora.dev/quick-start) to start adding integrations to your product +Visit our [Quickstart Guide](https://docs.panora.dev/quick-start) to start adding integrations to your product ! + +See also [our selfhost guide here !](https://docs.panora.dev/open-source/selfhost/self-host-guide) + +# πŸ‘Ύ Join the community + +- [Join the Discord](https://discord.com/invite/PH5k7gGubt) # πŸ€” Questions? Ask the core team diff --git a/apps/embedded-catalog/react/CHANGELOG.md b/apps/embedded-catalog/react/CHANGELOG.md index 5098d8805..59b27f7af 100644 --- a/apps/embedded-catalog/react/CHANGELOG.md +++ b/apps/embedded-catalog/react/CHANGELOG.md @@ -1,5 +1,11 @@ # @panora/embedded-card-react +## 1.3.1 + +### Patch Changes + +- ded2580: Readme patch + ## 1.3.0 ### Minor Changes diff --git a/apps/embedded-catalog/react/README.md b/apps/embedded-catalog/react/README.md index c1e8f2e20..1940a6e64 100644 --- a/apps/embedded-catalog/react/README.md +++ b/apps/embedded-catalog/react/README.md @@ -21,36 +21,51 @@ or yarn add @panora/embedded-card-react ``` -## Import the component +## Import the components -```bash -# Import the css file +```ts import "@panora/embedded-card-react/dist/index.css"; -import PanoraProviderCard from "@panora/embedded-card-react"; +import { PanoraDynamicCatalogCard, PanoraProviderCard } from '@panora/embedded-card-react'; ``` ## Use the component - The `optionalApiUrl` is an optional prop to use the component with the self-hosted version of Panora. -```bash +```ts + + ``` ```ts -These are the types needed for the component. +These are the types needed for the components. + +The `` takes this props type: interface ProviderCardProp { name: string; projectId: string; - returnUrl: string; - linkedUserIdOrRemoteUserInfo: string; + linkedUserId: string; +} + +The `` takes this props type: + +interface DynamicCardProp { + projectId: string; + linkedUserId: string; + category?: ConnectorCategory; + optionalApiUrl?: string, } ``` diff --git a/apps/embedded-catalog/react/package.json b/apps/embedded-catalog/react/package.json index 6f2693bcd..7d1d95246 100644 --- a/apps/embedded-catalog/react/package.json +++ b/apps/embedded-catalog/react/package.json @@ -1,6 +1,6 @@ { "name": "@panora/embedded-card-react", - "version": "1.3.0", + "version": "1.3.1", "description": "", "main": "dist/index.js", "scripts": { diff --git a/apps/frontend-sdk/CHANGELOG.md b/apps/frontend-sdk/CHANGELOG.md index 162f1d783..9c76bbfc3 100644 --- a/apps/frontend-sdk/CHANGELOG.md +++ b/apps/frontend-sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # @panora/frontend-sdk +## 1.1.1 + +### Patch Changes + +- ded2580: Readme patch + ## 1.1.0 ### Minor Changes diff --git a/apps/frontend-sdk/README.md b/apps/frontend-sdk/README.md new file mode 100644 index 000000000..e51b3c32f --- /dev/null +++ b/apps/frontend-sdk/README.md @@ -0,0 +1,75 @@ + +## Frontend SDK (React) + +It is a React component aimed to be used in any of your pages so end-users can connect their 3rd parties in 1-click! + +## Installation + +```bash +npm i @panora/frontend-sdk +``` + +or + +```bash +pnpm i @panora/frontend-sdk +``` + +or + +```bash +yarn add @panora/frontend-sdk +``` + +## Use the component + +```ts + import { ConnectorCategory } from '@panora/shared' + import Panora from '@panora/frontend-sdk' + + const panora = new Panora({ apiKey: 'YOUR_PRIVATE_API_KEY' }); + + // kickstart the connection (OAuth, ApiKey, Basic) + panora.connect({ + providerName: "hubspot", + vertical: ConnectorCategory.Crm, + linkedUserId: "4c6ca51b-7b23-4e3a-9309-24d2d331a04d", + }) +``` + +```ts +The Panora SDK must be instantiated with this type: + +interface PanoraConfig { + apiKey: string; + overrideApiUrl: string; + // Optional (only if you are in selfhost mode and want to use localhost:3000), by default: api.panora.dev +} + +The .connect() function takes this type: + +interface ConnectOptions { + providerName: string; + vertical: ConnectorCategory; // Must be imported from @panora/shared + linkedUserId: string; // You can copy it from your Panora dahsbord under /configuration tab + credentials?: Credentials; // Optional if you try to use OAuth + options?: { + onSuccess?: () => void; + onError?: (error: Error) => void; + overrideReturnUrl?: string; + } +} + +By default, for OAuth we use Panora managed OAuth apps but if we dont have one registered OR you want to use your own, you must register that under /configuration tab from the webapp and it will automatically use these custom credentials ! + +interface Credentials { + username?: string; // Used for Basic Auth + password?: string; // Used for Basic Auth + apiKey?: string; // Used for Api Key Auth +} + +For Basic Auth some providers may only ask for username or password. + +In this case just specify either password or username depending on the 3rd party reference. + +``` diff --git a/apps/frontend-sdk/package.json b/apps/frontend-sdk/package.json index cab7d47b6..4092c1a76 100644 --- a/apps/frontend-sdk/package.json +++ b/apps/frontend-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@panora/frontend-sdk", - "version": "1.1.0", + "version": "1.1.1", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { diff --git a/apps/frontend-sdk/src/index.ts b/apps/frontend-sdk/src/index.ts index 6a8256af4..86fd60675 100644 --- a/apps/frontend-sdk/src/index.ts +++ b/apps/frontend-sdk/src/index.ts @@ -17,7 +17,7 @@ interface ConnectOptions { vertical: ConnectorCategory; linkedUserId: string; credentials?: Credentials; - options: { + options?: { onSuccess?: () => void; onError?: (error: Error) => void; overrideReturnUrl?: string; @@ -65,7 +65,7 @@ class Panora { } async connect(options: ConnectOptions): Promise { - const { providerName, vertical, linkedUserId, credentials, options: {onSuccess, onError, overrideReturnUrl} } = options; + const { providerName, vertical, linkedUserId, credentials, options: {onSuccess, onError, overrideReturnUrl} = {} } = options; try { const projectId = await this.fetchProjectId(); diff --git a/apps/magic-link/package.json b/apps/magic-link/package.json index dc25a4902..f3d04a29c 100644 --- a/apps/magic-link/package.json +++ b/apps/magic-link/package.json @@ -15,7 +15,6 @@ }, "dependencies": { "@panora/shared": "^1.4.0", - "@panora/typescript-sdk": "^1.0.3", "@radix-ui/react-label": "^2.0.2", "react-hook-form": "^7.51.2", "@hookform/resolvers": "^3.3.4", diff --git a/apps/magic-link/src/hooks/queries/useProjectConnectors.tsx b/apps/magic-link/src/hooks/queries/useProjectConnectors.tsx index 1949f8162..51d9d83a9 100644 --- a/apps/magic-link/src/hooks/queries/useProjectConnectors.tsx +++ b/apps/magic-link/src/hooks/queries/useProjectConnectors.tsx @@ -1,16 +1,22 @@ import { useQuery } from '@tanstack/react-query'; import config from '@/helpers/config'; -const useProjectConnectors = (id: string) => { +const useProjectConnectors = (id: string | null) => { return useQuery({ - queryKey: ['project-connectors', id], + queryKey: ['project-connectors', id], queryFn: async (): Promise => { + if (!id) { + throw new Error('Project ID is not available'); + } const response = await fetch(`${config.API_URL}/project-connectors?projectId=${id}`); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - } + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }, + enabled: !!id, // Only run the query if id is truthy + retry: false, // Don't retry if the project ID is not available }); }; -export default useProjectConnectors; + +export default useProjectConnectors; \ No newline at end of file diff --git a/apps/magic-link/src/hooks/queries/useUniqueMagicLink.tsx b/apps/magic-link/src/hooks/queries/useUniqueMagicLink.tsx index 61357c2ec..edf98ad60 100644 --- a/apps/magic-link/src/hooks/queries/useUniqueMagicLink.tsx +++ b/apps/magic-link/src/hooks/queries/useUniqueMagicLink.tsx @@ -4,16 +4,22 @@ import config from '@/helpers/config'; type Mlink = MagicLink & {id_project: string} -const useUniqueMagicLink = (id: string) => { - return useQuery({ - queryKey: ['magic-link', id], +const useUniqueMagicLink = (id: string | null) => { + return useQuery({ + queryKey: ['magic-link', id], queryFn: async (): Promise => { + if (!id) { + throw new Error('Magic Link ID is not available'); + } const response = await fetch(`${config.API_URL}/magic-links/single?id=${id.trim()}`); if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); - } + }, + enabled: !!id && id.trim().length > 0, // Only run the query if id is truthy and not just whitespace + retry: false, // Don't retry if the magic link ID is not available }); }; -export default useUniqueMagicLink; + +export default useUniqueMagicLink; \ No newline at end of file diff --git a/apps/magic-link/src/hooks/useOAuth.ts b/apps/magic-link/src/hooks/useOAuth.ts index c048607ec..50e6f6012 100644 --- a/apps/magic-link/src/hooks/useOAuth.ts +++ b/apps/magic-link/src/hooks/useOAuth.ts @@ -9,14 +9,14 @@ type UseOAuthProps = { returnUrl: string; // Return URL after OAuth flow projectId: string; // Project ID linkedUserId: string; // Linked User ID - optionalApiUrl?: string; // URL of the User's Server + redirectIngressUri: string | null; // URL of the User's Server onSuccess: () => void; }; -const useOAuth = ({ providerName, vertical, returnUrl, projectId, linkedUserId, onSuccess }: UseOAuthProps) => { +const useOAuth = ({ providerName, vertical, returnUrl, projectId, linkedUserId, redirectIngressUri, onSuccess }: UseOAuthProps) => { const [isReady, setIsReady] = useState(false); const intervalRef = useRef | null>(null); - const authWindowRef = useRef(null); + const authWindowRef = useRef(null); useEffect(() => { // Perform any setup logic here @@ -41,12 +41,12 @@ const useOAuth = ({ providerName, vertical, returnUrl, projectId, linkedUserId, } }; + const openModal = async (onWindowClose: () => void) => { const apiUrl = config.API_URL!; const authUrl = await constructAuthUrl({ - projectId, linkedUserId, providerName, returnUrl, apiUrl, vertical + projectId, linkedUserId, providerName, returnUrl, apiUrl , vertical, rediectUriIngress: redirectIngressUri }); - console.log('auth url is '+ authUrl) if (!authUrl) { throw new Error("Auth Url is Invalid " + authUrl); @@ -56,7 +56,7 @@ const useOAuth = ({ providerName, vertical, returnUrl, projectId, linkedUserId, const left = (window.innerWidth - width) / 2; const top = (window.innerHeight - height) / 2; const authWindow = window.open(authUrl as string, '_blank', `width=${width},height=${height},top=${top},left=${left}`); - authWindowRef.current = authWindow; + authWindowRef.current = authWindow; clearExistingInterval(false); diff --git a/apps/magic-link/src/lib/ProviderModal.tsx b/apps/magic-link/src/lib/ProviderModal.tsx index 5bb15a84c..c3d7e0cfd 100644 --- a/apps/magic-link/src/lib/ProviderModal.tsx +++ b/apps/magic-link/src/lib/ProviderModal.tsx @@ -50,6 +50,7 @@ const ProviderModal = () => { const [openApiKeyDialog,setOpenApiKeyDialog] = useState(false); const [projectId, setProjectId] = useState(""); const [data, setData] = useState([]); + const [isProjectIdReady, setIsProjectIdReady] = useState(false); const [errorResponse,setErrorResponse] = useState<{ errorPresent: boolean; errorMessage : string }>({errorPresent:false,errorMessage:''}) @@ -58,14 +59,14 @@ const ProviderModal = () => { status: boolean; provider: string }>({status: false, provider: ''}); - const [uniqueMagicLinkId, setUniqueMagicLinkId] = useState(''); + const [uniqueMagicLinkId, setUniqueMagicLinkId] = useState(null); const [openSuccessDialog,setOpenSuccessDialog] = useState(false); const [currentProviderLogoURL,setCurrentProviderLogoURL] = useState('') const [currentProvider,setCurrentProvider] = useState('') - + const [redirectIngressUri, setRedirectIngressUri] = useState(null); const {mutate : createApiKeyConnection} = useCreateApiKeyConnection(); const {data: magicLink} = useUniqueMagicLink(uniqueMagicLinkId); - const {data: connectorsForProject} = useProjectConnectors(projectId); + const {data: connectorsForProject} = useProjectConnectors(isProjectIdReady ? projectId : null); // const form = useForm>({ // resolver: zodResolver(formSchema), @@ -85,31 +86,39 @@ const ProviderModal = () => { } }, []); + useEffect(() => { + const queryParams = new URLSearchParams(window.location.search); + const redirectIngressUri = queryParams.get('redirectIngressUri'); + if (redirectIngressUri) { + setRedirectIngressUri(redirectIngressUri); + } + }, []); + useEffect(() => { if (magicLink) { setProjectId(magicLink?.id_project); + setIsProjectIdReady(true); } }, [magicLink]); useEffect(()=>{ - const PROVIDERS = selectedCategory == "All" ? providersArray() : providersArray(selectedCategory); - const getConnectorsToDisplay = () => { - // First, check if the company selected custom connectors in the UI or not - const unwanted_connectors = transformConnectorsStatus(connectorsForProject).filter(connector => connector.status === "false"); - // Filter out the providers present in the unwanted connectors array - const filteredProviders = PROVIDERS.filter(provider => { - return !unwanted_connectors.some( (unwanted) => - unwanted.category === provider.vertical && unwanted.connector_name === provider.name - ); - }); - return filteredProviders; - } - - if(connectorsForProject) { - setData(getConnectorsToDisplay()) + if (isProjectIdReady && connectorsForProject) { + const PROVIDERS = selectedCategory == "All" ? providersArray() : providersArray(selectedCategory); + const getConnectorsToDisplay = () => { + // First, check if the company selected custom connectors in the UI or not + const unwanted_connectors = transformConnectorsStatus(connectorsForProject).filter(connector => connector.status === "false"); + // Filter out the providers present in the unwanted connectors array + const filteredProviders = PROVIDERS.filter(provider => { + return !unwanted_connectors.some( (unwanted) => + unwanted.category === provider.vertical && unwanted.connector_name === provider.name + ); + }); + return filteredProviders; + } + setData(getConnectorsToDisplay()) } - }, [connectorsForProject, selectedCategory]) + }, [connectorsForProject, selectedCategory, isProjectIdReady]) const { open, isReady } = useOAuth({ providerName: selectedProvider?.provider!, @@ -117,6 +126,7 @@ const ProviderModal = () => { returnUrl: window.location.href, projectId: projectId, linkedUserId: magicLink?.id_linked_user as string, + redirectIngressUri, onSuccess: () => { console.log('OAuth successful'); setOpenSuccessDialog(true); diff --git a/apps/webapp/src/app/layout.tsx b/apps/webapp/src/app/layout.tsx index 8eb767539..a6d8f03e9 100644 --- a/apps/webapp/src/app/layout.tsx +++ b/apps/webapp/src/app/layout.tsx @@ -9,7 +9,7 @@ const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Panora", - description: "Unfied API", + description: "Unified API", }; export default function RootLayout({ diff --git a/apps/webapp/src/components/Configuration/Webhooks/AddWebhook.tsx b/apps/webapp/src/components/Configuration/Webhooks/AddWebhook.tsx index 9323c7975..b3537072a 100644 --- a/apps/webapp/src/components/Configuration/Webhooks/AddWebhook.tsx +++ b/apps/webapp/src/components/Configuration/Webhooks/AddWebhook.tsx @@ -80,7 +80,6 @@ const AddWebhook = () => { createWebhookPromise({ url: values.url, description: values.description, - id_project: idProject, scope: selectedScopes, }), { diff --git a/apps/webapp/src/components/Connection/ConnectionTable.tsx b/apps/webapp/src/components/Connection/ConnectionTable.tsx index 69c583b63..6371a21eb 100644 --- a/apps/webapp/src/components/Connection/ConnectionTable.tsx +++ b/apps/webapp/src/components/Connection/ConnectionTable.tsx @@ -55,7 +55,8 @@ export default function ConnectionTable() { connectionToken: connection.connection_token! })) - + const INGRESS_REDIRECT = config.DISTRIBUTION == 'selfhost' && config.REDIRECT_WEBHOOK_INGRESS; + return ( <>
@@ -114,7 +115,7 @@ export default function ConnectionTable() {
- diff --git a/apps/webapp/src/components/Connection/CopyLinkInput.tsx b/apps/webapp/src/components/Connection/CopyLinkInput.tsx index 538861246..067f2f6b5 100644 --- a/apps/webapp/src/components/Connection/CopyLinkInput.tsx +++ b/apps/webapp/src/components/Connection/CopyLinkInput.tsx @@ -12,10 +12,11 @@ const CopyLinkInput = () => { const [copied, setCopied] = useState(false); const {uniqueLink} = useMagicLinkStore(); + const INGRESS_REDIRECT = config.DISTRIBUTION == 'selfhost' && config.REDIRECT_WEBHOOK_INGRESS; const handleCopy = async () => { try { - await navigator.clipboard.writeText(`${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}`); + await navigator.clipboard.writeText(`${config.MAGIC_LINK_DOMAIN}/?uniqueLink=${uniqueLink}&redirectIngressUri=${INGRESS_REDIRECT}`); toast.success("Magic link copied", { action: { label: "Close", @@ -34,7 +35,7 @@ const CopyLinkInput = () => { {uniqueLink !== 'https://' ? <> diff --git a/apps/webapp/src/components/shared/data-table-row-actions.tsx b/apps/webapp/src/components/shared/data-table-row-actions.tsx index 00121144f..7cd1e8ffa 100644 --- a/apps/webapp/src/components/shared/data-table-row-actions.tsx +++ b/apps/webapp/src/components/shared/data-table-row-actions.tsx @@ -29,6 +29,7 @@ export function DataTableRowActions({ const {deleteApiKeyPromise} = useDeleteApiKey(); const {deleteWebhookPromise} = useDeleteWebhook(); + const queryClient = useQueryClient(); const handleDeletion = () => { diff --git a/apps/webapp/src/hooks/create/useCreateWebhook.tsx b/apps/webapp/src/hooks/create/useCreateWebhook.tsx index aa2169df3..00eff7bd4 100644 --- a/apps/webapp/src/hooks/create/useCreateWebhook.tsx +++ b/apps/webapp/src/hooks/create/useCreateWebhook.tsx @@ -5,12 +5,11 @@ import Cookies from 'js-cookie'; interface IWebhookDto { url: string; description?: string; - id_project: string; scope: string[]; } const useCreateWebhook = () => { const add = async (data: IWebhookDto) => { - const response = await fetch(`${config.API_URL}/webhook`, { + const response = await fetch(`${config.API_URL}/webhook/internal`, { method: 'POST', body: JSON.stringify(data), headers: { diff --git a/apps/webapp/src/hooks/delete/useDeleteWebhook.tsx b/apps/webapp/src/hooks/delete/useDeleteWebhook.tsx index 7796e2dc9..5ee7c13fb 100644 --- a/apps/webapp/src/hooks/delete/useDeleteWebhook.tsx +++ b/apps/webapp/src/hooks/delete/useDeleteWebhook.tsx @@ -8,7 +8,7 @@ interface IWebhookDto { const useDeleteWebhook = () => { const remove = async (webhookData: IWebhookDto) => { - const response = await fetch(`${config.API_URL}/webhook/${webhookData.id_webhook}`, { + const response = await fetch(`${config.API_URL}/webhook/internal/${webhookData.id_webhook}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', diff --git a/apps/webapp/src/hooks/get/useWebhooks.tsx b/apps/webapp/src/hooks/get/useWebhooks.tsx index 7d15bf01d..a9c591c24 100644 --- a/apps/webapp/src/hooks/get/useWebhooks.tsx +++ b/apps/webapp/src/hooks/get/useWebhooks.tsx @@ -8,7 +8,7 @@ const useWebhooks = () => { queryKey: ['webhooks'], queryFn: async (): Promise => { console.log("Webhook mutation called") - const response = await fetch(`${config.API_URL}/webhook`, + const response = await fetch(`${config.API_URL}/webhook/internal`, { method: 'GET', headers: { diff --git a/apps/webapp/src/hooks/update/useUpdateWebhookStatus.tsx b/apps/webapp/src/hooks/update/useUpdateWebhookStatus.tsx index 2d08a028f..537c7b87b 100644 --- a/apps/webapp/src/hooks/update/useUpdateWebhookStatus.tsx +++ b/apps/webapp/src/hooks/update/useUpdateWebhookStatus.tsx @@ -8,7 +8,7 @@ interface IWebhookUpdateDto { } const useUpdateWebhookStatus = () => { const update = async (data: IWebhookUpdateDto) => { - const response = await fetch(`${config.API_URL}/webhook/${data.id}`, { + const response = await fetch(`${config.API_URL}/webhook/internal/${data.id}`, { method: 'PUT', body: JSON.stringify({active: data.active}), headers: { diff --git a/apps/webapp/src/lib/config.ts b/apps/webapp/src/lib/config.ts index abf350b1b..201afaa65 100644 --- a/apps/webapp/src/lib/config.ts +++ b/apps/webapp/src/lib/config.ts @@ -1,6 +1,7 @@ const config = { API_URL: process.env.NEXT_PUBLIC_BACKEND_DOMAIN, MAGIC_LINK_DOMAIN: process.env.NEXT_PUBLIC_MAGIC_LINK_DOMAIN, + REDIRECT_WEBHOOK_INGRESS: process.env.NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS, POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST, POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, DISTRIBUTION: process.env.NEXT_PUBLIC_DISTRIBUTION, diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 0341c7383..064ea2ff9 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -206,6 +206,7 @@ services: NEXT_PUBLIC_BACKEND_DOMAIN: ${NEXT_PUBLIC_BACKEND_DOMAIN} NEXT_PUBLIC_MAGIC_LINK_DOMAIN: ${NEXT_PUBLIC_MAGIC_LINK_DOMAIN} NEXT_PUBLIC_WEBAPP_DOMAIN: ${NEXT_PUBLIC_WEBAPP_DOMAIN} + NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS: ${REDIRECT_TUNNEL_INGRESS} restart: unless-stopped ports: - 80:8090 diff --git a/docker-compose.source.yml b/docker-compose.source.yml index 3d21e7b8a..0d830a2ae 100644 --- a/docker-compose.source.yml +++ b/docker-compose.source.yml @@ -204,6 +204,7 @@ services: NEXT_PUBLIC_BACKEND_DOMAIN: ${NEXT_PUBLIC_BACKEND_DOMAIN} NEXT_PUBLIC_FRONTEND_DOMAIN: ${NEXT_PUBLIC_FRONTEND_DOMAIN} NEXT_PUBLIC_WEBAPP_DOMAIN: ${NEXT_PUBLIC_WEBAPP_DOMAIN} + NEXT_PUBLIC_REDIRECT_WEBHOOK_INGRESS: ${REDIRECT_TUNNEL_INGRESS} restart: unless-stopped ports: - 80:8090 diff --git a/docker-compose.yml b/docker-compose.yml index 7e580085f..fafe6b1d4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -216,20 +216,6 @@ services: networks: - backend - frontend - - ngrok: - image: ngrok/ngrok:latest - restart: always - command: - - "start" - - "--all" - - "--config" - - "/etc/ngrok.yml" - volumes: - - ./ngrok.yml:/etc/ngrok.yml - ports: - - 4040:4040 - network_mode: "host" networks: frontend: diff --git a/docs/images/custom-connectors-widget.png b/docs/images/custom-connectors-widget.png new file mode 100644 index 000000000..6b1a6ddf4 Binary files /dev/null and b/docs/images/custom-connectors-widget.png differ diff --git a/docs/images/embed-video.mp4 b/docs/images/embed-video.mp4 new file mode 100644 index 000000000..e03fb18d9 Binary files /dev/null and b/docs/images/embed-video.mp4 differ diff --git a/docs/images/frontend-sdk-video.mp4 b/docs/images/frontend-sdk-video.mp4 new file mode 100644 index 000000000..b7929ba51 Binary files /dev/null and b/docs/images/frontend-sdk-video.mp4 differ diff --git a/docs/images/integrations/hubspot.svg b/docs/images/integrations/hubspot.svg new file mode 100644 index 000000000..b79fe597c --- /dev/null +++ b/docs/images/integrations/hubspot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/integrations-catalog.mdx b/docs/integrations-catalog.mdx new file mode 100644 index 000000000..823ccb4bd --- /dev/null +++ b/docs/integrations-catalog.mdx @@ -0,0 +1,18 @@ +--- +title: "Integration Catalog" +description: "" +icon: album-collection +--- + + + + + + + + + + + + + diff --git a/docs/integrations/ats/ashby/index.mdx b/docs/integrations/ats/ashby/index.mdx new file mode 100644 index 000000000..d855a7ad7 --- /dev/null +++ b/docs/integrations/ats/ashby/index.mdx @@ -0,0 +1,31 @@ +--- +title: "Ashby" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Activity | Yes βœ… | /candidate.listNotes | +| Application | Yes βœ… | [/application.create , /application.list] | +| Attachment | Yes βœ… | [/candidate.uploadResume , /candidate.uploadFile ] | +| Candidate | Yes βœ…| [/candidate.create, /candidate.list] | +| Department | Yes βœ… | /departement.list | +| Eeocs | No 🚫| | +| Interview | Yes βœ…| /interviewSchedule.list | +| Job | Yes βœ… | /job.list | +| Job Interview Stage | Yes βœ…| /interviewStage.list | +| Offer | Yes βœ…| /offer.list | +| Office | Yes βœ…| /location.list | +| Reject Reason | Yes βœ…| /archiveReason.list | +| Score Card | No 🚫| | +| Tag | Yes βœ…| /candidateTag.list | +| User | Yes βœ…| /user.list | + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes || +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/ats/index.mdx b/docs/integrations/ats/index.mdx new file mode 100644 index 000000000..a80c0797e --- /dev/null +++ b/docs/integrations/ats/index.mdx @@ -0,0 +1,12 @@ +--- +title: "Providers" +description: "" +icon: crab +--- + + + + } horizontal> + + diff --git a/docs/integrations/crm/attio/index.mdx b/docs/integrations/crm/attio/index.mdx new file mode 100644 index 000000000..7b2e6fe93 --- /dev/null +++ b/docs/integrations/crm/attio/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Attio" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Contact | Yes βœ… | [/objects/people/records , /objects/people/records/query] | +| Company | Yes βœ… | [/objects/companies/records , /objects/companies/records/query] | +| Deal | Yes βœ… | [/objects/deals/records , /objects/deals/records/query] | +| Engagement | No 🚫| | +| Note | Yes βœ… | /notes | +| Stage | No 🚫| | +| Task | Yes βœ…| /tasks | +| User | Yes βœ… | /workspace_members | + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes | | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/crm/close/index.mdx b/docs/integrations/crm/close/index.mdx new file mode 100644 index 000000000..cc2bec29f --- /dev/null +++ b/docs/integrations/crm/close/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Close" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Contact | Yes βœ… | /contact | +| Company | Yes βœ… | /lead | +| Deal | Yes βœ… | /opportunity | +| Engagement | Yes βœ…| [/activity/calls , /activity/meetings , /activity/emails] | +| Note | Yes βœ… | /activity/note | +| Stage | Yes βœ…| /activity/status_change/opportunity/?opportunity_id=:remote_deal_id | +| Task | Yes βœ…| /task | +| User | Yes βœ… | /user | + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes | | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/crm/hubspot/index.mdx b/docs/integrations/crm/hubspot/index.mdx new file mode 100644 index 000000000..2a459e0ee --- /dev/null +++ b/docs/integrations/crm/hubspot/index.mdx @@ -0,0 +1,24 @@ +--- +title: "Hubspot" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Contact | Yes βœ… | /objects/contacts | +| Company | Yes βœ… | /objects/companies | +| Deal | Yes βœ… | /objects/deals | +| Engagement | Yes βœ…| [/objects/calls , /objects/meetings , /objects/emails] | +| Note | Yes βœ… | /objects/notes | +| Stage | Yes βœ…| /crm-pipelines/v1/pipelines/deals | +| Task | Yes βœ…| /objects/tasks | +| User | Yes βœ… | /owners | + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes |crm.objects.companies.read crm.objects.companies.write crm.objects.contacts.read crm.objects.contacts.write crm.objects.deals.read crm.objects.deals.write crm.objects.custom.read crm.objects.custom.write crm.objects.leads.read crm.objects.leads.write crm.objects.owners.read crm.objects.users.read crm.objects.users.write oauth sales-email-read | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/crm/index.mdx b/docs/integrations/crm/index.mdx new file mode 100644 index 000000000..8e80b832c --- /dev/null +++ b/docs/integrations/crm/index.mdx @@ -0,0 +1,32 @@ +--- +title: "Providers" +description: "" +icon: crab +--- + + + + } horizontal> + + + } horizontal> + + + } horizontal> + + + } horizontal> + + + } horizontal> + + + } horizontal> + + diff --git a/docs/integrations/crm/pipedrive/index.mdx b/docs/integrations/crm/pipedrive/index.mdx new file mode 100644 index 000000000..cb82d8be0 --- /dev/null +++ b/docs/integrations/crm/pipedrive/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Pipedrive" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Contact | Yes βœ… | /persons | +| Company | Yes βœ… | /organizations | +| Deal | Yes βœ… | /deals | +| Engagement | Yes βœ…| /activities | +| Note | Yes βœ… | /notes | +| Stage | Yes βœ… | [/deals , /stages] | +| Task | Yes βœ…| [/activities?type=task , /activities] | +| User | Yes βœ… | /users | + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes | | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/crm/zendesk/index.mdx b/docs/integrations/crm/zendesk/index.mdx new file mode 100644 index 000000000..7fb5f4b00 --- /dev/null +++ b/docs/integrations/crm/zendesk/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Zendesk" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Contact | Yes βœ… | /contacts | +| Company | Yes βœ… | /contacts | +| Deal | Yes βœ… | /deals | +| Engagement | Yes βœ…| /calls | +| Note | Yes βœ… | /notes | +| Stage | Yes βœ… | [/stages , /deals/:remote_deal_id] | +| Task | Yes βœ…| /tasks | +| User | Yes βœ… | /users | + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes || +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/crm/zoho/index.mdx b/docs/integrations/crm/zoho/index.mdx new file mode 100644 index 000000000..c061c4fde --- /dev/null +++ b/docs/integrations/crm/zoho/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Zoho" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Contact | Yes βœ… | /Contacts | +| Company | Yes βœ… | /Accounts | +| Deal | Yes βœ… | /Deals | +| Engagement | Yes βœ…| [/Calls , /Events] | +| Note | Yes βœ… | [/Notes, /Notes/:remote_note_id, ] | +| Stage | No 🚫| | +| Task | Yes βœ…| /Tasks | +| User | Yes βœ… | /users?type=AllUsers | + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes |ZohoCRM.modules.ALL,ZohoCRM.users.READ | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/ecommerce/index.mdx b/docs/integrations/ecommerce/index.mdx new file mode 100644 index 000000000..931fa1f19 --- /dev/null +++ b/docs/integrations/ecommerce/index.mdx @@ -0,0 +1,12 @@ +--- +title: "Providers" +description: "" +icon: crab +--- + + + + } horizontal> + + diff --git a/docs/integrations/ecommerce/shopify/index.mdx b/docs/integrations/ecommerce/shopify/index.mdx new file mode 100644 index 000000000..438ab930c --- /dev/null +++ b/docs/integrations/ecommerce/shopify/index.mdx @@ -0,0 +1,21 @@ +--- +title: "Shopify" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Customer | Yes βœ… | /admin/api/2024-07/customers.json | +| Order | Yes βœ… | /admin/api/2024-07/orders.json | +| Fulfillment | Yes βœ… | /admin/api/2024-07/orders/:remote_order_id/fulfillments.json| +| Fulfillment Orders | No 🚫| | +| Product | Yes βœ… | /admin/api/2024-07/products.json | + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes | | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/filestorage/box/index.mdx b/docs/integrations/filestorage/box/index.mdx new file mode 100644 index 000000000..e60082cb2 --- /dev/null +++ b/docs/integrations/filestorage/box/index.mdx @@ -0,0 +1,24 @@ +--- +title: "Box" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Drive |No 🚫 | | +| File | Yes βœ… | /folders/:remote_folder_id/items | +| Folder | Yes βœ… | [/folders, /folders/:remote_folder_id/items] | +| Group | Yes βœ…| /groups | +| Permission | No 🚫 | Coming Soon | +| Shared Link | No 🚫| Coming Soon | +| User | Yes βœ… | /users | + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes | | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/filestorage/index.mdx b/docs/integrations/filestorage/index.mdx new file mode 100644 index 000000000..37a19fd2b --- /dev/null +++ b/docs/integrations/filestorage/index.mdx @@ -0,0 +1,12 @@ +--- +title: "Providers" +description: "" +icon: crab +--- + + + + } horizontal> + + diff --git a/docs/integrations/ticketing/front/index.mdx b/docs/integrations/ticketing/front/index.mdx new file mode 100644 index 000000000..2cae8e8c2 --- /dev/null +++ b/docs/integrations/ticketing/front/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Front" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Account | Yes βœ… | /accounts | +| Attachment | No 🚫 | Coming Soon | +| Collection |No 🚫 | | +| Comment | Yes βœ…| /conversations/:remote_ticket_id/comments | +| Contact | Yes βœ… | /contacts | +| Tag | Yes βœ…| /tags | +| Team | Yes βœ…| /teams | +| Ticket | Yes βœ… | /conversations | +| User | Yes βœ…| /teammates| + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes | | +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/ticketing/gitlab/index.mdx b/docs/integrations/ticketing/gitlab/index.mdx new file mode 100644 index 000000000..5be3057c5 --- /dev/null +++ b/docs/integrations/ticketing/gitlab/index.mdx @@ -0,0 +1,26 @@ +--- +title: "Gitlab" +description: "" +--- + +# Common Objects + + | Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Account | No 🚫 | | +| Attachment | No 🚫 | Coming Soon | +| Collection | Yes βœ… | /projects?membership=true | +| Comment | Yes βœ…| /projects/:remote_collection_id/issues/:remote_ticket_id/notes | +| Contact | No 🚫| | +| Tag | Yes βœ…| [/groups/:group.id/labels, /groups] | +| Team | No 🚫| | +| Ticket | Yes βœ… | [/projects/:remote_collection_id/issues , /issues?scope=created_by_me&scope=assigned_to_me , /projects/:remote_collection_id/issues/:remote_ticket_id/notes] | +| User | Yes βœ…| [/groups , /groups/:group.id/members] | + + + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes |api read_api read_user create_runner k8s_proxy read_repository write_repository sudo admin_mode read_service_ping openid profile email| +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/ticketing/index.mdx b/docs/integrations/ticketing/index.mdx new file mode 100644 index 000000000..1ea6c979c --- /dev/null +++ b/docs/integrations/ticketing/index.mdx @@ -0,0 +1,24 @@ +--- +title: "Providers" +description: "" +icon: crab +--- + + + + } horizontal> + + + } horizontal> + + + } horizontal> + + + } horizontal> + + diff --git a/docs/integrations/ticketing/jira/index.mdx b/docs/integrations/ticketing/jira/index.mdx new file mode 100644 index 000000000..f42f33dea --- /dev/null +++ b/docs/integrations/ticketing/jira/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Jira" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Account | No 🚫 | | +| Attachment | No 🚫 | Coming Soon| +| Collection | Yes βœ… | /project/search | +| Comment | Yes βœ…| /issue/:remote_ticket_id/comment | +| Contact | No 🚫 | | +| Tag | Yes βœ…| /issue/:remote_ticket_id?fields=labels | +| Team | Yes βœ…| /groups/picker | +| Ticket | Yes βœ… | [/issuetype/project?projectId=:remote_collection_id , /issuetype , /issue , /issue/:remote_ticket_id/comment , /search] | +| User | Yes βœ…| [/users/search , /users/emails?accountId=${accountId}] | + + +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes |read:jira-work manage:jira-project manage:jira-configuration read:jira-user write:jira-work manage:jira-webhook manage:jira-data-provider offline_access| +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/integrations/ticketing/zendesk/index.mdx b/docs/integrations/ticketing/zendesk/index.mdx new file mode 100644 index 000000000..90e926d6c --- /dev/null +++ b/docs/integrations/ticketing/zendesk/index.mdx @@ -0,0 +1,25 @@ +--- +title: "Zendesk" +description: "" +--- + +# Common Objects + + +| Unified Model | Supported | Provider Endpoints | +| -------- | ------------------------------------- | ------------------------------------- | +| Account | Yes βœ… | [/organizations , /organizations/:remote_account_id.json] | +| Attachment | No 🚫 | Coming Soon | +| Collection | No 🚫 | | +| Comment | Yes βœ…| [/tickets/:remote_ticket_id.json , /tickets/:remote_ticket_id/comments.json] | +| Contact | Yes βœ… | [/users.json , /users/:remote_contact_id.json] | +| Tag | Yes βœ…| /tickets/:remote_ticket_id/tags | +| Team | Yes βœ…| /groups.json | +| Ticket | Yes βœ… | [/tickets.json , /tickets/:remote_ticket_id.json] | +| User | Yes βœ…| [/users.json , /users/remote_user_id.json] | + +Β  +| Features | Supported | +| -------- | ------------------------------------- | +| Scopes |read write| +| Realtime webhook | No 🚫| \ No newline at end of file diff --git a/docs/logo/logo-panora-dark.png b/docs/logo/logo-panora-dark.png new file mode 100644 index 000000000..5847c7937 Binary files /dev/null and b/docs/logo/logo-panora-dark.png differ diff --git a/docs/logo/logo-panora-light.png b/docs/logo/logo-panora-light.png new file mode 100644 index 000000000..0b316f404 Binary files /dev/null and b/docs/logo/logo-panora-light.png differ diff --git a/docs/mint.json b/docs/mint.json index cf7bce06d..2dc334483 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -30,14 +30,14 @@ "raiseIssue": true }, "logo": { - "dark": "/logo/logo-panora-white-hq.png", - "light": "/logo/logo-panora-white.png" + "dark": "/logo/logo-panora-dark.png", + "light": "/logo/logo-panora-light.png" }, "favicon": "logo/fav.png", "colors": { - "primary": "#085faf", - "light": "#085faf", - "dark": "#073763", + "primary": "#000000", +"light": "#FFFFFF", +"dark": "#000000", "anchors": { "from": "#000000", "to": "#000000" @@ -59,7 +59,7 @@ }, "tabs": [ { - "name": "Crm", + "name": "CRM", "url": "crm" }, { @@ -69,6 +69,10 @@ { "name": "API Reference", "url": "api-reference" + }, + { + "name": "Integrations", + "url": "integrations" } ], "anchors": [ @@ -78,7 +82,8 @@ "group": "Documentation", "pages": [ "welcome", - "quick-start" + "quick-start", + "integrations-catalog" ] }, { @@ -158,6 +163,55 @@ } ] }, + { + "group": "", + "pages": [ + { + "group": "CRM", + "pages": [ + "integrations/crm/index", + "integrations/crm/hubspot/index", + "integrations/crm/zoho/index", + "integrations/crm/close/index", + "integrations/crm/attio/index", + "integrations/crm/zendesk/index", + "integrations/crm/pipedrive/index" + ] + }, + { + "group": "Ticketing", + "pages": [ + "integrations/ticketing/index", + "integrations/ticketing/zendesk/index", + "integrations/ticketing/jira/index", + "integrations/ticketing/gitlab/index", + "integrations/ticketing/front/index" + ] + }, + { + "group": "ATS", + "pages": [ + "integrations/ats/index", + "integrations/ats/ashby/index" + ] + }, + { + "group": "File Storage", + "pages": [ + "integrations/filestorage/index", + "integrations/filestorage/box/index" + ] + }, + { + "group": "Ecommerce", + "pages": [ + "integrations/ecommerce/index", + "integrations/ecommerce/shopify/index" + ] + } + + ] + }, { "group": "", "pages": [ @@ -171,7 +225,7 @@ "group": "Unified Objects", "pages": [ { - "group": "Crm", + "group": "CRM", "pages": [ { "group": "Contact", @@ -414,6 +468,7 @@ { "group": "Recipes", "pages": [ + "recipes/frontend-sdk", "recipes/embed-catalog", "recipes/import-existing-users", "recipes/catch-connection-token", @@ -433,7 +488,7 @@ "group": "Devtools", "pages": [ { - "group": "SDKs", + "group": "Backend SDKs", "pages": ["backend-sdk/typescript", "backend-sdk/python"] } ] @@ -452,8 +507,25 @@ { "group": "Open-Source", "pages": [ - "open-source/self-host-guide", - "open-source/contributors" + { + "group": "Selfhost", + "icon": "house-laptop", + "pages": [ + "open-source/selfhost/self-host-guide", + "open-source/selfhost/envVariables", + "open-source/selfhost/ngrok" + ] + }, + { + "group": "Build a connector", + "icon": "star", + "pages": [ + "open-source/contributors/setup", + "open-source/contributors/metadata", + "open-source/contributors/build", + "open-source/contributors/test" + ] + } ] }, { diff --git a/docs/open-source/contributors.mdx b/docs/open-source/contributors/build.mdx similarity index 58% rename from docs/open-source/contributors.mdx rename to docs/open-source/contributors/build.mdx index 0326306ae..af4259a2e 100644 --- a/docs/open-source/contributors.mdx +++ b/docs/open-source/contributors/build.mdx @@ -1,60 +1,9 @@ --- -title: "Build a connector" +title: "3. Build" description: "We welcome all contributions to Panora; from small UI enhancements to brand new integrations. We love seeing community members level up and give people power-ups!" icon: "star" --- -## Introduction - -### Setup your environnement - -We made a docker file that builds Panora from sources, specifically to help you locally test your contributions. Here's how to use it. - -Execute these from your Panora's repo root folder - - - - ```bash - cp .env.example .env - ``` - - - - You must manually create a custom OAuth2 App inside the 3rd party and copy the - critical values (likely _client id and client secret_) - - You have the option to manage a custom 3rd party OAuth App. - - **Each custom 3rd party environment variable must be of the form `PROVIDER_category_SOFTWAREMODE_ATTRIBUTE` where** - - - `PROVIDER` is any 3rd party name - - `category` is for example [CRM, TICKETING, MARKETINGAUTOMATION, ...] - - `SOFTWAREMODE` is [ CLOUD, ONPREMISE ] - - `ATTRIBUTE` is for example [ CLIENT_ID, CLIENT_SECRET, SUBDOMAIN, ... ] - - _Example_ : - - - `HUBSPOT_CRM_CLOUD_CLIENT_ID = client_id_value_pasted_from_hubspot_developer_app` - - `HUBSPOT_CRM_CLOUD_CLIENT_SECRET = client_secret_value_pasted_from_hubspot_developer_app` - - - ```bash - rm -rf node_modules .pnpm-store ./packages/api/dist ./packages/api/node_modules ./apps/webapp/node_modules ./apps/frontend_snippet/node_modules - ``` - For Mac users only: ```echo -e "node-linker=hoisted\n package-import-method=clone-or-copy" > .npmrc``` - - - If you have to create an oAuth app for a provider and needs an **https** redirect uri you must enable Grok service and use your secure domain from them (it proxies requests to `localhost:3000`): - - Add your auth token and domain from your Ngrok dashboard to the `ngrok.yml` file. - - Uncomment the Grok service inside the `docker-compose.dev.yml` file. - - - ```bash - docker compose -f docker-compose.dev.yml up - ``` - - - That's all ! You can find the backend and other services running at their usual location. Editing code locally will **immediately** reflect. ## Adding new Integrations ✨ @@ -81,11 +30,6 @@ You can find all categories inside [`packages/shared/src/categories.ts`](https:/ to the `crm` category. -# Step 1: Ensure 3rd party metadata is set - -Look into the `packages/shared/src/connectors/metadata.ts` file and check if the provider you want to build has its metadata set inside the `CONNECTORS_METADATA` object. - -It should be available (if not [contact us](https://app.cal.com/rflih/30)) with `active` field set to `false` meaning the integration has not been built. Actually an integration is built in 2 parts : @@ -242,48 +186,4 @@ For the sake of this guide, let's map the common object `contact` under `crm` ca The script will automatically scan the `/crm/contact/services` folder and detect any new service folder so all dependencies and imports are updated across the codebase. - - -# Step 3: Test your new connector - -Now you must test Panora unified endpoints with the new connector built. - - - **If the connector uses oAuth:**
- You need a set of credentials that you'll set in `.env` if you use oAuth (check above how it works). - Check [this guide](/core-concepts/magic-links) to create a connection through it. - To activate your new connector on the magic link, visit this [**file**](https://github.com/panoratech/Panora/blob/main/packages/shared/src/connectors/metadata.ts#L6) : - and change your connector status to `active: true`. - - **If the connector uses API KEY auth:**
- You currently have to do a POST request to create a connection.
- Create your query encoded URI by computing this:
- ```ts - const state = encodeURIComponent(JSON.stringify({ projectId, linkedUserId, providerName, vertical, returnUrl })); - ``` - The values for `linkedUserId` and `projectId` are to be found within the webapp at `localhost` - ```shell Insert an api key connection - curl --request POST \ - --url http://localhost:3000/connections/apikey/callback?state=state \ - --header 'Content-Type: application/json' \ - --data '{ - "apikey": "YOUR_CONNECTOR_API_KEY", - // if your connector needs more parameters for auth (e.g username or whatever) - //"username": "YOUR_USERNAME_VALUE" - }' - ``` -
- - Now after successfully creating a new connection, you can copy a `connection token` inside the webapp under `http://localhost/connections`. - Create a Panora Api Key and make unified request with your `connection token`. - Check [this guide](/core-concepts/auth) to do it ! - -
- - - -### Congrats, you now have built a new integration with Panora ! πŸ¦Έβ€β™€οΈ - - - Nb: The development kit to add integrations out of the blue is coming soon 🎸 - + \ No newline at end of file diff --git a/docs/open-source/contributors/metadata.mdx b/docs/open-source/contributors/metadata.mdx new file mode 100644 index 000000000..807f1e28d --- /dev/null +++ b/docs/open-source/contributors/metadata.mdx @@ -0,0 +1,11 @@ +--- +title: "2. Metadata" +description: "We welcome all contributions to Panora; from small UI enhancements to brand new integrations. We love seeing community members level up and give people power-ups!" +icon: "star" +--- + +# Ensure 3rd party metadata is set + +Look into the `packages/shared/src/connectors/metadata.ts` [file](https://github.com/panoratech/Panora/blob/main/packages/shared/src/connectors/metadata.ts) and check if the provider you want to build has its metadata set inside the `CONNECTORS_METADATA` object. + +It should be available (if not [contact us](https://app.cal.com/rflih/30)) with `active` field set to `false` meaning the integration has not been built. diff --git a/docs/open-source/contributors/setup.mdx b/docs/open-source/contributors/setup.mdx new file mode 100644 index 000000000..b2fe8ce34 --- /dev/null +++ b/docs/open-source/contributors/setup.mdx @@ -0,0 +1,45 @@ +--- +title: "1. Setup" +description: "We welcome all contributions to Panora; from small UI enhancements to brand new integrations. We love seeing community members level up and give people power-ups!" +icon: "star" +--- + +## Introduction + +### Setup your environnement + +We made a docker file that builds Panora from sources, specifically to help you locally test your contributions. Here's how to use it. + +Execute these from your Panora's repo root folder + + + + Check environment variables reference [here](/open-source/selfhost/envVariables) ! + ```bash + cp .env.example .env + ``` + + + ```bash + rm -rf node_modules .pnpm-store ./packages/api/dist ./packages/api/node_modules ./apps/webapp/node_modules ./apps/frontend_snippet/node_modules + ``` + For Mac users only: ```echo -e "node-linker=hoisted\n package-import-method=clone-or-copy" > .npmrc``` + + + If you have to create an oAuth app for a provider and needs an **https** redirect uri you must enable Grok service and use your secure domain from them (it proxies requests to `localhost:3000`). + Check this [quick guide](/open-source/selfhost/ngrok) to set it up ! + + + ```bash + docker compose -f docker-compose.dev.yml up + ``` + + + That's all ! You can find the backend and other services running at their usual location. Editing code locally will **immediately** reflect. + + + + + + + diff --git a/docs/open-source/contributors/test.mdx b/docs/open-source/contributors/test.mdx new file mode 100644 index 000000000..79f9e2cec --- /dev/null +++ b/docs/open-source/contributors/test.mdx @@ -0,0 +1,28 @@ +--- +title: "4. Test" +description: "We welcome all contributions to Panora; from small UI enhancements to brand new integrations. We love seeing community members level up and give people power-ups!" +icon: "star" +--- + +# Test your new connector + +Now you must test Panora unified endpoints with the new connector built. + + + You need a set of credentials that you'll set in `.env` if you use oAuth (check [here](/open-source/selfhost/envVariables#providers-specific-credentials) how it works). + Check [this guide](/core-concepts/magic-links) to create a connection through it. + To activate your new connector on the magic link, visit this [**file**](https://github.com/panoratech/Panora/blob/main/packages/shared/src/connectors/metadata.ts#L6) : + and change your connector status to `active: true`. + + + Now after successfully creating a new connection, you can copy a `connection token` inside the webapp under `http://localhost/connections`. + Create a Panora Api Key and make unified request with your `connection token`. + Check [this guide](/core-concepts/auth) to do it ! + + + +### Congrats, you now have built a new integration with Panora ! πŸ¦Έβ€β™€οΈ + + + Nb: The development kit to add integrations out of the blue is coming soon 🎸 + diff --git a/docs/open-source/selfhost/envVariables.mdx b/docs/open-source/selfhost/envVariables.mdx new file mode 100644 index 000000000..db13ccf7a --- /dev/null +++ b/docs/open-source/selfhost/envVariables.mdx @@ -0,0 +1,206 @@ +--- +title: "Environment Variables" +description: "" +--- + + +# Backend + +## Secrets + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| JWT_SECRET | secret_jwt | Jwt token used to authenticate requests coming from the webapp to the server | +| ENCRYPT_CRYPTO_SECRET_KEY | 0123456789abcdef0123456789abcdef | 256-Bit Encryption key used to encrypt sensitive data | + +## Configuration + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| ENV | dev | Values are dev, prod or sandbox | +| DISTRIBUTION | selfhost | Values are selfhost or managed | +| PANORA_BASE_API_URL | http://localhost:3000 | | +| REDIS_HOST | redis | The redis host | +| REDIS_PASS | A3vniod98Zbuvn9u5 | The redis password | +| REDIS_PORT | 6379 |The redis port| + +## Errors & Analytics (Optional in Selfhost) + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| SENTRY_DSN | | Sentry Dsn | +| SENTRY_ENABLED | | Flag which indicates if you want to enable Sentry | + + +## Database + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| POSTGRES_USER | my_user | username to get access access to the db | +| POSTGRES_DB | panora_db | database name to get access access to the db | +| POSTGRES_HOST | postgres | host to get access access to the db | +| POSTGRES_PASSWORD | my_password | password to get access access to the db | + + +## Ingress + +Check [here](/selfhost/ngrok) to set up Ngrok ! + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| REDIRECT_TUNNEL_INGRESS | https://MY-NGROK-DOMAIN.ngrok-free.app | Mandatory only when DISTRIBUTION=selfhost. Endpoint (an Ngrok tunnel domain) when you have to test your OAuth App and needs a HTTPS redirectUri that redirects to your localhost (useful for contributors that might need to test their oAuth flow) | +| WEBHOOK_INGRESS | https://MY-NGROK-DOMAIN.ngrok-free.app | Same job as REDIRECT_TUNNEL_INGRESS. You can use the same value for both if you prefer. | + + +## Providers Specific Credentials + + + You must manually create a custom OAuth2 App inside the 3rd party and copy the + critical values (likely _client id and client secret_) + + You have the option to manage a custom 3rd party OAuth App. + + **Each custom 3rd party environment variable must be of the form `PROVIDER_category_SOFTWAREMODE_ATTRIBUTE` where** + + - `PROVIDER` is any 3rd party name + - `category` is for example [CRM, TICKETING, MARKETINGAUTOMATION, ...] + - `SOFTWAREMODE` is [ CLOUD, ONPREMISE ] + - `ATTRIBUTE` is for example [ CLIENT_ID, CLIENT_SECRET, SUBDOMAIN, ... ] + + _Example_ : + + - `HUBSPOT_CRM_CLOUD_CLIENT_ID = client_id_value_pasted_from_hubspot_developer_app` + - `HUBSPOT_CRM_CLOUD_CLIENT_SECRET = client_secret_value_pasted_from_hubspot_developer_app` + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| HUBSPOT_CRM_CLOUD_CLIENT_ID | | | +| HUBSPOT_CRM_CLOUD_CLIENT_SECRET | | | +| ZOHO_CRM_CLOUD_CLIENT_ID | | | +| ZOHO_CRM_CLOUD_CLIENT_SECRET | | | +| PIPEDRIVE_CRM_CLOUD_CLIENT_ID | | | +| PIPEDRIVE_CRM_CLOUD_CLIENT_SECRET | | | +| ZENDESK_CRM_CLOUD_CLIENT_ID | | | +| ZENDESK_CRM_CLOUD_CLIENT_SECRET | | | +| ATTIO_CRM_CLOUD_CLIENT_ID | | | +| ATTIO_CRM_CLOUD_CLIENT_SECRET | | | +| CLOSE_CRM_CLOUD_CLIENT_ID | | | +| CLOSE_CRM_CLOUD_CLIENT_SECRET | | | +| CAPSULE_CRM_CLOUD_CLIENT_ID | | | +| CAPSULE_CRM_CLOUD_CLIENT_SECRET | | | +| TEAMLEADER_CRM_CLOUD_CLIENT_ID | | | +| TEAMLEADER_CRM_CLOUD_CLIENT_SECRET | | | +| ZENDESK_TICKETING_CLOUD_CLIENT_ID | | | +| ZENDESK_TICKETING_CLOUD_CLIENT_SECRET | | | +| ZENDESK_TICKETING_CLOUD_SUBDOMAIN | | | +| JIRA_TICKETING_CLOUD_CLIENT_ID | | | +| JIRA_TICKETING_CLOUD_CLIENT_SECRET | | | +| FRONT_TICKETING_CLOUD_CLIENT_ID | | | +| FRONT_TICKETING_CLOUD_CLIENT_SECRET | | | +| GORGIAS_TICKETING_CLOUD_CLIENT_ID | | | +| GORGIAS_TICKETING_CLOUD_CLIENT_SECRET | | | +| LINEAR_TICKETING_CLOUD_CLIENT_ID | | | +| LINEAR_TICKETING_CLOUD_CLIENT_SECRET | | | +| CLICKUP_TICKETING_CLOUD_CLIENT_ID | | | +| CLICKUP_TICKETING_CLOUD_CLIENT_SECRET | | | +| ASANA_TICKETING_CLOUD_CLIENT_ID | | | +| ASANA_TICKETING_CLOUD_CLIENT_SECRET | | | +| AHA_TICKETING_CLOUD_CLIENT_ID | | | +| AHA_TICKETING_CLOUD_CLIENT_SECRET | | | +| AHA_TICKETING_CLOUD_SUBDOMAIN | | | +| HELPSCOUT_TICKETING_CLOUD_CLIENT_ID | | | +| HELPSCOUT_TICKETING_CLOUD_CLIENT_SECRET | | | +| IRONCLAD_TICKETING_CLOUD_CLIENT_ID | | | +| IRONCLAD_TICKETING_CLOUD_CLIENT_SECRET | | | +| GITHUB_TICKETING_CLOUD_CLIENT_ID | | | +| GITHUB_TICKETING_CLOUD_CLIENT_SECRET | | | +| WRIKE_TICKETING_CLOUD_CLIENT_ID | | | +| WRIKE_TICKETING_CLOUD_CLIENT_SECRET | | | +| GITLAB_TICKETING_CLOUD_CLIENT_ID | | | +| GITLAB_TICKETING_CLOUD_CLIENT_SECRET | | | +| SAGE_ACCOUNTING_CLOUD_CLIENT_ID | | | +| SAGE_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| PENNYLANE_ACCOUNTING_CLOUD_CLIENT_ID | | | +| PENNYLANE_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| FREEAGENT_ACCOUNTING_CLOUD_CLIENT_ID | | | +| FREEAGENT_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| FRESHBOOKS_ACCOUNTING_CLOUD_CLIENT_ID | | | +| FRESHBOOKS_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| MONEYBIRD_ACCOUNTING_CLOUD_CLIENT_ID | | | +| MONEYBIRD_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| QUICKBOOKS_ACCOUNTING_CLOUD_CLIENT_ID | | | +| QUICKBOOKS_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| XERO_ACCOUNTING_CLOUD_CLIENT_ID | | | +| XERO_ACCOUNTING_CLOUD_CLIENT_SECRET | | | +| DEEL_HRIS_CLOUD_CLIENT_ID | | | +| DEEL_HRIS_CLOUD_CLIENT_SECRET | | | +| RIPPLING_HRIS_CLOUD_CLIENT_ID | | | +| RIPPLING_HRIS_CLOUD_CLIENT_SECRET | | | +| GUSTO_HRIS_CLOUD_CLIENT_ID | | | +| GUSTO_HRIS_CLOUD_CLIENT_SECRET | | | +| FACTORIAL_HRIS_CLOUD_CLIENT_ID | | | +| FACTORIAL_HRIS_CLOUD_CLIENT_SECRET | | | +| NAMELY_HRIS_CLOUD_CLIENT_ID | | | +| NAMELY_HRIS_CLOUD_CLIENT_SECRET | | | +| NAMELY_HRIS_CLOUD_SUBDOMAIN | | | +| GOOGLEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID | | | +| GOOGLEDRIVE_FILESTORAGE_CLOUD_CLIENT_SECRET | | | +| ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_ID | | | +| ONEDRIVE_FILESTORAGE_CLOUD_CLIENT_SECRET | | | +| SHAREPOINT_FILESTORAGE_CLOUD_CLIENT_ID | | | +| SHAREPOINT_FILESTORAGE_CLOUD_CLIENT_SECRET | | | +| DROPBOX_FILESTORAGE_CLOUD_CLIENT_ID | | | +| DROPBOX_FILESTORAGE_CLOUD_CLIENT_SECRET | | | +| BOX_FILESTORAGE_CLOUD_CLIENT_ID | | | +| BOX_FILESTORAGE_CLOUD_CLIENT_SECRET | | | +| KEAP_MARKETINGAUTOMATION_CLOUD_CLIENT_ID | | | +| KEAP_MARKETINGAUTOMATION_CLOUD_CLIENT_SECRET | | | +| MAILCHIMP_MARKETINGAUTOMATION_CLOUD_CLIENT_ID | | | +| MAILCHIMP_MARKETINGAUTOMATION_CLOUD_CLIENT_SECRET | | | +| KLAVIYO_TICKETING_CLOUD_CLIENT_ID | | | +| KLAVIYO_TICKETING_CLOUD_CLIENT_SECRET | | | +| NOTION_MANAGEMENT_CLOUD_CLIENT_ID | | | +| NOTION_MANAGEMENT_CLOUD_CLIENT_SECRET | | | +| SLACK_MANAGEMENT_CLOUD_CLIENT_ID | | | +| SLACK_MANAGEMENT_CLOUD_CLIENT_SECRET | | | +| GREENHOUSE_ATS_CLOUD_CLIENT_ID | | | +| GREENHOUSE_ATS_CLOUD_CLIENT_SECRET | | | +| JOBADDER_ATS_CLOUD_CLIENT_ID | | | +| JOBADDER_ATS_CLOUD_CLIENT_SECRET | | | +| LEVER_ATS_CLOUD_CLIENT_ID | | | +| LEVER_ATS_CLOUD_CLIENT_SECRET | | | +| SHOPIFY_ECOMMERCE_CLOUD_CLIENT_ID | | | +| SHOPIFY_ECOMMERCE_CLOUD_CLIENT_SECRET | | | +| SHOPIFY_ECOMMERCE_CLOUD_SUBDOMAIN | | | + + +# Frontend + +## Configuration + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| NEXT_PUBLIC_BACKEND_DOMAIN | http://localhost:3000 | Server url. Values are [http://localhost:3000, https://api-dev.panora.dev, https://api-sandbox.panora.dev, https://api.panora.dev] | +| NEXT_PUBLIC_MAGIC_LINK_DOMAIN | http://localhost:81 | Magic link server. Values are [http://localhost:81, https://connect-dev.panora.dev, https://connect.panora.dev ] | +| NEXT_PUBLIC_DISTRIBUTION | managed | Values are selfhost or managed | +| NEXT_PUBLIC_WEBAPP_DOMAIN | http://localhost | Webapp client url. Values are [http://localhost, https://app-dev.panora.dev, https://app.panora.dev, ] | +| NEXT_PUBLIC_POSTHOG_HOST | | Host domain of your posthog | +| NEXT_PUBLIC_POSTHOG_KEY | | Key of your posthog account | + + + +## Storage (Optional) + +You can let them empty for now ! + +| Variable | Example | Purpose | +| -------- | ------------------------------------- | ------------------------------------- | +| MINIO_ROOT_USER | myaccesskey13 | | +| MINIO_ROOT_PASSWORD | mysecretkey12 | | +| AWS_S3_REGION | us-east-1 | | +| S3_TCG_ATTACHMENTS_BUCKETNAME | tcg-attachments| | +| BUCKET_TCG_ATTACHMENTS_USER | BUCKET_TCG_ATTACHMENTS_USER01 | | +| BUCKET_TCG_ATTACHMENTS_PW | BUCKET_TCG_ATTACHMENTS_PW01 | | + + + diff --git a/docs/open-source/selfhost/ngrok.mdx b/docs/open-source/selfhost/ngrok.mdx new file mode 100644 index 000000000..930f83498 --- /dev/null +++ b/docs/open-source/selfhost/ngrok.mdx @@ -0,0 +1,47 @@ +--- +title: "Set up Ngrok" +description: "" +--- + + + ```bash + cp ngrok.yml.example ngrok.yml + ``` + + + ```bash + version: 2 + authtoken: AUTH_TOKEN # Grab your auth token here + log_level: debug + log: stdout + + tunnels: + api-tunnel: + proto: http + addr: 3000 + domain: DOMAIN # Grab your domain here + ``` + - AUTH_TOKEN can be found [in your ngrok dashboard](https://dashboard.ngrok.com/get-started/your-authtoken) + - DOMAIN can be found also [in your ngrok dashboard](https://dashboard.ngrok.com/cloud-edge/domains) + + + Uncomment ngrok service in docker-compose[.dev, .source].yml. The following code **MUST BE** uncommented. + ```bash + ngrok: + image: ngrok/ngrok:latest + restart: always + command: + - "start" + - "--all" + - "--config" + - "/etc/ngrok.yml" + volumes: + - ./ngrok.yml:/etc/ngrok.yml + ports: + - 4040:4040 + network_mode: "host" + ``` + + +That's all ! After launching the dockers, all requests targeted at REDIRECT_TUNNEL_INGRESS or WEBHOOK_INGRESS are effectively tunneled to your backend server ! + diff --git a/docs/open-source/self-host-guide.mdx b/docs/open-source/selfhost/self-host-guide.mdx similarity index 83% rename from docs/open-source/self-host-guide.mdx rename to docs/open-source/selfhost/self-host-guide.mdx index 9c7dceeb9..78f7c34a7 100644 --- a/docs/open-source/self-host-guide.mdx +++ b/docs/open-source/selfhost/self-host-guide.mdx @@ -1,23 +1,10 @@ --- -title: "Self-hosting Guide" +title: "Quickstart" description: "Try Panora in no time with full control." -icon: "house-laptop" --- **Prerequisite:** You should have Git and Docker installed - - - - ```bash @@ -26,6 +13,7 @@ git clone https://github.com/panoratech/Panora.git + Check [here](/open-source/selfhost/envVariables) to fill the right env variables ! ```bash cd Panora && cp .env.example .env ``` diff --git a/docs/recipes/embed-catalog.mdx b/docs/recipes/embed-catalog.mdx index 5fd06fc97..ef8e7199b 100644 --- a/docs/recipes/embed-catalog.mdx +++ b/docs/recipes/embed-catalog.mdx @@ -4,21 +4,20 @@ description: "" icon: "square-terminal" --- -**insert**video - # Step 1: Import the code snippet ```shell React - npm i @panora/embedded-card-react + pnpm i @panora/embedded-card-react ``` - - By default, all connectors are rendered but you have the option to use the - `category` prop to filter them by category. Learn more about values accepted - [here](/glossary/metadata/category). - +