Image Upload, spinner aktivirt, listings & details überarbeitet
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
import { Controller, Param, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
|
||||
@Controller('account')
|
||||
export class AccountController {
|
||||
constructor(private fileService:FileService){}
|
||||
|
||||
@Post('uploadProfile/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadProfile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storeProfilePicture(file,id);
|
||||
}
|
||||
@Post('uploadCompanyLogo/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadCompanyLogo(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storeCompanyLogo(file,id);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AccountController } from './account.controller.js';
|
||||
import { AccountService } from './account.service.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
|
||||
@Module({
|
||||
controllers: [AccountController],
|
||||
providers: [AccountService,FileService]
|
||||
})
|
||||
export class AccountModule {}
|
||||
@@ -10,8 +10,6 @@ import { SelectOptionsService } from './select-options/select-options.service.js
|
||||
import { SubscriptionsController } from './subscriptions/subscriptions.controller.js';
|
||||
import { RedisModule } from './redis/redis.module.js';
|
||||
import { ListingsService } from './listings/listings.service.js';
|
||||
import { AccountController } from './account/account.controller.js';
|
||||
import { AccountService } from './account/account.service.js';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import path, { join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
@@ -22,9 +20,9 @@ import { AuthModule } from './auth/auth.module.js';
|
||||
import { GeoModule } from './geo/geo.module.js';
|
||||
import { UserModule } from './user/user.module.js';
|
||||
import { ListingsModule } from './listings/listings.module.js';
|
||||
import { AccountModule } from './account/account.module.js';
|
||||
import { SelectOptionsModule } from './select-options/select-options.module.js';
|
||||
import { CommercialPropertyListingsController } from './listings/commercial-property-listings.controller.js';
|
||||
import { ImageModule } from './image/image.module.js';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@@ -52,9 +50,9 @@ const __dirname = path.dirname(__filename);
|
||||
GeoModule,
|
||||
UserModule,
|
||||
ListingsModule,
|
||||
AccountModule,
|
||||
SelectOptionsModule,
|
||||
RedisModule
|
||||
RedisModule,
|
||||
ImageModule
|
||||
],
|
||||
controllers: [AppController, SubscriptionsController],
|
||||
providers: [AppService, FileService],
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { fstat, readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { ImageProperty } from 'src/models/main.model.js';
|
||||
|
||||
import { ImageProperty } from '../models/main.model.js';
|
||||
import sharp from 'sharp';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger } from 'winston';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
@Injectable()
|
||||
export class FileService {
|
||||
private subscriptions: any;
|
||||
constructor() {
|
||||
constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
|
||||
this.loadSubscriptions();
|
||||
fs.ensureDirSync(`./pictures`);
|
||||
fs.ensureDirSync(`./pictures/profile`);
|
||||
@@ -20,42 +22,60 @@ export class FileService {
|
||||
fs.ensureDirSync(`./pictures/property`);
|
||||
}
|
||||
private loadSubscriptions(): void {
|
||||
const filePath = join(__dirname,'..', 'assets', 'subscriptions.json');
|
||||
const filePath = join(__dirname, '..', 'assets', 'subscriptions.json');
|
||||
const rawData = readFileSync(filePath, 'utf8');
|
||||
this.subscriptions = JSON.parse(rawData);
|
||||
}
|
||||
getSubscriptions() {
|
||||
return this.subscriptions
|
||||
}
|
||||
async storeProfilePicture(file: Express.Multer.File,userId: string){
|
||||
const suffix = file.mimetype.includes('png')?'png':'jpg'
|
||||
await fs.outputFile(`./pictures/profile/${userId}`,file.buffer);
|
||||
async storeProfilePicture(file: Express.Multer.File, userId: string) {
|
||||
const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
await fs.outputFile(`./pictures/profile/${userId}`, file.buffer);
|
||||
}
|
||||
async storeCompanyLogo(file: Express.Multer.File,userId: string){
|
||||
const suffix = file.mimetype.includes('png')?'png':'jpg'
|
||||
await fs.outputFile(`./pictures/logo/${userId}`,file.buffer);
|
||||
hasProfile(userId: string){
|
||||
return fs.existsSync(`./pictures/profile/${userId}`)
|
||||
}
|
||||
async getPropertyImages(listingId: string):Promise<ImageProperty[]>{
|
||||
const result:ImageProperty[]=[]
|
||||
|
||||
async storeCompanyLogo(file: Express.Multer.File, userId: string) {
|
||||
const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
await fs.outputFile(`./pictures/logo/${userId}`, file.buffer);
|
||||
}
|
||||
hasCompanyLogo(userId: string){
|
||||
return fs.existsSync(`./pictures/logo/${userId}`)
|
||||
}
|
||||
|
||||
async getPropertyImages(listingId: string): Promise<ImageProperty[]> {
|
||||
const result: ImageProperty[] = []
|
||||
const directory = `./pictures/property/${listingId}`
|
||||
if (fs.existsSync(directory)){
|
||||
if (fs.existsSync(directory)) {
|
||||
const files = await fs.readdir(directory);
|
||||
files.forEach(f=>{
|
||||
const image:ImageProperty={name:f,id:'',code:''};
|
||||
files.forEach(f => {
|
||||
const image: ImageProperty = { name: f, id: '', code: '' };
|
||||
result.push(image)
|
||||
})
|
||||
return result;
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
async storePropertyPicture(file: Express.Multer.File,listingId: string){
|
||||
const suffix = file.mimetype.includes('png')?'png':'jpg'
|
||||
async hasPropertyImages(listingId: string): Promise<boolean> {
|
||||
const result: ImageProperty[] = []
|
||||
const directory = `./pictures/property/${listingId}`
|
||||
if (fs.existsSync(directory)) {
|
||||
const files = await fs.readdir(directory);
|
||||
return files.length>0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
async storePropertyPicture(file: Express.Multer.File, listingId: string) {
|
||||
const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
const directory = `./pictures/property/${listingId}`
|
||||
fs.ensureDirSync(`${directory}`);
|
||||
const imageName = await this.getNextImageName(directory);
|
||||
await fs.outputFile(`${directory}/${imageName}`,file.buffer);
|
||||
//await fs.outputFile(`${directory}/${imageName}`, file.buffer);
|
||||
await this.resizeImageToAVIF(file.buffer,150 * 1024,imageName,directory);
|
||||
}
|
||||
async getNextImageName(directory) {
|
||||
try {
|
||||
@@ -64,7 +84,7 @@ export class FileService {
|
||||
.map(file => parseInt(file, 10)) // Dateinamen direkt in Zahlen umwandeln
|
||||
.filter(number => !isNaN(number)) // Sicherstellen, dass die Konvertierung gültig ist
|
||||
.sort((a, b) => a - b); // Aufsteigend sortieren
|
||||
|
||||
|
||||
const nextNumber = imageNumbers.length > 0 ? Math.max(...imageNumbers) + 1 : 1;
|
||||
return `${nextNumber}`; // Keine Endung für den Dateinamen
|
||||
} catch (error) {
|
||||
@@ -72,4 +92,24 @@ export class FileService {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async resizeImageToAVIF(buffer: Buffer, maxSize: number,imageName:string,directory:string) {
|
||||
let quality = 50; // AVIF kann mit niedrigeren Qualitätsstufen gute Ergebnisse erzielen
|
||||
let output;
|
||||
let start = Date.now();
|
||||
do {
|
||||
output = await sharp(buffer)
|
||||
.resize({ width: 1500 })
|
||||
.avif({ quality }) // Verwende AVIF
|
||||
//.webp({ quality }) // Verwende Webp
|
||||
.toBuffer();
|
||||
|
||||
if (output.byteLength > maxSize) {
|
||||
quality -= 5; // Justiere Qualität in feineren Schritten
|
||||
}
|
||||
} while (output.byteLength > maxSize && quality > 0);
|
||||
let timeTaken = Date.now() - start;
|
||||
this.logger.info(`Quality: ${quality} - Time: ${timeTaken} milliseconds`)
|
||||
await sharp(output).toFile(`${directory}/${imageName}.avif`); // Ersetze Dateierweiterung
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
35
bizmatch-server/src/image/image.controller.ts
Normal file
35
bizmatch-server/src/image/image.controller.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Body, Controller, Delete, Get, Inject, Param, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
|
||||
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
|
||||
import { Logger } from 'winston';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
|
||||
@Controller('image')
|
||||
export class ImageController {
|
||||
|
||||
constructor(private fileService:FileService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
|
||||
}
|
||||
|
||||
@Post('uploadPropertyPicture/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
async uploadFile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
await this.fileService.storePropertyPicture(file,id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async getPropertyImagesById(@Param('id') id:string): Promise<any> {
|
||||
return await this.fileService.getPropertyImages(id);
|
||||
}
|
||||
|
||||
@Post('uploadProfile/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadProfile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storeProfilePicture(file,id);
|
||||
}
|
||||
|
||||
@Post('uploadCompanyLogo/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadCompanyLogo(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storeCompanyLogo(file,id);
|
||||
}
|
||||
}
|
||||
10
bizmatch-server/src/image/image.module.ts
Normal file
10
bizmatch-server/src/image/image.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ImageController } from './image.controller.js';
|
||||
import { ImageService } from './image.service.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
|
||||
@Module({
|
||||
controllers: [ImageController],
|
||||
providers: [ImageService,FileService]
|
||||
})
|
||||
export class ImageModule {}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AccountService {}
|
||||
export class ImageService {}
|
||||
@@ -11,11 +11,7 @@ export class CommercialPropertyListingsController {
|
||||
constructor(private readonly listingsService:ListingsService,private fileService:FileService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll(): any {
|
||||
return this.listingsService.getAllCommercialListings();
|
||||
}
|
||||
|
||||
|
||||
@Get(':id')
|
||||
findById(@Param('id') id:string): any {
|
||||
return this.listingsService.getCommercialPropertyListingById(id);
|
||||
@@ -43,14 +39,5 @@ export class CommercialPropertyListingsController {
|
||||
this.listingsService.deleteCommercialPropertyListing(id)
|
||||
}
|
||||
|
||||
@Post('uploadPropertyPicture/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadFile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storePropertyPicture(file,id);
|
||||
}
|
||||
|
||||
@Get('images/:id')
|
||||
getPropertyImagesById(@Param('id') id:string): any {
|
||||
return this.fileService.getPropertyImages(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,6 @@ export class ListingsService {
|
||||
async getAllCommercialListings(start?: number, end?: number) {
|
||||
return await this.commercialPropertyListingRepository.search().return.all()
|
||||
}
|
||||
|
||||
|
||||
async findBusinessListings(criteria:ListingCriteria): Promise<any> {
|
||||
let listings = await this.getAllBusinessListings();
|
||||
return this.find(criteria,listings);
|
||||
|
||||
@@ -20,9 +20,9 @@ export class UserController {
|
||||
return this.userService.saveUser(user);
|
||||
}
|
||||
|
||||
// @Put()
|
||||
// update(@Body() user: any):Promise<UserEntity>{
|
||||
// this.logger.info(`update User`);
|
||||
// return this.userService.saveUser(user);
|
||||
// }
|
||||
@Post('search')
|
||||
find(@Body() criteria: any): any {
|
||||
return this.userService.findUser(criteria);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ import { Module } from '@nestjs/common';
|
||||
import { UserController } from './user.controller.js';
|
||||
import { UserService } from './user.service.js';
|
||||
import { RedisModule } from '../redis/redis.module.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
|
||||
@Module({
|
||||
imports: [RedisModule],
|
||||
controllers: [UserController],
|
||||
providers: [UserService]
|
||||
providers: [UserService,FileService]
|
||||
})
|
||||
export class UserModule {
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Entity, Repository, Schema } from 'redis-om';
|
||||
import { ListingCriteria, User } from '../models/main.model.js';
|
||||
import { REDIS_CLIENT } from '../redis/redis.module.js';
|
||||
import { UserEntity } from '../models/server.model.js';
|
||||
import { FileService } from '../file/file.service.js';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
@@ -24,12 +25,15 @@ export class UserService {
|
||||
}, {
|
||||
dataStructure: 'JSON'
|
||||
})
|
||||
constructor(@Inject(REDIS_CLIENT) private readonly redis: any){
|
||||
constructor(@Inject(REDIS_CLIENT) private readonly redis: any,private fileService:FileService){
|
||||
this.userRepository = new Repository(this.userSchema, redis)
|
||||
this.userRepository.createIndex();
|
||||
}
|
||||
async getUserById( id:string){
|
||||
return await this.userRepository.fetch(id);
|
||||
const user = await this.userRepository.fetch(id) as UserEntity;
|
||||
user.hasCompanyLogo=this.fileService.hasCompanyLogo(id);
|
||||
user.hasProfile=this.fileService.hasProfile(id);
|
||||
return user;
|
||||
}
|
||||
async saveUser(user:any):Promise<UserEntity>{
|
||||
return await this.userRepository.save(user.id,user) as UserEntity
|
||||
|
||||
Reference in New Issue
Block a user