refactor(quick-260921-m34): Aufgabe 1 - Mandantenbindung entzaubert, 105 unnoetige any-Zusicherungen entfernt
- prisma-tenant.extension.ts: (prisma as any) und die Handannotation an
$allOperations in forTenant()/forSystem() entfernt; Kopfkommentar
unveraendert. .then((results: any[]) => ...) auf unknown[] umgestellt.
- 105 Aufrufstellen `const X = forTenant(...) as any` / `forSystem(...) as
any` von der Zusicherung befreit, Zuweisungsform woertlich erhalten
(rls-access-inventory.spec.ts bleibt scharf, 30/30 gruen einzeln
geprueft).
- withTenantTransaction(): Prisma.TransactionClient fuer tx probiert,
gemessen verworfen - bricht das Testdoppel in
prisma-tenant.extension.spec.ts (TS2322 auf einem absichtlich
unvollstaendigen Fake-Objekt). tx bleibt any, mit Begruendung am Typ.
- Gefolge des jetzt getypten Klienten entfernt: any[]-Annotationen und
.map((x: any) => ...) in groups.service.ts, module-grants.service.ts,
dkv.service.ts, ldap-config.service.ts, tenders.controller.ts:270.
- Befund (D-03): tender-matching.service.ts:159 trug eine Handannotation
(match: { tender: unknown }), die den Wert nur deshalb auf unknown
verengte, um TS7006 unter dem alten any-Klienten zu vermeiden - mit dem
getypten Klienten war das falsch. Annotation geloescht, kein Ersatz
durch Zusicherung.
- Zwei any bleiben gezielt in groups.service.ts (u/a in
ensureDefaultGroup(), gefolge von tx: any) - Begruendung am Code.
noExplicitAny apps/api/src: 288 -> 149 (Schranke 155). type-check 4/4,
lint 5/5 (0 error). apps/api 72/1143 gruen, apps/web 73/531 gruen,
rls-access-inventory.spec.ts 30/30 gruen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -115,7 +115,7 @@ export class AuthService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, user.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, user.tenantId);
|
||||||
|
|
||||||
// LDAP users have no local password — authenticate them against the
|
// LDAP users have no local password — authenticate them against the
|
||||||
// directory by binding as their OWN DN with the password they entered.
|
// directory by binding as their OWN DN with the password they entered.
|
||||||
@@ -233,7 +233,7 @@ export class AuthService {
|
|||||||
|
|
||||||
// Create the reset token record — mandantengebunden, sobald der
|
// Create the reset token record — mandantengebunden, sobald der
|
||||||
// Benutzer und damit sein Mandant bekannt sind (WINDOWS #20, Aufgabe 1).
|
// Benutzer und damit sein Mandant bekannt sind (WINDOWS #20, Aufgabe 1).
|
||||||
const tenantPrisma = forTenant(this.prisma, user.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, user.tenantId);
|
||||||
await tenantPrisma.passwordResetToken.create({
|
await tenantPrisma.passwordResetToken.create({
|
||||||
data: {
|
data: {
|
||||||
token,
|
token,
|
||||||
@@ -274,7 +274,7 @@ export class AuthService {
|
|||||||
|
|
||||||
// Mandant ist ab hier bekannt (aus der Funktion mitgeliefert) — beide
|
// Mandant ist ab hier bekannt (aus der Funktion mitgeliefert) — beide
|
||||||
// Schreibzugriffe laufen gebunden (WINDOWS #20, Aufgabe 1).
|
// Schreibzugriffe laufen gebunden (WINDOWS #20, Aufgabe 1).
|
||||||
const tenantPrisma = forTenant(this.prisma, resetToken.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, resetToken.tenantId);
|
||||||
|
|
||||||
// Hash the new password and update user
|
// Hash the new password and update user
|
||||||
const passwordHash = await argon2.hash(newPassword);
|
const passwordHash = await argon2.hash(newPassword);
|
||||||
@@ -308,7 +308,7 @@ export class AuthService {
|
|||||||
* Zeile.
|
* Zeile.
|
||||||
*/
|
*/
|
||||||
async getMe(tenantId: string, userId: string) {
|
async getMe(tenantId: string, userId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const user = await tenantPrisma.user.findUnique({
|
const user = await tenantPrisma.user.findUnique({
|
||||||
where: { id: userId },
|
where: { id: userId },
|
||||||
select: {
|
select: {
|
||||||
@@ -353,7 +353,7 @@ export class AuthService {
|
|||||||
newPassword: string,
|
newPassword: string,
|
||||||
response: Response,
|
response: Response,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const user = await tenantPrisma.user.findUnique({
|
const user = await tenantPrisma.user.findUnique({
|
||||||
where: { id: userId },
|
where: { id: userId },
|
||||||
});
|
});
|
||||||
@@ -417,7 +417,7 @@ export class AuthService {
|
|||||||
newPassword: string,
|
newPassword: string,
|
||||||
mustChangePassword: boolean = true,
|
mustChangePassword: boolean = true,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const user = await tenantPrisma.user.findUnique({
|
const user = await tenantPrisma.user.findUnique({
|
||||||
where: { id: userId },
|
where: { id: userId },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export class BugReportsService {
|
|||||||
|
|
||||||
// (4) Benutzerzeile: gebunden an den Sitzungs-Mandanten, nie an Rumpfdaten
|
// (4) Benutzerzeile: gebunden an den Sitzungs-Mandanten, nie an Rumpfdaten
|
||||||
// (T-M97-06; Zeile in docs/mandantentrennung-zugriffsklassifikation.md).
|
// (T-M97-06; Zeile in docs/mandantentrennung-zugriffsklassifikation.md).
|
||||||
const tenantPrisma = forTenant(this.prisma, user.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, user.tenantId);
|
||||||
const row = await tenantPrisma.user.findUnique({
|
const row = await tenantPrisma.user.findUnique({
|
||||||
where: { id: user.id },
|
where: { id: user.id },
|
||||||
select: { username: true, displayName: true, email: true, role: true },
|
select: { username: true, displayName: true, email: true, role: true },
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export class DkvService {
|
|||||||
* 260914-eym systemgebunden, eine Zeile je aktivem Mandanten).
|
* 260914-eym systemgebunden, eine Zeile je aktivem Mandanten).
|
||||||
*/
|
*/
|
||||||
async loadConfig(tenantId: string) {
|
async loadConfig(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.dkvModuleConfig.findUnique({
|
return tenantPrisma.dkvModuleConfig.findUnique({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
select: CONFIG_SAFE_SELECT,
|
select: CONFIG_SAFE_SELECT,
|
||||||
@@ -153,7 +153,7 @@ export class DkvService {
|
|||||||
* (T-07-12) — der Planer braucht nur tenantId und pollIntervalMin.
|
* (T-07-12) — der Planer braucht nur tenantId und pollIntervalMin.
|
||||||
*/
|
*/
|
||||||
async loadActiveConfigsForScheduler() {
|
async loadActiveConfigsForScheduler() {
|
||||||
const systemPrisma = forSystem(this.prisma) as any;
|
const systemPrisma = forSystem(this.prisma);
|
||||||
return systemPrisma.dkvModuleConfig.findMany({
|
return systemPrisma.dkvModuleConfig.findMany({
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
select: CONFIG_SAFE_SELECT,
|
select: CONFIG_SAFE_SELECT,
|
||||||
@@ -171,7 +171,7 @@ export class DkvService {
|
|||||||
* Methode).
|
* Methode).
|
||||||
*/
|
*/
|
||||||
async getConfigForApi(tenantId: string) {
|
async getConfigForApi(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const safe = await tenantPrisma.dkvModuleConfig.findUnique({
|
const safe = await tenantPrisma.dkvModuleConfig.findUnique({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
select: CONFIG_SAFE_SELECT,
|
select: CONFIG_SAFE_SELECT,
|
||||||
@@ -214,7 +214,7 @@ export class DkvService {
|
|||||||
* Bindung als zweite Schicht bestehen.
|
* Bindung als zweite Schicht bestehen.
|
||||||
*/
|
*/
|
||||||
async saveConfig(tenantId: string, dto: DkvConfigDto) {
|
async saveConfig(tenantId: string, dto: DkvConfigDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
let encryptedInboxCreds: string | undefined;
|
let encryptedInboxCreds: string | undefined;
|
||||||
|
|
||||||
const credChanged = (dto.password && dto.password.length > 0) ||
|
const credChanged = (dto.password && dto.password.length > 0) ||
|
||||||
@@ -277,7 +277,7 @@ export class DkvService {
|
|||||||
* Rueckgriff auf die gespeicherten Zugangsdaten.
|
* Rueckgriff auf die gespeicherten Zugangsdaten.
|
||||||
*/
|
*/
|
||||||
async testConnection(tenantId: string, dto: DkvConfigDto): Promise<{ success: boolean; message?: string }> {
|
async testConnection(tenantId: string, dto: DkvConfigDto): Promise<{ success: boolean; message?: string }> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
let password: string | undefined = dto.password;
|
let password: string | undefined = dto.password;
|
||||||
|
|
||||||
// If no password in DTO, fall back to the stored one
|
// If no password in DTO, fall back to the stored one
|
||||||
@@ -362,7 +362,7 @@ export class DkvService {
|
|||||||
private async _runPipeline(tenantId: string): Promise<void> {
|
private async _runPipeline(tenantId: string): Promise<void> {
|
||||||
// Load raw config (need encryptedInboxCreds for decryption).
|
// Load raw config (need encryptedInboxCreds for decryption).
|
||||||
// Mandantengebunden (260909-mir).
|
// Mandantengebunden (260909-mir).
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const config = await tenantPrisma.dkvModuleConfig.findUnique({ where: { tenantId } });
|
const config = await tenantPrisma.dkvModuleConfig.findUnique({ where: { tenantId } });
|
||||||
if (!config) {
|
if (!config) {
|
||||||
this.logger.warn(`DKV processInbox: no config for tenant ${tenantId}`);
|
this.logger.warn(`DKV processInbox: no config for tenant ${tenantId}`);
|
||||||
@@ -448,7 +448,7 @@ export class DkvService {
|
|||||||
// Mandantengebunden (260909-mir): EIN gebundener Klient fuer beide
|
// Mandantengebunden (260909-mir): EIN gebundener Klient fuer beide
|
||||||
// dkvInvoiceHistory.create()-Aufrufe dieser Methode (Erfolgsfall UND
|
// dkvInvoiceHistory.create()-Aufrufe dieser Methode (Erfolgsfall UND
|
||||||
// Zerlegungsfehler-Fall).
|
// Zerlegungsfehler-Fall).
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
// D-10: Up to 3 parse retries
|
// D-10: Up to 3 parse retries
|
||||||
let parseResult: Awaited<ReturnType<typeof this.parser.parsePdf>> | null = null;
|
let parseResult: Awaited<ReturnType<typeof this.parser.parsePdf>> | null = null;
|
||||||
@@ -546,7 +546,7 @@ export class DkvService {
|
|||||||
// ─── Vehicle CRUD ────────────────────────────────────────────────────────────
|
// ─── Vehicle CRUD ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async listVehicles(tenantId: string) {
|
async listVehicles(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.dkvVehicleMaster.findMany({
|
return tenantPrisma.dkvVehicleMaster.findMany({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
orderBy: { kennzeichen: 'asc' },
|
orderBy: { kennzeichen: 'asc' },
|
||||||
@@ -554,7 +554,7 @@ export class DkvService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createVehicle(tenantId: string, dto: CreateVehicleDto) {
|
async createVehicle(tenantId: string, dto: CreateVehicleDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.dkvVehicleMaster.create({
|
return tenantPrisma.dkvVehicleMaster.create({
|
||||||
data: { tenantId, ...dto },
|
data: { tenantId, ...dto },
|
||||||
});
|
});
|
||||||
@@ -571,7 +571,7 @@ export class DkvService {
|
|||||||
* Schreibzugriff dahinter waere genau die Luecke, nicht die Loesung.
|
* Schreibzugriff dahinter waere genau die Luecke, nicht die Loesung.
|
||||||
*/
|
*/
|
||||||
async updateVehicle(tenantId: string, id: string, dto: UpdateVehicleDto) {
|
async updateVehicle(tenantId: string, id: string, dto: UpdateVehicleDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const existing = await tenantPrisma.dkvVehicleMaster.findFirst({
|
const existing = await tenantPrisma.dkvVehicleMaster.findFirst({
|
||||||
where: { id, tenantId },
|
where: { id, tenantId },
|
||||||
});
|
});
|
||||||
@@ -581,7 +581,7 @@ export class DkvService {
|
|||||||
|
|
||||||
/** Mandantengebunden (260909-mir, Befund G) — siehe updateVehicle() oben. */
|
/** Mandantengebunden (260909-mir, Befund G) — siehe updateVehicle() oben. */
|
||||||
async deleteVehicle(tenantId: string, id: string): Promise<{ deleted: boolean }> {
|
async deleteVehicle(tenantId: string, id: string): Promise<{ deleted: boolean }> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const existing = await tenantPrisma.dkvVehicleMaster.findFirst({
|
const existing = await tenantPrisma.dkvVehicleMaster.findFirst({
|
||||||
where: { id, tenantId },
|
where: { id, tenantId },
|
||||||
});
|
});
|
||||||
@@ -611,7 +611,7 @@ export class DkvService {
|
|||||||
csvText: string,
|
csvText: string,
|
||||||
mode: 'merge' | 'replace',
|
mode: 'merge' | 'replace',
|
||||||
): Promise<{ imported: number; mode: string }> {
|
): Promise<{ imported: number; mode: string }> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const vehicles = _parseVehicleCsv(csvText);
|
const vehicles = _parseVehicleCsv(csvText);
|
||||||
if (vehicles.length === 0) {
|
if (vehicles.length === 0) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -661,7 +661,7 @@ export class DkvService {
|
|||||||
page: number;
|
page: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}> {
|
}> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const skip = (page - 1) * limit;
|
const skip = (page - 1) * limit;
|
||||||
const [items, total] = await Promise.all([
|
const [items, total] = await Promise.all([
|
||||||
tenantPrisma.dkvInvoiceHistory.findMany({
|
tenantPrisma.dkvInvoiceHistory.findMany({
|
||||||
@@ -714,7 +714,7 @@ export class DkvService {
|
|||||||
|
|
||||||
// Stage 2 (NEW, 260909-mir): the ownership gate. A bound read — the
|
// Stage 2 (NEW, 260909-mir): the ownership gate. A bound read — the
|
||||||
// only tenant-scoped statement of who this file belongs to.
|
// only tenant-scoped statement of who this file belongs to.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const owningHistoryRow = await tenantPrisma.dkvInvoiceHistory.findFirst({
|
const owningHistoryRow = await tenantPrisma.dkvInvoiceHistory.findFirst({
|
||||||
where: { tenantId, exportFilename: filename },
|
where: { tenantId, exportFilename: filename },
|
||||||
});
|
});
|
||||||
@@ -751,8 +751,8 @@ export class DkvService {
|
|||||||
vehicleFormatString: string,
|
vehicleFormatString: string,
|
||||||
): Promise<{ lieferdatum: string; fahrzeug: string; fahrer: string; ort: string; kilometerstand: number | null }[]> {
|
): Promise<{ lieferdatum: string; fahrzeug: string; fahrer: string; ort: string; kilometerstand: number | null }[]> {
|
||||||
// Batch load vehicle master to avoid N+1 queries
|
// Batch load vehicle master to avoid N+1 queries
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const masters: any[] = await tenantPrisma.dkvVehicleMaster.findMany({ where: { tenantId } });
|
const masters = await tenantPrisma.dkvVehicleMaster.findMany({ where: { tenantId } });
|
||||||
// Normalize keys: DKV PDF may omit hyphens or use spaces ("GP JL 740E" vs "GP-JL 740E")
|
// Normalize keys: DKV PDF may omit hyphens or use spaces ("GP JL 740E" vs "GP-JL 740E")
|
||||||
const masterMap = new Map(masters.map((m) => [_normalizeKennzeichen(m.kennzeichen), m]));
|
const masterMap = new Map(masters.map((m) => [_normalizeKennzeichen(m.kennzeichen), m]));
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class FavoritesService {
|
|||||||
async list(tenantId: string, userId: string, widgetId: string) {
|
async list(tenantId: string, userId: string, widgetId: string) {
|
||||||
if (!widgetId) throw new BadRequestException('widgetId is required');
|
if (!widgetId) throw new BadRequestException('widgetId is required');
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
return tenantPrisma.favoriteLink.findMany({
|
return tenantPrisma.favoriteLink.findMany({
|
||||||
where: { userId, widgetId },
|
where: { userId, widgetId },
|
||||||
orderBy: [{ position: 'asc' }, { title: 'asc' }],
|
orderBy: [{ position: 'asc' }, { title: 'asc' }],
|
||||||
@@ -79,7 +79,7 @@ export class FavoritesService {
|
|||||||
* If iconUrl is not provided, triggers server-side icon discovery with SSRF protection.
|
* If iconUrl is not provided, triggers server-side icon discovery with SSRF protection.
|
||||||
*/
|
*/
|
||||||
async create(tenantId: string, userId: string, dto: CreateFavoriteDto) {
|
async create(tenantId: string, userId: string, dto: CreateFavoriteDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
|
|
||||||
// T-GWH-05: der Fremdschluessel prueft an der Zeilenschutz-Regel von
|
// T-GWH-05: der Fremdschluessel prueft an der Zeilenschutz-Regel von
|
||||||
// WidgetInstance vorbei (Aufgabe 1, Pruefung 7) — ohne diesen Riegel
|
// WidgetInstance vorbei (Aufgabe 1, Pruefung 7) — ohne diesen Riegel
|
||||||
@@ -123,7 +123,7 @@ export class FavoritesService {
|
|||||||
* Accepts null as an explicit value for iconUrl (clears stored icon).
|
* Accepts null as an explicit value for iconUrl (clears stored icon).
|
||||||
*/
|
*/
|
||||||
async update(tenantId: string, id: string, userId: string, dto: UpdateFavoriteDto) {
|
async update(tenantId: string, id: string, userId: string, dto: UpdateFavoriteDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const link = await tenantPrisma.favoriteLink.findUnique({ where: { id } });
|
const link = await tenantPrisma.favoriteLink.findUnique({ where: { id } });
|
||||||
|
|
||||||
if (!link || link.userId !== userId) {
|
if (!link || link.userId !== userId) {
|
||||||
@@ -164,7 +164,7 @@ export class FavoritesService {
|
|||||||
* Verifies userId ownership before deleting (T-08-06).
|
* Verifies userId ownership before deleting (T-08-06).
|
||||||
*/
|
*/
|
||||||
async remove(tenantId: string, id: string, userId: string) {
|
async remove(tenantId: string, id: string, userId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const link = await tenantPrisma.favoriteLink.findUnique({ where: { id } });
|
const link = await tenantPrisma.favoriteLink.findUnique({ where: { id } });
|
||||||
|
|
||||||
if (!link || link.userId !== userId) {
|
if (!link || link.userId !== userId) {
|
||||||
@@ -210,7 +210,7 @@ export class FavoritesService {
|
|||||||
throw new BadRequestException('ids must match the favorites of this widget exactly');
|
throw new BadRequestException('ids must match the favorites of this widget exactly');
|
||||||
}
|
}
|
||||||
|
|
||||||
return withTenantTransaction(this.prisma, tenantId, async (tx: any) => {
|
return withTenantTransaction(this.prisma, tenantId, async (tx) => {
|
||||||
const existing = await tx.favoriteLink.findMany({
|
const existing = await tx.favoriteLink.findMany({
|
||||||
where: { userId, widgetId: dto.widgetId },
|
where: { userId, widgetId: dto.widgetId },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
@@ -258,7 +258,7 @@ export class FavoritesService {
|
|||||||
id: string,
|
id: string,
|
||||||
userId: string,
|
userId: string,
|
||||||
): Promise<{ contentType: string; body: Buffer }> {
|
): Promise<{ contentType: string; body: Buffer }> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const link = await tenantPrisma.favoriteLink.findUnique({ where: { id } });
|
const link = await tenantPrisma.favoriteLink.findUnique({ where: { id } });
|
||||||
|
|
||||||
if (!link || link.userId !== userId || !link.iconUrl) {
|
if (!link || link.userId !== userId || !link.iconUrl) {
|
||||||
|
|||||||
@@ -53,20 +53,14 @@ export class GroupsService {
|
|||||||
* Mitgliederzahl. Ein Mandant ohne Gruppen liefert ein leeres Array.
|
* Mitgliederzahl. Ein Mandant ohne Gruppen liefert ein leeres Array.
|
||||||
*/
|
*/
|
||||||
async listForTenant(tenantId: string) {
|
async listForTenant(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
// Explizit als any[] annotiert (nicht nur der Rueckgabewert von await):
|
const groups = await tenantPrisma.group.findMany({
|
||||||
// ohne diese Array-Verankerung inferiert TypeScript den Rueckgabewert
|
|
||||||
// dieser Methode als bloss `any` statt `any[]`, und Aufrufer, die auf
|
|
||||||
// dem Ergebnis `.find()` aufrufen, wuerden TS7006 (impliziter any-Typ
|
|
||||||
// im Callback-Parameter) melden, obwohl der gebundene Client bewusst
|
|
||||||
// `any` ist (siehe forTenant()-Aufrufe in dieser Datei).
|
|
||||||
const groups: any[] = await tenantPrisma.group.findMany({
|
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
orderBy: { name: 'asc' },
|
orderBy: { name: 'asc' },
|
||||||
include: { _count: { select: { memberships: true } } },
|
include: { _count: { select: { memberships: true } } },
|
||||||
});
|
});
|
||||||
|
|
||||||
return groups.map((g: any) => ({
|
return groups.map((g) => ({
|
||||||
id: g.id,
|
id: g.id,
|
||||||
tenantId: g.tenantId,
|
tenantId: g.tenantId,
|
||||||
name: g.name,
|
name: g.name,
|
||||||
@@ -92,7 +86,7 @@ export class GroupsService {
|
|||||||
throw new BadRequestException('Gruppenname darf nicht leer sein');
|
throw new BadRequestException('Gruppenname darf nicht leer sein');
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.group.create({
|
return await tenantPrisma.group.create({
|
||||||
data: { tenantId, name },
|
data: { tenantId, name },
|
||||||
@@ -112,7 +106,7 @@ export class GroupsService {
|
|||||||
* Mandanten liefert NotFoundException statt eines Treffers.
|
* Mandanten liefert NotFoundException statt eines Treffers.
|
||||||
*/
|
*/
|
||||||
private async findOwned(tenantId: string, id: string) {
|
private async findOwned(tenantId: string, id: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const group = await tenantPrisma.group.findFirst({
|
const group = await tenantPrisma.group.findFirst({
|
||||||
where: { id, tenantId },
|
where: { id, tenantId },
|
||||||
});
|
});
|
||||||
@@ -188,7 +182,7 @@ export class GroupsService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (data.isDefault === true) {
|
if (data.isDefault === true) {
|
||||||
const updated = await withTenantTransaction(this.prisma, tenantId, async (tx: any) => {
|
const updated = await withTenantTransaction(this.prisma, tenantId, async (tx) => {
|
||||||
await tx.group.updateMany({
|
await tx.group.updateMany({
|
||||||
where: { tenantId, isDefault: true },
|
where: { tenantId, isDefault: true },
|
||||||
data: { isDefault: false },
|
data: { isDefault: false },
|
||||||
@@ -205,7 +199,7 @@ export class GroupsService {
|
|||||||
updateData.isDefault = false;
|
updateData.isDefault = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return await tenantPrisma.group.update({
|
return await tenantPrisma.group.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: updateData,
|
data: updateData,
|
||||||
@@ -227,7 +221,7 @@ export class GroupsService {
|
|||||||
async getImpact(tenantId: string, id: string) {
|
async getImpact(tenantId: string, id: string) {
|
||||||
await this.findOwned(tenantId, id);
|
await this.findOwned(tenantId, id);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const [memberCount, grantCount] = await Promise.all([
|
const [memberCount, grantCount] = await Promise.all([
|
||||||
tenantPrisma.groupMembership.count({ where: { groupId: id } }),
|
tenantPrisma.groupMembership.count({ where: { groupId: id } }),
|
||||||
tenantPrisma.moduleGrant.count({ where: { groupId: id } }),
|
tenantPrisma.moduleGrant.count({ where: { groupId: id } }),
|
||||||
@@ -247,7 +241,7 @@ export class GroupsService {
|
|||||||
async remove(tenantId: string, id: string) {
|
async remove(tenantId: string, id: string) {
|
||||||
await this.findOwned(tenantId, id);
|
await this.findOwned(tenantId, id);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.group.delete({ where: { id } });
|
return await tenantPrisma.group.delete({ where: { id } });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -265,7 +259,7 @@ export class GroupsService {
|
|||||||
async listMembers(tenantId: string, id: string) {
|
async listMembers(tenantId: string, id: string) {
|
||||||
await this.findOwned(tenantId, id);
|
await this.findOwned(tenantId, id);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.groupMembership.findMany({
|
return tenantPrisma.groupMembership.findMany({
|
||||||
where: { groupId: id },
|
where: { groupId: id },
|
||||||
include: {
|
include: {
|
||||||
@@ -286,12 +280,12 @@ export class GroupsService {
|
|||||||
async addMembers(tenantId: string, id: string, userIds: string[]) {
|
async addMembers(tenantId: string, id: string, userIds: string[]) {
|
||||||
await this.findOwned(tenantId, id);
|
await this.findOwned(tenantId, id);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const validUsers = await tenantPrisma.user.findMany({
|
const validUsers = await tenantPrisma.user.findMany({
|
||||||
where: { id: { in: userIds }, tenantId },
|
where: { id: { in: userIds }, tenantId },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
const validIds = validUsers.map((u: any) => u.id);
|
const validIds = validUsers.map((u) => u.id);
|
||||||
if (validIds.length === 0) {
|
if (validIds.length === 0) {
|
||||||
return { added: 0 };
|
return { added: 0 };
|
||||||
}
|
}
|
||||||
@@ -316,7 +310,7 @@ export class GroupsService {
|
|||||||
async removeMember(tenantId: string, id: string, userId: string) {
|
async removeMember(tenantId: string, id: string, userId: string) {
|
||||||
await this.findOwned(tenantId, id);
|
await this.findOwned(tenantId, id);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
await tenantPrisma.groupMembership.deleteMany({
|
await tenantPrisma.groupMembership.deleteMany({
|
||||||
where: { groupId: id, userId, source: MembershipSource.MANUAL },
|
where: { groupId: id, userId, source: MembershipSource.MANUAL },
|
||||||
});
|
});
|
||||||
@@ -354,14 +348,14 @@ export class GroupsService {
|
|||||||
* propagieren.
|
* propagieren.
|
||||||
*/
|
*/
|
||||||
async ensureDefaultGroup(tenantId: string) {
|
async ensureDefaultGroup(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const existingCount = await tenantPrisma.group.count({ where: { tenantId } });
|
const existingCount = await tenantPrisma.group.count({ where: { tenantId } });
|
||||||
if (existingCount > 0) {
|
if (existingCount > 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await withTenantTransaction(this.prisma, tenantId, async (tx: any) => {
|
return await withTenantTransaction(this.prisma, tenantId, async (tx) => {
|
||||||
const group = await tx.group.create({
|
const group = await tx.group.create({
|
||||||
data: { tenantId, name: DEFAULT_GROUP_NAME, isDefault: true },
|
data: { tenantId, name: DEFAULT_GROUP_NAME, isDefault: true },
|
||||||
});
|
});
|
||||||
@@ -372,6 +366,9 @@ export class GroupsService {
|
|||||||
});
|
});
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
await tx.groupMembership.createMany({
|
await tx.groupMembership.createMany({
|
||||||
|
// u: any bleibt (gemessen, Aufgabe 1 260921-m34) - tx ist selbst
|
||||||
|
// any (siehe Begruendung an withTenantTransaction()), any.map()
|
||||||
|
// gibt hier keine kontextuelle Typisierung des Parameters.
|
||||||
data: users.map((u: any) => ({
|
data: users.map((u: any) => ({
|
||||||
groupId: group.id,
|
groupId: group.id,
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
@@ -387,6 +384,8 @@ export class GroupsService {
|
|||||||
});
|
});
|
||||||
if (activations.length > 0) {
|
if (activations.length > 0) {
|
||||||
await tx.moduleGrant.createMany({
|
await tx.moduleGrant.createMany({
|
||||||
|
// a: any bleibt (gemessen, Aufgabe 1 260921-m34) - selbe Ursache
|
||||||
|
// wie bei `u` oben: tx ist any.
|
||||||
data: activations.map((a: any) => ({
|
data: activations.map((a: any) => ({
|
||||||
tenantId,
|
tenantId,
|
||||||
moduleId: a.moduleId,
|
moduleId: a.moduleId,
|
||||||
@@ -435,7 +434,7 @@ export class GroupsService {
|
|||||||
* werfen — exakt das Muster aus ensureDefaultGroup().
|
* werfen — exakt das Muster aus ensureDefaultGroup().
|
||||||
*/
|
*/
|
||||||
async reassignDefaultBeforeDelete(tenantId: string, groupId: string): Promise<boolean> {
|
async reassignDefaultBeforeDelete(tenantId: string, groupId: string): Promise<boolean> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
const group = await tenantPrisma.group.findFirst({
|
const group = await tenantPrisma.group.findFirst({
|
||||||
where: { id: groupId, tenantId },
|
where: { id: groupId, tenantId },
|
||||||
@@ -458,7 +457,7 @@ export class GroupsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await withTenantTransaction(this.prisma, tenantId, async (tx: any) => {
|
await withTenantTransaction(this.prisma, tenantId, async (tx) => {
|
||||||
await tx.group.updateMany({
|
await tx.group.updateMany({
|
||||||
where: { tenantId, isDefault: true },
|
where: { tenantId, isDefault: true },
|
||||||
data: { isDefault: false },
|
data: { isDefault: false },
|
||||||
@@ -497,7 +496,7 @@ export class GroupsService {
|
|||||||
* Treffer folgenlos zurückkehren statt zu werfen.
|
* Treffer folgenlos zurückkehren statt zu werfen.
|
||||||
*/
|
*/
|
||||||
async addUserToDefaultGroup(tenantId: string, userId: string) {
|
async addUserToDefaultGroup(tenantId: string, userId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const defaultGroup = await tenantPrisma.group.findFirst({
|
const defaultGroup = await tenantPrisma.group.findFirst({
|
||||||
where: { tenantId, isDefault: true },
|
where: { tenantId, isDefault: true },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export class ModuleGrantsService {
|
|||||||
groupId?: string,
|
groupId?: string,
|
||||||
userId?: string,
|
userId?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
const group = await tenantPrisma.group.findFirst({
|
const group = await tenantPrisma.group.findFirst({
|
||||||
where: { id: groupId, tenantId },
|
where: { id: groupId, tenantId },
|
||||||
@@ -107,7 +107,7 @@ export class ModuleGrantsService {
|
|||||||
// Datenbank" entfallen.
|
// Datenbank" entfallen.
|
||||||
await this.assertTargetBelongsToTenant(tenantId, groupId, userId);
|
await this.assertTargetBelongsToTenant(tenantId, groupId, userId);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const activation = await tenantPrisma.tenantModuleActivation.findUnique({
|
const activation = await tenantPrisma.tenantModuleActivation.findUnique({
|
||||||
where: { tenantId_moduleId: { tenantId, moduleId } },
|
where: { tenantId_moduleId: { tenantId, moduleId } },
|
||||||
});
|
});
|
||||||
@@ -166,7 +166,7 @@ export class ModuleGrantsService {
|
|||||||
const { moduleId, groupId, userId } = data;
|
const { moduleId, groupId, userId } = data;
|
||||||
const target = groupId ? `group=${groupId}` : `user=${userId}`;
|
const target = groupId ? `group=${groupId}` : `user=${userId}`;
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
await tenantPrisma.moduleGrant.deleteMany({
|
await tenantPrisma.moduleGrant.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -189,7 +189,7 @@ export class ModuleGrantsService {
|
|||||||
* hinweg stabil.
|
* hinweg stabil.
|
||||||
*/
|
*/
|
||||||
async getMatrix(tenantId: string) {
|
async getMatrix(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const [activations, groups, groupGrants] = await Promise.all([
|
const [activations, groups, groupGrants] = await Promise.all([
|
||||||
tenantPrisma.tenantModuleActivation.findMany({
|
tenantPrisma.tenantModuleActivation.findMany({
|
||||||
where: { tenantId, isActive: true },
|
where: { tenantId, isActive: true },
|
||||||
@@ -206,18 +206,17 @@ export class ModuleGrantsService {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const modules = activations
|
const modules = activations
|
||||||
.map((a: any) => a.module)
|
.map((a) => a.module)
|
||||||
.sort(
|
.sort(
|
||||||
(a: any, b: any) =>
|
(a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name),
|
||||||
a.category.localeCompare(b.category) || a.name.localeCompare(b.name),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modules,
|
modules,
|
||||||
groups,
|
groups,
|
||||||
grants: groupGrants.map((g: any) => ({
|
grants: groupGrants.map((g) => ({
|
||||||
moduleId: g.moduleId as string,
|
moduleId: g.moduleId,
|
||||||
groupId: g.groupId as string,
|
groupId: g.groupId,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -246,7 +245,7 @@ export class ModuleGrantsService {
|
|||||||
async getUserAccess(tenantId: string, userId: string) {
|
async getUserAccess(tenantId: string, userId: string) {
|
||||||
await this.assertTargetBelongsToTenant(tenantId, undefined, userId);
|
await this.assertTargetBelongsToTenant(tenantId, undefined, userId);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const [activations, groupGrants, directGrants, memberships] = await Promise.all([
|
const [activations, groupGrants, directGrants, memberships] = await Promise.all([
|
||||||
tenantPrisma.tenantModuleActivation.findMany({
|
tenantPrisma.tenantModuleActivation.findMany({
|
||||||
where: { tenantId, isActive: true },
|
where: { tenantId, isActive: true },
|
||||||
@@ -275,9 +274,9 @@ export class ModuleGrantsService {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const directModuleIds = new Set(directGrants.map((g: any) => g.moduleId as string));
|
const directModuleIds = new Set(directGrants.map((g) => g.moduleId));
|
||||||
const groupNamesByModule = new Map<string, string[]>();
|
const groupNamesByModule = new Map<string, string[]>();
|
||||||
for (const g of groupGrants as any[]) {
|
for (const g of groupGrants) {
|
||||||
if (!g.group) continue;
|
if (!g.group) continue;
|
||||||
const names = groupNamesByModule.get(g.moduleId) ?? [];
|
const names = groupNamesByModule.get(g.moduleId) ?? [];
|
||||||
names.push(g.group.internalName ?? g.group.name);
|
names.push(g.group.internalName ?? g.group.name);
|
||||||
@@ -285,13 +284,12 @@ export class ModuleGrantsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const modules = activations
|
const modules = activations
|
||||||
.map((a: any) => a.module)
|
.map((a) => a.module)
|
||||||
.sort(
|
.sort(
|
||||||
(a: any, b: any) =>
|
(a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name),
|
||||||
a.category.localeCompare(b.category) || a.name.localeCompare(b.name),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const groups = (memberships as any[])
|
const groups = memberships
|
||||||
.filter((m) => m.group)
|
.filter((m) => m.group)
|
||||||
.map((m) => ({
|
.map((m) => ({
|
||||||
id: m.group.id as string,
|
id: m.group.id as string,
|
||||||
@@ -302,7 +300,7 @@ export class ModuleGrantsService {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
groups,
|
groups,
|
||||||
modules: modules.map((module: any) => ({
|
modules: modules.map((module) => ({
|
||||||
module,
|
module,
|
||||||
viaGroups: groupNamesByModule.get(module.id) ?? [],
|
viaGroups: groupNamesByModule.get(module.id) ?? [],
|
||||||
direct: directModuleIds.has(module.id),
|
direct: directModuleIds.has(module.id),
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
*/
|
*/
|
||||||
async onApplicationBootstrap(): Promise<void> {
|
async onApplicationBootstrap(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const systemPrisma = forSystem(this.prisma) as any;
|
const systemPrisma = forSystem(this.prisma);
|
||||||
const configs: { id: string; tenantId: string; encryptedBindPassword: string | null }[] =
|
const configs: { id: string; tenantId: string; encryptedBindPassword: string | null }[] =
|
||||||
await systemPrisma.ldapConfig.findMany({
|
await systemPrisma.ldapConfig.findMany({
|
||||||
select: { id: true, tenantId: true, encryptedBindPassword: true },
|
select: { id: true, tenantId: true, encryptedBindPassword: true },
|
||||||
@@ -84,7 +84,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
for (const config of legacy) {
|
for (const config of legacy) {
|
||||||
// Schreiben je Altzeile GEBUNDEN an den Mandanten der Zeile — unter
|
// Schreiben je Altzeile GEBUNDEN an den Mandanten der Zeile — unter
|
||||||
// Systemkontext wuerde die Datenbank das Update abweisen (P2025).
|
// Systemkontext wuerde die Datenbank das Update abweisen (P2025).
|
||||||
const tenantPrisma = forTenant(this.prisma, config.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, config.tenantId);
|
||||||
await tenantPrisma.ldapConfig.update({
|
await tenantPrisma.ldapConfig.update({
|
||||||
where: { id: config.id },
|
where: { id: config.id },
|
||||||
data: {
|
data: {
|
||||||
@@ -146,7 +146,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
* bewusst ueber alle Mandanten liest.
|
* bewusst ueber alle Mandanten liest.
|
||||||
*/
|
*/
|
||||||
async getConfig(tenantId: string) {
|
async getConfig(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const config = await tenantPrisma.ldapConfig.findUnique({
|
const config = await tenantPrisma.ldapConfig.findUnique({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
include: { fieldMappings: true },
|
include: { fieldMappings: true },
|
||||||
@@ -166,7 +166,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
* gemessen.
|
* gemessen.
|
||||||
*/
|
*/
|
||||||
async createConfig(tenantId: string, dto: CreateLdapConfigDto) {
|
async createConfig(tenantId: string, dto: CreateLdapConfigDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const created = await tenantPrisma.ldapConfig.create({
|
const created = await tenantPrisma.ldapConfig.create({
|
||||||
data: {
|
data: {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -209,7 +209,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
* Mandantengebunden (WINDOWS #20 Etappe 2, 260909-ipc).
|
* Mandantengebunden (WINDOWS #20 Etappe 2, 260909-ipc).
|
||||||
*/
|
*/
|
||||||
async updateConfig(tenantId: string, dto: UpdateLdapConfigDto) {
|
async updateConfig(tenantId: string, dto: UpdateLdapConfigDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const updated = await tenantPrisma.ldapConfig.update({
|
const updated = await tenantPrisma.ldapConfig.update({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
data: {
|
data: {
|
||||||
@@ -257,7 +257,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
configId: string,
|
configId: string,
|
||||||
dto: CreateFieldMappingDto,
|
dto: CreateFieldMappingDto,
|
||||||
) {
|
) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.ldapFieldMapping.create({
|
return tenantPrisma.ldapFieldMapping.create({
|
||||||
data: {
|
data: {
|
||||||
ldapConfigId: configId,
|
ldapConfigId: configId,
|
||||||
@@ -283,7 +283,7 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
* Loeschung.
|
* Loeschung.
|
||||||
*/
|
*/
|
||||||
async removeFieldMapping(tenantId: string, mappingId: string) {
|
async removeFieldMapping(tenantId: string, mappingId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const mapping = await tenantPrisma.ldapFieldMapping.findUnique({
|
const mapping = await tenantPrisma.ldapFieldMapping.findUnique({
|
||||||
where: { id: mappingId },
|
where: { id: mappingId },
|
||||||
});
|
});
|
||||||
@@ -319,11 +319,11 @@ export class LdapConfigService implements OnApplicationBootstrap {
|
|||||||
* den es dann nicht gibt.
|
* den es dann nicht gibt.
|
||||||
*/
|
*/
|
||||||
async getAllActiveConfigs() {
|
async getAllActiveConfigs() {
|
||||||
const systemPrisma = forSystem(this.prisma) as any;
|
const systemPrisma = forSystem(this.prisma);
|
||||||
const configs = await systemPrisma.ldapConfig.findMany({
|
const configs = await systemPrisma.ldapConfig.findMany({
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
include: { tenant: true, fieldMappings: true },
|
include: { tenant: true, fieldMappings: true },
|
||||||
});
|
});
|
||||||
return configs.map((config: any) => this.withDecryptedPassword(config));
|
return configs.map((config) => this.withDecryptedPassword(config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ export class LdapService {
|
|||||||
// Mandantengescopter Lesepfad (WINDOWS #20 Etappe 2, 260909-ipc): die
|
// Mandantengescopter Lesepfad (WINDOWS #20 Etappe 2, 260909-ipc): die
|
||||||
// "bereits importiert"-Markierung darf nur die Gruppen DIESES Mandanten
|
// "bereits importiert"-Markierung darf nur die Gruppen DIESES Mandanten
|
||||||
// sehen.
|
// sehen.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.bind(client, config.bindDn, config.bindPassword);
|
await this.bind(client, config.bindDn, config.bindPassword);
|
||||||
@@ -471,7 +471,7 @@ export class LdapService {
|
|||||||
// Mandantengescopter Identitaets-/Schreibpfad (WINDOWS #20 Etappe 2,
|
// Mandantengescopter Identitaets-/Schreibpfad (WINDOWS #20 Etappe 2,
|
||||||
// 260909-ipc). Nicht zu verwechseln mit resolveEmailForWrite() oben, die
|
// 260909-ipc). Nicht zu verwechseln mit resolveEmailForWrite() oben, die
|
||||||
// bewusst ungebunden bleibt.
|
// bewusst ungebunden bleibt.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const existingByDn = await tenantPrisma.user.findFirst({
|
const existingByDn = await tenantPrisma.user.findFirst({
|
||||||
where: { ldapDn: dn, tenantId },
|
where: { ldapDn: dn, tenantId },
|
||||||
});
|
});
|
||||||
@@ -566,7 +566,7 @@ export class LdapService {
|
|||||||
// Mandantengescopter Lesepfad (WINDOWS #20 Etappe 2, 260909-ipc): die
|
// Mandantengescopter Lesepfad (WINDOWS #20 Etappe 2, 260909-ipc): die
|
||||||
// "bereits importiert"-Markierung darf nur die Konten DIESES Mandanten
|
// "bereits importiert"-Markierung darf nur die Konten DIESES Mandanten
|
||||||
// sehen.
|
// sehen.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const first = (v: unknown): string =>
|
const first = (v: unknown): string =>
|
||||||
Array.isArray(v) ? String(v[0] ?? '') : v != null ? String(v) : '';
|
Array.isArray(v) ? String(v[0] ?? '') : v != null ? String(v) : '';
|
||||||
|
|
||||||
@@ -663,7 +663,7 @@ export class LdapService {
|
|||||||
);
|
);
|
||||||
// Mandantengescopter Dedup-/Schreibpfad (WINDOWS #20 Etappe 2,
|
// Mandantengescopter Dedup-/Schreibpfad (WINDOWS #20 Etappe 2,
|
||||||
// 260909-ipc).
|
// 260909-ipc).
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.bind(client, config.bindDn, config.bindPassword);
|
await this.bind(client, config.bindDn, config.bindPassword);
|
||||||
@@ -791,7 +791,7 @@ export class LdapService {
|
|||||||
);
|
);
|
||||||
// Mandantengescopter Schreibpfad (T-16-02): app.current_tenant wird vor
|
// Mandantengescopter Schreibpfad (T-16-02): app.current_tenant wird vor
|
||||||
// jedem group.create() gesetzt, RLS ist das zweite Netz.
|
// jedem group.create() gesetzt, RLS ist das zweite Netz.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.bind(client, config.bindDn, config.bindPassword);
|
await this.bind(client, config.bindDn, config.bindPassword);
|
||||||
@@ -934,7 +934,7 @@ export class LdapService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create tenant-scoped Prisma client per Pitfall 2
|
// Create tenant-scoped Prisma client per Pitfall 2
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Bind with service account (anonymous when not configured)
|
// 1. Bind with service account (anonymous when not configured)
|
||||||
@@ -1208,7 +1208,7 @@ export class LdapService {
|
|||||||
tenantId: string,
|
tenantId: string,
|
||||||
result: LdapSyncResult,
|
result: LdapSyncResult,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
const boundGroups: { id: string; name: string; ldapDn: string | null }[] =
|
const boundGroups: { id: string; name: string; ldapDn: string | null }[] =
|
||||||
await tenantPrisma.group.findMany({
|
await tenantPrisma.group.findMany({
|
||||||
@@ -1371,7 +1371,7 @@ export class LdapService {
|
|||||||
tenantId: string,
|
tenantId: string,
|
||||||
result: LdapSyncResult,
|
result: LdapSyncResult,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
const candidates: {
|
const candidates: {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export class ModuleAccessService {
|
|||||||
// bestehen: der Schalter ist weiterhin aus (#18), die Datenbankregel
|
// bestehen: der Schalter ist weiterhin aus (#18), die Datenbankregel
|
||||||
// wirkt heute nicht, und die Anwendungspruefung ist bis zum
|
// wirkt heute nicht, und die Anwendungspruefung ist bis zum
|
||||||
// Scharfschalten der einzige tatsaechliche Schutz.
|
// Scharfschalten der einzige tatsaechliche Schutz.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
if (role === 'ADMIN' || role === 'SUPER_ADMIN') {
|
if (role === 'ADMIN' || role === 'SUPER_ADMIN') {
|
||||||
const activations = await tenantPrisma.tenantModuleActivation.findMany({
|
const activations = await tenantPrisma.tenantModuleActivation.findMany({
|
||||||
@@ -142,7 +142,7 @@ export class ModuleAccessService {
|
|||||||
// erzeugt ihren EIGENEN Klienten (dieselbe Konvention wie
|
// erzeugt ihren EIGENEN Klienten (dieselbe Konvention wie
|
||||||
// `module-grants.service.ts`: gebundene Klienten werden nicht zwischen
|
// `module-grants.service.ts`: gebundene Klienten werden nicht zwischen
|
||||||
// Methoden weitergereicht). Beide laufen wie bisher nebenlaeufig.
|
// Methoden weitergereicht). Beide laufen wie bisher nebenlaeufig.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const [activations, accessibleIds] = await Promise.all([
|
const [activations, accessibleIds] = await Promise.all([
|
||||||
tenantPrisma.tenantModuleActivation.findMany({
|
tenantPrisma.tenantModuleActivation.findMany({
|
||||||
where: { tenantId, isActive: true },
|
where: { tenantId, isActive: true },
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export class ModuleRegistryService {
|
|||||||
* Returns all active modules for a given tenant.
|
* Returns all active modules for a given tenant.
|
||||||
*/
|
*/
|
||||||
async findActiveForTenant(tenantId: string) {
|
async findActiveForTenant(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const activations = await tenantPrisma.tenantModuleActivation.findMany({
|
const activations = await tenantPrisma.tenantModuleActivation.findMany({
|
||||||
where: {
|
where: {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -77,7 +77,7 @@ export class ModuleRegistryService {
|
|||||||
throw new NotFoundException(`Module with id '${moduleId}' not found`);
|
throw new NotFoundException(`Module with id '${moduleId}' not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.tenantModuleActivation.upsert({
|
return tenantPrisma.tenantModuleActivation.upsert({
|
||||||
where: {
|
where: {
|
||||||
tenantId_moduleId: {
|
tenantId_moduleId: {
|
||||||
@@ -117,7 +117,7 @@ export class ModuleRegistryService {
|
|||||||
// EIN gebundener Klient fuer beide Aktivierungszugriffe dieser Methode
|
// EIN gebundener Klient fuer beide Aktivierungszugriffe dieser Methode
|
||||||
// (Lesen, Schreiben) — nicht ein Klient je Zugriff (260910-exd,
|
// (Lesen, Schreiben) — nicht ein Klient je Zugriff (260910-exd,
|
||||||
// Aufgabe 3, dieselbe Konvention wie `module-access.service.ts`).
|
// Aufgabe 3, dieselbe Konvention wie `module-access.service.ts`).
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
// Check if activation record exists
|
// Check if activation record exists
|
||||||
const activation = await tenantPrisma.tenantModuleActivation.findUnique({
|
const activation = await tenantPrisma.tenantModuleActivation.findUnique({
|
||||||
@@ -172,7 +172,7 @@ export class ModuleRegistryService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const activation = await tenantPrisma.tenantModuleActivation.findUnique({
|
const activation = await tenantPrisma.tenantModuleActivation.findUnique({
|
||||||
where: {
|
where: {
|
||||||
tenantId_moduleId: {
|
tenantId_moduleId: {
|
||||||
|
|||||||
@@ -197,13 +197,11 @@ import { PrismaClient } from '@prisma/client';
|
|||||||
export function forTenant(prisma: PrismaClient, tenantId: string, userId?: string) {
|
export function forTenant(prisma: PrismaClient, tenantId: string, userId?: string) {
|
||||||
return prisma.$extends({
|
return prisma.$extends({
|
||||||
query: {
|
query: {
|
||||||
$allOperations({ args, query }: { args: any; query: (args: any) => any }) {
|
$allOperations({ args, query }) {
|
||||||
const setContext = (prisma as any)
|
const setContext = prisma.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true), set_config('app.current_user', ${userId ?? ''}, true), set_config('app.system_context', '', true)`;
|
||||||
.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true), set_config('app.current_user', ${userId ?? ''}, true), set_config('app.system_context', '', true)`;
|
|
||||||
|
|
||||||
return (prisma as any)
|
return prisma.$transaction([setContext, query(args)])
|
||||||
.$transaction([setContext, query(args)])
|
.then((results: unknown[]) => results[1]);
|
||||||
.then((results: any[]) => results[1]);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -226,13 +224,11 @@ export function forTenant(prisma: PrismaClient, tenantId: string, userId?: strin
|
|||||||
export function forSystem(prisma: PrismaClient) {
|
export function forSystem(prisma: PrismaClient) {
|
||||||
return prisma.$extends({
|
return prisma.$extends({
|
||||||
query: {
|
query: {
|
||||||
$allOperations({ args, query }: { args: any; query: (args: any) => any }) {
|
$allOperations({ args, query }) {
|
||||||
const setContext = (prisma as any)
|
const setContext = prisma.$executeRaw`SELECT set_config('app.system_context', 'true', true), set_config('app.current_tenant', '', true), set_config('app.current_user', '', true)`;
|
||||||
.$executeRaw`SELECT set_config('app.system_context', 'true', true), set_config('app.current_tenant', '', true), set_config('app.current_user', '', true)`;
|
|
||||||
|
|
||||||
return (prisma as any)
|
return prisma.$transaction([setContext, query(args)])
|
||||||
.$transaction([setContext, query(args)])
|
.then((results: unknown[]) => results[1]);
|
||||||
.then((results: any[]) => results[1]);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -259,9 +255,15 @@ export function forSystem(prisma: PrismaClient) {
|
|||||||
export function withTenantTransaction<T>(
|
export function withTenantTransaction<T>(
|
||||||
prisma: PrismaClient,
|
prisma: PrismaClient,
|
||||||
tenantId: string,
|
tenantId: string,
|
||||||
|
// tx bleibt `any` (gemessen, Aufgabe 1 260921-m34): `Prisma.TransactionClient`
|
||||||
|
// erzwingt an den vier Aufrufstellen (groups.service.ts, favorites.service.ts)
|
||||||
|
// vollstaendige Prisma-Erzeugungstypen und bricht deren Testdoppel in
|
||||||
|
// prisma-tenant.extension.spec.ts (TS2322 auf einem absichtlich unvollstaendigen
|
||||||
|
// Fake-Objekt). Das waere eine Verhaltensaenderung an einer Teststruktur,
|
||||||
|
// nicht ehrliches Typisieren (D-02/D-03) - bleibt.
|
||||||
fn: (tx: any) => Promise<T>,
|
fn: (tx: any) => Promise<T>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
return (prisma as any).$transaction(async (tx: any) => {
|
return prisma.$transaction(async (tx: any) => {
|
||||||
await tx.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true), set_config('app.system_context', '', true)`;
|
await tx.$executeRaw`SELECT set_config('app.current_tenant', ${tenantId}, true), set_config('app.system_context', '', true)`;
|
||||||
return fn(tx);
|
return fn(tx);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export class SettingsService {
|
|||||||
* Methode, wie die restlichen Anfragewege dieser Datei.
|
* Methode, wie die restlichen Anfragewege dieser Datei.
|
||||||
*/
|
*/
|
||||||
async getSmtpConfig(tenantId: string) {
|
async getSmtpConfig(tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.smtpConfig.findUnique({
|
return tenantPrisma.smtpConfig.findUnique({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
select: {
|
select: {
|
||||||
@@ -61,7 +61,7 @@ export class SettingsService {
|
|||||||
* Mandantengebunden (260911-gwh): EIN Klient `tenantPrisma`.
|
* Mandantengebunden (260911-gwh): EIN Klient `tenantPrisma`.
|
||||||
*/
|
*/
|
||||||
async saveSmtpConfig(tenantId: string, dto: SmtpConfigDto) {
|
async saveSmtpConfig(tenantId: string, dto: SmtpConfigDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
// Determine the encrypted password to store
|
// Determine the encrypted password to store
|
||||||
let encryptedPassword: string | undefined;
|
let encryptedPassword: string | undefined;
|
||||||
@@ -104,7 +104,7 @@ export class SettingsService {
|
|||||||
* schmalem `select` — das verschluesselte Kennwort wird hier nie geladen.
|
* schmalem `select` — das verschluesselte Kennwort wird hier nie geladen.
|
||||||
*/
|
*/
|
||||||
async getBugReportRecipient(tenantId: string): Promise<string | null> {
|
async getBugReportRecipient(tenantId: string): Promise<string | null> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const row = await tenantPrisma.smtpConfig.findUnique({
|
const row = await tenantPrisma.smtpConfig.findUnique({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
select: { bugReportRecipient: true },
|
select: { bugReportRecipient: true },
|
||||||
@@ -134,7 +134,7 @@ export class SettingsService {
|
|||||||
fromAddress: string;
|
fromAddress: string;
|
||||||
decryptedPassword: string | null;
|
decryptedPassword: string | null;
|
||||||
} | null> {
|
} | null> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
const config = await tenantPrisma.smtpConfig.findUnique({
|
const config = await tenantPrisma.smtpConfig.findUnique({
|
||||||
where: { tenantId },
|
where: { tenantId },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export class TenantController {
|
|||||||
|
|
||||||
const results: any[] = [];
|
const results: any[] = [];
|
||||||
for (const tenant of tenants) {
|
for (const tenant of tenants) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenant.id) as any;
|
const tenantPrisma = forTenant(this.prisma, tenant.id);
|
||||||
const userCount = await tenantPrisma.user.count({
|
const userCount = await tenantPrisma.user.count({
|
||||||
where: { tenantId: tenant.id },
|
where: { tenantId: tenant.id },
|
||||||
});
|
});
|
||||||
@@ -99,7 +99,7 @@ export class TenantController {
|
|||||||
throw new NotFoundException('Tenant not found');
|
throw new NotFoundException('Tenant not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, id) as any;
|
const tenantPrisma = forTenant(this.prisma, id);
|
||||||
const userCount = await tenantPrisma.user.count({
|
const userCount = await tenantPrisma.user.count({
|
||||||
where: { tenantId: id },
|
where: { tenantId: id },
|
||||||
});
|
});
|
||||||
@@ -160,7 +160,7 @@ export class TenantController {
|
|||||||
throw new NotFoundException('Tenant not found');
|
throw new NotFoundException('Tenant not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, id) as any;
|
const tenantPrisma = forTenant(this.prisma, id);
|
||||||
const activeUserCount = await tenantPrisma.user.count({
|
const activeUserCount = await tenantPrisma.user.count({
|
||||||
where: { tenantId: id, isActive: true },
|
where: { tenantId: id, isActive: true },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export class TenderDigestScheduler implements OnModuleInit {
|
|||||||
// `distinct(['userId'])` liefert dann nur EINE der moeglichen
|
// `distinct(['userId'])` liefert dann nur EINE der moeglichen
|
||||||
// tenantId-Werte je Nutzer, welche ist von der internen Zeilenreihenfolge
|
// tenantId-Werte je Nutzer, welche ist von der internen Zeilenreihenfolge
|
||||||
// abhaengig. Siehe docs/mandantentrennung-etappe2-fehlerrichtung.md.
|
// abhaengig. Siehe docs/mandantentrennung-etappe2-fehlerrichtung.md.
|
||||||
const systemPrisma = forSystem(this.prisma) as any;
|
const systemPrisma = forSystem(this.prisma);
|
||||||
const candidates: { userId: string; tenantId: string }[] = await systemPrisma.tenderMatch.findMany({
|
const candidates: { userId: string; tenantId: string }[] = await systemPrisma.tenderMatch.findMany({
|
||||||
where: { notifiedAt: null },
|
where: { notifiedAt: null },
|
||||||
select: { userId: true, tenantId: true },
|
select: { userId: true, tenantId: true },
|
||||||
@@ -145,7 +145,7 @@ export class TenderDigestScheduler implements OnModuleInit {
|
|||||||
// `userId` sieht dieser Zugriff den ganzen Mandanten, exakt wie vor
|
// `userId` sieht dieser Zugriff den ganzen Mandanten, exakt wie vor
|
||||||
// der Migration. Ein Systemkontext fuer Hintergrunddienste ist
|
// der Migration. Ein Systemkontext fuer Hintergrunddienste ist
|
||||||
// Etappe 3c, nicht Teil dieser Aenderung.
|
// Etappe 3c, nicht Teil dieser Aenderung.
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
|
|
||||||
const pref = await tenantPrisma.tenderNotificationPref.findUnique({
|
const pref = await tenantPrisma.tenderNotificationPref.findUnique({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export class TenderEmailConfigService {
|
|||||||
* by userId (T-17-01) — a user only ever reads their own mailbox.
|
* by userId (T-17-01) — a user only ever reads their own mailbox.
|
||||||
*/
|
*/
|
||||||
async getConfigForApi(userId: string, tenantId: string) {
|
async getConfigForApi(userId: string, tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const safe = await tenantPrisma.tenderEmailConfig.findUnique({
|
const safe = await tenantPrisma.tenderEmailConfig.findUnique({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
select: EMAIL_CONFIG_SAFE_SELECT,
|
select: EMAIL_CONFIG_SAFE_SELECT,
|
||||||
@@ -152,7 +152,7 @@ export class TenderEmailConfigService {
|
|||||||
*/
|
*/
|
||||||
async saveConfig(ctx: { userId: string; tenantId: string }, dto: TenderEmailConfigDto) {
|
async saveConfig(ctx: { userId: string; tenantId: string }, dto: TenderEmailConfigDto) {
|
||||||
const { userId, tenantId } = ctx;
|
const { userId, tenantId } = ctx;
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
let encryptedInboxCreds: string | undefined;
|
let encryptedInboxCreds: string | undefined;
|
||||||
|
|
||||||
const credChanged =
|
const credChanged =
|
||||||
@@ -240,7 +240,7 @@ export class TenderEmailConfigService {
|
|||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
try {
|
try {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const existing = await tenantPrisma.tenderEmailConfig.findUnique({ where: { userId } });
|
const existing = await tenantPrisma.tenderEmailConfig.findUnique({ where: { userId } });
|
||||||
if (existing?.encryptedInboxCreds) {
|
if (existing?.encryptedInboxCreds) {
|
||||||
const stored = JSON.parse(this.crypto.decrypt(existing.encryptedInboxCreds)) as {
|
const stored = JSON.parse(this.crypto.decrypt(existing.encryptedInboxCreds)) as {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export class TenderMatchingService {
|
|||||||
// und wuerde stumm. Treffer-Anlage und Sofortmeldung bleiben je Profil
|
// und wuerde stumm. Treffer-Anlage und Sofortmeldung bleiben je Profil
|
||||||
// GEBUNDEN (unten); der Katalog-Lesezugriff (`tender`, D-03) bleibt
|
// GEBUNDEN (unten); der Katalog-Lesezugriff (`tender`, D-03) bleibt
|
||||||
// ungebunden. Eine LEERE Profilliste ist Nichtstun (keine Treffer).
|
// ungebunden. Eine LEERE Profilliste ist Nichtstun (keine Treffer).
|
||||||
const systemPrisma = forSystem(this.prisma) as any;
|
const systemPrisma = forSystem(this.prisma);
|
||||||
const savedSearches: Prisma.TenderSavedSearchGetPayload<Record<string, never>>[] =
|
const savedSearches: Prisma.TenderSavedSearchGetPayload<Record<string, never>>[] =
|
||||||
await systemPrisma.tenderSavedSearch.findMany();
|
await systemPrisma.tenderSavedSearch.findMany();
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ export class TenderMatchingService {
|
|||||||
// Gebunden an den Mandanten DIESES Profils (260909-laa, Aufgabe 3)
|
// Gebunden an den Mandanten DIESES Profils (260909-laa, Aufgabe 3)
|
||||||
// — EIN gebundener Client je Profil, nicht je Treffer, sonst
|
// — EIN gebundener Client je Profil, nicht je Treffer, sonst
|
||||||
// entstuende pro Zeile eine eigene Transaktion.
|
// entstuende pro Zeile eine eigene Transaktion.
|
||||||
const tenantPrisma = forTenant(this.prisma, search.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, search.tenantId);
|
||||||
|
|
||||||
for (const hit of hits) {
|
for (const hit of hits) {
|
||||||
await tenantPrisma.tenderMatch.upsert({
|
await tenantPrisma.tenderMatch.upsert({
|
||||||
@@ -134,7 +134,7 @@ export class TenderMatchingService {
|
|||||||
try {
|
try {
|
||||||
// Gebunden an den Mandanten DIESES Profils (260909-laa, Aufgabe 3)
|
// Gebunden an den Mandanten DIESES Profils (260909-laa, Aufgabe 3)
|
||||||
// — EIN gebundener Client je Profil.
|
// — EIN gebundener Client je Profil.
|
||||||
const tenantPrisma = forTenant(this.prisma, profile.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, profile.tenantId);
|
||||||
|
|
||||||
const fresh = await tenantPrisma.tenderMatch.findMany({
|
const fresh = await tenantPrisma.tenderMatch.findMany({
|
||||||
where: {
|
where: {
|
||||||
@@ -156,7 +156,7 @@ export class TenderMatchingService {
|
|||||||
{ email: user.email },
|
{ email: user.email },
|
||||||
profile.tenantId,
|
profile.tenantId,
|
||||||
{ name: profile.name },
|
{ name: profile.name },
|
||||||
fresh.map((match: { tender: unknown }) => match.tender),
|
fresh.map((match) => match.tender),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sent) {
|
if (sent) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export class TenderNotificationPrefService {
|
|||||||
* autowrite needed to represent "using the default".
|
* autowrite needed to represent "using the default".
|
||||||
*/
|
*/
|
||||||
async getForUser(userId: string, tenantId: string): Promise<{ digestInterval: string }> {
|
async getForUser(userId: string, tenantId: string): Promise<{ digestInterval: string }> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const existing = await tenantPrisma.tenderNotificationPref.findUnique({
|
const existing = await tenantPrisma.tenderNotificationPref.findUnique({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
});
|
});
|
||||||
@@ -63,7 +63,7 @@ export class TenderNotificationPrefService {
|
|||||||
* than creating a new one.
|
* than creating a new one.
|
||||||
*/
|
*/
|
||||||
async setForUser(userId: string, tenantId: string, digestInterval: string) {
|
async setForUser(userId: string, tenantId: string, digestInterval: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.tenderNotificationPref.upsert({
|
return await tenantPrisma.tenderNotificationPref.upsert({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class TenderRssFeedSourceService {
|
|||||||
* Bindung nicht überflüssig, sondern das zweite Netz.
|
* Bindung nicht überflüssig, sondern das zweite Netz.
|
||||||
*/
|
*/
|
||||||
async listForUser(userId: string, tenantId: string) {
|
async listForUser(userId: string, tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
return tenantPrisma.tenderRssFeedSource.findMany({
|
return tenantPrisma.tenderRssFeedSource.findMany({
|
||||||
where: { OR: [{ userId: null }, { userId }] },
|
where: { OR: [{ userId: null }, { userId }] },
|
||||||
orderBy: { createdAt: 'asc' },
|
orderBy: { createdAt: 'asc' },
|
||||||
@@ -93,7 +93,7 @@ export class TenderRssFeedSourceService {
|
|||||||
) {
|
) {
|
||||||
this.assertUrlAllowed(dto.url);
|
this.assertUrlAllowed(dto.url);
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, ctx.tenantId, ctx.userId) as any;
|
const tenantPrisma = forTenant(this.prisma, ctx.tenantId, ctx.userId);
|
||||||
const existingCount = await tenantPrisma.tenderRssFeedSource.count({
|
const existingCount = await tenantPrisma.tenderRssFeedSource.count({
|
||||||
where: { userId: ctx.userId },
|
where: { userId: ctx.userId },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export class TenderSavedSearchService {
|
|||||||
* strictly by userId (V4/IDOR) — a foreign userId sees nothing.
|
* strictly by userId (V4/IDOR) — a foreign userId sees nothing.
|
||||||
*/
|
*/
|
||||||
async list(userId: string, tenantId: string) {
|
async list(userId: string, tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
return tenantPrisma.tenderSavedSearch.findMany({
|
return tenantPrisma.tenderSavedSearch.findMany({
|
||||||
where: { userId },
|
where: { userId },
|
||||||
orderBy: { name: 'asc' },
|
orderBy: { name: 'asc' },
|
||||||
@@ -58,7 +58,7 @@ export class TenderSavedSearchService {
|
|||||||
* users, since the uniqueness is scoped per-user.
|
* users, since the uniqueness is scoped per-user.
|
||||||
*/
|
*/
|
||||||
async create(userId: string, tenantId: string, dto: CreateSavedSearchDto) {
|
async create(userId: string, tenantId: string, dto: CreateSavedSearchDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.tenderSavedSearch.create({
|
return await tenantPrisma.tenderSavedSearch.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -87,7 +87,7 @@ export class TenderSavedSearchService {
|
|||||||
* leaking whether another user's profile exists).
|
* leaking whether another user's profile exists).
|
||||||
*/
|
*/
|
||||||
async update(id: string, userId: string, tenantId: string, dto: UpdateSavedSearchDto) {
|
async update(id: string, userId: string, tenantId: string, dto: UpdateSavedSearchDto) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const existing = await tenantPrisma.tenderSavedSearch.findUnique({
|
const existing = await tenantPrisma.tenderSavedSearch.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
});
|
});
|
||||||
@@ -124,7 +124,7 @@ export class TenderSavedSearchService {
|
|||||||
* update().
|
* update().
|
||||||
*/
|
*/
|
||||||
async remove(id: string, userId: string, tenantId: string) {
|
async remove(id: string, userId: string, tenantId: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const existing = await tenantPrisma.tenderSavedSearch.findUnique({
|
const existing = await tenantPrisma.tenderSavedSearch.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export class TenderTriageService {
|
|||||||
update.favoritedAt = dto.isFavorite ? now : null;
|
update.favoritedAt = dto.isFavorite ? now : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.tenderTriage.upsert({
|
return await tenantPrisma.tenderTriage.upsert({
|
||||||
where: { userId_tenderId: { userId, tenderId } },
|
where: { userId_tenderId: { userId, tenderId } },
|
||||||
@@ -111,7 +111,7 @@ export class TenderTriageService {
|
|||||||
*/
|
*/
|
||||||
async listForUser(userId: string, tenantId: string, tenderIds: string[]) {
|
async listForUser(userId: string, tenantId: string, tenderIds: string[]) {
|
||||||
if (!tenderIds.length) return [];
|
if (!tenderIds.length) return [];
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
return tenantPrisma.tenderTriage.findMany({
|
return tenantPrisma.tenderTriage.findMany({
|
||||||
where: { userId, tenderId: { in: tenderIds } },
|
where: { userId, tenderId: { in: tenderIds } },
|
||||||
});
|
});
|
||||||
@@ -123,7 +123,7 @@ export class TenderTriageService {
|
|||||||
* tender-query.builder.ts's buildTenderWhere.
|
* tender-query.builder.ts's buildTenderWhere.
|
||||||
*/
|
*/
|
||||||
async favoriteIds(userId: string, tenantId: string): Promise<string[]> {
|
async favoriteIds(userId: string, tenantId: string): Promise<string[]> {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||||
const rows = await tenantPrisma.tenderTriage.findMany({
|
const rows = await tenantPrisma.tenderTriage.findMany({
|
||||||
where: { userId, isFavorite: true },
|
where: { userId, isFavorite: true },
|
||||||
select: { tenderId: true },
|
select: { tenderId: true },
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ export class TendersController {
|
|||||||
const { userId, tenantId } = this.extractTriageContext(req);
|
const { userId, tenantId } = this.extractTriageContext(req);
|
||||||
const feeds = await this.tenderRssFeedSource.listForUser(userId, tenantId);
|
const feeds = await this.tenderRssFeedSource.listForUser(userId, tenantId);
|
||||||
|
|
||||||
return feeds.map(({ userId: ownerUserId, ...rest }: any) => ({
|
return feeds.map(({ userId: ownerUserId, ...rest }) => ({
|
||||||
...rest,
|
...rest,
|
||||||
isPlatformWide: ownerUserId === null,
|
isPlatformWide: ownerUserId === null,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export class AdminSeedService implements OnApplicationBootstrap {
|
|||||||
// `user-ungebundenes-einfuegen-abgelehnt`): eine FRISCHE Installation
|
// `user-ungebundenes-einfuegen-abgelehnt`): eine FRISCHE Installation
|
||||||
// haette ihren allerersten Administrator gar nicht anlegen koennen.
|
// haette ihren allerersten Administrator gar nicht anlegen koennen.
|
||||||
const passwordHash = await argon2.hash(password);
|
const passwordHash = await argon2.hash(password);
|
||||||
const tenantPrisma = forTenant(this.prisma, tenant.id) as any;
|
const tenantPrisma = forTenant(this.prisma, tenant.id);
|
||||||
try {
|
try {
|
||||||
await tenantPrisma.user.create({
|
await tenantPrisma.user.create({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export class UserController {
|
|||||||
// Mandantenbedingung im where BLEIBT erhalten -- nicht entfernen mit
|
// Mandantenbedingung im where BLEIBT erhalten -- nicht entfernen mit
|
||||||
// dem Argument, das mache jetzt die Datenbank; dieselbe Regel, die die
|
// dem Argument, das mache jetzt die Datenbank; dieselbe Regel, die die
|
||||||
// Bereiche `tenders` und `dkv` aufgestellt haben.
|
// Bereiche `tenders` und `dkv` aufgestellt haben.
|
||||||
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId);
|
||||||
return tenantPrisma.user.findMany({
|
return tenantPrisma.user.findMany({
|
||||||
where: { tenantId: currentUser.tenantId },
|
where: { tenantId: currentUser.tenantId },
|
||||||
select: {
|
select: {
|
||||||
@@ -322,7 +322,7 @@ export class UserController {
|
|||||||
|
|
||||||
// Persist relative path (relative to monorepo root)
|
// Persist relative path (relative to monorepo root)
|
||||||
const relativePath = path.join('user-files', 'avatars', filename);
|
const relativePath = path.join('user-files', 'avatars', filename);
|
||||||
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId);
|
||||||
await tenantPrisma.user.update({
|
await tenantPrisma.user.update({
|
||||||
where: { id: currentUser.id },
|
where: { id: currentUser.id },
|
||||||
data: { avatarPath: relativePath },
|
data: { avatarPath: relativePath },
|
||||||
@@ -337,7 +337,7 @@ export class UserController {
|
|||||||
*/
|
*/
|
||||||
@Delete('me/avatar')
|
@Delete('me/avatar')
|
||||||
async deleteAvatar(@CurrentUser() currentUser: any) {
|
async deleteAvatar(@CurrentUser() currentUser: any) {
|
||||||
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId);
|
||||||
const user = await tenantPrisma.user.findUnique({
|
const user = await tenantPrisma.user.findUnique({
|
||||||
where: { id: currentUser.id },
|
where: { id: currentUser.id },
|
||||||
select: { avatarPath: true },
|
select: { avatarPath: true },
|
||||||
@@ -372,7 +372,7 @@ export class UserController {
|
|||||||
throw new BadRequestException('Invalid color format. Use hex (#rrggbb).');
|
throw new BadRequestException('Invalid color format. Use hex (#rrggbb).');
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId);
|
||||||
await tenantPrisma.user.update({
|
await tenantPrisma.user.update({
|
||||||
where: { id: currentUser.id },
|
where: { id: currentUser.id },
|
||||||
data: { accentColor: body.color ?? null },
|
data: { accentColor: body.color ?? null },
|
||||||
@@ -391,7 +391,7 @@ export class UserController {
|
|||||||
@CurrentUser() currentUser: any,
|
@CurrentUser() currentUser: any,
|
||||||
@Res() res: Response,
|
@Res() res: Response,
|
||||||
) {
|
) {
|
||||||
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, currentUser.tenantId);
|
||||||
const user = await tenantPrisma.user.findUnique({
|
const user = await tenantPrisma.user.findUnique({
|
||||||
where: { id: currentUser.id },
|
where: { id: currentUser.id },
|
||||||
select: { avatarPath: true },
|
select: { avatarPath: true },
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export class UserService {
|
|||||||
* (Aufgabe 1, `user-gebunden-nur-eigener-mandant`).
|
* (Aufgabe 1, `user-gebunden-nur-eigener-mandant`).
|
||||||
*/
|
*/
|
||||||
async findById(tenantId: string, id: string) {
|
async findById(tenantId: string, id: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.user.findUnique({ where: { id } });
|
return tenantPrisma.user.findUnique({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ export class UserService {
|
|||||||
ldapDn?: string;
|
ldapDn?: string;
|
||||||
}) {
|
}) {
|
||||||
const { password, ...rest } = data;
|
const { password, ...rest } = data;
|
||||||
const tenantPrisma = forTenant(this.prisma, data.tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, data.tenantId);
|
||||||
|
|
||||||
let created: any;
|
let created: any;
|
||||||
try {
|
try {
|
||||||
@@ -168,7 +168,7 @@ export class UserService {
|
|||||||
updateData.passwordHash = await argon2.hash(password);
|
updateData.passwordHash = await argon2.hash(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
try {
|
try {
|
||||||
return await tenantPrisma.user.update({
|
return await tenantPrisma.user.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
@@ -189,7 +189,7 @@ export class UserService {
|
|||||||
* Mandanten.
|
* Mandanten.
|
||||||
*/
|
*/
|
||||||
async deactivate(tenantId: string, id: string) {
|
async deactivate(tenantId: string, id: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.user.update({
|
return tenantPrisma.user.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { isActive: false },
|
data: { isActive: false },
|
||||||
@@ -200,7 +200,7 @@ export class UserService {
|
|||||||
* Hard delete a user, gebunden an den uebergebenen Mandanten.
|
* Hard delete a user, gebunden an den uebergebenen Mandanten.
|
||||||
*/
|
*/
|
||||||
async delete(tenantId: string, id: string) {
|
async delete(tenantId: string, id: string) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenantId) as any;
|
const tenantPrisma = forTenant(this.prisma, tenantId);
|
||||||
return tenantPrisma.user.delete({ where: { id } });
|
return tenantPrisma.user.delete({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ export class UserService {
|
|||||||
|
|
||||||
const results: any[] = [];
|
const results: any[] = [];
|
||||||
for (const tenant of tenants) {
|
for (const tenant of tenants) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenant.id) as any;
|
const tenantPrisma = forTenant(this.prisma, tenant.id);
|
||||||
const users = await tenantPrisma.user.findMany({
|
const users = await tenantPrisma.user.findMany({
|
||||||
where: { tenantId: tenant.id },
|
where: { tenantId: tenant.id },
|
||||||
select: {
|
select: {
|
||||||
@@ -265,7 +265,7 @@ export class UserService {
|
|||||||
const tenants = await this.prisma.tenant.findMany({ select: { id: true } });
|
const tenants = await this.prisma.tenant.findMany({ select: { id: true } });
|
||||||
|
|
||||||
for (const tenant of tenants) {
|
for (const tenant of tenants) {
|
||||||
const tenantPrisma = forTenant(this.prisma, tenant.id) as any;
|
const tenantPrisma = forTenant(this.prisma, tenant.id);
|
||||||
const user = await tenantPrisma.user.findUnique({ where: { id } });
|
const user = await tenantPrisma.user.findUnique({ where: { id } });
|
||||||
if (user) {
|
if (user) {
|
||||||
return user;
|
return user;
|
||||||
|
|||||||
Reference in New Issue
Block a user