From bc173e7ab71dadb2f306c0b096c650735c8aa1c7 Mon Sep 17 00:00:00 2001 From: dvirlabs Date: Thu, 11 Dec 2025 03:57:16 +0200 Subject: [PATCH] Add tasko --- .../templates/db-schema-configmap.yaml | 95 ++++++++ .../tasko-chart/templates/db-statefulset.yaml | 6 + charts/tasko-chart/values.yaml | 203 +++++++++--------- manifests/tasko/values.yaml | 10 +- 4 files changed, 212 insertions(+), 102 deletions(-) create mode 100644 charts/tasko-chart/templates/db-schema-configmap.yaml diff --git a/charts/tasko-chart/templates/db-schema-configmap.yaml b/charts/tasko-chart/templates/db-schema-configmap.yaml new file mode 100644 index 0000000..272752a --- /dev/null +++ b/charts/tasko-chart/templates/db-schema-configmap.yaml @@ -0,0 +1,95 @@ +{{- if .Values.postgres }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "tasko.fullname" . }}-db-schema + labels: + {{- include "tasko.labels" . | nindent 4 }} + app.kubernetes.io/component: database +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 UNIQUE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_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 INDEX IF NOT EXISTS idx_users_display_name ON users (display_name); + + -- Create tasks table + CREATE TABLE IF NOT EXISTS tasks ( + id SERIAL PRIMARY KEY, + title TEXT NOT NULL, + description TEXT, + status TEXT NOT NULL DEFAULT 'pending', -- pending / in_progress / completed / cancelled + priority TEXT DEFAULT 'medium', -- low / medium / high / urgent + due_date TIMESTAMP, + user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, + assigned_to INTEGER REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + completed_at TIMESTAMP + ); + + -- Indexes for tasks + CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks (status); + CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks (priority); + CREATE INDEX IF NOT EXISTS idx_tasks_user_id ON tasks (user_id); + CREATE INDEX IF NOT EXISTS idx_tasks_assigned_to ON tasks (assigned_to); + CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks (due_date); + + -- Create tags table + CREATE TABLE IF NOT EXISTS tags ( + id SERIAL PRIMARY KEY, + name TEXT UNIQUE NOT NULL, + color TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + -- Create task_tags junction table + CREATE TABLE IF NOT EXISTS task_tags ( + task_id INTEGER REFERENCES tasks(id) ON DELETE CASCADE, + tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE, + PRIMARY KEY (task_id, tag_id) + ); + + CREATE INDEX IF NOT EXISTS idx_task_tags_task_id ON task_tags (task_id); + CREATE INDEX IF NOT EXISTS idx_task_tags_tag_id ON task_tags (tag_id); + + -- Add display_name column if it doesn't exist (migration support) + 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; + ALTER TABLE users ADD CONSTRAINT users_display_name_key UNIQUE (display_name); + END IF; + END $$; + + -- Verify schema + SELECT 'Users table:' as info; + SELECT column_name, data_type, is_nullable + FROM information_schema.columns + WHERE table_name = 'users' + ORDER BY ordinal_position; + + SELECT 'Tasks table:' as info; + SELECT column_name, data_type, is_nullable + FROM information_schema.columns + WHERE table_name = 'tasks' + ORDER BY ordinal_position; +{{- end }} diff --git a/charts/tasko-chart/templates/db-statefulset.yaml b/charts/tasko-chart/templates/db-statefulset.yaml index 66b3f55..50ddcc5 100644 --- a/charts/tasko-chart/templates/db-statefulset.yaml +++ b/charts/tasko-chart/templates/db-statefulset.yaml @@ -39,6 +39,8 @@ spec: volumeMounts: - name: data mountPath: /var/lib/postgresql/data + - name: init-sql + mountPath: /docker-entrypoint-initdb.d livenessProbe: exec: command: @@ -66,6 +68,10 @@ spec: limits: cpu: {{ .Values.postgres.resources.limits.cpu }} memory: {{ .Values.postgres.resources.limits.memory }} + volumes: + - name: init-sql + configMap: + name: {{ include "tasko.fullname" . }}-db-schema volumeClaimTemplates: - metadata: name: data diff --git a/charts/tasko-chart/values.yaml b/charts/tasko-chart/values.yaml index 1308377..e155c3a 100644 --- a/charts/tasko-chart/values.yaml +++ b/charts/tasko-chart/values.yaml @@ -1,47 +1,13 @@ -replicaCount: 1 - -frontend: - image: - repository: tasko-frontend - pullPolicy: IfNotPresent - tag: "latest" - - service: - type: ClusterIP - port: 80 - targetPort: 80 - - ingress: - enabled: true - className: "nginx" - annotations: - cert-manager.io/cluster-issuer: "letsencrypt-prod" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - hosts: - - host: tasko.dvirlabs.com - paths: - - path: / - pathType: Prefix - tls: - - secretName: tasko-frontend-tls - hosts: - - tasko.dvirlabs.com - - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 100m - memory: 128Mi - - env: - - name: VITE_API_URL - value: "https://api-tasko.dvirlabs.com" +global: + namespace: my-apps + imagePullSecrets: [] +# Backend configuration backend: + name: backend + replicaCount: 1 image: - repository: tasko-backend + repository: harbor.dvirlabs.com/my-apps/tasko-backend pullPolicy: IfNotPresent tag: "latest" @@ -50,76 +16,121 @@ backend: port: 8000 targetPort: 8000 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + + env: + PYTHONUNBUFFERED: "1" + ingress: enabled: true - className: "nginx" + className: "traefik" annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" cert-manager.io/cluster-issuer: "letsencrypt-prod" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - nginx.ingress.kubernetes.io/cors-allow-origin: "https://tasko.dvirlabs.com" - nginx.ingress.kubernetes.io/enable-cors: "true" - nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS" - nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" hosts: - host: api-tasko.dvirlabs.com paths: - path: / pathType: Prefix tls: - - secretName: tasko-backend-tls + - secretName: api-tasko-tls hosts: - api-tasko.dvirlabs.com + +# Frontend configuration +frontend: + name: frontend + replicaCount: 1 + image: + repository: harbor.dvirlabs.com/my-apps/tasko-frontend + pullPolicy: IfNotPresent + tag: "latest" - resources: - limits: - cpu: 500m - memory: 512Mi - requests: - cpu: 250m - memory: 256Mi + service: + type: ClusterIP + port: 80 + targetPort: 80 env: - - name: DATABASE_URL - valueFrom: - secretKeyRef: - name: tasko-secrets - key: database-url + VITE_API_URL: "https://api-tasko.dvirlabs.com" + + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi -postgresql: - enabled: true - auth: - username: tasko_user - password: tasko_password - database: tasko_db - primary: - persistence: - enabled: true - size: 8Gi - resources: - limits: - cpu: 500m - memory: 512Mi - requests: - cpu: 250m - memory: 256Mi + ingress: + enabled: true + className: "traefik" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: tasko.dvirlabs.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: tasko-tls + hosts: + - tasko.dvirlabs.com -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" +# PostgreSQL configuration +postgres: + name: db + image: + repository: postgres + tag: "16-alpine" + pullPolicy: IfNotPresent + + user: tasko_user + password: tasko_password + database: tasko_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 -serviceAccount: - create: true - annotations: {} - name: "" - -podAnnotations: {} - -podSecurityContext: {} - -securityContext: {} - -nodeSelector: {} - -tolerations: [] - -affinity: {} +# Ingress configuration +ingress: + enabled: false # Individual frontend/backend ingress resources handle routing instead + className: "traefik" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: tasko.dvirlabs.com + paths: + - path: / + pathType: Prefix + backend: frontend + tls: + - secretName: tasko-tls + hosts: + - tasko.dvirlabs.com diff --git a/manifests/tasko/values.yaml b/manifests/tasko/values.yaml index 64c7361..5c36ac2 100644 --- a/manifests/tasko/values.yaml +++ b/manifests/tasko/values.yaml @@ -12,8 +12,8 @@ backend: service: type: ClusterIP - port: 8001 - targetPort: 8001 + port: 8000 + targetPort: 8000 resources: requests: cpu: 100m @@ -22,8 +22,7 @@ backend: cpu: 500m memory: 512Mi env: - - name: PYTHONUNBUFFERED - value: "1" + PYTHONUNBUFFERED: "1" ingress: enabled: true className: "traefik" @@ -54,8 +53,7 @@ frontend: port: 80 targetPort: 80 env: - - name: VITE_API_URL - value: "https://api-tasko.dvirlabs.com" + VITE_API_URL: "https://api-tasko.dvirlabs.com" resources: requests: cpu: 50m