add route to get pre-signed s3 upload url

This commit is contained in:
MarconLP 2023-04-11 21:15:42 +02:00
parent 8eceee520f
commit 6e69c1b3e3
No known key found for this signature in database
GPG key ID: F4CAFFDFA3451D5E
9 changed files with 2556 additions and 12 deletions

View file

@ -18,5 +18,15 @@ const config = {
locales: ["en"],
defaultLocale: "en",
},
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '*.backblazeb2.com',
port: '',
pathname: '/**',
},
],
},
};
export default config;

2469
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,8 @@
"start": "next start"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.310.0",
"@aws-sdk/s3-request-presigner": "^3.310.0",
"@next-auth/prisma-adapter": "^1.0.5",
"@prisma/client": "^4.11.0",
"@tanstack/react-query": "^4.28.0",

View file

@ -21,6 +21,8 @@ model Video {
updatedAt DateTime @updatedAt
title String
video_url String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
// Necessary for Next auth
@ -58,6 +60,7 @@ model User {
image String?
accounts Account[]
sessions Session[]
videos Video[]
}
model VerificationToken {

View file

@ -23,6 +23,11 @@ const server = z.object({
GOOGLE_CLIENT_SECRET: z.string(),
GITHUB_ID: z.string(),
GITHUB_SECRET: z.string(),
AWS_ENDPOINT: z.string(),
AWS_REGION: z.string(),
AWS_KEY_ID: z.string(),
AWS_ACCESS_KEY: z.string(),
AWS_BUCKET_NAME: z.string()
});
/**
@ -49,6 +54,11 @@ const processEnv = {
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
GITHUB_ID: process.env.GITHUB_ID,
GITHUB_SECRET: process.env.GITHUB_SECRET,
AWS_ENDPOINT: process.env.AWS_ENDPOINT,
AWS_REGION: process.env.AWS_REGION,
AWS_KEY_ID: process.env.AWS_KEY_ID,
AWS_ACCESS_KEY: process.env.AWS_ACCESS_KEY,
AWS_BUCKET_NAME: process.env.AWS_BUCKET_NAME
};
// Don't touch the part below

View file

@ -3,8 +3,25 @@ import Head from "next/head";
import { api } from "~/utils/api";
import Link from "next/link";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import Image from "next/image";
const VideoList: NextPage = () => {
const router = useRouter();
const { status } = useSession();
const { data: videos } = api.video.getAll.useQuery();
const getUploadUrl = api.video.getUploadUrl.useMutation();
if (status === "unauthenticated") {
void router.push("/sign-in");
}
const onUpload = () => {
const data = getUploadUrl.mutate({ key: "something random" });
console.log(data);
};
return (
<>
<Head>
@ -16,7 +33,10 @@ const VideoList: NextPage = () => {
<div className="flex min-h-[80px] w-full items-center justify-between border-b border-solid border-b-[#E7E9EB] bg-white px-6">
<span>Screenity</span>
<div>
<span className="cursor-pointer rounded border border-[#0000001a] px-2 py-2 text-sm text-[#292d34] hover:bg-[#fafbfc]">
<span
onClick={onUpload}
className="cursor-pointer rounded border border-[#0000001a] px-2 py-2 text-sm text-[#292d34] hover:bg-[#fafbfc]"
>
New video
</span>
</div>
@ -135,10 +155,12 @@ const VideoCard = ({ title, id, timestamp }: VideoCardProps) => {
return (
<Link href={`/share/${id}`}>
<div className="h-[240px] w-[250px] cursor-pointer overflow-hidden rounded-lg border border-[#6c668533] text-sm font-normal">
<figure>
<img
src="https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg"
alt="Shoes"
<figure className="relative">
<Image
src="https://f003.backblazeb2.com/file/test-bucket-dev/green+vs+blue+bbbles.jpg"
alt="video thumbnail"
fill={true}
className="!relative object-contain"
/>
</figure>
<div className="m-4 flex flex-col">

View file

@ -1,16 +1,16 @@
import { z } from "zod";
import {
createTRPCRouter,
publicProcedure,
protectedProcedure,
} from "~/server/api/trpc";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { env } from "~/env.mjs";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export const videoRouter = createTRPCRouter({
getAll: publicProcedure.query(({ ctx }) => {
getAll: protectedProcedure.query(({ ctx }) => {
console.log(ctx.session);
return ctx.prisma.video.findMany();
}),
get: publicProcedure
get: protectedProcedure
.input(z.object({ videoId: z.string() }))
.query(async ({ ctx, input }) => {
const video = await ctx.prisma.video.findUnique({
@ -20,4 +20,19 @@ export const videoRouter = createTRPCRouter({
});
return video;
}),
getUploadUrl: protectedProcedure
.input(z.object({ key: z.string() }))
.mutation(async ({ ctx, input }) => {
const { key } = input;
const { s3 } = ctx;
const putObjectCommand = new PutObjectCommand({
Bucket: env.AWS_BUCKET_NAME,
Key: key,
});
const signedUrl = await getSignedUrl(s3, putObjectCommand);
return signedUrl;
}),
});

View file

@ -38,6 +38,7 @@ const createInnerTRPCContext = (opts: CreateContextOptions) => {
return {
session: opts.session,
prisma,
s3,
};
};
@ -68,6 +69,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";
import { s3 } from "~/server/aws/s3";
const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,

11
src/server/aws/s3.ts Normal file
View file

@ -0,0 +1,11 @@
import { S3 } from "@aws-sdk/client-s3";
import { env } from "~/env.mjs";
export const s3 = new S3({
endpoint: env.AWS_ENDPOINT,
region: env.AWS_REGION,
credentials: {
accessKeyId: env.AWS_KEY_ID,
secretAccessKey: env.AWS_ACCESS_KEY,
},
});