feat: video upload - disable muse upload + upgrade server (#207)
* fix: only upload to cloudflare * feat: ci/cd for apps/backend
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
name: Deloy Backend to Flyio
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: Deploy backend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
concurrency: deploy-group # optional: ensure only one action runs at a time
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: superfly/flyctl-actions/setup-flyctl@master
|
||||||
|
- run: flyctl deploy --remote-only
|
||||||
|
env:
|
||||||
|
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "hellonode",
|
"name": "backend",
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "Express Server for long processing",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
@@ -10,14 +10,14 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/fly-examples/hellonode.git"
|
"url": "git+https://github.com/rotimi-best/classroomio.git"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/fly-examples/hellonode/issues"
|
"url": "https://github.com/rotimi-best/classroomio/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/fly-examples/hellonode#readme",
|
"homepage": "https://github.com/rotimi-best/classroomio#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.388.0",
|
"@aws-sdk/client-s3": "^3.388.0",
|
||||||
"@supabase/supabase-js": "^2.32.0",
|
"@supabase/supabase-js": "^2.32.0",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ app.use(cors());
|
|||||||
// Rate limiting middleware
|
// Rate limiting middleware
|
||||||
const limiter = rateLimit({
|
const limiter = rateLimit({
|
||||||
windowMs: 1000,
|
windowMs: 1000,
|
||||||
max: 10,
|
max: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(limiter);
|
app.use(limiter);
|
||||||
@@ -34,9 +34,9 @@ app.use('/downloadCourse', downloadCourse);
|
|||||||
app.use('/uploadVideo', uploadVideo);
|
app.use('/uploadVideo', uploadVideo);
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
res.send('Hello ClassroomIO');
|
res.send('Welcome to ClassroomIO');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`HelloNode app listening on port ${port}!`);
|
console.log(`ClassroomIO Backend listening on port ${port}!`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
// const Queue = require('bull');
|
|
||||||
const { promisify } = require('util');
|
const { promisify } = require('util');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
@@ -9,7 +8,7 @@ const unlinkAsync = promisify(fs.unlink);
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
S3Client,
|
S3Client,
|
||||||
PutObjectCommand,
|
PutObjectCommand
|
||||||
// GetObjectCommand,
|
// GetObjectCommand,
|
||||||
} = require('@aws-sdk/client-s3');
|
} = require('@aws-sdk/client-s3');
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ const {
|
|||||||
CLOUDFLARE_SECRET_ACCESS_KEY,
|
CLOUDFLARE_SECRET_ACCESS_KEY,
|
||||||
CLOUDFLARE_ACCOUNT_ID,
|
CLOUDFLARE_ACCOUNT_ID,
|
||||||
CLOUDFLARE_PUBLIC_ACCOUNT_ID,
|
CLOUDFLARE_PUBLIC_ACCOUNT_ID,
|
||||||
MUSE_API_KEY,
|
MUSE_API_KEY
|
||||||
} = process.env;
|
} = process.env;
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
@@ -32,8 +31,8 @@ const S3 = new S3Client({
|
|||||||
endpoint: `https://${CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
endpoint: `https://${CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: CLOUDFLARE_ACCESS_KEY,
|
accessKeyId: CLOUDFLARE_ACCESS_KEY,
|
||||||
secretAccessKey: CLOUDFLARE_SECRET_ACCESS_KEY,
|
secretAccessKey: CLOUDFLARE_SECRET_ACCESS_KEY
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// const supabase = getSupabase();
|
// const supabase = getSupabase();
|
||||||
@@ -45,7 +44,7 @@ const S3 = new S3Client({
|
|||||||
router.post('/', upload.single('videoFile'), async (req, res) => {
|
router.post('/', upload.single('videoFile'), async (req, res) => {
|
||||||
const {
|
const {
|
||||||
file,
|
file,
|
||||||
query: { lessonId },
|
query: { lessonId }
|
||||||
} = req;
|
} = req;
|
||||||
console.log('lessonId', lessonId);
|
console.log('lessonId', lessonId);
|
||||||
console.log('file', file);
|
console.log('file', file);
|
||||||
@@ -53,67 +52,56 @@ router.post('/', upload.single('videoFile'), async (req, res) => {
|
|||||||
if (!file?.fieldname || !file?.fieldname) {
|
if (!file?.fieldname || !file?.fieldname) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'You must provide a file to upload',
|
message: 'You must provide a file to upload'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file?.size > 30 * 1024 * 1024) {
|
if (file?.size > 1000 * 1024 * 1024) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
type: 'FILE_TOO_LARGE',
|
type: 'FILE_TOO_LARGE',
|
||||||
message: 'File is too large',
|
message: 'File is too large'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const fileData = fs.readFileSync(file.path);
|
const fileData = fs.readFileSync(file.path);
|
||||||
|
|
||||||
const fileName = `${genUniqueId()}-${file.originalname}`;
|
const fileName = `${genUniqueId()}-${file.originalname.split(' ').join('-')}`;
|
||||||
const params = {
|
const params = {
|
||||||
Bucket: 'videos',
|
Bucket: 'videos',
|
||||||
Key: fileName,
|
Key: fileName,
|
||||||
Body: fileData,
|
Body: fileData
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileUrl = `https://pub-${CLOUDFLARE_PUBLIC_ACCOUNT_ID}.r2.dev/${fileName}`;
|
const fileUrl = `https://pub-${CLOUDFLARE_PUBLIC_ACCOUNT_ID}.r2.dev/${fileName}`;
|
||||||
let metadata = {};
|
let metadata = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const s3Upload = S3.send(new PutObjectCommand(params));
|
console.log('Upload starting');
|
||||||
const museUpload = uploadToMuseNow(fileData, file.originalname);
|
const s3UploadRes = await S3.send(new PutObjectCommand(params));
|
||||||
|
|
||||||
Promise.all([s3Upload, museUpload]).then((values) => {
|
|
||||||
const [s3UploadRes, museUploadRes] = values;
|
|
||||||
|
|
||||||
// Delete file saved by mutler in file
|
|
||||||
unlinkAsync(file.path);
|
|
||||||
|
|
||||||
console.log('s3UploadRes', s3UploadRes);
|
console.log('s3UploadRes', s3UploadRes);
|
||||||
console.log('museUploadRes', museUploadRes);
|
|
||||||
|
|
||||||
if (museUploadRes.metadata) {
|
unlinkAsync(file.path);
|
||||||
metadata = museUploadRes.metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
url: fileUrl,
|
url: fileUrl,
|
||||||
metadata,
|
metadata,
|
||||||
message: 'Uploaded successfully',
|
message: 'Uploaded successfully'
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// error handling.
|
// error handling.
|
||||||
const { requestId, cfId, extendedRequestId } = error.$$metadata || {};
|
const { requestId, cfId, extendedRequestId } = error.$$metadata || {};
|
||||||
|
|
||||||
console.error('Error uploading file', error);
|
console.error('Upload failed', error);
|
||||||
console.log({ requestId, cfId, extendedRequestId });
|
console.log({ requestId, cfId, extendedRequestId });
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Uploaded failed: ' + error,
|
message: 'Uploaded failed: ' + error
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
// finally.
|
// finally.
|
||||||
console.log('File upload complete');
|
console.log('Upload complete');
|
||||||
|
|
||||||
// console.log('Add muse queue');
|
// console.log('Add muse queue');
|
||||||
// museUploadQueue.add({
|
// museUploadQueue.add({
|
||||||
@@ -144,12 +132,12 @@ async function uploadToMuseNow(fileData, fileName) {
|
|||||||
maxContentLength: Infinity,
|
maxContentLength: Infinity,
|
||||||
maxBodyLength: Infinity,
|
maxBodyLength: Infinity,
|
||||||
headers: {
|
headers: {
|
||||||
Key: MUSE_API_KEY,
|
Key: MUSE_API_KEY
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metadata: response.data,
|
metadata: response.data
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Upload to Muse failed', error?.response?.data);
|
console.log('Upload to Muse failed', error?.response?.data);
|
||||||
|
|||||||
Reference in New Issue
Block a user