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

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