feat(integrations): one-step install for workspace-scoped providers

This commit is contained in:
Philipinho
2026-05-23 12:22:26 +01:00
parent 2c80459674
commit 2f313187ab
3 changed files with 62 additions and 24 deletions
@@ -19,6 +19,7 @@ export default function ConnectionRow({
isDisconnecting, isDisconnecting,
}: ConnectionRowProps) { }: ConnectionRowProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const isWorkspaceScoped = definition.oauth?.connectionScope === 'workspace';
return ( return (
<Box <Box
@@ -42,6 +43,21 @@ export default function ConnectionRow({
</Group> </Group>
<Group gap="sm" wrap="nowrap" style={{ flexShrink: 0 }}> <Group gap="sm" wrap="nowrap" style={{ flexShrink: 0 }}>
{isWorkspaceScoped ? (
<>
{connection ? (
<Text size="xs" c="green">
{t("Linked")}
{connection.providerUserId && ` as @${connection.providerUserId}`}
</Text>
) : (
<Text size="xs" c="dimmed">
{t("Use")} <code>/docmost help</code> {t("in")} {definition.name} {t("to link your account.")}
</Text>
)}
</>
) : (
<>
{connection ? ( {connection ? (
<> <>
<Text size="xs" c="green"> <Text size="xs" c="green">
@@ -67,6 +83,8 @@ export default function ConnectionRow({
{t("Connect")} {t("Connect")}
</Button> </Button>
)} )}
</>
)}
</Group> </Group>
</Group> </Group>
</Box> </Box>
@@ -14,6 +14,7 @@ import {
useUpdateIntegrationSettings, useUpdateIntegrationSettings,
} from "../queries/integration-query"; } from "../queries/integration-query";
import { Integration } from "../types/integration.types"; import { Integration } from "../types/integration.types";
import { getOAuthAuthorizeUrl } from "../services/integration-service";
export default function Integrations() { export default function Integrations() {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -28,10 +29,21 @@ export default function Integrations() {
const [configuring, setConfiguring] = useState<Integration | null>(null); const [configuring, setConfiguring] = useState<Integration | null>(null);
const handleInstall = useCallback( const handleInstall = useCallback(
(type: string) => { async (type: string) => {
installMutation.mutate({ type }); const definition = available?.find((d) => d.type === type);
try {
const integration = await installMutation.mutateAsync({ type });
if (definition?.oauth?.connectionScope === 'workspace') {
const { authorizationUrl } = await getOAuthAuthorizeUrl({
integrationId: integration.id,
});
window.location.href = authorizationUrl;
}
} catch (err) {
// installMutation's onError already shows a notification
}
}, },
[installMutation], [installMutation, available],
); );
const handleUninstall = useCallback( const handleUninstall = useCallback(
@@ -1,11 +1,19 @@
export type IntegrationCapability = "oauth" | "unfurl" | "actions" | "webhooks"; export type IntegrationCapability = "oauth" | "unfurl" | "actions" | "webhooks";
export type OAuthConfig = {
authUrl: string;
tokenUrl: string;
scopes: string[];
connectionScope?: 'workspace' | 'user';
};
export type IntegrationDefinition = { export type IntegrationDefinition = {
type: string; type: string;
name: string; name: string;
description: string; description: string;
icon: string; icon: string;
capabilities: IntegrationCapability[]; capabilities: IntegrationCapability[];
oauth?: OAuthConfig;
}; };
export type Integration = { export type Integration = {