Create a new admin page and backend endpoints to allow authorized users to modify product price, image, and description for the eight boxes. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 375ec6d3-d5af-4f82-ab81-5c60fd4a86a3 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 534b4c21-8691-4e0a-ba0c-0091bb20606a Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/147e665c-8c0d-48ec-b0ad-fdc89cd4460f/375ec6d3-d5af-4f82-ab81-5c60fd4a86a3/e238nM8 Replit-Helium-Checkpoint-Created: true
245 lines
6.5 KiB
JavaScript
245 lines
6.5 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Simple file-based storage for demo purposes
|
|
const PRODUCTS_FILE = path.join(__dirname, '../data/products.json');
|
|
|
|
// Ensure data directory exists
|
|
const dataDir = path.dirname(PRODUCTS_FILE);
|
|
if (!fs.existsSync(dataDir)) {
|
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
}
|
|
|
|
// Initialize products file if it doesn't exist
|
|
if (!fs.existsSync(PRODUCTS_FILE)) {
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify([], null, 2));
|
|
}
|
|
|
|
// Product Schema (simplified for file storage)
|
|
class Product {
|
|
constructor(data) {
|
|
this._id = data._id || Date.now().toString();
|
|
this.name = data.name;
|
|
this.description = data.description;
|
|
this.price = Number(data.price || 0);
|
|
this.salePrice = data.salePrice || null;
|
|
this.category = data.category;
|
|
this.image = data.image;
|
|
this.images = data.images || [data.image];
|
|
this.stock = Number(data.stock || 0);
|
|
this.sku = data.sku;
|
|
this.rating = data.rating || 0;
|
|
this.reviewCount = data.reviewCount || 0;
|
|
this.tags = data.tags || [];
|
|
this.isActive = data.isActive !== undefined ? data.isActive : true;
|
|
this.createdAt = data.createdAt || new Date();
|
|
this.updatedAt = data.updatedAt || new Date();
|
|
}
|
|
|
|
// Get public data (without sensitive info)
|
|
getPublicData() {
|
|
return {
|
|
_id: this._id,
|
|
name: this.name,
|
|
description: this.description,
|
|
price: this.price,
|
|
salePrice: this.salePrice,
|
|
category: this.category,
|
|
image: this.image,
|
|
images: this.images,
|
|
stock: this.stock,
|
|
sku: this.sku,
|
|
rating: this.rating,
|
|
reviewCount: this.reviewCount,
|
|
tags: this.tags,
|
|
isActive: this.isActive,
|
|
createdAt: this.createdAt,
|
|
updatedAt: this.updatedAt,
|
|
};
|
|
}
|
|
}
|
|
|
|
// Static methods for file operations
|
|
Product.find = async (query = {}) => {
|
|
try {
|
|
const data = fs.readFileSync(PRODUCTS_FILE, 'utf8');
|
|
let products = JSON.parse(data);
|
|
|
|
// Apply filters
|
|
if (query.isActive !== undefined) {
|
|
products = products.filter(p => p.isActive === query.isActive);
|
|
}
|
|
|
|
if (query.category) {
|
|
products = products.filter(p => p.category === query.category);
|
|
}
|
|
|
|
if (query.$text) {
|
|
const searchTerm = query.$text.$search.toLowerCase();
|
|
products = products.filter(p =>
|
|
p.name.toLowerCase().includes(searchTerm) ||
|
|
p.description.toLowerCase().includes(searchTerm) ||
|
|
p.tags.some(tag => tag.toLowerCase().includes(searchTerm))
|
|
);
|
|
}
|
|
|
|
return products.map(p => new Product(p));
|
|
} catch (error) {
|
|
console.error('Error reading products:', error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
Product.findById = async (id) => {
|
|
try {
|
|
const products = await Product.find();
|
|
const product = products.find(p => p._id === id);
|
|
return product || null;
|
|
} catch (error) {
|
|
console.error('Error finding product:', error);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
Product.findOne = async (query) => {
|
|
try {
|
|
const products = await Product.find();
|
|
if (query.sku) {
|
|
return products.find(p => p.sku === query.sku) || null;
|
|
}
|
|
if (query.email) {
|
|
return products.find(p => p.email === query.email) || null;
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error finding product:', error);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
Product.create = async (data) => {
|
|
try {
|
|
const products = await Product.find();
|
|
const newProduct = new Product(data);
|
|
|
|
// Check for duplicate SKU
|
|
if (products.some(p => p.sku === newProduct.sku)) {
|
|
throw new Error('Product with this SKU already exists');
|
|
}
|
|
|
|
products.push(newProduct);
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify(products, null, 2));
|
|
|
|
return newProduct;
|
|
} catch (error) {
|
|
console.error('Error creating product:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
Product.findByIdAndUpdate = async (id, updateData) => {
|
|
try {
|
|
const products = await Product.find();
|
|
const index = products.findIndex(p => p._id === id);
|
|
|
|
if (index === -1) {
|
|
return null;
|
|
}
|
|
|
|
// Update the product
|
|
Object.assign(products[index], updateData, { updatedAt: new Date() });
|
|
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify(products, null, 2));
|
|
|
|
return new Product(products[index]);
|
|
} catch (error) {
|
|
console.error('Error updating product:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
Product.replaceAll = async (productsData) => {
|
|
try {
|
|
const products = productsData.map(data => new Product(data));
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify(products, null, 2));
|
|
return products;
|
|
} catch (error) {
|
|
console.error('Error replacing products:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
Product.findByIdAndDelete = async (id) => {
|
|
try {
|
|
const products = await Product.find();
|
|
const index = products.findIndex(p => p._id === id);
|
|
|
|
if (index === -1) {
|
|
return null;
|
|
}
|
|
|
|
const deletedProduct = products.splice(index, 1)[0];
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify(products, null, 2));
|
|
|
|
return new Product(deletedProduct);
|
|
} catch (error) {
|
|
console.error('Error deleting product:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
Product.distinct = async (field) => {
|
|
try {
|
|
const products = await Product.find();
|
|
const values = [...new Set(products.map(p => p[field]))];
|
|
return values.filter(v => v !== undefined && v !== null);
|
|
} catch (error) {
|
|
console.error('Error getting distinct values:', error);
|
|
return [];
|
|
}
|
|
};
|
|
|
|
Product.countDocuments = async (query = {}) => {
|
|
try {
|
|
const products = await Product.find(query);
|
|
return products.length;
|
|
} catch (error) {
|
|
console.error('Error counting documents:', error);
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
Product.deleteMany = async () => {
|
|
try {
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify([], null, 2));
|
|
return { deletedCount: 0 }; // Simplified
|
|
} catch (error) {
|
|
console.error('Error deleting products:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
Product.insertMany = async (productsData) => {
|
|
try {
|
|
const existingProducts = await Product.find();
|
|
const newProducts = productsData.map(data => new Product(data));
|
|
|
|
// Check for duplicate SKUs
|
|
for (const product of newProducts) {
|
|
if (existingProducts.some(p => p.sku === product.sku)) {
|
|
throw new Error(`Product with SKU ${product.sku} already exists`);
|
|
}
|
|
}
|
|
|
|
const allProducts = [...existingProducts, ...newProducts];
|
|
fs.writeFileSync(PRODUCTS_FILE, JSON.stringify(allProducts, null, 2));
|
|
|
|
return newProducts;
|
|
} catch (error) {
|
|
console.error('Error inserting products:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
module.exports = Product;
|