This commit is contained in:
unknown committed 2026-06-25 16:46:38 +07:00
1 parent c23a4723ed
commit 07e9413a6b
13 files changed
+497 -94

No files matched your search

+186 -16
View File
@@ -4,15 +4,35 @@ import prisma from "../lib/prisma";
export const productsRoute = new Elysia({ prefix: "/products" })
.decorate("validateVariants", (options: any[] = [], variants: any[] = []) => {
if (options.length > 3) throw new Error("Options array length cannot exceed 3.");
const getPrimaryValues = (values: any[]) => {
return values.map(val => {
if (typeof val === 'string') return val;
if (typeof val === 'object' && val !== null) {
return val.th || Object.values(val)[0];
}
return String(val);
});
};
for (const v of variants) {
if (v.option1 && options[0] && !options[0].values.includes(v.option1)) {
throw new Error(`Variant option1 '${v.option1}' is not found in options[0].values [${options[0].values.join(', ')}]`);
if (v.option1 && options[0]) {
const validValues = getPrimaryValues(options[0].values);
if (!validValues.includes(v.option1)) {
throw new Error(`Variant option1 '${v.option1}' is not found in options[0].values [${validValues.join(', ')}]`);
}
}
if (v.option2 && options[1] && !options[1].values.includes(v.option2)) {
throw new Error(`Variant option2 '${v.option2}' is not found in options[1].values [${options[1].values.join(', ')}]`);
if (v.option2 && options[1]) {
const validValues = getPrimaryValues(options[1].values);
if (!validValues.includes(v.option2)) {
throw new Error(`Variant option2 '${v.option2}' is not found in options[1].values [${validValues.join(', ')}]`);
}
}
if (v.option3 && options[2] && !options[2].values.includes(v.option3)) {
throw new Error(`Variant option3 '${v.option3}' is not found in options[2].values [${options[2].values.join(', ')}]`);
if (v.option3 && options[2]) {
const validValues = getPrimaryValues(options[2].values);
if (!validValues.includes(v.option3)) {
throw new Error(`Variant option3 '${v.option3}' is not found in options[2].values [${validValues.join(', ')}]`);
}
}
}
})
@@ -369,10 +389,28 @@ export const productsRoute = new Elysia({ prefix: "/products" })
validateVariants(payload.options || [], payload.variants);
}
const product = await prisma.$transaction(async (tx) => {
let categoryId = payload.categoryId;
if (!categoryId && payload.categoryName) {
let cat = await tx.category.findUnique({ where: { name: payload.categoryName } });
if (!cat) {
cat = await tx.category.create({ data: { name: payload.categoryName } });
}
categoryId = cat.id;
}
let brandId = payload.brandId;
if (!brandId && payload.brandName) {
let brand = await tx.productBrand.findUnique({ where: { name: payload.brandName } });
if (!brand) {
brand = await tx.productBrand.create({ data: { name: payload.brandName } });
}
brandId = brand.id;
}
const prod = await tx.product.create({
data: {
categoryId: payload.categoryId,
brandId: payload.brandId,
categoryId: categoryId,
brandId: brandId,
name: payload.name || "New Product",
price: payload.price || 0,
description: payload.description || "",
@@ -381,15 +419,29 @@ export const productsRoute = new Elysia({ prefix: "/products" })
}
});
await tx.productTranslation.create({
data: {
productId: prod.id,
langCode: 'th',
name: payload.name || "New Product",
description: payload.description || "",
details: payload.details || "",
if (payload.translations && typeof payload.translations === 'object') {
for (const [lang, trans] of Object.entries(payload.translations)) {
await tx.productTranslation.create({
data: {
productId: prod.id,
langCode: lang,
name: (trans as any).name || payload.name || "New Product",
description: (trans as any).description || payload.description || "",
details: (trans as any).details || payload.details || "",
}
});
}
});
} else {
await tx.productTranslation.create({
data: {
productId: prod.id,
langCode: 'th',
name: payload.name || "New Product",
description: payload.description || "",
details: payload.details || "",
}
});
}
if (payload.variants && payload.variants.length > 0) {
for (const v of payload.variants) {
@@ -437,6 +489,124 @@ export const productsRoute = new Elysia({ prefix: "/products" })
},
}
)
.post(
"/bulk",
async ({ body, status, validateVariants }) => {
const payloadArray = body as any[];
if (!Array.isArray(payloadArray)) {
return status(400, { success: false, message: "Payload must be an array of products" });
}
try {
const createdProducts = await prisma.$transaction(async (tx) => {
const results = [];
for (const payload of payloadArray) {
if (payload.variants && Array.isArray(payload.variants)) {
validateVariants(payload.options || [], payload.variants);
}
let categoryId = payload.categoryId;
if (!categoryId && payload.categoryName) {
let cat = await tx.category.findUnique({ where: { name: payload.categoryName } });
if (!cat) {
cat = await tx.category.create({ data: { name: payload.categoryName } });
}
categoryId = cat.id;
}
let brandId = payload.brandId;
if (!brandId && payload.brandName) {
let brand = await tx.productBrand.findUnique({ where: { name: payload.brandName } });
if (!brand) {
brand = await tx.productBrand.create({ data: { name: payload.brandName } });
}
brandId = brand.id;
}
const prod = await tx.product.create({
data: {
categoryId: categoryId,
brandId: brandId,
name: payload.name || "New Product",
price: payload.price || 0,
description: payload.description || "",
details: payload.details || "",
options: payload.options || [],
}
});
if (payload.translations && typeof payload.translations === 'object') {
for (const [lang, trans] of Object.entries(payload.translations)) {
await tx.productTranslation.create({
data: {
productId: prod.id,
langCode: lang,
name: (trans as any).name || payload.name || "New Product",
description: (trans as any).description || payload.description || "",
details: (trans as any).details || payload.details || "",
}
});
}
} else {
await tx.productTranslation.create({
data: {
productId: prod.id,
langCode: 'th',
name: payload.name || "New Product",
description: payload.description || "",
details: payload.details || "",
}
});
}
if (payload.variants && payload.variants.length > 0) {
for (const v of payload.variants) {
await tx.productVariant.create({
data: {
productId: prod.id,
sku: v.sku || null,
barcode: v.barcode || null,
stock: v.stock || 0,
option1: v.option1 || "",
option2: v.option2 || "",
option3: v.option3 || "",
price: v.price || 0,
}
});
}
}
if (payload.images && payload.images.length > 0) {
for (const [idx, img] of payload.images.entries()) {
await tx.productImage.create({
data: {
productId: prod.id,
url: img.url,
order: img.order ?? idx,
}
});
}
}
results.push(prod);
}
return results;
});
return { success: true, products: createdProducts };
} catch (err: any) {
console.error("Bulk create products error:", err);
return status(500, { success: false, message: err?.message || "Failed to bulk create products" });
}
},
{
body: t.Array(t.Any()),
detail: {
summary: "Bulk Create Products",
tags: ["products"],
},
}
)
.patch(
"/:id",
async ({ params, body, status }) => {