Bladeren bron

admin app support, and images

latapp 9 maanden geleden
bovenliggende
commit
4253d761f5
5 gewijzigde bestanden met toevoegingen van 57 en 13 verwijderingen
  1. 2 1
      .gitignore
  2. 11 9
      config/mails.py
  3. 3 0
      config/settings.py
  4. 1 0
      routes/products.py
  5. 40 3
      services/data_service.py

+ 2 - 1
.gitignore

@@ -11,4 +11,5 @@ dksdabjhvjhSADhsbjksf.txt
 data/data.db
 /logs
 mrda.txt
-.env
+.env
+public/images/

+ 11 - 9
config/mails.py

@@ -1,12 +1,14 @@
+from config.settings import APP_NAME, CURRENT_URL
+
 
 REGISTER_MAIL = {
-    "subject": "Bienvenido a Pedidos Express - Confirma tu registro",
+    "subject": f"Bienvenido a {APP_NAME} - Confirma tu registro",
     "body": """
 <html>
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>¡Bienvenido a {app_name}!</title>
+    <title>¡Bienvenido a """+APP_NAME+"""!</title>
     <style>
         body {{
             margin: 0;
@@ -222,20 +224,20 @@ REGISTER_MAIL = {
             <!-- Header -->
             <div class="header">
                 <h1>¡Hola {name}!</h1>
-                <p>Bienvenido a {app_name}</p>
+                <p>Bienvenido a """+APP_NAME+"""</p>
             </div>
             
             <!-- Content -->
             <div class="content">
                 <!-- Welcome Message -->
                 <div class="welcome-message">
-                    <h2>Te damos la bienvenida a {app_name}</h2>
+                    <h2>Te damos la bienvenida a """+APP_NAME+"""</h2>
                     <p>Tu registro ha sido exitoso. <br>Estamos emocionados de tenerte con nosotros. Para completar tu registro, necesitas crear tu PIN de seguridad.</p>
                 </div>
                 
                 <!-- Benefits Section -->
                 <div class="benefits-section">
-                    <h3 class="benefits-title">¿Qué puedes hacer con {app_name}?</h3>
+                    <h3 class="benefits-title">¿Qué puedes hacer con """+APP_NAME+"""?</h3>
                     <ul class="benefits-list">
                         <li>Realizar pedidos de forma rápida y sencilla</li>
                         <li>Acceder a promociones exclusivas</li>
@@ -250,12 +252,12 @@ REGISTER_MAIL = {
                     <div class="verification-icon">🔐</div>
                     <div class="verification-description">Crea tu PIN de seguridad para comenzar</div>
                     <div class="verification-description">Vence en <strong>1 hora</strong></div>
-                    <a href="http://179.57.171.91:5001/verify?q={verification_code}" class="cta-button">Crear mi PIN ahora</a>
+                    <a href='"""+CURRENT_URL+"""/verify?q={verification_code}' class="cta-button">Crear mi PIN ahora</a>
                 </div>
                 
                 <!-- Security Note -->  
                 <div class="security-note">
-                    <p>🔒 Tu PIN será tu clave personal para acceder de forma segura a {app_name}</p>
+                    <p>🔒 Tu PIN será tu clave personal para acceder de forma segura a """+APP_NAME+"""}</p>
                 </div>
                 
                 <!-- Website Section -->
@@ -437,7 +439,7 @@ PIN_RECOVERY_MAIL = {
                                 <tr>
                                     <td style="text-align: center; margin-bottom: 32px;">
                                         <h2 style="color: #101419; font-size: 24px; margin: 0 0 12px 0; font-weight: bold;">¿Olvidaste tu PIN?</h2>
-                                        <p style="color: #6b7280; font-size: 16px; line-height: 1.6; margin: 0;">No te preocupes, puedes crear un nuevo PIN de acceso a {app_name}.</p>
+                                        <p style="color: #6b7280; font-size: 16px; line-height: 1.6; margin: 0;">No te preocupes, puedes crear un nuevo PIN de acceso a """+APP_NAME+""".</p>
                                     </td>
                                 </tr>
                             </table>
@@ -495,7 +497,7 @@ PIN_RECOVERY_MAIL = {
                                         <p style="color: #374151; font-size: 14px; line-height: 1.5; margin: 0;">
                                             1. Haz clic en "Crear Nuevo PIN"<br>
                                             2. Elige un PIN fácil de recordar pero seguro<br>
-                                            3. Ingresa a {app_name} con tu nuevo PIN<br>
+                                            3. Ingresa a """+APP_NAME+""" con tu nuevo PIN<br>
                                             4. ¡Disfruta de todos nuestros beneficios!
                                         </p>
                                     </td>

+ 3 - 0
config/settings.py

@@ -24,9 +24,12 @@ logging.basicConfig(
 )
 
 # Configuration
+APP_NAME = os.getenv("APP_NAME", "Pedidos Express")
 FEEDBACK_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs', 'feedback.json')
 OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
+IMAGE_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'public', 'images')
 PORT = int(os.getenv("PORT", 6001))
+CURRENT_URL = os.getenv("CURRENT_URL", "http://localhost:6001")
 PIN_KEY = os.getenv("PIN_KEY", "-1")
 DEVELOPMENT = True if os.getenv("NODE_ENV", "development").lower() == "development" else False
 if PIN_KEY == "-1":

+ 1 - 0
routes/products.py

@@ -101,6 +101,7 @@ async def edit_product(product_id: int, product: ProductEditRequest, current_use
     
     # Check if user has sufficient permissions (manager level or above)
     if user_data_service.permissions(current_user.id) > 0:
+
         # Update product with provided data (excluding unset fields)
         product_data_service.update(product_id, **product.model_dump(exclude_unset=True))
         

+ 40 - 3
services/data_service.py

@@ -1,15 +1,16 @@
 import json
 from math import log
 import os
+import re
 import sqlite3
 from typing import List, Dict, Optional, Any
 from abc import ABC, abstractmethod
-from config.settings import BG_DATA_PATH, DB_PATH, PRODUCT_DATA_PATH
+from config.settings import BG_DATA_PATH, DB_PATH, IMAGE_PATH, PRODUCT_DATA_PATH, CURRENT_URL
 from logging import getLogger
 from datetime import datetime
 from cryptography.fernet import Fernet
 from config.settings import PIN_KEY
-
+import base64 as b64
 # Import models
 from models.user import User
 from models.items import Product
@@ -457,6 +458,12 @@ class ProductDataService(BaseDataService):
         """Add a new product to the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
+        if image and "base64" in image:
+            extension = re.search(r'data:image/(.*?);base64,', image)
+            image_name = f"{id}_img"
+            if extension:
+                image_name += f".{extension.group(1)}"
+            image = self._image_process(image_name, image)
         try:
             cursor.execute(
                 "INSERT INTO products (id, name, type, description, price, image, status) VALUES (?, ?, ?, ?, ?, ?, ?)",
@@ -514,7 +521,7 @@ class ProductDataService(BaseDataService):
             ) for product in products
         ]
     
-    def get_by_id(self, product_id: int) -> Optional[Product]:
+    def     get_by_id(self, product_id: int) -> Optional[Product]:
         """Get product by ID"""
         conn = self._get_connection()
         cursor = conn.cursor()
@@ -594,10 +601,25 @@ class ProductDataService(BaseDataService):
         ]
     #endregion
     #region Update
+    def _image_process(self, image: str, base64: str) -> str:
+        """Process image for storage"""
+        if not image or not base64:
+            raise ValueError("Image and base64 data must be provided")
+        image_path = os.path.join(IMAGE_PATH, image)
+        if not os.path.exists(IMAGE_PATH):
+            os.makedirs(IMAGE_PATH)
+        with open(image_path, 'wb') as img_file:
+            img_file.write(b64.b64decode(base64.split(',')[1]))
+        return CURRENT_URL+"/images/" + image  # Return url
+
     def update(self, product_id: int, name=None, type=None, description=None, price=None, image=None, status=None) -> bool:
         """Update product information"""
         conn = self._get_connection()
         cursor = conn.cursor()
+        product = self.get_by_id(product_id)
+        if not product:
+            logger.error(f"Product with ID {product_id} not found.")
+            return False
         updates = []
         params = []
         if name is not None:
@@ -613,6 +635,18 @@ class ProductDataService(BaseDataService):
             updates.append("price = ?")
             params.append(price)
         if image is not None:
+            if product.image and product.image != image:
+                if os.path.exists(os.path.join(IMAGE_PATH, product.image)):
+                    os.remove(os.path.join(IMAGE_PATH, product.image))
+                try:
+                    extension = re.search(r'data:image/(.*?);base64,', image)
+                    if not extension:
+                        raise ValueError("Invalid image format")
+                    extension = extension.group(1)
+                    image = self._image_process(f"{product.id}_img.{extension}", image)
+                except ValueError as e:
+                    logger.error(f"Failed to process image: {e}")
+                    return False
             updates.append("image = ?")
             params.append(image)
         if status is not None:
@@ -692,6 +726,9 @@ class ProductDataService(BaseDataService):
         """Delete a product from the database"""
         conn = self._get_connection()
         cursor = conn.cursor()
+        product = self.get_by_id(product_id)
+        if product and product.image:
+            os.remove(os.path.join(IMAGE_PATH, product.image.removeprefix(CURRENT_URL + "/images/")))
         cursor.execute("DELETE FROM products WHERE id = ?", (product_id,))
         conn.commit()
         success = cursor.rowcount > 0