From f5c89159f48eacf93ac4ad559e3aacfd3305f3c2 Mon Sep 17 00:00:00 2001 From: maaz519 Date: Thu, 23 Jul 2026 14:35:07 +0530 Subject: [PATCH] =?UTF-8?q?chore(search):=20wire=20Meilisearch=20=E2=80=94?= =?UTF-8?q?=20local=20compose=20+=20prod=20k8s=20manifests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a meilisearch service to the dev docker-compose (port 7700, persistent volume) and MEILI_URL/MEILI_KEY (+ IIOS_CRED_KEY, IIOS_MEDIA_GC_INTERVAL_MS) to .env.example. Add deploy/meilisearch.yaml — a ready-to-apply k8s Deployment/Service/PVC/Secret for the ArgoCD prod stack (copy into k8s-pods/services/iios/), with wiring notes for the iios-service env (MEILI_URL=http://meilisearch:7700, MEILI_KEY from the secret). Co-Authored-By: Claude Opus 4.8 --- .env.example | 15 +++ docker-compose.yml | 19 ++++ packages/iios-service/deploy/meilisearch.yaml | 98 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 packages/iios-service/deploy/meilisearch.yaml diff --git a/.env.example b/.env.example index e4929e0..b99a182 100644 --- a/.env.example +++ b/.env.example @@ -6,3 +6,18 @@ DATABASE_URL="postgresql://iios:iios@localhost:5434/iios?schema=public" JWT_SECRET="dev-only-change-me" # Per-app HS256 secrets, JSON map keyed by appId (the `session` platform port) APP_SECRETS={"portal-demo":"dev-secret"} + +# Full-text message search (Meilisearch). Unset MEILI_URL → search is disabled (no-op). +# MEILI_KEY must equal the meilisearch server's MEILI_MASTER_KEY (docker-compose default below). +MEILI_URL="http://localhost:7700" +MEILI_KEY="dev-meili-master-key" +# Consumed by docker-compose's meilisearch service; keep in sync with MEILI_KEY. +MEILI_MASTER_KEY="dev-meili-master-key" + +# BYO integration credentials at rest (Twilio SMS, SMTP): 32-byte base64 AES-256-GCM key. +# Generate with: openssl rand -base64 32 +IIOS_CRED_KEY="" + +# Orphaned-media garbage collection (uploaded-but-never-sent attachments). +# Interval 0 = off; set e.g. 3600000 (hourly) in prod. Grace defaults to 24h. +IIOS_MEDIA_GC_INTERVAL_MS=0 diff --git a/docker-compose.yml b/docker-compose.yml index 275a98c..b5eea66 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,5 +29,24 @@ services: timeout: 5s retries: 10 + # Full-text message search. The service uses it when MEILI_URL is set (else search no-ops). + # Point the service at it with MEILI_URL=http://localhost:7700 + MEILI_KEY=. + meilisearch: + image: getmeili/meilisearch:v1.11 + container_name: iios-meili + ports: + - "7700:7700" + environment: + MEILI_NO_ANALYTICS: "true" + MEILI_MASTER_KEY: ${MEILI_MASTER_KEY:-dev-meili-master-key} + volumes: + - iios_meili_data:/meili_data + healthcheck: + test: ["CMD-SHELL", "curl -sf http://localhost:7700/health || exit 1"] + interval: 5s + timeout: 5s + retries: 10 + volumes: iios_postgres_data: + iios_meili_data: diff --git a/packages/iios-service/deploy/meilisearch.yaml b/packages/iios-service/deploy/meilisearch.yaml new file mode 100644 index 0000000..d8be578 --- /dev/null +++ b/packages/iios-service/deploy/meilisearch.yaml @@ -0,0 +1,98 @@ +# Meilisearch for IIOS message search (prod). +# +# IIOS deploys to Kubernetes via ArgoCD from the platform-engineering/k8s-pods repo +# (services/iios/). This file is the template for the prod Meilisearch instance — copy it into +# k8s-pods/services/iios/meilisearch.yaml and let ArgoCD sync it. Then wire the service: +# +# 1. Create the master key once (32+ random chars) and set it in the Secret below (or via a +# sealed-secret / your secret manager — do NOT commit a real key in plaintext): +# kubectl -n create secret generic iios-meili \ +# --from-literal=MEILI_MASTER_KEY="$(openssl rand -base64 32)" +# 2. Add these env vars to the iios-service Deployment (services/iios/deployment.yaml): +# - name: MEILI_URL +# value: http://meilisearch:7700 +# - name: MEILI_KEY +# valueFrom: { secretKeyRef: { name: iios-meili, key: MEILI_MASTER_KEY } } +# 3. After the first deploy, backfill existing messages once: POST /v1/search/reindex per tenant +# (authenticated as a scope member) — new messages index automatically on message.sent. +# +# Set the namespace on each object (or via kustomize) to match the iios-service namespace so the +# `meilisearch` Service DNS resolves as http://meilisearch:7700 from the pod. +--- +apiVersion: v1 +kind: Secret +metadata: + name: iios-meili +type: Opaque +stringData: + # Replace with a real key (or manage out-of-band per note #1 and delete this stringData block). + MEILI_MASTER_KEY: "CHANGE_ME_meili_master_key" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: iios-meili-data +spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meilisearch + labels: { app: meilisearch } +spec: + replicas: 1 + strategy: { type: Recreate } # single RWO volume — never run two writers + selector: + matchLabels: { app: meilisearch } + template: + metadata: + labels: { app: meilisearch } + spec: + containers: + - name: meilisearch + image: getmeili/meilisearch:v1.11 + ports: + - containerPort: 7700 + env: + - name: MEILI_ENV + value: "production" + - name: MEILI_NO_ANALYTICS + value: "true" + - name: MEILI_MASTER_KEY + valueFrom: + secretKeyRef: { name: iios-meili, key: MEILI_MASTER_KEY } + volumeMounts: + - name: data + mountPath: /meili_data + resources: + requests: { cpu: "100m", memory: "256Mi" } + limits: { cpu: "1", memory: "1Gi" } + readinessProbe: + httpGet: { path: /health, port: 7700 } + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: { path: /health, port: 7700 } + initialDelaySeconds: 15 + periodSeconds: 20 + volumes: + - name: data + persistentVolumeClaim: + claimName: iios-meili-data +--- +apiVersion: v1 +kind: Service +metadata: + name: meilisearch + labels: { app: meilisearch } +spec: + selector: { app: meilisearch } + ports: + - port: 7700 + targetPort: 7700 + # ClusterIP (internal only) — reached by iios-service as http://meilisearch:7700. + type: ClusterIP