first commit

This commit is contained in:
AyrisAI
2026-07-12 20:18:55 +03:00
commit cbc59222c2
65 changed files with 16871 additions and 0 deletions
+675
View File
@@ -0,0 +1,675 @@
import { db } from './db'
export interface Category {
id: string
slug: string
nameTr: string
nameEn: string
nameRu: string
}
export interface Neighborhood {
id: string
slug: string
nameTr: string
nameEn: string
nameRu: string
}
export interface Gallery {
id: string
listingId: string
url: string
}
export interface Listing {
id: string
slug: string
categoryId: string
neighborhoodId: string
city: string
nameTr: string
nameEn: string
nameRu: string
descriptionTr: string
descriptionEn: string
descriptionRu: string
address: string
phone?: string | null
whatsapp?: string | null
website?: string | null
instagram?: string | null
priceRange: number
rating?: number | null
isLocalApproved: boolean
latitude?: number | null
longitude?: number | null
openingHours?: any | null
images: Gallery[]
category?: Category
neighborhood?: Neighborhood
createdAt: Date
updatedAt: Date
deletedAt?: Date | null
}
export interface BusinessSubmission {
id: string
businessName: string
categoryId: string
neighborhoodId: string
address: string
phone?: string | null
whatsapp?: string | null
description: string
contactName: string
contactEmail: string
imageUrl?: string | null
status: string
createdAt: Date
updatedAt: Date
}
export interface ContactMessage {
id: string
name: string
email: string
subject: string
message: string
isRead: boolean
createdAt: Date
updatedAt: Date
}
const globalForMockDb = globalThis as unknown as {
categories: Category[]
neighborhoods: Neighborhood[]
listings: Listing[]
submissions: BusinessSubmission[]
messages: ContactMessage[]
initialized: boolean
}
if (!globalForMockDb.initialized) {
globalForMockDb.categories = [
{ id: 'cat-1', slug: 'restoran', nameTr: 'Restoran', nameEn: 'Restaurant', nameRu: 'Ресторан' },
{ id: 'cat-2', slug: 'apart', nameTr: 'Apart', nameEn: 'Apart Hotel', nameRu: 'Апарт-отель' },
{ id: 'cat-3', slug: 'isletme', nameTr: 'İşletme & Hizmet', nameEn: 'Business & Service', nameRu: 'Бизнес и Услуги' }
]
globalForMockDb.neighborhoods = [
{ id: 'neigh-1', slug: 'yat-limani', nameTr: 'Yat Limanı', nameEn: 'Marina', nameRu: 'Марина' },
{ id: 'neigh-2', slug: 'icmeler', nameTr: 'İçmeler', nameEn: 'Icmeler', nameRu: 'Ичмелер' },
{ id: 'neigh-3', slug: 'armutalan', nameTr: 'Armutalan', nameEn: 'Armutalan', nameRu: 'Армуталан' },
{ id: 'neigh-4', slug: 'siteler', nameTr: 'Siteler', nameEn: 'Siteler', nameRu: 'Сителер' },
{ id: 'neigh-5', slug: 'turunc', nameTr: 'Turunç', nameEn: 'Turunc', nameRu: 'Турунч' }
]
globalForMockDb.submissions = []
globalForMockDb.messages = []
// Pre-populate with realistic Marmaris data
globalForMockDb.listings = [
{
id: 'list-1',
slug: 'iskele-balik-ocakbasi',
categoryId: 'cat-1',
neighborhoodId: 'neigh-1',
city: 'marmaris',
nameTr: 'İskele Balık Ocakbaşı',
nameEn: 'Iskele Fish & Grill',
nameRu: 'Рыбный Гриль Искеле',
descriptionTr: 'Yat Limanı\'nda taze Ege balıkları ve geleneksel meze çeşitleriyle yerel lezzet durağınız. Mükemmel körfez manzarası eşliğinde akşam yemeği.',
descriptionEn: 'Your local taste stop at the Marina with fresh Aegean fish and traditional appetizers. Dinner accompanied by excellent bay views.',
descriptionRu: 'Ваша местная гастрономическая остановка в Марине со свежей эгейской рыбой и традиционными закусками. Ужин в сопровождении великолепного вида на залив.',
address: 'Yat Limanı No:12, Marmaris',
phone: '+90 252 412 34 56',
whatsapp: '+90 532 123 45 67',
website: 'https://iskelemarmaris.com',
instagram: 'https://instagram.com/iskele_marmaris',
priceRange: 3,
rating: 4.8,
isLocalApproved: true,
latitude: 36.8524,
longitude: 28.2741,
openingHours: { all: '12:00 - 00:00' },
images: [
{ id: 'img-1-1', listingId: 'list-1', url: 'https://images.unsplash.com/photo-1519708227418-c8fd9a32b7a2?w=800&auto=format&fit=crop&q=80' },
{ id: 'img-1-2', listingId: 'list-1', url: 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-2',
slug: 'mavi-beyaz-restoran',
categoryId: 'cat-1',
neighborhoodId: 'neigh-1',
city: 'marmaris',
nameTr: 'Mavi Beyaz Restoran',
nameEn: 'Blue White Restaurant',
nameRu: 'Ресторан Сине-Белый',
descriptionTr: 'Ege kıyılarının esintisini taşıyan, deniz ürünleri ağırlıklı menüsü ve gün batımı eşliğindeki eşsiz mezeleriyle ünlüdür.',
descriptionEn: 'Famous for its seafood-oriented menu carrying the breeze of the Aegean coasts and unique appetizers accompanied by sunset.',
descriptionRu: 'Славится своим меню из морепродуктов, несущим бриз Эгейского побережья, и уникальными закусками на фоне заката.',
address: 'Kordon Caddesi No:45, Yat Limanı, Marmaris',
phone: '+90 252 412 78 90',
whatsapp: '+90 533 987 65 43',
website: 'https://mavibeyazmarmaris.com',
instagram: 'https://instagram.com/mavibeyaz_marmaris',
priceRange: 2,
rating: 4.5,
isLocalApproved: true,
latitude: 36.8530,
longitude: 28.2725,
openingHours: { all: '11:00 - 23:30' },
images: [
{ id: 'img-2-1', listingId: 'list-2', url: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-3',
slug: 'dostlar-kebap',
categoryId: 'cat-1',
neighborhoodId: 'neigh-3',
city: 'marmaris',
nameTr: 'Dostlar Kebap',
nameEn: 'Dostlar Kebab House',
nameRu: 'Кебаб Хаус Достлар',
descriptionTr: 'Marmaris Armutalan\'da yıllardır değişmeyen lezzetiyle gerçek ocakbaşı ve kebap deneyimi sunan samimi bir mahalle restoranı.',
descriptionEn: 'A cozy neighborhood restaurant offering real grill and kebab experience with its unchanged taste for years in Armutalan, Marmaris.',
descriptionRu: 'Уютный районный ресторан, предлагающий настоящий гриль и кебаб с неизменным вкусом на протяжении многих лет в Армуталане, Мармарис.',
address: 'Vatan Caddesi No:18, Armutalan, Marmaris',
phone: '+90 252 413 11 22',
whatsapp: null,
website: null,
instagram: 'https://instagram.com/dostlarkebap_marmaris',
priceRange: 1,
rating: 4.7,
isLocalApproved: false,
latitude: 36.8488,
longitude: 28.2450,
openingHours: { all: '11:00 - 22:00' },
images: [
{ id: 'img-3-1', listingId: 'list-3', url: 'https://images.unsplash.com/photo-1544025162-d76694265947?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-4',
slug: 'zeytin-bahcesi-apart',
categoryId: 'cat-2',
neighborhoodId: 'neigh-3',
city: 'marmaris',
nameTr: 'Zeytin Bahçesi Apart',
nameEn: 'Olive Garden Apart',
nameRu: 'Апарт Оливковый Сад',
descriptionTr: 'Zeytin ağaçları arasında, sakin ve huzurlu bir ortamda ailece tatil yapmak isteyenler için tasarlanmış geniş mutfaklı apart daireler.',
descriptionEn: 'Spacious apart apartments with kitchens designed for families who want to have a holiday in a quiet and peaceful environment among olive trees.',
descriptionRu: 'Просторные апартаменты с кухнями, предназначенные для семей, желающих отдохнуть в тихой и спокойной обстановке среди оливковых деревьев.',
address: 'Zeytinlik Sokak No:5, Armutalan, Marmaris',
phone: '+90 252 413 55 66',
whatsapp: '+90 535 555 66 77',
website: 'https://zeytinbahcesiapart.com',
instagram: null,
priceRange: 2,
rating: 4.6,
isLocalApproved: true,
latitude: 36.8510,
longitude: 28.2415,
openingHours: null,
images: [
{ id: 'img-4-1', listingId: 'list-4', url: 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=800&auto=format&fit=crop&q=80' },
{ id: 'img-4-2', listingId: 'list-4', url: 'https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-5',
slug: 'deniz-apart',
categoryId: 'cat-2',
neighborhoodId: 'neigh-4',
city: 'marmaris',
nameTr: 'Deniz Apart',
nameEn: 'Sea Apart Hotel',
nameRu: 'Апарт-отель Дениз',
descriptionTr: 'Denize sadece 100 metre mesafede, bütçe dostu fiyatları ve güler yüzlü yerel işletme sahibiyle Marmaris\'te sıcak konaklama.',
descriptionEn: 'Cozy accommodation in Marmaris, just 100 meters from the sea, with budget-friendly prices and a friendly local owner.',
descriptionRu: 'Уютное жилье в Мармарисе, всего в 100 метрах от моря, с доступными ценами и дружелюбным местным владельцем.',
address: 'Sahil Yolu Caddesi No:88, Siteler, Marmaris',
phone: '+90 252 417 88 99',
whatsapp: null,
website: null,
instagram: null,
priceRange: 1,
rating: 4.2,
isLocalApproved: false,
latitude: 36.8375,
longitude: 28.2580,
openingHours: null,
images: [
{ id: 'img-5-1', listingId: 'list-5', url: 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-6',
slug: 'marina-dalis-merkezi',
categoryId: 'cat-3',
neighborhoodId: 'neigh-2',
city: 'marmaris',
nameTr: 'Marina Dalış Merkezi',
nameEn: 'Marina Diving Center',
nameRu: 'Дайвинг-центр Марина',
descriptionTr: 'Marmaris\'in eşsiz koylarında profesyonel eğitmenler eşliğinde tüplü dalış deneyimi. Başlangıç seviyesinden PADI sertifikasyonuna kadar hizmet.',
descriptionEn: 'Scuba diving experience in unique bays of Marmaris accompanied by professional instructors. Service from discovery dive to PADI certification.',
descriptionRu: 'Опыт подводного плавания в уникальных бухтах Мармариса в сопровождении профессиональных инструкторов. Услуги от ознакомительного погружения до сертификации PADI.',
address: 'Liman Yolu No:32, İçmeler, Marmaris',
phone: '+90 252 455 22 33',
whatsapp: '+90 536 777 88 99',
website: 'https://marinadivingmarmaris.com',
instagram: 'https://instagram.com/marinadiving_marmaris',
priceRange: 3,
rating: 4.9,
isLocalApproved: true,
latitude: 36.8020,
longitude: 28.2320,
openingHours: { all: '08:30 - 19:30' },
images: [
{ id: 'img-6-1', listingId: 'list-6', url: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-7',
slug: 'ege-ruzgari-tekne-kiralama',
categoryId: 'cat-3',
neighborhoodId: 'neigh-1',
city: 'marmaris',
nameTr: 'Ege Rüzgarı Tekne Kiralama',
nameEn: 'Aegean Wind Boat Rental',
nameRu: 'Аренда Лодок Эгейский Ветер',
descriptionTr: 'Kaptanlı veya kaptansız günlük ve haftalık özel tekne kiralama hizmeti. Marmaris koylarını kendi rotanızda özgürce keşfedin.',
descriptionEn: 'Daily and weekly private boat rental service with or without skipper. Discover Marmaris bays freely on your own route.',
descriptionRu: 'Ежедневная и еженедельная аренда частных лодок со шкипером или без. Откройте для себя бухты Мармариса свободно по собственному маршруту.',
address: 'Yat Limanı G İskelesi, Marmaris',
phone: '+90 532 999 88 77',
whatsapp: '+90 532 999 88 77',
website: 'https://egeruzgariboat.com',
instagram: 'https://instagram.com/egeruzgariboat',
priceRange: 3,
rating: 4.8,
isLocalApproved: true,
latitude: 36.8528,
longitude: 28.2755,
openingHours: { all: '08:00 - 21:00' },
images: [
{ id: 'img-7-1', listingId: 'list-7', url: 'https://images.unsplash.com/photo-1567899378494-47b22a2ae96a?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'list-8',
slug: 'marmaris-transfer-tours',
categoryId: 'cat-3',
neighborhoodId: 'neigh-3',
city: 'marmaris',
nameTr: 'Marmaris VIP Transfer & Tur',
nameEn: 'Marmaris VIP Transfer & Tours',
nameRu: 'Marmaris VIP Трансфер и Туры',
descriptionTr: 'Dalaman Havalimanı transferleri ve Marmaris çevresindeki tarihi/doğal alanlara özel konforlu turlar. Güvenilir ve lüks taşımacılık.',
descriptionEn: 'Dalaman Airport transfers and comfortable private tours to historical/natural areas around Marmaris. Reliable and luxurious transportation.',
descriptionRu: 'Трансфер из аэропорта Даламан и комфортабельные частные туры по историческим и природным местам вокруг Мармариса. Надежный и роскошный транспорт.',
address: 'Atatürk Caddesi No:102, Armutalan, Marmaris',
phone: '+90 252 413 77 88',
whatsapp: '+90 541 333 44 55',
website: 'https://marmarisviptransfer.com',
instagram: null,
priceRange: 2,
rating: 4.6,
isLocalApproved: false,
latitude: 36.8495,
longitude: 28.2430,
openingHours: { all: '24 Hours Open' },
images: [
{ id: 'img-8-1', listingId: 'list-8', url: 'https://images.unsplash.com/photo-1549317661-bd32c8ce0db2?w=800&auto=format&fit=crop&q=80' }
],
createdAt: new Date(),
updatedAt: new Date()
}
]
globalForMockDb.initialized = true
}
export const mockDb = {
// Config helpers
isMock: () => process.env.USE_MOCK === 'true',
// Categories
async getCategories() {
if (this.isMock()) {
return globalForMockDb.categories
}
return db.category.findMany({ orderBy: { nameTr: 'asc' } })
},
async getCategoryBySlug(slug: string) {
if (this.isMock()) {
return globalForMockDb.categories.find(c => c.slug === slug) || null
}
return db.category.findUnique({ where: { slug } })
},
async createCategory(data: { slug: string; nameTr: string; nameEn: string; nameRu: string }) {
if (this.isMock()) {
const newCat = { id: `cat-${Date.now()}`, ...data }
globalForMockDb.categories.push(newCat)
return newCat
}
return db.category.create({ data })
},
async updateCategory(id: string, data: { slug: string; nameTr: string; nameEn: string; nameRu: string }) {
if (this.isMock()) {
const idx = globalForMockDb.categories.findIndex(c => c.id === id)
if (idx !== -1) {
globalForMockDb.categories[idx] = { ...globalForMockDb.categories[idx], ...data }
return globalForMockDb.categories[idx]
}
return null
}
return db.category.update({ where: { id }, data })
},
// Neighborhoods
async getNeighborhoods() {
if (this.isMock()) {
return globalForMockDb.neighborhoods
}
return db.neighborhood.findMany({ orderBy: { nameTr: 'asc' } })
},
async getNeighborhoodBySlug(slug: string) {
if (this.isMock()) {
return globalForMockDb.neighborhoods.find(n => n.slug === slug) || null
}
return db.neighborhood.findUnique({ where: { slug } })
},
async createNeighborhood(data: { slug: string; nameTr: string; nameEn: string; nameRu: string }) {
if (this.isMock()) {
const newNeigh = { id: `neigh-${Date.now()}`, ...data }
globalForMockDb.neighborhoods.push(newNeigh)
return newNeigh
}
return db.neighborhood.create({ data })
},
async updateNeighborhood(id: string, data: { slug: string; nameTr: string; nameEn: string; nameRu: string }) {
if (this.isMock()) {
const idx = globalForMockDb.neighborhoods.findIndex(n => n.id === id)
if (idx !== -1) {
globalForMockDb.neighborhoods[idx] = { ...globalForMockDb.neighborhoods[idx], ...data }
return globalForMockDb.neighborhoods[idx]
}
return null
}
return db.neighborhood.update({ where: { id }, data })
},
// Listings
async getListings(filters?: {
categoryId?: string
neighborhoodId?: string
priceRange?: number
isLocalApproved?: boolean
search?: string
}) {
if (this.isMock()) {
let result = globalForMockDb.listings.filter(l => !l.deletedAt)
if (filters) {
if (filters.categoryId) result = result.filter(l => l.categoryId === filters.categoryId)
if (filters.neighborhoodId) result = result.filter(l => l.neighborhoodId === filters.neighborhoodId)
if (filters.priceRange) result = result.filter(l => l.priceRange === filters.priceRange)
if (filters.isLocalApproved !== undefined) result = result.filter(l => l.isLocalApproved === filters.isLocalApproved)
if (filters.search) {
const s = filters.search.toLowerCase()
result = result.filter(l =>
l.nameTr.toLowerCase().includes(s) ||
l.nameEn.toLowerCase().includes(s) ||
l.nameRu.toLowerCase().includes(s) ||
l.address.toLowerCase().includes(s)
)
}
}
return result.map(l => ({
...l,
category: globalForMockDb.categories.find(c => c.id === l.categoryId),
neighborhood: globalForMockDb.neighborhoods.find(n => n.id === l.neighborhoodId)
}))
}
const where: any = { deletedAt: null }
if (filters) {
if (filters.categoryId) where.categoryId = filters.categoryId
if (filters.neighborhoodId) where.neighborhoodId = filters.neighborhoodId
if (filters.priceRange) where.priceRange = filters.priceRange
if (filters.isLocalApproved !== undefined) where.isLocalApproved = filters.isLocalApproved
if (filters.search) {
where.OR = [
{ nameTr: { contains: filters.search, mode: 'insensitive' } },
{ nameEn: { contains: filters.search, mode: 'insensitive' } },
{ nameRu: { contains: filters.search, mode: 'insensitive' } },
{ address: { contains: filters.search, mode: 'insensitive' } },
]
}
}
return db.listing.findMany({
where,
include: { category: true, neighborhood: true, images: true },
orderBy: { createdAt: 'desc' }
})
},
async getListingBySlug(slug: string) {
if (this.isMock()) {
const listing = globalForMockDb.listings.find(l => l.slug === slug && !l.deletedAt)
if (!listing) return null
return {
...listing,
category: globalForMockDb.categories.find(c => c.id === listing.categoryId),
neighborhood: globalForMockDb.neighborhoods.find(n => n.id === listing.neighborhoodId)
}
}
return db.listing.findUnique({
where: { slug },
include: { category: true, neighborhood: true, images: true }
})
},
async getListingById(id: string) {
if (this.isMock()) {
const listing = globalForMockDb.listings.find(l => l.id === id && !l.deletedAt)
if (!listing) return null
return {
...listing,
category: globalForMockDb.categories.find(c => c.id === listing.categoryId),
neighborhood: globalForMockDb.neighborhoods.find(n => n.id === listing.neighborhoodId)
}
}
return db.listing.findUnique({
where: { id },
include: { category: true, neighborhood: true, images: true }
})
},
async createListing(data: Omit<Listing, 'id' | 'images' | 'createdAt' | 'updatedAt'> & { images?: string[] }) {
const { images, category, neighborhood, ...rest } = data
if (this.isMock()) {
const newId = `list-${Date.now()}`
const gallery = (images || []).map((url, i) => ({ id: `img-${newId}-${i}`, listingId: newId, url }))
const newListing: Listing = {
id: newId,
...rest,
images: gallery,
createdAt: new Date(),
updatedAt: new Date()
}
globalForMockDb.listings.unshift(newListing)
return newListing
}
return db.listing.create({
data: {
...rest,
images: images ? { create: images.map(url => ({ url })) } : undefined
},
include: { images: true }
})
},
async updateListing(id: string, data: Partial<Omit<Listing, 'id' | 'images' | 'createdAt' | 'updatedAt'>> & { images?: string[] }) {
const { images, category, neighborhood, ...rest } = data
if (this.isMock()) {
const idx = globalForMockDb.listings.findIndex(l => l.id === id)
if (idx !== -1) {
const oldListing = globalForMockDb.listings[idx]
const updatedGallery = images
? images.map((url, i) => ({ id: `img-${id}-${i}`, listingId: id, url }))
: oldListing.images
const updated = {
...oldListing,
...rest,
images: updatedGallery,
updatedAt: new Date()
} as Listing
globalForMockDb.listings[idx] = updated
return updated
}
return null
}
if (images) {
await db.gallery.deleteMany({ where: { listingId: id } })
}
return db.listing.update({
where: { id },
data: {
...rest,
images: images ? { create: images.map(url => ({ url })) } : undefined
},
include: { images: true }
})
},
async deleteListing(id: string) {
if (this.isMock()) {
const idx = globalForMockDb.listings.findIndex(l => l.id === id)
if (idx !== -1) {
globalForMockDb.listings[idx].deletedAt = new Date()
return true
}
return false
}
await db.listing.update({
where: { id },
data: { deletedAt: new Date() }
})
return true
},
// Business Submissions
async getSubmissions() {
if (this.isMock()) {
return globalForMockDb.submissions.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
}
return db.businessSubmission.findMany({ orderBy: { createdAt: 'desc' } })
},
async createSubmission(data: Omit<BusinessSubmission, 'id' | 'status' | 'createdAt' | 'updatedAt'>) {
if (this.isMock()) {
const newSub: BusinessSubmission = {
id: `sub-${Date.now()}`,
...data,
status: 'PENDING',
createdAt: new Date(),
updatedAt: new Date()
}
globalForMockDb.submissions.push(newSub)
return newSub
}
return db.businessSubmission.create({ data: { ...data, status: 'PENDING' } })
},
async updateSubmissionStatus(id: string, status: 'APPROVED' | 'REJECTED') {
if (this.isMock()) {
const idx = globalForMockDb.submissions.findIndex(s => s.id === id)
if (idx !== -1) {
globalForMockDb.submissions[idx].status = status
globalForMockDb.submissions[idx].updatedAt = new Date()
return globalForMockDb.submissions[idx]
}
return null
}
return db.businessSubmission.update({
where: { id },
data: { status }
})
},
async getSubmissionById(id: string) {
if (this.isMock()) {
return globalForMockDb.submissions.find(s => s.id === id) || null
}
return db.businessSubmission.findUnique({ where: { id } })
},
// Contact Messages
async getMessages() {
if (this.isMock()) {
return globalForMockDb.messages.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
}
return db.contactMessage.findMany({ orderBy: { createdAt: 'desc' } })
},
async createMessage(data: Omit<ContactMessage, 'id' | 'isRead' | 'createdAt' | 'updatedAt'>) {
if (this.isMock()) {
const newMsg: ContactMessage = {
id: `msg-${Date.now()}`,
...data,
isRead: false,
createdAt: new Date(),
updatedAt: new Date()
}
globalForMockDb.messages.push(newMsg)
return newMsg
}
return db.contactMessage.create({ data: { ...data, isRead: false } })
},
async markMessageAsRead(id: string) {
if (this.isMock()) {
const idx = globalForMockDb.messages.findIndex(m => m.id === id)
if (idx !== -1) {
globalForMockDb.messages[idx].isRead = true
globalForMockDb.messages[idx].updatedAt = new Date()
return globalForMockDb.messages[idx]
}
return null
}
return db.contactMessage.update({
where: { id },
data: { isRead: true }
})
}
}