Skip to content

Commit ed93f6a

Browse files
authored
Merge branch 'main' into brandfetch-plugin
2 parents d82911e + 05a05af commit ed93f6a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+7432
-1371
lines changed

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
# testing
1414
/coverage
15+
/test-results/
16+
/playwright-report/
17+
/blob-report/
18+
/playwright/.cache/
1519

1620
# next.js
1721
/.next/
@@ -47,4 +51,5 @@ tmp/
4751
# generated files
4852
lib/types/integration.ts
4953
lib/codegen-registry.ts
50-
lib/step-registry.ts
54+
lib/step-registry.ts
55+
lib/output-display-configs.ts

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Visit [http://localhost:3000](http://localhost:3000) to get started.
8181
<!-- PLUGINS:START - Do not remove. Auto-generated by discover-plugins -->
8282
- **AI Gateway**: Generate Text, Generate Image
8383
- **Blob**: Put Blob, List Blobs
84+
- **Clerk**: Get User, Create User, Update User, Delete User
8485
- **fal.ai**: Generate Image, Generate Video, Upscale Image, Remove Background, Image to Image
8586
- **Firecrawl**: Scrape URL, Search Web
8687
- **GitHub**: Create Issue, List Issues, Get Issue, Update Issue
@@ -292,7 +293,7 @@ This template is built on top of Workflow DevKit, a powerful workflow execution
292293
- Built-in logging and error handling
293294
- Serverless deployment support
294295

295-
Learn more about Workflow DevKit at [workflow.dev](https://useworkflow.dev)
296+
Learn more about Workflow DevKit at [useworkflow.dev](https://useworkflow.dev)
296297

297298
## License
298299

app/api/api-keys/[keyId]/route.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { and, eq } from "drizzle-orm";
2+
import { NextResponse } from "next/server";
3+
import { auth } from "@/lib/auth";
4+
import { db } from "@/lib/db";
5+
import { apiKeys } from "@/lib/db/schema";
6+
7+
// DELETE - Delete an API key
8+
export async function DELETE(
9+
request: Request,
10+
context: { params: Promise<{ keyId: string }> }
11+
) {
12+
try {
13+
const { keyId } = await context.params;
14+
const session = await auth.api.getSession({
15+
headers: request.headers,
16+
});
17+
18+
if (!session?.user) {
19+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
20+
}
21+
22+
// Delete the key (only if it belongs to the user)
23+
const result = await db
24+
.delete(apiKeys)
25+
.where(and(eq(apiKeys.id, keyId), eq(apiKeys.userId, session.user.id)))
26+
.returning({ id: apiKeys.id });
27+
28+
if (result.length === 0) {
29+
return NextResponse.json({ error: "API key not found" }, { status: 404 });
30+
}
31+
32+
return NextResponse.json({ success: true });
33+
} catch (error) {
34+
console.error("Failed to delete API key:", error);
35+
return NextResponse.json(
36+
{ error: "Failed to delete API key" },
37+
{ status: 500 }
38+
);
39+
}
40+
}

app/api/api-keys/route.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { createHash, randomBytes } from "node:crypto";
2+
import { eq } from "drizzle-orm";
3+
import { NextResponse } from "next/server";
4+
import { auth } from "@/lib/auth";
5+
import { db } from "@/lib/db";
6+
import { apiKeys } from "@/lib/db/schema";
7+
8+
// Generate a secure API key
9+
function generateApiKey(): { key: string; hash: string; prefix: string } {
10+
const randomPart = randomBytes(24).toString("base64url");
11+
const key = `wfb_${randomPart}`;
12+
const hash = createHash("sha256").update(key).digest("hex");
13+
const prefix = key.slice(0, 11); // "wfb_" + first 7 chars
14+
return { key, hash, prefix };
15+
}
16+
17+
// GET - List all API keys for the current user
18+
export async function GET(request: Request) {
19+
try {
20+
const session = await auth.api.getSession({
21+
headers: request.headers,
22+
});
23+
24+
if (!session?.user) {
25+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
26+
}
27+
28+
const keys = await db.query.apiKeys.findMany({
29+
where: eq(apiKeys.userId, session.user.id),
30+
columns: {
31+
id: true,
32+
name: true,
33+
keyPrefix: true,
34+
createdAt: true,
35+
lastUsedAt: true,
36+
},
37+
orderBy: (table, { desc }) => [desc(table.createdAt)],
38+
});
39+
40+
return NextResponse.json(keys);
41+
} catch (error) {
42+
console.error("Failed to list API keys:", error);
43+
return NextResponse.json(
44+
{ error: "Failed to list API keys" },
45+
{ status: 500 }
46+
);
47+
}
48+
}
49+
50+
// POST - Create a new API key
51+
export async function POST(request: Request) {
52+
try {
53+
const session = await auth.api.getSession({
54+
headers: request.headers,
55+
});
56+
57+
if (!session?.user) {
58+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
59+
}
60+
61+
// Check if user is anonymous
62+
const isAnonymous =
63+
session.user.name === "Anonymous" ||
64+
session.user.email?.startsWith("temp-");
65+
66+
if (isAnonymous) {
67+
return NextResponse.json(
68+
{ error: "Anonymous users cannot create API keys" },
69+
{ status: 403 }
70+
);
71+
}
72+
73+
const body = await request.json().catch(() => ({}));
74+
const name = body.name || null;
75+
76+
// Generate new API key
77+
const { key, hash, prefix } = generateApiKey();
78+
79+
// Save to database
80+
const [newKey] = await db
81+
.insert(apiKeys)
82+
.values({
83+
userId: session.user.id,
84+
name,
85+
keyHash: hash,
86+
keyPrefix: prefix,
87+
})
88+
.returning({
89+
id: apiKeys.id,
90+
name: apiKeys.name,
91+
keyPrefix: apiKeys.keyPrefix,
92+
createdAt: apiKeys.createdAt,
93+
});
94+
95+
// Return the full key only on creation (won't be shown again)
96+
return NextResponse.json({
97+
...newKey,
98+
key, // Full key - only returned once!
99+
});
100+
} catch (error) {
101+
console.error("Failed to create API key:", error);
102+
return NextResponse.json(
103+
{ error: "Failed to create API key" },
104+
{ status: 500 }
105+
);
106+
}
107+
}

0 commit comments

Comments
 (0)