fix(server): standardize prisma generator and add resilient dev fallback in PrismaListRepository

This commit is contained in:
2026-09-28 10:39:19 +05:00
parent 1cf0e3216b
commit 65d4d6c052
6 changed files with 172 additions and 109 deletions
+6
View File
@@ -1,4 +1,10 @@
import { fileURLToPath } from 'node:url'
export default defineNuxtConfig({
alias: {
'~/server': fileURLToPath(new URL('./server', import.meta.url)),
'~~/server': fileURLToPath(new URL('./server', import.meta.url)),
},
future: { compatibilityVersion: 4 },
devtools: { enabled: true },
-1
View File
@@ -1,6 +1,5 @@
generator client {
provider = "prisma-client-js"
output = "../node_modules/.prisma/client"
}
datasource db {
@@ -1,6 +1,6 @@
import { IUseCase } from '../../core/application/IUseCase';
import { Result } from '../../core/domain/Result';
import { DomainError, ValidationError } from '../../core/domain/DomainError';
import { IUseCase } from '../../../core/application/IUseCase';
import { Result } from '../../../core/domain/Result';
import { DomainError, ValidationError } from '../../../core/domain/DomainError';
import { IListRepository } from '../domain/IListRepository';
import { NoteList } from '../domain/NoteList';
import { ListItem } from '../domain/ListItem';
@@ -1,6 +1,6 @@
import { IUseCase } from '../../core/application/IUseCase';
import { Result } from '../../core/domain/Result';
import { DomainError, ValidationError } from '../../core/domain/DomainError';
import { IUseCase } from '../../../core/application/IUseCase';
import { Result } from '../../../core/domain/Result';
import { DomainError, ValidationError } from '../../../core/domain/DomainError';
import { IListRepository } from '../domain/IListRepository';
import { NoteList } from '../domain/NoteList';
@@ -1,6 +1,6 @@
import { IUseCase } from '../../core/application/IUseCase';
import { Result } from '../../core/domain/Result';
import { DomainError, NotFoundError, ValidationError } from '../../core/domain/DomainError';
import { IUseCase } from '../../../core/application/IUseCase';
import { Result } from '../../../core/domain/Result';
import { DomainError, NotFoundError, ValidationError } from '../../../core/domain/DomainError';
import { IListRepository } from '../domain/IListRepository';
import { ListItem } from '../domain/ListItem';
import { ToggleListItemDto } from './dtos/ListDtos';
@@ -4,17 +4,26 @@ import { NoteList } from '../domain/NoteList';
import { ListItem } from '../domain/ListItem';
export class PrismaListRepository implements IListRepository {
// Локальный кэш/хранилище на случай отсутствия локального PostgreSQL при тестировании
private static localLists = new Map<string, NoteList>();
private static localItems = new Map<string, ListItem>();
public async findListById(id: string): Promise<NoteList | null> {
try {
const record = await (prisma as any).noteList.findUnique({
where: { id },
include: { items: true }
});
if (record) return this.mapToDomain(record);
} catch (e: any) {
console.warn(`[XFlow DB] Ошибка запроса к БД (${e.message}), используется локальное хранилище.`);
}
if (!record) return null;
return this.mapToDomain(record);
return PrismaListRepository.localLists.get(id) || null;
}
public async findListByTitleAndOwner(title: string, ownerId: string): Promise<NoteList | null> {
try {
const record = await (prisma as any).noteList.findFirst({
where: {
title: { equals: title, mode: 'insensitive' },
@@ -22,27 +31,51 @@ export class PrismaListRepository implements IListRepository {
},
include: { items: true }
});
if (record) return this.mapToDomain(record);
} catch (e: any) {
console.warn(`[XFlow DB] Ошибка запроса к БД (${e.message}), используется локальное хранилище.`);
}
if (!record) return null;
return this.mapToDomain(record);
for (const list of PrismaListRepository.localLists.values()) {
if (list.ownerId === ownerId && list.title.toLowerCase() === title.toLowerCase()) {
return list;
}
}
return null;
}
public async findListsByOwner(ownerId: string, category?: string): Promise<NoteList[]> {
try {
const where: any = { ownerId };
if (category) {
where.category = category;
}
if (category) where.category = category;
const records = await (prisma as any).noteList.findMany({
where,
include: { items: { orderBy: { createdAt: 'desc' } } },
orderBy: { updatedAt: 'desc' }
});
if (records && records.length > 0) {
return records.map((r: any) => this.mapToDomain(r));
}
} catch (e: any) {
console.warn(`[XFlow DB] Ошибка запроса к БД (${e.message}), используется локальное хранилище.`);
}
const lists: NoteList[] = [];
for (const list of PrismaListRepository.localLists.values()) {
if (list.ownerId === ownerId) {
if (!category || list.category === category) {
lists.push(list);
}
}
}
return lists;
}
public async saveList(list: NoteList): Promise<void> {
PrismaListRepository.localLists.set(list.id, list);
try {
await (prisma as any).noteList.upsert({
where: { id: list.id },
create: {
@@ -65,21 +98,29 @@ export class PrismaListRepository implements IListRepository {
updatedAt: new Date()
}
});
} catch (e: any) {
console.warn(`[XFlow DB] Ошибка записи списка в БД (${e.message}). Сохранено в памяти.`);
}
}
public async deleteList(id: string, ownerId: string): Promise<boolean> {
PrismaListRepository.localLists.delete(id);
try {
const res = await (prisma as any).noteList.deleteMany({
where: { id, ownerId }
});
return res.count > 0;
} catch {
return true;
}
}
public async findItemById(id: string): Promise<ListItem | null> {
try {
const record = await (prisma as any).listItem.findUnique({
where: { id }
});
if (!record) return null;
if (record) {
return new ListItem({
id: record.id,
listId: record.listId,
@@ -94,8 +135,17 @@ export class PrismaListRepository implements IListRepository {
updatedAt: record.updatedAt
});
}
} catch (e: any) {
console.warn(`[XFlow DB] Ошибка запроса элемента (${e.message}).`);
}
return PrismaListRepository.localItems.get(id) || null;
}
public async saveItem(item: ListItem): Promise<void> {
PrismaListRepository.localItems.set(item.id, item);
try {
await (prisma as any).listItem.upsert({
where: { id: item.id },
create: {
@@ -122,13 +172,21 @@ export class PrismaListRepository implements IListRepository {
updatedAt: new Date()
}
});
} catch (e: any) {
console.warn(`[XFlow DB] Ошибка записи элемента в БД (${e.message}). Сохранено в памяти.`);
}
}
public async deleteItem(id: string): Promise<boolean> {
PrismaListRepository.localItems.delete(id);
try {
const res = await (prisma as any).listItem.delete({
where: { id }
});
return !!res;
} catch {
return true;
}
}
private mapToDomain(record: any): NoteList {