37 Commits

Author SHA1 Message Date
d1d6d24dbb remove redundant Siteworx CA certificates installation steps from CI workflow
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m29s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 1m44s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m23s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 4m51s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m4s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 35s
2026-02-06 20:20:25 -05:00
6e908bad17 bump frontend and backend image versions to v0.0.37 2026-02-06 20:10:21 -05:00
f6ed88fd78 ignore ArgoCD paths in push trigger for CI workflow
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m45s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 1m47s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m22s
2026-02-06 20:09:48 -05:00
b122fca494 add checkout step for release branch in CI build process
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Has been cancelled
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Has been cancelled
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Has been cancelled
2026-02-06 20:09:15 -05:00
1cc6d5ece4 update Go version in Dockerfile to 1.25.6
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m37s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 1m53s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m26s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 5m39s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m41s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 1m51s
2026-02-06 19:54:37 -05:00
74ea300f80 update Go version and change utilities package import path
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m46s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 4m20s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 5m48s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Failing after 4m31s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 6m15s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Has been skipped
2026-02-06 19:39:43 -05:00
f8958dfbef add automated build artifact recommit step in CI workflow
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m30s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Failing after 1m40s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Failing after 1m39s
2026-02-06 19:36:10 -05:00
22e045730f update dependencies and improve bullet manufacturer options deduplication
Some checks failed
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Failing after 1m49s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Failing after 2m6s
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 2m33s
2026-02-06 19:31:10 -05:00
a4edafdd2e update DB_HOST in configmap.yml to use service name
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 3m32s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Failing after 4m42s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Failing after 5m3s
2026-02-06 19:23:10 -05:00
f8974acab5 update DB_HOST in configmap.yml to use service name
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Has started running
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Has started running
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Has been cancelled
2026-02-06 19:22:24 -05:00
ac3cd505bf update DB_HOST in configmap.yml to use service name
Some checks are pending
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Has started running
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Has started running
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Has started running
2026-02-06 19:20:20 -05:00
03762a0be7 [create-pull-request] automated change (#10)
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 4m1s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 7m0s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 7m13s
Co-authored-by: rrise <+rrise@users.noreply.github.com>
Reviewed-on: Siteworxpro/reloading-manager#10
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-06-11 02:04:49 +00:00
0f915ccd82 happy monday _ bleh _ (#9)
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m23s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 1m32s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m35s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 6m11s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m47s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 53s
Reviewed-on: Siteworxpro/reloading-manager#9
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-06-11 01:43:53 +00:00
dd383b6fb3 fix tpyo (#8)
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 7m28s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 7m40s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 10m24s
Reviewed-on: Siteworxpro/reloading-manager#8
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-06-10 15:56:03 +00:00
e484aa7e31 tagging release w.t.f.
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 5m58s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 9m2s
2025-05-16 21:18:03 -04:00
3c60c1b012 [create-pull-request] automated change (#7)
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 6m18s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 9m20s
Co-authored-by: rrise <+rrise@users.noreply.github.com>
Reviewed-on: Siteworxpro/reloading-manager#7
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-05-16 15:34:23 -04:00
c5f1f32b44 You know the rules and so do I
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 10m30s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 19m6s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 20m46s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 1m18s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 22m13s
2025-05-16 14:05:49 -04:00
343abbb801 Actual final build before release
Some checks failed
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Failing after 2m42s
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Failing after 2m47s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 18m37s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 20m14s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 54s
2025-05-16 14:02:19 -04:00
13aad5254e Feed. You. Stuff. No time. (#6)
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m16s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m53s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 5m9s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m9s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 9s
Reviewed-on: Siteworxpro/reloading-manager#6
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-05-16 11:20:41 -04:00
aeba95dc41 [create-pull-request] automated change (#5)
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m15s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m14s
Co-authored-by: rrise <+rrise@users.noreply.github.com>
Reviewed-on: Siteworxpro/reloading-manager#5
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-05-16 11:13:40 -04:00
b40edf70d4 and a comma (#4)
Some checks failed
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 2m50s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 2m45s
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m16s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 5m25s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m23s
Reviewed-on: Siteworxpro/reloading-manager#4
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-05-16 10:21:03 -04:00
4f750ae15c [create-pull-request] automated change (#3)
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 8m6s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 10m49s
Co-authored-by: rrise <+rrise@users.noreply.github.com>
Reviewed-on: Siteworxpro/reloading-manager#3
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
2025-05-16 10:08:55 -04:00
3246450d77 Merge pull request 'This is not the commit message you are looking for' (#2) from deployments into master
Some checks failed
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 5m47s
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m15s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m16s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 49s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m11s
Reviewed-on: Siteworxpro/reloading-manager#2
2025-05-16 09:47:00 -04:00
74681e01b3 This is not the commit message you are looking for
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m23s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m0s
2025-05-16 09:42:39 -04:00
9dce6b8930 various changes
Some checks failed
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 6m36s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 7m45s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 52s
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 3m14s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 5m22s
2025-05-16 09:19:02 -04:00
7c02aa7148 Merge pull request '📝🔄 Update deployment manifest with new image tags' (#1) from release/v0.0.29-deploy into master
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 4m48s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 8m1s
Reviewed-on: Siteworxpro/reloading-manager#1
2025-05-16 09:09:05 -04:00
GitHub Action 🤖
a8c470a474 📝🔄 Update deployment manifest with new image tags 2025-05-16 13:01:57 +00:00
95b626b439 It's Working!
All checks were successful
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 7m3s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Successful in 57s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 5m7s
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m17s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 5m35s
2025-05-16 08:50:55 -04:00
b9a6598e87 Fucking templates.
All checks were successful
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 2m24s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 5m18s
2025-05-16 08:45:01 -04:00
4251a03139 That last commit was cringe
Some checks failed
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Has been cancelled
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Has been cancelled
2025-05-16 08:44:02 -04:00
691a32ff56 Just committing so I can go home
All checks were successful
🧪✨ Unit Tests Workflow / 🔍🐹 Go Tests (push) Successful in 8m21s
🧪✨ Unit Tests Workflow / 🧪📜 JavaScript Tests (push) Successful in 1m8s
2025-05-16 08:29:57 -04:00
3631a4b5dc ci test 2025-04-21 18:39:16 -04:00
edbd5b4f49 I would rather be playing Destiny 2. 2025-04-21 18:35:37 -04:00
a20bef65ae should work now. 2025-04-21 18:34:38 -04:00
4056e6705d Merge branch 'update-deployment-v0.0.28' into 'master'
Update deployment manifest with new image tags

See merge request rrise/reloading-manager!2
2025-04-21 22:34:01 +00:00
fc4305f161 yo recipes 2025-04-21 18:32:09 -04:00
f0fc8b7707 Update deployment manifest with new image tags 2025-04-21 22:29:48 +00:00
32 changed files with 990 additions and 369 deletions

View File

@@ -1,7 +1,7 @@
on:
push:
tags:
- '**'
- 'v*'
name: 🏗️ ✨ Build Workflow
@@ -10,13 +10,6 @@ jobs:
name: 🖼️ 🔨 Build Frontend
runs-on: ubuntu-latest
steps:
- name: 🛡️🔒 Add Siteworx CA Certificates
run: |
apt update && apt install -yq ca-certificates curl
curl -Ls https://siteworxpro.com/hosted/Siteworx+Root+CA.pem -o /usr/local/share/ca-certificates/sw.crt
update-ca-certificates
- name: 📖 🔍 Checkout Repository Code
uses: actions/checkout@v2
with:
@@ -25,13 +18,13 @@ jobs:
- name: 🔑 🔐 Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: 🔑 🛠️ Login to Siteworx Registry
uses: docker/login-action@v3
with:
username: ${{ vars.SITEWORX_USERNAME }}
username: ${{ secrets.SITEWORX_USERNAME }}
password: ${{ secrets.SITEWORX_PASSWORD }}
registry: scr.siteworxpro.com
@@ -62,13 +55,6 @@ jobs:
name: 🖥️ 🔨 Build Backend
runs-on: ubuntu-latest
steps:
- name: 🛡️🔒 Add Siteworx CA Certificates
run: |
apt update && apt install -yq ca-certificates curl
curl -Ls https://siteworxpro.com/hosted/Siteworx+Root+CA.pem -o /usr/local/share/ca-certificates/sw.crt
update-ca-certificates
- name: 📖🔍 Checkout Repository Code
uses: actions/checkout@v2
with:
@@ -77,13 +63,13 @@ jobs:
- name: 🔑 🔐 Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: 🔑 🛠️ Login to Siteworx Registry
uses: docker/login-action@v3
with:
username: ${{ vars.SITEWORX_USERNAME }}
username: ${{ secrets.SITEWORX_USERNAME }}
password: ${{ secrets.SITEWORX_PASSWORD }}
registry: scr.siteworxpro.com
@@ -111,13 +97,6 @@ jobs:
runs-on: ubuntu-latest
needs: [BuildFrontend, BuildBackend]
steps:
- name: 🛡️🔒 Add Siteworx CA Certificates
run: |
apt update && apt install -yq ca-certificates curl
curl -Ls https://siteworxpro.com/hosted/Siteworx+Root+CA.pem -o /usr/local/share/ca-certificates/sw.crt
update-ca-certificates
- name: 📖 🔍 Checkout Repository Code
uses: actions/checkout@v2
with:
@@ -128,15 +107,36 @@ jobs:
echo "## Do not edit this file directly. It is auto-generated by the script." > argocd/deployment/deployment.yml
sed "s|__TAG__|${{ gitea.ref_name }}|g" argocd/template/deployment.yml >> argocd/deployment/deployment.yml
- name: 💾✅ Commit Updated Manifest
uses: EndBug/add-and-commit@v9
- name: 📤 📦 Recommit Build Artifacts
uses: addnab/docker-run-action@v3
with:
new_branch: release/${{ gitea.ref_name }}-deploy
add: argocd/deployment/deployment.yml
author_name: "GitHub Action 🤖"
author_email: gitia@siteworxpro.com
message: "📝🔄 Update deployment manifest with new image tags"
image: alpine/git
options: --volumes-from ${{ env.JOB_CONTAINER_NAME }} -w ${{ gitea.workspace }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan -H gitea.siteworxpro.com >> ~/.ssh/known_hosts
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa
git config --global user.email "gitea@siteworxpro.com"
git config --global user.name "Gitea-Runner via Gitea Actions"
git config --global push.autoSetupRemote true
git remote rm origin && git remote add origin "git@gitea.siteworxpro.com:siteworxpro/reloading-manager.git"
git add -f "argocd/deployment/*" || true
git checkout -b release/${{ gitea.ref_name }}-deploy || git checkout release/${{ gitea.ref_name }}-deploy
git commit -m "Build Auto Commit" || echo "No changes to commit"
git push origin HEAD:${{ gitea.ref_name }} -o ci.skip || echo "No changes to push"
- name: 🚀 ✨ Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v7
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
with:
base: master
add-paths: argocd/deployment/deployment.yml
title: "🚀 ✨ Release ${GITHUB_REF_NAME} - Deploy"
branch: release/${{ gitea.ref_name }}-deploy
committer: "Gitea Action 🤖 <gitia@siteworxpro.com>"
body: "📝 🔄 Update deployment manifest with new image tags for release ${GITHUB_REF_NAME}"

View File

@@ -1,5 +1,13 @@
on:
workflow_dispatch:
inputs:
test:
description: 'Run tests'
required: true
default: 'true'
push:
paths-ignore:
- argocd/**/*
branches:
- "*"
@@ -32,6 +40,29 @@ jobs:
cd frontend
npm run build
golangci-lint:
name: 🧪 🐹 GolangCI-Lint
runs-on: ubuntu-latest
steps:
- name: 🛡️ 🔒 Add Siteworx CA Certificates
run: |
apt update && apt install -yq ca-certificates curl
curl -Ls https://siteworxpro.com/hosted/Siteworx+Root+CA.pem -o /usr/local/share/ca-certificates/sw.crt
update-ca-certificates
- name: 📖 🔍 Checkout Repository Code
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: ⚙️ 🐹 Set up Go Environment
uses: actions/setup-go@v2
with:
go-version: '1.25.6'
cache: true
- name: ✅ 🧪 Run GolangCI-Lint
uses: golangci/golangci-lint-action@v8.0.0
with:
working-directory: backend
test-go:
env:
GOPRIVATE: 'git.siteworxpro.com'
@@ -47,7 +78,7 @@ jobs:
- name: ⚙️ 🐹 Set up Go Environment
uses: actions/setup-go@v2
with:
go-version: '1.24.0'
go-version: '1.25.6'
cache: true
- name: 📖 🔍 Checkout Repository Code
uses: actions/checkout@v2

View File

@@ -9,8 +9,11 @@ stages:
NodeJs Tests:
stage: Tests
image: node:22.14.0
except:
- tags
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
changes:
- frontend/**
- frontend/.gitlab-ci.yml
before_script:
- cd frontend
- npm install
@@ -21,6 +24,11 @@ include:
- project: 'shared/blueprints'
file: 'jobs/golang-tests.yml'
ref: master
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
changes:
- backend/**
- backend/.gitlab-ci.yml
inputs:
job_name: "Go Tests"
working_directory: "backend"
@@ -62,6 +70,18 @@ include:
context: "frontend"
dockerfile: "frontend/Dockerfile"
- project: 'shared/blueprints'
file: 'jobs/trigger-argocd.yml'
ref: master
rules:
- changes:
- argocd/**/*
inputs:
stage: Trigger
argocdServer: ${ARGOCD_SERVER}
argocdAuthToken: ${ARGOCD_AUTH_TOKEN}
argocdAppName: ${ARGOCD_APP_NAME}
Update Deployment:
image: siteworxpro/alpine:3.21.3
rules:
@@ -108,19 +128,8 @@ Create Merge Request:
curl --request POST --header "PRIVATE-TOKEN: glpat-hv-uxCx3PDNKn7ihyXce" \
--data "source_branch=update-deployment-${CI_COMMIT_TAG}" \
--data "target_branch=master" \
--data "title=Update deployment manifest with new image tags" \
--data "title=Update deployment manifest for version ${CI_COMMIT_TAG}" \
--data "description=This merge request updates the deployment manifest with the new image tags." \
--data "remove_source_branch=true" \
--data "squash_commits=true" \
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests"
Trigger ArgoCD:
stage: Trigger
image: siteworxpro/argocd:v2.14.10
rules:
- changes:
- argocd/deployment/*
variables:
ARGOCD_AUTH_TOKEN: ${ARGOCD_AUTH_TOKEN}
ARGOCD_SERVER: ${ARGOCD_SERVER}
script: |
argocd --grpc-web app sync ${ARGOCD_APP_NAME}

View File

@@ -3,7 +3,7 @@ kind: ConfigMap
metadata:
name: reloading-cm
data:
DB_HOST: "192.168.1.30"
DB_HOST: "postgres.postgres.svc.cluster.local"
DB_DATABASE: "loading"
DB_USER: "loading"
DB_PASSWORD: "loading"

View File

@@ -19,11 +19,11 @@ spec:
- name: siteworxpro
containers:
- name: frontend
image: scr.siteworxpro.com/reloading-manager/frontend:v0.0.27
image: scr.siteworxpro.com/reloading-manager/frontend:v0.0.37
ports:
- containerPort: 80
- name: backend
image: scr.siteworxpro.com/reloading-manager/backend:v0.0.27
image: scr.siteworxpro.com/reloading-manager/backend:v0.0.37
ports:
- containerPort: 8080
envFrom:

17
backend/.golangci.yml Normal file
View File

@@ -0,0 +1,17 @@
version: "2"
linters:
default: standard
enable:
- whitespace
- tagalign
- reassign
- bodyclose
- contextcheck
- containedctx
- godot
- usestdlibvars
formatters:
settings:
gofmt:
simplify: true

View File

@@ -1,4 +1,4 @@
FROM siteworxpro/golang:1.24.0 AS build
FROM siteworxpro/golang:1.25.6 AS build
WORKDIR /app

View File

@@ -4,7 +4,7 @@ docker run -v $(pwd)/postgres:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=password \
--rm --name postgres \
-p 5432:5432 \
-d scr.siteworxpro.com/library/postgres:17
-d postgres:17
```
```shell

View File

@@ -3,12 +3,12 @@ package database
import (
"context"
"fmt"
"git.siteworxpro.com/packages/go/utilities/Env"
"git.siteworxpro.com/reloading-manager/backend/models/bullets"
"git.siteworxpro.com/reloading-manager/backend/models/loads"
"git.siteworxpro.com/reloading-manager/backend/models/manufacturer"
"git.siteworxpro.com/reloading-manager/backend/models/powder"
"git.siteworxpro.com/reloading-manager/backend/models/primers"
"gitea.siteworxpro.com/golang-packages/utilities/Env"
"github.com/jackc/pgx/v5"
"log"
)
@@ -20,6 +20,10 @@ const (
DbPassword Env.EnvironmentVariable = "DB_PASSWORD"
)
type contextKeyType string
const dbContextKey contextKeyType = "dbcontext"
type Database struct {
Db *pgx.Conn
connected bool
@@ -50,6 +54,24 @@ func (*Database) DSN(hidePassword bool) string {
return fmt.Sprintf("postgres://%s:%s@%s:5432/%s%s", dbUser, dbPassword, dbHost, dbDatabase, extraParams)
}
func NewWithContext(ctx context.Context) context.Context {
db := GetNewDatabase()
return context.WithValue(ctx, dbContextKey, db)
}
func NewFromContext(ctx context.Context) *Database {
if ok := ctx.Value(dbContextKey); ok != nil {
return ctx.Value(dbContextKey).(*Database)
}
return nil
}
func WithContext(ctx context.Context, database *Database) context.Context {
return context.WithValue(ctx, dbContextKey, database)
}
func GetNewDatabase() *Database {
var dbSingleton Database

View File

@@ -11,6 +11,9 @@ import (
func (db *Database) Migrate() {
sqlDB, err := sql.Open("postgres", db.DSN(false))
if err != nil {
log.Fatal(err)
}
driver, err := postgres.WithInstance(sqlDB, &postgres.Config{
MigrationsTable: "schema_migrations",

View File

@@ -1,16 +1,16 @@
module git.siteworxpro.com/reloading-manager/backend
go 1.24.0
go 1.25.6
require (
git.siteworxpro.com/packages/go/utilities v1.3.0
gitea.siteworxpro.com/golang-packages/utilities v1.0.0
github.com/go-jet/jet/v2 v2.13.0
github.com/go-playground/validator v9.31.0+incompatible
github.com/go-sql-driver/mysql v1.9.2
github.com/golang-migrate/migrate v3.5.4+incompatible
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.7.4
github.com/labstack/echo/v4 v4.13.3
github.com/jackc/pgx/v5 v5.7.5
github.com/labstack/echo/v4 v4.13.4
github.com/labstack/gommon v0.4.2
)
@@ -41,11 +41,11 @@ require (
github.com/stretchr/testify v1.10.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/time v0.12.0 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -1,7 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.siteworxpro.com/packages/go/utilities v1.3.0 h1:931q66COBJATgIQksPDSZlWMIwENJhhfC/GVf22ER5s=
git.siteworxpro.com/packages/go/utilities v1.3.0/go.mod h1:iWhICNrMnB03PY9dM9eCNs9uQPEsPwae5pJDG+HHUPI=
gitea.siteworxpro.com/golang-packages/utilities v1.0.0 h1:f5JqAeZWBn/HBO9k5dzg0Wm91a69uwU5UC2P9ebQ9J0=
gitea.siteworxpro.com/golang-packages/utilities v1.0.0/go.mod h1:QNqclnfv/BT2D5tbXgsGm7uhhe2Baovi5F6j0pVvMGc=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -40,8 +40,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -50,8 +50,8 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@@ -86,41 +86,41 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -21,12 +21,15 @@ type BulletResponse struct {
func Photo(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
byId, err := db.Bullets.GetBulletById(context.Background(), *uid)
@@ -48,15 +51,18 @@ func Photo(c echo.Context) error {
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
err := db.Bullets.DeleteBullet(context.Background(), *uid)
err = db.Bullets.DeleteBullet(context.Background(), *uid)
if err != nil {
return err
}
@@ -66,12 +72,14 @@ func Delete(c echo.Context) error {
func Put(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
byId, err := db.Bullets.GetBulletById(context.Background(), *uid)
@@ -101,9 +109,9 @@ func Put(c echo.Context) error {
weight, _ := strconv.ParseInt(c.FormValue("weight"), 10, 32)
diameter, _ := strconv.ParseInt(c.FormValue("diameter"), 10, 32)
manufacturerId := c.FormValue("manufacturer_id")
manufacturerUid := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if manufacturerUid == nil {
return nil
manufacturerUid, err := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if err == nil || manufacturerUid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
name := c.FormValue("name")
@@ -147,13 +155,15 @@ func Put(c echo.Context) error {
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
byId, err := db.Bullets.GetBulletById(context.Background(), *uid)
@@ -210,7 +220,6 @@ func Get(c echo.Context) error {
}
func Post(c echo.Context) error {
file, err := c.FormFile("photo")
if err != nil {
c.Logger().Error(err)
@@ -245,11 +254,13 @@ func Post(c echo.Context) error {
}
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
manufacturerUid := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if manufacturerUid == nil {
return nil
manufacturerUid, err := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if err != nil || manufacturerUid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
manufacturer, err := db.Manufacturer.GetById(context.Background(), *manufacturerUid)

View File

@@ -22,7 +22,9 @@ type postRequest struct {
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
cartridges, err := db.Loads.GetCartridges(context.Background())
if err != nil {
@@ -45,7 +47,9 @@ func Get(c echo.Context) error {
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
req := postRequest{}
@@ -79,14 +83,16 @@ func Post(c echo.Context) error {
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
uid := handlers.ParseUuidOrBadRequest(c, c.Param("id"))
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, c.Param("id"))
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid id")
}
err := db.Loads.DeleteCartridge(context.Background(), *uid)
err = db.Loads.DeleteCartridge(context.Background(), *uid)
if err != nil {
return err

View File

@@ -2,18 +2,12 @@ package handlers
import (
"github.com/labstack/echo/v4"
"net/http"
)
func ReadFile(c echo.Context, formName string) ([]byte, error) {
file, err := c.FormFile(formName)
if err != nil {
c.Logger().Error(err)
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "No file provided",
})
return nil, err
}

View File

@@ -2,7 +2,6 @@ package loads
import (
"context"
"fmt"
"git.siteworxpro.com/reloading-manager/backend/.gen/loading/public/table"
"git.siteworxpro.com/reloading-manager/backend/database"
"git.siteworxpro.com/reloading-manager/backend/handlers"
@@ -10,8 +9,10 @@ import (
"git.siteworxpro.com/reloading-manager/backend/handlers/primers"
"git.siteworxpro.com/reloading-manager/backend/models/loads"
"github.com/go-jet/jet/v2/postgres"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"mime"
"net/http"
"strings"
)
@@ -45,6 +46,7 @@ type row struct {
type loadResponseResults struct {
Id string `json:"id"`
Cartridge string `json:"cartridge"`
CartridgeId string `json:"cartridge_id"`
Col float32 `json:"col"`
Powder handlers.Powder `json:"powder"`
PowderGr float32 `json:"powder_gr"`
@@ -63,8 +65,28 @@ type ResultChan[T any] struct {
}
func Post(c echo.Context) error {
var exists *loads.GetLoadByIdRow
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
if id != "" {
uuid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "id is not a valid UUID")
}
gbi, err := db.Loads.GetLoadById(context.Background(), *uuid)
if err != nil || !gbi.ID.Valid {
return handlers.NotFound(c, "load not found")
}
exists = &gbi
}
cartridgeID, err := handlers.ParseUuid(c.FormValue("cartridge_id"))
if err != nil {
@@ -87,8 +109,11 @@ func Post(c echo.Context) error {
}
file, err := handlers.ReadFile(c, "photo")
if err != nil {
if err != nil && exists == nil {
return handlers.BadRequest(c, "photo is not valid")
} else if err != nil {
// If we are updating an existing load, we can ignore the error
file = exists.Photo
}
meta := c.FormValue("meta")
@@ -115,6 +140,26 @@ func Post(c echo.Context) error {
return handlers.BadRequest(c, "col is not valid")
}
if exists != nil {
err = db.Loads.UpdateLoad(context.Background(), loads.UpdateLoadParams{
ID: exists.ID,
CartridgeID: *cartridgeID,
Col: colFl,
PowderID: *powderId,
PowderGr: powderGrFl,
PrimerID: *primerId,
BulletID: *bulletId,
Photo: file,
Meta: []byte(meta),
})
if err != nil {
return err
}
return c.JSON(http.StatusOK, handlers.Response[string]{Payload: exists.ID.String()})
}
uid, err := db.Loads.CreateLoad(context.Background(), loads.CreateLoadParams{
CartridgeID: *cartridgeID,
Col: colFl,
@@ -133,16 +178,66 @@ func Post(c echo.Context) error {
return c.JSON(http.StatusCreated, handlers.Response[string]{Payload: uid.String()})
}
func Photo(c echo.Context) error {
id := c.Param("id")
if id == "" {
return handlers.BadRequest(c, "id is required")
}
uuid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "id is not a valid UUID")
}
db := c.(*database.CustomContext).Db
defer func() {
_ = db.Db.Close(context.Background())
}()
file, err := db.Loads.GetLoadById(context.Background(), *uuid)
if err != nil {
return handlers.NotFound(c, "load photo not found")
}
mt, _, _ := mime.ParseMediaType(string(file.Photo))
return c.Blob(http.StatusOK, mt, file.Photo)
}
func Get(c echo.Context) error {
cTotal := make(chan ResultChan[int64])
id := c.Param("id")
cResults := make(chan ResultChan[[]loadResponseResults])
if id != "" {
go execResultsQuery(cResults, c)
results := <-cResults
if results.Err != nil {
return results.Err
}
if len(results.Result) == 0 {
return handlers.NotFound(c, "load not found")
}
return c.JSON(http.StatusOK, handlers.Response[loadResponseResults]{Status: http.StatusText(http.StatusOK), Payload: results.Result[0]})
}
cTotal := make(chan ResultChan[int64])
go func(ch chan ResultChan[int64]) {
db := database.GetNewDatabase()
defer db.Db.Close(context.Background())
defer func(Db *pgx.Conn, ctx context.Context) {
_ = Db.Close(ctx)
}(db.Db, context.Background())
q := getQuery(c, true)
if q == nil {
ch <- ResultChan[int64]{Result: 0}
return
}
sql, params := q.Sql()
var total int64
@@ -155,24 +250,41 @@ func Get(c echo.Context) error {
} else {
ch <- ResultChan[int64]{Result: total}
}
}(cTotal)
go execResultsQuery(cResults, c)
go func(ch chan ResultChan[[]loadResponseResults]) {
total := <-cTotal
if total.Err != nil {
return total.Err
}
results := <-cResults
if results.Err != nil {
return results.Err
}
return c.JSON(http.StatusOK, handlers.Response[loadResponse]{Status: http.StatusText(http.StatusOK), Payload: loadResponse{Total: int(total.Result), Results: results.Result}})
}
func execResultsQuery(ch chan ResultChan[[]loadResponseResults], c echo.Context) {
db := database.GetNewDatabase()
defer db.Db.Close(context.Background())
defer func(Db *pgx.Conn, ctx context.Context) {
_ = Db.Close(ctx)
}(db.Db, context.Background())
q := getQuery(c, false)
fmt.Println(q.DebugSql())
if q == nil {
ch <- ResultChan[[]loadResponseResults]{Result: []loadResponseResults{}}
return
}
sql, params := q.Sql()
rows, err := db.Db.Query(context.Background(), sql, params...)
if err != nil {
ch <- ResultChan[[]loadResponseResults]{Err: err}
return
}
@@ -218,6 +330,7 @@ func Get(c echo.Context) error {
results = append(results, loadResponseResults{
Id: row.ID.String(),
Cartridge: row.CartridgeName,
CartridgeId: row.CartridgeID.String(),
Col: row.Col,
Powder: handlers.Powder{
Id: row.PowderID.String(),
@@ -251,19 +364,6 @@ func Get(c echo.Context) error {
}
ch <- ResultChan[[]loadResponseResults]{Result: results}
}(cResults)
total := <-cTotal
if total.Err != nil {
return total.Err
}
results := <-cResults
if results.Err != nil {
return results.Err
}
return c.JSON(http.StatusOK, handlers.Response[loadResponse]{Status: http.StatusText(http.StatusOK), Payload: loadResponse{Total: int(total.Result), Results: results.Result}})
}
func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement {
@@ -289,7 +389,6 @@ func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement {
if countOnly {
q = tb.SELECT(postgres.COUNT(l.ID).AS("total"))
} else {
q = tb.SELECT(
// Load
l.ID.AS("id"),
@@ -329,6 +428,17 @@ func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement {
// where expressions
expressions := make([]postgres.BoolExpression, 0)
if c.Param("id") != "" {
uuid, err := handlers.ParseUuid(c.Param("id"))
if err != nil {
return nil
}
q = q.WHERE(l.ID.EQ(postgres.UUID(uuid)))
return q
}
if c.QueryParam("cartridge_id") != "" {
ids := strings.Split(c.QueryParam("cartridge_id"), ",")
if len(ids) > 0 {
@@ -438,17 +548,48 @@ func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement {
if limit == "" {
limit = "50"
}
offset := c.QueryParam("offset")
if offset == "" {
offset = "0"
pageInt := handlers.ParseInt64OrDefault(c.QueryParam("page"), 1)
if pageInt < 1 {
pageInt = 1
}
offset := (pageInt - 1) * handlers.ParseInt64OrDefault(limit, 50)
q = q.LIMIT(handlers.ParseInt64OrDefault(limit, 50)).
OFFSET(handlers.ParseInt64OrDefault(offset, 0))
OFFSET(offset)
return q
}
func Delete(c echo.Context) error {
id := c.Param("id")
if id == "" {
return handlers.BadRequest(c, "id is required")
}
uuid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "id is not a valid UUID")
}
db := c.(*database.CustomContext).Db
defer func() {
_ = db.Db.Close(context.Background())
}()
exists, err := db.Loads.GetLoadById(context.Background(), *uuid)
if err != nil || !exists.ID.Valid {
return handlers.NotFound(c, "load not found")
}
err = db.Loads.DeleteLoad(context.Background(), *uuid)
if err != nil {
return err
}
return c.NoContent(http.StatusNoContent)
}
func getUuidExpr(ids []string) []postgres.Expression {
expr := make([]postgres.Expression, 0)
for _, id := range ids {

View File

@@ -21,13 +21,15 @@ type manufacturerResponse struct {
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
byId, err := db.Manufacturer.GetById(context.Background(), *uid)
@@ -73,15 +75,17 @@ func Get(c echo.Context) error {
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
_, err := db.Manufacturer.GetById(context.Background(), *uid)
_, err = db.Manufacturer.GetById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
@@ -97,16 +101,18 @@ func Delete(c echo.Context) error {
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID format")
}
_, err := db.Manufacturer.GetById(context.Background(), *uid)
_, err = db.Manufacturer.GetById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}

View File

@@ -13,7 +13,9 @@ import (
func Photo(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid, err := handlers.ParseUuid(id)
@@ -41,7 +43,9 @@ func Photo(c echo.Context) error {
func Delete(c echo.Context) error {
id := c.Param("id")
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
uid, err := handlers.ParseUuid(id)
if err != nil {
@@ -58,7 +62,9 @@ func Delete(c echo.Context) error {
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")
@@ -163,7 +169,9 @@ func Post(c echo.Context) error {
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")

View File

@@ -3,6 +3,7 @@ package primers
import (
"context"
"encoding/json"
"fmt"
"git.siteworxpro.com/reloading-manager/backend/database"
"git.siteworxpro.com/reloading-manager/backend/handlers"
"git.siteworxpro.com/reloading-manager/backend/models/primers"
@@ -19,7 +20,9 @@ type PrimerResponse struct {
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid, err := handlers.ParseUuid(id)
@@ -37,13 +40,15 @@ func Delete(c echo.Context) error {
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID.")
}
p, err := db.Primer.GetPrimerById(context.Background(), *uid)
@@ -56,9 +61,9 @@ func Post(c echo.Context) error {
}
if c.FormValue("manufacturer_id") != "" {
mid := handlers.ParseUuidOrBadRequest(c, c.FormValue("manufacturer_id"))
if mid == nil {
return nil
mid, err := handlers.ParseUuidOrBadRequest(c, c.FormValue("manufacturer_id"))
if err != nil || mid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid Manufacturer ID.")
}
p.ManufacturerID = *mid
@@ -112,13 +117,14 @@ func Post(c echo.Context) error {
if metaString == "" {
metaString = "{}"
}
var meta json.RawMessage = []byte(metaString)
meta := []byte(metaString)
newUuid := uuid.New().String()
uid, _ := handlers.ParseUuid(newUuid)
mid := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if mid == nil {
return nil
mid, err := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if err != nil || mid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid Manufacturer ID.")
}
err = db.Primer.InsertPrimer(context.Background(), primers.InsertPrimerParams{
@@ -145,12 +151,14 @@ func Post(c echo.Context) error {
func Photo(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID.")
}
byId, err := db.Primer.GetPrimerById(context.Background(), *uid)
@@ -172,18 +180,20 @@ func Photo(c echo.Context) error {
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
uid, err := handlers.ParseUuidOrBadRequest(c, id)
if err != nil || uid == nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid UUID.")
}
row, err := db.Primer.GetPrimerById(context.Background(), *uid)
if err != nil {
return err
return fmt.Errorf("primer not found: %v", err)
}
return c.JSON(http.StatusOK, handlers.Response[PrimerResponse]{

View File

@@ -11,7 +11,6 @@ type TestContext struct {
}
func (t TestContext) JSON(code int, i interface{}) error {
if code != 400 {
t.t.Fatal("expected 400")
}

View File

@@ -1,6 +1,7 @@
package handlers
import (
"fmt"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
@@ -23,7 +24,7 @@ func ParseUuidOrEmpty(s string) *pgtype.UUID {
func ParseUuid(s string) (*pgtype.UUID, error) {
uid, err := uuid.Parse(s)
if err != nil {
return nil, err
return nil, fmt.Errorf("invalid UUID format: %v", err)
}
return &pgtype.UUID{
@@ -32,14 +33,11 @@ func ParseUuid(s string) (*pgtype.UUID, error) {
}, nil
}
func ParseUuidOrBadRequest(c echo.Context, s string) *pgtype.UUID {
func ParseUuidOrBadRequest(c echo.Context, s string) (*pgtype.UUID, error) {
uid, err := ParseUuid(s)
if err != nil {
_ = BadRequest(c, "Invalid UUID.")
return nil
return nil, BadRequest(c, fmt.Sprintf("invalid UUID. %v", err))
}
return uid
return uid, nil
}

View File

@@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
"git.siteworxpro.com/packages/go/utilities/Env"
"git.siteworxpro.com/reloading-manager/backend/database"
"git.siteworxpro.com/reloading-manager/backend/handlers/bullets"
"git.siteworxpro.com/reloading-manager/backend/handlers/cartridge"
@@ -11,6 +10,7 @@ import (
"git.siteworxpro.com/reloading-manager/backend/handlers/manufacturer"
"git.siteworxpro.com/reloading-manager/backend/handlers/powder"
"git.siteworxpro.com/reloading-manager/backend/handlers/primers"
"gitea.siteworxpro.com/golang-packages/utilities/Env"
"github.com/go-playground/validator"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
@@ -98,7 +98,11 @@ func main() {
// loads
e.GET("/load", loads.Get)
e.GET("/load/:id", loads.Get)
e.GET("/load/:id/photo", loads.Photo)
e.POST("/load", loads.Post)
e.POST("/load/:id", loads.Post)
e.DELETE("/load/:id", loads.Delete)
addr := fmt.Sprintf("0.0.0.0:%s", Port.GetEnvString("8080"))
@@ -113,7 +117,9 @@ func migrate(e *echo.Echo) {
db := database.GetNewDatabase()
defer db.Db.Close(context.Background())
defer func() {
_ = db.Db.Close(context.Background())
}()
db.Migrate()
e.Logger.Info("✅ Complete!")

View File

@@ -18,6 +18,22 @@ delete
from cartridges
where id = $1;
-- name: DeleteLoad :exec
delete from loads
where id = $1;
-- name: UpdateLoad :exec
update loads set
cartridge_id = $1,
col = $2,
powder_id = $3,
powder_gr = $4,
primer_id = $5,
bullet_id = $6,
photo = $7,
meta = $8
where id = $9;
-- name: CreateLoad :one
insert into loads (cartridge_id, col, powder_id, powder_gr, primer_id, bullet_id, photo, meta)
values ($1, $2, $3, $4, $5, $6, $7, $8)
@@ -25,6 +41,7 @@ returning id;
-- name: GetLoadById :one
select l.id as id,
l.photo as photo,
c.id as cartridge_id,
c.name as cartridge_name,
c.meta as cartridge_meta,

View File

@@ -73,6 +73,16 @@ func (q *Queries) DeleteCartridge(ctx context.Context, id pgtype.UUID) error {
return err
}
const deleteLoad = `-- name: DeleteLoad :exec
delete from loads
where id = $1
`
func (q *Queries) DeleteLoad(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, deleteLoad, id)
return err
}
const getCartridgeById = `-- name: GetCartridgeById :one
select c.id as id, c.name, c.meta
from cartridges c
@@ -126,6 +136,7 @@ func (q *Queries) GetCartridges(ctx context.Context) ([]GetCartridgesRow, error)
const getLoadById = `-- name: GetLoadById :one
select l.id as id,
l.photo as photo,
c.id as cartridge_id,
c.name as cartridge_name,
c.meta as cartridge_meta,
@@ -147,6 +158,7 @@ where l.id = $1
type GetLoadByIdRow struct {
ID pgtype.UUID `json:"id"`
Photo []byte `json:"photo"`
CartridgeID pgtype.UUID `json:"cartridge_id"`
CartridgeName string `json:"cartridge_name"`
CartridgeMeta []byte `json:"cartridge_meta"`
@@ -165,6 +177,7 @@ func (q *Queries) GetLoadById(ctx context.Context, id pgtype.UUID) (GetLoadByIdR
var i GetLoadByIdRow
err := row.Scan(
&i.ID,
&i.Photo,
&i.CartridgeID,
&i.CartridgeName,
&i.CartridgeMeta,
@@ -376,3 +389,43 @@ func (q *Queries) TotalLoads(ctx context.Context) (int64, error) {
err := row.Scan(&count)
return count, err
}
const updateLoad = `-- name: UpdateLoad :exec
update loads set
cartridge_id = $1,
col = $2,
powder_id = $3,
powder_gr = $4,
primer_id = $5,
bullet_id = $6,
photo = $7,
meta = $8
where id = $9
`
type UpdateLoadParams struct {
CartridgeID pgtype.UUID `json:"cartridge_id"`
Col float32 `json:"col"`
PowderID pgtype.UUID `json:"powder_id"`
PowderGr float32 `json:"powder_gr"`
PrimerID pgtype.UUID `json:"primer_id"`
BulletID pgtype.UUID `json:"bullet_id"`
Photo []byte `json:"photo"`
Meta []byte `json:"meta"`
ID pgtype.UUID `json:"id"`
}
func (q *Queries) UpdateLoad(ctx context.Context, arg UpdateLoadParams) error {
_, err := q.db.Exec(ctx, updateLoad,
arg.CartridgeID,
arg.Col,
arg.PowderID,
arg.PowderGr,
arg.PrimerID,
arg.BulletID,
arg.Photo,
arg.Meta,
arg.ID,
)
return err
}

View File

@@ -1 +1 @@
v22.14.0
v25.4.0

View File

@@ -1685,13 +1685,13 @@
}
},
"node_modules/axios": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
"integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@@ -1703,9 +1703,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2031,14 +2031,15 @@
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -2758,6 +2759,51 @@
"node": ">=6"
}
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -2817,14 +2863,17 @@
}
},
"node_modules/vite": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
"integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
"picomatch": "^4.0.2",
"postcss": "^8.5.3",
"rollup": "^4.30.1"
"rollup": "^4.34.9",
"tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"
@@ -2887,6 +2936,35 @@
}
}
},
"node_modules/vite/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vscode-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",

View File

@@ -0,0 +1,49 @@
<template>
<div class="loader" v-if="loading">
<div class="flex flex-col justify-center items-center">
<ProgressSpinner :style="{ width: '50px', height: '50px' }" strokeWidth="5" />
</div>
<div class="flex flex-row justify-center items-center">
<span class="text-2xl">{{ message }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
const ProgressSpinner = defineAsyncComponent(() => import('primevue/progressspinner'))
defineProps({
loading: {
type: Boolean,
default: false
},
message: {
type: String,
default: 'Loading...'
}
})
</script>
<style scoped lang="scss">
.loader {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(9, 5, 5, 0.56);
z-index: 1000;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.text-2xl {
margin-top: 10px;
font-size: 24px;
font-weight: bold;
color: #ffffff;
}
}
</style>

View File

@@ -5,4 +5,5 @@ export const icons = {
upload: 'fa-thin fa-sharp fa-upload',
save: 'fa-thin fa-sharp fa-floppy-disk',
delete: 'fa-thin fa-sharp fa-trash',
loading: 'fa-solid fa-sharp fa-cog fa-spin',
}

View File

@@ -1,7 +1,8 @@
<template>
<Card class="md:w-2/3 w-full">
<template #title>
Add New Load
<span v-if="route.params.id">Edit Load</span>
<span v-else>Add New Load</span>
</template>
<template #content>
<div class="grid grid-cols-1">
@@ -66,7 +67,7 @@
COL
</div>
<div class="w-full mt-3">
<InputMask placeholder="0.000" v-model="load.col" mask="9.999" class="w-full" />
<InputMask :unmask="loading" :value="load.col" placeholder="0.000" v-model="load.col" mask="9.999" class="w-full" />
<Message v-if="v$.$dirty && v$.col.$invalid" :value="false" size="small" severity="error"
variant="simple">COL Required
</Message>
@@ -74,15 +75,20 @@
<div class="w-full mt-5">
<label>Picture</label>
<Message v-if="v$.$dirty && !file" :value="false" size="small" severity="error"
<Message v-if="v$.$dirty && (!route.params.id && !file)" :value="false" size="small" severity="error"
variant="simple">Picture Required
</Message>
<div class="w-1/2">
<img v-if="pictureUrl && route.params.id && !loading" :src="pictureUrl" alt="picture" />
</div>
<FileUpload v-model="file" mode="basic" @select="fileSelected" customUpload />
</div>
</div>
</template>
<template #footer>
<Button label="Add" :icon="icons.add" @click="add" />
<Button v-if="!route.params.id" label="Add" :icon="icons.add" @click="add" />
<Button v-else label="Save" :icon="icons.edit" @click="add" />
<Button class="ml-3" severity="danger" v-if="route.params.id" label="Delete" :icon="icons.delete" @click="deleteLoad" />
</template>
</Card>
@@ -103,6 +109,7 @@
</template>
</Dialog>
<FullScreenLoader :loading="loading" />
</template>
<script lang="ts">
interface Select {
@@ -121,13 +128,16 @@ import { defineAsyncComponent, onMounted, ref } from 'vue'
import { Primers } from '../../types/primers'
import { Powder } from '../../types/powder'
import axios from 'axios'
import { Response } from '../../types/Response'
import { Load, Response } from '../../types/Response'
import { icons } from '../../lib/icons.ts'
import { FileUploadSelectEvent } from 'primevue/fileupload'
import useVuelidate from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import { useToast } from 'primevue/usetoast'
import router from '../../router'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
const route = useRoute()
const Card = defineAsyncComponent(() => import('primevue/card'))
const Select = defineAsyncComponent(() => import('primevue/select'))
@@ -138,6 +148,7 @@ const InputMask = defineAsyncComponent(() => import('primevue/inputmask'))
const FileUpload = defineAsyncComponent(() => import('primevue/fileupload'))
const Dialog = defineAsyncComponent(() => import('primevue/dialog'))
const Message = defineAsyncComponent(() => import('primevue/message'))
const FullScreenLoader = defineAsyncComponent(() => import('../../components/FullScreenLoader.vue'))
const toast = useToast()
@@ -145,6 +156,7 @@ const bullets = ref<Select[]>([])
const primers = ref<Select[]>([])
const powders = ref<Select[]>([])
const cartridges = ref<Select[]>([])
const loading = ref(false)
const cartridgeName = ref('')
const addCartridgeDialog = ref(false)
@@ -154,6 +166,12 @@ const fileSelected = (e: FileUploadSelectEvent) => {
file.value = e.files[0]
}
const pictureUrl = ref<string | null>(null)
const calcUrl = () => {
const cache = new Date().getMilliseconds()
pictureUrl.value = import.meta.env.VITE_API + `/load/${route.params.id}/photo?cache=${cache}`
}
const load = ref({
bullet: '',
cartridge: '',
@@ -191,21 +209,18 @@ const fetchBullets = async () => {
bullets.value.push({ label: `${bullet.manufacturer.name} ${bullet.weight}gr ${bullet.name}`, value: bullet.id })
})
}
const fetchPrimers = async () => {
const response = await axios.get<any, Response<Primers[]>>(`${import.meta.env.VITE_API}/primer`)
response.data.payload.forEach((primer: Primers) => {
primers.value.push({ label: `${primer.manufacturer.name} ${primer.name}`, value: primer.id })
})
}
const fetchPowders = async () => {
const response = await axios.get<any, Response<Powder[]>>(`${import.meta.env.VITE_API}/powder`)
response.data.payload.forEach((powder: Powder) => {
powders.value.push({ label: `${powder.manufacturer.name} ${powder.name}`, value: powder.id })
})
}
const fetchCartridges = async () => {
cartridges.value = []
@@ -214,7 +229,6 @@ const fetchCartridges = async () => {
cartridges.value.push({ label: `${cartridge.name}`, value: cartridge.id })
})
}
const addCartridgeName = async () => {
if (cartridgeName.value === '') {
return
@@ -237,11 +251,10 @@ const addCartridgeName = async () => {
cartridgeName.value = ''
addCartridgeDialog.value = false
}
const add = async () => {
v$.value.$touch()
if (v$.value.$invalid || !file.value) {
if (v$.value.$invalid || (!route.params.id && !file.value)) {
toast.add({
severity: 'error',
summary: 'Error',
@@ -252,6 +265,12 @@ const add = async () => {
return
}
let id = ""
if (route.params.id) {
id = "/" + route.params.id.toString()
}
const formData = new FormData()
formData.append('bullet_id', load.value.bullet)
formData.append('cartridge_id', load.value.cartridge)
@@ -259,22 +278,43 @@ const add = async () => {
formData.append('powder_gr', load.value.powderGrs.toString())
formData.append('primer_id', load.value.primer)
formData.append('col', load.value.col)
if (file.value) {
formData.append('photo', file.value)
}
try {
const response = await axios.post<any, Response<string>>(`${import.meta.env.VITE_API}/load`, formData, {
const response = await axios.post<any, Response<string>>(`${import.meta.env.VITE_API}/load${id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
const message = route.params.id ? 'Load updated' : 'Load added'
toast.add({
severity: 'success',
summary: 'Success',
detail: 'Load added',
detail: message,
life: 3000,
})
if (route.params.id) {
load.value = {
bullet: '',
cartridge: '',
powder: '',
powderGrs: 0,
primer: '',
col: '',
}
file.value = null
v$.value.$reset()
await fetchLoad()
return
}
await router.push(`/loads/edit/${response.data.payload}`)
} catch (error) {
toast.add({
@@ -284,14 +324,85 @@ const add = async () => {
life: 3000,
})
}
}
const deleteLoad = async () => {
if (!route.params.id) {
return
}
try {
await axios.delete(`${import.meta.env.VITE_API}/load/${route.params.id}`)
toast.add({
severity: 'success',
summary: 'Success',
detail: 'Load deleted',
life: 3000,
})
await router.push('/loads/search')
} catch (error) {
toast.add({
severity: 'error',
summary: 'Error',
detail: 'Error deleting load',
life: 3000,
})
}
}
const fetchLoad = async () => {
if (!route.params.id) {
return
}
loading.value = true
const response = await axios.get<any, Response<Load>>(`${import.meta.env.VITE_API}/load/${route.params.id}`)
if (response.data.payload) {
load.value.cartridge = response.data.payload.cartridge_id
load.value.bullet = response.data.payload.bullet.id
load.value.powder = response.data.payload.powder.id
load.value.powderGrs = response.data.payload.powder_gr
load.value.primer = response.data.payload.primer.id
load.value.col = response.data.payload.col.toString()
if (!load.value.col.includes('.')) {
load.value.col = `${load.value.col}.000`
} else {
const parts = load.value.col.split('.')
if (parts[1].length < 3) {
load.value.col = `${parts[0]}.${parts[1].padEnd(3, '0')}`
}
}
calcUrl()
} else {
toast.add({
severity: 'error',
summary: 'Error',
detail: 'Load not found',
life: 3000,
})
await router.push('/loads')
}
loading.value = false
}
onMounted(() => {
fetchBullets()
fetchPrimers()
fetchPowders()
fetchCartridges()
if (route.params.id) {
setTimeout(() => {
fetchLoad()
}, 100)
}
})
</script>

View File

@@ -8,13 +8,15 @@
:value="loads"
filterDisplay="row"
paginator
:rowsPerPageOptions="[5, 10, 20, 50]"
size="small"
:sortField="sortField"
:sortOrder="sortOrder"
@update:sortField="(e: string) => {sortField = e; fetchLoads()}"
@update:sortOrder="(e: number | undefined) => {sortOrder = e; fetchLoads()}"
@page="updatePagination"
:rows="rowsPerPage"
lazy
:rows="50"
:totalRecords="total"
:loading="loading"
>
@@ -42,7 +44,9 @@
@change="fetchLoads" />
</template>
<template #body="{ data }">
<a target="_blank" :href="data.bullet.manufacturer.url">
{{ data.bullet.manufacturer.name }}
</a>
</template>
</Column>
<Column field="bullet_name" header="Bullet" :sortable="true" :showFilterMenu="false">
@@ -63,7 +67,9 @@
@change="fetchLoads" />
</template>
<template #body="{ data }">
<a target="_blank" :href="data.primer.manufacturer.url">
{{ data.primer.manufacturer.name }}
</a>
</template>
</Column>
<Column field="primer_name" header="Primer" :sortable="true" :showFilterMenu="false">
@@ -83,7 +89,9 @@
@change="fetchLoads" />
</template>
<template #body="{ data }">
<a target="_blank" :href="data.powder.manufacturer.url">
{{ data.powder.manufacturer.name }}
</a>
</template>
</Column>
@@ -104,8 +112,13 @@
</Column>
<Column field="edit" header="Edit">
<template #body>
<Button size="small" text :icon="icons.edit" />
<template #body="{ data }">
<Button
size="small"
text
:icon="icons.edit"
@click="router.push('/loads/edit/' + data.id)"
/>
</template>
</Column>
</DataTable>
@@ -116,22 +129,13 @@
import { Bullet } from '../../types/bullet'
import { Powder } from '../../types/powder'
import { Primers } from '../../types/primers'
import { Load } from '../../types/Response'
interface LoadResponse {
total: number
results: Load[]
}
interface Load {
id: string
bullet: Bullet
cartridge: string
powder: Powder
powder_gr: number
primer: Primers
col: number
}
interface Option {
label: string
value: string
@@ -143,6 +147,7 @@ import { Response } from '../../types/Response'
import axios from 'axios'
import Column from 'primevue/column'
import { icons } from '../../lib/icons.ts'
import router from '../../router'
const DataTable = defineAsyncComponent(() => import('primevue/datatable'))
const Card = defineAsyncComponent(() => import('primevue/card'))
@@ -151,6 +156,8 @@ const MultiSelect = defineAsyncComponent(() => import('primevue/multiselect'))
const loads = ref<Load[]>([])
const total = ref(0)
const rowsPerPage = ref(50)
const page = ref(1)
const loading = ref(true)
// use local storage to store the selected filters
@@ -181,6 +188,14 @@ const powderManufacturerSelected = ref<string[]>(localStorageFilters.powderManuf
const powderOptions = ref<Option[]>([])
const powderSelected = ref<string[]>(localStorageFilters.powderSelected ?? [])
const updatePagination = (e: { page: number, rows: number }) => {
console.log(e)
page.value = e.page + 1
rowsPerPage.value = e.rows
fetchLoads()
}
const fetchLoads = async () => {
loading.value = true
@@ -220,6 +235,9 @@ const fetchLoads = async () => {
searchParams.unshift(`powder_id=${powderSelected.value.join(',')}`)
}
searchParams.unshift(`limit=${rowsPerPage.value}`)
searchParams.unshift(`page=${page.value}`)
localStorage.setItem(localStorageKey, JSON.stringify({
cartridgeSelected: cartridgeSelected.value,
bulletManufacturerSelected: bulletManufacturerSelected.value,
@@ -257,6 +275,11 @@ onMounted(() => {
value: bullet.manufacturer.id,
}))
// dedupe the bullet Manufacturer options
bulletManufacturerOptions.value = bulletManufacturerOptions.value.filter((option, index, self) =>
index === self.findIndex((o) => o.value === option.value)
)
bulletOptions.value = resp.data.payload.map((bullet) => ({
label: `${bullet.name} ${bullet.weight}gr .${bullet.diameter}`,
value: bullet.id,
@@ -269,6 +292,11 @@ onMounted(() => {
value: primer.manufacturer.id,
}))
// dedupe the primer Manufacturer options
primerManufacturerOptions.value = primerManufacturerOptions.value.filter((option, index, self) =>
index === self.findIndex((o) => o.value === option.value)
)
primerOptions.value = resp.data.payload.map((primer) => ({
label: primer.name,
value: primer.id,
@@ -281,6 +309,11 @@ onMounted(() => {
value: powder.manufacturer.id,
}))
// dedupe the powder Manufacturer options
powderManufacturerOptions.value = powderManufacturerOptions.value.filter((option, index, self) =>
index === self.findIndex((o) => o.value === option.value)
)
powderOptions.value = resp.data.payload.map((powder) => ({
label: powder.name,
value: powder.id,

View File

@@ -61,6 +61,10 @@ const routes = [
path: '/loads/search',
component: () => import('../pages/loads/Search.vue'),
},
{
path: '/loads/edit/:id',
component: () => import('../pages/loads/Add.vue'),
},
] as RouteRecordRaw[]
const router = createRouter({

View File

@@ -1,4 +1,18 @@
import {AxiosResponse} from "axios";
import { Bullet } from './bullet'
import { Powder } from './powder'
import { Primers } from './primers'
interface Load {
id: string
bullet: Bullet
cartridge: string
cartridge_id: string
powder: Powder
powder_gr: number
primer: Primers
col: number
}
export interface Response<T> extends AxiosResponse {
data: {