-
-
Save adrianhajdin/060e4c9d3d8d4274b7669e260dbbcc8e to your computer and use it in GitHub Desktop.
/* eslint-disable camelcase */ | |
// Resource: https://clerk.com/docs/users/sync-data-to-your-backend | |
// Above article shows why we need webhooks i.e., to sync data to our backend | |
// Resource: https://docs.svix.com/receiving/verifying-payloads/why | |
// It's a good practice to verify webhooks. Above article shows why we should do it | |
import { Webhook, WebhookRequiredHeaders } from "svix"; | |
import { headers } from "next/headers"; | |
import { IncomingHttpHeaders } from "http"; | |
import { NextResponse } from "next/server"; | |
import { | |
addMemberToCommunity, | |
createCommunity, | |
deleteCommunity, | |
removeUserFromCommunity, | |
updateCommunityInfo, | |
} from "@/lib/actions/community.actions"; | |
// Resource: https://clerk.com/docs/integration/webhooks#supported-events | |
// Above document lists the supported events | |
type EventType = | |
| "organization.created" | |
| "organizationInvitation.created" | |
| "organizationMembership.created" | |
| "organizationMembership.deleted" | |
| "organization.updated" | |
| "organization.deleted"; | |
type Event = { | |
data: Record<string, string | number | Record<string, string>[]>; | |
object: "event"; | |
type: EventType; | |
}; | |
export const POST = async (request: Request) => { | |
const payload = await request.json(); | |
const header = headers(); | |
const heads = { | |
"svix-id": header.get("svix-id"), | |
"svix-timestamp": header.get("svix-timestamp"), | |
"svix-signature": header.get("svix-signature"), | |
}; | |
// Activitate Webhook in the Clerk Dashboard. | |
// After adding the endpoint, you'll see the secret on the right side. | |
const wh = new Webhook(process.env.NEXT_CLERK_WEBHOOK_SECRET || ""); | |
let evnt: Event | null = null; | |
try { | |
evnt = wh.verify( | |
JSON.stringify(payload), | |
heads as IncomingHttpHeaders & WebhookRequiredHeaders | |
) as Event; | |
} catch (err) { | |
return NextResponse.json({ message: err }, { status: 400 }); | |
} | |
const eventType: EventType = evnt?.type!; | |
// Listen organization creation event | |
if (eventType === "organization.created") { | |
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organizations#operation/CreateOrganization | |
// Show what evnt?.data sends from above resource | |
const { id, name, slug, logo_url, image_url, created_by } = | |
evnt?.data ?? {}; | |
try { | |
// @ts-ignore | |
await createCommunity( | |
// @ts-ignore | |
id, | |
name, | |
slug, | |
logo_url || image_url, | |
"org bio", | |
created_by | |
); | |
return NextResponse.json({ message: "User created" }, { status: 201 }); | |
} catch (err) { | |
console.log(err); | |
return NextResponse.json( | |
{ message: "Internal Server Error" }, | |
{ status: 500 } | |
); | |
} | |
} | |
// Listen organization invitation creation event. | |
// Just to show. You can avoid this or tell people that we can create a new mongoose action and | |
// add pending invites in the database. | |
if (eventType === "organizationInvitation.created") { | |
try { | |
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organization-Invitations#operation/CreateOrganizationInvitation | |
console.log("Invitation created", evnt?.data); | |
return NextResponse.json( | |
{ message: "Invitation created" }, | |
{ status: 201 } | |
); | |
} catch (err) { | |
console.log(err); | |
return NextResponse.json( | |
{ message: "Internal Server Error" }, | |
{ status: 500 } | |
); | |
} | |
} | |
// Listen organization membership (member invite & accepted) creation | |
if (eventType === "organizationMembership.created") { | |
try { | |
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organization-Memberships#operation/CreateOrganizationMembership | |
// Show what evnt?.data sends from above resource | |
const { organization, public_user_data } = evnt?.data; | |
console.log("created", evnt?.data); | |
// @ts-ignore | |
await addMemberToCommunity(organization.id, public_user_data.user_id); | |
return NextResponse.json( | |
{ message: "Invitation accepted" }, | |
{ status: 201 } | |
); | |
} catch (err) { | |
console.log(err); | |
return NextResponse.json( | |
{ message: "Internal Server Error" }, | |
{ status: 500 } | |
); | |
} | |
} | |
// Listen member deletion event | |
if (eventType === "organizationMembership.deleted") { | |
try { | |
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organization-Memberships#operation/DeleteOrganizationMembership | |
// Show what evnt?.data sends from above resource | |
const { organization, public_user_data } = evnt?.data; | |
console.log("removed", evnt?.data); | |
// @ts-ignore | |
await removeUserFromCommunity(public_user_data.user_id, organization.id); | |
return NextResponse.json({ message: "Member removed" }, { status: 201 }); | |
} catch (err) { | |
console.log(err); | |
return NextResponse.json( | |
{ message: "Internal Server Error" }, | |
{ status: 500 } | |
); | |
} | |
} | |
// Listen organization updation event | |
if (eventType === "organization.updated") { | |
try { | |
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organizations#operation/UpdateOrganization | |
// Show what evnt?.data sends from above resource | |
const { id, logo_url, name, slug } = evnt?.data; | |
console.log("updated", evnt?.data); | |
// @ts-ignore | |
await updateCommunityInfo(id, name, slug, logo_url); | |
return NextResponse.json({ message: "Member removed" }, { status: 201 }); | |
} catch (err) { | |
console.log(err); | |
return NextResponse.json( | |
{ message: "Internal Server Error" }, | |
{ status: 500 } | |
); | |
} | |
} | |
// Listen organization deletion event | |
if (eventType === "organization.deleted") { | |
try { | |
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organizations#operation/DeleteOrganization | |
// Show what evnt?.data sends from above resource | |
const { id } = evnt?.data; | |
console.log("deleted", evnt?.data); | |
// @ts-ignore | |
await deleteCommunity(id); | |
return NextResponse.json( | |
{ message: "Organization deleted" }, | |
{ status: 201 } | |
); | |
} catch (err) { | |
console.log(err); | |
return NextResponse.json( | |
{ message: "Internal Server Error" }, | |
{ status: 500 } | |
); | |
} | |
} | |
}; |
"use server"; | |
import { FilterQuery, SortOrder } from "mongoose"; | |
import Community from "../models/community.model"; | |
import Thread from "../models/thread.model"; | |
import User from "../models/user.model"; | |
import { connectToDB } from "../mongoose"; | |
export async function createCommunity( | |
id: string, | |
name: string, | |
username: string, | |
image: string, | |
bio: string, | |
createdById: string // Change the parameter name to reflect it's an id | |
) { | |
try { | |
connectToDB(); | |
// Find the user with the provided unique id | |
const user = await User.findOne({ id: createdById }); | |
if (!user) { | |
throw new Error("User not found"); // Handle the case if the user with the id is not found | |
} | |
const newCommunity = new Community({ | |
id, | |
name, | |
username, | |
image, | |
bio, | |
createdBy: user._id, // Use the mongoose ID of the user | |
}); | |
const createdCommunity = await newCommunity.save(); | |
// Update User model | |
user.communities.push(createdCommunity._id); | |
await user.save(); | |
return createdCommunity; | |
} catch (error) { | |
// Handle any errors | |
console.error("Error creating community:", error); | |
throw error; | |
} | |
} | |
export async function fetchCommunityDetails(id: string) { | |
try { | |
connectToDB(); | |
const communityDetails = await Community.findOne({ id }).populate([ | |
"createdBy", | |
{ | |
path: "members", | |
model: User, | |
select: "name username image _id id", | |
}, | |
]); | |
return communityDetails; | |
} catch (error) { | |
// Handle any errors | |
console.error("Error fetching community details:", error); | |
throw error; | |
} | |
} | |
export async function fetchCommunityPosts(id: string) { | |
try { | |
connectToDB(); | |
const communityPosts = await Community.findById(id).populate({ | |
path: "threads", | |
model: Thread, | |
populate: [ | |
{ | |
path: "author", | |
model: User, | |
select: "name image id", // Select the "name" and "_id" fields from the "User" model | |
}, | |
{ | |
path: "children", | |
model: Thread, | |
populate: { | |
path: "author", | |
model: User, | |
select: "image _id", // Select the "name" and "_id" fields from the "User" model | |
}, | |
}, | |
], | |
}); | |
return communityPosts; | |
} catch (error) { | |
// Handle any errors | |
console.error("Error fetching community posts:", error); | |
throw error; | |
} | |
} | |
export async function fetchCommunities({ | |
searchString = "", | |
pageNumber = 1, | |
pageSize = 20, | |
sortBy = "desc", | |
}: { | |
searchString?: string; | |
pageNumber?: number; | |
pageSize?: number; | |
sortBy?: SortOrder; | |
}) { | |
try { | |
connectToDB(); | |
// Calculate the number of communities to skip based on the page number and page size. | |
const skipAmount = (pageNumber - 1) * pageSize; | |
// Create a case-insensitive regular expression for the provided search string. | |
const regex = new RegExp(searchString, "i"); | |
// Create an initial query object to filter communities. | |
const query: FilterQuery<typeof Community> = {}; | |
// If the search string is not empty, add the $or operator to match either username or name fields. | |
if (searchString.trim() !== "") { | |
query.$or = [ | |
{ username: { $regex: regex } }, | |
{ name: { $regex: regex } }, | |
]; | |
} | |
// Define the sort options for the fetched communities based on createdAt field and provided sort order. | |
const sortOptions = { createdAt: sortBy }; | |
// Create a query to fetch the communities based on the search and sort criteria. | |
const communitiesQuery = Community.find(query) | |
.sort(sortOptions) | |
.skip(skipAmount) | |
.limit(pageSize) | |
.populate("members"); | |
// Count the total number of communities that match the search criteria (without pagination). | |
const totalCommunitiesCount = await Community.countDocuments(query); | |
const communities = await communitiesQuery.exec(); | |
// Check if there are more communities beyond the current page. | |
const isNext = totalCommunitiesCount > skipAmount + communities.length; | |
return { communities, isNext }; | |
} catch (error) { | |
console.error("Error fetching communities:", error); | |
throw error; | |
} | |
} | |
export async function addMemberToCommunity( | |
communityId: string, | |
memberId: string | |
) { | |
try { | |
connectToDB(); | |
// Find the community by its unique id | |
const community = await Community.findOne({ id: communityId }); | |
if (!community) { | |
throw new Error("Community not found"); | |
} | |
// Find the user by their unique id | |
const user = await User.findOne({ id: memberId }); | |
if (!user) { | |
throw new Error("User not found"); | |
} | |
// Check if the user is already a member of the community | |
if (community.members.includes(user._id)) { | |
throw new Error("User is already a member of the community"); | |
} | |
// Add the user's _id to the members array in the community | |
community.members.push(user._id); | |
await community.save(); | |
// Add the community's _id to the communities array in the user | |
user.communities.push(community._id); | |
await user.save(); | |
return community; | |
} catch (error) { | |
// Handle any errors | |
console.error("Error adding member to community:", error); | |
throw error; | |
} | |
} | |
export async function removeUserFromCommunity( | |
userId: string, | |
communityId: string | |
) { | |
try { | |
connectToDB(); | |
const userIdObject = await User.findOne({ id: userId }, { _id: 1 }); | |
const communityIdObject = await Community.findOne( | |
{ id: communityId }, | |
{ _id: 1 } | |
); | |
if (!userIdObject) { | |
throw new Error("User not found"); | |
} | |
if (!communityIdObject) { | |
throw new Error("Community not found"); | |
} | |
// Remove the user's _id from the members array in the community | |
await Community.updateOne( | |
{ _id: communityIdObject._id }, | |
{ $pull: { members: userIdObject._id } } | |
); | |
// Remove the community's _id from the communities array in the user | |
await User.updateOne( | |
{ _id: userIdObject._id }, | |
{ $pull: { communities: communityIdObject._id } } | |
); | |
return { success: true }; | |
} catch (error) { | |
// Handle any errors | |
console.error("Error removing user from community:", error); | |
throw error; | |
} | |
} | |
export async function updateCommunityInfo( | |
communityId: string, | |
name: string, | |
username: string, | |
image: string | |
) { | |
try { | |
connectToDB(); | |
// Find the community by its _id and update the information | |
const updatedCommunity = await Community.findOneAndUpdate( | |
{ id: communityId }, | |
{ name, username, image } | |
); | |
if (!updatedCommunity) { | |
throw new Error("Community not found"); | |
} | |
return updatedCommunity; | |
} catch (error) { | |
// Handle any errors | |
console.error("Error updating community information:", error); | |
throw error; | |
} | |
} | |
export async function deleteCommunity(communityId: string) { | |
try { | |
connectToDB(); | |
// Find the community by its ID and delete it | |
const deletedCommunity = await Community.findOneAndDelete({ | |
id: communityId, | |
}); | |
if (!deletedCommunity) { | |
throw new Error("Community not found"); | |
} | |
// Delete all threads associated with the community | |
await Thread.deleteMany({ community: communityId }); | |
// Find all users who are part of the community | |
const communityUsers = await User.find({ communities: communityId }); | |
// Remove the community from the 'communities' array for each user | |
const updateUserPromises = communityUsers.map((user) => { | |
user.communities.pull(communityId); | |
return user.save(); | |
}); | |
await Promise.all(updateUserPromises); | |
return deletedCommunity; | |
} catch (error) { | |
console.error("Error deleting community: ", error); | |
throw error; | |
} | |
} |
import Image from "next/image"; | |
import Link from "next/link"; | |
import { Button } from "../ui/button"; | |
interface Props { | |
id: string; | |
name: string; | |
username: string; | |
imgUrl: string; | |
bio: string; | |
members: { | |
image: string; | |
}[]; | |
} | |
function CommunityCard({ id, name, username, imgUrl, bio, members }: Props) { | |
return ( | |
<article className='community-card'> | |
<div className='flex flex-wrap items-center gap-3'> | |
<Link href={`/communities/${id}`} className='relative h-12 w-12'> | |
<Image | |
src={imgUrl} | |
alt='community_logo' | |
fill | |
className='rounded-full object-cover' | |
/> | |
</Link> | |
<div> | |
<Link href={`/communities/${id}`}> | |
<h4 className='text-base-semibold text-light-1'>{name}</h4> | |
</Link> | |
<p className='text-small-medium text-gray-1'>@{username}</p> | |
</div> | |
</div> | |
<p className='mt-4 text-subtle-medium text-gray-1'>{bio}</p> | |
<div className='mt-5 flex flex-wrap items-center justify-between gap-3'> | |
<Link href={`/communities/${id}`}> | |
<Button size='sm' className='community-card_btn'> | |
View | |
</Button> | |
</Link> | |
{members.length > 0 && ( | |
<div className='flex items-center'> | |
{members.map((member, index) => ( | |
<Image | |
key={index} | |
src={member.image} | |
alt={`user_${index}`} | |
width={28} | |
height={28} | |
className={`${ | |
index !== 0 && "-ml-2" | |
} rounded-full object-cover`} | |
/> | |
))} | |
{members.length > 3 && ( | |
<p className='ml-1 text-subtle-medium text-gray-1'> | |
{members.length}+ Users | |
</p> | |
)} | |
</div> | |
)} | |
</div> | |
</article> | |
); | |
} | |
export default CommunityCard; |
export const sidebarLinks = [ | |
{ | |
imgURL: "/assets/home.svg", | |
route: "/", | |
label: "Home", | |
}, | |
{ | |
imgURL: "/assets/search.svg", | |
route: "/search", | |
label: "Search", | |
}, | |
{ | |
imgURL: "/assets/heart.svg", | |
route: "/activity", | |
label: "Activity", | |
}, | |
{ | |
imgURL: "/assets/create.svg", | |
route: "/create-thread", | |
label: "Create Thread", | |
}, | |
{ | |
imgURL: "/assets/community.svg", | |
route: "/communities", | |
label: "Communities", | |
}, | |
{ | |
imgURL: "/assets/user.svg", | |
route: "/profile", | |
label: "Profile", | |
}, | |
]; | |
export const profileTabs = [ | |
{ value: "threads", label: "Threads", icon: "/assets/reply.svg" }, | |
{ value: "replies", label: "Replies", icon: "/assets/members.svg" }, | |
{ value: "tagged", label: "Tagged", icon: "/assets/tag.svg" }, | |
]; | |
export const communityTabs = [ | |
{ value: "threads", label: "Threads", icon: "/assets/reply.svg" }, | |
{ value: "members", label: "Members", icon: "/assets/members.svg" }, | |
{ value: "requests", label: "Requests", icon: "/assets/request.svg" }, | |
]; |
@tailwind base; | |
@tailwind components; | |
@tailwind utilities; | |
@layer components { | |
/* main */ | |
.main-container { | |
@apply flex min-h-screen flex-1 flex-col items-center bg-dark-1 px-6 pb-10 pt-28 max-md:pb-32 sm:px-10; | |
} | |
/* Head Text */ | |
.head-text { | |
@apply text-heading2-bold text-light-1; | |
} | |
/* Activity */ | |
.activity-card { | |
@apply flex items-center gap-2 rounded-md bg-dark-2 px-7 py-4; | |
} | |
/* No Result */ | |
.no-result { | |
@apply text-center !text-base-regular text-light-3; | |
} | |
/* Community Card */ | |
.community-card { | |
@apply w-full rounded-lg bg-dark-3 px-4 py-5 sm:w-96; | |
} | |
.community-card_btn { | |
@apply rounded-lg bg-primary-500 px-5 py-1.5 text-small-regular !text-light-1 !important; | |
} | |
/* thread card */ | |
.thread-card_bar { | |
@apply relative mt-2 w-0.5 grow rounded-full bg-neutral-800; | |
} | |
/* User card */ | |
.user-card { | |
@apply flex flex-col justify-between gap-4 max-xs:rounded-xl max-xs:bg-dark-3 max-xs:p-4 xs:flex-row xs:items-center; | |
} | |
.user-card_avatar { | |
@apply flex flex-1 items-start justify-start gap-3 xs:items-center; | |
} | |
.user-card_btn { | |
@apply h-auto min-w-[74px] rounded-lg bg-primary-500 text-[12px] text-light-1 !important; | |
} | |
.searchbar { | |
@apply flex gap-1 rounded-lg bg-dark-3 px-4 py-2; | |
} | |
.searchbar_input { | |
@apply border-none bg-dark-3 text-base-regular text-light-4 outline-none !important; | |
} | |
.topbar { | |
@apply fixed top-0 z-30 flex w-full items-center justify-between bg-dark-2 px-6 py-3; | |
} | |
.bottombar { | |
@apply fixed bottom-0 z-10 w-full rounded-t-3xl bg-glassmorphism p-4 backdrop-blur-lg xs:px-7 md:hidden; | |
} | |
.bottombar_container { | |
@apply flex items-center justify-between gap-3 xs:gap-5; | |
} | |
.bottombar_link { | |
@apply relative flex flex-col items-center gap-2 rounded-lg p-2 sm:flex-1 sm:px-2 sm:py-2.5; | |
} | |
.leftsidebar { | |
@apply sticky left-0 top-0 z-20 flex h-screen w-fit flex-col justify-between overflow-auto border-r border-r-dark-4 bg-dark-2 pb-5 pt-28 max-md:hidden; | |
} | |
.leftsidebar_link { | |
@apply relative flex justify-start gap-4 rounded-lg p-4; | |
} | |
.pagination { | |
@apply mt-10 flex w-full items-center justify-center gap-5; | |
} | |
.rightsidebar { | |
@apply sticky right-0 top-0 z-20 flex h-screen w-fit flex-col justify-between gap-12 overflow-auto border-l border-l-dark-4 bg-dark-2 px-10 pb-6 pt-28 max-xl:hidden; | |
} | |
} | |
@layer utilities { | |
.css-invert { | |
@apply invert-[50%] brightness-200; | |
} | |
.custom-scrollbar::-webkit-scrollbar { | |
width: 3px; | |
height: 3px; | |
border-radius: 2px; | |
} | |
.custom-scrollbar::-webkit-scrollbar-track { | |
background: #09090a; | |
} | |
.custom-scrollbar::-webkit-scrollbar-thumb { | |
background: #5c5c7b; | |
border-radius: 50px; | |
} | |
.custom-scrollbar::-webkit-scrollbar-thumb:hover { | |
background: #7878a3; | |
} | |
} | |
/* Clerk Responsive fix */ | |
.cl-organizationSwitcherTrigger .cl-userPreview .cl-userPreviewTextContainer { | |
@apply max-sm:hidden; | |
} | |
.cl-organizationSwitcherTrigger | |
.cl-organizationPreview | |
.cl-organizationPreviewTextContainer { | |
@apply max-sm:hidden; | |
} | |
/* Shadcn Component Styles */ | |
/* Tab */ | |
.tab { | |
@apply flex min-h-[50px] flex-1 items-center gap-3 bg-dark-2 text-light-2 data-[state=active]:bg-[#0e0e12] data-[state=active]:text-light-2 !important; | |
} | |
.no-focus { | |
@apply focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 !important; | |
} | |
/* Account Profile */ | |
.account-form_image-label { | |
@apply flex h-24 w-24 items-center justify-center rounded-full bg-dark-4 !important; | |
} | |
.account-form_image-input { | |
@apply cursor-pointer border-none bg-transparent outline-none file:text-blue !important; | |
} | |
.account-form_input { | |
@apply border border-dark-4 bg-dark-3 text-light-1 !important; | |
} | |
/* Comment Form */ | |
.comment-form { | |
@apply mt-10 flex items-center gap-4 border-y border-y-dark-4 py-5 max-xs:flex-col !important; | |
} | |
.comment-form_btn { | |
@apply rounded-3xl bg-primary-500 px-8 py-2 !text-small-regular text-light-1 max-xs:w-full !important; | |
} |
/** @type {import('next').NextConfig} */ | |
const nextConfig = { | |
experimental: { | |
serverActions: true, | |
serverComponentsExternalPackages: ["mongoose"], | |
}, | |
images: { | |
remotePatterns: [ | |
{ | |
protocol: "https", | |
hostname: "img.clerk.com", | |
}, | |
{ | |
protocol: "https", | |
hostname: "images.clerk.dev", | |
}, | |
{ | |
protocol: "https", | |
hostname: "uploadthing.com", | |
}, | |
{ | |
protocol: "https", | |
hostname: "placehold.co", | |
}, | |
], | |
typescript: { | |
ignoreBuildErrors: true, | |
}, | |
}, | |
}; | |
module.exports = nextConfig; |
/** @type {import('tailwindcss').Config} */ | |
module.exports = { | |
darkMode: ["class"], | |
content: [ | |
"./pages/**/*.{ts,tsx}", | |
"./components/**/*.{ts,tsx}", | |
"./app/**/*.{ts,tsx}", | |
"./src/**/*.{ts,tsx}", | |
], | |
theme: { | |
container: { | |
center: true, | |
padding: "2rem", | |
screens: { | |
"2xl": "1400px", | |
}, | |
}, | |
fontSize: { | |
"heading1-bold": [ | |
"36px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "700", | |
}, | |
], | |
"heading1-semibold": [ | |
"36px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "600", | |
}, | |
], | |
"heading2-bold": [ | |
"30px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "700", | |
}, | |
], | |
"heading2-semibold": [ | |
"30px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "600", | |
}, | |
], | |
"heading3-bold": [ | |
"24px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "700", | |
}, | |
], | |
"heading4-medium": [ | |
"20px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "500", | |
}, | |
], | |
"body-bold": [ | |
"18px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "700", | |
}, | |
], | |
"body-semibold": [ | |
"18px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "600", | |
}, | |
], | |
"body-medium": [ | |
"18px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "500", | |
}, | |
], | |
"body-normal": [ | |
"18px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "400", | |
}, | |
], | |
"body1-bold": [ | |
"18px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "700", | |
}, | |
], | |
"base-regular": [ | |
"16px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "400", | |
}, | |
], | |
"base-medium": [ | |
"16px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "500", | |
}, | |
], | |
"base-semibold": [ | |
"16px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "600", | |
}, | |
], | |
"base1-semibold": [ | |
"16px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "600", | |
}, | |
], | |
"small-regular": [ | |
"14px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "400", | |
}, | |
], | |
"small-medium": [ | |
"14px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "500", | |
}, | |
], | |
"small-semibold": [ | |
"14px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "600", | |
}, | |
], | |
"subtle-medium": [ | |
"12px", | |
{ | |
lineHeight: "16px", | |
fontWeight: "500", | |
}, | |
], | |
"subtle-semibold": [ | |
"12px", | |
{ | |
lineHeight: "16px", | |
fontWeight: "600", | |
}, | |
], | |
"tiny-medium": [ | |
"10px", | |
{ | |
lineHeight: "140%", | |
fontWeight: "500", | |
}, | |
], | |
"x-small-semibold": [ | |
"7px", | |
{ | |
lineHeight: "9.318px", | |
fontWeight: "600", | |
}, | |
], | |
}, | |
extend: { | |
colors: { | |
"primary-500": "#877EFF", | |
"secondary-500": "#FFB620", | |
blue: "#0095F6", | |
"logout-btn": "#FF5A5A", | |
"navbar-menu": "rgba(16, 16, 18, 0.6)", | |
"dark-1": "#000000", | |
"dark-2": "#121417", | |
"dark-3": "#101012", | |
"dark-4": "#1F1F22", | |
"light-1": "#FFFFFF", | |
"light-2": "#EFEFEF", | |
"light-3": "#7878A3", | |
"light-4": "#5C5C7B", | |
"gray-1": "#697C89", | |
glassmorphism: "rgba(16, 16, 18, 0.60)", | |
}, | |
boxShadow: { | |
"count-badge": "0px 0px 6px 2px rgba(219, 188, 159, 0.30)", | |
"groups-sidebar": "-30px 0px 60px 0px rgba(28, 28, 31, 0.50)", | |
}, | |
screens: { | |
xs: "400px", | |
}, | |
keyframes: { | |
"accordion-down": { | |
from: { height: 0 }, | |
to: { height: "var(--radix-accordion-content-height)" }, | |
}, | |
"accordion-up": { | |
from: { height: "var(--radix-accordion-content-height)" }, | |
to: { height: 0 }, | |
}, | |
}, | |
animation: { | |
"accordion-down": "accordion-down 0.2s ease-out", | |
"accordion-up": "accordion-up 0.2s ease-out", | |
}, | |
}, | |
}, | |
plugins: [require("tailwindcss-animate")], | |
}; |
"use server"; | |
import { revalidatePath } from "next/cache"; | |
import { connectToDB } from "../mongoose"; | |
import User from "../models/user.model"; | |
import Thread from "../models/thread.model"; | |
import Community from "../models/community.model"; | |
export async function fetchPosts(pageNumber = 1, pageSize = 20) { | |
connectToDB(); | |
// Calculate the number of posts to skip based on the page number and page size. | |
const skipAmount = (pageNumber - 1) * pageSize; | |
// Create a query to fetch the posts that have no parent (top-level threads) (a thread that is not a comment/reply). | |
const postsQuery = Thread.find({ parentId: { $in: [null, undefined] } }) | |
.sort({ createdAt: "desc" }) | |
.skip(skipAmount) | |
.limit(pageSize) | |
.populate({ | |
path: "author", | |
model: User, | |
}) | |
.populate({ | |
path: "community", | |
model: Community, | |
}) | |
.populate({ | |
path: "children", // Populate the children field | |
populate: { | |
path: "author", // Populate the author field within children | |
model: User, | |
select: "_id name parentId image", // Select only _id and username fields of the author | |
}, | |
}); | |
// Count the total number of top-level posts (threads) i.e., threads that are not comments. | |
const totalPostsCount = await Thread.countDocuments({ | |
parentId: { $in: [null, undefined] }, | |
}); // Get the total count of posts | |
const posts = await postsQuery.exec(); | |
const isNext = totalPostsCount > skipAmount + posts.length; | |
return { posts, isNext }; | |
} | |
interface Params { | |
text: string, | |
author: string, | |
communityId: string | null, | |
path: string, | |
} | |
export async function createThread({ text, author, communityId, path }: Params | |
) { | |
try { | |
connectToDB(); | |
const communityIdObject = await Community.findOne( | |
{ id: communityId }, | |
{ _id: 1 } | |
); | |
const createdThread = await Thread.create({ | |
text, | |
author, | |
community: communityIdObject, // Assign communityId if provided, or leave it null for personal account | |
}); | |
// Update User model | |
await User.findByIdAndUpdate(author, { | |
$push: { threads: createdThread._id }, | |
}); | |
if (communityIdObject) { | |
// Update Community model | |
await Community.findByIdAndUpdate(communityIdObject, { | |
$push: { threads: createdThread._id }, | |
}); | |
} | |
revalidatePath(path); | |
} catch (error: any) { | |
throw new Error(`Failed to create thread: ${error.message}`); | |
} | |
} | |
async function fetchAllChildThreads(threadId: string): Promise<any[]> { | |
const childThreads = await Thread.find({ parentId: threadId }); | |
const descendantThreads = []; | |
for (const childThread of childThreads) { | |
const descendants = await fetchAllChildThreads(childThread._id); | |
descendantThreads.push(childThread, ...descendants); | |
} | |
return descendantThreads; | |
} | |
export async function deleteThread(id: string, path: string): Promise<void> { | |
try { | |
connectToDB(); | |
// Find the thread to be deleted (the main thread) | |
const mainThread = await Thread.findById(id).populate("author community"); | |
if (!mainThread) { | |
throw new Error("Thread not found"); | |
} | |
// Fetch all child threads and their descendants recursively | |
const descendantThreads = await fetchAllChildThreads(id); | |
// Get all descendant thread IDs including the main thread ID and child thread IDs | |
const descendantThreadIds = [ | |
id, | |
...descendantThreads.map((thread) => thread._id), | |
]; | |
// Extract the authorIds and communityIds to update User and Community models respectively | |
const uniqueAuthorIds = new Set( | |
[ | |
...descendantThreads.map((thread) => thread.author?._id?.toString()), // Use optional chaining to handle possible undefined values | |
mainThread.author?._id?.toString(), | |
].filter((id) => id !== undefined) | |
); | |
const uniqueCommunityIds = new Set( | |
[ | |
...descendantThreads.map((thread) => thread.community?._id?.toString()), // Use optional chaining to handle possible undefined values | |
mainThread.community?._id?.toString(), | |
].filter((id) => id !== undefined) | |
); | |
// Recursively delete child threads and their descendants | |
await Thread.deleteMany({ _id: { $in: descendantThreadIds } }); | |
// Update User model | |
await User.updateMany( | |
{ _id: { $in: Array.from(uniqueAuthorIds) } }, | |
{ $pull: { threads: { $in: descendantThreadIds } } } | |
); | |
// Update Community model | |
await Community.updateMany( | |
{ _id: { $in: Array.from(uniqueCommunityIds) } }, | |
{ $pull: { threads: { $in: descendantThreadIds } } } | |
); | |
revalidatePath(path); | |
} catch (error: any) { | |
throw new Error(`Failed to delete thread: ${error.message}`); | |
} | |
} | |
export async function fetchThreadById(threadId: string) { | |
connectToDB(); | |
try { | |
const thread = await Thread.findById(threadId) | |
.populate({ | |
path: "author", | |
model: User, | |
select: "_id id name image", | |
}) // Populate the author field with _id and username | |
.populate({ | |
path: "community", | |
model: Community, | |
select: "_id id name image", | |
}) // Populate the community field with _id and name | |
.populate({ | |
path: "children", // Populate the children field | |
populate: [ | |
{ | |
path: "author", // Populate the author field within children | |
model: User, | |
select: "_id id name parentId image", // Select only _id and username fields of the author | |
}, | |
{ | |
path: "children", // Populate the children field within children | |
model: Thread, // The model of the nested children (assuming it's the same "Thread" model) | |
populate: { | |
path: "author", // Populate the author field within nested children | |
model: User, | |
select: "_id id name parentId image", // Select only _id and username fields of the author | |
}, | |
}, | |
], | |
}) | |
.exec(); | |
return thread; | |
} catch (err) { | |
console.error("Error while fetching thread:", err); | |
throw new Error("Unable to fetch thread"); | |
} | |
} | |
export async function addCommentToThread( | |
threadId: string, | |
commentText: string, | |
userId: string, | |
path: string | |
) { | |
connectToDB(); | |
try { | |
// Find the original thread by its ID | |
const originalThread = await Thread.findById(threadId); | |
if (!originalThread) { | |
throw new Error("Thread not found"); | |
} | |
// Create the new comment thread | |
const commentThread = new Thread({ | |
text: commentText, | |
author: userId, | |
parentId: threadId, // Set the parentId to the original thread's ID | |
}); | |
// Save the comment thread to the database | |
const savedCommentThread = await commentThread.save(); | |
// Add the comment thread's ID to the original thread's children array | |
originalThread.children.push(savedCommentThread._id); | |
// Save the updated original thread to the database | |
await originalThread.save(); | |
revalidatePath(path); | |
} catch (err) { | |
console.error("Error while adding comment:", err); | |
throw new Error("Unable to add comment"); | |
} | |
} |
// Resource: https://docs.uploadthing.com/api-reference/react#generatereacthelpers | |
// Copy paste (be careful with imports) | |
import { generateReactHelpers } from "@uploadthing/react/hooks"; | |
import type { OurFileRouter } from "@/app/api/uploadthing/core"; | |
export const { useUploadThing, uploadFiles } = generateReactHelpers<OurFileRouter>(); |
"use server"; | |
import { FilterQuery, SortOrder } from "mongoose"; | |
import { revalidatePath } from "next/cache"; | |
import Community from "../models/community.model"; | |
import Thread from "../models/thread.model"; | |
import User from "../models/user.model"; | |
import { connectToDB } from "../mongoose"; | |
export async function fetchUser(userId: string) { | |
try { | |
connectToDB(); | |
return await User.findOne({ id: userId }).populate({ | |
path: "communities", | |
model: Community, | |
}); | |
} catch (error: any) { | |
throw new Error(`Failed to fetch user: ${error.message}`); | |
} | |
} | |
interface Params { | |
userId: string; | |
username: string; | |
name: string; | |
bio: string; | |
image: string; | |
path: string; | |
} | |
export async function updateUser({ | |
userId, | |
bio, | |
name, | |
path, | |
username, | |
image, | |
}: Params): Promise<void> { | |
try { | |
connectToDB(); | |
await User.findOneAndUpdate( | |
{ id: userId }, | |
{ | |
username: username.toLowerCase(), | |
name, | |
bio, | |
image, | |
onboarded: true, | |
}, | |
{ upsert: true } | |
); | |
if (path === "/profile/edit") { | |
revalidatePath(path); | |
} | |
} catch (error: any) { | |
throw new Error(`Failed to create/update user: ${error.message}`); | |
} | |
} | |
export async function fetchUserPosts(userId: string) { | |
try { | |
connectToDB(); | |
// Find all threads authored by the user with the given userId | |
const threads = await User.findOne({ id: userId }).populate({ | |
path: "threads", | |
model: Thread, | |
populate: [ | |
{ | |
path: "community", | |
model: Community, | |
select: "name id image _id", // Select the "name" and "_id" fields from the "Community" model | |
}, | |
{ | |
path: "children", | |
model: Thread, | |
populate: { | |
path: "author", | |
model: User, | |
select: "name image id", // Select the "name" and "_id" fields from the "User" model | |
}, | |
}, | |
], | |
}); | |
return threads; | |
} catch (error) { | |
console.error("Error fetching user threads:", error); | |
throw error; | |
} | |
} | |
// Almost similar to Thead (search + pagination) and Community (search + pagination) | |
export async function fetchUsers({ | |
userId, | |
searchString = "", | |
pageNumber = 1, | |
pageSize = 20, | |
sortBy = "desc", | |
}: { | |
userId: string; | |
searchString?: string; | |
pageNumber?: number; | |
pageSize?: number; | |
sortBy?: SortOrder; | |
}) { | |
try { | |
connectToDB(); | |
// Calculate the number of users to skip based on the page number and page size. | |
const skipAmount = (pageNumber - 1) * pageSize; | |
// Create a case-insensitive regular expression for the provided search string. | |
const regex = new RegExp(searchString, "i"); | |
// Create an initial query object to filter users. | |
const query: FilterQuery<typeof User> = { | |
id: { $ne: userId }, // Exclude the current user from the results. | |
}; | |
// If the search string is not empty, add the $or operator to match either username or name fields. | |
if (searchString.trim() !== "") { | |
query.$or = [ | |
{ username: { $regex: regex } }, | |
{ name: { $regex: regex } }, | |
]; | |
} | |
// Define the sort options for the fetched users based on createdAt field and provided sort order. | |
const sortOptions = { createdAt: sortBy }; | |
const usersQuery = User.find(query) | |
.sort(sortOptions) | |
.skip(skipAmount) | |
.limit(pageSize); | |
// Count the total number of users that match the search criteria (without pagination). | |
const totalUsersCount = await User.countDocuments(query); | |
const users = await usersQuery.exec(); | |
// Check if there are more users beyond the current page. | |
const isNext = totalUsersCount > skipAmount + users.length; | |
return { users, isNext }; | |
} catch (error) { | |
console.error("Error fetching users:", error); | |
throw error; | |
} | |
} | |
export async function getActivity(userId: string) { | |
try { | |
connectToDB(); | |
// Find all threads created by the user | |
const userThreads = await Thread.find({ author: userId }); | |
// Collect all the child thread ids (replies) from the 'children' field of each user thread | |
const childThreadIds = userThreads.reduce((acc, userThread) => { | |
return acc.concat(userThread.children); | |
}, []); | |
// Find and return the child threads (replies) excluding the ones created by the same user | |
const replies = await Thread.find({ | |
_id: { $in: childThreadIds }, | |
author: { $ne: userId }, // Exclude threads authored by the same user | |
}).populate({ | |
path: "author", | |
model: User, | |
select: "name image _id", | |
}); | |
return replies; | |
} catch (error) { | |
console.error("Error fetching replies: ", error); | |
throw error; | |
} | |
} |
import { type ClassValue, clsx } from "clsx"; | |
import { twMerge } from "tailwind-merge"; | |
// generated by shadcn | |
export function cn(...inputs: ClassValue[]) { | |
return twMerge(clsx(inputs)); | |
} | |
// created by chatgpt | |
export function isBase64Image(imageData: string) { | |
const base64Regex = /^data:image\/(png|jpe?g|gif|webp);base64,/; | |
return base64Regex.test(imageData); | |
} | |
// created by chatgpt | |
export function formatDateString(dateString: string) { | |
const options: Intl.DateTimeFormatOptions = { | |
year: "numeric", | |
month: "short", | |
day: "numeric", | |
}; | |
const date = new Date(dateString); | |
const formattedDate = date.toLocaleDateString(undefined, options); | |
const time = date.toLocaleTimeString([], { | |
hour: "numeric", | |
minute: "2-digit", | |
}); | |
return `${time} - ${formattedDate}`; | |
} | |
// created by chatgpt | |
export function formatThreadCount(count: number): string { | |
if (count === 0) { | |
return "No Threads"; | |
} else { | |
const threadCount = count.toString().padStart(2, "0"); | |
const threadWord = count === 1 ? "Thread" : "Threads"; | |
return `${threadCount} ${threadWord}`; | |
} | |
} |
i keep getting this error : MongoParseError: URI malformed, around the 2:22:01 mark when working on the backend and i also get another error : index.esm.mjs:2279 Uncaught (in promise) Error: Failed to create/update user: Operation users.findOneAndUpdate()
buffering timed out after 10000ms, pls i noticed some people also encountered the same error, please did anyone find the solution?
i keep getting this error : MongoParseError: URI malformed, around the 2:22:01 mark when working on the backend and i also get another error : index.esm.mjs:2279 Uncaught (in promise) Error: Failed to create/update user: Operation
users.findOneAndUpdate()
buffering timed out after 10000ms, pls i noticed some people also encountered the same error, please did anyone find the solution?
do not use characters in your password like $,@,#, etc. MongoDB will confuse it for termination. use auto generated password or just pure letters and numbers, no characters. It should work.
anytime i click on responses to my messages on the activity page i get this error:
Unhandled Runtime Error
Error: Error fetching thread: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Thread"
Source
lib/actions/thread.actions.ts (109:11) @ fetchThreadById
107 | return thread;
108 | } catch (error: any) {
109 | throw new Error(
Error fetching thread: ${error.message}
);
| ^
110 | }
111 | }
112 |
Call Stack
please did anybody experience this and any solution to this?
concerning the password issue there was a time when that gave me problem around the 2 hr mark but i have resolved that since, this current issue is at the 4:49 mark once i click response in the activity page i end up with that error message i pasted, secondly what do u mean database named threads and how can i do that because im not sure i saw anything like that in the tutorial, how can i name the database threads????thank you as i anticipate your response
Make sure you have the database named 'Threads' as it is and do not use funny symbols and characters in your database password. If you use something like @, or # etc. Javascript will mistake it for the end of the password. Use auto-generated password, or simply letters and numbers, no characters. Hope this helps.
…
On Thu, Jul 4, 2024 at 10:17 PM WelldiusPrimeX @.> wrote: @.* commented on this gist. ------------------------------ anytime i click on responses to my messages on the activity page i get this error: Unhandled Runtime Error Error: Error fetching thread: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Thread" Source lib/actions/thread.actions.ts (109:11) @ fetchThreadById 107 | return thread; 108 | } catch (error: any) { 109 | throw new Error(Error fetching thread: ${error.message}); | ^ 110 | } 111 | } 112 | Call Stack — Reply to this email directly, view it on GitHub https://gist.github.com/adrianhajdin/060e4c9d3d8d4274b7669e260dbbcc8e#gistcomment-5111328 or unsubscribe https://github.com/notifications/unsubscribe-auth/AJMFIQULSTKYPEQ77BUPF4DZKWNTBBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEMZYGY4TINJRU52HE2LHM5SXFJTDOJSWC5DF . You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .
no, for the database name just leave it, the problem should be solved by now after the password. Kindly share the screenshot On Sat, Jul 6, 2024 at 12:45 AM WelldiusPrimeX @.> wrote:
…
@.* commented on this gist. ------------------------------ concerning the password issue there was a time when that gave me problem around the 2 hr mark but i have resolved that since, this current issue is at the 4:49 mark once i click response in the activity page i end up with that error message i pasted, secondly what do u mean database named threads and how can i do that because im not sure i saw anything like that in the tutorial, how can i name the database threads????thank you as i anticipate your response Make sure you have the database named 'Threads' as it is and do not use funny symbols and characters in your database password. If you use something like @, or # etc. Javascript will mistake it for the end of the password. Use auto-generated password, or simply letters and numbers, no characters. Hope this helps. … <#m_2640396956183329713_> On Thu, Jul 4, 2024 at 10:17 PM WelldiusPrimeX @.*> wrote: @.** commented on this gist. ------------------------------ anytime i click on responses to my messages on the activity page i get this error: Unhandled Runtime Error Error: Error fetching thread: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Thread" Source lib/actions/thread.actions.ts (109:11) @ fetchThreadById 107 | return thread; 108 | } catch (error: any) { 109 | throw new Error(Error fetching thread: ${error.message}); | ^ 110 | } 111 | } 112 | Call Stack — Reply to this email directly, view it on GitHub https://gist.github.com/adrianhajdin/060e4c9d3d8d4274b7669e260dbbcc8e#gistcomment-5111328 or unsubscribe https://github.com/notifications/unsubscribe-auth/AJMFIQULSTKYPEQ77BUPF4DZKWNTBBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEMZYGY4TINJRU52HE2LHM5SXFJTDOJSWC5DF . You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub . — Reply to this email directly, view it on GitHub https://gist.github.com/adrianhajdin/060e4c9d3d8d4274b7669e260dbbcc8e#gistcomment-5112538 or unsubscribe https://github.com/notifications/unsubscribe-auth/AJMFIQWE4RSFWHKQVYQOV5DZK4HYPBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEMZYGY4TINJRU52HE2LHM5SXFJTDOJSWC5DF . You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .
please do u mean screenshot of the error or screenshot of the pages referenced in the error?
please let me know if you need any other thing to help me resolve the error
no, for the database name just leave it, the problem should be solved by now after the password. Kindly share the screenshot On Sat, Jul 6, 2024 at 12:45 AM WelldiusPrimeX @.> wrote:
…
@.* commented on this gist. ------------------------------ concerning the password issue there was a time when that gave me problem around the 2 hr mark but i have resolved that since, this current issue is at the 4:49 mark once i click response in the activity page i end up with that error message i pasted, secondly what do u mean database named threads and how can i do that because im not sure i saw anything like that in the tutorial, how can i name the database threads????thank you as i anticipate your response Make sure you have the database named 'Threads' as it is and do not use funny symbols and characters in your database password. If you use something like @, or # etc. Javascript will mistake it for the end of the password. Use auto-generated password, or simply letters and numbers, no characters. Hope this helps. … <#m_2640396956183329713_> On Thu, Jul 4, 2024 at 10:17 PM WelldiusPrimeX @.*> wrote: @.** commented on this gist. ------------------------------ anytime i click on responses to my messages on the activity page i get this error: Unhandled Runtime Error Error: Error fetching thread: Cast to ObjectId failed for value "undefined" (type string) at path "_id" for model "Thread" Source lib/actions/thread.actions.ts (109:11) @ fetchThreadById 107 | return thread; 108 | } catch (error: any) { 109 | throw new Error(Error fetching thread: ${error.message}); | ^ 110 | } 111 | } 112 | Call Stack — Reply to this email directly, view it on GitHub https://gist.github.com/adrianhajdin/060e4c9d3d8d4274b7669e260dbbcc8e#gistcomment-5111328 or unsubscribe https://github.com/notifications/unsubscribe-auth/AJMFIQULSTKYPEQ77BUPF4DZKWNTBBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEMZYGY4TINJRU52HE2LHM5SXFJTDOJSWC5DF . You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub . — Reply to this email directly, view it on GitHub https://gist.github.com/adrianhajdin/060e4c9d3d8d4274b7669e260dbbcc8e#gistcomment-5112538 or unsubscribe https://github.com/notifications/unsubscribe-auth/AJMFIQWE4RSFWHKQVYQOV5DZK4HYPBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEMZYGY4TINJRU52HE2LHM5SXFJTDOJSWC5DF . You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .
I had those errors too. The password for Atlas and Mongo configuration file must be the same. Go to .env and make sure it matches the one on the Atlas browser configuration password.
This stuff is really becoming fustrating, i just changed my password again and i have effected the change in my env file and still no difference unless if i dont understand what you are saying, i doubt this is what is causing the problem because if it was the site wont work at all but in my case evething is working until u vlick the thread in the activity page thats when the fetchthreadbyid errors throws up that error about cast to objectid failed for undefined i honestly dont even know what to do again
I had those errors too. The password for Atlas and Mongo configuration file must be the same. Go to .env and make sure it matches the one on the Atlas browser configuration password.
Pls i want to confirm are you saying you also encountered the cast to objectid failed error at the activity page around the 4:49 hr mark or at another place because its getting really frustrating
I had those errors too. The password for Atlas and Mongo configuration file must be the same. Go to .env and make sure it matches the one on the Atlas browser configuration password.
this is the link: https://github.com/WelldiusPrimeX/socialmediaapp.git
send your github link I will check the error
Everytime I try to open create thread it takes me back to onboarding. please help
my onboarding page is showing error Property 'username' does not exist on type '{}'
and it is showing in every userData that i have created
you need to update your middleware.ts using clerkMiddleware
, instead of authMiddleware
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)']);
export default clerkMiddleware((auth, request) => {
PublicRoutes: ['/','/api/webhook/clerk']
ignoredRoutes: ['/api/webhook/clerk']
if(!isPublicRoute(request)) {
auth().protect();
}
}, { debug: true });
export const config = {
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};
works well on my system
Does anybody else have a problem where the communities that are created, aren't being saved in MongoDB? My webhook is also going crazy with just saying attempting for each event:
Sorry if this is vague, just curious if other people have had the same problem
I am also facing the same issue. Did you find any solution ?I did find a solution! I entered the wrong link for the deployment endpoint from vercel. Go on vercel and grab the deployment link and not the link from when you go to the deployed site in the search bar
how did you fix it? he used the browser link in the video right?
i tried the deployment link as well but it did not work
did you manage?