Skip to content

Commit

Permalink
Merge branch 'feature/s3-buckets' of https://github.com/COS301-SE-202…
Browse files Browse the repository at this point in the history
…4/TuneIn into develop
  • Loading branch information
lkekana committed Jun 12, 2024
2 parents b4dfb61 + eb956d3 commit aeb588c
Show file tree
Hide file tree
Showing 18 changed files with 1,190 additions and 162 deletions.
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ pids

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# S3 uploads
uploads
24 changes: 19 additions & 5 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/multer": "^1.4.11",
"@types/node": "^20.3.1",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38",
Expand Down
52 changes: 50 additions & 2 deletions backend/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { Controller, Get } from "@nestjs/common";
import {
Controller,
Get,
Post,
UploadedFile,
UseInterceptors,
} from "@nestjs/common";
import { AppService } from "./app.service";
import { ApiOkResponse, ApiOperation } from "@nestjs/swagger";
import { FileInterceptor } from "@nestjs/platform-express";
import { S3Service } from "./s3/s3.service";
import { diskStorage } from "multer";

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
constructor(
private readonly appService: AppService,
private readonly s3Service: S3Service,
) {}

@Get()
@ApiOperation({ summary: "Hello World!" })
Expand All @@ -15,4 +27,40 @@ export class AppController {
getHello(): string {
return this.appService.getHello();
}

@Post("upload")
@ApiOperation({ summary: "Upload a file to our AWS S3 storage bucket" })
@UseInterceptors(
FileInterceptor("file", {
storage: diskStorage({
destination: "./uploads",
filename: (req, file, callback) => {
const uniqueSuffix =
Date.now() + "-" + Math.round(Math.random() * 1e9);
const filename = `${uniqueSuffix}-${file.originalname}`;
callback(null, filename);
},
}),
/*
limits: { fileSize: 5 * 1024 * 1024 },
fileFilter: (req, file, callback) => {
if (!file.originalname.match(/\.(jpg|png)$/)) {
return callback(
new Error("Only .png and .jpg files are allowed!"),
false,
);
}
callback(null, true);
},
*/
}),
)
async uploadFile(@UploadedFile() file: Express.Multer.File) {
console.log("File uploaded");
console.log(file);
const result = await this.s3Service.uploadFile(file);
return {
url: result.Location,
};
}
}
5 changes: 4 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { DtoGenService } from "./modules/dto-gen/dto-gen.service";
import { DtoGenModule } from "./modules/dto-gen/dto-gen.module";
import { DbUtilsService } from "./modules/db-utils/db-utils.service";
import { DbUtilsModule } from "./modules/db-utils/db-utils.module";
import { S3Service } from "./s3/s3.service";
import { S3Module } from "./s3/s3.module";

@Module({
imports: [
Expand All @@ -22,8 +24,9 @@ import { DbUtilsModule } from "./modules/db-utils/db-utils.module";
ProfileModule,
DtoGenModule,
DbUtilsModule,
S3Module,
],
controllers: [AppController],
providers: [AppService, DtoGenService, DbUtilsService],
providers: [AppService, DtoGenService, DbUtilsService, S3Service],
})
export class AppModule {}
9 changes: 8 additions & 1 deletion backend/src/modules/profile/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ import {
UseGuards,
Request,
} from "@nestjs/common";
import { ApiBadRequestResponse, ApiBearerAuth, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiOkResponse,
ApiOperation,
ApiParam,
ApiTags,
} from "@nestjs/swagger";
import { UserProfileDto } from "./dto/userprofile.dto";
import { ProfileService } from "./profile.service";
import { UpdateUserProfileDto } from "./dto/updateuserprofile.dto";
Expand Down
8 changes: 7 additions & 1 deletion backend/src/modules/profile/profile.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { AuthService } from "src/auth/auth.service";

@Module({
controllers: [ProfileController],
providers: [ProfileService, PrismaService, DtoGenService, DbUtilsService, AuthService],
providers: [
ProfileService,
PrismaService,
DtoGenService,
DbUtilsService,
AuthService,
],
imports: [PrismaModule],
})
export class ProfileModule {}
1 change: 0 additions & 1 deletion backend/src/modules/rooms/rooms.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
ApiOkResponse,
ApiOperation,
ApiParam,
ApiResponse,
ApiTags,
} from "@nestjs/swagger";
import { SongInfoDto } from "./dto/songinfo.dto";
Expand Down
8 changes: 7 additions & 1 deletion backend/src/modules/rooms/rooms.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { AuthService } from "src/auth/auth.service";

@Module({
controllers: [RoomsController],
providers: [RoomsService, PrismaService, DtoGenService, DbUtilsService, AuthService],
providers: [
RoomsService,
PrismaService,
DtoGenService,
DbUtilsService,
AuthService,
],
imports: [PrismaModule],
})
export class RoomsModule {}
1 change: 0 additions & 1 deletion backend/src/modules/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Put,
UseGuards,
Request,
HttpException,
} from "@nestjs/common";
import { UsersService } from "./users.service";
import { UpdateUserDto } from "./dto/update-user.dto";
Expand Down
8 changes: 7 additions & 1 deletion backend/src/modules/users/users.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import { AuthService } from "src/auth/auth.service";
@Module({
imports: [PrismaModule],
controllers: [UsersController],
providers: [UsersService, PrismaService, DtoGenService, DbUtilsService, AuthService],
providers: [
UsersService,
PrismaService,
DtoGenService,
DbUtilsService,
AuthService,
],
})
export class UsersModule {}
10 changes: 10 additions & 0 deletions backend/src/s3/s3.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { S3Service } from "./s3.service";

@Module({
imports: [ConfigModule],
providers: [S3Service],
exports: [S3Service],
})
export class S3Module {}
18 changes: 18 additions & 0 deletions backend/src/s3/s3.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { S3Service } from './s3.service';

describe('S3Service', () => {
let service: S3Service;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [S3Service],
}).compile();

service = module.get<S3Service>(S3Service);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
68 changes: 68 additions & 0 deletions backend/src/s3/s3.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Injectable } from "@nestjs/common";
import { S3 } from "aws-sdk";
import { ConfigService } from "@nestjs/config";
import { Express } from "express";

@Injectable()
export class S3Service {
private s3: S3;
private bucketName: string;
private accessKeyId: string;
private secretAccessKey: string;
private region: string;

constructor(private configService: ConfigService) {
const bucketName = this.configService.get<string>("AWS_S3_BUCKET_NAME");
if (!bucketName) {
throw new Error("AWS_S3_BUCKET_NAME is not defined");
}
this.bucketName = bucketName;

const accessKeyId = this.configService.get<string>("AWS_ACCESS_KEY_ID");
if (!accessKeyId) {
throw new Error("AWS_ACCESS_KEY_ID is not defined");
}
this.accessKeyId = accessKeyId;

const secretAccessKey = this.configService.get<string>(
"AWS_SECRET_ACCESS_KEY",
);
if (!secretAccessKey) {
throw new Error("AWS_SECRET_ACCESS_KEY is not defined");
}
this.secretAccessKey = secretAccessKey;

const region = this.configService.get<string>("AWS_S3_REGION");
if (!region) {
throw new Error("AWS_S3_REGION is not defined");
}
this.region = region;

this.s3 = new S3({
accessKeyId: this.accessKeyId,
secretAccessKey: this.secretAccessKey,
region: this.region,
});
this.bucketName = bucketName;
}

async uploadFile(
file: Express.Multer.File,
): Promise<S3.ManagedUpload.SendData> {
console.log("Uploading file to S3");
console.log(file);
const params = {
Bucket: this.bucketName,
Key: `${Date.now()}-${file.originalname}`,
Body: file.buffer,
ContentType: file.mimetype,
};

return await this.s3.upload(params).promise();
}

async getFileUrl(key: string): Promise<string> {
const params = { Bucket: this.bucketName, Key: key };
return this.s3.getSignedUrlPromise("getObject", params);
}
}
2 changes: 1 addition & 1 deletion frontend/app/components/PhotoSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const PhotoSelect = ({ isVisible, onClose, onImageUpload }) => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
aspect: [1, 1],
quality: 1,
});

Expand Down
Loading

0 comments on commit aeb588c

Please sign in to comment.