feat: tải ảnh lên bằng tài khoản public và cho phép bình luận

This commit is contained in:
3dtours committed 2026-06-19 22:38:16 +07:00
1 parent 4640526e4b
commit 6c1b77d7d2
29 files changed
+1752 -165

No files matched your search

+2
View File
@@ -24,5 +24,7 @@ export declare class CommentGateway implements OnGatewayConnection {
server: Server;
handleConnection(client: Socket): void;
handleJoinTour(client: Socket, tourId: string): void;
handleJoinPhoto(client: Socket, photoId: string): void;
notifyNewComment(tourId: string, data: any): void;
notifyNewPhotoComment(photoId: string, data: any): void;
}
+282 -17
View File
@@ -56,6 +56,8 @@ require("reflect-metadata");
const zlib = __importStar(require("zlib"));
const util_1 = require("util");
const sharp_1 = __importDefault(require("sharp"));
const exifr_1 = __importDefault(require("exifr"));
const heic_convert_1 = __importDefault(require("heic-convert"));
const core_1 = require("@nestjs/core");
const common_1 = require("@nestjs/common");
const platform_express_1 = require("@nestjs/platform-express");
@@ -147,8 +149,11 @@ let TourRoleGuard = class TourRoleGuard {
}
else {
const photo = await this.prisma.photo.findUnique({ where: { id: resourceId }, select: { tourId: true } });
if (photo)
if (photo) {
if (!photo.tourId)
return true;
tourId = photo.tourId;
}
}
}
}
@@ -320,6 +325,23 @@ let AuthController = class AuthController {
},
};
}
async createGuestUser() {
const user = await this.prisma.user.create({
data: {
isAnonymous: true,
name: `Lữ khách #${Math.floor(1000 + Math.random() * 9000)}`,
},
});
const payload = { sub: user.id, isAnonymous: true };
return {
access_token: this.jwtService.sign(payload),
user: {
id: user.id,
name: user.name,
isAnonymous: user.isAnonymous,
},
};
}
async getStatus() {
const userCount = await this.prisma.user.count();
console.log(`[Status Check] Users found: ${userCount}`);
@@ -363,7 +385,7 @@ let AuthController = class AuthController {
}
}
async signupVerify(body) {
const { email, otp } = body;
const { email, otp, guestId } = body;
const storedOtp = await this.cacheManager.get(`signup_otp:${email}`);
if (!storedOtp || storedOtp !== otp) {
throw new common_1.BadRequestException('Mã OTP không chính xác hoặc đã hết hạn.');
@@ -373,13 +395,41 @@ let AuthController = class AuthController {
throw new common_1.BadRequestException('Thông tin đăng ký đã hết hạn. Vui lòng thực hiện lại từ đầu.');
}
const { password, name, phone, address } = JSON.parse(cachedDataStr);
const userCount = await this.prisma.user.count();
const shouldBeAdmin = userCount === 0;
const passwordHash = await bcrypt.hash(password, 10);
const user = await this.prisma.user.create({
data: { email, passwordHash, name, phone, address, isAdmin: shouldBeAdmin },
select: { id: true, email: true, name: true, phone: true, address: true, isAdmin: true }
});
let user;
if (guestId) {
const guestUser = await this.prisma.user.findUnique({
where: { id: guestId }
});
if (guestUser && guestUser.isAnonymous) {
const existingUser = await this.prisma.user.findFirst({
where: { email, isAnonymous: false }
});
if (existingUser) {
throw new common_1.BadRequestException('Email này đã được đăng ký bởi tài khoản khác.');
}
user = await this.prisma.user.update({
where: { id: guestId },
data: {
email,
passwordHash,
name,
phone,
address,
isAnonymous: false,
},
select: { id: true, email: true, name: true, phone: true, address: true, isAdmin: true }
});
}
}
if (!user) {
const userCount = await this.prisma.user.count();
const shouldBeAdmin = userCount === 0;
user = await this.prisma.user.create({
data: { email, passwordHash, name, phone, address, isAdmin: shouldBeAdmin },
select: { id: true, email: true, name: true, phone: true, address: true, isAdmin: true }
});
}
await Promise.all([
this.cacheManager.del(`signup_otp:${email}`),
this.cacheManager.del(`signup_data:${email}`)
@@ -394,6 +444,12 @@ __decorate([
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "convertGuestToOfficial", null);
__decorate([
(0, common_1.Post)('create-guest'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], AuthController.prototype, "createGuestUser", null);
__decorate([
(0, common_1.Get)('status'),
__metadata("design:type", Function),
@@ -998,12 +1054,28 @@ let TourController = class TourController {
fs.mkdirSync(tourDisplayPath, { recursive: true });
return Promise.all(files.map(async (file) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
const extension = path.extname(file.originalname).toLowerCase() || '.jpg';
const filename = `${uniqueSuffix}${extension}`;
const originalFilePath = path.join(memberOriginalDir, filename);
const displayFilePath = path.join(tourDisplayPath, filename);
const originalExtension = path.extname(file.originalname).toLowerCase() || '.jpg';
const originalFilename = `${uniqueSuffix}${originalExtension}`;
const displayFilename = `${uniqueSuffix}.jpg`;
const originalFilePath = path.join(memberOriginalDir, originalFilename);
const displayFilePath = path.join(tourDisplayPath, displayFilename);
await fs.promises.writeFile(originalFilePath, file.buffer);
await (0, sharp_1.default)(file.buffer)
let processBuffer = file.buffer;
const isHeic = file.originalname.toLowerCase().endsWith('.heic') || file.originalname.toLowerCase().endsWith('.heif') || file.mimetype === 'image/heic' || file.mimetype === 'image/heif';
if (isHeic) {
try {
processBuffer = await (0, heic_convert_1.default)({
buffer: file.buffer,
format: 'JPEG',
quality: 1
});
console.log(`[HEIC] Converted original HEIC image to JPEG for display`);
}
catch (e) {
console.warn('[HEIC] Failed to convert HEIC to JPEG, attempting to process anyway:', e.message);
}
}
await (0, sharp_1.default)(processBuffer)
.resize(2560, 2560, {
fit: 'inside',
withoutEnlargement: true
@@ -1014,8 +1086,8 @@ let TourController = class TourController {
data: {
tourId: tourId,
uploaderId: uploaderId,
imageUrl: `/uploads/tours/${filename}`,
originalUrl: `/uploads/members/${uploaderId}/originals/${filename}`,
imageUrl: `/uploads/tours/${displayFilename}`,
originalUrl: `/uploads/members/${uploaderId}/originals/${originalFilename}`,
privacy: 'TOUR_ONLY',
}
});
@@ -1488,6 +1560,86 @@ let PhotoController = class PhotoController {
constructor(prisma) {
this.prisma = prisma;
}
async uploadAnonymousPhoto(files, req) {
if (!files || files.length === 0) {
throw new common_1.BadRequestException('Vui lòng chọn một ảnh.');
}
const uploaderId = req.user.id;
const isAnonymous = req.user.isAnonymous;
if (!isAnonymous) {
throw new common_1.ForbiddenException('Chỉ người dùng khách mới có thể sử dụng tính năng này.');
}
const file = files[0];
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
const originalExtension = path.extname(file.originalname).toLowerCase() || '.jpg';
const originalFilename = `${uniqueSuffix}${originalExtension}`;
const displayFilename = `${uniqueSuffix}.jpg`;
const memberOriginalDir = path.join(UPLOAD_ROOT, 'members', uploaderId, 'originals');
const displayFilePath = path.join(UPLOAD_ROOT, 'tours', displayFilename);
const originalFilePath = path.join(memberOriginalDir, originalFilename);
if (!fs.existsSync(memberOriginalDir)) {
fs.mkdirSync(memberOriginalDir, { recursive: true });
}
await fs.promises.writeFile(originalFilePath, file.buffer);
let lat;
let lng;
try {
const gps = await exifr_1.default.gps(file.buffer);
if (gps && typeof gps.latitude === 'number' && typeof gps.longitude === 'number') {
lat = gps.latitude;
lng = gps.longitude;
console.log(`[EXIF GPS] Đã tìm thấy tọa độ từ EXIF: lat=${lat}, lng=${lng}`);
}
}
catch (e) {
console.warn('[EXIF GPS] Không thể giải nén GPS từ EXIF ảnh:', e.message);
}
if (lat === undefined || lng === undefined) {
const bodyLat = req.body.latitude ? parseFloat(req.body.latitude) : undefined;
const bodyLng = req.body.longitude ? parseFloat(req.body.longitude) : undefined;
if (bodyLat !== undefined && !isNaN(bodyLat) && bodyLng !== undefined && !isNaN(bodyLng)) {
lat = bodyLat;
lng = bodyLng;
console.log(`[EXIF GPS] Sử dụng tọa độ dự phòng từ Frontend: lat=${lat}, lng=${lng}`);
}
}
if (lat === undefined || lng === undefined) {
lat = 10.7769;
lng = 106.7009;
console.log(`[EXIF GPS] Không tìm thấy tọa độ nào, ghim tại TP.HCM mặc định: lat=${lat}, lng=${lng}`);
}
let processBuffer = file.buffer;
const isHeic = file.originalname.toLowerCase().endsWith('.heic') || file.originalname.toLowerCase().endsWith('.heif') || file.mimetype === 'image/heic' || file.mimetype === 'image/heif';
if (isHeic) {
try {
processBuffer = await (0, heic_convert_1.default)({
buffer: file.buffer,
format: 'JPEG',
quality: 1
});
console.log(`[HEIC] Converted anonymous HEIC image to JPEG for display`);
}
catch (e) {
console.warn('[HEIC] Failed to convert HEIC to JPEG, attempting to process anyway:', e.message);
}
}
await (0, sharp_1.default)(processBuffer)
.resize(2560, 2560, { fit: 'inside', withoutEnlargement: true })
.jpeg({ quality: 85 })
.toFile(displayFilePath);
return this.prisma.photo.create({
data: {
uploaderId: uploaderId,
imageUrl: `/uploads/tours/${displayFilename}`,
originalUrl: `/uploads/members/${uploaderId}/originals/${originalFilename}`,
privacy: 'PUBLIC',
metadata: {
lat: lat,
lng: lng
}
},
});
}
async deletePhoto(id, req) {
const photo = await this.prisma.photo.findUnique({
where: { id },
@@ -1495,7 +1647,7 @@ let PhotoController = class PhotoController {
if (!photo) {
throw new common_1.NotFoundException('Không tìm thấy ảnh.');
}
if (photo.uploaderId !== req.user.id) {
if (photo.uploaderId !== req.user.id && !req.user.isAdmin) {
throw new common_1.ForbiddenException('Bạn không có quyền xóa ảnh này.');
}
if (photo.imageUrl) {
@@ -1514,6 +1666,16 @@ let PhotoController = class PhotoController {
return { message: 'Ảnh đã được xóa thành công.' };
}
};
__decorate([
(0, common_1.Post)('upload-anonymous'),
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.UseInterceptors)((0, platform_express_1.FilesInterceptor)('images', 1)),
__param(0, (0, common_1.UploadedFiles)()),
__param(1, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Array, Object]),
__metadata("design:returntype", Promise)
], PhotoController.prototype, "uploadAnonymousPhoto", null);
__decorate([
(0, common_1.Delete)(':id'),
__param(0, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
@@ -1661,9 +1823,16 @@ let CommentGateway = class CommentGateway {
client.join(`tour_${tourId}`);
console.log(`[WS] Client ${client.id} joined room: tour_${tourId}`);
}
handleJoinPhoto(client, photoId) {
client.join(`photo_${photoId}`);
console.log(`[WS] Client ${client.id} joined room: photo_${photoId}`);
}
notifyNewComment(tourId, data) {
this.server.to(`tour_${tourId}`).emit('commentAdded', data);
}
notifyNewPhotoComment(photoId, data) {
this.server.to(`photo_${photoId}`).emit('photoCommentAdded', data);
}
};
exports.CommentGateway = CommentGateway;
__decorate([
@@ -1676,6 +1845,12 @@ __decorate([
__metadata("design:paramtypes", [socket_io_1.Socket, String]),
__metadata("design:returntype", void 0)
], CommentGateway.prototype, "handleJoinTour", null);
__decorate([
(0, websockets_1.SubscribeMessage)('joinPhoto'),
__metadata("design:type", Function),
__metadata("design:paramtypes", [socket_io_1.Socket, String]),
__metadata("design:returntype", void 0)
], CommentGateway.prototype, "handleJoinPhoto", null);
exports.CommentGateway = CommentGateway = __decorate([
(0, websockets_1.WebSocketGateway)({ cors: { origin: '*' } })
], CommentGateway);
@@ -1842,6 +2017,96 @@ AdminOtpController = __decorate([
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
EmailService, Object])
], AdminOtpController);
let PublicPhotoController = class PublicPhotoController {
constructor(prisma, commentGateway) {
this.prisma = prisma;
this.commentGateway = commentGateway;
}
async getPublicPhotos() {
return this.prisma.photo.findMany({
where: { privacy: 'PUBLIC' },
select: {
id: true,
imageUrl: true,
originalUrl: true,
capturedAt: true,
metadata: true,
uploader: {
select: {
id: true,
name: true,
}
}
},
orderBy: { capturedAt: 'desc' }
});
}
async getPhotoComments(photoId) {
return this.prisma.comment.findMany({
where: { photoId },
include: {
user: {
select: { id: true, name: true }
}
},
orderBy: { createdAt: 'asc' }
});
}
async addPhotoComment(photoId, body, req) {
const { content } = body;
if (!content || content.trim() === '') {
throw new common_1.BadRequestException('Nội dung bình luận không được để trống.');
}
const photo = await this.prisma.photo.findUnique({
where: { id: photoId }
});
if (!photo) {
throw new common_1.NotFoundException('Không tìm thấy ảnh.');
}
const comment = await this.prisma.comment.create({
data: {
content: content.trim(),
photoId,
userId: req.user.id
},
include: {
user: {
select: { id: true, name: true }
}
}
});
this.commentGateway.notifyNewPhotoComment(photoId, comment);
return comment;
}
};
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], PublicPhotoController.prototype, "getPublicPhotos", null);
__decorate([
(0, common_1.Get)(':photoId/comments'),
__param(0, (0, common_1.Param)('photoId', common_1.ParseUUIDPipe)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], PublicPhotoController.prototype, "getPhotoComments", null);
__decorate([
(0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard),
(0, common_1.Post)(':photoId/comments'),
__param(0, (0, common_1.Param)('photoId', common_1.ParseUUIDPipe)),
__param(1, (0, common_1.Body)()),
__param(2, (0, common_1.Req)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object, Object]),
__metadata("design:returntype", Promise)
], PublicPhotoController.prototype, "addPhotoComment", null);
PublicPhotoController = __decorate([
(0, common_1.Controller)('public-photos'),
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
CommentGateway])
], PublicPhotoController);
let AppModule = class AppModule {
};
AppModule = __decorate([
@@ -1866,7 +2131,7 @@ AppModule = __decorate([
signOptions: { expiresIn: '1d' },
}),
],
controllers: [AppController, AuthController, PublicTourController, TourController, UserController, RoutingController, LegController, LocationController, CommentController, PhotoController, AdminOtpController],
controllers: [AppController, AuthController, PublicTourController, TourController, UserController, RoutingController, LegController, LocationController, CommentController, PhotoController, PublicPhotoController, AdminOtpController],
providers: [prisma_service_1.PrismaService, jwt_strategy_1.JwtStrategy, TourRoleGuard, jwt_auth_guard_1.JwtAuthGuard, admin_guard_1.AdminGuard, CommentGateway, core_2.Reflector, EmailService, config_1.ConfigService],
exports: [prisma_service_1.PrismaService]
})
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"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 truncated
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"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 truncated