Update schema.sql
This commit is contained in:
parent
d020819ec0
commit
cf6c9e7bd7
@ -1,14 +0,0 @@
|
|||||||
apiVersion: v2
|
|
||||||
name: my-recipes
|
|
||||||
description: Complete recipe management application with PostgreSQL, FastAPI backend, and React frontend
|
|
||||||
type: application
|
|
||||||
version: 1.0.0
|
|
||||||
appVersion: "1.0.0"
|
|
||||||
keywords:
|
|
||||||
- recipes
|
|
||||||
- fastapi
|
|
||||||
- react
|
|
||||||
- postgresql
|
|
||||||
maintainers:
|
|
||||||
- name: Development Team
|
|
||||||
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-add-missing-tables
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
data:
|
|
||||||
add-tables.sql: |
|
|
||||||
-- Create grocery lists table
|
|
||||||
CREATE TABLE IF NOT EXISTS grocery_lists (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
items TEXT[] NOT NULL DEFAULT '{}',
|
|
||||||
owner_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
is_pinned BOOLEAN DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Create grocery list shares table
|
|
||||||
CREATE TABLE IF NOT EXISTS grocery_list_shares (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
list_id INTEGER NOT NULL REFERENCES grocery_lists(id) ON DELETE CASCADE,
|
|
||||||
shared_with_user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
can_edit BOOLEAN DEFAULT FALSE,
|
|
||||||
shared_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
UNIQUE(list_id, shared_with_user_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_grocery_lists_owner_id ON grocery_lists (owner_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_grocery_list_shares_list_id ON grocery_list_shares (list_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_grocery_list_shares_user_id ON grocery_list_shares (shared_with_user_id);
|
|
||||||
|
|
||||||
-- Create notifications table
|
|
||||||
CREATE TABLE IF NOT EXISTS notifications (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
type TEXT NOT NULL,
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
related_id INTEGER,
|
|
||||||
is_read BOOLEAN DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications (user_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_notifications_is_read ON notifications (is_read);
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-add-missing-tables
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
annotations:
|
|
||||||
"helm.sh/hook": post-upgrade
|
|
||||||
"helm.sh/hook-weight": "6"
|
|
||||||
"helm.sh/hook-delete-policy": before-hook-creation
|
|
||||||
spec:
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
restartPolicy: Never
|
|
||||||
containers:
|
|
||||||
- name: add-tables
|
|
||||||
image: postgres:16-alpine
|
|
||||||
env:
|
|
||||||
- name: PGHOST
|
|
||||||
value: {{ .Release.Name }}-db
|
|
||||||
- name: PGPORT
|
|
||||||
value: "{{ .Values.postgres.port }}"
|
|
||||||
- name: PGDATABASE
|
|
||||||
value: {{ .Values.postgres.database }}
|
|
||||||
- name: PGUSER
|
|
||||||
value: {{ .Values.postgres.user }}
|
|
||||||
- name: PGPASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PASSWORD
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
echo "Waiting for database to be ready..."
|
|
||||||
until pg_isready -h $PGHOST -p $PGPORT -U $PGUSER; do
|
|
||||||
echo "Database not ready, waiting..."
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "Database ready, adding missing tables..."
|
|
||||||
psql -v ON_ERROR_STOP=1 -f /sql/add-tables.sql
|
|
||||||
echo "Tables added successfully!"
|
|
||||||
volumeMounts:
|
|
||||||
- name: sql
|
|
||||||
mountPath: /sql
|
|
||||||
volumes:
|
|
||||||
- name: sql
|
|
||||||
configMap:
|
|
||||||
name: {{ .Release.Name }}-add-missing-tables
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-admin-init
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
data:
|
|
||||||
create-admin.py: |
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import psycopg2
|
|
||||||
import bcrypt
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
def wait_for_db():
|
|
||||||
"""Wait for database to be ready"""
|
|
||||||
max_retries = 30
|
|
||||||
retry_count = 0
|
|
||||||
|
|
||||||
while retry_count < max_retries:
|
|
||||||
try:
|
|
||||||
conn = psycopg2.connect(
|
|
||||||
host=os.environ['DB_HOST'],
|
|
||||||
port=os.environ['DB_PORT'],
|
|
||||||
database=os.environ['DB_NAME'],
|
|
||||||
user=os.environ['DB_USER'],
|
|
||||||
password=os.environ['DB_PASSWORD']
|
|
||||||
)
|
|
||||||
conn.close()
|
|
||||||
print("✓ Database is ready")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
retry_count += 1
|
|
||||||
print(f"Waiting for database... ({retry_count}/{max_retries})")
|
|
||||||
sleep(2)
|
|
||||||
|
|
||||||
print("✗ Database connection timeout")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def create_admin_user():
|
|
||||||
"""Create admin user if not exists"""
|
|
||||||
try:
|
|
||||||
# Hash the password
|
|
||||||
password = os.environ.get('ADMIN_PASSWORD', 'admin123')
|
|
||||||
password_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
|
||||||
|
|
||||||
# Connect to database
|
|
||||||
conn = psycopg2.connect(
|
|
||||||
host=os.environ['DB_HOST'],
|
|
||||||
port=os.environ['DB_PORT'],
|
|
||||||
database=os.environ['DB_NAME'],
|
|
||||||
user=os.environ['DB_USER'],
|
|
||||||
password=os.environ['DB_PASSWORD']
|
|
||||||
)
|
|
||||||
cur = conn.cursor()
|
|
||||||
|
|
||||||
# Insert admin user
|
|
||||||
cur.execute("""
|
|
||||||
INSERT INTO users (username, email, password_hash, first_name, last_name, display_name, is_admin)
|
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
|
||||||
ON CONFLICT (username) DO UPDATE SET
|
|
||||||
email = EXCLUDED.email,
|
|
||||||
password_hash = EXCLUDED.password_hash,
|
|
||||||
first_name = EXCLUDED.first_name,
|
|
||||||
last_name = EXCLUDED.last_name,
|
|
||||||
display_name = EXCLUDED.display_name,
|
|
||||||
is_admin = EXCLUDED.is_admin
|
|
||||||
""", (
|
|
||||||
os.environ.get('ADMIN_USERNAME', 'admin'),
|
|
||||||
os.environ.get('ADMIN_EMAIL', 'admin@myrecipes.local'),
|
|
||||||
password_hash,
|
|
||||||
os.environ.get('ADMIN_FIRST_NAME', 'Admin'),
|
|
||||||
os.environ.get('ADMIN_LAST_NAME', 'User'),
|
|
||||||
os.environ.get('ADMIN_DISPLAY_NAME', 'מנהל'),
|
|
||||||
True
|
|
||||||
))
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
cur.close()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
print(f"✓ Admin user '{os.environ.get('ADMIN_USERNAME', 'admin')}' created/updated successfully")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ Error creating admin user: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("Starting admin user initialization...")
|
|
||||||
|
|
||||||
if not wait_for_db():
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not create_admin_user():
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
print("✓ Admin user initialization completed")
|
|
||||||
sys.exit(0)
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-admin-init-{{ .Release.Revision }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-admin-init
|
|
||||||
component: init
|
|
||||||
annotations:
|
|
||||||
"helm.sh/hook": post-install,post-upgrade
|
|
||||||
"helm.sh/hook-weight": "10"
|
|
||||||
"helm.sh/hook-delete-policy": before-hook-creation
|
|
||||||
spec:
|
|
||||||
ttlSecondsAfterFinished: 300
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-admin-init
|
|
||||||
spec:
|
|
||||||
restartPolicy: Never
|
|
||||||
containers:
|
|
||||||
- name: admin-init
|
|
||||||
image: python:3.12-slim
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
pip install --no-cache-dir psycopg2-binary bcrypt > /dev/null 2>&1
|
|
||||||
python3 /scripts/create-admin.py
|
|
||||||
env:
|
|
||||||
- name: DB_HOST
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_HOST
|
|
||||||
- name: DB_PORT
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PORT
|
|
||||||
- name: DB_NAME
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_NAME
|
|
||||||
- name: DB_USER
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_USER
|
|
||||||
- name: DB_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PASSWORD
|
|
||||||
- name: ADMIN_USERNAME
|
|
||||||
value: {{ .Values.admin.username | quote }}
|
|
||||||
- name: ADMIN_EMAIL
|
|
||||||
value: {{ .Values.admin.email | quote }}
|
|
||||||
- name: ADMIN_PASSWORD
|
|
||||||
value: {{ .Values.admin.password | quote }}
|
|
||||||
- name: ADMIN_FIRST_NAME
|
|
||||||
value: {{ .Values.admin.firstName | quote }}
|
|
||||||
- name: ADMIN_LAST_NAME
|
|
||||||
value: {{ .Values.admin.lastName | quote }}
|
|
||||||
- name: ADMIN_DISPLAY_NAME
|
|
||||||
value: {{ .Values.admin.displayName | quote }}
|
|
||||||
volumeMounts:
|
|
||||||
- name: init-script
|
|
||||||
mountPath: /scripts
|
|
||||||
volumes:
|
|
||||||
- name: init-script
|
|
||||||
configMap:
|
|
||||||
name: {{ .Release.Name }}-admin-init
|
|
||||||
defaultMode: 0755
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-app-secrets
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
# Google OAuth
|
|
||||||
GOOGLE_CLIENT_ID: {{ .Values.oauth.google.clientId | quote }}
|
|
||||||
GOOGLE_CLIENT_SECRET: {{ .Values.oauth.google.clientSecret | quote }}
|
|
||||||
GOOGLE_REDIRECT_URI: {{ .Values.oauth.google.redirectUri | quote }}
|
|
||||||
|
|
||||||
# Microsoft Entra ID (Azure AD) OAuth
|
|
||||||
AZURE_CLIENT_ID: {{ .Values.oauth.azure.clientId | quote }}
|
|
||||||
AZURE_CLIENT_SECRET: {{ .Values.oauth.azure.clientSecret | quote }}
|
|
||||||
AZURE_TENANT_ID: {{ .Values.oauth.azure.tenantId | quote }}
|
|
||||||
AZURE_REDIRECT_URI: {{ .Values.oauth.azure.redirectUri | quote }}
|
|
||||||
|
|
||||||
# Email Configuration
|
|
||||||
SMTP_HOST: {{ .Values.email.smtpHost | quote }}
|
|
||||||
SMTP_PORT: {{ .Values.email.smtpPort | quote }}
|
|
||||||
SMTP_USER: {{ .Values.email.smtpUser | quote }}
|
|
||||||
SMTP_PASSWORD: {{ .Values.email.smtpPassword | quote }}
|
|
||||||
SMTP_FROM: {{ .Values.email.smtpFrom | quote }}
|
|
||||||
|
|
||||||
# Frontend URL for redirects
|
|
||||||
FRONTEND_URL: {{ .Values.frontend.externalUrl | quote }}
|
|
||||||
|
|
||||||
# S3 Backup Configuration
|
|
||||||
S3_ENDPOINT: {{ .Values.s3.endpoint | quote }}
|
|
||||||
S3_ACCESS_KEY: {{ .Values.s3.accessKey | quote }}
|
|
||||||
S3_SECRET_KEY: {{ .Values.s3.secretKey | quote }}
|
|
||||||
S3_BUCKET_NAME: {{ .Values.s3.bucketName | quote }}
|
|
||||||
S3_REGION: {{ .Values.s3.region | quote }}
|
|
||||||
BACKUP_INTERVAL: {{ .Values.s3.backupInterval | quote }}
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
component: backend
|
|
||||||
spec:
|
|
||||||
replicas: {{ .Values.backend.replicaCount }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
component: backend
|
|
||||||
spec:
|
|
||||||
initContainers:
|
|
||||||
- name: db-migration
|
|
||||||
image: postgres:16-alpine
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
echo "Waiting for database to be ready..."
|
|
||||||
until pg_isready -h $DB_HOST -U $DB_USER; do
|
|
||||||
echo "Database not ready, waiting..."
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "Database is ready, running migration..."
|
|
||||||
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -U $DB_USER -d $DB_NAME -f /migration/migrate.sql
|
|
||||||
echo "Migration completed successfully"
|
|
||||||
env:
|
|
||||||
- name: DB_HOST
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_HOST
|
|
||||||
- name: DB_PORT
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PORT
|
|
||||||
- name: DB_NAME
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_NAME
|
|
||||||
- name: DB_USER
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_USER
|
|
||||||
- name: DB_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PASSWORD
|
|
||||||
volumeMounts:
|
|
||||||
- name: migration-script
|
|
||||||
mountPath: /migration
|
|
||||||
containers:
|
|
||||||
- name: {{ .Values.backend.name }}
|
|
||||||
image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}"
|
|
||||||
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
|
|
||||||
ports:
|
|
||||||
- containerPort: {{ .Values.backend.service.targetPort }}
|
|
||||||
name: http
|
|
||||||
protocol: TCP
|
|
||||||
env:
|
|
||||||
{{- if .Values.backend.env }}
|
|
||||||
{{- range $key, $value := .Values.backend.env }}
|
|
||||||
- name: {{ $key }}
|
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
- secretRef:
|
|
||||||
name: {{ .Release.Name }}-app-secrets
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /docs
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 15
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 30
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /docs
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /docs
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 2
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: {{ .Values.backend.resources.requests.cpu }}
|
|
||||||
memory: {{ .Values.backend.resources.requests.memory }}
|
|
||||||
limits:
|
|
||||||
cpu: {{ .Values.backend.resources.limits.cpu }}
|
|
||||||
memory: {{ .Values.backend.resources.limits.memory }}
|
|
||||||
volumes:
|
|
||||||
- name: migration-script
|
|
||||||
configMap:
|
|
||||||
name: {{ .Release.Name }}-db-migration
|
|
||||||
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
component: backend
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.backend.service.type }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.backend.service.port }}
|
|
||||||
targetPort: {{ .Values.backend.service.targetPort }}
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.backend.name }}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-db-migration
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
data:
|
|
||||||
migrate.sql: |
|
|
||||||
-- Add made_by column to recipes if it doesn't exist
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'recipes' AND column_name = 'made_by'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE recipes ADD COLUMN made_by TEXT;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- Create index if it doesn't exist
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_made_by ON recipes (made_by);
|
|
||||||
|
|
||||||
-- Add is_admin column to users if it doesn't exist
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users' AND column_name = 'is_admin'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE users ADD COLUMN is_admin BOOLEAN DEFAULT FALSE;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- Add auth_provider column to users if it doesn't exist
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users' AND column_name = 'auth_provider'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE users ADD COLUMN auth_provider TEXT DEFAULT 'local';
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- Verify recipes schema
|
|
||||||
SELECT column_name, data_type
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_name = 'recipes'
|
|
||||||
ORDER BY ordinal_position;
|
|
||||||
|
|
||||||
-- Verify users schema
|
|
||||||
SELECT column_name, data_type
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users'
|
|
||||||
ORDER BY ordinal_position;
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
apiVersion: batch/v1
|
|
||||||
kind: Job
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-db-migration-{{ .Release.Revision }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-db-migration
|
|
||||||
component: migration
|
|
||||||
annotations:
|
|
||||||
"helm.sh/hook": post-upgrade,post-install
|
|
||||||
"helm.sh/hook-weight": "5"
|
|
||||||
"helm.sh/hook-delete-policy": before-hook-creation
|
|
||||||
spec:
|
|
||||||
ttlSecondsAfterFinished: 300
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-db-migration
|
|
||||||
spec:
|
|
||||||
restartPolicy: Never
|
|
||||||
containers:
|
|
||||||
- name: migrate
|
|
||||||
image: postgres:16-alpine
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
echo "Waiting for database to be ready..."
|
|
||||||
until pg_isready -h $DB_HOST -U $DB_USER; do
|
|
||||||
echo "Database not ready, waiting..."
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "Database is ready, running migration..."
|
|
||||||
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -U $DB_USER -d $DB_NAME -f /migration/migrate.sql
|
|
||||||
echo "Migration completed successfully"
|
|
||||||
env:
|
|
||||||
- name: DB_HOST
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_HOST
|
|
||||||
- name: DB_PORT
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PORT
|
|
||||||
- name: DB_NAME
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_NAME
|
|
||||||
- name: DB_USER
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_USER
|
|
||||||
- name: DB_PASSWORD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
key: DB_PASSWORD
|
|
||||||
volumeMounts:
|
|
||||||
- name: migration-script
|
|
||||||
mountPath: /migration
|
|
||||||
volumes:
|
|
||||||
- name: migration-script
|
|
||||||
configMap:
|
|
||||||
name: {{ .Release.Name }}-db-migration
|
|
||||||
|
|
||||||
@ -1,134 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-db-schema
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
data:
|
|
||||||
schema.sql: |
|
|
||||||
-- Create users table
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
username TEXT UNIQUE NOT NULL,
|
|
||||||
email TEXT UNIQUE NOT NULL,
|
|
||||||
password_hash TEXT NOT NULL,
|
|
||||||
first_name TEXT,
|
|
||||||
last_name TEXT,
|
|
||||||
display_name TEXT NOT NULL,
|
|
||||||
is_admin BOOLEAN DEFAULT FALSE,
|
|
||||||
auth_provider TEXT DEFAULT 'local',
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users (username);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users (email);
|
|
||||||
|
|
||||||
-- Create recipes table (matching backend schema with TEXT[] arrays)
|
|
||||||
CREATE TABLE IF NOT EXISTS recipes (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
meal_type TEXT NOT NULL, -- breakfast / lunch / dinner / snack
|
|
||||||
time_minutes INTEGER NOT NULL,
|
|
||||||
tags TEXT[] NOT NULL DEFAULT '{}', -- {"מהיר", "בריא"}
|
|
||||||
ingredients TEXT[] NOT NULL DEFAULT '{}', -- {"ביצה", "עגבניה", "מלח"}
|
|
||||||
steps TEXT[] NOT NULL DEFAULT '{}', -- {"לחתוך", "לבשל", ...}
|
|
||||||
image TEXT, -- Base64-encoded image or image URL
|
|
||||||
made_by TEXT, -- Person who created this recipe version
|
|
||||||
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL, -- Recipe owner
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Indexes for filters
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_meal_type
|
|
||||||
ON recipes (meal_type);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_time_minutes
|
|
||||||
ON recipes (time_minutes);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_made_by
|
|
||||||
ON recipes (made_by);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_recipes_user_id
|
|
||||||
ON recipes (user_id);
|
|
||||||
|
|
||||||
-- Add new columns to existing users table
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users' AND column_name = 'first_name'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE users ADD COLUMN first_name TEXT;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users' AND column_name = 'last_name'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE users ADD COLUMN last_name TEXT;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users' AND column_name = 'display_name'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE users ADD COLUMN display_name TEXT;
|
|
||||||
-- Set display_name to username for existing users
|
|
||||||
UPDATE users SET display_name = username WHERE display_name IS NULL;
|
|
||||||
ALTER TABLE users ALTER COLUMN display_name SET NOT NULL;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.columns
|
|
||||||
WHERE table_name = 'users' AND column_name = 'is_admin'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE users ADD COLUMN is_admin BOOLEAN DEFAULT FALSE;
|
|
||||||
END IF;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
-- Create grocery lists table
|
|
||||||
CREATE TABLE IF NOT EXISTS grocery_lists (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL,
|
|
||||||
items TEXT[] NOT NULL DEFAULT '{}',
|
|
||||||
owner_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
is_pinned BOOLEAN DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Create grocery list shares table
|
|
||||||
CREATE TABLE IF NOT EXISTS grocery_list_shares (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
list_id INTEGER NOT NULL REFERENCES grocery_lists(id) ON DELETE CASCADE,
|
|
||||||
shared_with_user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
can_edit BOOLEAN DEFAULT FALSE,
|
|
||||||
shared_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
UNIQUE(list_id, shared_with_user_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_grocery_lists_owner_id ON grocery_lists (owner_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_grocery_list_shares_list_id ON grocery_list_shares (list_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_grocery_list_shares_user_id ON grocery_list_shares (shared_with_user_id);
|
|
||||||
|
|
||||||
-- Create notifications table
|
|
||||||
CREATE TABLE IF NOT EXISTS notifications (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
type TEXT NOT NULL,
|
|
||||||
message TEXT NOT NULL,
|
|
||||||
related_id INTEGER,
|
|
||||||
is_read BOOLEAN DEFAULT FALSE,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications (user_id);
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_notifications_is_read ON notifications (is_read);
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-db-credentials
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
type: Opaque
|
|
||||||
stringData:
|
|
||||||
{{- if .Values.database }}
|
|
||||||
# External database (e.g., AWS RDS)
|
|
||||||
DB_HOST: {{ .Values.database.host | quote }}
|
|
||||||
DB_PORT: {{ .Values.database.port | quote }}
|
|
||||||
DB_NAME: {{ .Values.database.name | quote }}
|
|
||||||
DB_USER: {{ .Values.database.user | quote }}
|
|
||||||
DB_PASSWORD: {{ .Values.database.password | quote }}
|
|
||||||
{{- else }}
|
|
||||||
# In-cluster PostgreSQL
|
|
||||||
DB_HOST: {{ printf "%s-%s-headless.%s.svc.cluster.local" .Release.Name .Values.postgres.name .Values.global.namespace }}
|
|
||||||
DB_PORT: "{{ .Values.postgres.port }}"
|
|
||||||
DB_NAME: {{ .Values.postgres.database | quote }}
|
|
||||||
DB_USER: {{ .Values.postgres.user | quote }}
|
|
||||||
DB_PASSWORD: {{ .Values.postgres.password | quote }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
{{- if not .Values.database }}
|
|
||||||
{{- /* Only deploy in-cluster PostgreSQL services if external database is not configured */ -}}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.postgres.name }}-headless
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
component: database
|
|
||||||
spec:
|
|
||||||
clusterIP: None
|
|
||||||
selector:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
ports:
|
|
||||||
- name: postgres
|
|
||||||
port: {{ .Values.postgres.port }}
|
|
||||||
targetPort: {{ .Values.postgres.port }}
|
|
||||||
protocol: TCP
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
component: database
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.postgres.service.type }}
|
|
||||||
selector:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
ports:
|
|
||||||
- name: postgres
|
|
||||||
port: {{ .Values.postgres.service.port }}
|
|
||||||
targetPort: {{ .Values.postgres.port }}
|
|
||||||
protocol: TCP
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
{{- if not .Values.database }}
|
|
||||||
{{- /* Only deploy in-cluster PostgreSQL if external database is not configured */ -}}
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: StatefulSet
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
component: database
|
|
||||||
spec:
|
|
||||||
serviceName: {{ .Release.Name }}-{{ .Values.postgres.name }}-headless
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.postgres.name }}
|
|
||||||
component: database
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: postgres
|
|
||||||
image: "{{ .Values.postgres.image.repository }}:{{ .Values.postgres.image.tag }}"
|
|
||||||
imagePullPolicy: {{ .Values.postgres.image.pullPolicy }}
|
|
||||||
ports:
|
|
||||||
- containerPort: {{ .Values.postgres.port }}
|
|
||||||
name: postgres
|
|
||||||
protocol: TCP
|
|
||||||
env:
|
|
||||||
- name: POSTGRES_USER
|
|
||||||
value: {{ .Values.postgres.user | quote }}
|
|
||||||
- name: POSTGRES_PASSWORD
|
|
||||||
value: {{ .Values.postgres.password | quote }}
|
|
||||||
- name: POSTGRES_DB
|
|
||||||
value: {{ .Values.postgres.database | quote }}
|
|
||||||
- name: PGDATA
|
|
||||||
value: /var/lib/postgresql/data/pgdata
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /var/lib/postgresql/data
|
|
||||||
- name: init-sql
|
|
||||||
mountPath: /docker-entrypoint-initdb.d
|
|
||||||
livenessProbe:
|
|
||||||
exec:
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- pg_isready -U {{ .Values.postgres.user }}
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
readinessProbe:
|
|
||||||
exec:
|
|
||||||
command:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- pg_isready -U {{ .Values.postgres.user }}
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 2
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: {{ .Values.postgres.resources.requests.cpu }}
|
|
||||||
memory: {{ .Values.postgres.resources.requests.memory }}
|
|
||||||
limits:
|
|
||||||
cpu: {{ .Values.postgres.resources.limits.cpu }}
|
|
||||||
memory: {{ .Values.postgres.resources.limits.memory }}
|
|
||||||
volumes:
|
|
||||||
- name: init-sql
|
|
||||||
configMap:
|
|
||||||
name: {{ .Release.Name }}-db-schema
|
|
||||||
volumeClaimTemplates:
|
|
||||||
- metadata:
|
|
||||||
name: data
|
|
||||||
spec:
|
|
||||||
accessModes:
|
|
||||||
- {{ .Values.postgres.persistence.accessMode }}
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: {{ .Values.postgres.persistence.size }}
|
|
||||||
{{- if .Values.postgres.persistence.storageClass }}
|
|
||||||
storageClassName: {{ .Values.postgres.persistence.storageClass | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
component: frontend
|
|
||||||
spec:
|
|
||||||
replicas: {{ .Values.frontend.replicaCount }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
component: frontend
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: {{ .Values.frontend.name }}
|
|
||||||
image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}"
|
|
||||||
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
|
|
||||||
ports:
|
|
||||||
- containerPort: {{ .Values.frontend.service.targetPort }}
|
|
||||||
name: http
|
|
||||||
protocol: TCP
|
|
||||||
{{- with .Values.frontend.env }}
|
|
||||||
env:
|
|
||||||
{{- range $key, $value := . }}
|
|
||||||
- name: {{ $key }}
|
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 10
|
|
||||||
timeoutSeconds: 5
|
|
||||||
failureThreshold: 3
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /
|
|
||||||
port: http
|
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 5
|
|
||||||
timeoutSeconds: 3
|
|
||||||
failureThreshold: 2
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: {{ .Values.frontend.resources.requests.cpu }}
|
|
||||||
memory: {{ .Values.frontend.resources.requests.memory }}
|
|
||||||
limits:
|
|
||||||
cpu: {{ .Values.frontend.resources.limits.cpu }}
|
|
||||||
memory: {{ .Values.frontend.resources.limits.memory }}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
component: frontend
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.frontend.service.type }}
|
|
||||||
ports:
|
|
||||||
- port: {{ .Values.frontend.service.port }}
|
|
||||||
targetPort: {{ .Values.frontend.service.targetPort }}
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app: {{ .Release.Name }}-{{ .Values.frontend.name }}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
{{- if .Values.frontend.ingress.enabled }}
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-frontend
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-frontend
|
|
||||||
component: frontend
|
|
||||||
{{- with .Values.frontend.ingress.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
{{- if .Values.frontend.ingress.className }}
|
|
||||||
ingressClassName: {{ .Values.frontend.ingress.className }}
|
|
||||||
{{- end }}
|
|
||||||
rules:
|
|
||||||
{{- range .Values.frontend.ingress.hosts }}
|
|
||||||
- host: {{ .host | quote }}
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
{{- range .paths }}
|
|
||||||
- path: {{ .path }}
|
|
||||||
pathType: {{ .pathType }}
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ $.Release.Name }}-{{ $.Values.frontend.name }}
|
|
||||||
port:
|
|
||||||
number: {{ $.Values.frontend.service.port }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.frontend.ingress.tls }}
|
|
||||||
tls:
|
|
||||||
{{- range .Values.frontend.ingress.tls }}
|
|
||||||
- hosts:
|
|
||||||
{{- range .hosts }}
|
|
||||||
- {{ . | quote }}
|
|
||||||
{{- end }}
|
|
||||||
secretName: {{ .secretName }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
{{- if .Values.backend.ingress.enabled }}
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-backend
|
|
||||||
namespace: {{ .Values.global.namespace }}
|
|
||||||
labels:
|
|
||||||
app: {{ .Release.Name }}-backend
|
|
||||||
component: backend
|
|
||||||
{{- with .Values.backend.ingress.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
{{- if .Values.backend.ingress.className }}
|
|
||||||
ingressClassName: {{ .Values.backend.ingress.className }}
|
|
||||||
{{- end }}
|
|
||||||
rules:
|
|
||||||
{{- range .Values.backend.ingress.hosts }}
|
|
||||||
- host: {{ .host | quote }}
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
{{- range .paths }}
|
|
||||||
- path: {{ .path }}
|
|
||||||
pathType: {{ .pathType }}
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: {{ $.Release.Name }}-{{ $.Values.backend.name }}
|
|
||||||
port:
|
|
||||||
number: {{ $.Values.backend.service.port }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.backend.ingress.tls }}
|
|
||||||
tls:
|
|
||||||
{{- range .Values.backend.ingress.tls }}
|
|
||||||
- hosts:
|
|
||||||
{{- range .hosts }}
|
|
||||||
- {{ . | quote }}
|
|
||||||
{{- end }}
|
|
||||||
secretName: {{ .secretName }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
@ -1,182 +0,0 @@
|
|||||||
global:
|
|
||||||
namespace: my-apps
|
|
||||||
imagePullSecrets: []
|
|
||||||
|
|
||||||
# Backend configuration
|
|
||||||
backend:
|
|
||||||
name: backend
|
|
||||||
replicaCount: 2
|
|
||||||
image:
|
|
||||||
repository: harbor.dvirlabs.com/my-apps/my-recipes-backend
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
tag: "latest"
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 8000
|
|
||||||
targetPort: 8000
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 512Mi
|
|
||||||
|
|
||||||
env:
|
|
||||||
PYTHONUNBUFFERED: "1"
|
|
||||||
|
|
||||||
# Secrets are created in db-secret.yaml
|
|
||||||
# These are passed via envFrom secretRef
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: "alb"
|
|
||||||
annotations:
|
|
||||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
|
||||||
alb.ingress.kubernetes.io/target-type: ip
|
|
||||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
|
|
||||||
alb.ingress.kubernetes.io/certificate-arn: "" # Set in project-specific values
|
|
||||||
hosts:
|
|
||||||
- host: api-my-recipes.dvirlabs.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
tls:
|
|
||||||
- secretName: api-my-recipes-tls
|
|
||||||
hosts:
|
|
||||||
- api-my-recipes.dvirlabs.com
|
|
||||||
|
|
||||||
# Frontend configuration
|
|
||||||
frontend:
|
|
||||||
name: frontend
|
|
||||||
replicaCount: 2
|
|
||||||
image:
|
|
||||||
repository: harbor.dvirlabs.com/my-apps/my-recipes-frontend
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
tag: "latest"
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 80
|
|
||||||
targetPort: 80
|
|
||||||
|
|
||||||
env:
|
|
||||||
API_BASE: "https://api-my-recipes.dvirlabs.com"
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 50m
|
|
||||||
memory: 64Mi
|
|
||||||
limits:
|
|
||||||
cpu: 200m
|
|
||||||
memory: 256Mi
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: "alb"
|
|
||||||
annotations:
|
|
||||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
|
||||||
alb.ingress.kubernetes.io/target-type: ip
|
|
||||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
|
|
||||||
alb.ingress.kubernetes.io/certificate-arn: "" # Set in project-specific values
|
|
||||||
hosts:
|
|
||||||
- host: my-recipes.dvirlabs.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
tls:
|
|
||||||
- secretName: my-recipes-tls
|
|
||||||
hosts:
|
|
||||||
- my-recipes.dvirlabs.com
|
|
||||||
externalUrl: "https://my-recipes.dvirlabs.com"
|
|
||||||
|
|
||||||
# PostgreSQL configuration
|
|
||||||
postgres:
|
|
||||||
name: db
|
|
||||||
image:
|
|
||||||
repository: postgres
|
|
||||||
tag: "16-alpine"
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
|
|
||||||
user: recipes_user
|
|
||||||
password: recipes_password
|
|
||||||
database: recipes_db
|
|
||||||
port: 5432
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 5432
|
|
||||||
targetPort: 5432
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
accessMode: ReadWriteOnce
|
|
||||||
storageClass: "nfs-client"
|
|
||||||
size: 10Gi
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 1000m
|
|
||||||
memory: 1Gi
|
|
||||||
|
|
||||||
# OAuth Configuration
|
|
||||||
oauth:
|
|
||||||
google:
|
|
||||||
clientId: "143092846986-hsi59m0on2c9rb5qrdoejfceieao2ioc.apps.googleusercontent.com"
|
|
||||||
clientSecret: "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"
|
|
||||||
redirectUri: "https://api-my-recipes.dvirlabs.com/auth/google/callback"
|
|
||||||
|
|
||||||
azure:
|
|
||||||
clientId: "db244cf5-eb11-4738-a2ea-5b0716c9ec0a"
|
|
||||||
clientSecret: "Zad8Q~qRBxaQq8up0lLXAq4pHzrVM2JFGFJhHaDp"
|
|
||||||
tenantId: "consumers"
|
|
||||||
redirectUri: "https://api-my-recipes.dvirlabs.com/auth/azure/callback"
|
|
||||||
|
|
||||||
# Email Configuration
|
|
||||||
email:
|
|
||||||
smtpHost: "smtp.gmail.com"
|
|
||||||
smtpPort: "587"
|
|
||||||
smtpUser: "dvirlabs@gmail.com"
|
|
||||||
smtpPassword: "agaanrhbbazbdytv"
|
|
||||||
smtpFrom: "dvirlabs@gmail.com"
|
|
||||||
|
|
||||||
# S3 Backup Configuration
|
|
||||||
s3:
|
|
||||||
endpoint: "https://s3.amazonaws.com" # Can be overridden for specific regions
|
|
||||||
accessKey: "" # Set this in project-specific values.yaml
|
|
||||||
secretKey: "" # Set this in project-specific values.yaml
|
|
||||||
bucketName: "" # Set this in project-specific values.yaml
|
|
||||||
region: "us-east-1" # Set this in project-specific values.yaml
|
|
||||||
backupInterval: "weekly" # Options: test (1 min), daily, weekly
|
|
||||||
|
|
||||||
# Admin User Configuration
|
|
||||||
admin:
|
|
||||||
username: "admin"
|
|
||||||
email: "admin@example.com"
|
|
||||||
password: "admin123" # Change this in production!
|
|
||||||
firstName: "Admin"
|
|
||||||
lastName: "User"
|
|
||||||
displayName: "Admin User"
|
|
||||||
|
|
||||||
# Ingress configuration
|
|
||||||
ingress:
|
|
||||||
enabled: false # Individual frontend/backend ingress resources handle routing instead
|
|
||||||
className: "nginx"
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
|
||||||
hosts:
|
|
||||||
- host: my-recipes.dvirlabs.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend: frontend
|
|
||||||
tls:
|
|
||||||
- secretName: recipes-tls
|
|
||||||
hosts:
|
|
||||||
- my-recipes.dvirlabs.com
|
|
||||||
|
|
||||||
110
aws/values.yaml
110
aws/values.yaml
@ -1,110 +0,0 @@
|
|||||||
# Project-specific values for AWS EKS deployment
|
|
||||||
# This file overrides the base values in my-recipes-chart/values.yaml
|
|
||||||
|
|
||||||
global:
|
|
||||||
namespace: my-apps
|
|
||||||
|
|
||||||
# Backend configuration
|
|
||||||
backend:
|
|
||||||
replicaCount: 2
|
|
||||||
image:
|
|
||||||
repository: 430842105273.dkr.ecr.eu-central-1.amazonaws.com/my-recipes-backend # Update with your ECR repository
|
|
||||||
tag: "latest"
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
className: "alb"
|
|
||||||
annotations:
|
|
||||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
|
||||||
alb.ingress.kubernetes.io/target-type: ip
|
|
||||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
|
|
||||||
# Add your ACM certificate ARN below if you have one
|
|
||||||
# alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:..."
|
|
||||||
hosts:
|
|
||||||
- host: api-my-recipes.aws-dvirlabs.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
|
|
||||||
# Frontend configuration
|
|
||||||
frontend:
|
|
||||||
replicaCount: 2
|
|
||||||
image:
|
|
||||||
repository: 430842105273.dkr.ecr.eu-central-1.amazonaws.com/my-recipes-frontend # Update with your ECR repository
|
|
||||||
tag: "latest"
|
|
||||||
|
|
||||||
env:
|
|
||||||
API_BASE: "https://api-my-recipes.aws-dvirlabs.com"
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
className: "alb"
|
|
||||||
annotations:
|
|
||||||
alb.ingress.kubernetes.io/scheme: internet-facing
|
|
||||||
alb.ingress.kubernetes.io/target-type: ip
|
|
||||||
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
|
|
||||||
# Add your ACM certificate ARN below if you have one
|
|
||||||
# alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:..."
|
|
||||||
hosts:
|
|
||||||
- host: my-recipes.aws-dvirlabs.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
|
|
||||||
externalUrl: "https://my-recipes.aws-dvirlabs.com"
|
|
||||||
|
|
||||||
# PostgreSQL configuration
|
|
||||||
postgres:
|
|
||||||
# For AWS RDS, set this to use external database
|
|
||||||
# Leave enabled: true to use in-cluster database
|
|
||||||
enabled: false # Set to false if using RDS
|
|
||||||
|
|
||||||
# If using RDS, these values are ignored but kept for reference
|
|
||||||
persistence:
|
|
||||||
storageClass: "gp3" # EKS default storage class
|
|
||||||
size: 20Gi
|
|
||||||
|
|
||||||
# OAuth Configuration
|
|
||||||
oauth:
|
|
||||||
google:
|
|
||||||
clientId: "143092846986-hsi59m0on2c9rb5qrdoejfceieao2ioc.apps.googleusercontent.com"
|
|
||||||
clientSecret: "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"
|
|
||||||
redirectUri: "https://api-my-recipes.aws-dvirlabs.com/auth/google/callback"
|
|
||||||
|
|
||||||
azure:
|
|
||||||
clientId: "db244cf5-eb11-4738-a2ea-5b0716c9ec0a"
|
|
||||||
clientSecret: "Zad8Q~qRBxaQq8up0lLXAq4pHzrVM2JFGFJhHaDp"
|
|
||||||
tenantId: "consumers"
|
|
||||||
redirectUri: "https://api-my-recipes.aws-dvirlabs.com/auth/azure/callback"
|
|
||||||
|
|
||||||
# Email Configuration
|
|
||||||
email:
|
|
||||||
smtpHost: "smtp.gmail.com"
|
|
||||||
smtpPort: "587"
|
|
||||||
smtpUser: "dvirlabs@gmail.com"
|
|
||||||
smtpPassword: "agaanrhbbazbdytv"
|
|
||||||
smtpFrom: "dvirlabs@gmail.com"
|
|
||||||
|
|
||||||
# S3 Backup Configuration for AWS
|
|
||||||
s3:
|
|
||||||
endpoint: "https://s3.eu-central-1.amazonaws.com" # Update with your region
|
|
||||||
accessKey: "AKIAXXXXXXXXXXXXXXXX" # Replace with your AWS Access Key
|
|
||||||
secretKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Replace with your AWS Secret Key
|
|
||||||
bucketName: "my-recipes-backups" # Update with your S3 bucket name
|
|
||||||
region: "eu-central-1" # Update with your region
|
|
||||||
backupInterval: "weekly"
|
|
||||||
|
|
||||||
# Admin User Configuration
|
|
||||||
admin:
|
|
||||||
username: "admin"
|
|
||||||
email: "dvirlabs@gmail.com"
|
|
||||||
password: "AdminPassword123!" # Change this after first login!
|
|
||||||
firstName: "Dvir"
|
|
||||||
lastName: "Admin"
|
|
||||||
displayName: "Dvir Admin"
|
|
||||||
|
|
||||||
# Database connection for AWS RDS (used when postgres.enabled: false)
|
|
||||||
database:
|
|
||||||
host: "my-recipes-rds.chw4omcguqv7.eu-central-1.rds.amazonaws.com"
|
|
||||||
port: "5432"
|
|
||||||
name: "recipes_db"
|
|
||||||
user: "recipes_user"
|
|
||||||
password: "recipes_password" # Store securely in AWS Secrets Manager in production
|
|
||||||
Loading…
x
Reference in New Issue
Block a user