Harden platform security, reliability, and CI after full audit.

Close deployment IDOR and gate stub payment endpoints, add production
secret validation, health probes, Redis-backed build progress, GitHub
Actions CI, expanded tests, billing/k8s refactors, and ops runbooks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-06-29 20:59:49 +03:30
parent a87bc49393
commit 837f0fa63f
83 changed files with 3953 additions and 1308 deletions
@@ -0,0 +1,33 @@
-- Mobile-first auth: phone is the login identifier, email becomes an optional
-- contact field, plus a table of short-lived one-time SMS codes for verifying
-- a phone (registration/login completion and number changes).
-- Email becomes optional (login no longer uses it). Postgres treats NULLs as
-- distinct, so the existing UNIQUE constraint keeps working for users without one.
ALTER TABLE users ALTER COLUMN email DROP NOT NULL;
ALTER TABLE users ADD COLUMN IF NOT EXISTS phone VARCHAR;
ALTER TABLE users ADD COLUMN IF NOT EXISTS "phoneVerified" BOOLEAN NOT NULL DEFAULT FALSE;
-- Unique per non-null phone (NULLs allowed for legacy email-only staff accounts).
CREATE UNIQUE INDEX IF NOT EXISTS users_phone_unique ON users (phone) WHERE phone IS NOT NULL;
-- One-time SMS verification codes (hashed).
DO $$ BEGIN
CREATE TYPE verification_codes_purpose_enum AS ENUM ('login', 'change_phone');
EXCEPTION WHEN duplicate_object THEN null; END $$;
CREATE TABLE IF NOT EXISTS verification_codes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"userId" UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
purpose verification_codes_purpose_enum NOT NULL,
destination VARCHAR NOT NULL,
"codeHash" VARCHAR NOT NULL,
"expiresAt" TIMESTAMPTZ NOT NULL,
attempts INT NOT NULL DEFAULT 0,
"consumedAt" TIMESTAMPTZ,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS verification_codes_user_purpose_idx
ON verification_codes ("userId", purpose);
@@ -93,14 +93,14 @@ spec:
mountPath: /app/uploads
livenessProbe:
httpGet:
path: /api/docs
path: /api/v1/health
port: 4000
initialDelaySeconds: 60
periodSeconds: 15
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /api/docs
path: /api/v1/ready
port: 4000
initialDelaySeconds: 20
periodSeconds: 10
@@ -0,0 +1,35 @@
{{- if .Values.monitoring.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "cloudhost-platform.fullname" . }}-backend-metrics
namespace: {{ include "cloudhost-platform.namespace" . }}
labels:
{{- include "cloudhost-platform.labels" . | nindent 4 }}
app.kubernetes.io/component: backend
spec:
type: ClusterIP
ports:
- name: http
port: 4000
targetPort: 4000
selector:
app: {{ include "cloudhost-platform.backend.fullname" . }}
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "cloudhost-platform.fullname" . }}-backend
namespace: {{ include "cloudhost-platform.namespace" . }}
labels:
{{- include "cloudhost-platform.labels" . | nindent 4 }}
release: prometheus
spec:
selector:
matchLabels:
app: {{ include "cloudhost-platform.backend.fullname" . }}
endpoints:
- port: http
path: /api/v1/health
interval: 30s
{{- end }}
@@ -0,0 +1,62 @@
{{- if .Values.backups.postgres.enabled }}
apiVersion: batch/v1
kind: CronJob
metadata:
name: {{ include "cloudhost-platform.fullname" . }}-postgres-backup
namespace: {{ include "cloudhost-platform.namespace" . }}
labels:
{{- include "cloudhost-platform.labels" . | nindent 4 }}
spec:
schedule: {{ .Values.backups.postgres.schedule | quote }}
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: {{ .Values.images.postgres | quote }}
env:
- name: PGHOST
value: {{ include "cloudhost-platform.postgres.fullname" . }}
- name: PGPORT
value: "5432"
- name: PGDATABASE
value: {{ .Values.postgres.database | quote }}
- name: PGUSER
value: {{ .Values.postgres.username | quote }}
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: {{ include "cloudhost-platform.secretName" . }}
key: postgres-password
command:
- sh
- -c
- |
set -e
STAMP=$(date +%Y%m%d-%H%M%S)
FILE="/backup/cloudhost-${STAMP}.sql.gz"
pg_dump | gzip > "$FILE"
echo "Backup written to $FILE"
volumeMounts:
- name: backup
mountPath: /backup
volumes:
- name: backup
persistentVolumeClaim:
claimName: {{ include "cloudhost-platform.fullname" . }}-postgres-backup
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "cloudhost-platform.fullname" . }}-postgres-backup
namespace: {{ include "cloudhost-platform.namespace" . }}
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: {{ .Values.backups.postgres.storageSize }}
{{- end }}
@@ -97,3 +97,12 @@ ingress:
migrations:
enabled: true
image: postgres:16-alpine
monitoring:
enabled: false
backups:
postgres:
enabled: false
schedule: "0 3 * * *"
storageSize: 10Gi