Skip to content

Commit

Permalink
feat: add api key feature
Browse files Browse the repository at this point in the history
planning to add automating and a backend to get any request from external tool and automate the work
  • Loading branch information
vignesh-gupta committed Jun 11, 2024
1 parent ce19945 commit ebbb872
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 8 deletions.
69 changes: 61 additions & 8 deletions app/(main)/dashboard/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,65 @@
import { Button } from '@/components/ui/button'
import React from 'react'
"use client";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { api } from "@/convex/_generated/api";
import useApiMutation from "@/lib/hooks/use-api-mutation";
import { useQuery } from "convex/react";
import { Copy, RefreshCcw, Trash } from "lucide-react";
import { toast } from "sonner";

const AccountSettingsPage = () => {
const existingAPIKey = useQuery(api.api_key.get);

const { mutate: createAPIKey, isPending } = useApiMutation(
api.api_key.create
);

const { mutate: revokeAPIKey, isPending: isRevoking } = useApiMutation(
api.api_key.revoke
);

const handleCopy = () => {
navigator.clipboard
.writeText(existingAPIKey?.key || "")
.then(() => toast.success("API key copied to clipboard"))
.catch(() => toast.error("Failed to copy API key to clipboard"));
};

const handleCreate = () => {
createAPIKey()
.then(() => toast.success("API key generated"))
.catch(() => toast.error("Failed to generate API key"));
};

const handleRevoke = () => {
revokeAPIKey()
.then(() => toast.success("API key revoked"))
.catch(() => toast.error("Failed to revoke API key"));
};

return (
<div>
<Button>Generate a token</Button>
</div>
)
}
<>
{existingAPIKey ? (
<div className="flex gap-3 items-center">
<Input value={existingAPIKey.key} disabled className="w-auto" />
<Button onClick={handleCopy} disabled={isPending} size="icon">
<Copy />
</Button>
<Button onClick={handleCreate} disabled={isPending} size="icon">
<RefreshCcw />
</Button>
<Button onClick={handleRevoke} disabled={isRevoking} size="icon">
<Trash />
</Button>
</div>
) : (
<Button onClick={handleCreate} disabled={isPending}>
Generate a token
</Button>
)}
</>
);
};

export default AccountSettingsPage
export default AccountSettingsPage;
2 changes: 2 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
FilterApi,
FunctionReference,
} from "convex/server";
import type * as api_key from "../api_key.js";
import type * as http from "../http.js";
import type * as message from "../message.js";
import type * as project from "../project.js";
Expand All @@ -35,6 +36,7 @@ import type * as work_item from "../work_item.js";
* ```
*/
declare const fullApi: ApiFromModules<{
api_key: typeof api_key;
http: typeof http;
message: typeof message;
project: typeof project;
Expand Down
85 changes: 85 additions & 0 deletions convex/api_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { mutation, query } from "./_generated/server";

export const create = mutation({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new Error("Unauthenticated user cannot create messages");
}

const user = await ctx.db
.query("users")
.withIndex("by_clerk", (q) => q.eq("clerkId", identity.subject))
.first();

if (!user) {
throw new Error("User not found");
}

const existingKey = await ctx.db
.query("api_keys")
.withIndex("by_user", (q) => q.eq("userId", user._id))
.first();

if (existingKey) {
ctx.db.delete(existingKey._id);
}

return ctx.db.insert("api_keys", {
userId: user._id,
key: Math.random().toString(36).substring(2),
});
},
});

export const revoke = mutation({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new Error("Unauthenticated user cannot create messages");
}

const user = await ctx.db
.query("users")
.withIndex("by_clerk", (q) => q.eq("clerkId", identity.subject))
.first();

if (!user) {
throw new Error("User not found");
}

const key = await ctx.db
.query("api_keys")
.withIndex("by_user", (q) => q.eq("userId", user._id))
.first();

if (!key) {
throw new Error("API key not found");
}

return ctx.db.delete(key._id);
},
});

export const get = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) {
throw new Error("Unauthenticated user cannot create messages");
}

const user = await ctx.db
.query("users")
.withIndex("by_clerk", (q) => q.eq("clerkId", identity.subject))
.first();

if (!user) {
throw new Error("User not found");
}

return ctx.db
.query("api_keys")
.withIndex("by_user", (q) => q.eq("userId", user?._id))
.first();
},
});
5 changes: 5 additions & 0 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,9 @@ export default defineSchema({
senderName: v.string(),
senderImageUrl: v.string(),
}).index("by_project", ["projectId"]),

api_keys: defineTable({
userId: v.id("users"),
key: v.string(),
}).index("by_user", ["userId"]),
});

0 comments on commit ebbb872

Please sign in to comment.