You've already forked reloading-manager
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
0f915ccd82
|
|||
|
dd383b6fb3
|
|||
|
e484aa7e31
|
|||
|
3c60c1b012
|
|||
|
c5f1f32b44
|
|||
|
343abbb801
|
|||
|
13aad5254e
|
|||
|
aeba95dc41
|
|||
|
b40edf70d4
|
|||
|
4f750ae15c
|
|||
|
3246450d77
|
|||
|
74681e01b3
|
|||
|
9dce6b8930
|
|||
|
7c02aa7148
|
|||
|
|
a8c470a474 | ||
|
95b626b439
|
|||
|
b9a6598e87
|
|||
|
4251a03139
|
|||
|
691a32ff56
|
|||
|
3631a4b5dc
|
|||
|
edbd5b4f49
|
|||
|
a20bef65ae
|
|||
| 4056e6705d | |||
|
fc4305f161
|
|||
| f0fc8b7707 |
@@ -1,44 +1,44 @@
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '**'
|
||||
- 'v*'
|
||||
|
||||
name: 🏗️✨ Build Workflow
|
||||
name: 🏗️ ✨ Build Workflow
|
||||
|
||||
jobs:
|
||||
BuildFrontend:
|
||||
name: 🖼️🔨 Build Frontend
|
||||
name: 🖼️ 🔨 Build Frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: 🛡️🔒 Add Siteworx CA Certificates
|
||||
- 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
|
||||
- name: 📖 🔍 Checkout Repository Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 🔑🔐 Login to Docker Hub
|
||||
- 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
|
||||
- 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
|
||||
|
||||
- name: 🏗️🔧 Set up Docker Buildx
|
||||
- name: 🏗️ 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: 🐳🔨 Build Frontend Container
|
||||
- name: 🐳 🔨 Build Frontend Container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
tags: scr.siteworxpro.com/reloading-manager/frontend:${{ gitea.ref_name }}
|
||||
push: true
|
||||
|
||||
- name: 📦✨ Build Latest Frontend Container
|
||||
- name: 📦 ✨ Build Latest Frontend Container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
push: true
|
||||
|
||||
BuildBackend:
|
||||
name: 🖥️🔨 Build Backend
|
||||
name: 🖥️ 🔨 Build Backend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -74,23 +74,23 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 🔑🔐 Login to Docker Hub
|
||||
- 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
|
||||
- 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
|
||||
|
||||
- name: 🏗️🔧 Set up Docker Buildx
|
||||
- name: 🏗️ 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: 🐳🔨 Build Backend Container
|
||||
- name: 🐳 🔨 Build Backend Container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./backend
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
tags: scr.siteworxpro.com/reloading-manager/backend:${{ gitea.ref_name }}
|
||||
push: true
|
||||
|
||||
- name: 📦✨ Build Latest Backend Container
|
||||
- name: 📦 ✨ Build Latest Backend Container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./backend
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
push: true
|
||||
|
||||
Deploy:
|
||||
name: 🚀✨ Deploy Application
|
||||
name: 🚀 ✨ Deploy Application
|
||||
runs-on: ubuntu-latest
|
||||
needs: [BuildFrontend, BuildBackend]
|
||||
steps:
|
||||
@@ -118,25 +118,33 @@ jobs:
|
||||
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
|
||||
- name: 📖 🔍 Checkout Repository Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 📝🔧 Update Deployment Manifest
|
||||
- name: 📝 🔧 Update Deployment Manifest
|
||||
run: |
|
||||
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
|
||||
# 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"
|
||||
|
||||
- name: 💾✅ Commit Updated Manifest
|
||||
uses: EndBug/add-and-commit@v9
|
||||
- name: 🚀 ✨ Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
env:
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: 0
|
||||
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"
|
||||
|
||||
- name: 🚀✨ Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
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}"
|
||||
@@ -1,63 +1,92 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
test:
|
||||
description: 'Run tests'
|
||||
required: true
|
||||
default: 'true'
|
||||
push:
|
||||
branches:
|
||||
- "*"
|
||||
|
||||
name: 🧪✨ Unit Tests Workflow
|
||||
name: 🧪 ✨ Unit Tests Workflow
|
||||
|
||||
jobs:
|
||||
build-javascript:
|
||||
name: 🧪📜 JavaScript Tests
|
||||
name: 🧪 📜 JavaScript Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🛡️🔒 Add Siteworx CA Certificates
|
||||
- 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
|
||||
- name: 📖 🔍 Checkout Repository Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: ⚙️🔧 Set up Node.js Environment
|
||||
- name: ⚙️ 🔧 Set up Node.js Environment
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '22.14.0'
|
||||
- name: 📦📥 Install Dependencies
|
||||
- name: 📦 📥 Install Dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
npm install
|
||||
- name: ✅🧪 Run JavaScript Tests
|
||||
- name: ✅ 🧪 Run JavaScript Tests
|
||||
run: |
|
||||
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.24.3'
|
||||
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'
|
||||
GOPROXY: 'direct'
|
||||
name: 🔍🐹 Go Tests
|
||||
name: 🔍 🐹 Go Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🛡️🔒 Add Siteworx CA Certificates
|
||||
- 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: ⚙️🐹 Set up Go Environment
|
||||
- name: ⚙️ 🐹 Set up Go Environment
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.24.0'
|
||||
go-version: '1.24.3'
|
||||
cache: true
|
||||
- name: 📖🔍 Checkout Repository Code
|
||||
- name: 📖 🔍 Checkout Repository Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: 📦📥 Install Dependencies
|
||||
- name: 📦 📥 Install Dependencies
|
||||
run: |
|
||||
cd backend
|
||||
go mod download
|
||||
- name: ✅🔍 Run Go Tests
|
||||
- name: ✅ 🔍 Run Go Tests
|
||||
run: |
|
||||
cd backend
|
||||
go test -v ./... -coverprofile=coverage.out
|
||||
@@ -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" \
|
||||
"${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}
|
||||
--data "squash_commits=true" \
|
||||
"${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests"
|
||||
@@ -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.35
|
||||
ports:
|
||||
- containerPort: 80
|
||||
- name: backend
|
||||
image: scr.siteworxpro.com/reloading-manager/backend:v0.0.27
|
||||
image: scr.siteworxpro.com/reloading-manager/backend:v0.0.35
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
envFrom:
|
||||
|
||||
17
backend/.golangci.yml
Normal file
17
backend/.golangci.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
version: "2"
|
||||
linters:
|
||||
default: standard
|
||||
enable:
|
||||
- whitespace
|
||||
- tagalign
|
||||
- reassign
|
||||
- bodyclose
|
||||
- contextcheck
|
||||
- containedctx
|
||||
- godot
|
||||
- usestdlibvars
|
||||
|
||||
formatters:
|
||||
settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM siteworxpro/golang:1.24.0 AS build
|
||||
FROM siteworxpro/golang:1.24.3 AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module git.siteworxpro.com/reloading-manager/backend
|
||||
|
||||
go 1.24.0
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
git.siteworxpro.com/packages/go/utilities v1.3.0
|
||||
@@ -9,8 +9,8 @@ require (
|
||||
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
|
||||
)
|
||||
|
||||
@@ -42,6 +42,8 @@ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7Ulw
|
||||
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=
|
||||
@@ -52,6 +54,8 @@ 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=
|
||||
@@ -88,33 +92,45 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
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/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/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/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.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -43,13 +44,14 @@ type row struct {
|
||||
}
|
||||
|
||||
type loadResponseResults struct {
|
||||
Id string `json:"id"`
|
||||
Cartridge string `json:"cartridge"`
|
||||
Col float32 `json:"col"`
|
||||
Powder handlers.Powder `json:"powder"`
|
||||
PowderGr float32 `json:"powder_gr"`
|
||||
Primer primers.PrimerResponse `json:"primer"`
|
||||
Bullet bullets.BulletResponse `json:"bullet"`
|
||||
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"`
|
||||
Primer primers.PrimerResponse `json:"primer"`
|
||||
Bullet bullets.BulletResponse `json:"bullet"`
|
||||
}
|
||||
|
||||
type loadResponse struct {
|
||||
@@ -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,103 +250,8 @@ func Get(c echo.Context) error {
|
||||
} else {
|
||||
ch <- ResultChan[int64]{Result: total}
|
||||
}
|
||||
|
||||
}(cTotal)
|
||||
|
||||
go func(ch chan ResultChan[[]loadResponseResults]) {
|
||||
db := database.GetNewDatabase()
|
||||
|
||||
defer db.Db.Close(context.Background())
|
||||
|
||||
q := getQuery(c, false)
|
||||
|
||||
fmt.Println(q.DebugSql())
|
||||
|
||||
sql, params := q.Sql()
|
||||
rows, err := db.Db.Query(context.Background(), sql, params...)
|
||||
|
||||
if err != nil {
|
||||
ch <- ResultChan[[]loadResponseResults]{Err: err}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
results := make([]loadResponseResults, 0)
|
||||
for rows.Next() {
|
||||
row := row{}
|
||||
err = rows.Scan(
|
||||
&row.ID,
|
||||
&row.Col,
|
||||
&row.PowderGr,
|
||||
|
||||
&row.CartridgeID,
|
||||
&row.CartridgeName,
|
||||
&row.CartridgeMeta,
|
||||
|
||||
&row.BulletID,
|
||||
&row.BulletName,
|
||||
&row.BulletDiameter,
|
||||
&row.BulletWeight,
|
||||
&row.BulletMeta,
|
||||
&row.BulletManufacturerName,
|
||||
&row.BulletManufacturerUrl,
|
||||
|
||||
&row.PrimerID,
|
||||
&row.PrimerName,
|
||||
&row.PrimerMeta,
|
||||
&row.PrimerManufacturerName,
|
||||
&row.PrimerManufacturerUrl,
|
||||
|
||||
&row.PowderID,
|
||||
&row.PowderName,
|
||||
&row.PowderMeta,
|
||||
&row.PowderManufacturerName,
|
||||
&row.PowderManufacturerUrl,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ch <- ResultChan[[]loadResponseResults]{Err: err}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
results = append(results, loadResponseResults{
|
||||
Id: row.ID.String(),
|
||||
Cartridge: row.CartridgeName,
|
||||
Col: row.Col,
|
||||
Powder: handlers.Powder{
|
||||
Id: row.PowderID.String(),
|
||||
Name: row.PowderName,
|
||||
Meta: string(row.PowderMeta),
|
||||
Manufacturer: handlers.Manufacturer{
|
||||
Name: row.PowderManufacturerName,
|
||||
Url: row.PowderManufacturerUrl.String,
|
||||
},
|
||||
},
|
||||
PowderGr: row.PowderGr,
|
||||
Primer: primers.PrimerResponse{
|
||||
ID: row.PrimerID.String(),
|
||||
Name: row.PrimerName,
|
||||
Manufacturer: handlers.Manufacturer{
|
||||
Name: row.PrimerManufacturerName,
|
||||
Url: row.PrimerManufacturerUrl.String,
|
||||
},
|
||||
},
|
||||
Bullet: bullets.BulletResponse{
|
||||
Id: row.BulletID.String(),
|
||||
Name: row.BulletName,
|
||||
Diameter: row.BulletDiameter,
|
||||
Weight: row.BulletWeight,
|
||||
Manufacturer: handlers.Manufacturer{
|
||||
Name: row.BulletManufacturerName,
|
||||
Url: row.BulletManufacturerUrl.String,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
ch <- ResultChan[[]loadResponseResults]{Result: results}
|
||||
}(cResults)
|
||||
go execResultsQuery(cResults, c)
|
||||
|
||||
total := <-cTotal
|
||||
if total.Err != nil {
|
||||
@@ -266,6 +266,106 @@ func Get(c echo.Context) error {
|
||||
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 func(Db *pgx.Conn, ctx context.Context) {
|
||||
_ = Db.Close(ctx)
|
||||
}(db.Db, context.Background())
|
||||
|
||||
q := getQuery(c, false)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
results := make([]loadResponseResults, 0)
|
||||
for rows.Next() {
|
||||
row := row{}
|
||||
err = rows.Scan(
|
||||
&row.ID,
|
||||
&row.Col,
|
||||
&row.PowderGr,
|
||||
|
||||
&row.CartridgeID,
|
||||
&row.CartridgeName,
|
||||
&row.CartridgeMeta,
|
||||
|
||||
&row.BulletID,
|
||||
&row.BulletName,
|
||||
&row.BulletDiameter,
|
||||
&row.BulletWeight,
|
||||
&row.BulletMeta,
|
||||
&row.BulletManufacturerName,
|
||||
&row.BulletManufacturerUrl,
|
||||
|
||||
&row.PrimerID,
|
||||
&row.PrimerName,
|
||||
&row.PrimerMeta,
|
||||
&row.PrimerManufacturerName,
|
||||
&row.PrimerManufacturerUrl,
|
||||
|
||||
&row.PowderID,
|
||||
&row.PowderName,
|
||||
&row.PowderMeta,
|
||||
&row.PowderManufacturerName,
|
||||
&row.PowderManufacturerUrl,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ch <- ResultChan[[]loadResponseResults]{Err: err}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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(),
|
||||
Name: row.PowderName,
|
||||
Meta: string(row.PowderMeta),
|
||||
Manufacturer: handlers.Manufacturer{
|
||||
Name: row.PowderManufacturerName,
|
||||
Url: row.PowderManufacturerUrl.String,
|
||||
},
|
||||
},
|
||||
PowderGr: row.PowderGr,
|
||||
Primer: primers.PrimerResponse{
|
||||
ID: row.PrimerID.String(),
|
||||
Name: row.PrimerName,
|
||||
Manufacturer: handlers.Manufacturer{
|
||||
Name: row.PrimerManufacturerName,
|
||||
Url: row.PrimerManufacturerUrl.String,
|
||||
},
|
||||
},
|
||||
Bullet: bullets.BulletResponse{
|
||||
Id: row.BulletID.String(),
|
||||
Name: row.BulletName,
|
||||
Diameter: row.BulletDiameter,
|
||||
Weight: row.BulletWeight,
|
||||
Manufacturer: handlers.Manufacturer{
|
||||
Name: row.BulletManufacturerName,
|
||||
Url: row.BulletManufacturerUrl.String,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
ch <- ResultChan[[]loadResponseResults]{Result: results}
|
||||
}
|
||||
|
||||
func getQuery(c echo.Context, countOnly bool) postgres.SelectStatement {
|
||||
l := table.Loads.AS("l")
|
||||
ctg := table.Cartridges.AS("c")
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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]{
|
||||
|
||||
@@ -11,7 +11,6 @@ type TestContext struct {
|
||||
}
|
||||
|
||||
func (t TestContext) JSON(code int, i interface{}) error {
|
||||
|
||||
if code != 400 {
|
||||
t.t.Fatal("expected 400")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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!")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
49
frontend/src/components/FullScreenLoader.vue
Normal file
49
frontend/src/components/FullScreenLoader.vue
Normal 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>
|
||||
@@ -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',
|
||||
}
|
||||
@@ -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)
|
||||
formData.append('photo', file.value)
|
||||
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>
|
||||
@@ -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 }">
|
||||
{{ data.bullet.manufacturer.name }}
|
||||
<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 }">
|
||||
{{ data.primer.manufacturer.name }}
|
||||
<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 }">
|
||||
{{ data.powder.manufacturer.name }}
|
||||
<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,
|
||||
@@ -269,6 +287,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 +304,12 @@ 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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
14
frontend/src/types/Response.d.ts
vendored
14
frontend/src/types/Response.d.ts
vendored
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user