diff --git a/package.json b/package.json
index 83a293b..96fb6c1 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,7 @@
"zod": "^3.22.4"
},
"devDependencies": {
+ "@tailwindcss/line-clamp": "^0.4.4",
"@tailwindcss/typography": "^0.5.10",
"@types/node": "20.10.4",
"@types/react": "18.2.45",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ff8a965..051bab7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -109,6 +109,9 @@ dependencies:
version: 3.22.4
devDependencies:
+ '@tailwindcss/line-clamp':
+ specifier: ^0.4.4
+ version: 0.4.4(tailwindcss@3.3.6)
'@tailwindcss/typography':
specifier: ^0.5.10
version: 0.5.10(tailwindcss@3.3.6)
@@ -3935,6 +3938,14 @@ packages:
dependencies:
tslib: 2.6.2
+ /@tailwindcss/line-clamp@0.4.4(tailwindcss@3.3.6):
+ resolution: {integrity: sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==}
+ peerDependencies:
+ tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
+ dependencies:
+ tailwindcss: 3.3.6
+ dev: true
+
/@tailwindcss/typography@0.5.10(tailwindcss@3.3.6):
resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==}
peerDependencies:
diff --git a/src/app/api/auth/login/github/callback/route.ts b/src/app/api/auth/login/github/callback/route.ts
index da9a4a7..cec76f4 100644
--- a/src/app/api/auth/login/github/callback/route.ts
+++ b/src/app/api/auth/login/github/callback/route.ts
@@ -1,9 +1,8 @@
import { OAuthRequestError } from "@lucia-auth/oauth";
import { cookies, headers } from "next/headers";
-import { auth, githubAuth } from "~/lib/auth";
-
import type { NextRequest } from "next/server";
-import { sendMail } from "~/server/actions";
+import { auth, githubAuth } from "~/lib/auth";
+import { sendMail } from "~/lib/resend";
export const GET = async (request: NextRequest) => {
const storedState = cookies().get("github_oauth_state")?.value;
@@ -30,20 +29,16 @@ export const GET = async (request: NextRequest) => {
picture: githubUser.avatar_url,
},
});
- return user;
- };
-
- const user = await getUser();
-
- const existingUser = await getExistingUser();
- if (!existingUser) {
sendMail({
toMail: user.email,
data: {
name: user.name,
},
});
- }
+ return user;
+ };
+
+ const user = await getUser();
const session = await auth.createSession({
userId: user.userId,
diff --git a/src/app/dashboard/billing/page.tsx b/src/app/dashboard/billing/page.tsx
index 6e6ec76..44367ea 100644
--- a/src/app/dashboard/billing/page.tsx
+++ b/src/app/dashboard/billing/page.tsx
@@ -1,18 +1,19 @@
import { AlertTriangleIcon } from "lucide-react";
import { BillingForm } from "~/components/billing-form";
import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";
+import { getPageSession } from "~/lib/auth";
import { stripe } from "~/lib/stripe";
import { getUserSubscriptionPlan } from "~/lib/subscription";
-import { getUser } from "~/server/user";
-import { type CurrentUser } from "~/types";
export const revalidate = 0;
export const dynamic = "force-dynamic";
export default async function Billing() {
- const user = (await getUser()) as CurrentUser;
+ const session = await getPageSession();
- const subscriptionPlan = await getUserSubscriptionPlan(user.id);
+ const subscriptionPlan = await getUserSubscriptionPlan(
+ session?.user?.userId as string
+ );
// If user has a pro plan, check cancel status on Stripe.
let isCanceled = false;
diff --git a/src/app/dashboard/projects/action.ts b/src/app/dashboard/projects/action.ts
index 3483e66..ab0d845 100644
--- a/src/app/dashboard/projects/action.ts
+++ b/src/app/dashboard/projects/action.ts
@@ -3,9 +3,9 @@
import { type Project } from "@prisma/client";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
+import { getPageSession } from "~/lib/auth";
import db from "~/lib/db";
import { getUserSubscriptionPlan } from "~/lib/subscription";
-import { getUser } from "~/server/user";
interface Payload {
name: string;
@@ -13,14 +13,14 @@ interface Payload {
}
export async function createProject(payload: Payload) {
- const user = await getUser();
+ const session = await getPageSession();
await db.project.create({
data: {
...payload,
user: {
connect: {
- id: user?.id,
+ id: session?.user?.userId,
},
},
},
@@ -30,8 +30,10 @@ export async function createProject(payload: Payload) {
}
export async function checkIfFreePlanLimitReached() {
- const user = await getUser();
- const subscriptionPlan = await getUserSubscriptionPlan(user?.id as string);
+ const session = await getPageSession();
+ const subscriptionPlan = await getUserSubscriptionPlan(
+ session?.user?.userId as string
+ );
// If user is on a free plan.
// Check if user has reached limit of 3 projects.
@@ -39,7 +41,7 @@ export async function checkIfFreePlanLimitReached() {
const count = await db.project.count({
where: {
- userId: user?.id,
+ userId: session?.user?.userId,
},
});
@@ -47,10 +49,10 @@ export async function checkIfFreePlanLimitReached() {
}
export async function getProjects() {
- const user = await getUser();
+ const session = await getPageSession();
const projects = await db.project.findMany({
where: {
- userId: user?.id,
+ userId: session?.user?.userId,
},
orderBy: {
createdAt: "desc",
@@ -60,22 +62,22 @@ export async function getProjects() {
}
export async function getProjectById(id: string) {
- const user = await getUser();
+ const session = await getPageSession();
const project = await db.project.findFirst({
where: {
id,
- userId: user?.id,
+ userId: session?.user?.userId,
},
});
return project as Project;
}
export async function updateProjectById(id: string, payload: Payload) {
- const user = await getUser();
+ const session = await getPageSession();
await db.project.update({
where: {
id,
- userId: user?.id,
+ userId: session?.user?.userId,
},
data: payload,
});
@@ -83,11 +85,11 @@ export async function updateProjectById(id: string, payload: Payload) {
}
export async function deleteProjectById(id: string) {
- const user = await getUser();
+ const session = await getPageSession();
await db.project.delete({
where: {
id,
- userId: user?.id,
+ userId: session?.user?.userId,
},
});
revalidatePath(`/dashboard/projects`);
diff --git a/src/app/dashboard/projects/create-project-modal.tsx b/src/app/dashboard/projects/create-project-modal.tsx
index 642dac3..65a7ea9 100644
--- a/src/app/dashboard/projects/create-project-modal.tsx
+++ b/src/app/dashboard/projects/create-project-modal.tsx
@@ -25,7 +25,7 @@ import {
} from "~/components/ui/form";
import { Input } from "~/components/ui/input";
import { toast } from "~/components/ui/use-toast";
-import { FreePlanLimitError } from "~/server/utils";
+import { FreePlanLimitError } from "~/lib/utils";
import { checkIfFreePlanLimitReached, createProject } from "./action";
export const projectSchema = z.object({
diff --git a/src/app/dashboard/settings/actions.ts b/src/app/dashboard/settings/actions.ts
new file mode 100644
index 0000000..b043bc3
--- /dev/null
+++ b/src/app/dashboard/settings/actions.ts
@@ -0,0 +1,50 @@
+"use server";
+
+import { revalidatePath } from "next/cache";
+import db from "~/lib/db";
+import { getImageKeyFromUrl, isOurCdnUrl } from "~/lib/utils";
+import { utapi } from "~/server/uploadthing";
+import { type payload } from "~/types";
+
+export const updateUser = async (id: string, payload: payload) => {
+ await db.user.update({
+ where: { id },
+ data: { ...payload },
+ });
+
+ revalidatePath("/dashboard/settings");
+};
+
+export async function removeUserOldImageFromCDN(
+ id: string,
+ newImageUrl: string
+) {
+ const user = await db.user.findFirst({
+ where: { id },
+ select: { picture: true },
+ });
+
+ const currentImageUrl = user?.picture;
+
+ if (!currentImageUrl) throw new Error("User Picture Missing");
+
+ try {
+ if (isOurCdnUrl(currentImageUrl)) {
+ const currentImageFileKey = getImageKeyFromUrl(currentImageUrl);
+
+ await utapi.deleteFiles(currentImageFileKey as string);
+ revalidatePath("/dashboard/settings");
+ }
+ } catch (e) {
+ if (e instanceof Error) {
+ const newImageFileKey = getImageKeyFromUrl(newImageUrl);
+ await utapi.deleteFiles(newImageFileKey as string);
+ console.error(e.message);
+ }
+ }
+}
+
+export async function removeNewImageFromCDN(image: string) {
+ const imageFileKey = getImageKeyFromUrl(image);
+ await utapi.deleteFiles(imageFileKey as string);
+}
diff --git a/src/app/dashboard/settings/page.tsx b/src/app/dashboard/settings/page.tsx
index ebdd9c4..142bf12 100644
--- a/src/app/dashboard/settings/page.tsx
+++ b/src/app/dashboard/settings/page.tsx
@@ -1,6 +1,6 @@
+import { type User } from "lucia";
import { type Metadata } from "next";
-import { getUser } from "~/server/user";
-import { type CurrentUser } from "~/types";
+import { getPageSession } from "~/lib/auth";
import SettingsForm from "./settings-form";
export const metadata: Metadata = {
@@ -8,6 +8,6 @@ export const metadata: Metadata = {
};
export default async function Settings() {
- const currentUser = (await getUser()) as CurrentUser;
- return ;
+ const session = await getPageSession();
+ return ;
}
diff --git a/src/app/dashboard/settings/settings-form.tsx b/src/app/dashboard/settings/settings-form.tsx
index 7e85372..abef414 100644
--- a/src/app/dashboard/settings/settings-form.tsx
+++ b/src/app/dashboard/settings/settings-form.tsx
@@ -1,10 +1,16 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
+import { type User } from "lucia";
import { Loader2 } from "lucide-react";
import dynamic from "next/dynamic";
import { useEffect, useRef, useState, useTransition } from "react";
import { useForm } from "react-hook-form";
+import {
+ removeNewImageFromCDN,
+ removeUserOldImageFromCDN,
+ updateUser,
+} from "~/app/dashboard/settings/actions";
import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar";
import { Button } from "~/components/ui/button";
import {
@@ -18,12 +24,7 @@ import {
} from "~/components/ui/form";
import { Input } from "~/components/ui/input";
import { toast } from "~/components/ui/use-toast";
-import {
- saRemoveNewImageFromCDN,
- saRemoveUserOldImageFromCDN,
- saUpdateUserInDb,
-} from "~/server/actions";
-import { settingsSchema, type CurrentUser, type SettingsValues } from "~/types";
+import { settingsSchema, type SettingsValues } from "~/types";
const ImageUploadModal = dynamic(
() => import("~/components/layout/image-upload-modal")
@@ -33,11 +34,7 @@ const CancelConfirmModal = dynamic(
() => import("~/components/layout/cancel-confirm-modal")
);
-export default function SettingsForm({
- currentUser,
-}: {
- currentUser: CurrentUser;
-}) {
+export default function SettingsForm({ currentUser }: { currentUser: User }) {
const oldImage = useRef("");
const [pending, startTransition] = useTransition();
@@ -65,43 +62,31 @@ export default function SettingsForm({
function onSubmit(data: SettingsValues) {
if (!formState.isDirty) return;
- if (isImageChanged) {
- startTransition(() =>
- saRemoveUserOldImageFromCDN(currentUser.id, data.picture)
- .then(() => saUpdateUserInDb(currentUser.id, data))
- .then(() => {
- toast({
- title: "Updated successfully!",
- });
- })
- .catch(() => {
- toast({
- title: "Something went wrong.",
- variant: "destructive",
- });
- })
- );
- } else {
- startTransition(() =>
- saUpdateUserInDb(currentUser.id, data)
- .then(() => {
- toast({
- title: "Updated successfully!",
- });
- })
- .catch(() => {
- toast({
- title: "Something went wrong.",
- variant: "destructive",
- });
- })
- );
- }
+ startTransition(() => {
+ const updatePromise = isImageChanged
+ ? removeUserOldImageFromCDN(currentUser.userId, data.picture).then(() =>
+ updateUser(currentUser.userId, data)
+ )
+ : updateUser(currentUser.userId, data);
+
+ return updatePromise
+ .then(() => {
+ toast({
+ title: "Updated successfully!",
+ });
+ })
+ .catch(() => {
+ toast({
+ title: "Something went wrong.",
+ variant: "destructive",
+ });
+ });
+ });
}
function handleReset() {
if (isImageChanged) {
- saRemoveNewImageFromCDN(form.getValues().picture)
+ removeNewImageFromCDN(form.getValues().picture)
.then(() => form.reset())
.catch((error) => console.error(error));
} else {
diff --git a/src/components/layout/auth-form.tsx b/src/components/layout/auth-form.tsx
index 486383a..abc8aff 100644
--- a/src/components/layout/auth-form.tsx
+++ b/src/components/layout/auth-form.tsx
@@ -1,127 +1,28 @@
"use client";
import Link from "next/link";
-import { buttonVariants } from "~/components/ui/button";
+import { useState } from "react";
+import { Button, buttonVariants } from "~/components/ui/button";
import { cn } from "~/lib/utils";
import Icons from "../shared/icons";
-// const userAuthSchema = z.object({
-// email: z.string().email("Please enter a valid email address."),
-// });
-
-// type FormData = z.infer;
-
export default function AuthForm() {
- // const [isLoading, setIsLoading] = useState(false);
- // const [isGithubLoading, setIsGithubLoading] = useState(false);
-
- // const {
- // register,
- // handleSubmit,
- // reset,
- // formState: { errors },
- // } = useForm({
- // resolver: zodResolver(userAuthSchema),
- // });
-
- // async function onSubmit(data: FormData) {
- // setIsLoading(true);
-
- // saCheckEmailExists(data.email.toLowerCase())
- // .then(async () => {
- // try {
- // const res = await signIn("email", {
- // email: data.email.toLowerCase(),
- // redirect: false,
- // });
-
- // if (!res?.ok) {
- // throw new Error("Something went wrong.");
- // }
-
- // toast({
- // title: "Check your email",
- // description:
- // "We sent you a sign in link. Be sure to check your spam too.",
- // });
- // reset();
- // } catch (err) {
- // toast({
- // title: "Something went wrong.",
- // description: "Your signin request failed. Please try again.",
- // variant: "destructive",
- // });
- // } finally {
- // setIsLoading(false);
- // }
- // })
- // .catch(() => {
- // toast({
- // title: "Account not found.",
- // description:
- // "You have to use the email already linked to your account.",
- // variant: "destructive",
- // });
- // setIsLoading(false);
- // });
- // }
+ const [isLoading, setIsLoading] = useState(false);
return (
- {/*
-
*/}
-
{
- // setIsGithubLoading(true);
- // signIn("github");
- // }}
- // disabled={isLoading || isGithubLoading}
- >
- {/* {isGithubLoading ? (
+ {isLoading ? (
+
+ ) : (
+
setIsLoading(true)}
+ >
+ Continue with
+
+ )}
);
}
diff --git a/src/components/layout/header/index.tsx b/src/components/layout/header/index.tsx
index 00a5c20..bc60e22 100644
--- a/src/components/layout/header/index.tsx
+++ b/src/components/layout/header/index.tsx
@@ -1,13 +1,13 @@
-import { getUser } from "~/server/user";
-import { type CurrentUser } from "~/types";
+import { type User } from "lucia";
+import { getPageSession } from "~/lib/auth";
import Navbar from "./navbar";
export default async function Header() {
- const currentUser = (await getUser()) as CurrentUser;
+ const session = await getPageSession();
return (
);
diff --git a/src/components/layout/header/navbar.tsx b/src/components/layout/header/navbar.tsx
index cb590fa..c517741 100644
--- a/src/components/layout/header/navbar.tsx
+++ b/src/components/layout/header/navbar.tsx
@@ -1,18 +1,14 @@
"use client";
+import { type User } from "lucia";
import { MenuIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { buttonVariants } from "~/components/ui/button";
import { Sheet, SheetContent, SheetTrigger } from "~/components/ui/sheet";
-import { type CurrentUser } from "~/types";
import UserNav from "../user-nav";
-export default function Navbar({
- loggedInUser,
-}: {
- loggedInUser: CurrentUser;
-}) {
+export default function Navbar({ loggedInUser }: { loggedInUser: User }) {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
-
-
-
-
-
Next.js 13
-
- App dir, Routing, Layouts, API routes, Server Components, Server
- actions.
-
-
+
+
+
+
+ App dir, Routing, Layouts, API routes, Server Components, Server
+ actions.
+
-
-
-
-
Shadcn UI
-
- UI components built using Radix UI and styled with Tailwind CSS.
-
-
+
+
+
+
+ UI components built using Radix UI and styled with Tailwind CSS.
+
-
-
-
-
Database
-
- Using Postgres with Prisma ORM, hosted on Vercel Postgres.
-
-
+
+
+
+
+ Using Postgres with Prisma ORM, hosted on Vercel Postgres.
+
-
-
-
-
Authentication
-
- Authentication using NextAuth.js and middlewares.
-
-
+
+
+
+
+ Authentication and Authorization using LuciaAuth.
+
-
-
-
-
File Upload
-
- Upload and preview files effortlessly with UploadThing.
-
-
+
+
+
+
+ Upload and preview files effortlessly with UploadThing.
+
-
-
-
-
Emails
-
- Create emails using React Email and Send with Resend.
-
-
+
+
+
+
+ Create emails using React Email and Send with Resend.
+
+
+
+
+
+
+ Receive and process payments with Stripe.
+
+
+
+
+
+
+ Production and Preview deployments with Vercel.
+
ChadNext also includes changelog page built using Contentlayer and
- MDX.
+ Markdown.
diff --git a/src/components/sections/hero.tsx b/src/components/sections/hero.tsx
index 996f8a1..adf5c6e 100644
--- a/src/components/sections/hero.tsx
+++ b/src/components/sections/hero.tsx
@@ -1,8 +1,8 @@
import { StarIcon } from "lucide-react";
import Link from "next/link";
import Balancer from "react-wrap-balancer";
+import { BrandIcons } from "~/components/shared/brand-icons";
import Icons from "~/components/shared/icons";
-import { ToolIcons } from "~/components/shared/tool-icons";
import { buttonVariants } from "~/components/ui/button";
import { nFormatter } from "~/lib/utils";
@@ -40,7 +40,7 @@ export default async function Hero() {
-
+
Get Started
@@ -34,14 +34,16 @@ export default async function Pricing() {
- Free Plan
+
+ Free Plan{" "}
+ {subscription && !subscription?.isPro && (
+ Current
+ )}
+
Up to 3 projects
@@ -55,25 +57,28 @@ export default async function Pricing() {
-
- {subscriptionPlan === null
- ? "Get Started"
- : subscriptionPlan?.isPro
- ? "Switch Plan"
- : "Manage Plan"}
-
+ {!subscription ? (
+
+ Get Started
+
+ ) : (
+ ""
+ )}
- Pro Plan
+
+ Pro Plan{" "}
+ {subscription && subscription?.isPro && (
+ Current
+ )}
+
Unlimited projects
@@ -88,11 +93,11 @@ export default async function Pricing() {
- {subscriptionPlan === null
+ {!subscription
? "Get Started"
- : subscriptionPlan?.isPro
- ? "Manage Plan"
- : "Upgrade Plan"}
+ : subscription?.isPro
+ ? "Manage Plan"
+ : "Upgrade Plan"}
diff --git a/src/components/shared/tool-icons.tsx b/src/components/shared/brand-icons.tsx
similarity index 77%
rename from src/components/shared/tool-icons.tsx
rename to src/components/shared/brand-icons.tsx
index 245ff9b..583088b 100644
--- a/src/components/shared/tool-icons.tsx
+++ b/src/components/shared/brand-icons.tsx
@@ -1,4 +1,4 @@
-export const ToolIcons = {
+export const BrandIcons = {
ts: () => (
),
+ luciaAuth: () => (
+
+ LuciaAuth
+
+
+ ),
+ stripe: () => (
+
+ Stripe
+
+
+ ),
};
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
new file mode 100644
index 0000000..3381ece
--- /dev/null
+++ b/src/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "~/lib/utils";
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+);
+
+export interface BadgeProps
+ extends React.HTMLAttributes
,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx
index c1d1194..86e9a6e 100644
--- a/src/components/ui/toast.tsx
+++ b/src/components/ui/toast.tsx
@@ -14,7 +14,7 @@ const ToastViewport = React.forwardRef<
{
+ const subject = "Thanks for using ChadNext!";
+ const temp = ThanksTemp({ userName: data.name });
+
+ //@ts-expect-error text field is required
+ await resend.emails.send({
+ from: `ChadNext App `,
+ to: toMail,
+ subject: subject,
+ headers: {
+ "X-Entity-Ref-ID": nanoid(),
+ },
+ react: temp,
+ });
+};
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 92ec7dc..c61dbd2 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -39,3 +39,17 @@ export function formatDate(input: string | number): string {
year: "numeric",
});
}
+
+export const isOurCdnUrl = (url: string) =>
+ url?.includes("utfs.io") || url?.includes("uploadthing.com");
+
+export const getImageKeyFromUrl = (url: string) => {
+ const parts = url.split("/");
+ return parts.at(-1);
+};
+
+export class FreePlanLimitError extends Error {
+ constructor(message = "Upgrade your plan!") {
+ super(message);
+ }
+}
diff --git a/src/server/actions.ts b/src/server/actions.ts
deleted file mode 100644
index 7c1cf7d..0000000
--- a/src/server/actions.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-"use server";
-
-import { revalidatePath } from "next/cache";
-import { type SendMailProps, type payload } from "~/types";
-import { updateUser } from "./user";
-
-import db from "~/lib/db";
-import { utapi } from "./uploadthing";
-import { getImageKeyFromUrl, isOurCdnUrl } from "./utils";
-
-import ThanksTemp from "emails/thanks";
-import { nanoid } from "nanoid";
-import { resend } from "~/lib/resend";
-
-export async function checkEmailExists(email: string) {
- const user = await db.user.findFirst({
- where: { email },
- });
-
- if (!user) throw new Error("Email does not exist");
-}
-
-export async function saUpdateUserInDb(id: string, data: payload) {
- await updateUser(id, data);
- revalidatePath("/dashboard/settings");
-}
-
-export async function saRemoveUserOldImageFromCDN(id: string, image: string) {
- await removeUserOldImageCDN(id, image);
- revalidatePath("/dashboard/settings");
-}
-
-export async function saRemoveNewImageFromCDN(image: string) {
- await removeNewImageFromCDN(image);
-}
-
-export async function saCheckEmailExists(email: string) {
- await checkEmailExists(email);
-}
-
-export async function removeUserOldImageCDN(id: string, newImageUrl: string) {
- const user = await db.user.findFirst({
- where: { id },
- select: { picture: true },
- });
-
- const currentImageUrl = user?.picture;
-
- if (!currentImageUrl) return;
-
- try {
- if (isOurCdnUrl(currentImageUrl)) {
- const currentImageFileKey = getImageKeyFromUrl(currentImageUrl);
-
- await utapi.deleteFiles(currentImageFileKey as string);
- }
- } catch (e) {
- if (e instanceof Error) {
- const newImageFileKey = getImageKeyFromUrl(newImageUrl);
- await utapi.deleteFiles(newImageFileKey as string);
- console.error(e.message);
- }
- }
-}
-
-export async function removeNewImageFromCDN(image: string) {
- const imageFileKey = getImageKeyFromUrl(image);
- await utapi.deleteFiles(imageFileKey as string);
-}
-
-export const sendMail = async ({ toMail, data }: SendMailProps) => {
- const subject = "Thanks for using ChadNext!";
- const temp = ThanksTemp({ userName: data.name });
-
- //@ts-expect-error text field is required
- await resend.emails.send({
- from: `ChadNext App `,
- to: toMail,
- subject: subject,
- headers: {
- "X-Entity-Ref-ID": nanoid(),
- },
- react: temp,
- });
-};
diff --git a/src/server/user.ts b/src/server/user.ts
deleted file mode 100644
index d09b1af..0000000
--- a/src/server/user.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { cache } from "react";
-import db from "~/lib/db";
-import { getPageSession } from "~/lib/auth";
-import { type payload } from "~/types";
-
-export const getUser = cache(async () => {
- const session = await getPageSession();
-
- if (session) {
- const user = await db.user.findFirst({
- where: { id: session.user.userId },
- });
-
- if (user) {
- return user;
- }
- }
-});
-
-export const updateUser = async (id: string, payload: payload) => {
- const updatedUser = await db.user.update({
- where: { id },
- data: { ...payload },
- });
-
- return updatedUser;
-};
diff --git a/src/server/utils.ts b/src/server/utils.ts
deleted file mode 100644
index 38ae662..0000000
--- a/src/server/utils.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export const isOurCdnUrl = (url: string) =>
- url?.includes("utfs.io") || url?.includes("uploadthing.com");
-
-export const getImageKeyFromUrl = (url: string) => {
- const parts = url.split("/");
- return parts.at(-1);
-};
-
-export class FreePlanLimitError extends Error {
- constructor(message = "Upgrade your plan!") {
- super(message);
- }
-}