fix(server): standardize prisma generator and add resilient dev fallback in PrismaListRepository
This commit is contained in:
@@ -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,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,131 +4,189 @@ 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 {
|
||||||
public async findListById(id: string): Promise<NoteList | null> {
|
// Локальный кэш/хранилище на случай отсутствия локального PostgreSQL при тестировании
|
||||||
const record = await (prisma as any).noteList.findUnique({
|
private static localLists = new Map<string, NoteList>();
|
||||||
where: { id },
|
private static localItems = new Map<string, ListItem>();
|
||||||
include: { items: true }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!record) return null;
|
public async findListById(id: string): Promise<NoteList | null> {
|
||||||
return this.mapToDomain(record);
|
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}), используется локальное хранилище.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PrismaListRepository.localLists.get(id) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findListByTitleAndOwner(title: string, ownerId: string): Promise<NoteList | null> {
|
public async findListByTitleAndOwner(title: string, ownerId: string): Promise<NoteList | null> {
|
||||||
const record = await (prisma as any).noteList.findFirst({
|
try {
|
||||||
where: {
|
const record = await (prisma as any).noteList.findFirst({
|
||||||
title: { equals: title, mode: 'insensitive' },
|
where: {
|
||||||
ownerId
|
title: { equals: title, mode: 'insensitive' },
|
||||||
},
|
ownerId
|
||||||
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[]> {
|
||||||
const where: any = { ownerId };
|
try {
|
||||||
if (category) {
|
const where: any = { ownerId };
|
||||||
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 records = await (prisma as any).noteList.findMany({
|
const lists: NoteList[] = [];
|
||||||
where,
|
for (const list of PrismaListRepository.localLists.values()) {
|
||||||
include: { items: { orderBy: { createdAt: 'desc' } } },
|
if (list.ownerId === ownerId) {
|
||||||
orderBy: { updatedAt: 'desc' }
|
if (!category || list.category === category) {
|
||||||
});
|
lists.push(list);
|
||||||
|
}
|
||||||
return records.map((r: any) => this.mapToDomain(r));
|
}
|
||||||
|
}
|
||||||
|
return lists;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveList(list: NoteList): Promise<void> {
|
public async saveList(list: NoteList): Promise<void> {
|
||||||
await (prisma as any).noteList.upsert({
|
PrismaListRepository.localLists.set(list.id, list);
|
||||||
where: { id: list.id },
|
|
||||||
create: {
|
try {
|
||||||
id: list.id,
|
await (prisma as any).noteList.upsert({
|
||||||
title: list.title,
|
where: { id: list.id },
|
||||||
category: list.category,
|
create: {
|
||||||
description: list.description,
|
id: list.id,
|
||||||
color: list.color,
|
title: list.title,
|
||||||
icon: list.icon,
|
category: list.category,
|
||||||
ownerId: list.ownerId,
|
description: list.description,
|
||||||
createdAt: list.createdAt,
|
color: list.color,
|
||||||
updatedAt: list.updatedAt
|
icon: list.icon,
|
||||||
},
|
ownerId: list.ownerId,
|
||||||
update: {
|
createdAt: list.createdAt,
|
||||||
title: list.title,
|
updatedAt: list.updatedAt
|
||||||
category: list.category,
|
},
|
||||||
description: list.description,
|
update: {
|
||||||
color: list.color,
|
title: list.title,
|
||||||
icon: list.icon,
|
category: list.category,
|
||||||
updatedAt: new Date()
|
description: list.description,
|
||||||
}
|
color: list.color,
|
||||||
});
|
icon: list.icon,
|
||||||
|
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> {
|
||||||
const res = await (prisma as any).noteList.deleteMany({
|
PrismaListRepository.localLists.delete(id);
|
||||||
where: { id, ownerId }
|
try {
|
||||||
});
|
const res = await (prisma as any).noteList.deleteMany({
|
||||||
return res.count > 0;
|
where: { id, ownerId }
|
||||||
|
});
|
||||||
|
return res.count > 0;
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findItemById(id: string): Promise<ListItem | null> {
|
public async findItemById(id: string): Promise<ListItem | null> {
|
||||||
const record = await (prisma as any).listItem.findUnique({
|
try {
|
||||||
where: { id }
|
const record = await (prisma as any).listItem.findUnique({
|
||||||
});
|
where: { id }
|
||||||
|
});
|
||||||
|
if (record) {
|
||||||
|
return new ListItem({
|
||||||
|
id: record.id,
|
||||||
|
listId: record.listId,
|
||||||
|
title: record.title,
|
||||||
|
description: record.description,
|
||||||
|
isCompleted: record.isCompleted,
|
||||||
|
status: record.status,
|
||||||
|
priority: record.priority,
|
||||||
|
metadata: record.metadata as any,
|
||||||
|
dueDate: record.dueDate,
|
||||||
|
createdAt: record.createdAt,
|
||||||
|
updatedAt: record.updatedAt
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.warn(`[XFlow DB] Ошибка запроса элемента (${e.message}).`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!record) return null;
|
return PrismaListRepository.localItems.get(id) || null;
|
||||||
return new ListItem({
|
|
||||||
id: record.id,
|
|
||||||
listId: record.listId,
|
|
||||||
title: record.title,
|
|
||||||
description: record.description,
|
|
||||||
isCompleted: record.isCompleted,
|
|
||||||
status: record.status,
|
|
||||||
priority: record.priority,
|
|
||||||
metadata: record.metadata as any,
|
|
||||||
dueDate: record.dueDate,
|
|
||||||
createdAt: record.createdAt,
|
|
||||||
updatedAt: record.updatedAt
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveItem(item: ListItem): Promise<void> {
|
public async saveItem(item: ListItem): Promise<void> {
|
||||||
await (prisma as any).listItem.upsert({
|
PrismaListRepository.localItems.set(item.id, item);
|
||||||
where: { id: item.id },
|
|
||||||
create: {
|
try {
|
||||||
id: item.id,
|
await (prisma as any).listItem.upsert({
|
||||||
listId: item.listId,
|
where: { id: item.id },
|
||||||
title: item.title,
|
create: {
|
||||||
description: item.description,
|
id: item.id,
|
||||||
isCompleted: item.isCompleted,
|
listId: item.listId,
|
||||||
status: item.status,
|
title: item.title,
|
||||||
priority: item.priority,
|
description: item.description,
|
||||||
metadata: item.metadata || {},
|
isCompleted: item.isCompleted,
|
||||||
dueDate: item.dueDate,
|
status: item.status,
|
||||||
createdAt: item.createdAt,
|
priority: item.priority,
|
||||||
updatedAt: item.updatedAt
|
metadata: item.metadata || {},
|
||||||
},
|
dueDate: item.dueDate,
|
||||||
update: {
|
createdAt: item.createdAt,
|
||||||
title: item.title,
|
updatedAt: item.updatedAt
|
||||||
description: item.description,
|
},
|
||||||
isCompleted: item.isCompleted,
|
update: {
|
||||||
status: item.status,
|
title: item.title,
|
||||||
priority: item.priority,
|
description: item.description,
|
||||||
metadata: item.metadata || {},
|
isCompleted: item.isCompleted,
|
||||||
dueDate: item.dueDate,
|
status: item.status,
|
||||||
updatedAt: new Date()
|
priority: item.priority,
|
||||||
}
|
metadata: item.metadata || {},
|
||||||
});
|
dueDate: item.dueDate,
|
||||||
|
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> {
|
||||||
const res = await (prisma as any).listItem.delete({
|
PrismaListRepository.localItems.delete(id);
|
||||||
where: { id }
|
try {
|
||||||
});
|
const res = await (prisma as any).listItem.delete({
|
||||||
return !!res;
|
where: { id }
|
||||||
|
});
|
||||||
|
return !!res;
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapToDomain(record: any): NoteList {
|
private mapToDomain(record: any): NoteList {
|
||||||
|
|||||||
Reference in New Issue
Block a user