diff --git a/adapters/requests/dealCreate.adapter.js b/adapters/requests/dealCreate.adapter.js new file mode 100644 index 0000000..20fe308 --- /dev/null +++ b/adapters/requests/dealCreate.adapter.js @@ -0,0 +1,32 @@ +function mapCreateDealRequestToDealCreateData( + data, + userId +) { + return { + title: data.title, + description: data.description ?? null, + url: data.url ?? null, + price: data.price ?? null, + + // 🔑 adapter burada seller’ı “custom” gibi yazar + // service bunu düzeltecek + customCompany: data.sellerName, + + user: { + connect: { id: userId }, + }, + + images: data.images?.length + ? { + create: data.images.map((imgUrl, index) => ({ + imageUrl: imgUrl, + order: index, + })), + } + : undefined, + } +} + +module.exports = { + mapCreateDealRequestToDealCreateData, +} diff --git a/adapters/responses/dealCard.adapter.js b/adapters/responses/dealCard.adapter.js new file mode 100644 index 0000000..7416cad --- /dev/null +++ b/adapters/responses/dealCard.adapter.js @@ -0,0 +1,34 @@ +function mapDealToDealCardResponse(deal) { + return { + id: deal.id, + title: deal.title, + description: deal.description || "", + price: deal.price ?? null, + + score: deal.score, + commentsCount: deal._count?.comments ?? 0, + + status: deal.status, + saleType: deal.saletype, + affiliateType: deal.affiliateType, + + createdAt: deal.createdAt, + updatedAt: deal.updatedAt, + + user: { + id: deal.user.id, + username: deal.user.username, + avatarUrl: deal.user.avatarUrl ?? null, + }, + + seller: deal.company + ? { name: deal.company.name, + url:deal.company.url + } + : { name: deal.customCompany || "" }, + + imageUrl: deal.images?.[0]?.imageUrl || "", + } +} + +module.exports = { mapDealToDealCardResponse } diff --git a/adapters/responses/dealDetail.adapter.js b/adapters/responses/dealDetail.adapter.js new file mode 100644 index 0000000..fae0e6e --- /dev/null +++ b/adapters/responses/dealDetail.adapter.js @@ -0,0 +1,50 @@ +function mapDealToDealDetailResponse(deal) { + return { + id: deal.id, + title: deal.title, + description: deal.description || "", + url: deal.url ?? null, + price: deal.price ?? null, + score: deal.score, + + commentsCount: deal._count?.comments ?? 0, + + status: deal.status, + saleType: deal.saletype, + affiliateType: deal.affiliateType, + + createdAt: deal.createdAt, + updatedAt: deal.updatedAt, + + user: { + id: deal.user.id, + username: deal.user.username, + avatarUrl: deal.user.avatarUrl ?? null, + }, + + seller: deal.company + ? { id: deal.company.id, name: deal.company.name } + : { name: deal.customCompany || "Bilinmiyor" }, + + images: deal.images.map((img) => ({ + id: img.id, + imageUrl: img.imageUrl, + order: img.order, + })), + + comments: deal.comments.map((comment) => ({ + id: comment.id, + text: comment.text, + createdAt: comment.createdAt, + user: { + id: comment.user.id, + username: comment.user.username, + avatarUrl: comment.user.avatarUrl ?? null, + }, + })), + } +} + +module.exports = { + mapDealToDealDetailResponse, +} diff --git a/db/seller.db.js b/db/seller.db.js new file mode 100644 index 0000000..9efb39f --- /dev/null +++ b/db/seller.db.js @@ -0,0 +1,28 @@ +const { PrismaClient } = require("@prisma/client") +const prisma = new PrismaClient() + +async function findCompany(where, options = {}) { + return prisma.company.findFirst({ + where, + include: options.include || undefined, + select: options.select || undefined, + }) +} + +async function findCompanyByDomain(domain) { + return prisma.company.findFirst({ + where: { + domains: { + some: { + domain: domain, + }, + }, + }, + }) +} + + +module.exports = { + findCompany, + findCompanyByDomain, +} diff --git a/package-lock.json b/package-lock.json index cb09741..7bf8604 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,9 +70,9 @@ } }, "node_modules/@prisma/client": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", - "integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", + "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", "hasInstallScript": true, "license": "Apache-2.0", "engines": { @@ -92,9 +92,9 @@ } }, "node_modules/@prisma/config": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", - "integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", + "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -105,145 +105,146 @@ } }, "node_modules/@prisma/debug": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", - "integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", + "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", - "integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", + "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.18.0", - "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "@prisma/fetch-engine": "6.18.0", - "@prisma/get-platform": "6.18.0" + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.2", + "@prisma/get-platform": "6.19.2" } }, "node_modules/@prisma/engines-version": { - "version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", - "integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", - "integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", + "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.18.0", - "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", - "@prisma/get-platform": "6.18.0" + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.2" } }, "node_modules/@prisma/get-platform": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", - "integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", + "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.18.0" + "@prisma/debug": "6.19.2" } }, "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "devOptional": true, "license": "MIT" }, "node_modules/@supabase/auth-js": { - "version": "2.78.0", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.78.0.tgz", - "integrity": "sha512-cXDtu1U0LeZj/xfnFoV7yCze37TcbNo8FCxy1FpqhMbB9u9QxxDSW6pA5gm/07Ei7m260Lof4CZx67Cu6DPeig==", + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.90.1.tgz", + "integrity": "sha512-vxb66dgo6h3yyPbR06735Ps+dK3hj0JwS8w9fdQPVZQmocSTlKUW5MfxSy99mN0XqCCuLMQ3jCEiIIUU23e9ng==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "2.6.15", "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/functions-js": { - "version": "2.78.0", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.78.0.tgz", - "integrity": "sha512-t1jOvArBsOINyqaRee1xJ3gryXLvkBzqnKfi6q3YRzzhJbGS6eXz0pXR5fqmJeB01fLC+1njpf3YhMszdPEF7g==", + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.90.1.tgz", + "integrity": "sha512-x9mV9dF1Lam9qL3zlpP6mSM5C9iqMPtF5B/tU1Jj/F0ufX5mjDf9ghVBaErVxmrQJRL4+iMKWKY2GnODkpS8tw==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "2.6.15", "tslib": "2.8.1" - } - }, - "node_modules/@supabase/node-fetch": { - "version": "2.6.15", - "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", - "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" }, "engines": { - "node": "4.x || >=6.0.0" + "node": ">=20.0.0" } }, "node_modules/@supabase/postgrest-js": { - "version": "2.78.0", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.78.0.tgz", - "integrity": "sha512-AwhpYlSvJ+PSnPmIK8sHj7NGDyDENYfQGKrMtpVIEzQA2ApUjgpUGxzXWN4Z0wEtLQsvv7g4y9HVad9Hzo1TNA==", + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.90.1.tgz", + "integrity": "sha512-jh6vqzaYzoFn3raaC0hcFt9h+Bt+uxNRBSdc7PfToQeRGk7PDPoweHsbdiPWREtDVTGKfu+PyPW9e2jbK+BCgQ==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "2.6.15", "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/realtime-js": { - "version": "2.78.0", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.78.0.tgz", - "integrity": "sha512-rCs1zmLe7of7hj4s7G9z8rTqzWuNVtmwDr3FiCRCJFawEoa+RQO1xpZGbdeuVvVmKDyVN6b542Okci+117y/LQ==", + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.90.1.tgz", + "integrity": "sha512-PWbnEMkcQRuor8jhObp4+Snufkq8C6fBp+MchVp2qBPY1NXk/c3Iv3YyiFYVzo0Dzuw4nAlT4+ahuPggy4r32w==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "2.6.15", "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/storage-js": { - "version": "2.78.0", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.78.0.tgz", - "integrity": "sha512-n17P0JbjHOlxqJpkaGFOn97i3EusEKPEbWOpuk1r4t00Wg06B8Z4GUiq0O0n1vUpjiMgJUkLIMuBVp+bEgunzQ==", + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.90.1.tgz", + "integrity": "sha512-GHY+Ps/K/RBfRj7kwx+iVf2HIdqOS43rM2iDOIDpapyUnGA9CCBFzFV/XvfzznGykd//z2dkGZhlZZprsVFqGg==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "2.6.15", + "iceberg-js": "^0.8.1", "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@supabase/supabase-js": { - "version": "2.78.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.78.0.tgz", - "integrity": "sha512-xYMRNBFmKp2m1gMuwcp/gr/HlfZKqjye1Ib8kJe29XJNsgwsfO/f8skxnWiscFKTlkOKLuBexNgl5L8dzGt6vA==", + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.90.1.tgz", + "integrity": "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w==", "license": "MIT", "dependencies": { - "@supabase/auth-js": "2.78.0", - "@supabase/functions-js": "2.78.0", - "@supabase/node-fetch": "2.6.15", - "@supabase/postgrest-js": "2.78.0", - "@supabase/realtime-js": "2.78.0", - "@supabase/storage-js": "2.78.0" + "@supabase/auth-js": "2.90.1", + "@supabase/functions-js": "2.90.1", + "@supabase/postgrest-js": "2.90.1", + "@supabase/realtime-js": "2.90.1", + "@supabase/storage-js": "2.90.1" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", "dev": true, "license": "MIT" }, @@ -300,21 +301,21 @@ } }, "node_modules/@types/express": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", - "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^1" + "@types/serve-static": "^2" } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", - "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "dev": true, "license": "MIT", "dependencies": { @@ -331,26 +332,19 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { - "version": "24.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", - "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@types/phoenix": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", - "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", + "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", "license": "MIT" }, "node_modules/@types/qs": { @@ -378,25 +372,13 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", - "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/serve-static/node_modules/@types/send": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", - "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -478,23 +460,6 @@ "node": ">=0.4.0" } }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -525,32 +490,36 @@ "license": "MIT" }, "node_modules/bcryptjs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", - "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", "license": "BSD-3-Clause", "bin": { "bcrypt": "bin/bcrypt" } }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", - "debug": "^4.4.0", + "debug": "^4.4.3", "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/buffer-equal-constant-time": { @@ -749,15 +718,16 @@ } }, "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -851,32 +821,30 @@ } }, "node_modules/dependency-cruiser": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-17.2.0.tgz", - "integrity": "sha512-EiOkB41fdAOCef3ycMX4pAXqkWrCOQ+G4IKOjcs9uPILShAuki2fHlTQPnv4aIHka/3lGJJdpRVrrwTMD7v1dg==", + "version": "17.3.6", + "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-17.3.6.tgz", + "integrity": "sha512-k0mXZaNvR2hZC0Dh2y4NGZZGqWLM6AbxQXmKL0T2m+KRE19nK5gAhm+PdV7f1L+AhdwwSOnebok7C6P27l2xXA==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "acorn-jsx-walk": "^2.0.0", - "acorn-loose": "^8.5.2", - "acorn-walk": "^8.3.4", - "ajv": "^8.17.1", - "commander": "^14.0.2", - "enhanced-resolve": "^5.18.3", - "ignore": "^7.0.5", - "interpret": "^3.1.1", - "is-installed-globally": "^1.0.0", - "json5": "^2.2.3", - "memoize": "^10.2.0", - "picomatch": "^4.0.3", - "prompts": "^2.4.2", - "rechoir": "^0.8.0", - "safe-regex": "^2.1.1", - "semver": "^7.7.3", - "tsconfig-paths-webpack-plugin": "^4.2.0", - "watskeburt": "^4.2.3" + "acorn": "8.15.0", + "acorn-jsx": "5.3.2", + "acorn-jsx-walk": "2.0.0", + "acorn-loose": "8.5.2", + "acorn-walk": "8.3.4", + "commander": "14.0.2", + "enhanced-resolve": "5.18.4", + "ignore": "7.0.5", + "interpret": "3.1.1", + "is-installed-globally": "1.0.0", + "json5": "2.2.3", + "picomatch": "4.0.3", + "prompts": "2.4.2", + "rechoir": "0.8.0", + "safe-regex": "2.1.1", + "semver": "7.7.3", + "tsconfig-paths-webpack-plugin": "4.2.0", + "watskeburt": "5.0.0" }, "bin": { "depcruise": "bin/dependency-cruise.mjs", @@ -980,9 +948,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1039,18 +1007,19 @@ } }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -1081,9 +1050,9 @@ } }, "node_modules/exsolve": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", - "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "devOptional": true, "license": "MIT" }, @@ -1110,34 +1079,10 @@ "node": ">=8.0.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -1148,7 +1093,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/forwarded": { @@ -1303,40 +1252,48 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=20.0.0" } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -1446,13 +1403,6 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -1467,12 +1417,12 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { - "jws": "^3.2.2", + "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", @@ -1489,9 +1439,9 @@ } }, "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -1500,12 +1450,12 @@ } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -1586,22 +1536,6 @@ "node": ">= 0.8" } }, - "node_modules/memoize": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.2.0.tgz", - "integrity": "sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/memoize?sponsor=1" - } - }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -1624,28 +1558,19 @@ } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/minimist": { @@ -1887,15 +1812,15 @@ } }, "node_modules/prisma": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.18.0.tgz", - "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz", + "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/config": "6.18.0", - "@prisma/engines": "6.18.0" + "@prisma/config": "6.19.2", + "@prisma/engines": "6.19.2" }, "bin": { "prisma": "build/index.js" @@ -1957,9 +1882,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -1981,36 +1906,20 @@ } }, "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/rc9": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", @@ -2073,16 +1982,6 @@ "regexp-tree": "bin/regexp-tree" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -2169,31 +2068,35 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -2203,6 +2106,10 @@ }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -2367,11 +2274,14 @@ } }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "devOptional": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/toidentifier": { "version": "1.0.1", @@ -2382,12 +2292,6 @@ "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -2541,32 +2445,16 @@ } }, "node_modules/watskeburt": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-4.2.3.tgz", - "integrity": "sha512-uG9qtQYoHqAsnT711nG5iZc/8M5inSmkGCOp7pFaytKG2aTfIca7p//CjiVzAE4P7hzaYuCozMjNNaLgmhbK5g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-5.0.0.tgz", + "integrity": "sha512-fEMhfIzu9WOuAJdDcTT+aPjn0JHI2+UeJ+zWSEs/tgMvc+MFDZVmhlZ8C1uJWXax1ETYc4trUnHFHyx2DrG0jQ==", "dev": true, "license": "MIT", "bin": { "watskeburt": "dist/run-cli.js" }, "engines": { - "node": "^18||>=20" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node": "^20.12||^22.13||>=24.0" } }, "node_modules/wrappy": { @@ -2576,9 +2464,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -2616,9 +2504,9 @@ } }, "node_modules/zod": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", - "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz", + "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 26793c4..13c4d71 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" + }, + "prisma": { + "seed": "node prisma/seed.js" }, "keywords": [], "author": "", diff --git a/prisma/migrations/20260118124917_add_company_and_affiliate_fields/migration.sql b/prisma/migrations/20260118124917_add_company_and_affiliate_fields/migration.sql new file mode 100644 index 0000000..00757d8 --- /dev/null +++ b/prisma/migrations/20260118124917_add_company_and_affiliate_fields/migration.sql @@ -0,0 +1,56 @@ +-- CreateEnum +CREATE TYPE "DealStatus" AS ENUM ('PENDING', 'ACTIVE', 'EXPIRED', 'REJECTED'); + +-- CreateEnum +CREATE TYPE "SaleType" AS ENUM ('ONLINE', 'OFFLINE', 'CODE'); + +-- CreateEnum +CREATE TYPE "AffiliateType" AS ENUM ('AFFILIATE', 'NON_AFFILIATE', 'USER_AFFILIATE'); + +-- AlterTable +ALTER TABLE "Deal" ADD COLUMN "affiliateType" "AffiliateType" NOT NULL DEFAULT 'NON_AFFILIATE', +ADD COLUMN "companyId" INTEGER, +ADD COLUMN "customCompany" TEXT, +ADD COLUMN "saletype" "SaleType" NOT NULL DEFAULT 'ONLINE', +ADD COLUMN "status" "DealStatus" NOT NULL DEFAULT 'PENDING'; + +-- CreateTable +CREATE TABLE "CompanyDomain" ( + "id" SERIAL NOT NULL, + "domain" TEXT NOT NULL, + "companyId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdById" INTEGER NOT NULL, + + CONSTRAINT "CompanyDomain_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Company" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "Links" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdById" INTEGER NOT NULL, + + CONSTRAINT "Company_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "CompanyDomain_domain_key" ON "CompanyDomain"("domain"); + +-- CreateIndex +CREATE UNIQUE INDEX "Company_name_key" ON "Company"("name"); + +-- AddForeignKey +ALTER TABLE "CompanyDomain" ADD CONSTRAINT "CompanyDomain_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CompanyDomain" ADD CONSTRAINT "CompanyDomain_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Company" ADD CONSTRAINT "Company_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Deal" ADD CONSTRAINT "Deal_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5bbe3ec..4b60f9c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -24,18 +24,72 @@ model User { Deal Deal[] votes DealVote[] comments Comment[] + companies Company[] + domains CompanyDomain[] } +enum DealStatus { + PENDING + ACTIVE + EXPIRED + REJECTED +} + +enum SaleType{ + ONLINE + OFFLINE + CODE +} + +enum AffiliateType{ + AFFILIATE + NON_AFFILIATE + USER_AFFILIATE +} + + model CompanyDomain { + id Int @id @default(autoincrement()) + domain String @unique + companyId Int + company Company @relation(fields: [companyId], references: [id]) + + createdAt DateTime @default(now()) + createdById Int + createdBy User @relation(fields: [createdById], references: [id]) + } + + model Company { + id Int @id @default(autoincrement()) + name String @unique + isActive Boolean @default(true) + Links String? + createdAt DateTime @default(now()) + createdById Int + + deals Deal[] + createdBy User @relation(fields: [createdById], references: [id]) + domains CompanyDomain[] + } + model Deal { id Int @id @default(autoincrement()) title String description String? url String? price Float? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + userId Int score Int @default(0) + status DealStatus @default(PENDING) + saletype SaleType @default(ONLINE) + affiliateType AffiliateType @default(NON_AFFILIATE) + + companyId Int? + customCompany String? + + company Company? @relation(fields: [companyId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id]) votes DealVote[] diff --git a/prisma/seed.js b/prisma/seed.js new file mode 100644 index 0000000..ee82bd8 --- /dev/null +++ b/prisma/seed.js @@ -0,0 +1,123 @@ +const { PrismaClient, DealStatus, SaleType, AffiliateType } = require('@prisma/client') +const bcrypt = require("bcryptjs"); + +const prisma = new PrismaClient() + +async function main() { + const password = 'test' + const hashedPassword = await bcrypt.hash(password, 10) + + // ---------- USERS ---------- + const admin = await prisma.user.upsert({ + where: { email: 'test' }, + update: {}, + create: { + username: 'test', + email: 'test', + passwordHash: hashedPassword, + }, + }) + + const user = await prisma.user.upsert({ + where: { email: 'test2' }, + update: {}, + create: { + username: 'test2', + email: 'test2', + passwordHash: hashedPassword, + }, + }) + + // ---------- COMPANY ---------- + const amazon = await prisma.company.upsert({ + where: { name: 'Amazon' }, + update: {}, + create: { + name: 'Amazon', + isActive: true, + createdById: admin.id, + }, + }) + + // ---------- COMPANY DOMAINS ---------- + const domains = ['amazon.com', 'amazon.com.tr'] + + for (const domain of domains) { + await prisma.companyDomain.upsert({ + where: { domain }, + update: {}, + create: { + domain, + companyId: amazon.id, + createdById: admin.id, + }, + }) + } + + // ---------- DEAL ---------- + const deal = await prisma.deal.create({ + data: { + title: 'Samsung SSD 1TB', + description: 'Test deal açıklaması', + url: 'https://www.amazon.com.tr/dp/test', + price: 1299.99, + status: DealStatus.ACTIVE, + saletype: SaleType.ONLINE, + affiliateType: AffiliateType.NON_AFFILIATE, + userId: user.id, + companyId: amazon.id, + }, + }) + + // ---------- DEAL IMAGES ---------- + await prisma.dealImage.createMany({ + data: [ + { + dealId: deal.id, + imageUrl: 'https://placehold.co/600x400', + order: 0, + }, + { + dealId: deal.id, + imageUrl: 'https://placehold.co/600x401', + order: 1, + }, + ], + }) + + // ---------- VOTE ---------- + await prisma.dealVote.upsert({ + where: { + dealId_userId: { + dealId: deal.id, + userId: admin.id, + }, + }, + update: {}, + create: { + dealId: deal.id, + userId: admin.id, + voteType: 'UP', + }, + }) + + // ---------- COMMENT ---------- + await prisma.comment.create({ + data: { + text: 'Gerçekten iyi fırsat', + userId: admin.id, + dealId: deal.id, + }, + }) + + console.log('✅ Seed tamamlandı') +} + +main() + .catch(err => { + console.error(err) + process.exit(1) + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/routes/account/accountSettings.routes.js b/routes/account/accountSettings.routes.js new file mode 100644 index 0000000..d88d789 --- /dev/null +++ b/routes/account/accountSettings.routes.js @@ -0,0 +1,46 @@ +const express = require("express") +const multer = require("multer") +const fs = require("fs") +const { uploadProfileImage } = require("../../services/supabase/supabaseUpload.service") +const { validateImage } = require("../../utils/validateImage") +const authMiddleware = require("../../middleware/authMiddleware") +const { getUserProfile } = require("../../services/profile/profile.service") + +const router = express.Router() +const upload = multer({ dest: "uploads/" }) + +const { updateUserAvatar } = require("../../services/account/avatar.service") + +router.post( + "/avatar", + authMiddleware, + upload.single("file"), + async (req, res) => { + try { + const updatedUser = await updateUserAvatar( + req.user.userId, + req.file + ) + + res.json({ + message: "Avatar updated", + user: updatedUser, + }) + } catch (err) { + console.error(err) + res.status(400).json({ error: err.message }) + } + } +) + + +router.get("/me", authMiddleware, async (req, res) => { + try { + const user = await getUserProfile(req.user.id) + res.json(user) + } catch (err) { + res.status(400).json({ error: err.message }) + } +}) + +module.exports = router diff --git a/routes/account/accountSettingsRoutes.js b/routes/account/accountSettingsRoutes.js deleted file mode 100644 index e1e96a6..0000000 --- a/routes/account/accountSettingsRoutes.js +++ /dev/null @@ -1,40 +0,0 @@ -const express = require("express") -const multer = require("multer") -const fs = require("fs") -const { uploadProfileImage } = require("../../services/supabase/supabaseUploadService") -const { validateImage } = require("../../utils/validateImage") -const authMiddleware = require("../../middleware/authMiddleware") -const { updateAvatarUrl, getUserProfile } = require("../../services/profile/myProfileService") - -const router = express.Router() -const upload = multer({ dest: "uploads/" }) - -router.post("/avatar", authMiddleware, upload.single("file"), async (req, res) => { - try { - const file = req.file - if (!file) return res.status(400).json({ error: "No file uploaded" }) - - validateImage({ mimetype: file.mimetype, size: file.size }) - - const data = fs.readFileSync(file.path) - const url = await uploadProfileImage(req.user.userId, { data, mimetype: file.mimetype }) - fs.unlinkSync(file.path) - - const updated = await updateAvatarUrl(req.user.userId, url) - res.json({ message: "Avatar updated", user: updated }) - } catch (err) { - console.error(err) - res.status(400).json({ error: err.message }) - } -}) - -router.get("/me", authMiddleware, async (req, res) => { - try { - const user = await getUserProfile(req.user.id) - res.json(user) - } catch (err) { - res.status(400).json({ error: err.message }) - } -}) - -module.exports = router diff --git a/routes/authRoutes.js b/routes/auth.routes.js similarity index 100% rename from routes/authRoutes.js rename to routes/auth.routes.js diff --git a/routes/deal/commentRoutes.js b/routes/deal/comment.routes.js similarity index 95% rename from routes/deal/commentRoutes.js rename to routes/deal/comment.routes.js index cd8e0d0..6d93050 100644 --- a/routes/deal/commentRoutes.js +++ b/routes/deal/comment.routes.js @@ -4,7 +4,7 @@ const { getCommentsByDealId, createComment, deleteComment, -} = require("../../services/deal/commentService") +} = require("../../services/deal/comment.service") const router = express.Router() diff --git a/routes/deal/dealRoutes.js b/routes/deal/deal.routes.js similarity index 97% rename from routes/deal/dealRoutes.js rename to routes/deal/deal.routes.js index f6556a8..1572b00 100644 --- a/routes/deal/dealRoutes.js +++ b/routes/deal/deal.routes.js @@ -1,6 +1,6 @@ const express = require("express") const router = express.Router() -const { getAllDeals, getDealById, createDeal,searchDeals } = require("../../services/deal/dealService") +const { getAllDeals, getDealById, createDeal,searchDeals } = require("../../services/deal/deal.service") const authMiddleware = require("../../middleware/authMiddleware") router.get("/", async (req, res) => { @@ -55,4 +55,7 @@ router.post("/", authMiddleware, async (req, res) => { res.status(500).json({ error: "Sunucu hatası" }) } }) + + + module.exports = router diff --git a/routes/deal/voteRoutes.js b/routes/deal/vote.routes.js similarity index 99% rename from routes/deal/voteRoutes.js rename to routes/deal/vote.routes.js index 3a18a00..b9b22e3 100644 --- a/routes/deal/voteRoutes.js +++ b/routes/deal/vote.routes.js @@ -1,6 +1,6 @@ const express = require("express") const authMiddleware = require("../../middleware/authMiddleware") -const { voteDeal, getVotes } = require("../../services/deal/dealService") +const { voteDeal, getVotes } = require("../../services/deal/deal.service") const { z } = require("zod") const router = express.Router() diff --git a/routes/seller/seller.routes.js b/routes/seller/seller.routes.js new file mode 100644 index 0000000..3455387 --- /dev/null +++ b/routes/seller/seller.routes.js @@ -0,0 +1,34 @@ +const express = require("express") +const router = express.Router() +const authMiddleware = require("../../middleware/authMiddleware") +const { findCompanyFromLink } = require("../../services/seller/seller.service") + + +router.post("/from-link", authMiddleware, async (req, res) => { + try { + const seller = req.body.seller + + if (!seller) { + return res.status(400).json({ error: "URL gerekli" }) + } + + const company = await findCompanyFromLink(url) + + if (!company) { + return res.json({ + sellerId: -1, + sellerName: null, + }) + } + + return res.json({ + sellerId: company.id, + sellerName: company.name, + }) + } catch (e) { + console.error(e) + res.status(500).json({ error: "Sunucu hatası" }) + } +}) + +module.exports = router diff --git a/routes/userRoutes.js b/routes/user.routes.js similarity index 100% rename from routes/userRoutes.js rename to routes/user.routes.js diff --git a/routes/user/userRoutes.js b/routes/user/user.routes.js similarity index 100% rename from routes/user/userRoutes.js rename to routes/user/user.routes.js diff --git a/server.js b/server.js index 87bba4a..4655858 100644 --- a/server.js +++ b/server.js @@ -2,14 +2,14 @@ const express = require("express") const cors = require("cors") require("dotenv").config() -const userRoutesneedRefactor = require("./routes/userRoutes") -const dealRoutes = require("./routes/deal/dealRoutes") -const authRoutes = require("./routes/authRoutes") -const dealVoteRoutes = require("./routes/deal/voteRoutes") -const commentRoutes = require("./routes/deal/commentRoutes") -const accountSettingsRoutes = require("./routes/account/accountSettingsRoutes") -const userRoutes = require("./routes/user/userRoutes") - +const userRoutesneedRefactor = require("./routes/user.routes") +const dealRoutes = require("./routes/deal/deal.routes") +const authRoutes = require("./routes/auth.routes") +const dealVoteRoutes = require("./routes/deal/vote.routes") +const commentRoutes = require("./routes/deal/comment.routes") +const accountSettingsRoutes = require("./routes/account/accountSettings.routes") +const userRoutes = require("./routes/user/user.routes") +const sellerRoutes = require("./routes/seller/seller.routes") const app = express() app.use(cors()) @@ -23,5 +23,5 @@ app.use("/api/deal-votes", dealVoteRoutes) app.use("/api/comments", commentRoutes) app.use("/api/account", accountSettingsRoutes) app.use("/api/user", userRoutes) - +app.use("/api/seller", sellerRoutes) app.listen(3000, () => console.log("Server running on http://localhost:3000")) diff --git a/services/account/avatar.service.js b/services/account/avatar.service.js new file mode 100644 index 0000000..4fb1313 --- /dev/null +++ b/services/account/avatar.service.js @@ -0,0 +1,45 @@ +const fs = require("fs") +const { uploadImage } = require("../uploadImage.service") +const { validateImage } = require("../../utils/validateImage") + +const userDB = require("../../db/user.db") + +async function updateUserAvatar(userId, file) { + if (!file) { + throw new Error("No file uploaded") + } + + validateImage({ + mimetype: file.mimetype, + size: file.size, + }) + + const buffer = fs.readFileSync(file.path) + + const imageUrl = await uploadImage({ + bucket: "avatars", + path: `${userId}_${Date.now()}.jpg`, + fileBuffer: buffer, + contentType: file.mimetype, + }) + + fs.unlinkSync(file.path) + + return updateAvatarUrl(userId, imageUrl) +} + + +async function updateAvatarUrl(userId, imageUrl) { + return userDB.updateUser( + { id: userId }, + { avatarUrl: imageUrl }, + { + select: { + id: true, + username: true, + avatarUrl: true, + }, + } + ) +} +module.exports = { updateUserAvatar } diff --git a/services/deal/commentService.js b/services/deal/comment.service.js similarity index 100% rename from services/deal/commentService.js rename to services/deal/comment.service.js diff --git a/services/deal/dealService.js b/services/deal/deal.service.js similarity index 55% rename from services/deal/dealService.js rename to services/deal/deal.service.js index cc12c9d..a878225 100644 --- a/services/deal/dealService.js +++ b/services/deal/deal.service.js @@ -1,4 +1,10 @@ const dealDB = require("../../db/deal.db") +const { mapDealToDealCardResponse } = require("../../adapters/responses/dealCard.adapter") +const { mapDealToDealDetailResponse } = require("../../adapters/responses/dealDetail.adapter") +const { + findCompanyFromLink, +} = require("../seller/seller.service") + async function searchDeals(query, page = 1, limit = 10) { const skip = (page - 1) * limit @@ -15,12 +21,14 @@ async function searchDeals(query, page = 1, limit = 10) { take: limit, orderBy: { createdAt: "desc" }, include: { - user: { select: { username: true } }, + company:{select:{name:true}}, + user: { select: {id:true,username: true } }, images: { orderBy: { order: "asc" }, take: 1, select: { imageUrl: true }, }, + }, }), dealDB.countDeals(where), @@ -30,7 +38,7 @@ async function searchDeals(query, page = 1, limit = 10) { page, total, totalPages: Math.ceil(total / limit), - results: deals, + results: deals.map(mapDealToDealCardResponse), } } @@ -43,7 +51,8 @@ async function getAllDeals(page = 1, limit = 10) { take: limit, orderBy: { createdAt: "desc" }, include: { - user: { select: { username: true } }, + company:{select:{name:true}}, + user: { select: { id:true,username: true } }, images: { orderBy: { order: "asc" }, take: 1, @@ -58,43 +67,88 @@ async function getAllDeals(page = 1, limit = 10) { page, total, totalPages: Math.ceil(total / limit), - results: deals, + results: deals.map(mapDealToDealCardResponse), } } async function getDealById(id) { - return dealDB.findDeal( + const deal=await dealDB.findDeal( { id: Number(id) }, { include: { - user: { select: { username: true } }, + company:{ + select: { + name:true, + url:true + }, + }, + user: { + select: { + id: true, + username: true, + avatarUrl: true, + }, + }, + company: { + select: { + id: true, + name: true, + }, + }, images: { orderBy: { order: "asc" }, - select: { id: true, imageUrl: true, order: true }, + select: { + id: true, + imageUrl: true, + order: true, + }, + }, + comments: { + orderBy: { createdAt: "desc" }, + select: { + id: true, + text: true, + createdAt: true, + user: { + select: { + id: true, + username: true, + avatarUrl: true, + }, + }, + }, + }, + _count: { + select: { + comments: true, + }, }, }, } ) + + return mapDealToDealDetailResponse(deal) } -async function createDeal(data, userId) { - const payload = { - title: data.title, - description: data.description, - url: data.url, - price: data.price, - user: { connect: { id: userId } }, - images: data.images?.length - ? { - create: data.images.map((imgUrl, index) => ({ - imageUrl: imgUrl, - order: index, - })), - } - : undefined, + +async function createDeal(dealCreateData) { + // 🔴 SADECE link varsa bak + if (dealCreateData.url) { + const company = await findCompanyFromLink( + dealCreateData.url + ) + + if (company) { + dealCreateData.company = { + connect: { id: company.id }, + } + dealCreateData.customCompany = null + } } - return dealDB.createDeal(payload, { include: { images: true } }) + return dealDB.createDeal(dealCreateData, { + include: { images: true }, + }) } async function voteDeal(dealId, userId, voteType) { diff --git a/services/profile/myProfileService.js b/services/profile/profile.service.js similarity index 100% rename from services/profile/myProfileService.js rename to services/profile/profile.service.js diff --git a/services/seller/seller.service.js b/services/seller/seller.service.js new file mode 100644 index 0000000..929e314 --- /dev/null +++ b/services/seller/seller.service.js @@ -0,0 +1,38 @@ +// services/company/companyService.js +const { findCompanyByDomain } = require("../../db/seller.db") + +function normalizeDomain(hostname) { + return hostname.replace(/^www\./, "") +} + +async function findCompanyFromLink(url) { + let hostname + + try { + hostname = new URL(url).hostname + } catch { + return null + } + + const domain = normalizeDomain(hostname) + + const company = await findCompanyByDomain(domain) + if (company) { + return company + } + + const domainParts = domain.split(".") + for (let i = 1; i <= domainParts.length - 2; i += 1) { + const parentDomain = domainParts.slice(i).join(".") + const parentCompany = await findCompanyByDomain(parentDomain) + if (parentCompany) { + return parentCompany + } + } + + return null +} + +module.exports = { + findCompanyFromLink, +} diff --git a/services/supabase/supabaseUpload.service.js b/services/supabase/supabaseUpload.service.js new file mode 100644 index 0000000..998ce88 --- /dev/null +++ b/services/supabase/supabaseUpload.service.js @@ -0,0 +1,39 @@ +const { createClient } = require("@supabase/supabase-js") + +const supabase = createClient( + process.env.SUPABASE_URL, + process.env.SUPABASE_KEY +) + +/** + * @param {Object} options + * @param {string} options.bucket - supabase bucket adı + * @param {string} options.path - storage içi path + * @param {Buffer} options.fileBuffer - file buffer + * @param {string} options.contentType + */ +async function uploadImage({ + bucket, + path, + fileBuffer, + contentType, +}) { + const { error } = await supabase.storage + .from(bucket) + .upload(path, fileBuffer, { + contentType, + upsert: true, + }) + + if (error) { + throw new Error(error.message) + } + + const { data } = supabase.storage + .from(bucket) + .getPublicUrl(path) + + return data.publicUrl +} + +module.exports = { uploadImage } diff --git a/services/supabase/supabaseUploadService.js b/services/supabase/supabaseUploadService.js deleted file mode 100644 index a383dfa..0000000 --- a/services/supabase/supabaseUploadService.js +++ /dev/null @@ -1,18 +0,0 @@ -const { createClient } = require("@supabase/supabase-js") - -const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY) - -async function uploadProfileImage(userId, file) { - const path = `avatars/${userId}_${Date.now()}.jpg` - const { data, error } = await supabase.storage - .from("deal") - .upload(path, file.data, { - contentType: "image/jpeg", - upsert: true, - }) - if (error) throw new Error(error.message) - const { data: publicUrl } = supabase.storage.from("avatars").getPublicUrl(path) - return publicUrl.publicUrl -} - -module.exports = { uploadProfileImage } diff --git a/services/uploadImage.service.js b/services/uploadImage.service.js new file mode 100644 index 0000000..a3077fa --- /dev/null +++ b/services/uploadImage.service.js @@ -0,0 +1,22 @@ +const { createClient } = require("@supabase/supabase-js") + +const supabase = createClient( + process.env.SUPABASE_URL, + process.env.SUPABASE_KEY +) + +async function uploadImage({ bucket, path, fileBuffer, contentType }) { + const { error } = await supabase.storage + .from(bucket) + .upload(path, fileBuffer, { + contentType, + upsert: true, + }) + + if (error) throw new Error(error.message) + + const { data } = supabase.storage.from(bucket).getPublicUrl(path) + return data.publicUrl +} + +module.exports = { uploadImage } diff --git a/uploads/0b53c6148449a40478b1fe87c65dda70 b/uploads/0b53c6148449a40478b1fe87c65dda70 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/0b53c6148449a40478b1fe87c65dda70 differ diff --git a/uploads/21d08aecb645549f48249a76124d50c9 b/uploads/21d08aecb645549f48249a76124d50c9 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/21d08aecb645549f48249a76124d50c9 differ diff --git a/uploads/3a83436509786675207c4f5363766b38 b/uploads/3a83436509786675207c4f5363766b38 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/3a83436509786675207c4f5363766b38 differ diff --git a/uploads/3c0d929717cdd5d913dd759443a6b45e b/uploads/3c0d929717cdd5d913dd759443a6b45e new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/3c0d929717cdd5d913dd759443a6b45e differ diff --git a/uploads/479bbfb0b12a0200474a3ae11b0016b1 b/uploads/479bbfb0b12a0200474a3ae11b0016b1 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/479bbfb0b12a0200474a3ae11b0016b1 differ diff --git a/uploads/8f76c7c982dae062103808f9ce5c6a5b b/uploads/8f76c7c982dae062103808f9ce5c6a5b new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/8f76c7c982dae062103808f9ce5c6a5b differ diff --git a/uploads/9cbbae9237322c45e9e0c264f4ab646e b/uploads/9cbbae9237322c45e9e0c264f4ab646e new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/9cbbae9237322c45e9e0c264f4ab646e differ diff --git a/uploads/a95339c91ab7d1595f9647256daa3def b/uploads/a95339c91ab7d1595f9647256daa3def new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/a95339c91ab7d1595f9647256daa3def differ diff --git a/uploads/d67424c4b6029594eca89ca75d452f66 b/uploads/d67424c4b6029594eca89ca75d452f66 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/d67424c4b6029594eca89ca75d452f66 differ diff --git a/uploads/e731c17cf3d0a024d02f8ed6adfa3046 b/uploads/e731c17cf3d0a024d02f8ed6adfa3046 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/e731c17cf3d0a024d02f8ed6adfa3046 differ diff --git a/uploads/f2e1b193bb53ebbc7bbc26389e1c0d9a b/uploads/f2e1b193bb53ebbc7bbc26389e1c0d9a new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/f2e1b193bb53ebbc7bbc26389e1c0d9a differ diff --git a/uploads/fa9eb4d6481c7dd318a7dc9b522d3fb9 b/uploads/fa9eb4d6481c7dd318a7dc9b522d3fb9 new file mode 100644 index 0000000..4271e2b Binary files /dev/null and b/uploads/fa9eb4d6481c7dd318a7dc9b522d3fb9 differ