Skip to content

Commit a4f062c

Browse files
author
Nevo David
committed
feat: fixes for uploading
1 parent e7273c4 commit a4f062c

File tree

16 files changed

+9450
-8785
lines changed

16 files changed

+9450
-8785
lines changed

apps/backend/src/api/api.module.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,6 @@ const authenticatedController = [
4646
@Module({
4747
imports: [
4848
UploadModule,
49-
...(!!process.env.UPLOAD_DIRECTORY &&
50-
!!process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY
51-
? [
52-
ServeStaticModule.forRoot({
53-
rootPath: process.env.UPLOAD_DIRECTORY,
54-
serveRoot: '/' + process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY,
55-
serveStaticOptions: {
56-
index: false,
57-
},
58-
}),
59-
]
60-
: []),
6149
],
6250
controllers: [
6351
RootController,

apps/backend/src/api/routes/media.controller.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,12 @@ export class MediaController {
5050

5151
@Post('/upload-simple')
5252
@UseInterceptors(FileInterceptor('file'))
53-
@UsePipes(new CustomFileValidationPipe())
5453
async uploadSimple(
5554
@GetOrgFromRequest() org: Organization,
56-
@UploadedFile('file')
57-
file: Express.Multer.File
55+
@UploadedFile('file') file: Express.Multer.File
5856
) {
59-
const filePath =
60-
file.path.indexOf('http') === 0
61-
? file.path
62-
: file.path.replace(process.env.UPLOAD_DIRECTORY, '');
63-
return this._mediaService.saveFile(org.id, file.originalname, filePath);
57+
const getFile = await this.storage.uploadFile(file);
58+
return this._mediaService.saveFile(org.id, getFile.originalname, getFile.path);
6459
}
6560

6661
@Post('/:endpoint')

apps/frontend/next.config.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,37 @@ const nextConfig = {
1515
transpilePackages: ['crypto-hash'],
1616
images: {
1717
remotePatterns: [
18+
{
19+
protocol: 'http',
20+
hostname: '**',
21+
},
1822
{
1923
protocol: 'https',
2024
hostname: '**',
2125
},
2226
],
23-
}
27+
},
28+
async redirects() {
29+
return [
30+
{
31+
source: '/api/uploads/:path*',
32+
destination:
33+
process.env.STORAGE_PROVIDER === 'local' ? '/uploads/:path*' : '/404',
34+
permanent: true,
35+
},
36+
];
37+
},
38+
async rewrites() {
39+
return [
40+
{
41+
source: '/uploads/:path*',
42+
destination:
43+
process.env.STORAGE_PROVIDER === 'local'
44+
? '/api/uploads/:path*'
45+
: '/404',
46+
},
47+
];
48+
},
2449
};
2550

2651
const plugins = [
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
import { createReadStream, statSync } from 'fs';
3+
// @ts-ignore
4+
import mime from 'mime';
5+
6+
async function* nodeStreamToIterator(stream: any) {
7+
for await (const chunk of stream) {
8+
yield chunk;
9+
}
10+
}
11+
12+
function iteratorToStream(iterator: any) {
13+
return new ReadableStream({
14+
async pull(controller) {
15+
const { value, done } = await iterator.next();
16+
17+
if (done) {
18+
controller.close();
19+
} else {
20+
controller.enqueue(new Uint8Array(value));
21+
}
22+
},
23+
});
24+
}
25+
26+
export const GET = (
27+
request: NextRequest,
28+
context: { params: { path: string[] } }
29+
) => {
30+
const filePath =
31+
process.env.UPLOAD_DIRECTORY + '/' + context.params.path.join('/');
32+
const response = createReadStream(filePath);
33+
34+
const fileStats = statSync(filePath);
35+
const contentType = mime.getType(filePath) || 'application/octet-stream';
36+
37+
const iterator = nodeStreamToIterator(response);
38+
const webStream = iteratorToStream(iterator);
39+
return new Response(webStream, {
40+
headers: {
41+
'Content-Type': contentType, // Set the appropriate content-type header
42+
'Content-Length': fileStats.size.toString(), // Set the content-length header
43+
'Last-Modified': fileStats.mtime.toUTCString(), // Set the last-modified header
44+
'Cache-Control': 'public, max-age=31536000, immutable', // Example cache-control header
45+
},
46+
});
47+
};

apps/frontend/src/components/media/media.component.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { ChangeEvent, FC, useCallback, useEffect, useState } from 'react';
3+
import { FC, useCallback, useEffect, useState } from 'react';
44
import { Button } from '@gitroom/react/form/button';
55
import useSWR from 'swr';
66
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
@@ -11,7 +11,6 @@ import EventEmitter from 'events';
1111
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
1212
import clsx from 'clsx';
1313
import { VideoFrame } from '@gitroom/react/helpers/video.frame';
14-
import { useToaster } from '@gitroom/react/toaster/toaster';
1514
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
1615
import { MultipartFileUploader } from '@gitroom/frontend/components/media/new.uploader';
1716
import dynamic from 'next/dynamic';

apps/frontend/src/middleware.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.man
77
export async function middleware(request: NextRequest) {
88
const nextUrl = request.nextUrl;
99
const authCookie = request.cookies.get('auth');
10-
10+
if (nextUrl.pathname.startsWith('/uploads/')) {
11+
return NextResponse.next();
12+
}
1113
// If the URL is logout, delete the cookie and redirect to login
1214
if (nextUrl.href.indexOf('/auth/logout') > -1) {
1315
const response = NextResponse.redirect(

libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { Injectable } from '@nestjs/common';
33
import dayjs from 'dayjs';
44
import { Integration } from '@prisma/client';
55
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
6-
import { simpleUpload } from '@gitroom/nestjs-libraries/upload/r2.uploader';
7-
import axios from 'axios';
86
import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto';
7+
import { UploadFactory } from '@gitroom/nestjs-libraries/upload/upload.factory';
98

109
@Injectable()
1110
export class IntegrationRepository {
11+
private storage = UploadFactory.createStorage();
1212
constructor(
1313
private _integration: PrismaRepository<'integration'>,
1414
private _posts: PrismaRepository<'post'>
@@ -32,16 +32,10 @@ export class IntegrationRepository {
3232
async updateIntegration(id: string, params: Partial<Integration>) {
3333
if (
3434
params.picture &&
35-
params.picture.indexOf(process.env.CLOUDFLARE_BUCKET_URL!) === -1
35+
(params.picture.indexOf(process.env.CLOUDFLARE_BUCKET_URL!) === -1 ||
36+
params.picture.indexOf(process.env.FRONTEND_URL!) === -1)
3637
) {
37-
const picture = await axios.get(params.picture, {
38-
responseType: 'arraybuffer',
39-
});
40-
params.picture = await simpleUpload(
41-
picture.data,
42-
`${makeId(10)}.png`,
43-
'image/png'
44-
);
38+
params.picture = await this.storage.uploadSimple(params.picture);
4539
}
4640

4741
return this._integration.model.integration.update({

libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import { timer } from '@gitroom/helpers/utils/timer';
2121
import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service';
2222
import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract';
2323
import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto';
24+
import { UploadFactory } from '@gitroom/nestjs-libraries/upload/upload.factory';
2425

2526
@Injectable()
2627
export class IntegrationService {
28+
private storage = UploadFactory.createStorage();
2729
constructor(
2830
private _integrationRepository: IntegrationRepository,
2931
private _integrationManager: IntegrationManager,
@@ -50,13 +52,7 @@ export class IntegrationService {
5052
timezone?: number,
5153
customInstanceDetails?: string
5254
) {
53-
const loadImage = await axios.get(picture, { responseType: 'arraybuffer' });
54-
const uploadedPicture = await simpleUpload(
55-
loadImage.data,
56-
`${makeId(10)}.png`,
57-
'image/png'
58-
);
59-
55+
const uploadedPicture = await this.storage.uploadSimple(picture);
6056
return this._integrationRepository.createOrUpdateIntegration(
6157
org,
6258
name,

libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider {
120120
for (const post of postDetails) {
121121
const images = await Promise.all(
122122
post.media?.map(async (p) => {
123+
const a = await fetch(p.url);
124+
console.log(p.url);
123125
return await agent.uploadBlob(
124126
new Blob([
125127
await sharp(await (await fetch(p.url)).arrayBuffer())

libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class RedditProvider extends SocialAbstract implements SocialProvider {
143143
? {
144144
url: `${
145145
firstPostSettings.value.media[0].path.indexOf('http') === -1
146-
? `${process.env.NEXT_PUBLIC_BACKEND_URL}/${process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY}`
146+
? `${process.env.NEXT_PUBLIC_BACKEND_URL}/uploads`
147147
: ``
148148
}${firstPostSettings.value.media[0].path}`,
149149
}

0 commit comments

Comments
 (0)