Fix issues

This commit is contained in:
Urtzi Alfaro
2025-10-01 14:39:10 +02:00
parent 6fa655275f
commit 36b44c41f1
5 changed files with 229 additions and 324 deletions

View File

@@ -8,6 +8,7 @@ import { SubscriptionSelection } from './SubscriptionSelection';
import PaymentForm from './PaymentForm'; import PaymentForm from './PaymentForm';
import { loadStripe } from '@stripe/stripe-js'; import { loadStripe } from '@stripe/stripe-js';
import { Elements } from '@stripe/react-stripe-js'; import { Elements } from '@stripe/react-stripe-js';
import { CheckCircle } from 'lucide-react';
// Initialize Stripe - In production, use environment variable // Initialize Stripe - In production, use environment variable
const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY || 'pk_test_51234567890123456789012345678901234567890123456789012345678901234567890123456789012345'); const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY || 'pk_test_51234567890123456789012345678901234567890123456789012345678901234567890123456789012345');
@@ -168,48 +169,69 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
}; };
// Render step indicator // Render step indicator
const renderStepIndicator = () => ( const renderStepIndicator = () => {
<div className="flex justify-center mb-6"> const steps = [
<div className="flex items-center space-x-4"> { key: 'basic_info', label: 'Información', number: 1 },
<div className={`flex items-center justify-center w-8 h-8 rounded-full ${ { key: 'subscription', label: 'Plan', number: 2 },
currentStep === 'basic_info' ? 'bg-color-primary text-white' : { key: 'payment', label: 'Pago', number: 3 }
currentStep === 'subscription' || currentStep === 'payment' ? 'bg-color-success text-white' : 'bg-bg-secondary text-text-secondary' ];
const getStepIndex = (step: RegistrationStep) => {
return steps.findIndex(s => s.key === step);
};
const currentIndex = getStepIndex(currentStep);
return (
<div className="mb-6 sm:mb-8">
<div className="flex justify-center items-center">
{steps.map((step, index) => (
<React.Fragment key={step.key}>
<div className="flex flex-col items-center">
<div className={`flex items-center justify-center w-8 h-8 sm:w-10 sm:h-10 rounded-full font-semibold text-sm transition-all duration-200 ${
index < currentIndex
? 'bg-color-success text-white'
: index === currentIndex
? 'bg-color-primary text-white ring-4 ring-color-primary/20'
: 'bg-bg-secondary text-text-secondary'
}`}> }`}>
1 {index < currentIndex ? (
<CheckCircle className="w-5 h-5" />
) : (
step.number
)}
</div> </div>
<div className="h-1 w-16 bg-border-primary"></div> <span className={`mt-1 sm:mt-2 text-[10px] sm:text-xs font-medium hidden sm:block ${
<div className={`flex items-center justify-center w-8 h-8 rounded-full ${ index <= currentIndex ? 'text-text-primary' : 'text-text-secondary'
currentStep === 'subscription' ? 'bg-color-primary text-white' :
currentStep === 'payment' ? 'bg-color-success text-white' : 'bg-bg-secondary text-text-secondary'
}`}> }`}>
2 {step.label}
</div> </span>
<div className="h-1 w-16 bg-border-primary"></div>
<div className={`flex items-center justify-center w-8 h-8 rounded-full ${
currentStep === 'payment' ? 'bg-color-primary text-white' : 'bg-bg-secondary text-text-secondary'
}`}>
3
</div> </div>
{index < steps.length - 1 && (
<div className={`h-0.5 w-8 sm:w-16 mx-2 sm:mx-4 transition-all duration-200 ${
index < currentIndex ? 'bg-color-success' : 'bg-border-primary'
}`} />
)}
</React.Fragment>
))}
</div> </div>
</div> </div>
); );
};
// Render current step // Render current step
const renderCurrentStep = () => { const renderCurrentStep = () => {
switch (currentStep) { switch (currentStep) {
case 'basic_info': case 'basic_info':
return ( return (
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
<div className="text-center mb-8"> <div className="text-center mb-4 sm:mb-6">
<h1 className="text-3xl font-bold text-text-primary mb-2"> <h1 className="text-2xl sm:text-3xl font-bold text-text-primary mb-2">
{t('auth:register.title', 'Crear Cuenta')} {t('auth:register.title', 'Crear Cuenta')}
</h1> </h1>
<p className="text-text-secondary text-lg"> <p className="text-text-secondary text-sm sm:text-base">
{t('auth:register.subtitle', 'Únete y comienza hoy mismo')} {t('auth:register.subtitle', 'Únete y comienza hoy mismo')}
</p> </p>
<p className="text-sm text-text-tertiary mt-2">
Paso 1 de 3: Información Básica
</p>
</div> </div>
<form onSubmit={(e) => { e.preventDefault(); handleNextStep(); }} className="space-y-6"> <form onSubmit={(e) => { e.preventDefault(); handleNextStep(); }} className="space-y-6">
@@ -396,14 +418,13 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
)} )}
</div> </div>
<div className="flex justify-between pt-4"> <div className="flex justify-end pt-4">
<div></div> {/* Spacer for alignment */}
<Button <Button
type="submit" type="submit"
variant="primary" variant="primary"
size="lg" size="lg"
disabled={isLoading || !validatePassword(formData.password) || !formData.acceptTerms || passwordMatchStatus !== 'match'} disabled={isLoading || !validatePassword(formData.password) || !formData.acceptTerms || passwordMatchStatus !== 'match'}
className="w-48" className="w-full sm:w-48"
> >
Siguiente Siguiente
</Button> </Button>
@@ -415,16 +436,13 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
case 'subscription': case 'subscription':
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="text-center mb-8"> <div className="text-center">
<h1 className="text-3xl font-bold text-text-primary mb-2"> <h1 className="text-2xl sm:text-3xl font-bold text-text-primary mb-2">
{t('auth:subscription.select_plan', 'Selecciona tu plan')} {t('auth:subscription.select_plan', 'Selecciona tu plan')}
</h1> </h1>
<p className="text-text-secondary text-lg"> <p className="text-text-secondary text-sm sm:text-base">
{t('auth:subscription.choose_plan', 'Elige el plan que mejor se adapte a tu negocio')} {t('auth:subscription.choose_plan', 'Elige el plan que mejor se adapte a tu negocio')}
</p> </p>
<p className="text-sm text-text-tertiary mt-2">
Paso 2 de 3: Plan de Suscripción
</p>
</div> </div>
<SubscriptionSelection <SubscriptionSelection
@@ -435,13 +453,13 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
trialSelected={useTrial} trialSelected={useTrial}
/> />
<div className="flex justify-between pt-4"> <div className="flex flex-col sm:flex-row justify-between gap-3 sm:gap-4 pt-2 border-t border-border-primary">
<Button <Button
variant="outline" variant="outline"
size="lg" size="lg"
onClick={handlePreviousStep} onClick={handlePreviousStep}
disabled={isLoading} disabled={isLoading}
className="w-48" className="w-full sm:w-48 order-2 sm:order-1"
> >
Anterior Anterior
</Button> </Button>
@@ -450,7 +468,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
size="lg" size="lg"
onClick={handleNextStep} onClick={handleNextStep}
disabled={isLoading} disabled={isLoading}
className="w-48" className="w-full sm:w-48 order-1 sm:order-2"
> >
Siguiente Siguiente
</Button> </Button>
@@ -460,17 +478,14 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
case 'payment': case 'payment':
return ( return (
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
<div className="text-center mb-8"> <div className="text-center mb-4 sm:mb-6">
<h1 className="text-3xl font-bold text-text-primary mb-2"> <h1 className="text-2xl sm:text-3xl font-bold text-text-primary mb-2">
{t('auth:payment.payment_info', 'Información de Pago')} {t('auth:payment.payment_info', 'Información de Pago')}
</h1> </h1>
<p className="text-text-secondary text-lg"> <p className="text-text-secondary text-xs sm:text-sm">
{t('auth:payment.secure_payment', 'Tu información de pago está protegida con encriptación de extremo a extremo')} {t('auth:payment.secure_payment', 'Tu información de pago está protegida con encriptación de extremo a extremo')}
</p> </p>
<p className="text-sm text-text-tertiary mt-2">
Paso 3 de 3: Procesamiento de Pago
</p>
</div> </div>
<Elements stripe={stripePromise}> <Elements stripe={stripePromise}>
@@ -482,13 +497,13 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
/> />
</Elements> </Elements>
<div className="flex justify-between pt-4"> <div className="flex justify-start pt-4">
<Button <Button
variant="outline" variant="outline"
size="lg" size="lg"
onClick={handlePreviousStep} onClick={handlePreviousStep}
disabled={isLoading} disabled={isLoading}
className="w-48" className="w-full sm:w-48"
> >
Anterior Anterior
</Button> </Button>
@@ -499,7 +514,7 @@ export const RegisterForm: React.FC<RegisterFormProps> = ({
}; };
return ( return (
<Card className={`p-8 w-full max-w-3xl ${className || ''}`} role="main"> <Card className={`p-4 sm:p-6 lg:p-8 w-full max-w-6xl ${className || ''}`} role="main">
{renderStepIndicator()} {renderStepIndicator()}
{renderCurrentStep()} {renderCurrentStep()}

View File

@@ -55,25 +55,16 @@ export const SubscriptionSelection: React.FC<SubscriptionSelectionProps> = ({
}; };
return ( return (
<div className={`space-y-6 ${className}`}> <div className={`space-y-4 ${className}`}>
<div className="text-center mb-8">
<h2 className="text-2xl font-bold text-text-primary mb-2">
{t('auth:subscription.select_plan', 'Selecciona tu plan')}
</h2>
<p className="text-text-secondary">
{t('auth:subscription.choose_plan', 'Elige el plan que mejor se adapte a tu negocio')}
</p>
</div>
{showTrialOption && ( {showTrialOption && (
<Card className="p-4 mb-6 bg-blue-50 border-blue-200"> <Card className="p-4 border-2 border-color-primary/30 bg-bg-primary">
<div className="flex items-center justify-between"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3 flex-1">
<div className="p-2 bg-blue-100 rounded-lg"> <div className="p-2.5 bg-color-primary/10 rounded-lg flex-shrink-0">
<Star className="w-5 h-5 text-blue-600" /> <Star className="w-5 h-5 text-color-primary" />
</div> </div>
<div> <div className="flex-1 min-w-0">
<h3 className="font-semibold text-text-primary"> <h3 className="font-semibold text-text-primary text-base">
{t('auth:subscription.trial_title', 'Prueba gratuita')} {t('auth:subscription.trial_title', 'Prueba gratuita')}
</h3> </h3>
<p className="text-sm text-text-secondary"> <p className="text-sm text-text-secondary">
@@ -83,8 +74,9 @@ export const SubscriptionSelection: React.FC<SubscriptionSelectionProps> = ({
</div> </div>
<Button <Button
variant={trialSelected ? "primary" : "outline"} variant={trialSelected ? "primary" : "outline"}
size="sm" size="md"
onClick={handleTrialToggle} onClick={handleTrialToggle}
className="w-full sm:w-auto flex-shrink-0 min-w-[100px]"
> >
{trialSelected {trialSelected
? t('auth:subscription.trial_active', 'Activo') ? t('auth:subscription.trial_active', 'Activo')
@@ -94,101 +86,100 @@ export const SubscriptionSelection: React.FC<SubscriptionSelectionProps> = ({
</Card> </Card>
)} )}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="space-y-3">
{Object.entries(availablePlans.plans).map(([planKey, plan]) => { {Object.entries(availablePlans.plans).map(([planKey, plan]) => {
const isSelected = selectedPlan === planKey; const isSelected = selectedPlan === planKey;
const getPlanColor = () => {
switch (planKey) {
case 'starter': return 'border-blue-500/30 bg-blue-500/5';
case 'professional': return 'border-purple-500/30 bg-purple-500/5';
case 'enterprise': return 'border-amber-500/30 bg-amber-500/5';
default: return 'border-border-primary bg-bg-secondary';
}
};
return ( return (
<Card <Card
key={planKey} key={planKey}
className={`relative p-6 cursor-pointer transition-all duration-200 hover:shadow-lg ${ className={`relative p-5 cursor-pointer transition-all duration-200 border-2 ${
getPlanColor() isSelected
} ${isSelected ? 'ring-2 ring-color-primary' : ''}`} ? 'border-color-primary bg-color-primary/5 shadow-lg'
: 'border-border-primary bg-bg-primary hover:border-color-primary/40 hover:shadow-md'
}`}
onClick={() => onPlanSelect(planKey)} onClick={() => onPlanSelect(planKey)}
> >
{/* Popular Badge */}
{plan.popular && ( {plan.popular && (
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2"> <div className="absolute -top-2.5 right-4 z-10">
<Badge variant="primary" className="px-3 py-1"> <Badge variant="primary" className="px-3 py-1 text-xs font-semibold flex items-center gap-1.5 shadow-md">
<Star className="w-3 h-3 mr-1" /> <Star className="w-3.5 h-3.5 fill-current" />
{t('auth:subscription.popular', 'Más Popular')} {t('auth:subscription.popular', 'Más Popular')}
</Badge> </Badge>
</div> </div>
)} )}
<div className="text-center mb-6"> {/* Horizontal Layout */}
<h4 className="text-xl font-bold text-text-primary mb-2">{plan.name}</h4> <div className="flex flex-col md:flex-row gap-6 items-start">
<div className="text-3xl font-bold text-color-primary mb-1"> {/* Left Section: Plan Info & Pricing */}
<div className="flex-shrink-0 md:w-52">
<h4 className="text-2xl font-bold text-text-primary mb-2">{plan.name}</h4>
<div className="flex items-baseline gap-1 mb-3">
<span className="text-4xl font-bold text-color-primary">
{subscriptionService.formatPrice(plan.monthly_price)} {subscriptionService.formatPrice(plan.monthly_price)}
<span className="text-lg text-text-secondary">/mes</span> </span>
</div> <span className="text-base text-text-secondary font-medium">/mes</span>
<p className="text-sm text-text-secondary">{plan.description}</p>
</div> </div>
<p className="text-sm text-text-secondary leading-relaxed mb-4">{plan.description}</p>
<div className="space-y-3 mb-6"> {/* Plan Limits */}
<div className="flex items-center gap-2 text-sm"> <div className="space-y-2.5 pt-2 border-t border-border-primary/50">
<Users className="w-4 h-4 text-color-primary" /> <div className="flex items-center gap-2.5 text-sm text-text-primary">
<span>{plan.max_users === -1 ? 'Usuarios ilimitados' : `${plan.max_users} usuarios`}</span> <Users className="w-4 h-4 text-color-primary flex-shrink-0" />
<span className="font-medium">{plan.max_users === -1 ? 'Usuarios ilimitados' : `${plan.max_users} usuarios`}</span>
</div> </div>
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2.5 text-sm text-text-primary">
<MapPin className="w-4 h-4 text-color-primary" /> <MapPin className="w-4 h-4 text-color-primary flex-shrink-0" />
<span>{plan.max_locations === -1 ? 'Ubicaciones ilimitadas' : `${plan.max_locations} ubicación${plan.max_locations > 1 ? 'es' : ''}`}</span> <span className="font-medium">{plan.max_locations === -1 ? 'Ubicaciones ilimitadas' : `${plan.max_locations} ubicación${plan.max_locations > 1 ? 'es' : ''}`}</span>
</div>
<div className="flex items-center gap-2.5 text-sm text-text-primary">
<Package className="w-4 h-4 text-color-primary flex-shrink-0" />
<span className="font-medium">{plan.max_products === -1 ? 'Productos ilimitados' : `${plan.max_products} productos`}</span>
</div> </div>
<div className="flex items-center gap-2 text-sm">
<Package className="w-4 h-4 text-color-primary" />
<span>{plan.max_products === -1 ? 'Productos ilimitados' : `${plan.max_products} productos`}</span>
</div> </div>
</div> </div>
{/* Features Section */} {/* Divider */}
<div className="border-t border-border-color pt-4 mb-6"> <div className="hidden md:block w-px self-stretch bg-border-primary/50"></div>
<h5 className="text-sm font-semibold text-text-primary mb-3 flex items-center">
<TrendingUp className="w-4 h-4 mr-2 text-color-primary" /> {/* Right Section: Features */}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-4">
<TrendingUp className="w-5 h-5 text-color-primary flex-shrink-0" />
<h5 className="text-base font-bold text-text-primary">
{t('auth:subscription.features', 'Funcionalidades Incluidas')} {t('auth:subscription.features', 'Funcionalidades Incluidas')}
</h5> </h5>
<div className="space-y-2"> </div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-x-6 gap-y-2.5">
{(() => { {(() => {
const getPlanFeatures = (planKey: string) => { const getPlanFeatures = (planKey: string) => {
switch (planKey) { switch (planKey) {
case 'starter': case 'starter':
return [ return [
'Panel de Control Básico', 'Panel de Control Básico',
'Gestión de Inventario', 'Gestión de Inventario',
'Gestión de Pedidos', 'Gestión de Pedidos',
'Gestión de Proveedores', 'Gestión de Proveedores',
'Punto de Venta Básico', 'Punto de Venta Básico'
'✗ Analytics Avanzados',
'✗ Pronósticos IA',
'✗ Insights Predictivos'
]; ];
case 'professional': case 'professional':
return [ return [
'✓ Panel de Control Avanzado', 'Todo lo de Starter',
'✓ Gestión de Inventario Completa', 'Panel Avanzado',
'Analytics de Ventas', 'Analytics de Ventas',
'Pronósticos con IA (92% precisión)', 'Pronósticos con IA',
'✓ Análisis de Rendimiento', 'Optimización de Producción'
'✓ Optimización de Producción',
'✓ Integración POS',
'✗ Insights Predictivos Avanzados'
]; ];
case 'enterprise': case 'enterprise':
return [ return [
'✓ Todas las funcionalidades Professional', 'Todo lo de Professional',
'Insights Predictivos con IA', 'Insights Predictivos IA',
'Analytics Multi-ubicación', 'Analytics Multi-ubicación',
'Integración ERP', 'Integración ERP',
'✓ API Personalizada', 'Soporte 24/7 Prioritario',
'✓ Gestor de Cuenta Dedicado', 'API Personalizada'
'✓ Soporte 24/7 Prioritario',
'✓ Demo Personalizada'
]; ];
default: default:
return []; return [];
@@ -196,21 +187,21 @@ export const SubscriptionSelection: React.FC<SubscriptionSelectionProps> = ({
}; };
return getPlanFeatures(planKey).map((feature, index) => ( return getPlanFeatures(planKey).map((feature, index) => (
<div key={index} className={`text-xs flex items-center gap-2 ${ <div key={index} className="flex items-start gap-2.5 text-sm">
feature.startsWith('✓') <CheckCircle className="w-4 h-4 text-color-success flex-shrink-0 mt-0.5" />
? 'text-green-600' <span className="text-text-primary leading-snug">{feature}</span>
: 'text-text-secondary opacity-60'
}`}>
<span>{feature}</span>
</div> </div>
)); ));
})()} })()}
</div> </div>
</div> </div>
{/* Action Button */}
<div className="flex-shrink-0 md:w-28 flex items-center justify-stretch md:justify-end pt-4 md:pt-0 w-full md:w-auto border-t md:border-t-0 md:border-l border-border-primary/50 md:pl-6">
<Button <Button
variant={isSelected ? "primary" : plan.popular ? "primary" : "outline"} variant={isSelected ? "primary" : "outline"}
className="w-full" className="w-full md:w-auto md:min-w-[100px]"
size="lg"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onPlanSelect(planKey); onPlanSelect(planKey);
@@ -218,16 +209,19 @@ export const SubscriptionSelection: React.FC<SubscriptionSelectionProps> = ({
> >
{isSelected ? ( {isSelected ? (
<> <>
<CheckCircle className="w-4 h-4 mr-2" /> <CheckCircle className="w-5 h-5 md:mr-2" />
{t('auth:subscription.selected', 'Seleccionado')} <span className="hidden md:inline">Seleccionado</span>
</> </>
) : ( ) : (
<> <>
{t('auth:subscription.select', 'Seleccionar Plan')} <span className="md:hidden">Seleccionar</span>
<ArrowRight className="w-4 h-4 ml-2" /> <span className="hidden md:inline">Elegir Plan</span>
<ArrowRight className="w-5 h-5 ml-2" />
</> </>
)} )}
</Button> </Button>
</div>
</div>
</Card> </Card>
); );
})} })}

View File

@@ -82,12 +82,7 @@ spec:
name: pos-integration-secrets name: pos-integration-secrets
- secretRef: - secretRef:
name: whatsapp-secrets name: whatsapp-secrets
env:
- name: TRAINING_PERSISTENCE_PATH
value: "/app/training_state"
volumeMounts: volumeMounts:
- name: training-state
mountPath: /app/training_state
- name: tmp-storage - name: tmp-storage
mountPath: /tmp mountPath: /tmp
resources: resources:
@@ -114,9 +109,6 @@ spec:
periodSeconds: 15 periodSeconds: 15
failureThreshold: 5 failureThreshold: 5
volumes: volumes:
- name: training-state
persistentVolumeClaim:
claimName: training-state-pvc
- name: tmp-storage - name: tmp-storage
emptyDir: emptyDir:
sizeLimit: 2Gi sizeLimit: 2Gi
@@ -140,20 +132,3 @@ spec:
selector: selector:
app.kubernetes.io/name: training-service app.kubernetes.io/name: training-service
app.kubernetes.io/component: microservice app.kubernetes.io/component: microservice
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: training-state-pvc
namespace: bakery-ia
labels:
app.kubernetes.io/name: training-service
app.kubernetes.io/component: storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard

View File

@@ -311,85 +311,6 @@ patches:
kind: Deployment kind: Deployment
name: external-service name: external-service
patch: |- patch: |-
- op: add
path: /spec/template/spec/initContainers
value:
- name: wait-for-external-db
image: postgres:13-alpine
command:
- sh
- -c
- |
until pg_isready -h $EXTERNAL_DB_HOST -p $EXTERNAL_DB_PORT -U $EXTERNAL_DB_USER; do
echo "Waiting for external database..."
sleep 2
done
echo "External database is ready!"
env:
- name: EXTERNAL_DB_HOST
valueFrom:
configMapKeyRef:
name: bakery-config
key: EXTERNAL_DB_HOST
- name: EXTERNAL_DB_PORT
valueFrom:
configMapKeyRef:
name: bakery-config
key: DB_PORT
- name: EXTERNAL_DB_USER
valueFrom:
secretKeyRef:
name: database-secrets
key: EXTERNAL_DB_USER
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: database-secrets
key: EXTERNAL_DB_PASSWORD
- name: wait-for-rabbitmq
image: busybox:1.35
command:
- sh
- -c
- |
until nc -z $RABBITMQ_HOST $RABBITMQ_PORT; do
echo "Waiting for RabbitMQ..."
sleep 2
done
echo "RabbitMQ is ready!"
env:
- name: RABBITMQ_HOST
valueFrom:
configMapKeyRef:
name: bakery-config
key: RABBITMQ_HOST
- name: RABBITMQ_PORT
valueFrom:
configMapKeyRef:
name: bakery-config
key: RABBITMQ_PORT
- name: wait-for-redis
image: redis:7-alpine
command:
- sh
- -c
- |
until redis-cli -h $REDIS_HOST -p $REDIS_PORT ping; do
echo "Waiting for Redis..."
sleep 2
done
echo "Redis is ready!"
env:
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: bakery-config
key: REDIS_HOST
- name: REDIS_PORT
valueFrom:
configMapKeyRef:
name: bakery-config
key: REDIS_PORT
- op: replace - op: replace
path: /spec/template/spec/containers/0/resources path: /spec/template/spec/containers/0/resources
value: value:

View File

@@ -85,34 +85,34 @@ class NotificationService(StandardFastAPIService):
} }
# Define custom health checks for notification service components # Define custom health checks for notification service components
async def check_email_service(): #async def check_email_service():
"""Check email service health - service is ready even if credentials are invalid""" # """Check email service health - service is ready even if credentials are invalid"""
try: # try:
if not self.email_service: # if not self.email_service:
return False # return False
# Service is considered healthy if it's initialized, even if credentials fail # # Service is considered healthy if it's initialized, even if credentials fail
# This allows the pod to be ready while external services may have config issues # # This allows the pod to be ready while external services may have config issues
await self.email_service.health_check() # await self.email_service.health_check()
return True # return True
except Exception as e: # except Exception as e:
# Log but don't fail readiness - email service config issues shouldn't block the pod # Log but don't fail readiness - email service config issues shouldn't block the pod
self.logger.error("Email service health check failed", error=str(e)) # self.logger.error("Email service health check failed", error=str(e))
# Return True to indicate service is ready (initialized) even if credentials are wrong # Return True to indicate service is ready (initialized) even if credentials are wrong
return True # return True
async def check_whatsapp_service(): #async def check_whatsapp_service():
"""Check WhatsApp service health - service is ready even if credentials are invalid""" # """Check WhatsApp service health - service is ready even if credentials are invalid"""
try: # try:
if not self.whatsapp_service: # if not self.whatsapp_service:
return False # return False
# Service is considered healthy if it's initialized, even if credentials fail # Service is considered healthy if it's initialized, even if credentials fail
await self.whatsapp_service.health_check() # await self.whatsapp_service.health_check()
return True # return True
except Exception as e: # except Exception as e:
# Log but don't fail readiness - WhatsApp config issues shouldn't block the pod # Log but don't fail readiness - WhatsApp config issues shouldn't block the pod
self.logger.error("WhatsApp service health check failed", error=str(e)) # self.logger.error("WhatsApp service health check failed", error=str(e))
# Return True to indicate service is ready (initialized) even if credentials are wrong # Return True to indicate service is ready (initialized) even if credentials are wrong
return True # return True
async def check_sse_service(): async def check_sse_service():
"""Check SSE service health""" """Check SSE service health"""
@@ -125,14 +125,14 @@ class NotificationService(StandardFastAPIService):
self.logger.error("SSE service health check failed", error=str(e)) self.logger.error("SSE service health check failed", error=str(e))
return False return False
async def check_messaging(): #async def check_messaging():
"""Check messaging service health""" # """Check messaging service health"""
try: # try:
from app.services.messaging import notification_publisher # from app.services.messaging import notification_publisher
return bool(notification_publisher and notification_publisher.connected) # return bool(notification_publisher and notification_publisher.connected)
except Exception as e: # except Exception as e:
self.logger.error("Messaging health check failed", error=str(e)) # self.logger.error("Messaging health check failed", error=str(e))
return False # return False
super().__init__( super().__init__(
service_name="notification-service", service_name="notification-service",
@@ -145,10 +145,10 @@ class NotificationService(StandardFastAPIService):
database_manager=database_manager, database_manager=database_manager,
expected_tables=notification_expected_tables, expected_tables=notification_expected_tables,
custom_health_checks={ custom_health_checks={
"email_service": check_email_service, # "email_service": check_email_service,
"whatsapp_service": check_whatsapp_service, # "whatsapp_service": check_whatsapp_service,
"sse_service": check_sse_service, "sse_service": check_sse_service,
"messaging": check_messaging # "messaging": check_messaging
}, },
enable_messaging=True, enable_messaging=True,
custom_metrics=notification_custom_metrics custom_metrics=notification_custom_metrics