Archive und Logging

This commit is contained in:
Bastian Wagner
2026-02-19 16:19:46 +01:00
parent ef45e91141
commit 7bd6dfae27
28 changed files with 358 additions and 44 deletions

View File

@@ -55,7 +55,6 @@ export class AuthService {
const payload: IExternalAccessPayload = this.jwt.decode(access_token);
return new Promise<User>(async (resolve) => {
let user = await this.userRepo.findByUsername(payload.username, { settings: true });
if (!user) {
user = await this.userRepo.createUser({
username: payload.username,

View File

@@ -24,6 +24,11 @@ export class CylinderController {
return this.service.getCylinders(req.user);
}
@Get('archive')
getCylinderArchive(@Req() req: AuthenticatedRequest): Promise<Cylinder[]> {
return this.service.getDeletedCylinders(req.user);
}
@Delete(':id')
deleteKey(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
return this.service.deleteCylinder(req.user, id);
@@ -44,4 +49,9 @@ export class CylinderController {
) {
return this.service.createCylinder(req.user, b);
}
@Put(':id/restore')
restoreKey(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
return this.service.restoreCylinder(req.user, id);
}
}

View File

@@ -2,7 +2,9 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cylinder, User } from 'src/model/entitites';
import { ActivityRepository, CylinderRepository, KeyRepository } from 'src/model/repositories';
import { ActivityHelperService } from 'src/shared/service/activity.logger.service';
import { HelperService } from 'src/shared/service/system.helper.service';
import { IsNull, Not } from 'typeorm';
@Injectable()
export class CylinderService {
@@ -11,7 +13,8 @@ export class CylinderService {
private readonly keyRepo: KeyRepository,
private systemActivityRepo: ActivityRepository,
private readonly helper: HelperService,
private readonly configService: ConfigService
private readonly configService: ConfigService,
private activityService: ActivityHelperService
) {}
get isDevelopMode(): boolean {
@@ -39,7 +42,8 @@ export class CylinderService {
const keysToDelete = cylinder.keys.filter(k => k.cylinder.length == 1);
await this.keyRepo.softRemove(keysToDelete);
await this.cylinderRepo.softDelete({id: cylinder.id})
await this.cylinderRepo.softDelete({id: cylinder.id});
this.activityService.logCylinderDeleted(user, cylinder)
return;
}
@@ -65,4 +69,27 @@ export class CylinderService {
});
return c
}
getDeletedCylinders(user: User) {
return this.cylinderRepo.find({
where: {
system: { managers: { id: user.id } },
deletedAt: Not(IsNull()),
},
withDeleted: true,
order: { deletedAt: { direction: 'DESC' } },
});
}
async restoreCylinder(user: User, keyID: string) {
const cylinder = await this.cylinderRepo.findOneOrFail({
where: { system: { managers: { id: user.id } } , id: keyID },
withDeleted: true,
});
cylinder.deletedAt = null;
await this.activityService.logCylinderRestored(user, cylinder);
await this.helper.deleteKeyArchiveCache();
return this.cylinderRepo.save(cylinder);
}
}

View File

@@ -63,7 +63,7 @@ export class KeyController {
return this.service.getKeyHandovers(req.user, id);
}
@Get('Archive')
@Get('archive')
getArchive(@Req() req: AuthenticatedRequest) {
return this.service.getDeletedKeys(req.user);
}

View File

@@ -74,7 +74,19 @@ export class KeyService {
}
if (k.keyLost != key.keyLost) {
await this.activityService.logKeyLostUpdate(user, key, key.keyLost);
}
try {
const k = await this.keyrepository.findOne({
where: { id: key.id },
relations: ['cylinder', 'cylinder.system', 'cylinder.system.managers', 'cylinder.system.managers.settings'],
withDeleted: false
});
for (const to of k.cylinder[0].system.managers.filter(m => m.settings.sendSystemUpdateMails)) {
this.mailService.sendKeyLostOrFoundMail({ key, to } )
}
} catch (e) {
console.error(e);
}
}
return this.keyrepository.save(this.keyrepository.create(key));
}

View File

@@ -36,7 +36,8 @@ export enum LogType {
export enum EmailEvent {
GrantSystemAccess,
RemoveSystemAccess,
KeyHandout
KeyHandout,
KeyLostOrFound
}
export interface EmailLogDto {

View File

@@ -10,10 +10,52 @@ export class MailService {
constructor(
private mailserService: MailerService,
private readonly configService: ConfigService,
private readonly logService: LogService,
private readonly logService: LogService
) {
}
async sendKeyLostOrFoundMail({to, key}: {to: User, key: Key}) {
// const subject
const keyAction = key.keyLost == null ? 'wurde gefunden' : 'wurde als verloren gemeldet';
const keyExtendedAction = key.keyLost == null ? `wurde als gefunden gemeldet` : `wurde am ${new Date(key.keyLost).toLocaleDateString()} als verloren gemeldet`;
const subject = key.keyLost == null ? 'Schlüssel gefunden' : 'Schlüssel verloren';
const context = {
keyAction,
keyExtendedAction,
firstName: to.firstName,
keyNr: key.nr,
keyName: key.name,
url: 'https://keyvaultpro.de/keys?nr=' + key.nr
}
this.mailserService.sendMail({
template: './key-handout-changed',
to: to.username,
from: this.configService.get<string>('MAILER_FROM'),
subject: subject,
context
}).then(v => {
this.logService.log(LogType.Mail, {
to: to.username,
success: true,
message: v.response,
type: EmailEvent.KeyLostOrFound,
system: key.cylinder[0].system,
context: JSON.stringify(key)
})
}).catch(e => {
this.logService.log(LogType.Mail, {
to,
success: false,
message: e.response,
type: EmailEvent.KeyLostOrFound,
system: key.cylinder[0].system,
context: JSON.stringify(key)
})
})
}
async sendKeyHandoutMail({to, key, handoutAction}: {to: User, key: Key, handoutAction: KeyHandout}) {
const keyAction = handoutAction.direction == 'out' ? 'wurde ausgegeben' : 'wurde zurückgegeben';
const keyExtendedAction = handoutAction.direction == 'return' ? `wurde von ${handoutAction.customer.name} zurückgegeben` : `wurde an ${handoutAction.customer.name} ausgegeben`;

View File

@@ -44,6 +44,7 @@ export class SystemService {
let systems = await this.systemRepo.find({
where: { managers: { id: user.id } },
order: { name: { direction: 'ASC' } },
relations: ['cylinders']
});
if (this.isDevelopMode) {

View File

@@ -4,8 +4,6 @@ import { UserService } from './user.service';
import { User } from 'src/model/entitites';
import { IUser } from 'src/model/interface';
import { AuthenticatedRequest } from 'src/model/interface/authenticated-request.interface';
import { HttpErrorByCode } from '@nestjs/common/utils/http-error-by-code.util';
import { HttpStatusCode } from 'axios';
import { UserSettings } from 'src/model/entitites/user/user.settings.entity';
@UseGuards(AuthGuard)

View File

@@ -46,7 +46,7 @@ export class UserService {
const keys = cylinders.map(c => c.keys).flat().map(k => k.id);
const keycount = [...new Set(keys)]
const handedOut = (await this.helper.getUsersKeys(user)).filter(k => k.handedOut).length;
const handedOut = (await this.helper.getUsersKeys(user)).filter(k => k.handedOut && k.keyLost == null).length;
return {
keys: keycount.length,
cylinders: cylinders.length,

View File

@@ -1,5 +1,5 @@
import { Injectable } from "@nestjs/common";
import { Key, KeyHandout, User } from "src/model/entitites";
import { Cylinder, Key, KeyHandout, User } from "src/model/entitites";
import { KeySystem } from "src/model/entitites/system.entity";
import { ActivityRepository, CylinderRepository, KeyRepository } from "src/model/repositories";
import { HelperService } from "./system.helper.service";
@@ -115,4 +115,30 @@ export class ActivityHelperService {
message: msg,
}))
}
async logCylinderRestored(user: User, cylinder: Cylinder) {
let msg = `Zylinder ${cylinder.name} wiederhergestellt`;
const system: KeySystem = await this.helper.getSystemOCylinder(cylinder);
this.activityRepo.save(
this.activityRepo.create({
system,
user,
message: msg,
}))
}
async logCylinderDeleted(user: User, cylinder: Cylinder) {
let msg = `Zylinder ${cylinder.name} gelöscht`;
const system: KeySystem = await this.helper.getSystemOCylinder(cylinder);
this.activityRepo.save(
this.activityRepo.create({
system,
user,
message: msg,
}))
}
}

View File

@@ -50,6 +50,16 @@ export class HelperService {
return found.system;
}
async getSystemOCylinder(cylinder: Cylinder): Promise<KeySystem> {
const k = await this.cylinderRepository.findOne({
where: { id: cylinder.id },
relations: ['system'],
withDeleted: true,
});
this.cache()
return k.system;
}
async cache() {
const value = await this.cacheManager.store.keys()
console.log(value)