Files
classroomio/apps/backend/src/routes/course/presign.ts
T
Nwosu EmmanuelandRotimi Best 937f1d8155 feat: add documents to Lesson materials (#572)
* feat: Initial logic for the pdf upload

* feat: uploa logic, translations and ui flow complete

* chore:console log cleanup

* feat: fixed upload issue and added cancel upload button

* feat: fixing the download document ish

* feat: All done and ready for use

* feat: updated the document viewing logic

* feat: some minor refactor

* feat: added migration file

* fix: dark mode, translations, restyling,

---------

Co-authored-by: Rotimi Best <best@classroomio.com>
2025-09-10 11:39:06 +01:00

200 lines
5.1 KiB
TypeScript

import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import {
ZCourseDocumentPresignUrlUpload,
ZCourseDownloadPresignedUrl,
ZCoursePresignUrlUpload
} from '$src/types/course';
import { BUCKET_NAME } from '$src/constants/upload';
import { CLOUDFLARE } from '$src/constants';
import type { GetSignedUrlParameters } from '$src/utils/s3';
import { Hono } from 'hono';
import { authMiddleware } from '$src/middlewares/auth';
import { generateFileKey } from '$src/utils/upload';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { s3Client } from '$src/utils/s3';
export const presignRouter = new Hono();
presignRouter.post('/upload', authMiddleware, async (c) => {
const body = await c.req.json();
const result = ZCoursePresignUrlUpload.parse(body);
const { fileName, fileType } = result;
const fileKey = generateFileKey(fileName);
try {
const command = new PutObjectCommand({
Bucket: BUCKET_NAME.VIDEOS,
Key: fileKey,
ContentType: fileType
}) as GetSignedUrlParameters[1];
const presignedUrl = await getSignedUrl(s3Client as GetSignedUrlParameters[0], command, {
expiresIn: CLOUDFLARE.R2.PRESIGN_EXPIRATION_TIME
});
return c.json({
success: true,
url: presignedUrl,
fileKey,
message: 'Pre-signed URL generated successfully'
});
} catch (error) {
console.error('Error generating pre-signed URL:', error);
if (error instanceof Error && error.name === 'SignatureDoesNotMatch') {
return c.json(
{
success: false,
type: 'SIGNATURE_ERROR',
message: 'Invalid signature for the request'
},
403
);
}
return c.json(
{
success: false,
type: 'INTERNAL_ERROR',
message: 'Error generating pre-signed URL'
},
500
);
}
});
presignRouter.post('/document/upload', authMiddleware, async (c) => {
const body = await c.req.json();
const result = ZCourseDocumentPresignUrlUpload.parse(body);
const { fileName, fileType } = result;
const fileKey = generateFileKey(fileName);
try {
const command = new PutObjectCommand({
Bucket: BUCKET_NAME.DOCUMENTS,
Key: fileKey,
ContentType: fileType
}) as GetSignedUrlParameters[1];
const presignedUrl = await getSignedUrl(s3Client as GetSignedUrlParameters[0], command, {
expiresIn: CLOUDFLARE.R2.PRESIGN_EXPIRATION_TIME
});
return c.json({
success: true,
url: presignedUrl,
fileKey,
message: 'Document pre-signed URL generated successfully'
});
} catch (error) {
console.error('Error generating document pre-signed URL:', error);
if (error instanceof Error && error.name === 'SignatureDoesNotMatch') {
return c.json(
{
success: false,
type: 'SIGNATURE_ERROR',
message: 'Invalid signature for the request'
},
403
);
}
return c.json(
{
success: false,
type: 'INTERNAL_ERROR',
message: 'Error generating document pre-signed URL'
},
500
);
}
});
presignRouter.post('/download', authMiddleware, async (c) => {
const body = await c.req.json();
const result = ZCourseDownloadPresignedUrl.parse(body);
const { keys } = result;
try {
const signedUrls: Record<string, string> = {};
await Promise.all(
keys.map(async (key) => {
const command = new GetObjectCommand({
Bucket: BUCKET_NAME.VIDEOS,
Key: key
}) as GetSignedUrlParameters[1];
const presignedUrl = await getSignedUrl(s3Client as GetSignedUrlParameters[0], command, {
expiresIn: CLOUDFLARE.R2.DOWNLOAD_EXPIRATION_TIME
});
signedUrls[key] = presignedUrl;
})
);
return c.json({
success: true,
urls: signedUrls,
message: 'Video URLs retrieved successfully'
});
} catch (error) {
console.error('Error Retrieving Video:', error);
return c.json(
{
success: false,
type: 'INTERNAL_ERROR',
message: 'Error Retrieving Video'
},
500
);
}
});
presignRouter.post('/download/document', authMiddleware, async (c) => {
const body = await c.req.json();
const result = ZCourseDownloadPresignedUrl.parse(body);
const { keys } = result;
try {
const signedUrls: Record<string, string> = {};
await Promise.all(
keys.map(async (key) => {
const command = new GetObjectCommand({
Bucket: BUCKET_NAME.DOCUMENTS,
Key: key
}) as GetSignedUrlParameters[1];
const presignedUrl = await getSignedUrl(s3Client as GetSignedUrlParameters[0], command, {
expiresIn: CLOUDFLARE.R2.DOWNLOAD_EXPIRATION_TIME
});
signedUrls[key] = presignedUrl;
})
);
return c.json({
success: true,
urls: signedUrls,
message: 'Document URLs retrieved successfully'
});
} catch (error) {
console.error('Error Retrieving Document:', error);
return c.json(
{
success: false,
type: 'INTERNAL_ERROR',
message: 'Error Retrieving Document'
},
500
);
}
});