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"],
|
locales: ["en"],
|
||||||
defaultLocale: "en",
|
defaultLocale: "en",
|
||||||
},
|
},
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: '*.backblazeb2.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/**',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
export default config;
|
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"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.310.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.310.0",
|
||||||
"@next-auth/prisma-adapter": "^1.0.5",
|
"@next-auth/prisma-adapter": "^1.0.5",
|
||||||
"@prisma/client": "^4.11.0",
|
"@prisma/client": "^4.11.0",
|
||||||
"@tanstack/react-query": "^4.28.0",
|
"@tanstack/react-query": "^4.28.0",
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ model Video {
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
title String
|
title String
|
||||||
video_url String
|
video_url String
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for Next auth
|
// Necessary for Next auth
|
||||||
|
|
@ -58,6 +60,7 @@ model User {
|
||||||
image String?
|
image String?
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
sessions Session[]
|
sessions Session[]
|
||||||
|
videos Video[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model VerificationToken {
|
model VerificationToken {
|
||||||
|
|
|
||||||
10
src/env.mjs
10
src/env.mjs
|
|
@ -23,6 +23,11 @@ const server = z.object({
|
||||||
GOOGLE_CLIENT_SECRET: z.string(),
|
GOOGLE_CLIENT_SECRET: z.string(),
|
||||||
GITHUB_ID: z.string(),
|
GITHUB_ID: z.string(),
|
||||||
GITHUB_SECRET: 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,
|
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
||||||
GITHUB_ID: process.env.GITHUB_ID,
|
GITHUB_ID: process.env.GITHUB_ID,
|
||||||
GITHUB_SECRET: process.env.GITHUB_SECRET,
|
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
|
// Don't touch the part below
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,25 @@ import Head from "next/head";
|
||||||
|
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import Link from "next/link";
|
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 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<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">
|
<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>
|
<span>Screenity</span>
|
||||||
<div>
|
<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
|
New video
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -135,10 +155,12 @@ const VideoCard = ({ title, id, timestamp }: VideoCardProps) => {
|
||||||
return (
|
return (
|
||||||
<Link href={`/share/${id}`}>
|
<Link href={`/share/${id}`}>
|
||||||
<div className="h-[240px] w-[250px] cursor-pointer overflow-hidden rounded-lg border border-[#6c668533] text-sm font-normal">
|
<div className="h-[240px] w-[250px] cursor-pointer overflow-hidden rounded-lg border border-[#6c668533] text-sm font-normal">
|
||||||
<figure>
|
<figure className="relative">
|
||||||
<img
|
<Image
|
||||||
src="https://daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.jpg"
|
src="https://f003.backblazeb2.com/file/test-bucket-dev/green+vs+blue+bbbles.jpg"
|
||||||
alt="Shoes"
|
alt="video thumbnail"
|
||||||
|
fill={true}
|
||||||
|
className="!relative object-contain"
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
<div className="m-4 flex flex-col">
|
<div className="m-4 flex flex-col">
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
import { PutObjectCommand } from "@aws-sdk/client-s3";
|
||||||
publicProcedure,
|
import { env } from "~/env.mjs";
|
||||||
protectedProcedure,
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const videoRouter = createTRPCRouter({
|
export const videoRouter = createTRPCRouter({
|
||||||
getAll: publicProcedure.query(({ ctx }) => {
|
getAll: protectedProcedure.query(({ ctx }) => {
|
||||||
|
console.log(ctx.session);
|
||||||
return ctx.prisma.video.findMany();
|
return ctx.prisma.video.findMany();
|
||||||
}),
|
}),
|
||||||
get: publicProcedure
|
get: protectedProcedure
|
||||||
.input(z.object({ videoId: z.string() }))
|
.input(z.object({ videoId: z.string() }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const video = await ctx.prisma.video.findUnique({
|
const video = await ctx.prisma.video.findUnique({
|
||||||
|
|
@ -20,4 +20,19 @@ export const videoRouter = createTRPCRouter({
|
||||||
});
|
});
|
||||||
return video;
|
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 {
|
return {
|
||||||
session: opts.session,
|
session: opts.session,
|
||||||
prisma,
|
prisma,
|
||||||
|
s3,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -68,6 +69,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
||||||
import { initTRPC, TRPCError } from "@trpc/server";
|
import { initTRPC, TRPCError } from "@trpc/server";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
import { s3 } from "~/server/aws/s3";
|
||||||
|
|
||||||
const t = initTRPC.context<typeof createTRPCContext>().create({
|
const t = initTRPC.context<typeof createTRPCContext>().create({
|
||||||
transformer: superjson,
|
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