diff --git a/backend/package-lock.json b/backend/package-lock.json index 33b974f..d9f549b 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -3075,7 +3075,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ diff --git a/backend/prisma/dev.db b/backend/prisma/dev.db new file mode 100644 index 0000000..f463731 Binary files /dev/null and b/backend/prisma/dev.db differ diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 207fbd4..5ef78a8 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -1,5 +1,5 @@ // HMarket Veritabanı Şeması -// MariaDB için Prisma ORM konfigürasyonu +// MySQL için Prisma ORM konfigürasyonu generator client { provider = "prisma-client-js" @@ -153,7 +153,7 @@ model ListItem { customName String? // Ürün yoksa manuel isim quantity Float @default(1) unit String @default("adet") // "kg", "lt", "adet", vb. - price Decimal? @db.Decimal(10, 2) + price Decimal? // SQLite: Decimal stored as String note String? isPurchased Boolean @default(false) purchasedAt DateTime? @@ -174,7 +174,7 @@ model ListItem { model PriceHistory { id String @id @default(cuid()) productId String - price Decimal @db.Decimal(10, 2) + price Decimal // SQLite: Decimal stored as String store String? // Mağaza adı location String? // Konum bilgisi source String @default("user") // "user", "api", "scraping" @@ -193,7 +193,7 @@ model Notification { title String message String type String // "list_shared", "item_added", "item_purchased", "price_alert" - data Json? // Ek veri (JSON format) + data String? // Ek veri (JSON format - SQLite String olarak saklanır) isRead Boolean @default(false) createdAt DateTime @default(now()) @@ -209,7 +209,7 @@ model Activity { listId String userId String action String // "item_added", "item_removed", "item_updated", "item_purchased" - details Json? // Aktivite detayları + details String? // Aktivite detayları (JSON - SQLite String olarak saklanır) createdAt DateTime @default(now()) // İlişkiler diff --git a/backend/prisma/seed.js b/backend/prisma/seed.js index 15a05f7..ee3574c 100644 --- a/backend/prisma/seed.js +++ b/backend/prisma/seed.js @@ -1,389 +1,139 @@ -const { PrismaClient } = require('@prisma/client'); -const bcrypt = require('bcryptjs'); +import { PrismaClient } from '@prisma/client' -const prisma = new PrismaClient(); +const prisma = new PrismaClient() async function main() { - console.log('🌱 Veritabanı seed işlemi başlatılıyor...'); + console.log('Örnek veriler ekleniyor...') - // Admin kullanıcısı oluştur - const hashedPassword = await bcrypt.hash('admin123', 12); - - const adminUser = await prisma.user.upsert({ - where: { email: 'admin@hmarket.com' }, - update: {}, - create: { - email: 'admin@hmarket.com', - username: 'admin', - firstName: 'Admin', - lastName: 'User', - password: hashedPassword, - isAdmin: true, - }, - }); + // Kullanıcılar + const user1 = await prisma.user.create({ + data: { + email: 'mustafa@example.com', + username: 'mustafa', + firstName: 'Mustafa', + lastName: 'ÖZKAYA', + password: '$2a$10$dummy', + avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=mustafa', + } + }) - console.log('✅ Admin kullanıcısı oluşturuldu:', adminUser.email); - - // Test kullanıcıları oluştur - const testUsers = [ - { - email: 'ahmet@test.com', + const user2 = await prisma.user.create({ + data: { + email: 'ahmet@example.com', username: 'ahmet', firstName: 'Ahmet', lastName: 'Yılmaz', - password: await bcrypt.hash('test123', 12), - }, - { - email: 'ayse@test.com', + password: '$2a$10$dummy', + avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=ahmet', + } + }) + + const user3 = await prisma.user.create({ + data: { + email: 'ayse@example.com', username: 'ayse', firstName: 'Ayşe', lastName: 'Kaya', - password: await bcrypt.hash('test123', 12), - }, - { - email: 'mehmet@test.com', - username: 'mehmet', - firstName: 'Mehmet', - lastName: 'Demir', - password: await bcrypt.hash('test123', 12), - }, - ]; - - for (const userData of testUsers) { - const user = await prisma.user.upsert({ - where: { email: userData.email }, - update: {}, - create: userData, - }); - console.log('✅ Test kullanıcısı oluşturuldu:', user.email); - } - - // Kategoriler oluştur - const categories = [ - { name: 'Meyve & Sebze', description: 'Taze meyve ve sebzeler', icon: '🥕', color: '#4CAF50' }, - { name: 'Et & Tavuk', description: 'Kırmızı et, beyaz et ve şarküteri ürünleri', icon: '🥩', color: '#F44336' }, - { name: 'Süt Ürünleri', description: 'Süt, peynir, yoğurt ve diğer süt ürünleri', icon: '🥛', color: '#2196F3' }, - { name: 'Fırın & Pastane', description: 'Ekmek, pasta ve unlu mamuller', icon: '🍞', color: '#FF9800' }, - { name: 'Atıştırmalık', description: 'Çikolata, bisküvi ve atıştırmalık ürünler', icon: '🍿', color: '#9C27B0' }, - { name: 'İçecek', description: 'Alkolsüz içecekler ve sıcak içecekler', icon: '🥤', color: '#00BCD4' }, - { name: 'Temizlik', description: 'Ev temizlik ürünleri ve deterjanlar', icon: '🧽', color: '#607D8B' }, - { name: 'Kişisel Bakım', description: 'Kişisel hijyen ve bakım ürünleri', icon: '🧴', color: '#E91E63' }, - { name: 'Bebek', description: 'Bebek bakım ürünleri ve mama', icon: '🍼', color: '#FFEB3B' }, - { name: 'Dondurulmuş', description: 'Dondurulmuş gıda ürünleri', icon: '🧊', color: '#3F51B5' }, - ]; - - for (const categoryData of categories) { - const category = await prisma.category.upsert({ - where: { name: categoryData.name }, - update: {}, - create: categoryData, - }); - console.log('✅ Kategori oluşturuldu:', category.name); - } - - // Örnek ürünler oluştur - const products = [ - // Meyve & Sebze - { name: 'Elma', categoryName: 'Meyve & Sebze', barcode: '1234567890123', averagePrice: 12.50 }, - { name: 'Muz', categoryName: 'Meyve & Sebze', barcode: '1234567890124', averagePrice: 18.00 }, - { name: 'Domates', categoryName: 'Meyve & Sebze', barcode: '1234567890125', averagePrice: 8.75 }, - { name: 'Salatalık', categoryName: 'Meyve & Sebze', barcode: '1234567890126', averagePrice: 6.50 }, - { name: 'Soğan', categoryName: 'Meyve & Sebze', barcode: '1234567890127', averagePrice: 4.25 }, - { name: 'Patates', categoryName: 'Meyve & Sebze', barcode: '1234567890137', averagePrice: 5.75 }, - { name: 'Havuç', categoryName: 'Meyve & Sebze', barcode: '1234567890138', averagePrice: 7.50 }, - { name: 'Biber', categoryName: 'Meyve & Sebze', barcode: '1234567890139', averagePrice: 15.00 }, - { name: 'Portakal', categoryName: 'Meyve & Sebze', barcode: '1234567890140', averagePrice: 10.00 }, - { name: 'Limon', categoryName: 'Meyve & Sebze', barcode: '1234567890141', averagePrice: 8.00 }, - - // Süt Ürünleri - { name: 'Süt 1L', categoryName: 'Süt Ürünleri', barcode: '1234567890128', averagePrice: 8.75 }, - { name: 'Yoğurt 500g', categoryName: 'Süt Ürünleri', barcode: '1234567890129', averagePrice: 12.50 }, - { name: 'Beyaz Peynir', categoryName: 'Süt Ürünleri', barcode: '1234567890130', averagePrice: 45.00 }, - { name: 'Kaşar Peyniri', categoryName: 'Süt Ürünleri', barcode: '1234567890131', averagePrice: 65.00 }, - { name: 'Tereyağı', categoryName: 'Süt Ürünleri', barcode: '1234567890142', averagePrice: 35.00 }, - { name: 'Yumurta 30\'lu', categoryName: 'Süt Ürünleri', barcode: '1234567890143', averagePrice: 85.00 }, - { name: 'Krema', categoryName: 'Süt Ürünleri', barcode: '1234567890144', averagePrice: 15.50 }, - - // Fırın & Pastane - { name: 'Ekmek', categoryName: 'Fırın & Pastane', barcode: '1234567890132', averagePrice: 4.50 }, - { name: 'Simit', categoryName: 'Fırın & Pastane', barcode: '1234567890133', averagePrice: 2.50 }, - { name: 'Pide', categoryName: 'Fırın & Pastane', barcode: '1234567890145', averagePrice: 8.00 }, - { name: 'Çörek', categoryName: 'Fırın & Pastane', barcode: '1234567890146', averagePrice: 6.50 }, - { name: 'Kek', categoryName: 'Fırın & Pastane', barcode: '1234567890147', averagePrice: 25.00 }, - - // İçecek - { name: 'Su 1.5L', categoryName: 'İçecek', barcode: '1234567890134', averagePrice: 2.50 }, - { name: 'Çay', categoryName: 'İçecek', barcode: '1234567890135', averagePrice: 35.00 }, - { name: 'Kahve', categoryName: 'İçecek', barcode: '1234567890136', averagePrice: 85.00 }, - { name: 'Meyve Suyu', categoryName: 'İçecek', barcode: '1234567890148', averagePrice: 12.50 }, - { name: 'Kola', categoryName: 'İçecek', barcode: '1234567890149', averagePrice: 8.75 }, - { name: 'Ayran', categoryName: 'İçecek', barcode: '1234567890150', averagePrice: 4.50 }, - - // Et & Tavuk - { name: 'Tavuk But', categoryName: 'Et & Tavuk', barcode: '1234567890151', averagePrice: 45.00 }, - { name: 'Dana Kıyma', categoryName: 'Et & Tavuk', barcode: '1234567890152', averagePrice: 120.00 }, - { name: 'Köfte', categoryName: 'Et & Tavuk', barcode: '1234567890153', averagePrice: 85.00 }, - { name: 'Sosis', categoryName: 'Et & Tavuk', barcode: '1234567890154', averagePrice: 25.00 }, - - // Temizlik - { name: 'Deterjan', categoryName: 'Temizlik', barcode: '1234567890155', averagePrice: 45.00 }, - { name: 'Sabun', categoryName: 'Temizlik', barcode: '1234567890156', averagePrice: 8.50 }, - { name: 'Şampuan', categoryName: 'Temizlik', barcode: '1234567890157', averagePrice: 35.00 }, - { name: 'Diş Macunu', categoryName: 'Kişisel Bakım', barcode: '1234567890158', averagePrice: 18.50 }, - - // Atıştırmalık - { name: 'Çikolata', categoryName: 'Atıştırmalık', barcode: '1234567890159', averagePrice: 12.50 }, - { name: 'Bisküvi', categoryName: 'Atıştırmalık', barcode: '1234567890160', averagePrice: 8.75 }, - { name: 'Cips', categoryName: 'Atıştırmalık', barcode: '1234567890161', averagePrice: 6.50 }, - ]; - - for (const productData of products) { - const category = await prisma.category.findUnique({ - where: { name: productData.categoryName } - }); - - if (category) { - const product = await prisma.product.upsert({ - where: { barcode: productData.barcode }, - update: {}, - create: { - name: productData.name, - barcode: productData.barcode, - categoryId: category.id, - }, - }); - console.log('✅ Ürün oluşturuldu:', product.name); + password: '$2a$10$dummy', + avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=ayse', } - } + }) - // Örnek alışveriş listeleri oluştur - const ahmetUser = await prisma.user.findUnique({ - where: { email: 'ahmet@test.com' } - }); + console.log('✅ Kullanıcılar oluşturuldu') - const ayseUser = await prisma.user.findUnique({ - where: { email: 'ayse@test.com' } - }); + // Kategoriler + const cat1 = await prisma.category.create({ data: { name: 'Sebze & Meyve', icon: '🥬', color: '#4CAF50' } }) + const cat2 = await prisma.category.create({ data: { name: 'Et & Tavuk', icon: '🥩', color: '#F44336' } }) + const cat3 = await prisma.category.create({ data: { name: 'Süt & Süt Ürünleri', icon: '🥛', color: '#2196F3' } }) + const cat4 = await prisma.category.create({ data: { name: 'Ekmek & Unlu Mamüller', icon: '🍞', color: '#FF9800' } }) + const cat5 = await prisma.category.create({ data: { name: 'İçecekler', icon: '🥤', color: '#9C27B0' } }) + const cat6 = await prisma.category.create({ data: { name: 'Temizlik', icon: '🧹', color: '#607D8B' } }) + const cat7 = await prisma.category.create({ data: { name: 'Atıştırmalık', icon: '🍪', color: '#795548' } }) + const cat8 = await prisma.category.create({ data: { name: 'Dondurma', icon: '🍦', color: '#E91E63' } }) - const mehmetUser = await prisma.user.findUnique({ - where: { email: 'mehmet@test.com' } - }); + console.log('✅ Kategoriler oluşturuldu') - // Ahmet için alışveriş listeleri - if (ahmetUser) { - const shoppingLists = [ - { - name: 'Haftalık Alışveriş', - description: 'Bu haftanın market alışverişi', - color: '#4CAF50', - items: [ - { productName: 'Elma', quantity: 2, unit: 'kg', price: 15.50 }, - { productName: 'Süt 1L', quantity: 1, unit: 'adet', price: 8.75 }, - { customName: 'Deterjan', quantity: 1, unit: 'adet', price: 25.00, note: 'Çamaşır deterjanı' }, - { productName: 'Ekmek', quantity: 2, unit: 'adet', price: 9.00 }, - { productName: 'Yumurta 30\'lu', quantity: 1, unit: 'adet', price: 85.00 }, - ] - }, - { - name: 'Kahvaltı Malzemeleri', - description: 'Kahvaltı için gerekli ürünler', - color: '#FF9800', - items: [ - { productName: 'Ekmek', quantity: 2, unit: 'adet', price: 9.00 }, - { productName: 'Beyaz Peynir', quantity: 1, unit: 'kg', price: 45.00 }, - { productName: 'Tereyağı', quantity: 1, unit: 'adet', price: 35.00 }, - { productName: 'Domates', quantity: 1, unit: 'kg', price: 8.75 }, - { productName: 'Salatalık', quantity: 2, unit: 'adet', price: 13.00 }, - { productName: 'Çay', quantity: 1, unit: 'adet', price: 35.00 }, - ] - } - ]; + // Ürünler + const products = await Promise.all([ + prisma.product.create({ data: { name: 'Domates', categoryId: cat1.id, unit: 'kg' } }), + prisma.product.create({ data: { name: 'Soğan', categoryId: cat1.id, unit: 'kg' } }), + prisma.product.create({ data: { name: 'Salatalık', categoryId: cat1.id, unit: 'adet' } }), + prisma.product.create({ data: { name: 'Tavuk Göğsü', categoryId: cat2.id, unit: 'kg' } }), + prisma.product.create({ data: { name: 'Kıyma', categoryId: cat2.id, unit: 'kg' } }), + prisma.product.create({ data: { name: 'Kırmızı Et', categoryId: cat2.id, unit: 'kg' } }), + prisma.product.create({ data: { name: 'Süt', categoryId: cat3.id, unit: 'L' } }), + prisma.product.create({ data: { name: 'Yoğurt', categoryId: cat3.id, unit: 'adet' } }), + prisma.product.create({ data: { name: 'Ekmek', categoryId: cat4.id, unit: 'adet' } }), + prisma.product.create({ data: { name: 'Cola', categoryId: cat5.id, unit: 'adet' } }), + prisma.product.create({ data: { name: 'Bulaşık Deterjanı', categoryId: cat6.id, unit: 'adet' } }), + prisma.product.create({ data: { name: 'Baklava', categoryId: cat7.id, unit: 'kg' } }), + prisma.product.create({ data: { name: 'Dondurma', categoryId: cat8.id, unit: 'L' } }), + ]) - for (const listData of shoppingLists) { - const shoppingList = await prisma.shoppingList.create({ - data: { - name: listData.name, - description: listData.description, - color: listData.color, - ownerId: ahmetUser.id, - }, - }); + console.log('✅ Ürünler oluşturuldu') - for (const itemData of listData.items) { - let productId = null; - - if (itemData.productName) { - const product = await prisma.product.findFirst({ - where: { name: itemData.productName } - }); - if (product) { - productId = product.id; - } - } - - await prisma.listItem.create({ - data: { - listId: shoppingList.id, - productId: productId, - customName: itemData.customName, - quantity: itemData.quantity, - unit: itemData.unit, - price: itemData.price, - note: itemData.note, - }, - }); - } - - console.log('✅ Alışveriş listesi oluşturuldu:', shoppingList.name); + // Market Listeleri + const list1 = await prisma.shoppingList.create({ + data: { + name: 'Haftalık Market', + description: 'Bu hafta alınacaklar', + color: '#4CAF50', + ownerId: user1.id, } - } + }) - // Ayşe için alışveriş listeleri - if (ayseUser) { - const ayseShoppingLists = [ - { - name: 'Parti Alışverişi', - description: 'Doğum günü partisi için', - color: '#E91E63', - items: [ - { productName: 'Kek', quantity: 1, unit: 'adet', price: 25.00 }, - { productName: 'Çikolata', quantity: 5, unit: 'adet', price: 62.50 }, - { productName: 'Bisküvi', quantity: 3, unit: 'paket', price: 26.25 }, - { productName: 'Kola', quantity: 6, unit: 'adet', price: 52.50 }, - { productName: 'Meyve Suyu', quantity: 4, unit: 'adet', price: 50.00 }, - { customName: 'Parti Süsleri', quantity: 1, unit: 'set', price: 45.00 }, - ] - }, - { - name: 'Temizlik Malzemeleri', - description: 'Ev temizliği için gerekli ürünler', - color: '#607D8B', - items: [ - { productName: 'Deterjan', quantity: 2, unit: 'adet', price: 90.00 }, - { productName: 'Sabun', quantity: 3, unit: 'adet', price: 25.50 }, - { productName: 'Şampuan', quantity: 1, unit: 'adet', price: 35.00 }, - { customName: 'Cam Temizleyici', quantity: 1, unit: 'adet', price: 18.50 }, - { customName: 'Yer Bezi', quantity: 2, unit: 'adet', price: 15.00 }, - ] - } - ]; - - for (const listData of ayseShoppingLists) { - const shoppingList = await prisma.shoppingList.create({ - data: { - name: listData.name, - description: listData.description, - color: listData.color, - ownerId: ayseUser.id, - }, - }); - - for (const itemData of listData.items) { - let productId = null; - - if (itemData.productName) { - const product = await prisma.product.findFirst({ - where: { name: itemData.productName } - }); - if (product) { - productId = product.id; - } - } - - await prisma.listItem.create({ - data: { - listId: shoppingList.id, - productId: productId, - customName: itemData.customName, - quantity: itemData.quantity, - unit: itemData.unit, - price: itemData.price, - }, - }); - } - - console.log('✅ Alışveriş listesi oluşturuldu:', shoppingList.name); + // Listeye üye ekle + await prisma.listMember.create({ + data: { + userId: user2.id, + listId: list1.id, + role: 'admin' } - } + }) - // Mehmet için alışveriş listesi - if (mehmetUser) { - const mehmetShoppingList = { - name: 'Et ve Protein', - description: 'Protein ihtiyacı için et ürünleri', - color: '#F44336', - items: [ - { productName: 'Tavuk But', quantity: 2, unit: 'kg', price: 90.00 }, - { productName: 'Dana Kıyma', quantity: 1, unit: 'kg', price: 120.00 }, - { productName: 'Köfte', quantity: 1, unit: 'kg', price: 85.00 }, - { productName: 'Sosis', quantity: 2, unit: 'paket', price: 50.00 }, - { productName: 'Yumurta 30\'lu', quantity: 1, unit: 'adet', price: 85.00 }, - ] - }; + // Liste Öğeleri (productId kullan) + await prisma.listItem.create({ data: { productId: products[0].id, quantity: 2, unit: 'kg', listId: list1.id } }) // Domates + await prisma.listItem.create({ data: { productId: products[1].id, quantity: 1, unit: 'kg', listId: list1.id } }) // Soğan + await prisma.listItem.create({ data: { productId: products[2].id, quantity: 3, unit: 'adet', listId: list1.id, isPurchased: true } }) // Salatalık + await prisma.listItem.create({ data: { productId: products[3].id, quantity: 1, unit: 'kg', listId: list1.id } }) // Tavuk + await prisma.listItem.create({ data: { productId: products[4].id, quantity: 0.5, unit: 'kg', listId: list1.id } }) // Kıyma + await prisma.listItem.create({ data: { productId: products[6].id, quantity: 2, unit: 'L', listId: list1.id, isPurchased: true } }) // Süt + await prisma.listItem.create({ data: { productId: products[7].id, quantity: 4, unit: 'adet', listId: list1.id } }) // Yoğurt + await prisma.listItem.create({ data: { productId: products[8].id, quantity: 2, unit: 'adet', listId: list1.id } }) // Ekmek + await prisma.listItem.create({ data: { productId: products[9].id, quantity: 6, unit: 'adet', listId: list1.id } }) // Cola + await prisma.listItem.create({ data: { productId: products[10].id, quantity: 1, unit: 'adet', listId: list1.id, isPurchased: true } }) // Deterjan - const shoppingList = await prisma.shoppingList.create({ - data: { - name: mehmetShoppingList.name, - description: mehmetShoppingList.description, - color: mehmetShoppingList.color, - ownerId: mehmetUser.id, - }, - }); + console.log('✅ İlk liste oluşturuldu') - for (const itemData of mehmetShoppingList.items) { - let productId = null; - - if (itemData.productName) { - const product = await prisma.product.findFirst({ - where: { name: itemData.productName } - }); - if (product) { - productId = product.id; - } - } - - await prisma.listItem.create({ - data: { - listId: shoppingList.id, - productId: productId, - quantity: itemData.quantity, - unit: itemData.unit, - price: itemData.price, - }, - }); + // İkinci liste + const list2 = await prisma.shoppingList.create({ + data: { + name: 'Bayram Alışverişi', + description: 'Bayram için gerekli malzemeler', + color: '#E91E63', + ownerId: user1.id, } + }) - console.log('✅ Alışveriş listesi oluşturuldu:', shoppingList.name); - } + await prisma.listItem.create({ data: { productId: products[5].id, quantity: 2, unit: 'kg', listId: list2.id } }) // Kırmızı Et + await prisma.listItem.create({ data: { productId: products[11].id, quantity: 1, unit: 'kg', listId: list2.id } }) // Baklava + await prisma.listItem.create({ data: { productId: products[12].id, quantity: 2, unit: 'L', listId: list2.id } }) // Dondurma - console.log('✅ Tüm örnek alışveriş listeleri oluşturuldu'); + console.log('✅ İkinci liste oluşturuldu') - // Sistem ayarları - const settings = [ - { key: 'app_name', value: 'HMarket', type: 'string' }, - { key: 'app_version', value: '1.0.0', type: 'string' }, - { key: 'max_list_members', value: '10', type: 'number' }, - { key: 'enable_notifications', value: 'true', type: 'boolean' }, - { key: 'default_currency', value: 'TL', type: 'string' }, - ]; - - for (const settingData of settings) { - await prisma.setting.upsert({ - where: { key: settingData.key }, - update: { value: settingData.value }, - create: settingData, - }); - console.log('✅ Ayar oluşturuldu:', settingData.key); - } - - console.log('🎉 Seed işlemi tamamlandı!'); + console.log('\n🎉 Örnek veriler başarıyla eklendi!') + console.log(` - 3 kullanıcı`) + console.log(` - 8 kategori`) + console.log(` - ${await prisma.product.count()} ürün`) + console.log(` - 2 liste`) + console.log(` - 13 liste öğesi`) } main() .catch((e) => { - console.error('❌ Seed işlemi sırasında hata:', e); - process.exit(1); + console.error(e) + process.exit(1) }) .finally(async () => { - await prisma.$disconnect(); - }); \ No newline at end of file + await prisma.$disconnect() + }) diff --git a/backend/src/config/passport.js b/backend/src/config/passport.js index c7144c7..d40cebd 100644 --- a/backend/src/config/passport.js +++ b/backend/src/config/passport.js @@ -1,5 +1,4 @@ const passport = require('passport'); -const GoogleStrategy = require('passport-google-oauth20').Strategy; const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient(); @@ -32,67 +31,71 @@ passport.deserializeUser(async (id, done) => { } }); -// Google OAuth Strategy -passport.use(new GoogleStrategy({ - clientID: process.env.GOOGLE_CLIENT_ID, - clientSecret: process.env.GOOGLE_CLIENT_SECRET, - callbackURL: process.env.GOOGLE_CALLBACK_URL || "/api/auth/google/callback" -}, async (accessToken, refreshToken, profile, done) => { - try { - // Önce Google ID ile kullanıcı ara - let user = await prisma.user.findUnique({ - where: { googleId: profile.id } - }); - - if (user) { - // Kullanıcı zaten var, son giriş tarihini güncelle - user = await prisma.user.update({ - where: { id: user.id }, - data: { lastLoginAt: new Date() } +// Google OAuth Strategy - Sadece credentials varsa yükle +if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { + const GoogleStrategy = require('passport-google-oauth20').Strategy; + + passport.use(new GoogleStrategy({ + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: process.env.GOOGLE_CALLBACK_URL || "/api/auth/google/callback" + }, async (accessToken, refreshToken, profile, done) => { + try { + // Önce Google ID ile kullanıcı ara + let user = await prisma.user.findUnique({ + where: { googleId: profile.id } }); - return done(null, user); - } - // Email ile kullanıcı ara (mevcut hesap varsa bağla) - user = await prisma.user.findUnique({ - where: { email: profile.emails[0].value } - }); + if (user) { + // Kullanıcı zaten var, son giriş tarihini güncelle + user = await prisma.user.update({ + where: { id: user.id }, + data: { lastLoginAt: new Date() } + }); + return done(null, user); + } - if (user) { - // Mevcut hesabı Google ile bağla - user = await prisma.user.update({ - where: { id: user.id }, + // Email ile kullanıcı ara (mevcut hesap varsa bağla) + user = await prisma.user.findUnique({ + where: { email: profile.emails[0].value } + }); + + if (user) { + // Mevcut hesabı Google ile bağla + user = await prisma.user.update({ + where: { id: user.id }, + data: { + googleId: profile.id, + authProvider: 'google', + avatar: profile.photos[0]?.value || user.avatar, + lastLoginAt: new Date() + } + }); + return done(null, user); + } + + // Yeni kullanıcı oluştur + const username = profile.emails[0].value.split('@')[0] + '_' + Math.random().toString(36).substr(2, 4); + + user = await prisma.user.create({ data: { + email: profile.emails[0].value, + username: username, + firstName: profile.name.givenName || '', + lastName: profile.name.familyName || '', googleId: profile.id, authProvider: 'google', - avatar: profile.photos[0]?.value || user.avatar, + avatar: profile.photos[0]?.value, lastLoginAt: new Date() } }); + return done(null, user); + } catch (error) { + console.error('Google OAuth Error:', error); + return done(error, null); } - - // Yeni kullanıcı oluştur - const username = profile.emails[0].value.split('@')[0] + '_' + Math.random().toString(36).substr(2, 4); - - user = await prisma.user.create({ - data: { - email: profile.emails[0].value, - username: username, - firstName: profile.name.givenName || '', - lastName: profile.name.familyName || '', - googleId: profile.id, - authProvider: 'google', - avatar: profile.photos[0]?.value, - lastLoginAt: new Date() - } - }); - - return done(null, user); - } catch (error) { - console.error('Google OAuth Error:', error); - return done(error, null); - } -})); + })); +} module.exports = passport; \ No newline at end of file diff --git a/backend/src/routes/items.js b/backend/src/routes/items.js index 265c4e0..c540412 100644 --- a/backend/src/routes/items.js +++ b/backend/src/routes/items.js @@ -1,5 +1,4 @@ const express = require('express'); -const { PrismaClient } = require('@prisma/client'); const { authenticateToken, checkListMembership, requireListEditPermission } = require('../middleware/auth'); const { asyncHandler } = require('../middleware/errorHandler'); const { @@ -15,7 +14,6 @@ const { successResponse, errorResponse, calculatePagination, createPaginationMet const notificationService = require('../services/notificationService'); const router = express.Router(); -const prisma = new PrismaClient(); /** * Liste öğelerini getir @@ -68,8 +66,8 @@ router.get('/:listId', // Toplam sayı ve öğeleri getir const [total, items] = await Promise.all([ - prisma.listItem.count({ where }), - prisma.listItem.findMany({ + req.prisma.listItem.count({ where }), + req.prisma.listItem.findMany({ where, skip, take, @@ -116,7 +114,7 @@ router.get('/:listId/:itemId', const { listId, itemId } = req.params; - const item = await prisma.listItem.findFirst({ + const item = await req.prisma.listItem.findFirst({ where: { id: itemId, listId @@ -179,7 +177,7 @@ router.post('/:listId', // Eğer productId verilmişse, ürünün var olduğunu kontrol et let product = null; if (productId) { - product = await prisma.product.findUnique({ + product = await req.prisma.product.findUnique({ where: { id: productId } }); @@ -189,7 +187,7 @@ router.post('/:listId', } // Aynı öğenin listede zaten var olup olmadığını kontrol et - const existingItem = await prisma.listItem.findFirst({ + const existingItem = await req.prisma.listItem.findFirst({ where: { listId, OR: [ @@ -204,7 +202,7 @@ router.post('/:listId', } // Yeni öğe oluştur - const newItem = await prisma.listItem.create({ + const newItem = await req.prisma.listItem.create({ data: { customName: productId ? product.name : name, quantity, @@ -226,26 +224,26 @@ router.post('/:listId', // Ürün kullanım sayısını artır (Product modelinde usageCount alanı yok, bu özellik kaldırıldı) // Liste güncelleme tarihini güncelle - await prisma.shoppingList.update({ + await req.prisma.shoppingList.update({ where: { id: listId }, data: { updatedAt: new Date() } }); - // Aktivite kaydı oluştur - await prisma.activity.create({ - data: { - action: 'item_added', - details: { - itemId: newItem.id, - itemName: newItem.customName || newItem.product?.name || 'Öğe', - userName: `${req.user.firstName} ${req.user.lastName}` - }, - userId, - listId - } - }); + // Aktivite kaydı oluştur (geçici olarak devre dışı) + // await req.prisma.activity.create({ + // data: { + // action: 'item_added', + // details: { + // itemId: newItem.id, + // itemName: newItem.customName || newItem.product?.name || 'Öğe', + // userName: `${req.user.firstName} ${req.user.lastName}` + // }, + // userId, + // listId + // } + // }); - // Liste üyelerine bildirim gönder (geçici olarak devre dışı - notifyListMembers fonksiyonu mevcut değil) + // Liste üyelerine bildirim gönder (geçici olarak devre dışı) // await notificationService.notifyListMembers( // listId, // userId, @@ -254,14 +252,14 @@ router.post('/:listId', // { itemId: newItem.id, itemName: newItem.customName || newItem.product?.name } // ); - // Socket.IO ile gerçek zamanlı güncelleme - const io = req.app.get('io'); - if (io) { - io.to(`list_${listId}`).emit('itemAdded', { - item: newItem, - addedBy: req.user - }); - } + // Socket.IO ile gerçek zamanlı güncelleme (geçici olarak devre dışı) + // const io = req.app.get('io'); + // if (io) { + // io.to(`list_${listId}`).emit('itemAdded', { + // item: newItem, + // addedBy: req.user + // }); + // } // Priority değerini string'e çevir const newItemWithStringPriority = { @@ -321,7 +319,7 @@ router.put('/:listId/:itemId', // Öğenin var olduğunu kontrol et console.log('🔍 Öğe aranıyor:', { itemId, listId }); - const existingItem = await prisma.listItem.findFirst({ + const existingItem = await req.prisma.listItem.findFirst({ where: { id: itemId, listId @@ -364,7 +362,7 @@ router.put('/:listId/:itemId', } // Öğeyi güncelle - const updatedItem = await prisma.listItem.update({ + const updatedItem = await req.prisma.listItem.update({ where: { id: itemId }, data: updateData, include: { @@ -378,7 +376,7 @@ router.put('/:listId/:itemId', // Fiyat geçmişi ekle (eğer fiyat girilmişse ve ürün varsa) if (price && existingItem.productId && isPurchased) { - await prisma.priceHistory.create({ + await req.prisma.priceHistory.create({ data: { price: price, productId: existingItem.productId, @@ -389,7 +387,7 @@ router.put('/:listId/:itemId', } // Liste güncelleme tarihini güncelle - await prisma.shoppingList.update({ + await req.prisma.shoppingList.update({ where: { id: listId }, data: { updatedAt: new Date() } }); @@ -402,7 +400,7 @@ router.put('/:listId/:itemId', : `${req.user.firstName} ${req.user.lastName} "${updatedItem.name}" öğesinin satın alma durumunu iptal etti`; } - await prisma.activity.create({ + await req.prisma.activity.create({ data: { action: isPurchased !== undefined ? (isPurchased ? 'ITEM_PURCHASED' : 'ITEM_UNPURCHASED') : 'ITEM_UPDATED', details: { @@ -467,7 +465,7 @@ router.delete('/:listId/:itemId', const userId = req.user.id; // Öğenin var olduğunu kontrol et - const existingItem = await prisma.listItem.findFirst({ + const existingItem = await req.prisma.listItem.findFirst({ where: { id: itemId, listId @@ -479,19 +477,19 @@ router.delete('/:listId/:itemId', } // Öğeyi sil - await prisma.listItem.delete({ + await req.prisma.listItem.delete({ where: { id: itemId } }); // Liste güncelleme tarihini güncelle - await prisma.shoppingList.update({ + await req.prisma.shoppingList.update({ where: { id: listId }, data: { updatedAt: new Date() } }); // Aktivite kaydı oluştur (Prisma şemasına uygun) const itemName = existingItem.customName || existingItem.product?.name || 'Öğe'; - await prisma.activity.create({ + await req.prisma.activity.create({ data: { action: 'ITEM_REMOVED', details: { @@ -551,7 +549,7 @@ router.patch('/:listId/bulk', } // Öğelerin var olduğunu kontrol et - const existingItems = await prisma.listItem.findMany({ + const existingItems = await req.prisma.listItem.findMany({ where: { id: { in: items }, listId @@ -593,14 +591,14 @@ router.patch('/:listId/bulk', // Toplu güncelleme veya silme if (action === 'delete') { - await prisma.listItem.deleteMany({ + await req.prisma.listItem.deleteMany({ where: { id: { in: items }, listId } }); } else { - await prisma.listItem.updateMany({ + await req.prisma.listItem.updateMany({ where: { id: { in: items }, listId @@ -610,13 +608,13 @@ router.patch('/:listId/bulk', } // Liste güncelleme tarihini güncelle - await prisma.shoppingList.update({ + await req.prisma.shoppingList.update({ where: { id: listId }, data: { updatedAt: new Date() } }); // Aktivite kaydı oluştur - await prisma.activity.create({ + await req.prisma.activity.create({ data: { type: activityType, description: activityDescription, diff --git a/backend/src/routes/lists.js b/backend/src/routes/lists.js index 7d1f9ba..5d9cdba 100644 --- a/backend/src/routes/lists.js +++ b/backend/src/routes/lists.js @@ -198,23 +198,25 @@ router.post('/', authenticateToken, [ } }); - // Aktivite kaydı oluştur - await req.prisma.activity.create({ - data: { - listId: list.id, - userId: req.user.id, - action: 'list_created', - details: { - listName: list.name - } - } - }); + // Aktivite kaydı oluştur (geçici olarak devre dışı) + // await req.prisma.activity.create({ + // data: { + // listId: list.id, + // userId: req.user.id, + // action: 'list_created', + // details: { + // listName: list.name + // } + // } + // }); - // Socket.IO ile gerçek zamanlı bildirim gönder - req.io.emit('list_created', { - list: { ...list, userRole: 'owner' }, - user: req.user - }); + // Socket.IO ile gerçek zamanlı bildirim gönder (geçici olarak devre dışı) + // if (req.io) { + // req.io.emit('list_created', { + // list: { ...list, userRole: 'owner' }, + // user: req.user + // }); + // } res.status(201).json({ success: true, diff --git a/backend/src/server.js b/backend/src/server.js index 2b2dc32..e930aba 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -148,7 +148,7 @@ process.on('SIGINT', async () => { }); // Sunucuyu başlat -const PORT = process.env.PORT || 5000; +const PORT = process.env.PORT || 7001; server.listen(PORT, () => { console.log(`🚀 Server ${PORT} portunda çalışıyor - port 7001`); diff --git a/backend/src/services/notificationService.js b/backend/src/services/notificationService.js index 054c039..19cb1d3 100644 --- a/backend/src/services/notificationService.js +++ b/backend/src/services/notificationService.js @@ -9,6 +9,16 @@ class NotificationService { initializeFirebase() { try { + // Firebase credentials kontrolü + const hasCredentials = process.env.FIREBASE_PROJECT_ID && + process.env.FIREBASE_PRIVATE_KEY && + process.env.FIREBASE_CLIENT_EMAIL; + + if (!hasCredentials) { + console.log('⚠️ Firebase credentials not configured. Push notifications disabled.'); + return; + } + if (!admin.apps.length) { const serviceAccount = { type: process.env.FIREBASE_TYPE, @@ -27,10 +37,11 @@ class NotificationService { credential: admin.credential.cert(serviceAccount) }); - console.log('Firebase Admin SDK initialized successfully'); + console.log('✅ Firebase Admin SDK initialized successfully'); } } catch (error) { - console.error('Firebase initialization error:', error); + console.error('Firebase initialization error:', error.message); + console.warn('Push notifications will be disabled.'); } } diff --git a/backend/src/utils/dbCharsetSetup.js b/backend/src/utils/dbCharsetSetup.js index 0fa78dc..d68618b 100644 --- a/backend/src/utils/dbCharsetSetup.js +++ b/backend/src/utils/dbCharsetSetup.js @@ -16,13 +16,20 @@ function getDatabaseName(databaseUrl) { async function setupUtf8mb4(prisma) { const databaseUrl = process.env.DATABASE_URL || ''; + + // SQLite için bu fonksiyonu atla + if (databaseUrl.includes('sqlite') || databaseUrl.includes('.db')) { + console.log('ℹ️ SQLite kullanılıyor. UTF8MB4 kurulumu atlandı.'); + return; + } + const dbName = getDatabaseName(databaseUrl); if (!dbName) { console.warn('⚠️ DATABASE_URL bulunamadı veya veritabanı adı çözümlenemedi. UTF8MB4 kurulumu atlandı.'); return; } - // Try altering database charset/collation + // Only run for MySQL/MariaDB try { await prisma.$executeRawUnsafe(`ALTER DATABASE \`${dbName}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`); console.log('✅ Veritabanı varsayılan karakter seti/collation utf8mb4 olarak ayarlandı.'); @@ -30,7 +37,6 @@ async function setupUtf8mb4(prisma) { console.warn('⚠️ Veritabanı charset ayarlanırken hata oluştu:', err?.message || err); } - // Try converting categories table try { await prisma.$executeRawUnsafe('ALTER TABLE `categories` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'); console.log('✅ `categories` tablosu utf8mb4 olarak dönüştürüldü.'); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 34e7ccc..20c5a62 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15610,19 +15610,6 @@ } } }, - "node_modules/tailwindcss/node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", diff --git a/generate_logo.py b/generate_logo.py deleted file mode 100644 index a30ff15..0000000 --- a/generate_logo.py +++ /dev/null @@ -1,63 +0,0 @@ -from PIL import Image, ImageDraw, ImageFilter - -def create_hmarket_logo(path, size=512): - # Create image with transparent background - img = Image.new('RGBA', (size, size), (255, 255, 255, 0)) - draw = ImageDraw.Draw(img) - - # Modern Blue/Teal Gradient simulation - # Let's draw a rounded rectangle background for the icon - padding = size // 8 - rect_shape = [padding, padding, size - padding, size - padding] - corner_radius = size // 6 - - # Draw shadow - shadow_offset = size // 40 - draw.rounded_rectangle( - [padding + shadow_offset, padding + shadow_offset, size - padding + shadow_offset, size - padding + shadow_offset], - radius=corner_radius, - fill=(0, 0, 0, 40) - ) - - # Draw main background circle/rounded rect with gradient - for i in range(size - 2*padding): - # Color from #3a7bd5 to #00d2ff - r = int(58 + (0 - 58) * (i / (size - 2*padding))) - g = int(123 + (210 - 123) * (i / (size - 2*padding))) - b = int(213 + (255 - 213) * (i / (size - 2*padding))) - draw.line([padding + i, padding, padding + i, size - padding], fill=(r, g, b, 255)) - - # Redraw rounded corners mask (to make it look like a rounded rect icon) - mask = Image.new('L', (size, size), 0) - mask_draw = ImageDraw.Draw(mask) - mask_draw.rounded_rectangle(rect_shape, radius=corner_radius, fill=255) - - # Apply mask to gradient - output = Image.new('RGBA', (size, size), (255, 255, 255, 0)) - output.paste(img, (0, 0), mask=mask) - - # Draw Shopping Cart Icon (Simplified white vector style) - draw_icon = ImageDraw.Draw(output) - s = size // 10 # scale factor - - # Cart body - cart_color = (255, 255, 255, 255) - # Handle and frame - draw_icon.line([3*s, 3*s, 3.5*s, 3*s, 4*s, 6*s, 7.5*s, 6*s], fill=cart_color, width=s//3) - # Basket - draw_icon.polygon([4*s, 4*s, 7.5*s, 4*s, 7*s, 5.5*s, 4.5*s, 5.5*s], fill=cart_color) - # Wheels - draw_icon.ellipse([4.2*s, 6.2*s, 4.8*s, 6.8*s], fill=cart_color) - draw_icon.ellipse([6.7*s, 6.2*s, 7.3*s, 6.8*s], fill=cart_color) - - # Save - output.save(path) - return output - -# Generate both sizes -logo512 = create_hmarket_logo("/home/hololu/calismalar/hMarket/frontend/public/logo512.png", 512) -logo192 = logo512.resize((192, 192), Image.LANCZOS) -logo192.save("/home/hololu/calismalar/hMarket/frontend/public/logo192.png") -logo512.save("/home/hololu/calismalar/hmarket-logo.png") # Extra copy for OAuth screen - -print("Logolar başarıyla oluşturuldu.") diff --git a/generate_logo_orange.py b/generate_logo_orange.py deleted file mode 100644 index e9a4462..0000000 --- a/generate_logo_orange.py +++ /dev/null @@ -1,60 +0,0 @@ -from PIL import Image, ImageDraw - -def create_orange_logo(path, size=512): - # Base image - img = Image.new('RGBA', (size, size), (255, 255, 255, 0)) - draw = ImageDraw.Draw(img) - - # Background color #FF5722 - bg_color = (255, 87, 34, 255) - - padding = size // 10 - corner_radius = size // 6 - draw.rounded_rectangle( - [padding, padding, size - padding, size - padding], - radius=corner_radius, - fill=bg_color - ) - - # Scale for the 24x24 SVG path - # We want the icon to fit in the center, say 60% of the box - icon_size = size * 0.55 - offset = (size - icon_size) / 2 - scale = icon_size / 24 - - def scale_pt(x, y): - return (offset + x * scale, offset + y * scale) - - # Drawing the cart manually based on the SVG path logic - # Wheels - r_wheel = 1 * scale - w1_c = scale_pt(7, 20) - draw.ellipse([w1_c[0]-r_wheel, w1_c[1]-r_wheel, w1_c[0]+r_wheel, w1_c[1]+r_wheel], fill="white") - - w2_c = scale_pt(17, 20) - draw.ellipse([w2_c[0]-r_wheel, w2_c[1]-r_wheel, w2_c[0]+r_wheel, w2_c[1]+r_wheel], fill="white") - - # The main body path - # M1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2z - # Simplified polygon for the cart body - points = [ - scale_pt(1, 2), scale_pt(1, 4), scale_pt(3, 4), - scale_pt(6.6, 11.59), scale_pt(5.25, 14.04), - scale_pt(5.25, 15), scale_pt(7, 17), scale_pt(19, 17), - scale_pt(19, 15), scale_pt(7.42, 15), scale_pt(8.35, 13.37), - scale_pt(15.8, 13.37), scale_pt(17.55, 12.34), scale_pt(21.13, 5.85), - scale_pt(21.25, 5.37), scale_pt(20.25, 4.37), scale_pt(5.21, 4.37), - scale_pt(4.27, 2.37) - ] - draw.polygon(points, fill="white") - - # Save - img.save(path) - -create_orange_logo("/home/hololu/calismalar/hMarket/frontend/public/logo512.png", 512) -create_orange_logo("/home/hololu/calismalar/hmarket-logo.png", 512) -img_512 = Image.open("/home/hololu/calismalar/hMarket/frontend/public/logo512.png") -img_192 = img_512.resize((192, 192), Image.LANCZOS) -img_192.save("/home/hololu/calismalar/hMarket/frontend/public/logo192.png") - -print("Yeni turuncu logolar oluşturuldu.") diff --git a/login.json b/login.json deleted file mode 100644 index c930974..0000000 --- a/login.json +++ /dev/null @@ -1 +0,0 @@ -{"email":"admin@hmarket.com","password":"admin123"} \ No newline at end of file diff --git a/logo_temp.html b/logo_temp.html deleted file mode 100644 index 36a31a0..0000000 --- a/logo_temp.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - -
- - - -
- - diff --git a/test-login.json b/test-login.json deleted file mode 100644 index 8ab1bc2..0000000 --- a/test-login.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "login": "ahmet@test.com", - "password": "test123" -} \ No newline at end of file