From 8470af05585b44fec8217507d6f56732375f7ea6 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Thu, 13 Mar 2025 20:19:41 -0500 Subject: [PATCH] receipts: Deploy Receipts management tool This is a custom-built application for managing purchase receipts. It integrates with Firefly III to fill some of the gaps that `xactmon` cannot handle, such as restaurant bills with tips, gas station purchases, purchases with the HSA debit card, refunds, and deposits. Photos of receipts can be taken directly within the application using the User Media Web API, or uploaded as existing files. Each photo is associated with transaction data, including date, vendor, amount, and general notes. These data are also synchronized with Firefly whenever possible. --- 20125/config.yml | 7 +++ argocd/applications/receipts.yaml | 18 ++++++ receipts/.gitignore | 1 + receipts/config.toml | 12 ++++ receipts/jenkins.yaml | 28 +++++++++ receipts/kustomization.yaml | 66 +++++++++++++++++++++ receipts/namespace.yaml | 7 +++ receipts/postgres-cert.yaml | 12 ++++ receipts/receipts.yaml | 97 +++++++++++++++++++++++++++++++ receipts/secrets.yaml | 35 +++++++++++ 10 files changed, 283 insertions(+) create mode 100644 argocd/applications/receipts.yaml create mode 100644 receipts/.gitignore create mode 100644 receipts/config.toml create mode 100644 receipts/jenkins.yaml create mode 100644 receipts/kustomization.yaml create mode 100644 receipts/namespace.yaml create mode 100644 receipts/postgres-cert.yaml create mode 100644 receipts/receipts.yaml create mode 100644 receipts/secrets.yaml diff --git a/20125/config.yml b/20125/config.yml index 64b9dc3..dc5f5f2 100644 --- a/20125/config.yml +++ b/20125/config.yml @@ -77,3 +77,10 @@ applications: url: icons/firefly-iii.svg alerts: - instance: *url + +- name: Receipts + url: &url https://receipts.pyrocufflink.blue/ + icon: + url: https://receipts.pyrocufflink.blue/static/icons/icon-512.png + alerts: + - instance: *url diff --git a/argocd/applications/receipts.yaml b/argocd/applications/receipts.yaml new file mode 100644 index 0000000..a437d4a --- /dev/null +++ b/argocd/applications/receipts.yaml @@ -0,0 +1,18 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: &name receipts + namespace: argocd + labels: + vendor: dustin +spec: + destination: + server: https://kubernetes.default.svc + project: default + source: + path: *name + repoURL: https://git.pyrocufflink.blue/infra/kubernetes.git + targetRevision: master + syncPolicy: + automated: + prune: true diff --git a/receipts/.gitignore b/receipts/.gitignore new file mode 100644 index 0000000..3b9b833 --- /dev/null +++ b/receipts/.gitignore @@ -0,0 +1 @@ +firefly.token diff --git a/receipts/config.toml b/receipts/config.toml new file mode 100644 index 0000000..2a83101 --- /dev/null +++ b/receipts/config.toml @@ -0,0 +1,12 @@ +[default.firefly] +url = "https://firefly.pyrocufflink.blue" +token = "/run/secrets/receipts/secrets/firefly.token" +search_query = "tag:Review has_attachments:false type:withdrawal has_any_bill:false" +default_account = "Amazon Rewards Visa (Chase)" + +[default.databases.receipts] +url = "postgresql://receipts@postgresql.pyrocufflink.blue/receipts?sslmode=verify-full&sslrootcert=/run/dch-ca/dch-root-ca.crt&sslcert=/run/secrets/receipts/postgresql/tls.crt&sslkey=/run/secrets/receipts/postgresql/tls.key" + +[default.limits] +file = "4MiB" +data-form = "4MiB" diff --git a/receipts/jenkins.yaml b/receipts/jenkins.yaml new file mode 100644 index 0000000..bf962d5 --- /dev/null +++ b/receipts/jenkins.yaml @@ -0,0 +1,28 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: jenkins +rules: +- apiGroups: + - apps + resources: + - deployments + resourceNames: + - receipts + verbs: + - get + - patch + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: jenkins +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: jenkins +subjects: +- kind: ServiceAccount + name: default + namespace: jenkins-jobs diff --git a/receipts/kustomization.yaml b/receipts/kustomization.yaml new file mode 100644 index 0000000..fbe89b2 --- /dev/null +++ b/receipts/kustomization.yaml @@ -0,0 +1,66 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +transformers: +- | + apiVersion: builtin + kind: NamespaceTransformer + metadata: + name: namespace-transformer + namespace: receipts + setRoleBindingSubjects: none + fieldSpecs: + - path: metadata/namespace + create: true + +labels: +- pairs: + app.kubernetes.io/instance: receipts + includeSelectors: true +- pairs: + app.kubernetes.io/part-of: receipts + includeTemplates: true + +resources: +- namespace.yaml +- secrets.yaml +- receipts.yaml +- postgres-cert.yaml +- ../dch-root-ca +- jenkins.yaml + +configMapGenerator: +- name: receipts-config + files: + - config.toml + options: + labels: + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts + +patches: +- patch: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: receipts + spec: + template: + spec: + containers: + - name: receipts + volumeMounts: + - mountPath: /run/dch-ca + name: dch-root-ca + readOnly: true + - mountPath: /run/secrets/receipts/postgresql + name: postgresql-cert + readOnly: true + volumes: + - name: dch-root-ca + configMap: + name: dch-root-ca + - name: postgresql-cert + secret: + secretName: postgres-client-cert + defaultMode: 0640 diff --git a/receipts/namespace.yaml b/receipts/namespace.yaml new file mode 100644 index 0000000..ac33b12 --- /dev/null +++ b/receipts/namespace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: receipts + labels: + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts diff --git a/receipts/postgres-cert.yaml b/receipts/postgres-cert.yaml new file mode 100644 index 0000000..f8ed43e --- /dev/null +++ b/receipts/postgres-cert.yaml @@ -0,0 +1,12 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: postgres-client-cert +spec: + commonName: receipts + privateKey: + algorithm: ECDSA + secretName: postgres-client-cert + issuerRef: + name: postgresql-ca + kind: ClusterIssuer diff --git a/receipts/receipts.yaml b/receipts/receipts.yaml new file mode 100644 index 0000000..16cdb0b --- /dev/null +++ b/receipts/receipts.yaml @@ -0,0 +1,97 @@ +apiVersion: v1 +kind: Service +metadata: + name: receipts + labels: &labels + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts +spec: + ports: + - name: http + port: 8000 + selector: *labels + type: ClusterIP + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: receipts + labels: &labels + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts +spec: + selector: + matchLabels: *labels + template: + metadata: + labels: *labels + spec: + containers: + - name: receipts + image: git.pyrocufflink.net/packages/receipts + imagePullPolicy: Always + env: + - name: RUST_LOG + value: info,rocket=warn,receipts=debug + - name: ROCKET_ADDRESS + value: 0.0.0.0 + ports: + - name: http + containerPort: 8000 + securityContext: + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /etc/receipts + name: config + readOnly: true + - mountPath: /run/secrets/receipts/secrets + name: secrets + readOnly: true + - mountPath: /tmp + name: tmp + subPath: tmp + imagePullSecrets: + - name: imagepull-gitea + securityContext: + runAsNonRoot: true + runAsUser: 943 + runAsGroup: 943 + fsGroup: 943 + volumes: + - name: config + configMap: + name: receipts-config + - name: secrets + secret: + secretName: receipts + - name: tmp + emptyDir: + medium: Memory + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: '0' + name: receipts +spec: + tls: + - hosts: + - receipts.pyrocufflink.blue + rules: + - host: receipts.pyrocufflink.blue + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: receipts + port: + name: http + diff --git a/receipts/secrets.yaml b/receipts/secrets.yaml new file mode 100644 index 0000000..29a9a66 --- /dev/null +++ b/receipts/secrets.yaml @@ -0,0 +1,35 @@ +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: imagepull-gitea + namespace: receipts + labels: &labels + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts +spec: + encryptedData: + .dockerconfigjson: AgCdye4FPceefzsWWdwX7BLLIkpCbJypTY/VMBHNZX4uNDjJiYICGtPFAbNceOnnBfKyQcXv47kfXgVWOzKl+OYv5ee9I3rsEpwXhU6zdkvRP2spZp/lXkDTrEitap3jcap3gGcK4j19ikXM42DfTCguSGkX5OM7jR7jg4xAQyB7M0FvZKkEnp9MwASp0+It3g4CxhQfQlrYOkbvuq7wY7qkpqHqoDVKOcKtmKM69HX6IMU5/gDFB3WZLdOkFxAhSQ6cEKJyqfyMx//nZlFw2jTFbpsiOBofQiqZ5dKFkz95OW22A6dcdxCoK1Xwmb2XvlD15wZ1ttaeh1GhpUWfqyKP9fePm+YAS4AvnPP0RurwpAKHh7C/EHKurwCt3o0UhfcQHDwhaIitA5c8lHEmDLPj76YGtjKreIH4cCEz3os6FyEg86pvfFHq4gjUKEV29qSuAEYYvfwAa7IRMjU5vjiD16EJ7/VaiKauKrA04tx53bq8Oq6oTZkOwO63ZU0kr82EJksPZ9jymHS7aq/cAnaXyZ2RamuT8HHGB/GZU6rXX/THaYww6Tii6al72EmGZ4OoY/Av+VXZBkxX1S762wbuA9KMwOG8raTPwXUVAm53Hl4E5piBAFMcGsboVdWNcKqr/yXWKeJfohlqKFr39g0aobekSB81ORAJEGHuSxE8tUdhfZYhbc5yemTzhuCu6iJFZj8yFPv6UwJV+OzSNQEuTZokyBNRPCteXh0xy2VxHZmp+oxakpM02oKPvS10z7yBIR0BgU9KddmqXozENekQP0v445i8BVVARpqoGFWBy3bbv4Z3suEJ8LIvb96vsq+bh0ia+DaslsnbXjiZ9XseGUrzYmWKZOBIFitpo181LJtWHRSU/GAm58GOUoVWCW66ldI79lZ4Z7xH+UJWGQIwbHQ+iky6Ooebsc42mdm3ToK4bi1Zkg4VdIxDAhFiPubOEmkacyoCKobqs+aeni6UB9lLjieClGWHNXdS7gQs4NPBE0dq2B0Sr2pBboA= + template: + metadata: + name: imagepull-gitea + namespace: receipts + labels: *labels + type: kubernetes.io/dockerconfigjson + +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: receipts + namespace: receipts + labels: &labels + app.kubernetes.io/name: receipts + app.kubernetes.io/component: receipts +spec: + encryptedData: + firefly.token: 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 + template: + metadata: + name: receipts + namespace: receipts + labels: *labels