Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ lerna-debug.log*

# Generic
postgresql
!helm/chart/templates/postgresql/
!helm/chart/templates/postgresql/*.yaml
node_modules
dist
dist-ssr
Expand Down
29 changes: 26 additions & 3 deletions helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,36 @@ A Helm chart for deploying [Sparkyfitness](https://github.com/CodeWithCJ/SparkyF
helm install sparkyfitness ./chart
```

This deploys Sparkyfitness with a bundled PostgreSQL instance, auto-generated secrets, and sane defaults. Access via `kubectl port-forward svc/sparkyfitness-frontend 8080:80`.
This deploys Sparkyfitness with a bundled PostgreSQL instance, auto-generated secrets, and sane defaults.

### Local Testing (Port-Forwarding)

By default, the chart enables private network CORS and trusts `http://localhost:3004` to make local testing easy.

In separate terminals, run:

```bash
kubectl port-forward svc/sparkyfitness-frontend 3004:80
```
```bash
kubectl port-forward svc/sparkyfitness-server 3010:3010
```

Now, navigate to [http://localhost:3004](http://localhost:3004) in your browser.

### Run Tests

To verify that the frontend is up and running:

```bash
helm test sparkyfitness
```

## Database

### Bundled PostgreSQL (default)

Enabled by default. Credentials are auto-generated on first install and preserved across upgrades.
Enabled by default. The chart creates a PostgreSQL `Service` + `StatefulSet` and chart-managed credentials (unless `postgresql.auth.existingSecret` is set). Passwords are auto-generated on first install when `postgresql.auth.password` is empty and preserved across upgrades.

```yaml
postgresql:
Expand Down Expand Up @@ -81,7 +104,7 @@ The chart manages five separate Kubernetes Secrets:
|--------|------|---------|
| `<release>-app` | `api_encryption_key`, `better_auth_secret` | Server |
| `<release>-appdb` | `username`, `password` | Server (app DB user) |
| `<release>-postgres` | `username`, `password` | Server (DB owner) |
| `<release>-postgres` | `username`, `password` (`database` optional) | Server (DB owner) + bundled PostgreSQL |
| `<release>-oidc` | `client_id`, `client_secret` | Server (if OIDC enabled) |
| `<release>-smtp` | `username`, `password` | Server (if email enabled) |

Expand Down
22 changes: 22 additions & 0 deletions helm/chart/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,25 @@ Frontend URL: {{ include "sparkyfitness.frontendUrl" . }}
-- Grant extension functions to the DB owner so the app can delegate them
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO "<owner>" WITH GRANT OPTION;
{{- end }}

================================================================================
PORT FORWARDING (Local Development / Testing)
================================================================================
To access the application locally via port-forwarding, run the following commands
in separate terminal windows:

1. Port-forward the Frontend to localhost:3004:
kubectl port-forward svc/{{ include "sparkyfitness.fullname" . }}-frontend 3004:80

2. Port-forward the Server to localhost:3010:
kubectl port-forward svc/{{ include "sparkyfitness.fullname" . }}-server 3010:3010

Then open your browser to: http://localhost:3004

================================================================================
TESTING
================================================================================
To run the included Helm tests to verify connectivity:

helm test {{ .Release.Name }}

23 changes: 23 additions & 0 deletions helm/chart/templates/postgresql/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{{- if and .Values.postgresql.enabled (include "sparkyfitness.createDatabaseSecret" .) }}
{{- $secretName := include "sparkyfitness.databaseSecretName" . -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace $secretName -}}
{{- $existingData := ($existing).data | default dict -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ $secretName }}
labels:
{{- include "sparkyfitness.labels" . | nindent 4 }}
app.kubernetes.io/component: postgresql
annotations:
argocd.argoproj.io/compare-options: IgnoreExtraneous
type: Opaque
data:
username: {{ .Values.postgresql.auth.username | b64enc }}
{{- if .Values.postgresql.auth.password }}
password: {{ .Values.postgresql.auth.password | b64enc }}
{{- else }}
password: {{ get $existingData "password" | default (randAlphaNum 48 | b64enc) }}
{{- end }}
database: {{ .Values.postgresql.auth.database | b64enc }}
{{- end }}
20 changes: 20 additions & 0 deletions helm/chart/templates/postgresql/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- if .Values.postgresql.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "sparkyfitness.fullname" . }}-postgresql
labels:
{{- include "sparkyfitness.labels" . | nindent 4 }}
app.kubernetes.io/component: postgresql
spec:
type: ClusterIP
clusterIP: None
ports:
- name: postgresql
port: 5432
targetPort: postgresql
protocol: TCP
selector:
{{- include "sparkyfitness.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: postgresql
{{- end }}
95 changes: 95 additions & 0 deletions helm/chart/templates/postgresql/statefulset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{{- if .Values.postgresql.enabled }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "sparkyfitness.fullname" . }}-postgresql
labels:
{{- include "sparkyfitness.labels" . | nindent 4 }}
app.kubernetes.io/component: postgresql
spec:
serviceName: {{ include "sparkyfitness.fullname" . }}-postgresql
replicas: 1
selector:
matchLabels:
{{- include "sparkyfitness.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: postgresql
template:
metadata:
labels:
{{- include "sparkyfitness.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: postgresql
spec:
{{- include "sparkyfitness.imagePullSecrets" . | nindent 6 }}
{{- with .Values.postgresql.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: postgresql
image: {{ include "sparkyfitness.image" (dict "image" .Values.postgresql.image "global" .Values.global "appVersion" .Chart.AppVersion) }}
imagePullPolicy: {{ .Values.postgresql.image.pullPolicy }}
ports:
- name: postgresql
containerPort: 5432
protocol: TCP
env:
- name: POSTGRES_DB
value: {{ .Values.postgresql.auth.database | quote }}
Comment thread
Sim-sat marked this conversation as resolved.
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: {{ include "sparkyfitness.databaseSecretName" . }}
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "sparkyfitness.databaseSecretName" . }}
key: password
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: {{ include "sparkyfitness.databaseSecretName" . }}
key: password
{{- with .Values.postgresql.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.postgresql.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.postgresql.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.postgresql.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql
{{- if not .Values.postgresql.persistence.enabled }}
volumes:
- name: data
emptyDir: {}
{{- end }}
{{- if .Values.postgresql.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: data
labels:
{{- include "sparkyfitness.labels" . | nindent 10 }}
app.kubernetes.io/component: postgresql
spec:
accessModes:
- {{ .Values.postgresql.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.postgresql.persistence.size }}
{{- $storageClass := include "sparkyfitness.storageClass" (dict "sc" .Values.postgresql.persistence.storageClass "global" .Values.global) }}
{{- if $storageClass }}
storageClassName: {{ $storageClass | quote }}
{{- end }}
{{- end }}
{{- end }}
15 changes: 15 additions & 0 deletions helm/chart/templates/tests/test-connection.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "sparkyfitness.fullname" . }}-test-connection"
labels:
{{- include "sparkyfitness.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "sparkyfitness.fullname" . }}-frontend:80']
restartPolicy: Never
12 changes: 8 additions & 4 deletions helm/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ config:
adminEmail: ""

# -- Allow CORS from private network ranges (only for self-hosted/private networks)
allowPrivateNetworkCors: false
allowPrivateNetworkCors: true
Comment thread
Sim-sat marked this conversation as resolved.
# -- Extra trusted origins, comma-separated (e.g. "http://192.168.1.5:8080")
extraTrustedOrigins: ""
extraTrustedOrigins: "http://localhost:3004"

# -- Force email/password login to be enabled (fail-safe against OIDC lockout)
forceEmailLogin: true
Expand Down Expand Up @@ -330,8 +330,11 @@ postgresql:
enabled: true
image:
repository: postgres
tag: "15-alpine"
tag: "18-alpine"
Comment thread
Sim-sat marked this conversation as resolved.
pullPolicy: IfNotPresent
extraEnv:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
livenessProbe:
exec:
command:
Expand Down Expand Up @@ -365,8 +368,9 @@ postgresql:
auth:
database: sparkyfitness
username: sparky
# -- Password for PostgreSQL owner user (auto-generated if empty)
password: ""
# -- Use an existing K8s Secret (keys: username, password)
# -- Use an existing K8s Secret (keys: username, password; optional: database)
existingSecret: ""
persistence:
enabled: true
Expand Down
Loading