add route to get pre-signed s3 upload url
This commit is contained in:
parent
8eceee520f
commit
6e69c1b3e3
9 changed files with 2556 additions and 12 deletions
|
|
@ -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
2469
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
10
src/env.mjs
10
src/env.mjs
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
11
src/server/aws/s3.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue