From 5145d97712d1c0111e52f9b0a5ed942eb771da75 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Sat, 13 Dec 2025 15:46:00 +0000 Subject: [PATCH] set up stripe to invoice --- .github/workflows/deploy-postgres.yml | 59 ++++++++++++ db/README.md | 0 db/atlas/atlas.hcl | 6 ++ db/atlas/migrations/0001_init.sql | 5 ++ db/atlas/migrations/0002_auth.sql | 6 ++ db/atlas/migrations/0003_stripe_xero.sql | 13 +++ db/k8s/backups/pg-backup-cronjob.yaml | 26 ++++++ db/k8s/migrations/atlas-job.yaml | 20 +++++ db/k8s/postgres/deployment.yaml | 110 +++++++++++++++++++++++ db/k8s/secrets/aws-backup-secret.yaml | 10 +++ db/k8s/secrets/postgres-secret.yaml | 9 ++ db/scripts/backup.sh | 0 db/scripts/migrate.sh | 0 stripe_to_invoice/app/globals.css | 0 stripe_to_invoice/app/page.tsx | 58 +++++++++++- stripe_to_invoice/run_local.sh | 1 + 16 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/deploy-postgres.yml create mode 100644 db/README.md create mode 100644 db/atlas/atlas.hcl create mode 100644 db/atlas/migrations/0001_init.sql create mode 100644 db/atlas/migrations/0002_auth.sql create mode 100644 db/atlas/migrations/0003_stripe_xero.sql create mode 100644 db/k8s/backups/pg-backup-cronjob.yaml create mode 100644 db/k8s/migrations/atlas-job.yaml create mode 100644 db/k8s/postgres/deployment.yaml create mode 100644 db/k8s/secrets/aws-backup-secret.yaml create mode 100644 db/k8s/secrets/postgres-secret.yaml create mode 100644 db/scripts/backup.sh create mode 100644 db/scripts/migrate.sh create mode 100644 stripe_to_invoice/app/globals.css create mode 100644 stripe_to_invoice/run_local.sh diff --git a/.github/workflows/deploy-postgres.yml b/.github/workflows/deploy-postgres.yml new file mode 100644 index 0000000..7dc7524 --- /dev/null +++ b/.github/workflows/deploy-postgres.yml @@ -0,0 +1,59 @@ +name: Deploy DB Infrastructure + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: mealcraft-runners + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + # Install kubectl + - name: Install kubectl + run: | + sudo apt-get update + sudo apt-get install -y curl ca-certificates + curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" + sudo install -m 0755 kubectl /usr/local/bin/kubectl + + # Configure kubeconfig (ARC in-cluster) + - name: Configure kubeconfig + run: | + KUBE_HOST="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" + SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) + CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) + + kubectl config set-cluster microk8s \ + --server="$KUBE_HOST" \ + --certificate-authority="$CA_CERT" + + kubectl config set-credentials runner \ + --token="$SA_TOKEN" + + kubectl config set-context runner-context \ + --cluster=microk8s \ + --user=runner \ + --namespace="$NAMESPACE" + + kubectl config use-context runner-context + + # 1️⃣ Secrets + - name: Apply DB secrets + run: | + kubectl apply -f db/k8s/secrets/ + + # 2️⃣ PostgreSQL + - name: Deploy Postgres + run: | + kubectl apply -f db/k8s/postgres/ + + # 3️⃣ Backups (CronJob) + - name: Deploy Postgres backups + run: | + kubectl apply -f db/k8s/backups/ diff --git a/db/README.md b/db/README.md new file mode 100644 index 0000000..e69de29 diff --git a/db/atlas/atlas.hcl b/db/atlas/atlas.hcl new file mode 100644 index 0000000..630b57b --- /dev/null +++ b/db/atlas/atlas.hcl @@ -0,0 +1,6 @@ +env "k8s" { + url = "postgres://$DB_USER:$DB_PASSWORD@postgres:5432/stripe_xero?sslmode=disable" + migration { + dir = "file://atlas/migrations" + } +} \ No newline at end of file diff --git a/db/atlas/migrations/0001_init.sql b/db/atlas/migrations/0001_init.sql new file mode 100644 index 0000000..22d8741 --- /dev/null +++ b/db/atlas/migrations/0001_init.sql @@ -0,0 +1,5 @@ +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + email TEXT NOT NULL UNIQUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); diff --git a/db/atlas/migrations/0002_auth.sql b/db/atlas/migrations/0002_auth.sql new file mode 100644 index 0000000..41fa9fb --- /dev/null +++ b/db/atlas/migrations/0002_auth.sql @@ -0,0 +1,6 @@ +CREATE TABLE sessions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + expires_at TIMESTAMPTZ NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); diff --git a/db/atlas/migrations/0003_stripe_xero.sql b/db/atlas/migrations/0003_stripe_xero.sql new file mode 100644 index 0000000..b4ad422 --- /dev/null +++ b/db/atlas/migrations/0003_stripe_xero.sql @@ -0,0 +1,13 @@ +CREATE TABLE stripe_accounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + stripe_account_id TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE xero_connections ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id), + tenant_id TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); diff --git a/db/k8s/backups/pg-backup-cronjob.yaml b/db/k8s/backups/pg-backup-cronjob.yaml new file mode 100644 index 0000000..d0383ff --- /dev/null +++ b/db/k8s/backups/pg-backup-cronjob.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: postgres-backup +spec: + schedule: "30 18 * * 5" # weekly on friday at 18:30 + jobTemplate: + spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: backup + image: postgres:16 + command: + - /bin/sh + - -c + - | + pg_dump stripe_xero \ + | gzip \ + | aws s3 cp - s3://$S3_BUCKET/stripe_xero/$(date +%F).sql.gz + envFrom: + - secretRef: + name: postgres-secret + - secretRef: + name: aws-backup-secret diff --git a/db/k8s/migrations/atlas-job.yaml b/db/k8s/migrations/atlas-job.yaml new file mode 100644 index 0000000..6c1e3b0 --- /dev/null +++ b/db/k8s/migrations/atlas-job.yaml @@ -0,0 +1,20 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: atlas-migrate +spec: + template: + spec: + restartPolicy: Never + containers: + - name: atlas + image: arigaio/atlas:latest + command: ["atlas", "migrate", "apply", "--env", "k8s"] + envFrom: + - secretRef: + name: postgres-secret + + +# You can run this: +# kubectl apply -f k8s/migrations/atlas-job.yaml +# Or later from CI. \ No newline at end of file diff --git a/db/k8s/postgres/deployment.yaml b/db/k8s/postgres/deployment.yaml new file mode 100644 index 0000000..9d0425d --- /dev/null +++ b/db/k8s/postgres/deployment.yaml @@ -0,0 +1,110 @@ +# -------------------------------------------------- +# PostgreSQL Secret +# -------------------------------------------------- +apiVersion: v1 +kind: Secret +metadata: + name: postgres-secret + namespace: default +type: Opaque +stringData: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgrespassword + POSTGRES_DB: stripe_xero + +--- +# -------------------------------------------------- +# PersistentVolume (local disk on mist) +# -------------------------------------------------- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: postgres-pv +spec: + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-storage + hostPath: + path: /home/kimjunte/k8s_storage/postgres/stripe_xero + +--- +# -------------------------------------------------- +# PersistentVolumeClaim +# -------------------------------------------------- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: default +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 20Gi + storageClassName: local-storage + +--- +# -------------------------------------------------- +# PostgreSQL Deployment +# -------------------------------------------------- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:16 + ports: + - containerPort: 5432 + envFrom: + - secretRef: + name: postgres-secret + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + readinessProbe: + tcpSocket: + port: 5432 + initialDelaySeconds: 10 + periodSeconds: 5 + livenessProbe: + tcpSocket: + port: 5432 + initialDelaySeconds: 30 + periodSeconds: 10 + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-pvc + +--- +# -------------------------------------------------- +# PostgreSQL Service (internal only) +# -------------------------------------------------- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: default +spec: + type: ClusterIP + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 diff --git a/db/k8s/secrets/aws-backup-secret.yaml b/db/k8s/secrets/aws-backup-secret.yaml new file mode 100644 index 0000000..548907b --- /dev/null +++ b/db/k8s/secrets/aws-backup-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: aws-backup-secret +type: Opaque +stringData: + AWS_ACCESS_KEY_ID: xxx + AWS_SECRET_ACCESS_KEY: yyy + AWS_REGION: eu-west-2 + S3_BUCKET: mist-db-backups diff --git a/db/k8s/secrets/postgres-secret.yaml b/db/k8s/secrets/postgres-secret.yaml new file mode 100644 index 0000000..2738709 --- /dev/null +++ b/db/k8s/secrets/postgres-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: postgres-secret +type: Opaque +stringData: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: averysecretpasswordPersonAppleWinter938 + POSTGRES_DB: stripe_xero diff --git a/db/scripts/backup.sh b/db/scripts/backup.sh new file mode 100644 index 0000000..e69de29 diff --git a/db/scripts/migrate.sh b/db/scripts/migrate.sh new file mode 100644 index 0000000..e69de29 diff --git a/stripe_to_invoice/app/globals.css b/stripe_to_invoice/app/globals.css new file mode 100644 index 0000000..e69de29 diff --git a/stripe_to_invoice/app/page.tsx b/stripe_to_invoice/app/page.tsx index 71333d6..768d031 100644 --- a/stripe_to_invoice/app/page.tsx +++ b/stripe_to_invoice/app/page.tsx @@ -1,3 +1,57 @@ +// app/page.tsx + export default function Home() { - return

Hello World

-} \ No newline at end of file + return ( +
+ + {/* What this is */} +
+

+ Stripe → Xero automation +

+

+ Automatically create and mark Xero invoices as paid when a Stripe payment succeeds. + + Built for people who value time more than pressing buttons. +

+
+ + {/* Steps */} +
+

How it works

+
    +
  1. Log in
  2. +
  3. Connect Stripe
  4. +
  5. Connect Xero
  6. +
  7. Make a payment
  8. +
  9. Invoice appears in Xero as paid
  10. +
+
+ + {/* Proof */} +
+

Proof, not promises

+

+ Your next Stripe payment will automatically reconcile in Xero. + No manual matching. No “awaiting payment”. +

+
+ + {/* Pricing */} +
+

Pricing

+

+ £200 / month — unlimited invoices. +

+
+ + {/* CTA */} +
+

+ This page is a placeholder. The product is the automation. +

+
+ +
+ ) +} diff --git a/stripe_to_invoice/run_local.sh b/stripe_to_invoice/run_local.sh new file mode 100644 index 0000000..ef232dd --- /dev/null +++ b/stripe_to_invoice/run_local.sh @@ -0,0 +1 @@ +npm run dev