* feat: implement Trello integration with OAuth and user fields * feat: migrate Trello integration to new integrations table * refactor: migrate Trello integration to use new integration system * refactor: migrate Trello integration to use new integrations table * fix: add loading check for Trello integration and update VSCode settings * feat: enhance import boards UI with dynamic integration providers and local storage config * feat: add select/unselect all buttons and loading state to board import form * feat: add Trello import env vars on README * docs: update Trello import guide to use OAuth flow instead of API keys * feat: add integrations table for OAuth token management * feat: connect trello from import modal * refactor: consolidate Trello integration endpoints into unified integration router * fix: trello import again * refactor: generalize integration authorization flow and improve error handling * refactor: update Trello API endpoints and tags for better organization * feat: add Integrations tag to OpenAPI specification * refactor: update Trello auth endpoint to use generic integration provider param * refactor: move Trello integration logic from trello.ts to import.ts router * refactor: consolidate Trello integration endpoints and fix API URL * chore: remove debug logs --------- Co-authored-by: Henry <henry_ball@hotmail.co.uk>
129 lines
3.4 KiB
TypeScript
129 lines
3.4 KiB
TypeScript
import { TRPCError } from "@trpc/server";
|
|
import { env } from "next-runtime-env";
|
|
import { z } from "zod";
|
|
|
|
import * as integrationsRepo from "@kan/db/repository/integration.repo";
|
|
|
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
|
|
|
export const urls = {
|
|
trello: "https://api.trello.com/1",
|
|
};
|
|
|
|
export const apiKeys = {
|
|
trello: process.env.TRELLO_APP_API_KEY,
|
|
};
|
|
|
|
export const integrationRouter = createTRPCRouter({
|
|
providers: protectedProcedure.query(async ({ ctx }) => {
|
|
const user = ctx.user;
|
|
|
|
if (!user)
|
|
throw new TRPCError({
|
|
message: "User not authenticated",
|
|
code: "UNAUTHORIZED",
|
|
});
|
|
|
|
const integrations = await integrationsRepo.getProvidersForUser(
|
|
ctx.db,
|
|
user.id,
|
|
);
|
|
|
|
return integrations;
|
|
}),
|
|
disconnect: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "Disconnect integration",
|
|
method: "POST",
|
|
path: "/integration/disconnect",
|
|
description: "Disconnects an integration",
|
|
tags: ["Integration"],
|
|
protect: true,
|
|
},
|
|
})
|
|
.input(z.object({ provider: z.enum(["trello"]) }))
|
|
.output(z.object({}))
|
|
.mutation(async ({ ctx, input }) => {
|
|
const user = ctx.user;
|
|
|
|
if (!user)
|
|
throw new TRPCError({
|
|
message: "User not authenticated",
|
|
code: "UNAUTHORIZED",
|
|
});
|
|
|
|
const integration = await integrationsRepo.getProviderForUser(
|
|
ctx.db,
|
|
user.id,
|
|
input.provider,
|
|
);
|
|
|
|
if (!integration)
|
|
throw new TRPCError({
|
|
message: "Integration not found",
|
|
code: "NOT_FOUND",
|
|
});
|
|
|
|
await integrationsRepo.deleteProviderForUser(
|
|
ctx.db,
|
|
user.id,
|
|
input.provider,
|
|
);
|
|
|
|
return {};
|
|
}),
|
|
getAuthorizationUrl: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "Get authorization URL for an integration",
|
|
method: "GET",
|
|
path: "/integration/authorize",
|
|
description: "Retrieves the authorization URL for an integration",
|
|
tags: ["Integration"],
|
|
protect: true,
|
|
},
|
|
})
|
|
.input(z.object({ provider: z.enum(["trello"]) }))
|
|
.output(z.object({ url: z.string() }))
|
|
.query(async ({ ctx, input }) => {
|
|
const apiKey = apiKeys[input.provider];
|
|
|
|
if (!apiKey)
|
|
throw new TRPCError({
|
|
message: `${input.provider.at(0)?.toUpperCase() + input.provider.slice(1)} API key not set in environment variables`,
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
});
|
|
|
|
const user = ctx.user;
|
|
|
|
if (!user)
|
|
throw new TRPCError({
|
|
message: "User not authenticated",
|
|
code: "UNAUTHORIZED",
|
|
});
|
|
|
|
const integration = await integrationsRepo.getProviderForUser(
|
|
ctx.db,
|
|
user.id,
|
|
input.provider,
|
|
);
|
|
|
|
if (integration)
|
|
throw new TRPCError({
|
|
message: `${input.provider.at(0)?.toUpperCase() + input.provider.slice(1)} integration already exists`,
|
|
code: "BAD_REQUEST",
|
|
});
|
|
|
|
if (input.provider === "trello") {
|
|
const url = `${urls[input.provider]}/authorize?key=${apiKey}&expiration=never&response_type=token&scope=read&return_url=${env("NEXT_PUBLIC_BASE_URL")}/settings/trello/authorize&callback_method=fragment`;
|
|
return { url };
|
|
}
|
|
|
|
throw new TRPCError({
|
|
message: "Invalid provider",
|
|
code: "BAD_REQUEST",
|
|
});
|
|
}),
|
|
});
|