diff --git a/apps/web/actions/videos/new-comment.ts b/apps/web/actions/videos/new-comment.ts index 85a4466f7..69cf28a89 100644 --- a/apps/web/actions/videos/new-comment.ts +++ b/apps/web/actions/videos/new-comment.ts @@ -3,12 +3,19 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { nanoId } from "@cap/database/helpers"; -import { comments } from "@cap/database/schema"; -import type { Video } from "@cap/web-domain"; +import { comments, videos } from "@cap/database/schema"; +import { Video } from "@cap/web-domain"; import { revalidatePath } from "next/cache"; import { createNotification } from "@/lib/Notification"; +import { eq } from "drizzle-orm"; -export async function newComment(data: { +export async function newComment({ + content, + videoId, + type, + parentCommentId, + timestamp, +}: { content: string; videoId: Video.VideoId; type: "text" | "emoji"; @@ -16,29 +23,23 @@ export async function newComment(data: { timestamp: number; }) { const user = await getCurrentUser(); + if (!user) throw new Error("User not authenticated"); - if (!user) { - throw new Error("User not authenticated"); - } - - const content = data.content; - const videoId = data.videoId; - const type = data.type; - const parentCommentId = data.parentCommentId; - const timestamp = data.timestamp; const conditionalType = parentCommentId ? "reply" : type === "emoji" ? "reaction" : "comment"; - if (!content || !videoId) { - throw new Error("Content and videoId are required"); - } - const id = nanoId(); + const [video] = await db() + .select({ orgId: videos.orgId }) + .from(videos) + .where(eq(videos.id, videoId)); + if (!content || !videoId) throw new Error("Content and videoId are required"); + if (!video) throw new Error("Video not found"); const newComment = { - id: id, + id: nanoId(), authorId: user.id, type: type, content: content, @@ -56,7 +57,7 @@ export async function newComment(data: { type: conditionalType, videoId, authorId: user.id, - comment: { id, content }, + comment: { id: newComment.id, content }, parentCommentId, }); } catch (error) { diff --git a/apps/web/app/(org)/dashboard/caps/page.tsx b/apps/web/app/(org)/dashboard/caps/page.tsx index 98f4753f4..23726f945 100644 --- a/apps/web/app/(org)/dashboard/caps/page.tsx +++ b/apps/web/app/(org)/dashboard/caps/page.tsx @@ -125,7 +125,7 @@ export default async function CapsPage(props: { domainVerified: organizations.domainVerified, }) .from(organizations) - .where(eq(organizations.id, user.activeOrganizationId)) + .where(eq(organizations.id, user.activeOrganizationId ?? sql`NULL`)) .limit(1); let customDomain: string | null = null; @@ -214,7 +214,7 @@ export default async function CapsPage(props: { .from(folders) .where( and( - eq(folders.organizationId, user.activeOrganizationId), + eq(folders.organizationId, user.activeOrganizationId ?? sql`NULL`), isNull(folders.parentId), isNull(folders.spaceId), ), diff --git a/apps/web/app/(org)/dashboard/settings/organization/page.tsx b/apps/web/app/(org)/dashboard/settings/organization/page.tsx index 48bed4f14..2519d8280 100644 --- a/apps/web/app/(org)/dashboard/settings/organization/page.tsx +++ b/apps/web/app/(org)/dashboard/settings/organization/page.tsx @@ -1,7 +1,7 @@ import { db } from "@cap/database"; import { getCurrentUser } from "@cap/database/auth/session"; import { organizationMembers, organizations } from "@cap/database/schema"; -import { and, eq } from "drizzle-orm"; +import { and, eq, sql } from "drizzle-orm"; import type { Metadata } from "next"; import { redirect } from "next/navigation"; import { getDashboardData } from "../../dashboard-data"; @@ -31,7 +31,7 @@ export default async function OrganizationPage() { .where( and( eq(organizationMembers.userId, user.id), - eq(organizations.id, user.activeOrganizationId), + eq(organizations.id, user.activeOrganizationId ?? sql`NULL`), ), ); diff --git a/apps/web/app/api/notifications/route.ts b/apps/web/app/api/notifications/route.ts index 0ea8748be..037055d87 100644 --- a/apps/web/app/api/notifications/route.ts +++ b/apps/web/app/api/notifications/route.ts @@ -55,7 +55,10 @@ export async function GET() { .where( and( eq(notifications.recipientId, currentUser.id), - eq(notifications.orgId, currentUser.activeOrganizationId), + eq( + notifications.orgId, + currentUser.activeOrganizationId ?? sql`NULL`, + ), ), ) .orderBy( @@ -72,7 +75,10 @@ export async function GET() { .where( and( eq(notifications.recipientId, currentUser.id), - eq(notifications.orgId, currentUser.activeOrganizationId), + eq( + notifications.orgId, + currentUser.activeOrganizationId ?? sql`NULL`, + ), ), ) .groupBy(notifications.type); diff --git a/apps/web/app/s/[videoId]/_components/tabs/Activity/Comment.tsx b/apps/web/app/s/[videoId]/_components/tabs/Activity/Comment.tsx index d88e54701..c6798d10e 100644 --- a/apps/web/app/s/[videoId]/_components/tabs/Activity/Comment.tsx +++ b/apps/web/app/s/[videoId]/_components/tabs/Activity/Comment.tsx @@ -52,7 +52,7 @@ const Comment: React.FC<{ const handleDelete = () => { if (window.confirm("Are you sure you want to delete this comment?")) { - onDelete(comment.id, comment.parentCommentId); + onDelete(comment.id, comment.parentCommentId ?? undefined); } }; diff --git a/apps/web/app/s/[videoId]/page.tsx b/apps/web/app/s/[videoId]/page.tsx index aa26626fc..85a39f664 100644 --- a/apps/web/app/s/[videoId]/page.tsx +++ b/apps/web/app/s/[videoId]/page.tsx @@ -619,7 +619,7 @@ async function AuthorizedContent({ .from(comments) .where(eq(comments.id, replyId)) .limit(1); - toplLevelCommentId = parentComment?.parentCommentId; + toplLevelCommentId = parentComment?.parentCommentId ?? undefined; } const commentToBringToTheTop = toplLevelCommentId ?? commentId; diff --git a/apps/web/lib/Notification.ts b/apps/web/lib/Notification.ts index 503574a2a..5d61b515d 100644 --- a/apps/web/lib/Notification.ts +++ b/apps/web/lib/Notification.ts @@ -88,7 +88,6 @@ export async function createNotification( const [recipientUser] = await db() .select({ preferences: users.preferences, - activeOrganizationId: users.activeOrganizationId, }) .from(users) .where(eq(users.id, recipientId)) @@ -122,7 +121,7 @@ export async function createNotification( await db().insert(notifications).values({ id: notificationId, - orgId: recipientUser.activeOrganizationId, + orgId: undefined, // TODO: BRUHHHHH: recipientUser.activeOrganizationId, recipientId, type, data, diff --git a/packages/database/auth/session.ts b/packages/database/auth/session.ts index 3434da801..6daaf4991 100644 --- a/packages/database/auth/session.ts +++ b/packages/database/auth/session.ts @@ -22,7 +22,12 @@ export const getCurrentUser = cache( .from(users) .where(eq(users.id, session.user.id)); - return currentUser ?? null; + return currentUser + ? { + ...currentUser, + activeOrganizationId: currentUser?.activeOrganizationId ?? null, + } + : null; }, ); diff --git a/packages/database/schema.ts b/packages/database/schema.ts index ccf63a2cc..f4a9311bb 100644 --- a/packages/database/schema.ts +++ b/packages/database/schema.ts @@ -1,7 +1,6 @@ import type { Folder, S3Bucket, Video } from "@cap/web-domain"; import { boolean, - customType, datetime, float, index, @@ -11,7 +10,6 @@ import { primaryKey, text, timestamp, - tinyint, uniqueIndex, varchar, } from "drizzle-orm/mysql-core"; @@ -20,35 +18,17 @@ import { relations } from "drizzle-orm/relations"; import { nanoIdLength } from "./helpers.ts"; import type { VideoMetadata } from "./types/index.ts"; -const nanoId = customType<{ data: string; notNull: true }>({ - dataType() { - return `varchar(${nanoIdLength})`; - }, -}); - -const nanoIdNullable = customType<{ data: string; notNull: false }>({ - dataType() { - return `varchar(${nanoIdLength})`; - }, -}); - -// Add a custom type for encrypted strings -const encryptedText = customType<{ data: string; notNull: true }>({ - dataType() { - return "text"; - }, -}); - -const encryptedTextNullable = customType<{ data: string; notNull: false }>({ - dataType() { - return "text"; - }, -}); +const nanoId = (name: string) => + varchar(name, { length: nanoIdLength }).notNull(); +const nanoIdNullable = (name: string) => + varchar(name, { length: nanoIdLength }); +const encryptedText = (name: string) => text(name).notNull(); +const encryptedTextNullable = (name: string) => text(name); export const users = mysqlTable( "users", { - id: nanoId("id").notNull().primaryKey().unique(), + id: nanoId("id").primaryKey().unique(), name: varchar("name", { length: 255 }), lastName: varchar("lastName", { length: 255 }), email: varchar("email", { length: 255 }).unique().notNull(), @@ -83,7 +63,7 @@ export const users = mysqlTable( }; } | null>() .default(null), - activeOrganizationId: nanoId("activeOrganizationId"), + activeOrganizationId: nanoIdNullable("activeOrganizationId"), created_at: timestamp("created_at").notNull().defaultNow(), updated_at: timestamp("updated_at").notNull().defaultNow().onUpdateNow(), onboarding_completed_at: timestamp("onboarding_completed_at"), @@ -320,7 +300,7 @@ export const comments = mysqlTable( videoId: nanoId("videoId").notNull().$type(), createdAt: timestamp("createdAt").notNull().defaultNow(), updatedAt: timestamp("updatedAt").notNull().defaultNow().onUpdateNow(), - parentCommentId: nanoId("parentCommentId"), + parentCommentId: nanoIdNullable("parentCommentId"), }, (table) => ({ videoIdIndex: index("video_id_idx").on(table.videoId),