358 lines
9.1 KiB
TypeScript
358 lines
9.1 KiB
TypeScript
import { z } from "zod";
|
|
|
|
import {
|
|
createTRPCRouter,
|
|
protectedProcedure,
|
|
publicProcedure,
|
|
} from "~/server/api/trpc";
|
|
import {
|
|
DeleteObjectCommand,
|
|
GetObjectCommand,
|
|
PutObjectCommand,
|
|
} from "@aws-sdk/client-s3";
|
|
import { env } from "~/env.mjs";
|
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
export const videoRouter = createTRPCRouter({
|
|
getAll: protectedProcedure.query(
|
|
async ({ ctx: { prisma, session, s3, posthog } }) => {
|
|
const videos = await prisma.video.findMany({
|
|
where: {
|
|
userId: session.user.id,
|
|
},
|
|
});
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "viewing video list",
|
|
properties: {
|
|
videoAmount: videos.length,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
const videosWithThumbnailUrl = await Promise.all(
|
|
videos.map(async (video) => {
|
|
const thumbnailUrl = await getSignedUrl(
|
|
s3,
|
|
new GetObjectCommand({
|
|
Bucket: env.AWS_BUCKET_NAME,
|
|
Key: video.userId + "/" + video.id + "-thumbnail",
|
|
})
|
|
);
|
|
|
|
return { ...video, thumbnailUrl };
|
|
})
|
|
);
|
|
|
|
return videosWithThumbnailUrl;
|
|
}
|
|
),
|
|
get: publicProcedure
|
|
.input(z.object({ videoId: z.string() }))
|
|
.query(async ({ ctx, input }) => {
|
|
const { s3, posthog, session, prisma } = ctx;
|
|
const video = await prisma.video.findUnique({
|
|
where: {
|
|
id: input.videoId,
|
|
},
|
|
include: {
|
|
user: true,
|
|
},
|
|
});
|
|
if (!video) {
|
|
throw new TRPCError({ code: "NOT_FOUND" });
|
|
}
|
|
|
|
if (video.userId !== session?.user.id && !video.sharing) {
|
|
throw new TRPCError({ code: "FORBIDDEN" });
|
|
}
|
|
|
|
if (session) {
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "viewing video",
|
|
properties: {
|
|
videoId: video.id,
|
|
videoCreatedAt: video.createdAt,
|
|
videoUpdatedAt: video.updatedAt,
|
|
videoUser: video.user.id,
|
|
videoSharing: video.sharing,
|
|
videoDeleteAfterLinkExpires: video.delete_after_link_expires,
|
|
videoShareLinkExpiresAt: video.shareLinkExpiresAt,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
}
|
|
|
|
const getObjectCommand = new GetObjectCommand({
|
|
Bucket: env.AWS_BUCKET_NAME,
|
|
Key: video.userId + "/" + video.id,
|
|
});
|
|
|
|
const signedUrl = await getSignedUrl(s3, getObjectCommand);
|
|
|
|
return { ...video, video_url: signedUrl };
|
|
}),
|
|
getUploadUrl: protectedProcedure
|
|
.input(z.object({ key: z.string() }))
|
|
.mutation(async ({ ctx: { prisma, session, s3, posthog }, input }) => {
|
|
const { key } = input;
|
|
|
|
const videos = await prisma.video.findMany({
|
|
where: {
|
|
userId: session.user.id,
|
|
},
|
|
});
|
|
|
|
if (
|
|
videos.length >= 10 &&
|
|
session.user.stripeSubscriptionStatus !== "active"
|
|
) {
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "hit video upload limit",
|
|
properties: {
|
|
videoAmount: videos.length,
|
|
stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
throw new TRPCError({
|
|
code: "FORBIDDEN",
|
|
message:
|
|
"Sorry, you have reached the maximum video upload limit on our free tier. Please upgrade to upload more.",
|
|
});
|
|
}
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "uploading video",
|
|
properties: {
|
|
videoAmount: videos.length,
|
|
stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
const video = await prisma.video.create({
|
|
data: {
|
|
userId: session.user.id,
|
|
title: key,
|
|
},
|
|
});
|
|
|
|
const signedVideoUrl = await getSignedUrl(
|
|
s3,
|
|
new PutObjectCommand({
|
|
Bucket: env.AWS_BUCKET_NAME,
|
|
Key: session.user.id + "/" + video.id,
|
|
})
|
|
);
|
|
|
|
const signedThumbnailUrl = await getSignedUrl(
|
|
s3,
|
|
new PutObjectCommand({
|
|
Bucket: env.AWS_BUCKET_NAME,
|
|
Key: video.userId + "/" + video.id + "-thumbnail",
|
|
})
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
id: video.id,
|
|
signedVideoUrl,
|
|
signedThumbnailUrl,
|
|
};
|
|
}),
|
|
setSharing: protectedProcedure
|
|
.input(z.object({ videoId: z.string(), sharing: z.boolean() }))
|
|
.mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
|
|
const updateVideo = await prisma.video.updateMany({
|
|
where: {
|
|
id: input.videoId,
|
|
userId: session.user.id,
|
|
},
|
|
data: {
|
|
sharing: input.sharing,
|
|
},
|
|
});
|
|
|
|
if (updateVideo.count === 0) {
|
|
throw new TRPCError({ code: "FORBIDDEN" });
|
|
}
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "update video setSharing",
|
|
properties: {
|
|
videoId: input.videoId,
|
|
videoSharing: input.sharing,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
return {
|
|
success: true,
|
|
updateVideo,
|
|
};
|
|
}),
|
|
setDeleteAfterLinkExpires: protectedProcedure
|
|
.input(
|
|
z.object({ videoId: z.string(), delete_after_link_expires: z.boolean() })
|
|
)
|
|
.mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
|
|
const updateVideo = await prisma.video.updateMany({
|
|
where: {
|
|
id: input.videoId,
|
|
userId: session.user.id,
|
|
},
|
|
data: {
|
|
delete_after_link_expires: input.delete_after_link_expires,
|
|
},
|
|
});
|
|
|
|
if (updateVideo.count === 0) {
|
|
throw new TRPCError({ code: "FORBIDDEN" });
|
|
}
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "update video delete_after_link_expires",
|
|
properties: {
|
|
videoId: input.videoId,
|
|
delete_after_link_expires: input.delete_after_link_expires,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
return {
|
|
success: true,
|
|
updateVideo,
|
|
};
|
|
}),
|
|
setShareLinkExpiresAt: protectedProcedure
|
|
.input(
|
|
z.object({
|
|
videoId: z.string(),
|
|
shareLinkExpiresAt: z.nullable(z.date()),
|
|
})
|
|
)
|
|
.mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
|
|
const updateVideo = await prisma.video.updateMany({
|
|
where: {
|
|
id: input.videoId,
|
|
userId: session.user.id,
|
|
},
|
|
data: {
|
|
shareLinkExpiresAt: input.shareLinkExpiresAt,
|
|
},
|
|
});
|
|
|
|
if (updateVideo.count === 0) {
|
|
throw new TRPCError({ code: "FORBIDDEN" });
|
|
}
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "update video shareLinkExpiresAt",
|
|
properties: {
|
|
videoId: input.videoId,
|
|
shareLinkExpiresAt: input.shareLinkExpiresAt,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
return {
|
|
success: true,
|
|
updateVideo,
|
|
};
|
|
}),
|
|
renameVideo: protectedProcedure
|
|
.input(
|
|
z.object({
|
|
videoId: z.string(),
|
|
title: z.string(),
|
|
})
|
|
)
|
|
.mutation(async ({ ctx: { prisma, session, posthog }, input }) => {
|
|
const updateVideo = await prisma.video.updateMany({
|
|
where: {
|
|
id: input.videoId,
|
|
userId: session.user.id,
|
|
},
|
|
data: {
|
|
title: input.title,
|
|
},
|
|
});
|
|
|
|
if (updateVideo.count === 0) {
|
|
throw new TRPCError({ code: "FORBIDDEN" });
|
|
}
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "update video title",
|
|
properties: {
|
|
videoId: input.videoId,
|
|
title: input.title,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
return {
|
|
success: true,
|
|
updateVideo,
|
|
};
|
|
}),
|
|
deleteVideo: protectedProcedure
|
|
.input(
|
|
z.object({
|
|
videoId: z.string(),
|
|
})
|
|
)
|
|
.mutation(async ({ ctx: { prisma, session, s3, posthog }, input }) => {
|
|
const deleteVideo = await prisma.video.deleteMany({
|
|
where: {
|
|
id: input.videoId,
|
|
userId: session.user.id,
|
|
},
|
|
});
|
|
|
|
if (deleteVideo.count === 0) {
|
|
throw new TRPCError({ code: "FORBIDDEN" });
|
|
}
|
|
|
|
posthog?.capture({
|
|
distinctId: session.user.id,
|
|
event: "video delete",
|
|
properties: {
|
|
videoId: input.videoId,
|
|
},
|
|
});
|
|
void posthog?.shutdownAsync();
|
|
|
|
const deleteVideoObject = await s3.send(
|
|
new DeleteObjectCommand({
|
|
Bucket: env.AWS_BUCKET_NAME,
|
|
Key: session.user.id + "/" + input.videoId,
|
|
})
|
|
);
|
|
|
|
const deleteThumbnailObject = await s3.send(
|
|
new DeleteObjectCommand({
|
|
Bucket: env.AWS_BUCKET_NAME,
|
|
Key: session.user.id + "/" + input.videoId + "-thumbnail",
|
|
})
|
|
);
|
|
|
|
return {
|
|
success: true,
|
|
deleteVideo,
|
|
deleteVideoObject,
|
|
deleteThumbnailObject,
|
|
};
|
|
}),
|
|
});
|