feat: add new auth_request flow for logging in with comfy deploy
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
export const nanoid = customAlphabet(
|
||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||
);
|
||||
const prefixes = {
|
||||
img: "img",
|
||||
vid: "vid",
|
||||
} as const;
|
||||
|
||||
export function newId(prefix: keyof typeof prefixes): string {
|
||||
return [prefixes[prefix], nanoid(16)].join("_");
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createRun } from "../server/createRun";
|
||||
import { db } from "@/db/db";
|
||||
import { deploymentsTable } from "@/db/schema";
|
||||
import type { App } from "@/routes/app";
|
||||
import { authError } from "@/routes/authError";
|
||||
import { z, createRoute } from "@hono/zod-openapi";
|
||||
import { createRoute, z } from "@hono/zod-openapi";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { createRun } from "../server/createRun";
|
||||
|
||||
const createRunRoute = createRoute({
|
||||
method: "post",
|
||||
@@ -99,7 +99,7 @@ export const registerCreateRunRoute = (app: App) => {
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import { db } from "@/db/db";
|
||||
import { authRequestsTable } from "@/db/schema";
|
||||
import type { App } from "@/routes/app";
|
||||
import { authError } from "@/routes/authError";
|
||||
import { z, createRoute } from "@hono/zod-openapi";
|
||||
import { eq } from "drizzle-orm";
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
import { getOrgOrUserDisplayName } from "@/server/getOrgOrUserDisplayName";
|
||||
import ms from "ms";
|
||||
|
||||
const route = createRoute({
|
||||
method: "get",
|
||||
path: "/auth-response/:request_id",
|
||||
tags: ["comfyui"],
|
||||
summary: "Get an API Key with code",
|
||||
description:
|
||||
"This endpoints is specifically built for ComfyUI workflow upload.",
|
||||
request: {
|
||||
params: z.object({
|
||||
request_id: z.string(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
api_key: z.string(),
|
||||
name: z.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "The returned API Key",
|
||||
},
|
||||
201: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
message: z.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "The API key is not yet ready",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
error: z.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Error when fetching the API Key with code",
|
||||
},
|
||||
...authError,
|
||||
},
|
||||
});
|
||||
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
};
|
||||
|
||||
export const registerGetAuthResponse = (app: App) => {
|
||||
return app.openapi(route, async (c) => {
|
||||
const { request_id } = c.req.valid("param");
|
||||
|
||||
try {
|
||||
const result = await db.query.authRequestsTable.findFirst({
|
||||
where: eq(authRequestsTable.request_id, request_id),
|
||||
});
|
||||
|
||||
if (result?.api_hash) {
|
||||
return c.json(
|
||||
{
|
||||
message: "Already used.",
|
||||
},
|
||||
{
|
||||
status: 201,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (result && result.user_id) {
|
||||
const expireTime = "1w";
|
||||
const token = jwt.sign(
|
||||
{ user_id: result.user_id, org_id: result.org_id },
|
||||
process.env.JWT_SECRET!,
|
||||
{
|
||||
expiresIn: expireTime,
|
||||
},
|
||||
);
|
||||
|
||||
const hash = crypto.createHash("sha256").update(token).digest("hex");
|
||||
|
||||
const now = new Date();
|
||||
const expiryDate = new Date(now.getTime() + ms(expireTime));
|
||||
|
||||
await db
|
||||
.update(authRequestsTable)
|
||||
.set({
|
||||
api_hash: hash,
|
||||
expired_date: expiryDate,
|
||||
})
|
||||
.where(eq(authRequestsTable.request_id, request_id));
|
||||
|
||||
const userName = await getOrgOrUserDisplayName(
|
||||
result.org_id,
|
||||
result.user_id,
|
||||
);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
api_key: token,
|
||||
name: userName,
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
return c.json(
|
||||
{
|
||||
error: errorMessage,
|
||||
},
|
||||
{
|
||||
statusText: "Invalid request",
|
||||
status: 500,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
}
|
||||
return c.json(
|
||||
{
|
||||
message: "Not ready yet.",
|
||||
},
|
||||
{
|
||||
status: 201,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -3,20 +3,7 @@ import { authError } from "@/routes/authError";
|
||||
import { getFileDownloadUrl } from "@/server/getFileDownloadUrl";
|
||||
import { handleResourceUpload } from "@/server/resource";
|
||||
import { z, createRoute } from "@hono/zod-openapi";
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
export const nanoid = customAlphabet(
|
||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||
);
|
||||
|
||||
const prefixes = {
|
||||
img: "img",
|
||||
vid: "vid",
|
||||
} as const;
|
||||
|
||||
export function newId(prefix: keyof typeof prefixes): string {
|
||||
return [prefixes[prefix], nanoid(16)].join("_");
|
||||
}
|
||||
import { newId } from "./newId";
|
||||
|
||||
const uploadUrlRoute = createRoute({
|
||||
method: "get",
|
||||
@@ -96,7 +83,7 @@ export const registerUploadRoute = (app: App) => {
|
||||
file_id: id,
|
||||
download_url: await getFileDownloadUrl(filePath),
|
||||
},
|
||||
200
|
||||
200,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
@@ -107,7 +94,7 @@ export const registerUploadRoute = (app: App) => {
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import { snapshotType, workflowAPIType, workflowType } from "@/db/schema";
|
||||
import type { App } from "@/routes/app";
|
||||
import { authError } from "@/routes/authError";
|
||||
import {
|
||||
createNewWorkflow,
|
||||
createNewWorkflowVersion,
|
||||
} from "@/server/createNewWorkflow";
|
||||
import { z, createRoute } from "@hono/zod-openapi";
|
||||
|
||||
const route = createRoute({
|
||||
method: "post",
|
||||
path: "/upload-workflow",
|
||||
tags: ["comfyui"],
|
||||
summary: "Upload workflow from ComfyUI",
|
||||
description:
|
||||
"This endpoints is specifically built for ComfyUI workflow upload.",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
workflow_id: z.string().optional(),
|
||||
workflow_name: z.string().min(1).optional(),
|
||||
workflow: workflowType,
|
||||
workflow_api: workflowAPIType,
|
||||
snapshot: snapshotType,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
workflow_id: z.string(),
|
||||
version: z.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Retrieve the output",
|
||||
},
|
||||
500: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.object({
|
||||
error: z.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
description: "Error when uploading the workflow",
|
||||
},
|
||||
...authError,
|
||||
},
|
||||
});
|
||||
|
||||
const corsHeaders = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
};
|
||||
|
||||
export const registerWorkflowUploadRoute = (app: App) => {
|
||||
app.openapi(route, async (c) => {
|
||||
const {
|
||||
// user_id,
|
||||
workflow,
|
||||
workflow_api,
|
||||
workflow_id: _workflow_id,
|
||||
workflow_name,
|
||||
snapshot,
|
||||
} = c.req.valid("json");
|
||||
const { org_id, user_id } = c.get("apiKeyTokenData")!;
|
||||
|
||||
if (!user_id)
|
||||
return c.json(
|
||||
{
|
||||
error: "Invalid user_id",
|
||||
},
|
||||
{
|
||||
headers: corsHeaders,
|
||||
status: 500,
|
||||
},
|
||||
);
|
||||
|
||||
let workflow_id = _workflow_id;
|
||||
|
||||
let version = -1;
|
||||
|
||||
try {
|
||||
if ((!workflow_id || workflow_id.length === 0) && workflow_name) {
|
||||
// Create a new parent workflow
|
||||
const { workflow_id: _workflow_id, version: _version } =
|
||||
await createNewWorkflow({
|
||||
user_id: user_id,
|
||||
org_id: org_id,
|
||||
workflow_name: workflow_name,
|
||||
workflowData: {
|
||||
workflow,
|
||||
workflow_api,
|
||||
snapshot,
|
||||
},
|
||||
});
|
||||
|
||||
workflow_id = _workflow_id;
|
||||
version = _version;
|
||||
} else if (workflow_id) {
|
||||
// Case 2 update workflow
|
||||
const { version: _version } = await createNewWorkflowVersion({
|
||||
workflow_id: workflow_id,
|
||||
workflowData: {
|
||||
workflow,
|
||||
workflow_api,
|
||||
snapshot,
|
||||
},
|
||||
});
|
||||
version = _version;
|
||||
} else {
|
||||
return c.json(
|
||||
{
|
||||
error: "Invalid request, missing either workflow_id or name",
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
statusText: "Invalid request",
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
return c.json(
|
||||
{
|
||||
error: errorMessage,
|
||||
},
|
||||
{
|
||||
statusText: "Invalid request",
|
||||
status: 500,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return c.json(
|
||||
{
|
||||
workflow_id: workflow_id,
|
||||
version: version,
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
headers: corsHeaders,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
app.route("/upload-workflow").options(async (c) => {
|
||||
return new Response(null, {
|
||||
status: 204,
|
||||
headers: corsHeaders,
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user