Skip to content

Commit

Permalink
feat(account): add account deletion action, create delete account but…
Browse files Browse the repository at this point in the history
…ton and form components, fix API limit bug
  • Loading branch information
phukon committed Mar 24, 2024
1 parent 64e4165 commit cc524e2
Show file tree
Hide file tree
Showing 16 changed files with 360 additions and 46 deletions.
61 changes: 37 additions & 24 deletions clack-cloudfare-worker/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
*/

export interface Env {

clackkv: KVNamespace;
clackkv: KVNamespace;
SECURITY_KEY: string;
API_HOST: string;
API_HOST: string;
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
// MY_KV_NAMESPACE: KVNamespace;
//
Expand All @@ -31,19 +30,22 @@ export interface Env {

export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (!isAuthorized(request, env)) {
return new Response('Unauthorized', { status: 401 });
}

if (!isAuthorized(request, env)) {
return new Response('Unauthorized', { status: 401 });
}


const reqUrl = new URL(request.url);
const key = reqUrl.searchParams.get('key');
const getAllFromUser = reqUrl.searchParams.get('getAllFromUser');

if(reqUrl.searchParams.has('sayonara')) {
return await deleteEverything(env)
}

if (reqUrl.searchParams.has('sayonara')) {
return await deleteEverything(env);
}

if (reqUrl.searchParams.has('deleteAllUserNotes')) {
const userEmail = reqUrl.searchParams.get('deleteAllUserNotes');
return await deleteAllNotesOfUser(env, userEmail!);
}

if (getAllFromUser) {
return await getAllKeys(env, getAllFromUser);
Expand Down Expand Up @@ -98,17 +100,28 @@ async function handleDelete(env: Env, key: string): Promise<Response> {
}

async function handleGet(env: Env, key: string): Promise<Response> {
const data = await env.clackkv.get(key);
if (!data) {
return new Response('Not found', { status: 404 });
}
return new Response(data);
const data = await env.clackkv.get(key);
if (!data) {
return new Response('Not found', { status: 404 });
}
return new Response(data);
}

async function deleteEverything(env: Env): Promise<Response> {
const keys = await env.clackkv.list();
for (const key of keys.keys) {
await env.clackkv.delete(key.name.toString());
}
return new Response('Deleted everything', { status: 200 });
}

async function deleteEverything(env: Env): Promise<Response>{
const keys = await env.clackkv.list();
for (const key of keys.keys) {
await env.clackkv.delete(key.name.toString());
}
return new Response('Deleted everything', {status: 200})
}
async function deleteAllNotesOfUser(env: Env, userEmail: string): Promise<Response> {
const prefix = userEmail + '-';
const keys = await env.clackkv.list({ prefix });

for (const key of keys.keys) {
await env.clackkv.delete(key.name);
}

return new Response('Deleted all notes of the user', { status: 200 });
}
16 changes: 16 additions & 0 deletions prisma/migrations/20240324064402_api_limits/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "UserApiLimit" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"count" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "UserApiLimit_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "UserApiLimit_userId_key" ON "UserApiLimit"("userId");

-- AddForeignKey
ALTER TABLE "UserApiLimit" ADD CONSTRAINT "UserApiLimit_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
34 changes: 23 additions & 11 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ model User {
years Year[]
contributions Contribution[]
notes Note[]
userApiLimit UserApiLimit[]
wordCountRef Int?
notionDetails Json? // The Notion O-Auth response containing access token that is received after posting the temporary code to the auth url.
notionDetails Json? // The Notion O-Auth response containing access token that is received after posting the temporary code to the auth url.
// Stripe
stripeCustomerId String? @unique @map(name: "stripe_customer_id")
Expand All @@ -101,23 +102,34 @@ model User {
stripePaymentDate DateTime?
}

model UserApiLimit {
id String @id @default(cuid())
userId String @unique
count Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

enum NoteType {
CLACK
NOTION
GOOGLEDOC
}

model Note {
id String @id @default(cuid())
name String?
type NoteType @default(CLACK)
url String?
wordCount Int?
userId String
data Json? // I may use an edge KV store
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
id String @id @default(cuid())
name String?
type NoteType @default(CLACK)
url String?
wordCount Int?
userId String
data Json? // I may use an edge KV store
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([url])
}

Expand Down
40 changes: 40 additions & 0 deletions src/actions/auth/deleteAccount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use server";

import { getUserById } from "@/data/user";
import { currentUser } from "@/lib/auth";
import { db } from "@/lib/db";

export const deleteAccount = async () => {
try {
const user = await currentUser();
if (!user || !user.id) {
throw new Error("Unauthorized: User not authenticated.");
}

const dbUser = await getUserById(user.id);
if (!dbUser) {
throw new Error("Unauthorized: User not found in the database.");
}

await db.user.delete({
where: { id: dbUser.id },
});

const getResponse = await fetch(
`${process.env.WORKER_BASE_URL}?deleteAllUserNotes=${dbUser.email}`,
{
method: "GET",
headers: {
"X-Custom-Auth-Key": `${process.env.SECURITY_KEY}`,
},
}
);

const data = await getResponse.text();
console.log(data);

return { success: "Your account and data has been removed from Clack." };
} catch (e: any) {
return { error: `There was a problem deleting your account ${e.message} ` };
}
};
6 changes: 6 additions & 0 deletions src/actions/auth/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export const register = async (values: z.infer<typeof RegisterSchema>) => {
},
});

await db.userApiLimit.create({
data: {
userId: user.id,
},
});

const currentYear = new Date().getFullYear();

const createdYear = await db.year.create({
Expand Down
11 changes: 2 additions & 9 deletions src/app/(static)/link-notion-page/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,8 @@ import { RiNotionFill } from "react-icons/ri";
import {
Plus as PlusSmallIcon,
Minus as MinusSmallIcon,
RefreshCw as ArrowPathIcon,
GitPullRequestArrow as CloudArrowUpIcon,
Settings as Cog6ToothIcon,
Fingerprint as FingerPrintIcon,
Lock as LockClosedIcon,
HardDrive as ServerIcon,
} from "lucide-react";
import Link from "next/link";
import Image from "next/image";

const faqs = [
{
Expand Down Expand Up @@ -64,7 +57,7 @@ export default function Home() {
Link a Notion Page
</h1>
<p className="mt-6 text-lg leading-8 text-gray-500">
Connect the Clack integration Notion and add the page link in{" "}
Connect the Clack Notion integration and add the page link in{" "}
<span className=" --local-comfortaa">Clack</span> dashboard.
</p>
{/* <div className="mt-10 flex items-center justify-center gap-x-6">
Expand Down Expand Up @@ -132,7 +125,7 @@ export default function Home() {
</div> */}
</div>
<p className=" mt-10 text-center leading-8 text-gray-500 font-medium">
A prompt apperas from Notion, it describes the integration&apos;s capabilities.
A prompt appears from Notion, it describes the integration&apos;s capabilities.
</p>
</div>

Expand Down
16 changes: 14 additions & 2 deletions src/app/api/generate/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import OpenAI from "openai";
import { OpenAIStream, StreamingTextResponse } from "ai";
import { kv } from "@vercel/kv";
import { Ratelimit } from "@upstash/ratelimit";
import { checkApiLimit, incrementApiLimit } from "@/lib/api-limit";

// Create an OpenAI API client (that's edge friendly!)
const openai = new OpenAI({
Expand Down Expand Up @@ -61,17 +62,28 @@ export async function POST(req: Request): Promise<Response> {

// const subscriptionPlan = await getUserSubscriptionPlan();
const isPaidUser = await getUserPaymentStatus();
const freeTrial = await checkApiLimit();

if (typeof isPaidUser === "boolean") {
// Handle boolean response
if (!isPaidUser) {
return new Response("Upgrade to Clack Pro to use the AI writing assistant ✨", {
if (!freeTrial) {
return new Response("Upgrade to Clack Pro to use the AI writing assistant ✨", {
status: 200,
});
} else {
await incrementApiLimit();
}
} else if (!freeTrial) {
return new Response("Buy credits to use the AI writing assistant ✨", {
status: 200,
});
} else {
await incrementApiLimit();
}
} else {
return new Response("Error: " + isPaidUser.error, {
status: 200,
status: 403,
});
}

Expand Down
1 change: 1 addition & 0 deletions src/app/api/note/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export async function DELETE(req: Request): Promise<Response> {
});

const data = await deleteResponse.text();

console.log(data);
if (deleteResponse.status !== 200) {
return new Response(data, {
Expand Down
7 changes: 7 additions & 0 deletions src/app/api/webhooks/stripePaymentIntent/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ export async function POST(request: Request) {
stripePaymentDate: paymentDate,
},
});

await db.userApiLimit.update({
where: {
id: userId,
},
data: { count: 50 },
});
}

return new Response(null, { status: 200 });
Expand Down
4 changes: 4 additions & 0 deletions src/app/dash/settings/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { useCurrentUser } from "@/hooks/use-current-user";
import FormError from "@/components/form-error";
import FormSuccess from "@/components/form-success";
import Navbar from "../navbar";
import { DeleteAccountButton } from "@/components/deleteAccount/DeleteAccountButton";

export default function Edit() {
const user = useCurrentUser();
Expand Down Expand Up @@ -208,6 +209,9 @@ export default function Edit() {
</Form>
</CardContent>
</Card>
<div className=" mt-5">
<DeleteAccountButton>Delete your account</DeleteAccountButton>
</div>
</>
);
}
6 changes: 6 additions & 0 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export const {
data: { emailVerified: new Date(), wordCountRef: 0 },
});

await db.userApiLimit.create({
data: {
userId: user.id!,
},
});

const currentYear = new Date().getFullYear();
const createdYear = await db.year.create({
data: {
Expand Down
26 changes: 26 additions & 0 deletions src/components/deleteAccount/DeleteAccountButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
import { DeleteAccountForm } from "./DeleteAccountForm";

interface LoginButtonProps {
children: React.ReactNode;
mode?: "modal" | "redirect";
asChild?: boolean;
}

export const DeleteAccountButton = ({ children, asChild }: LoginButtonProps) => {
return (
<Dialog>
<DialogTrigger
className="inline-flex border-2 border-red-400 rounded-lg px-3 items-center justify-center whitespace-nowrap text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border-input bg-background hover:bg-accent hover:text-accent-foreground"
asChild={asChild}
>
{children}
</DialogTrigger>
<DialogContent className="p-0 w-auto bg-transparent border-none">
<DeleteAccountForm />
</DialogContent>
</Dialog>
);
};
Loading

0 comments on commit cc524e2

Please sign in to comment.