This commit is contained in:
Burak Cüre 2026-01-20 12:16:14 +00:00
parent e966158d37
commit c46871f99f
39 changed files with 942 additions and 451 deletions

View File

@ -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,
}

View File

@ -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 }

View File

@ -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,
}

28
db/seller.db.js Normal file
View File

@ -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,
}

600
package-lock.json generated
View File

@ -70,9 +70,9 @@
} }
}, },
"node_modules/@prisma/client": { "node_modules/@prisma/client": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz",
"integrity": "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA==", "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@ -92,9 +92,9 @@
} }
}, },
"node_modules/@prisma/config": { "node_modules/@prisma/config": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz",
"integrity": "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ==", "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@ -105,145 +105,146 @@
} }
}, },
"node_modules/@prisma/debug": { "node_modules/@prisma/debug": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz",
"integrity": "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg==", "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@prisma/engines": { "node_modules/@prisma/engines": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz",
"integrity": "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA==", "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==",
"devOptional": true, "devOptional": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@prisma/debug": "6.18.0", "@prisma/debug": "6.19.2",
"@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
"@prisma/fetch-engine": "6.18.0", "@prisma/fetch-engine": "6.19.2",
"@prisma/get-platform": "6.18.0" "@prisma/get-platform": "6.19.2"
} }
}, },
"node_modules/@prisma/engines-version": { "node_modules/@prisma/engines-version": {
"version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f.tgz", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz",
"integrity": "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ==", "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/@prisma/fetch-engine": { "node_modules/@prisma/fetch-engine": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz",
"integrity": "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A==", "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@prisma/debug": "6.18.0", "@prisma/debug": "6.19.2",
"@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7",
"@prisma/get-platform": "6.18.0" "@prisma/get-platform": "6.19.2"
} }
}, },
"node_modules/@prisma/get-platform": { "node_modules/@prisma/get-platform": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.18.0.tgz", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz",
"integrity": "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg==", "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@prisma/debug": "6.18.0" "@prisma/debug": "6.19.2"
} }
}, },
"node_modules/@standard-schema/spec": { "node_modules/@standard-schema/spec": {
"version": "1.0.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@supabase/auth-js": { "node_modules/@supabase/auth-js": {
"version": "2.78.0", "version": "2.90.1",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.78.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.90.1.tgz",
"integrity": "sha512-cXDtu1U0LeZj/xfnFoV7yCze37TcbNo8FCxy1FpqhMbB9u9QxxDSW6pA5gm/07Ei7m260Lof4CZx67Cu6DPeig==", "integrity": "sha512-vxb66dgo6h3yyPbR06735Ps+dK3hj0JwS8w9fdQPVZQmocSTlKUW5MfxSy99mN0XqCCuLMQ3jCEiIIUU23e9ng==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/node-fetch": "2.6.15",
"tslib": "2.8.1" "tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
} }
}, },
"node_modules/@supabase/functions-js": { "node_modules/@supabase/functions-js": {
"version": "2.78.0", "version": "2.90.1",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.78.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.90.1.tgz",
"integrity": "sha512-t1jOvArBsOINyqaRee1xJ3gryXLvkBzqnKfi6q3YRzzhJbGS6eXz0pXR5fqmJeB01fLC+1njpf3YhMszdPEF7g==", "integrity": "sha512-x9mV9dF1Lam9qL3zlpP6mSM5C9iqMPtF5B/tU1Jj/F0ufX5mjDf9ghVBaErVxmrQJRL4+iMKWKY2GnODkpS8tw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/node-fetch": "2.6.15",
"tslib": "2.8.1" "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": { "engines": {
"node": "4.x || >=6.0.0" "node": ">=20.0.0"
} }
}, },
"node_modules/@supabase/postgrest-js": { "node_modules/@supabase/postgrest-js": {
"version": "2.78.0", "version": "2.90.1",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.78.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.90.1.tgz",
"integrity": "sha512-AwhpYlSvJ+PSnPmIK8sHj7NGDyDENYfQGKrMtpVIEzQA2ApUjgpUGxzXWN4Z0wEtLQsvv7g4y9HVad9Hzo1TNA==", "integrity": "sha512-jh6vqzaYzoFn3raaC0hcFt9h+Bt+uxNRBSdc7PfToQeRGk7PDPoweHsbdiPWREtDVTGKfu+PyPW9e2jbK+BCgQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/node-fetch": "2.6.15",
"tslib": "2.8.1" "tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
} }
}, },
"node_modules/@supabase/realtime-js": { "node_modules/@supabase/realtime-js": {
"version": "2.78.0", "version": "2.90.1",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.78.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.90.1.tgz",
"integrity": "sha512-rCs1zmLe7of7hj4s7G9z8rTqzWuNVtmwDr3FiCRCJFawEoa+RQO1xpZGbdeuVvVmKDyVN6b542Okci+117y/LQ==", "integrity": "sha512-PWbnEMkcQRuor8jhObp4+Snufkq8C6fBp+MchVp2qBPY1NXk/c3Iv3YyiFYVzo0Dzuw4nAlT4+ahuPggy4r32w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/node-fetch": "2.6.15",
"@types/phoenix": "^1.6.6", "@types/phoenix": "^1.6.6",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"tslib": "2.8.1", "tslib": "2.8.1",
"ws": "^8.18.2" "ws": "^8.18.2"
},
"engines": {
"node": ">=20.0.0"
} }
}, },
"node_modules/@supabase/storage-js": { "node_modules/@supabase/storage-js": {
"version": "2.78.0", "version": "2.90.1",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.78.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.90.1.tgz",
"integrity": "sha512-n17P0JbjHOlxqJpkaGFOn97i3EusEKPEbWOpuk1r4t00Wg06B8Z4GUiq0O0n1vUpjiMgJUkLIMuBVp+bEgunzQ==", "integrity": "sha512-GHY+Ps/K/RBfRj7kwx+iVf2HIdqOS43rM2iDOIDpapyUnGA9CCBFzFV/XvfzznGykd//z2dkGZhlZZprsVFqGg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/node-fetch": "2.6.15", "iceberg-js": "^0.8.1",
"tslib": "2.8.1" "tslib": "2.8.1"
},
"engines": {
"node": ">=20.0.0"
} }
}, },
"node_modules/@supabase/supabase-js": { "node_modules/@supabase/supabase-js": {
"version": "2.78.0", "version": "2.90.1",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.78.0.tgz", "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.90.1.tgz",
"integrity": "sha512-xYMRNBFmKp2m1gMuwcp/gr/HlfZKqjye1Ib8kJe29XJNsgwsfO/f8skxnWiscFKTlkOKLuBexNgl5L8dzGt6vA==", "integrity": "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@supabase/auth-js": "2.78.0", "@supabase/auth-js": "2.90.1",
"@supabase/functions-js": "2.78.0", "@supabase/functions-js": "2.90.1",
"@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "2.90.1",
"@supabase/postgrest-js": "2.78.0", "@supabase/realtime-js": "2.90.1",
"@supabase/realtime-js": "2.78.0", "@supabase/storage-js": "2.90.1"
"@supabase/storage-js": "2.78.0" },
"engines": {
"node": ">=20.0.0"
} }
}, },
"node_modules/@tsconfig/node10": { "node_modules/@tsconfig/node10": {
"version": "1.0.11", "version": "1.0.12",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@ -300,21 +301,21 @@
} }
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "5.0.5", "version": "5.0.6",
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
"integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0", "@types/express-serve-static-core": "^5.0.0",
"@types/serve-static": "^1" "@types/serve-static": "^2"
} }
}, },
"node_modules/@types/express-serve-static-core": { "node_modules/@types/express-serve-static-core": {
"version": "5.1.0", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
"integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -331,26 +332,19 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/@types/node": {
"version": "24.9.2", "version": "24.10.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz",
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
}, },
"node_modules/@types/phoenix": { "node_modules/@types/phoenix": {
"version": "1.6.6", "version": "1.6.7",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz",
"integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/qs": { "node_modules/@types/qs": {
@ -378,25 +372,13 @@
} }
}, },
"node_modules/@types/serve-static": { "node_modules/@types/serve-static": {
"version": "1.15.10", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
"integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/http-errors": "*", "@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": "*" "@types/node": "*"
} }
}, },
@ -478,23 +460,6 @@
"node": ">=0.4.0" "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": { "node_modules/ansi-styles": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@ -525,32 +490,36 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/bcryptjs": { "node_modules/bcryptjs": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz",
"integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"bin": { "bin": {
"bcrypt": "bin/bcrypt" "bcrypt": "bin/bcrypt"
} }
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.2.0", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bytes": "^3.1.2", "bytes": "^3.1.2",
"content-type": "^1.0.5", "content-type": "^1.0.5",
"debug": "^4.4.0", "debug": "^4.4.3",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.7.0",
"on-finished": "^2.4.1", "on-finished": "^2.4.1",
"qs": "^6.14.0", "qs": "^6.14.1",
"raw-body": "^3.0.0", "raw-body": "^3.0.1",
"type-is": "^2.0.0" "type-is": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/buffer-equal-constant-time": { "node_modules/buffer-equal-constant-time": {
@ -749,15 +718,16 @@
} }
}, },
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "1.0.0", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
"license": "MIT", "license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": { "engines": {
"node": ">= 0.6" "node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/content-type": { "node_modules/content-type": {
@ -851,32 +821,30 @@
} }
}, },
"node_modules/dependency-cruiser": { "node_modules/dependency-cruiser": {
"version": "17.2.0", "version": "17.3.6",
"resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-17.2.0.tgz", "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-17.3.6.tgz",
"integrity": "sha512-EiOkB41fdAOCef3ycMX4pAXqkWrCOQ+G4IKOjcs9uPILShAuki2fHlTQPnv4aIHka/3lGJJdpRVrrwTMD7v1dg==", "integrity": "sha512-k0mXZaNvR2hZC0Dh2y4NGZZGqWLM6AbxQXmKL0T2m+KRE19nK5gAhm+PdV7f1L+AhdwwSOnebok7C6P27l2xXA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"acorn": "^8.15.0", "acorn": "8.15.0",
"acorn-jsx": "^5.3.2", "acorn-jsx": "5.3.2",
"acorn-jsx-walk": "^2.0.0", "acorn-jsx-walk": "2.0.0",
"acorn-loose": "^8.5.2", "acorn-loose": "8.5.2",
"acorn-walk": "^8.3.4", "acorn-walk": "8.3.4",
"ajv": "^8.17.1", "commander": "14.0.2",
"commander": "^14.0.2", "enhanced-resolve": "5.18.4",
"enhanced-resolve": "^5.18.3", "ignore": "7.0.5",
"ignore": "^7.0.5", "interpret": "3.1.1",
"interpret": "^3.1.1", "is-installed-globally": "1.0.0",
"is-installed-globally": "^1.0.0", "json5": "2.2.3",
"json5": "^2.2.3", "picomatch": "4.0.3",
"memoize": "^10.2.0", "prompts": "2.4.2",
"picomatch": "^4.0.3", "rechoir": "0.8.0",
"prompts": "^2.4.2", "safe-regex": "2.1.1",
"rechoir": "^0.8.0", "semver": "7.7.3",
"safe-regex": "^2.1.1", "tsconfig-paths-webpack-plugin": "4.2.0",
"semver": "^7.7.3", "watskeburt": "5.0.0"
"tsconfig-paths-webpack-plugin": "^4.2.0",
"watskeburt": "^4.2.3"
}, },
"bin": { "bin": {
"depcruise": "bin/dependency-cruise.mjs", "depcruise": "bin/dependency-cruise.mjs",
@ -980,9 +948,9 @@
} }
}, },
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.18.3", "version": "5.18.4",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -1039,18 +1007,19 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "5.1.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.2.0", "body-parser": "^2.2.1",
"content-disposition": "^1.0.0", "content-disposition": "^1.0.0",
"content-type": "^1.0.5", "content-type": "^1.0.5",
"cookie": "^0.7.1", "cookie": "^0.7.1",
"cookie-signature": "^1.2.1", "cookie-signature": "^1.2.1",
"debug": "^4.4.0", "debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"etag": "^1.8.1", "etag": "^1.8.1",
@ -1081,9 +1050,9 @@
} }
}, },
"node_modules/exsolve": { "node_modules/exsolve": {
"version": "1.0.7", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
"integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
@ -1110,34 +1079,10 @@
"node": ">=8.0.0" "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": { "node_modules/finalhandler": {
"version": "2.1.0", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.4.0", "debug": "^4.4.0",
@ -1148,7 +1093,11 @@
"statuses": "^2.0.1" "statuses": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/forwarded": { "node_modules/forwarded": {
@ -1303,40 +1252,48 @@
} }
}, },
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"depd": "2.0.0", "depd": "~2.0.0",
"inherits": "2.0.4", "inherits": "~2.0.4",
"setprototypeof": "1.2.0", "setprototypeof": "~1.2.0",
"statuses": "2.0.1", "statuses": "~2.0.2",
"toidentifier": "1.0.1" "toidentifier": "~1.0.1"
}, },
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/http-errors/node_modules/statuses": { "node_modules/iceberg-js": {
"version": "2.0.1", "version": "0.8.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 0.8" "node": ">=20.0.0"
} }
}, },
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/ignore": { "node_modules/ignore": {
@ -1446,13 +1403,6 @@
"jiti": "lib/jiti-cli.mjs" "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": { "node_modules/json5": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@ -1467,12 +1417,12 @@
} }
}, },
"node_modules/jsonwebtoken": { "node_modules/jsonwebtoken": {
"version": "9.0.2", "version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"jws": "^3.2.2", "jws": "^4.0.1",
"lodash.includes": "^4.3.0", "lodash.includes": "^4.3.0",
"lodash.isboolean": "^3.0.3", "lodash.isboolean": "^3.0.3",
"lodash.isinteger": "^4.0.4", "lodash.isinteger": "^4.0.4",
@ -1489,9 +1439,9 @@
} }
}, },
"node_modules/jwa": { "node_modules/jwa": {
"version": "1.4.2", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"buffer-equal-constant-time": "^1.0.1", "buffer-equal-constant-time": "^1.0.1",
@ -1500,12 +1450,12 @@
} }
}, },
"node_modules/jws": { "node_modules/jws": {
"version": "3.2.2", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"jwa": "^1.4.1", "jwa": "^2.0.1",
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
@ -1586,22 +1536,6 @@
"node": ">= 0.8" "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": { "node_modules/merge-descriptors": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
@ -1624,28 +1558,19 @@
} }
}, },
"node_modules/mime-types": { "node_modules/mime-types": {
"version": "3.0.1", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"mime-db": "^1.54.0" "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": { "engines": {
"node": ">=18" "node": ">=18"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
@ -1887,15 +1812,15 @@
} }
}, },
"node_modules/prisma": { "node_modules/prisma": {
"version": "6.18.0", "version": "6.19.2",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.18.0.tgz", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz",
"integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==",
"devOptional": true, "devOptional": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@prisma/config": "6.18.0", "@prisma/config": "6.19.2",
"@prisma/engines": "6.18.0" "@prisma/engines": "6.19.2"
}, },
"bin": { "bin": {
"prisma": "build/index.js" "prisma": "build/index.js"
@ -1957,9 +1882,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/qs": { "node_modules/qs": {
"version": "6.14.0", "version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"side-channel": "^1.1.0" "side-channel": "^1.1.0"
@ -1981,36 +1906,20 @@
} }
}, },
"node_modules/raw-body": { "node_modules/raw-body": {
"version": "3.0.1", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "~3.1.2",
"http-errors": "2.0.0", "http-errors": "~2.0.1",
"iconv-lite": "0.7.0", "iconv-lite": "~0.7.0",
"unpipe": "1.0.0" "unpipe": "~1.0.0"
}, },
"engines": { "engines": {
"node": ">= 0.10" "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": { "node_modules/rc9": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
@ -2073,16 +1982,6 @@
"regexp-tree": "bin/regexp-tree" "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": { "node_modules/resolve": {
"version": "1.22.11", "version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@ -2169,31 +2068,35 @@
} }
}, },
"node_modules/send": { "node_modules/send": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.3.5", "debug": "^4.4.3",
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"etag": "^1.8.1", "etag": "^1.8.1",
"fresh": "^2.0.0", "fresh": "^2.0.0",
"http-errors": "^2.0.0", "http-errors": "^2.0.1",
"mime-types": "^3.0.1", "mime-types": "^3.0.2",
"ms": "^2.1.3", "ms": "^2.1.3",
"on-finished": "^2.4.1", "on-finished": "^2.4.1",
"range-parser": "^1.2.1", "range-parser": "^1.2.1",
"statuses": "^2.0.1" "statuses": "^2.0.2"
}, },
"engines": { "engines": {
"node": ">= 18" "node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "2.2.0", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
@ -2203,6 +2106,10 @@
}, },
"engines": { "engines": {
"node": ">= 18" "node": ">= 18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/setprototypeof": { "node_modules/setprototypeof": {
@ -2367,11 +2274,14 @@
} }
}, },
"node_modules/tinyexec": { "node_modules/tinyexec": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
"integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT",
"engines": {
"node": ">=18"
}
}, },
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
@ -2382,12 +2292,6 @@
"node": ">=0.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": { "node_modules/ts-node": {
"version": "10.9.2", "version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
@ -2541,32 +2445,16 @@
} }
}, },
"node_modules/watskeburt": { "node_modules/watskeburt": {
"version": "4.2.3", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-4.2.3.tgz", "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-5.0.0.tgz",
"integrity": "sha512-uG9qtQYoHqAsnT711nG5iZc/8M5inSmkGCOp7pFaytKG2aTfIca7p//CjiVzAE4P7hzaYuCozMjNNaLgmhbK5g==", "integrity": "sha512-fEMhfIzu9WOuAJdDcTT+aPjn0JHI2+UeJ+zWSEs/tgMvc+MFDZVmhlZ8C1uJWXax1ETYc4trUnHFHyx2DrG0jQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"watskeburt": "dist/run-cli.js" "watskeburt": "dist/run-cli.js"
}, },
"engines": { "engines": {
"node": "^18||>=20" "node": "^20.12||^22.13||>=24.0"
}
},
"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_modules/wrappy": { "node_modules/wrappy": {
@ -2576,9 +2464,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.18.3", "version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
@ -2616,9 +2504,9 @@
} }
}, },
"node_modules/zod": { "node_modules/zod": {
"version": "4.1.12", "version": "4.3.5",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"

View File

@ -5,6 +5,9 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
},
"prisma": {
"seed": "node prisma/seed.js"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",

View File

@ -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;

View File

@ -24,6 +24,51 @@ model User {
Deal Deal[] Deal Deal[]
votes DealVote[] votes DealVote[]
comments Comment[] 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 { model Deal {
@ -32,10 +77,19 @@ model Deal {
description String? description String?
url String? url String?
price Float? price Float?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId Int userId Int
score Int @default(0) 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]) user User @relation(fields: [userId], references: [id])
votes DealVote[] votes DealVote[]

123
prisma/seed.js Normal file
View File

@ -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()
})

View File

@ -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

View File

@ -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

View File

@ -4,7 +4,7 @@ const {
getCommentsByDealId, getCommentsByDealId,
createComment, createComment,
deleteComment, deleteComment,
} = require("../../services/deal/commentService") } = require("../../services/deal/comment.service")
const router = express.Router() const router = express.Router()

View File

@ -1,6 +1,6 @@
const express = require("express") const express = require("express")
const router = express.Router() 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") const authMiddleware = require("../../middleware/authMiddleware")
router.get("/", async (req, res) => { router.get("/", async (req, res) => {
@ -55,4 +55,7 @@ router.post("/", authMiddleware, async (req, res) => {
res.status(500).json({ error: "Sunucu hatası" }) res.status(500).json({ error: "Sunucu hatası" })
} }
}) })
module.exports = router module.exports = router

View File

@ -1,6 +1,6 @@
const express = require("express") const express = require("express")
const authMiddleware = require("../../middleware/authMiddleware") 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 { z } = require("zod")
const router = express.Router() const router = express.Router()

View File

@ -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

View File

@ -2,14 +2,14 @@ const express = require("express")
const cors = require("cors") const cors = require("cors")
require("dotenv").config() require("dotenv").config()
const userRoutesneedRefactor = require("./routes/userRoutes") const userRoutesneedRefactor = require("./routes/user.routes")
const dealRoutes = require("./routes/deal/dealRoutes") const dealRoutes = require("./routes/deal/deal.routes")
const authRoutes = require("./routes/authRoutes") const authRoutes = require("./routes/auth.routes")
const dealVoteRoutes = require("./routes/deal/voteRoutes") const dealVoteRoutes = require("./routes/deal/vote.routes")
const commentRoutes = require("./routes/deal/commentRoutes") const commentRoutes = require("./routes/deal/comment.routes")
const accountSettingsRoutes = require("./routes/account/accountSettingsRoutes") const accountSettingsRoutes = require("./routes/account/accountSettings.routes")
const userRoutes = require("./routes/user/userRoutes") const userRoutes = require("./routes/user/user.routes")
const sellerRoutes = require("./routes/seller/seller.routes")
const app = express() const app = express()
app.use(cors()) app.use(cors())
@ -23,5 +23,5 @@ app.use("/api/deal-votes", dealVoteRoutes)
app.use("/api/comments", commentRoutes) app.use("/api/comments", commentRoutes)
app.use("/api/account", accountSettingsRoutes) app.use("/api/account", accountSettingsRoutes)
app.use("/api/user", userRoutes) app.use("/api/user", userRoutes)
app.use("/api/seller", sellerRoutes)
app.listen(3000, () => console.log("Server running on http://localhost:3000")) app.listen(3000, () => console.log("Server running on http://localhost:3000"))

View File

@ -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 }

View File

@ -1,4 +1,10 @@
const dealDB = require("../../db/deal.db") 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) { async function searchDeals(query, page = 1, limit = 10) {
const skip = (page - 1) * limit const skip = (page - 1) * limit
@ -15,12 +21,14 @@ async function searchDeals(query, page = 1, limit = 10) {
take: limit, take: limit,
orderBy: { createdAt: "desc" }, orderBy: { createdAt: "desc" },
include: { include: {
user: { select: { username: true } }, company:{select:{name:true}},
user: { select: {id:true,username: true } },
images: { images: {
orderBy: { order: "asc" }, orderBy: { order: "asc" },
take: 1, take: 1,
select: { imageUrl: true }, select: { imageUrl: true },
}, },
}, },
}), }),
dealDB.countDeals(where), dealDB.countDeals(where),
@ -30,7 +38,7 @@ async function searchDeals(query, page = 1, limit = 10) {
page, page,
total, total,
totalPages: Math.ceil(total / limit), totalPages: Math.ceil(total / limit),
results: deals, results: deals.map(mapDealToDealCardResponse),
} }
} }
@ -43,7 +51,8 @@ async function getAllDeals(page = 1, limit = 10) {
take: limit, take: limit,
orderBy: { createdAt: "desc" }, orderBy: { createdAt: "desc" },
include: { include: {
user: { select: { username: true } }, company:{select:{name:true}},
user: { select: { id:true,username: true } },
images: { images: {
orderBy: { order: "asc" }, orderBy: { order: "asc" },
take: 1, take: 1,
@ -58,43 +67,88 @@ async function getAllDeals(page = 1, limit = 10) {
page, page,
total, total,
totalPages: Math.ceil(total / limit), totalPages: Math.ceil(total / limit),
results: deals, results: deals.map(mapDealToDealCardResponse),
} }
} }
async function getDealById(id) { async function getDealById(id) {
return dealDB.findDeal( const deal=await dealDB.findDeal(
{ id: Number(id) }, { id: Number(id) },
{ {
include: { 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: { images: {
orderBy: { order: "asc" }, 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 = { async function createDeal(dealCreateData) {
title: data.title, // 🔴 SADECE link varsa bak
description: data.description, if (dealCreateData.url) {
url: data.url, const company = await findCompanyFromLink(
price: data.price, dealCreateData.url
user: { connect: { id: userId } }, )
images: data.images?.length
? { if (company) {
create: data.images.map((imgUrl, index) => ({ dealCreateData.company = {
imageUrl: imgUrl, connect: { id: company.id },
order: index, }
})), dealCreateData.customCompany = null
} }
: undefined,
} }
return dealDB.createDeal(payload, { include: { images: true } }) return dealDB.createDeal(dealCreateData, {
include: { images: true },
})
} }
async function voteDeal(dealId, userId, voteType) { async function voteDeal(dealId, userId, voteType) {

View File

@ -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,
}

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB