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