diff --git a/web-plugin/index.js b/web-plugin/index.js index 7d22642..967bd30 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -126,16 +126,23 @@ function addButton() { console.log(graph); console.log(prompt); + const endpoint = localStorage.getItem("endpoint"); + const apiKey = localStorage.getItem("apiKey"); + + if (!endpoint || !apiKey) { + configDialog.show(); + return; + } + deploy.textContent = "Deploying..."; deploy.style.color = "orange"; - const apiRoute = "http://localhost:3000/api/upload"; - const userId = "user_2ZA6vuKD3IJXju16oJVQGLBcWwg"; + const apiRoute = endpoint + "/api/upload" + // const userId = apiKey try { let data = await fetch(apiRoute, { method: "POST", body: JSON.stringify({ - user_id: userId, workflow_name, workflow_id, workflow: prompt.workflow, @@ -143,6 +150,7 @@ function addButton() { }), headers: { "Content-Type": "application/json", + "Authorization": "Bearer " + apiKey, }, }); @@ -181,7 +189,15 @@ function addButton() { } }; + const config = document.createElement("button"); + config.textContent = "Config"; + config.className = "configbtn"; + config.onclick = () => { + configDialog.show(); + }; + menu.append(deploy); + menu.append(config); } app.registerExtension(ext); @@ -220,4 +236,57 @@ export class InfoDialog extends ComfyDialog { } } -export const infoDialog = new InfoDialog() \ No newline at end of file +export const infoDialog = new InfoDialog() + +export class ConfigDialog extends ComfyDialog { + constructor() { + super(); + this.element.classList.add("comfy-normal-modal"); + } + + createButtons() { + return [ + $el("button", { + type: "button", + textContent: "Save", + onclick: () => this.save(), + }), + $el("button", { + type: "button", + textContent: "Close", + onclick: () => this.close(), + }), + ]; + } + + close() { + this.element.style.display = "none"; + } + + save() { + const endpoint = this.textElement.querySelector("#endpoint").value; + const apiKey = this.textElement.querySelector("#apiKey").value; + localStorage.setItem("endpoint", endpoint); + localStorage.setItem("apiKey", apiKey); + this.close(); + } + + show() { + this.textElement.innerHTML = ` +
+ + +
+ `; + this.element.style.display = "flex"; + this.element.style.zIndex = 1001; + } +} + +export const configDialog = new ConfigDialog(); \ No newline at end of file diff --git a/web/.env.example b/web/.env.example index 1127d75..a398c8f 100644 --- a/web/.env.example +++ b/web/.env.example @@ -15,4 +15,6 @@ SPACES_ENDPOINT_CDN="http://localhost:4566" SPACES_REGION="nyc3" SPACES_BUCKET="comfyui-deploy" SPACES_KEY="xyz" -SPACES_SECRET="aaa" \ No newline at end of file +SPACES_SECRET="aaa" + +JWT_SECRET="openssl rand -hex 32" \ No newline at end of file diff --git a/web/bun.lockb b/web/bun.lockb index 44a53d6..bd05d75 100755 Binary files a/web/bun.lockb and b/web/bun.lockb differ diff --git a/web/package.json b/web/package.json index 9f6f6d3..b7d8df7 100644 --- a/web/package.json +++ b/web/package.json @@ -28,11 +28,13 @@ "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tabs": "^1.0.4", "@tanstack/react-table": "^8.10.7", + "@types/jsonwebtoken": "^9.0.5", "@types/uuid": "^9.0.7", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "dayjs": "^1.11.10", "drizzle-orm": "^0.29.1", + "jsonwebtoken": "^9.0.2", "lucide-react": "^0.294.0", "nanoid": "^5.0.4", "next": "14.0.3", diff --git a/web/src/app/api/upload/route.ts b/web/src/app/api/upload/route.ts index 139d48d..a8d71f3 100644 --- a/web/src/app/api/upload/route.ts +++ b/web/src/app/api/upload/route.ts @@ -7,6 +7,7 @@ import { } from "@/db/schema"; import { parseDataSafe } from "@/lib/parseDataSafe"; import { sql } from "drizzle-orm"; +import jwt from "jsonwebtoken"; import { NextResponse } from "next/server"; import { z } from "zod"; @@ -17,7 +18,7 @@ const corsHeaders = { }; const UploadRequest = z.object({ - user_id: z.string(), + // user_id: z.string(), workflow_id: z.string().optional(), workflow_name: z.string().optional(), workflow: workflowType, @@ -35,8 +36,39 @@ export async function OPTIONS(request: Request) { }); } +const APIKeyBodyRequest = z.object({ + user_id: z.string().optional(), + org_id: z.string().optional(), + iat: z.number(), +}); + +function parseJWT(token: string) { + try { + // Verify the token - this also decodes it + const decoded = jwt.verify(token, process.env.JWT_SECRET!); + return APIKeyBodyRequest.parse(decoded); + } catch (err) { + // Handle error (token is invalid, expired, etc.) + console.error(err); + return null; + } +} + export async function POST(request: Request) { - console.log("hi"); + const token = request.headers.get("Authorization")?.split(" ")?.[1]; // Assuming token is sent as "Bearer your_token" + const userData = token ? parseJWT(token) : undefined; + if (!userData) { + return new NextResponse("Invalid or expired token", { + status: 401, + headers: corsHeaders, + }); + } + + console.log(userData); + + const { user_id, org_id } = userData; + + if (!user_id) return new NextResponse("Invalid user_id", { status: 401 }); const [data, error] = await parseDataSafe( UploadRequest, @@ -47,7 +79,7 @@ export async function POST(request: Request) { if (!data || error) return error; const { - user_id, + // user_id, workflow, workflow_api, workflow_id: _workflow_id, diff --git a/web/src/server/curdApiKeys.ts b/web/src/server/curdApiKeys.ts index d55bc47..0dbbf44 100644 --- a/web/src/server/curdApiKeys.ts +++ b/web/src/server/curdApiKeys.ts @@ -4,31 +4,42 @@ import { db } from "@/db/db"; import { apiKeyTable } from "@/db/schema"; import { auth } from "@clerk/nextjs"; import { and, desc, eq } from "drizzle-orm"; -import { customAlphabet } from "nanoid"; +import jwt from "jsonwebtoken"; import { revalidatePath } from "next/cache"; -export const nanoid = customAlphabet( - "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" -); +// export const nanoid = customAlphabet( +// "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +// ); -const prefixes = { - cd: "cd", -} as const; +// const prefixes = { +// cd: "cd", +// } as const; -function newId(prefix: keyof typeof prefixes): string { - return [prefixes[prefix], nanoid(16)].join("_"); -} +// function newId(prefix: keyof typeof prefixes): string { +// return [prefixes[prefix], nanoid(16)].join("_"); +// } export async function addNewAPIKey(name: string) { const { userId, orgId } = auth(); if (!userId) throw new Error("No user id"); + let token: string; + + if (orgId) { + token = jwt.sign( + { user_id: userId, org_id: orgId }, + process.env.JWT_SECRET! + ); + } else { + token = jwt.sign({ user_id: userId }, process.env.JWT_SECRET!); + } + const key = await db .insert(apiKeyTable) .values({ name: name, - key: newId("cd"), + key: token, user_id: userId, org_id: orgId, })