clean up and logOut
This commit is contained in:
parent
b38261e387
commit
71b73d5fcb
@ -8,59 +8,59 @@ import {
|
|||||||
Request,
|
Request,
|
||||||
Logger,
|
Logger,
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
} from '@nestjs/common';
|
} from "@nestjs/common";
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from "./auth.service";
|
||||||
import { LoginDto } from '../dto/login.dto';
|
import { LoginDto } from "../dto/login.dto";
|
||||||
import { CreateUserDto } from '../dto/create-user.dto';
|
import { CreateUserDto } from "../dto/create-user.dto";
|
||||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
import { JwtAuthGuard } from "./jwt-auth.guard";
|
||||||
import { AdminGuard } from './admin.guard';
|
import { AdminGuard } from "./admin.guard";
|
||||||
//@UseGuards(JwtAuthGuard, AdminGuard)
|
//@UseGuards(JwtAuthGuard, AdminGuard)
|
||||||
@Controller('auth')
|
@Controller("auth")
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
private readonly logger = new Logger(AuthController.name);
|
private readonly logger = new Logger(AuthController.name);
|
||||||
|
|
||||||
constructor(private authService: AuthService) {
|
constructor(private authService: AuthService) {
|
||||||
this.logger.log('AuthController initialized');
|
this.logger.log("AuthController initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('login')
|
@Post("login")
|
||||||
async login(@Body() loginDto: LoginDto) {
|
async login(@Body() loginDto: LoginDto) {
|
||||||
this.logger.log('=== Login endpoint hit ===');
|
this.logger.log("=== Login endpoint hit ===");
|
||||||
this.logger.debug('Raw request body:', {
|
this.logger.debug("Raw request body:", {
|
||||||
username: loginDto.username,
|
username: loginDto.username,
|
||||||
email: loginDto.email,
|
email: loginDto.email,
|
||||||
hasPassword: !!loginDto.password,
|
hasPassword: !!loginDto.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
const email = loginDto.getEmail();
|
const email = loginDto.getEmail();
|
||||||
this.logger.debug('Normalized login request:', {
|
this.logger.debug("Normalized login request:", {
|
||||||
email,
|
email,
|
||||||
hasPassword: !!loginDto.password,
|
hasPassword: !!loginDto.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.logger.debug('Calling AuthService.validateUser...');
|
this.logger.debug("Calling AuthService.validateUser...");
|
||||||
const user = await this.authService.validateUser(
|
const user = await this.authService.validateUser(
|
||||||
email,
|
email,
|
||||||
loginDto.password,
|
loginDto.password,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
this.logger.warn(`Login failed: Invalid credentials for ${email}`);
|
this.logger.warn(`Login failed: Invalid credentials for ${email}`);
|
||||||
throw new UnauthorizedException('Invalid email or password');
|
throw new UnauthorizedException("Invalid email or password");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.debug('User validated successfully:', {
|
this.logger.debug("User validated successfully:", {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
isAdmin: user.isAdmin,
|
isAdmin: user.isAdmin,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.debug('Calling AuthService.login...');
|
this.logger.debug("Calling AuthService.login...");
|
||||||
const result = await this.authService.login(user);
|
const result = await this.authService.login(user);
|
||||||
|
|
||||||
this.logger.debug('Login successful, returning response:', {
|
this.logger.debug("Login successful, returning response:", {
|
||||||
hasAccessToken: !!result.access_token,
|
hasAccessToken: !!result.access_token,
|
||||||
user: {
|
user: {
|
||||||
id: result.user.id,
|
id: result.user.id,
|
||||||
@ -69,14 +69,14 @@ export class AuthController {
|
|||||||
isAdmin: result.user.isAdmin,
|
isAdmin: result.user.isAdmin,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof UnauthorizedException) {
|
if (error instanceof UnauthorizedException) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.error('Login failed:', {
|
this.logger.error("Login failed:", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
body: {
|
body: {
|
||||||
@ -85,35 +85,35 @@ export class AuthController {
|
|||||||
hasPassword: !!loginDto.password,
|
hasPassword: !!loginDto.password,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
throw new UnauthorizedException('Invalid email or password');
|
throw new UnauthorizedException("Invalid email or password");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('register')
|
@Post("register")
|
||||||
async register(@Body() createUserDto: CreateUserDto) {
|
async register(@Body() createUserDto: CreateUserDto) {
|
||||||
console.log('=== Registration endpoint hit ===');
|
console.log("=== Registration endpoint hit ===");
|
||||||
this.logger.log('=== Registration endpoint hit ===');
|
this.logger.log("=== Registration endpoint hit ===");
|
||||||
console.log('Registration request received:', createUserDto);
|
console.log("Registration request received:", createUserDto);
|
||||||
this.logger.log('Registration request received:', {
|
this.logger.log("Registration request received:", {
|
||||||
email: createUserDto.email,
|
email: createUserDto.email,
|
||||||
name: createUserDto.name,
|
name: createUserDto.name,
|
||||||
hasPassword: !!createUserDto.password
|
hasPassword: !!createUserDto.password,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Calling AuthService.createUser...');
|
console.log("Calling AuthService.createUser...");
|
||||||
this.logger.log('Calling AuthService.createUser...');
|
this.logger.log("Calling AuthService.createUser...");
|
||||||
const result = await this.authService.createUser(createUserDto);
|
const result = await this.authService.createUser(createUserDto);
|
||||||
console.log('Registration successful:', result);
|
console.log("Registration successful:", result);
|
||||||
this.logger.log('Registration successful:', {
|
this.logger.log("Registration successful:", {
|
||||||
id: result.id,
|
id: result.id,
|
||||||
email: result.email,
|
email: result.email,
|
||||||
name: result.name,
|
name: result.name,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Registration failed:', error);
|
console.error("Registration failed:", error);
|
||||||
this.logger.error('Registration failed:', {
|
this.logger.error("Registration failed:", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
code: error.code,
|
code: error.code,
|
||||||
command: error.command,
|
command: error.command,
|
||||||
@ -124,25 +124,25 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//@UseGuards(JwtAuthGuard)
|
//@UseGuards(JwtAuthGuard)
|
||||||
@Post('create-admin')
|
@Post("create-admin")
|
||||||
async createAdmin(@Body() createUserDto: CreateUserDto) {
|
async createAdmin(@Body() createUserDto: CreateUserDto) {
|
||||||
this.logger.log('=== Create admin endpoint hit ===');
|
this.logger.log("=== Create admin endpoint hit ===");
|
||||||
this.logger.debug('Admin creation request received:', {
|
this.logger.debug("Admin creation request received:", {
|
||||||
email: createUserDto.email,
|
email: createUserDto.email,
|
||||||
name: createUserDto.name,
|
name: createUserDto.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.logger.debug('Calling AuthService.createUser with isAdmin=true...');
|
this.logger.debug("Calling AuthService.createUser with isAdmin=true...");
|
||||||
const result = await this.authService.createUser(createUserDto, true);
|
const result = await this.authService.createUser(createUserDto, true);
|
||||||
this.logger.debug('Admin creation successful:', {
|
this.logger.debug("Admin creation successful:", {
|
||||||
id: result.id,
|
id: result.id,
|
||||||
email: result.email,
|
email: result.email,
|
||||||
name: result.name,
|
name: result.name,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Admin creation failed:', {
|
this.logger.error("Admin creation failed:", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
});
|
});
|
||||||
@ -151,29 +151,37 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get('user-info')
|
@Get("user-info")
|
||||||
async getUserInfo(@Request() req) {
|
async getUserInfo(@Request() req) {
|
||||||
return this.authService.getUserInfo(req.user.userId);
|
return this.authService.getUserInfo(req.user.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('forgot-password')
|
@Post("forgot-password")
|
||||||
async forgotPassword(@Body() { email }: { email: string }) {
|
async forgotPassword(@Body() { email }: { email: string }) {
|
||||||
if (!email) {
|
if (!email) {
|
||||||
throw new BadRequestException('Email is required');
|
throw new BadRequestException("Email is required");
|
||||||
}
|
}
|
||||||
return this.authService.sendPasswordResetToken(email);
|
return this.authService.sendPasswordResetToken(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('reset-password')
|
@Post("reset-password")
|
||||||
async resetPassword(
|
async resetPassword(
|
||||||
@Body() { token, newPassword }: { token: string; newPassword: string }
|
@Body() { token, newPassword }: { token: string; newPassword: string },
|
||||||
) {
|
) {
|
||||||
if (!token || !newPassword) {
|
if (!token || !newPassword) {
|
||||||
throw new BadRequestException('Token and new password are required');
|
throw new BadRequestException("Token and new password are required");
|
||||||
}
|
}
|
||||||
if (newPassword.length < 6) {
|
if (newPassword.length < 6) {
|
||||||
throw new BadRequestException('Password must be at least 6 characters long');
|
throw new BadRequestException(
|
||||||
|
"Password must be at least 6 characters long",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this.authService.resetPasswordWithToken(token, newPassword);
|
return this.authService.resetPasswordWithToken(token, newPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post("logout")
|
||||||
|
async logout(@Request() req) {
|
||||||
|
await this.authService.logout(req.user.userId);
|
||||||
|
return { message: "Logged out successfully" };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -221,17 +221,6 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// async getUserInfo(userId: number) {
|
|
||||||
// return this.prisma.user.findUnique({
|
|
||||||
// where: { id: userId },
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// name: true,
|
|
||||||
// email: true,
|
|
||||||
// isAdmin: true,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
async getUserInfo(userId: number) {
|
async getUserInfo(userId: number) {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
throw new Error("User ID is required");
|
throw new Error("User ID is required");
|
||||||
@ -327,9 +316,9 @@ export class AuthService {
|
|||||||
where: {
|
where: {
|
||||||
token,
|
token,
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
gt: new Date(), // Token must not be expired
|
gt: new Date(),
|
||||||
},
|
},
|
||||||
used: false, // Token must not be used
|
used: false,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
user: true,
|
user: true,
|
||||||
@ -386,4 +375,9 @@ export class AuthService {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async logout(userId: string): Promise<void> {
|
||||||
|
return console.log("User logged out:", userId);
|
||||||
|
this.logger.log("User logged out:", userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export const getDatabaseUrl = () => {
|
export const getDatabaseUrl = () => {
|
||||||
const url = process.env.DATABASE_URL;
|
const url = process.env.DATABASE_URL;
|
||||||
console.log("Database URL:", url); // For debugging
|
console.log("Database URL:", url);
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,21 @@
|
|||||||
import { Controller, Get, Param, Req, Res, UseGuards, Logger, Request, Post, UseInterceptors, UploadedFile, Body } from '@nestjs/common';
|
import {
|
||||||
import { Response } from 'express';
|
Body,
|
||||||
import { DocumentsService } from './documents.service';
|
Controller,
|
||||||
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
|
Get,
|
||||||
import { S3Service } from '../s3/s3.service';
|
Logger,
|
||||||
import { FileInterceptor } from '@nestjs/platform-express';
|
Param,
|
||||||
|
Post,
|
||||||
|
Request,
|
||||||
|
Res,
|
||||||
|
UploadedFile,
|
||||||
|
UseGuards,
|
||||||
|
UseInterceptors,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import { FileInterceptor } from "@nestjs/platform-express";
|
||||||
|
import { Response } from "express";
|
||||||
|
import { JwtAuthGuard } from "../auth/jwt-auth.guard";
|
||||||
|
import { S3Service } from "../s3/s3.service";
|
||||||
|
import { DocumentsService } from "./documents.service";
|
||||||
|
|
||||||
interface S3File {
|
interface S3File {
|
||||||
buffer: Buffer;
|
buffer: Buffer;
|
||||||
@ -12,28 +24,28 @@ interface S3File {
|
|||||||
fileName: string;
|
fileName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Controller('documents')
|
@Controller("documents")
|
||||||
export class DocumentsController {
|
export class DocumentsController {
|
||||||
private readonly logger = new Logger(DocumentsController.name);
|
private readonly logger = new Logger(DocumentsController.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly documentsService: DocumentsService,
|
private readonly documentsService: DocumentsService,
|
||||||
private readonly s3Service: S3Service
|
private readonly s3Service: S3Service,
|
||||||
) {
|
) {
|
||||||
this.logger.log('DocumentsController initialized');
|
this.logger.log("DocumentsController initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('upload')
|
@Post("upload")
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@UseInterceptors(FileInterceptor('file'))
|
@UseInterceptors(FileInterceptor("file"))
|
||||||
async uploadDocument(
|
async uploadDocument(
|
||||||
@UploadedFile() file: Express.Multer.File,
|
@UploadedFile() file: Express.Multer.File,
|
||||||
@Body('title') title: string,
|
@Body("title") title: string,
|
||||||
@Body('sharedWithId') sharedWithId: string,
|
@Body("sharedWithId") sharedWithId: string,
|
||||||
@Request() req,
|
@Request() req,
|
||||||
) {
|
) {
|
||||||
this.logger.log('=== Document upload endpoint hit ===');
|
this.logger.log("=== Document upload endpoint hit ===");
|
||||||
this.logger.debug('Upload request received:', {
|
this.logger.debug("Upload request received:", {
|
||||||
fileName: file?.originalname,
|
fileName: file?.originalname,
|
||||||
fileSize: file?.size,
|
fileSize: file?.size,
|
||||||
title,
|
title,
|
||||||
@ -48,16 +60,16 @@ export class DocumentsController {
|
|||||||
Number(sharedWithId),
|
Number(sharedWithId),
|
||||||
req.user.id,
|
req.user.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.debug('Document upload successful:', {
|
this.logger.debug("Document upload successful:", {
|
||||||
documentId: result.id,
|
documentId: result.id,
|
||||||
title: result.title,
|
title: result.title,
|
||||||
s3Key: result.s3Key,
|
s3Key: result.s3Key,
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Document upload failed:', {
|
this.logger.error("Document upload failed:", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
});
|
});
|
||||||
@ -65,61 +77,62 @@ export class DocumentsController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('shared/:userId')
|
@Get("shared/:userId")
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
async getSharedDocuments(@Param('userId') userId: string) {
|
async getSharedDocuments(@Param("userId") userId: string) {
|
||||||
return this.documentsService.getClientDocuments(Number(userId));
|
return this.documentsService.getClientDocuments(Number(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('download/:key')
|
@Get("download/:key")
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
async downloadDocument(
|
async downloadDocument(
|
||||||
@Param('key') key: string,
|
@Param("key") key: string,
|
||||||
@Request() req,
|
@Request() req,
|
||||||
@Res() res: Response
|
@Res() res: Response,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
this.logger.debug(`Download request for key: ${key}`);
|
this.logger.debug(`Download request for key: ${key}`);
|
||||||
|
|
||||||
const decodedKey = decodeURIComponent(key);
|
const decodedKey = decodeURIComponent(key);
|
||||||
this.logger.debug(`Decoded key: ${decodedKey}`);
|
this.logger.debug(`Decoded key: ${decodedKey}`);
|
||||||
|
|
||||||
// Get document from database first to verify access
|
// Get document from database first to verify access
|
||||||
const document = await this.documentsService.findDocumentByS3Key(decodedKey);
|
const document =
|
||||||
|
await this.documentsService.findDocumentByS3Key(decodedKey);
|
||||||
|
|
||||||
if (!document) {
|
if (!document) {
|
||||||
return res.status(404).json({ message: 'Document not found' });
|
return res.status(404).json({ message: "Document not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify user has access to this document
|
// Verify user has access to this document
|
||||||
const hasAccess = await this.documentsService.verifyDocumentAccess(
|
const hasAccess = await this.documentsService.verifyDocumentAccess(
|
||||||
document.id,
|
document.id,
|
||||||
req.user.id
|
req.user.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!hasAccess) {
|
if (!hasAccess) {
|
||||||
return res.status(403).json({ message: 'Access denied' });
|
return res.status(403).json({ message: "Access denied" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the file from S3
|
// Get the file from S3
|
||||||
const file = await this.s3Service.getFile(decodedKey);
|
const file = await this.s3Service.getFile(decodedKey);
|
||||||
|
|
||||||
if (!file || !file.buffer) {
|
if (!file || !file.buffer) {
|
||||||
return res.status(404).json({ message: 'File not found in storage' });
|
return res.status(404).json({ message: "File not found in storage" });
|
||||||
}
|
}
|
||||||
|
|
||||||
res.set({
|
res.set({
|
||||||
'Content-Type': file.contentType || 'application/octet-stream',
|
"Content-Type": file.contentType || "application/octet-stream",
|
||||||
'Content-Length': file.contentLength,
|
"Content-Length": file.contentLength,
|
||||||
'Content-Disposition': `attachment; filename="${encodeURIComponent(file.fileName)}"`,
|
"Content-Disposition": `attachment; filename="${encodeURIComponent(file.fileName)}"`,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.send(file.buffer);
|
return res.send(file.buffer);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Download error:', error);
|
this.logger.error("Download error:", error);
|
||||||
return res.status(500).json({
|
return res.status(500).json({
|
||||||
message: 'Failed to download file',
|
message: "Failed to download file",
|
||||||
error: error.message
|
error: error.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export class EmailService {
|
|||||||
private readonly from: string;
|
private readonly from: string;
|
||||||
|
|
||||||
constructor(private configService: ConfigService) {
|
constructor(private configService: ConfigService) {
|
||||||
console.log("Initializing EmailService..."); // Direct console log for debugging
|
console.log("Initializing EmailService...");
|
||||||
this.logger.log("Initializing EmailService...");
|
this.logger.log("Initializing EmailService...");
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
@ -88,7 +88,7 @@ export class EmailService {
|
|||||||
|
|
||||||
const info = await this.transporter.sendMail(mailOptions);
|
const info = await this.transporter.sendMail(mailOptions);
|
||||||
|
|
||||||
console.log("Email sent successfully:", info); // Direct console log
|
console.log("Email sent successfully:", info);
|
||||||
this.logger.log("Email sent successfully:", {
|
this.logger.log("Email sent successfully:", {
|
||||||
messageId: info.messageId,
|
messageId: info.messageId,
|
||||||
response: info.response,
|
response: info.response,
|
||||||
@ -97,7 +97,7 @@ export class EmailService {
|
|||||||
envelope: info.envelope,
|
envelope: info.envelope,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to send email:", error); // Direct console log
|
console.error("Failed to send email:", error);
|
||||||
this.logger.error("Failed to send email:", {
|
this.logger.error("Failed to send email:", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
code: error.code,
|
code: error.code,
|
||||||
|
|||||||
@ -1,7 +1,15 @@
|
|||||||
import { Injectable, Logger, InternalServerErrorException } from '@nestjs/common';
|
import {
|
||||||
import { ConfigService } from '@nestjs/config';
|
Injectable,
|
||||||
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
|
Logger,
|
||||||
import { S3File } from '../interfaces/s3-file.interface';
|
InternalServerErrorException,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import {
|
||||||
|
S3Client,
|
||||||
|
PutObjectCommand,
|
||||||
|
GetObjectCommand,
|
||||||
|
} from "@aws-sdk/client-s3";
|
||||||
|
import { S3File } from "../interfaces/s3-file.interface";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class S3Service {
|
export class S3Service {
|
||||||
@ -10,12 +18,14 @@ export class S3Service {
|
|||||||
|
|
||||||
constructor(private readonly configService: ConfigService) {
|
constructor(private readonly configService: ConfigService) {
|
||||||
this.s3Client = new S3Client({
|
this.s3Client = new S3Client({
|
||||||
region: this.configService.get<string>('AWS_REGION'),
|
region: this.configService.get<string>("AWS_REGION"),
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: this.configService.get<string>('AWS_ACCESS_KEY_ID'),
|
accessKeyId: this.configService.get<string>("AWS_ACCESS_KEY_ID"),
|
||||||
secretAccessKey: this.configService.get<string>('AWS_SECRET_ACCESS_KEY'),
|
secretAccessKey: this.configService.get<string>(
|
||||||
|
"AWS_SECRET_ACCESS_KEY",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
endpoint: this.configService.get<string>('AWS_ENDPOINT_URL'),
|
endpoint: this.configService.get<string>("AWS_ENDPOINT_URL"),
|
||||||
forcePathStyle: true, // Required for Contabo Object Storage
|
forcePathStyle: true, // Required for Contabo Object Storage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -23,9 +33,9 @@ export class S3Service {
|
|||||||
async uploadFile(file: Express.Multer.File, folder: string): Promise<string> {
|
async uploadFile(file: Express.Multer.File, folder: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const key = `${folder}/${Date.now()}-${file.originalname}`;
|
const key = `${folder}/${Date.now()}-${file.originalname}`;
|
||||||
|
|
||||||
const command = new PutObjectCommand({
|
const command = new PutObjectCommand({
|
||||||
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
|
Bucket: this.configService.get<string>("AWS_S3_BUCKET_NAME"),
|
||||||
Key: key,
|
Key: key,
|
||||||
Body: file.buffer,
|
Body: file.buffer,
|
||||||
ContentType: file.mimetype,
|
ContentType: file.mimetype,
|
||||||
@ -33,23 +43,25 @@ export class S3Service {
|
|||||||
|
|
||||||
await this.s3Client.send(command);
|
await this.s3Client.send(command);
|
||||||
this.logger.debug(`File uploaded successfully: ${key}`);
|
this.logger.debug(`File uploaded successfully: ${key}`);
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Error uploading file to S3: ${error.message}`);
|
this.logger.error(`Error uploading file to S3: ${error.message}`);
|
||||||
throw new InternalServerErrorException('Failed to upload file to storage');
|
throw new InternalServerErrorException(
|
||||||
|
"Failed to upload file to storage",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFile(key: string): Promise<S3File> {
|
async getFile(key: string): Promise<S3File> {
|
||||||
try {
|
try {
|
||||||
const command = new GetObjectCommand({
|
const command = new GetObjectCommand({
|
||||||
Bucket: this.configService.get<string>('AWS_S3_BUCKET_NAME'),
|
Bucket: this.configService.get<string>("AWS_S3_BUCKET_NAME"),
|
||||||
Key: key,
|
Key: key,
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await this.s3Client.send(command);
|
const response = await this.s3Client.send(command);
|
||||||
|
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
for await (const chunk of response.Body as any) {
|
for await (const chunk of response.Body as any) {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
@ -58,15 +70,16 @@ export class S3Service {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
buffer,
|
buffer,
|
||||||
contentType: response.ContentType || 'application/octet-stream',
|
contentType: response.ContentType || "application/octet-stream",
|
||||||
contentLength: response.ContentLength || buffer.length,
|
contentLength: response.ContentLength || buffer.length,
|
||||||
fileName: key.split('/').pop() || 'download',
|
fileName: key.split("-").pop() || "download",
|
||||||
|
// fileName: key.split("-").pop() || "download",
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Error getting file from S3: ${error.message}`);
|
this.logger.error(`Error getting file from S3: ${error.message}`);
|
||||||
throw new InternalServerErrorException('Failed to retrieve file from storage');
|
throw new InternalServerErrorException(
|
||||||
|
"Failed to retrieve file from storage",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +48,9 @@ export const downloadDocument = async (key) => {
|
|||||||
link.href = url;
|
link.href = url;
|
||||||
|
|
||||||
// Extract filename from key
|
// Extract filename from key
|
||||||
const fileName = key.split("/").pop();
|
const fileName = key.split("-").pop();
|
||||||
|
// const fileName = key.split("/").pop();
|
||||||
|
|
||||||
link.download = fileName;
|
link.download = fileName;
|
||||||
|
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
@ -106,7 +108,6 @@ export const getAllUsers = () => api.get("/admin/users");
|
|||||||
export const resetUserPassword = (userId, newPassword) =>
|
export const resetUserPassword = (userId, newPassword) =>
|
||||||
api.post(`/admin/users/${userId}/reset-password`, { password: newPassword });
|
api.post(`/admin/users/${userId}/reset-password`, { password: newPassword });
|
||||||
|
|
||||||
// Password reset endpoints
|
|
||||||
export const forgotPassword = (email) =>
|
export const forgotPassword = (email) =>
|
||||||
api.post("/auth/forgot-password", { email });
|
api.post("/auth/forgot-password", { email });
|
||||||
export const resetPassword = (token, newPassword) =>
|
export const resetPassword = (token, newPassword) =>
|
||||||
@ -121,4 +122,5 @@ api.interceptors.response.use(
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
export const logout = () => api.post("/auth/logout");
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user