R2 S3-API code

I feel like I must be doing something wrong if I have to do this confusedBecel
const key = parsedUrl.pathname.split('/').slice(2).join('/');
const key = parsedUrl.pathname.split('/').slice(2).join('/');
3 Replies
Harris
Harris14mo ago
import { NextRequest, NextResponse } from 'next/server';

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { randomUUID } from 'crypto';

if (!process.env.S3_ENDPOINT) {
throw new Error('S3_ENDPOINT is not defined');
}

if (!process.env.S3_ACCESS_KEY_ID) {
throw new Error('S3_ACCESS_KEY_ID is not defined');
}

if (!process.env.S3_SECRET_ACCESS_KEY) {
throw new Error('CLOUDFLARE_SECRET_ACCESS_KEY is not defined');
}

const S3 = new S3Client({
region: 'auto',
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
},
});

// JSON body: { "filename": "my-file.txt" }
export async function POST(request: NextRequest) {
const data = await request.json();
const filename = data.filename;

const url = await generateSignedUrl(filename);

return NextResponse.json({ url });
}

async function generateSignedUrl(filename: string) {
// Create randomized UUID for filename
const uuid = randomUUID();

// Attach file extension to UUID
const extension = filename.split('.').pop();

const key = `${uuid}.${extension}`;

const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: key,
});

const url = await getSignedUrl(S3, command, {
expiresIn: 60 * 60 * 24 * 7, // 7 days
});
return url;
}
import { NextRequest, NextResponse } from 'next/server';

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { randomUUID } from 'crypto';

if (!process.env.S3_ENDPOINT) {
throw new Error('S3_ENDPOINT is not defined');
}

if (!process.env.S3_ACCESS_KEY_ID) {
throw new Error('S3_ACCESS_KEY_ID is not defined');
}

if (!process.env.S3_SECRET_ACCESS_KEY) {
throw new Error('CLOUDFLARE_SECRET_ACCESS_KEY is not defined');
}

const S3 = new S3Client({
region: 'auto',
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
},
});

// JSON body: { "filename": "my-file.txt" }
export async function POST(request: NextRequest) {
const data = await request.json();
const filename = data.filename;

const url = await generateSignedUrl(filename);

return NextResponse.json({ url });
}

async function generateSignedUrl(filename: string) {
// Create randomized UUID for filename
const uuid = randomUUID();

// Attach file extension to UUID
const extension = filename.split('.').pop();

const key = `${uuid}.${extension}`;

const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: key,
});

const url = await getSignedUrl(S3, command, {
expiresIn: 60 * 60 * 24 * 7, // 7 days
});
return url;
}
import { NextRequest, NextResponse } from 'next/server';

import { S3Client, HeadObjectCommand } from '@aws-sdk/client-s3';
import { PrismaClient } from '@prisma/client';
import { getSessionUser } from '@/lib/session';

if (!process.env.S3_ENDPOINT) {
throw new Error('S3_ENDPOINT is not defined');
}

if (!process.env.S3_ACCESS_KEY_ID) {
throw new Error('S3_ACCESS_KEY_ID is not defined');
}

if (!process.env.S3_SECRET_ACCESS_KEY) {
throw new Error('CLOUDFLARE_SECRET_ACCESS_KEY is not defined');
}

const S3 = new S3Client({
region: 'auto',
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
},
});

const prisma = new PrismaClient();

export async function POST(request: NextRequest) {
const sessionUser = await getSessionUser();
if (!sessionUser) {
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
}

const user = await prisma.user.findUnique({
where: {
email: sessionUser.email,
},
});

if (!user) {
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
}

// Validation
const { url } = await request.json();

if (!url) {
return NextResponse.json({ error: 'url is required' });
}

// Determine the type of URL
const parsedUrl = new URL(url);

const camcorderS3Hostname = process.env.S3_HOSTNAME;
const youtubeHostnames = ['www.youtube.com', 'youtu.be'];

if (parsedUrl.hostname === camcorderS3Hostname) {
// This is a Camcorder S3 URL
// No need to download the file, since it's already on S3

let name;
let mimeType;
let size;

const key = parsedUrl.pathname.split('/').slice(2).join('/');

try {
const headObjectCommand = new HeadObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: key,
});

const headObjectResponse = await S3.send(headObjectCommand);

name = headObjectResponse.Metadata?.name;
mimeType = headObjectResponse.ContentType;
size = headObjectResponse.ContentLength;
} catch (error) {
console.error(error);
return NextResponse.json({ error: 'unknown url' });
}

const asset = await prisma.asset.create({
data: {
name: name || parsedUrl.pathname,
url: parsedUrl.hostname + parsedUrl.pathname,
mimeType: mimeType || 'video/mp4',
size: size || 0,
user: {
connect: {
id: user.id,
},
},
},
});

console.log(asset);
} else if (youtubeHostnames.includes(parsedUrl.hostname)) {
// This is a Youtube URL
// Need to trigger a Youtube download and then upload to S3
} else if (parsedUrl.pathname.endsWith('.mp4')) {
// This is an MP4 URL
// Need to download the file and then upload to S3
} else {
// This is an unknown URL
// Return an error
return NextResponse.json({ error: 'unknown url' });
}

// Save the associated video file's S3 URL to the database as a video record

// Process the video file through the transcription pipeline

// Save the transcription to the database as a transcription record

return NextResponse.json({ url });
}
import { NextRequest, NextResponse } from 'next/server';

import { S3Client, HeadObjectCommand } from '@aws-sdk/client-s3';
import { PrismaClient } from '@prisma/client';
import { getSessionUser } from '@/lib/session';

if (!process.env.S3_ENDPOINT) {
throw new Error('S3_ENDPOINT is not defined');
}

if (!process.env.S3_ACCESS_KEY_ID) {
throw new Error('S3_ACCESS_KEY_ID is not defined');
}

if (!process.env.S3_SECRET_ACCESS_KEY) {
throw new Error('CLOUDFLARE_SECRET_ACCESS_KEY is not defined');
}

const S3 = new S3Client({
region: 'auto',
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
},
});

const prisma = new PrismaClient();

export async function POST(request: NextRequest) {
const sessionUser = await getSessionUser();
if (!sessionUser) {
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
}

const user = await prisma.user.findUnique({
where: {
email: sessionUser.email,
},
});

if (!user) {
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
}

// Validation
const { url } = await request.json();

if (!url) {
return NextResponse.json({ error: 'url is required' });
}

// Determine the type of URL
const parsedUrl = new URL(url);

const camcorderS3Hostname = process.env.S3_HOSTNAME;
const youtubeHostnames = ['www.youtube.com', 'youtu.be'];

if (parsedUrl.hostname === camcorderS3Hostname) {
// This is a Camcorder S3 URL
// No need to download the file, since it's already on S3

let name;
let mimeType;
let size;

const key = parsedUrl.pathname.split('/').slice(2).join('/');

try {
const headObjectCommand = new HeadObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: key,
});

const headObjectResponse = await S3.send(headObjectCommand);

name = headObjectResponse.Metadata?.name;
mimeType = headObjectResponse.ContentType;
size = headObjectResponse.ContentLength;
} catch (error) {
console.error(error);
return NextResponse.json({ error: 'unknown url' });
}

const asset = await prisma.asset.create({
data: {
name: name || parsedUrl.pathname,
url: parsedUrl.hostname + parsedUrl.pathname,
mimeType: mimeType || 'video/mp4',
size: size || 0,
user: {
connect: {
id: user.id,
},
},
},
});

console.log(asset);
} else if (youtubeHostnames.includes(parsedUrl.hostname)) {
// This is a Youtube URL
// Need to trigger a Youtube download and then upload to S3
} else if (parsedUrl.pathname.endsWith('.mp4')) {
// This is an MP4 URL
// Need to download the file and then upload to S3
} else {
// This is an unknown URL
// Return an error
return NextResponse.json({ error: 'unknown url' });
}

// Save the associated video file's S3 URL to the database as a video record

// Process the video file through the transcription pipeline

// Save the transcription to the database as a transcription record

return NextResponse.json({ url });
}
Erisa
Erisa14mo ago
Sounds like the S3 client youre using is trying to use path style and is putting the bucket name on the path?
Harris
Harris14mo ago
I'm using the AWS S3 sdk I wonder if there's a way I can tell it not to do that