mirror of
https://github.com/docmost/docmost.git
synced 2026-08-25 07:22:14 +10:00
feat(integrations): add connectionScope (workspace|user) to OAuth flow
This commit is contained in:
@@ -149,7 +149,21 @@ export class OAuthService {
|
|||||||
? new Date(Date.now() + tokenResponse.expires_in * 1000)
|
? new Date(Date.now() + tokenResponse.expires_in * 1000)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const connection = await this.connectionRepo.upsert({
|
const connectionScope =
|
||||||
|
provider.definition.oauth?.connectionScope ?? 'user';
|
||||||
|
|
||||||
|
const connection =
|
||||||
|
connectionScope === 'workspace'
|
||||||
|
? await this.connectionRepo.upsertWorkspaceConnection({
|
||||||
|
integrationId,
|
||||||
|
userId,
|
||||||
|
workspaceId,
|
||||||
|
accessToken: encryptedAccessToken,
|
||||||
|
refreshToken: encryptedRefreshToken,
|
||||||
|
tokenExpiresAt,
|
||||||
|
scopes: tokenResponse.scope ?? null,
|
||||||
|
})
|
||||||
|
: await this.connectionRepo.upsert({
|
||||||
integrationId,
|
integrationId,
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ export type OAuthConfig = {
|
|||||||
authUrl: string;
|
authUrl: string;
|
||||||
tokenUrl: string;
|
tokenUrl: string;
|
||||||
scopes: string[];
|
scopes: string[];
|
||||||
|
// 'workspace' = one shared bot/app connection per integration (Slack model);
|
||||||
|
// 'user' (default) = each Docmost user OAuths separately and gets their own token (Linear, Jira, GitHub model)
|
||||||
|
connectionScope?: 'workspace' | 'user';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UnfurlPattern = {
|
export type UnfurlPattern = {
|
||||||
|
|||||||
@@ -95,6 +95,60 @@ export class IntegrationConnectionRepo {
|
|||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async upsertWorkspaceConnection(
|
||||||
|
input: {
|
||||||
|
integrationId: string;
|
||||||
|
userId: string;
|
||||||
|
workspaceId: string;
|
||||||
|
accessToken: string;
|
||||||
|
refreshToken?: string | null;
|
||||||
|
tokenExpiresAt?: Date | null;
|
||||||
|
scopes?: string | null;
|
||||||
|
},
|
||||||
|
trx?: KyselyTransaction,
|
||||||
|
): Promise<IntegrationConnection> {
|
||||||
|
const db = dbOrTx(this.db, trx);
|
||||||
|
|
||||||
|
const existing = await this.findWorkspaceConnection(input.integrationId, trx);
|
||||||
|
if (existing) {
|
||||||
|
return this.update(
|
||||||
|
existing.id,
|
||||||
|
{
|
||||||
|
accessToken: input.accessToken,
|
||||||
|
refreshToken: input.refreshToken ?? null,
|
||||||
|
tokenExpiresAt: input.tokenExpiresAt ?? null,
|
||||||
|
scopes: input.scopes ?? null,
|
||||||
|
userId: input.userId,
|
||||||
|
},
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear any stale non-workspace row for the same (integration, user) to
|
||||||
|
// avoid the uq(integration_id, user_id) constraint blocking the insert.
|
||||||
|
await db
|
||||||
|
.deleteFrom('integrationConnections')
|
||||||
|
.where('integrationId', '=', input.integrationId)
|
||||||
|
.where('userId', '=', input.userId)
|
||||||
|
.where('kind', '!=', 'workspace')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return db
|
||||||
|
.insertInto('integrationConnections')
|
||||||
|
.values({
|
||||||
|
integrationId: input.integrationId,
|
||||||
|
userId: input.userId,
|
||||||
|
workspaceId: input.workspaceId,
|
||||||
|
accessToken: input.accessToken,
|
||||||
|
refreshToken: input.refreshToken ?? null,
|
||||||
|
tokenExpiresAt: input.tokenExpiresAt ?? null,
|
||||||
|
scopes: input.scopes ?? null,
|
||||||
|
kind: 'workspace',
|
||||||
|
})
|
||||||
|
.returningAll()
|
||||||
|
.executeTakeFirstOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
connectionId: string,
|
connectionId: string,
|
||||||
data: UpdatableIntegrationConnection,
|
data: UpdatableIntegrationConnection,
|
||||||
|
|||||||
Reference in New Issue
Block a user