diff --git a/.env.example b/.env.example index 0a43f2ad..48222264 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,9 @@ S3_SECRET_ACCESS_KEY= BETTER_AUTH_SECRET= BETTER_AUTH_TRUSTED_ORIGINS= +TRELLO_APP_API_KEY= +TRELLO_APP_SECRET= + GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= DISCORD_CLIENT_ID= diff --git a/README.md b/README.md index 7dea739a..35e7f197 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,8 @@ pnpm dev | `DISCORD_CLIENT_SECRET` | Discord OAuth client secret | For Discord login | `xxx` | | `GITHUB_CLIENT_ID` | GitHub OAuth client ID | For GitHub login | `xxx` | | `GITHUB_CLIENT_SECRET` | GitHub OAuth client secret | For GitHub login | `xxx` | +| `TRELLO_APP_API_KEY` | Trello app API key | For Trello import | `xxx` | +| `TRELLO_APP_API_SECRET` | Trello app API secret | For Trello import | `xxx` | | `S3_REGION` | S3 storage region | For file uploads | `WEUR` | | `S3_ENDPOINT` | S3 endpoint URL | For file uploads | `https://xxx.r2.cloudflarestorage.com` | | `S3_ACCESS_KEY_ID` | S3 access key | For file uploads | `xxx` | diff --git a/apps/docs/imports/trello.mdx b/apps/docs/imports/trello.mdx index ab402779..b93a19b1 100644 --- a/apps/docs/imports/trello.mdx +++ b/apps/docs/imports/trello.mdx @@ -3,31 +3,25 @@ title: Trello description: "Import your Trello boards to Kan." --- -## Getting your Trello API key and token +## Authorizing Kan.bn for Trello -Before you can import your Trello boards, you need to get your Trello API key and token. Follow these steps (only takes a couple of minutes): +Before you can import your Trello boards, you need to authorize Kan.bn for Trello. Follow these steps (only takes a couple of minutes): -1. **Sign in to your Trello account** - Go to [https://trello.com](https://trello.com) and log in with your Trello account. +1. **Authorize Kan.bn for Trello** + Go to [https://kan.bn/settings](https://kan.bn/settings) and click **Connect Trello**. -2. **Create a new integration** - Visit [https://trello.com/power-ups/admin](https://trello.com/power-ups/admin). Click **Create new** and fill in the following details: +2. **Log in to your Trello account** + You will be redirected to Trello to log in to your Trello account. - - **Name**: Choose a name for your integration (e.g. "Kan Import") - - **Workspace**: Select the workspace containing the boards you want to import - - **Email**: Enter your email address - - **Support email**: Enter your email address - - **Author**: Add your name +3. **Authorize Kan.bn for Trello** + Approve the permissions and you will be redirected back to Kan.bn. -3. **Get a Trello API Key** - Now click **Generate a new API key** to create an API key. - -4. **Generate a Trello Token** - On the same page (to the right of the API key), click the link to **generate a Token**. Approve the permissions and copy your token (make sure to save it somewhere secure like a password manager). +4. **Verify the connection** + You should see a **Disconnect Trello** button. ## Importing your Trello boards -Once you have your API key and token, importing your Trello boards is easy. Follow these steps: +Once you have authorized Kan.bn for Trello, importing your Trello boards is easy. Follow these steps: 1. **Create a new import in Kan** Navigate to **[https://kan.bn/boards](https://kan.dev/boards)** and click **Import**. @@ -35,8 +29,5 @@ Once you have your API key and token, importing your Trello boards is easy. Foll 2. **Select Trello as the Import Source** Choose **Trello** from the list of import options and click **Select source**. -3. **Enter Your Trello API Key and Token** - Paste your API key and token into the provided fields and click **Fetch boards** (don't worry, we don't store your key or token). - -4. **Start the Import** +3. **Start the Import** Select the board you want to import and click **Import boards**. Once the import is complete, you'll see your boards in Kan. diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index 2eca268d..ad42bbf0 100644 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -22,6 +22,8 @@ export const env = createEnv({ ) .optional(), POSTGRES_URL: z.string().url(), + TRELLO_APP_API_KEY: z.string().optional(), + TRELLO_APP_SECRET: z.string().optional(), STRIPE_SECRET_KEY: z.string().optional(), GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_SECRET: z.string().optional(), diff --git a/apps/web/src/pages/api/auth/[...all].ts b/apps/web/src/pages/api/auth/[...all].ts index 7c619619..f81033aa 100644 --- a/apps/web/src/pages/api/auth/[...all].ts +++ b/apps/web/src/pages/api/auth/[...all].ts @@ -5,6 +5,6 @@ import { createDrizzleClient } from "@kan/db/client"; export const config = { api: { bodyParser: false } }; -const auth = initAuth(createDrizzleClient()); +export const auth = initAuth(createDrizzleClient()); export default toNodeHandler(auth.handler); diff --git a/apps/web/src/pages/api/trello/authenticate.ts b/apps/web/src/pages/api/trello/authenticate.ts new file mode 100644 index 00000000..50cbb9b9 --- /dev/null +++ b/apps/web/src/pages/api/trello/authenticate.ts @@ -0,0 +1,51 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { createNextApiContext } from "@kan/api/trpc"; +import { integrations } from "@kan/db/schema"; +import { addYears } from "date-fns"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + if (req.method !== "POST") { + return res.status(405).json({ message: "Method not allowed" }); + } + + const { user } = await createNextApiContext(req); + + if (!user) + return res.status(401).json({ message: "User not authenticated" }); + + const apiKey = process.env.TRELLO_APP_API_KEY; + + if (!apiKey) + return res.status(500).json({ message: "Trello API key not set in Environment Variables" }); + + const token = req.body.token; + + if (!token) + return res.status(400).json({ message: "No token found" }); + + try { + const { db } = await createNextApiContext(req); + + await db.insert(integrations).values({ + provider: "trello", + userId: user.id, + accessToken: token, + expiresAt: addYears(new Date(), 1), + }).onConflictDoUpdate({ + set: { + accessToken: token, + expiresAt: addYears(new Date(), 1), + }, + target: [integrations.userId, integrations.provider], + }); + + return res.status(200).json({ message: "Trello authentication successful" }); + } catch (err) { + console.error("Trello authentication error:", err); + return res.status(400).json({ message: "Trello authentication failed" }); + } +} \ No newline at end of file diff --git a/apps/web/src/pages/settings/trello/authorize.tsx b/apps/web/src/pages/settings/trello/authorize.tsx new file mode 100644 index 00000000..bada6e5c --- /dev/null +++ b/apps/web/src/pages/settings/trello/authorize.tsx @@ -0,0 +1,24 @@ +import { useEffect } from "react"; + +export default function TrelloAuthorize() { + useEffect(() => { + const hash = window.location.hash; + const token = hash.split("=")[1]; + if (token) { + console.log("Posting token to /api/trello/authenticate", token); + fetch("/api/trello/authenticate", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ token }), + }).then(() => { + window.close(); + }); + } + }, []); + + return
Connecting to Trello...
+