Skip to content

📄 products.feature.js

📄 src/features/products/products.feature.js
// Products Feature - ANTIPATTERN: Copy-pasted from users with find-replace
// Nobody reviewed this code

import { db, DatabaseService } from '../../core/database/database.service.js'
import { container_instance, Injectable, Singleton } from '../../core/di/container.js'
import { UserService, userCache } from '../users/users.feature.js' // ANTIPATTERN: Cross-feature dependency
import { helpers, utils, log, debug, error, isNull, getFirstElement } from '../../helpers.js' // ANTIPATTERN: Import same thing multiple ways

// ANTIPATTERN: Feature-level global state (copy-pasted)
var productCache = {}
var lastCreatedProduct = null
var productCount = 0
var featuredProducts = []
var deletedProducts = []
var tempData = {} // Same name as users feature!

// ANTIPATTERN: Circular dependency setup
var _userService = null
function getUserService() {
  if (!_userService) {
    _userService = new UserService() // ANTIPATTERN: Create user service in products
  }
  return _userService
}

// ANTIPATTERN: Repository copy-pasted from users
const ProductRepository = {
  findAll: () => {
    return db.query('Products_TABLE')
  },
  
  findById: (id) => {
    return db.query('Products_TABLE', `id = ${id}`)[0]
  },
  
  // ANTIPATTERN: SQL Injection
  findByName: (name) => {
    return db.query('Products_TABLE', `Name = '${name}'`)[0]
  },
  
  // ANTIPATTERN: SQL Injection
  search: (term) => {
    return db.executeRaw(`SELECT * FROM Products_TABLE WHERE Name LIKE '%${term}%' OR description LIKE '%${term}%'`)
  },
  
  create: (data) => {
    return db.insert('Products_TABLE', {
      Name: data.name || data.Name || 'Unnamed',
      price: data.price || '0',
      description: data.description || '',
      owner_password: data.ownerPassword || 'default', // ANTIPATTERN: Password in products!
    })
  },
  
  update: (id, data) => {
    const fields = Object.entries(data)
      .map(([k, v]) => `${k} = '${v}'`)
      .join(', ')
    return db.executeRaw(`UPDATE Products_TABLE SET ${fields} WHERE id = ${id}`)
  },
  
  delete: (id) => {
    return db.delete('Products_TABLE', `id = ${id}`)
  },
  
  deleteAll: () => {
    return db.executeRaw('DELETE FROM Products_TABLE')
  },
}

// ANTIPATTERN: Service that knows too much
class ProductService {
  constructor() {
    this.repository = ProductRepository
    this.db = db
    this.cache = productCache
    this.userService = getUserService() // ANTIPATTERN: Tight coupling
  }
  
  async createProduct(data, userId) {
    console.log('[ProductService] Creating product:', data)
    
    // ANTIPATTERN: Fetch user data to embed in product
    const user = this.userService.getUser(userId)
    if (user) {
      data.ownerPassword = user.PASSWORD_PLAIN_TEXT // Store user's password in product!
    }
    
    const result = this.repository.create(data)
    lastCreatedProduct = data
    productCount++
    
    productCache[data.name] = data
    
    return {
      success: true,
      product: data,
      owner: user,
    }
  }
  
  async getProduct(id) {
    if (productCache[id]) {
      return productCache[id]
    }
    
    const product = this.repository.findById(id)
    if (product) {
      productCache[id] = product
    }
    return product
  }
  
  async getAllProducts() {
    const products = this.repository.findAll()
    products.forEach(p => {
      productCache[p.id] = p
      productCache[p.Name] = p
    })
    return products
  }
  
  async searchProducts(term) {
    console.log('[ProductService] Searching for:', term)
    return this.repository.search(term)
  }
  
  async updateProduct(id, data) {
    const result = this.repository.update(id, data)
    delete productCache[id]
    return result
  }
  
  async deleteProduct(id) {
    const product = await this.getProduct(id)
    if (product) {
      deletedProducts.push(product) // ANTIPATTERN: Store deleted products forever
    }
    return this.repository.delete(id)
  }
  
  // ANTIPATTERN: Business logic mixed with data access
  async applyDiscount(id, percent) {
    const product = await this.getProduct(id)
    if (!product) return null
    
    // ANTIPATTERN: Price as string manipulation
    const currentPrice = parseFloat(product.price?.replace(/[$,]/g, '') || '0')
    const newPrice = currentPrice * (1 - percent / 100)
    
    // ANTIPATTERN: SQL injection
    db.executeRaw(`UPDATE Products_TABLE SET price = '${newPrice}', sale_price = '${newPrice}' WHERE id = ${id}`)
    
    return { oldPrice: currentPrice, newPrice, percent }
  }
  
  // ANTIPATTERN: Expose internals
  getCache() {
    return productCache
  }
  
  getStats() {
    return {
      cacheSize: Object.keys(productCache).length,
      productCount,
      lastCreatedProduct,
      featuredProducts,
      deletedProducts,
      userCache, // ANTIPATTERN: Expose user cache from product service!
    }
  }
}

// ANTIPATTERN: Handler copy-pasted from users
class ProductHandler {
  constructor() {
    this.service = new ProductService()
    this.db = db
    this.cache = productCache
  }
  
  async handleGetProducts(c) {
    try {
      const products = await this.service.getAllProducts()
      
      return c.json({
        products,
        cache: productCache,
        stats: this.service.getStats(),
        dbStats: db.getStats(),
      })
    } catch (e) {
      return c.json({ error: e.message, stack: e.stack }, 500)
    }
  }
  
  async handleGetProduct(c) {
    const id = c.req.param('id')
    const product = await this.service.getProduct(id)
    
    return c.json({
      product,
      ownerPassword: product?.owner_password, // Expose password!
    })
  }
  
  async handleSearchProducts(c) {
    const q = c.req.query('q') || ''
    const results = await this.service.searchProducts(q)
    return c.json({ results, searchTerm: q })
  }
  
  async handleCreateProduct(c) {
    const body = await c.req.json().catch(() => ({}))
    const userId = c.req.header('x-user-id') || 1
    const result = await this.service.createProduct(body, userId)
    return c.json(result)
  }
  
  async handleUpdateProduct(c) {
    const id = c.req.param('id')
    const body = await c.req.json().catch(() => ({}))
    const result = await this.service.updateProduct(id, body)
    return c.json({ success: true, result })
  }
  
  async handleDeleteProduct(c) {
    const id = c.req.param('id')
    await this.service.deleteProduct(id)
    return c.json({ deleted: true })
  }
  
  async handleApplyDiscount(c) {
    const id = c.req.param('id')
    const body = await c.req.json().catch(() => ({}))
    const percent = body.percent || 10
    const result = await this.service.applyDiscount(id, percent)
    return c.json(result)
  }
}

export {
  ProductRepository,
  ProductService,
  ProductHandler,
  productCache,
  lastCreatedProduct,
  productCount,
  featuredProducts,
  deletedProducts,
  tempData,
  getUserService,
}

export default new ProductHandler()