diff --git a/.dockerignore b/.dockerignore index be4439e..6962034 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,4 +12,5 @@ # Server /server/client/ -/server/tmp/ \ No newline at end of file +/server/tmp/ +/server/build/ \ No newline at end of file diff --git a/.envrc b/.envrc index 3550a30..986d33e 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ use flake +watch_file .scripts/* \ No newline at end of file diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a8bbfd1..1e6e39b 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -9,7 +9,7 @@ on: - main jobs: - release: + lint: runs-on: ubuntu-latest steps: - name: Checkout @@ -20,9 +20,11 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - - name: Install NPM Packages - working-directory: ./client - run: npm ci --legacy-peer-deps + - name: Use Cachix + uses: cachix/cachix-action@v16 + with: + name: treli + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Lint - run: nix develop --command ts-lint \ No newline at end of file + run: nix develop --command trevstack-lint \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1e45c58..1599ef8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,9 +19,15 @@ jobs: uses: cachix/install-nix-action@v31 with: nix_path: nixpkgs=channel:nixos-unstable + + - name: Use Cachix + uses: cachix/cachix-action@v16 + with: + name: treli + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Build - run: nix develop --command ts-build + run: nix develop --command trevstack-build - name: Create Release uses: softprops/action-gh-release@v2 diff --git a/.gitignore b/.gitignore index d115bc5..025065e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ .env - /docker-compose.* - +/result /.direnv/ -/build/ -/result/ \ No newline at end of file +/build/ \ No newline at end of file diff --git a/.scripts/build.sh b/.scripts/build.sh new file mode 100755 index 0000000..b14b4e6 --- /dev/null +++ b/.scripts/build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +git_root=$(git rev-parse --show-toplevel) +url=$(git config --get remote.origin.url) +name=$(basename -s .git "${url}") +git_version=$(git describe --tags --abbrev=0) +version=${git_version#v} + +echo "building client" +cd "${git_root}" +nix build .#trevstack-client +cp -a result/. server/client +chmod -R u+w server/client + +echo "building server" +cd "${git_root}/server" +echo "Building ${name}-windows-amd64-${version}.exe" +GOOS=windows GOARCH=amd64 go build -o "./build/${name}-windows-amd64-${version}.exe" . +echo "Building ${name}-linux-amd64-${version}" +GOOS=linux GOARCH=amd64 go build -o "./build/${name}-linux-amd64-${version}" . +echo "Building ${name}-linux-amd64-${version}" +GOOS=linux GOARCH=arm64 go build -o "./build/${name}-linux-arm64-${version}" . +echo "Building ${name}-linux-arm-${version}" +GOOS=linux GOARCH=arm go build -o "./build/${name}-linux-arm-${version}" . \ No newline at end of file diff --git a/.scripts/bump.sh b/.scripts/bump.sh new file mode 100755 index 0000000..c717593 --- /dev/null +++ b/.scripts/bump.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +git_root=$(git rev-parse --show-toplevel) +git_version=$(git describe --tags --abbrev=0) +version=${git_version#v} +next_version=$(echo "${version}" | awk -F. -v OFS=. '{$NF += 1 ; print}') + +echo "bumping client" +cd "${git_root}/client" +npm version "${next_version}" +git add package-lock.json +git add package.json + +echo "bumping nix" +cd "${git_root}" +nix-update --flake --version "${next_version}" default +git add flake.nix + +git commit -m "bump: v${version} -> v${next_version}" +git push origin main +git tag -a "v${next_version}" -m "bump: v${version} -> v${next_version}" +git push origin "v${next_version}" \ No newline at end of file diff --git a/.scripts/lint.sh b/.scripts/lint.sh new file mode 100755 index 0000000..9f1e390 --- /dev/null +++ b/.scripts/lint.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +git_root=$(git rev-parse --show-toplevel) + +echo "linting client" +cd "${git_root}/client" +npx prettier --check . +npx eslint . +npx svelte-check + +echo "linting server" +cd "${git_root}/server" +revive -config revive.toml -set_exit_status ./... +sqlfluff lint + +echo "linting protobuf" +cd "${git_root}" +buf lint + +echo "linting nix" +cd "${git_root}" +nix fmt -- flake.nix --check +nix flake check --all-systems \ No newline at end of file diff --git a/.scripts/update.sh b/.scripts/update.sh new file mode 100755 index 0000000..021c9aa --- /dev/null +++ b/.scripts/update.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +git_root=$(git rev-parse --show-toplevel) + +echo "updating client" +cd "${git_root}/client" +npm update --save +if ! git diff --exit-code package.json package-lock.json; then + git add package-lock.json + git add package.json + git commit -m "build(client): updated npm dependencies" +fi + +echo "updating server" +cd "${git_root}/server" +go get -u +go mod tidy +if ! git diff --exit-code go.mod go.sum; then + git add go.mod + git add go.sum + git commit -m "build(go): updated go dependencies" +fi + +echo "updating nix" +cd "${git_root}" +nix-update --flake --version=skip default +if ! git diff --exit-code flake.nix; then + git add flake.nix + git commit -m "build(nix): updated nix hashes" +fi \ No newline at end of file diff --git a/.treli.yaml b/.treli.yaml new file mode 100644 index 0000000..9332db7 --- /dev/null +++ b/.treli.yaml @@ -0,0 +1,72 @@ +apps: + buf: + color: "#cba6f7" + exts: + - proto + onstart: buf lint + onchange: buf lint && buf generate + + eslint: + color: "#fab387" + dir: client + exts: + - js + - ts + - svelte + onstart: npx eslint . + onchange: npx eslint . + + golang: + color: "#89dceb" + dir: server + exts: + - go + onstart: go build -o ./tmp/app -tags dev && ./tmp/app + onchange: go build -o ./tmp/app -tags dev && ./tmp/app + + prettier: + color: "#fab387" + dir: client + exts: + - js + - ts + - svelte + onstart: npx prettier --check . + onchange: npx prettier --check . || npx prettier --write . + + revive: + color: "#89dceb" + dir: server + exts: + - go + onstart: revive -config revive.toml -set_exit_status ./... + onchange: revive -config revive.toml -set_exit_status ./... + + sqlc: + color: "#a6e3a1" + dir: server + exts: + - sql + onstart: sqlc vet + onchange: sqlc vet && sqlc generate + + sqlfluff: + color: "#a6e3a1" + dir: server/db + exts: + - sql + onstart: sqlfluff lint + onchange: sqlfluff lint + + svelte: + color: "#fab387" + dir: client + exts: + - svelte + onstart: npx svelte-check + onchange: npx svelte-check + + vite: + color: "#fab387" + dir: client + onstart: npx vite dev \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index bb1f4cf..ed364c9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,4 +3,5 @@ "go.lintFlags": [ "--config=server/revive.toml" ], + "sqlfluff.config": "server/db/.sqlfluff", } \ No newline at end of file diff --git a/buf.gen.yaml b/buf.gen.yaml index 3c6d31f..f19c683 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -4,24 +4,24 @@ managed: enabled: true override: - file_option: go_package_prefix - value: github.com/spotdemo4/trevstack/server/internal/services + value: github.com/spotdemo4/trevstack/server/internal/connect plugins: - local: protoc-gen-go - out: server/internal/services + out: server/internal/connect opt: paths=source_relative - local: protoc-gen-connect-go - out: server/internal/services + out: server/internal/connect opt: paths=source_relative - local: protoc-gen-es - out: client/src/lib/services + out: client/src/lib/connect opt: target=ts - local: protoc-gen-connect-openapi out: client/static/openapi strategy: all opt: - - base=base.openapi.yaml + - base=openapi.base.yaml - path=openapi.yaml diff --git a/client/.prettierignore b/client/.prettierignore index 41240a2..ed6ea3d 100644 --- a/client/.prettierignore +++ b/client/.prettierignore @@ -12,4 +12,4 @@ node_modules static # Generated -src/lib/services \ No newline at end of file +src/lib/connect \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 6d45325..4046cba 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,12 +13,12 @@ "@eslint/compat": "^1.2.8", "@eslint/js": "^9.18.0", "@lucide/svelte": "^0.479.0", - "@scalar/api-reference": "^1.28.17", + "@scalar/api-reference": "^1.28.19", "@simplewebauthn/browser": "^13.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/kit": "^2.20.5", + "@sveltejs/kit": "^2.20.7", "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@tailwindcss/vite": "^4.1.3", + "@tailwindcss/vite": "^4.1.4", "bits-ui": "^1.3.19", "cbor2": "^1.12.0", "clsx": "^2.1.1", @@ -29,14 +29,14 @@ "prettier": "^3.5.3", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", - "svelte": "^5.25.12", - "svelte-check": "^4.1.5", + "svelte": "^5.27.0", + "svelte-check": "^4.1.6", "svelte-sonner": "^0.3.28", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.0.13", "tw-animate-css": "^1.2.5", "typescript": "^5.8.3", - "typescript-eslint": "^8.29.1", + "typescript-eslint": "^8.30.1", "vite": "^6.2.6" } }, @@ -736,9 +736,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", + "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", "dev": true, "license": "MIT", "dependencies": { @@ -1181,9 +1181,9 @@ } }, "node_modules/@internationalized/date": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz", - "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz", + "integrity": "sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1191,9 +1191,9 @@ } }, "node_modules/@internationalized/number": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.0.tgz", - "integrity": "sha512-PtrRcJVy7nw++wn4W2OuePQQfTqDzfusSuY1QTtui4wa7r+rGVtR75pO8CyKvHvzyQYi3Q1uO5sY0AsB4e65Bw==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.1.tgz", + "integrity": "sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1438,9 +1438,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", - "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "cpu": [ "arm" ], @@ -1452,9 +1452,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", - "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "cpu": [ "arm64" ], @@ -1466,9 +1466,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", - "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "cpu": [ "arm64" ], @@ -1480,9 +1480,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", - "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "cpu": [ "x64" ], @@ -1494,9 +1494,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", - "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "cpu": [ "arm64" ], @@ -1508,9 +1508,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", - "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "cpu": [ "x64" ], @@ -1522,9 +1522,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", - "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "cpu": [ "arm" ], @@ -1536,9 +1536,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", - "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "cpu": [ "arm" ], @@ -1550,9 +1550,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", - "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "cpu": [ "arm64" ], @@ -1564,9 +1564,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", - "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "cpu": [ "arm64" ], @@ -1578,9 +1578,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", - "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", "cpu": [ "loong64" ], @@ -1592,9 +1592,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", - "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "cpu": [ "ppc64" ], @@ -1606,9 +1606,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", - "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", "cpu": [ "riscv64" ], @@ -1620,9 +1620,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", - "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", "cpu": [ "riscv64" ], @@ -1634,9 +1634,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", - "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "cpu": [ "s390x" ], @@ -1648,9 +1648,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", - "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "cpu": [ "x64" ], @@ -1662,9 +1662,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", - "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "cpu": [ "x64" ], @@ -1676,9 +1676,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", - "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "cpu": [ "arm64" ], @@ -1690,9 +1690,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", - "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "cpu": [ "ia32" ], @@ -1704,9 +1704,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", - "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "cpu": [ "x64" ], @@ -1718,28 +1718,28 @@ ] }, "node_modules/@scalar/api-client": { - "version": "2.3.17", - "resolved": "https://registry.npmjs.org/@scalar/api-client/-/api-client-2.3.17.tgz", - "integrity": "sha512-SpolED02jqbaFLlguQ23D4rquUSdadnJAf0HNS0qvC5FXqmHsVQ2LA6EeAFyZ7WOoJ24s/2LDf5YDNZg5fcfGQ==", + "version": "2.3.19", + "resolved": "https://registry.npmjs.org/@scalar/api-client/-/api-client-2.3.19.tgz", + "integrity": "sha512-1Scff4QL6UExxcmSYv5j1dktvQZTXbmDUJp99RqmUROEhneNWEeWaZe+GZWsda5mvDoe4vP9zZKaymkulBDKYQ==", "dev": true, "license": "MIT", "dependencies": { "@headlessui/tailwindcss": "^0.2.0", "@headlessui/vue": "^1.7.20", - "@scalar/components": "0.13.46", + "@scalar/components": "0.13.47", "@scalar/draggable": "0.1.11", "@scalar/icons": "0.1.3", - "@scalar/import": "0.3.12", - "@scalar/oas-utils": "0.2.129", + "@scalar/import": "0.3.13", + "@scalar/oas-utils": "0.2.130", "@scalar/object-utils": "1.1.13", "@scalar/openapi-parser": "0.10.14", "@scalar/openapi-types": "0.2.0", - "@scalar/postman-to-openapi": "0.2.2", + "@scalar/postman-to-openapi": "0.2.3", "@scalar/snippetz": "0.2.19", - "@scalar/themes": "0.9.85", - "@scalar/types": "0.1.6", - "@scalar/use-codemirror": "0.11.91", - "@scalar/use-hooks": "0.1.39", + "@scalar/themes": "0.9.86", + "@scalar/types": "0.1.7", + "@scalar/use-codemirror": "0.11.92", + "@scalar/use-hooks": "0.1.40", "@scalar/use-toasts": "0.7.9", "@scalar/use-tooltip": "1.0.6", "@vueuse/core": "^10.10.0", @@ -1763,24 +1763,24 @@ } }, "node_modules/@scalar/api-reference": { - "version": "1.28.17", - "resolved": "https://registry.npmjs.org/@scalar/api-reference/-/api-reference-1.28.17.tgz", - "integrity": "sha512-MU+Eh6nb2q5r4QwuI4y5oo1NCeSIB+Kb1tu6yyIms0SvS/4ihN6In+8ClX1xbjm5dP46NlxlOLVu9iq1zwbRPg==", + "version": "1.28.19", + "resolved": "https://registry.npmjs.org/@scalar/api-reference/-/api-reference-1.28.19.tgz", + "integrity": "sha512-w3J6RgGXdql9+Fb1BkumargffjzmHRTdcEaWQUJcCRtxw8ZRvOLtA+hQ+/3mpZohf6Y0bC8T1hTeKZVQ1j3L5g==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/vue": "^1.0.2", "@headlessui/vue": "^1.7.20", - "@scalar/api-client": "2.3.17", + "@scalar/api-client": "2.3.19", "@scalar/code-highlight": "0.0.27", - "@scalar/components": "0.13.46", - "@scalar/oas-utils": "0.2.129", + "@scalar/components": "0.13.47", + "@scalar/oas-utils": "0.2.130", "@scalar/openapi-parser": "0.10.14", "@scalar/openapi-types": "0.2.0", "@scalar/snippetz": "0.2.19", - "@scalar/themes": "0.9.85", - "@scalar/types": "0.1.6", - "@scalar/use-hooks": "0.1.39", + "@scalar/themes": "0.9.86", + "@scalar/types": "0.1.7", + "@scalar/use-hooks": "0.1.40", "@scalar/use-toasts": "0.7.9", "@unhead/vue": "^1.11.11", "@vueuse/core": "^10.10.0", @@ -1825,9 +1825,9 @@ } }, "node_modules/@scalar/components": { - "version": "0.13.46", - "resolved": "https://registry.npmjs.org/@scalar/components/-/components-0.13.46.tgz", - "integrity": "sha512-KQldb8MC/ysGz6d8n1pQpt7tD0yK4FFzco9TNFfCX/mxDb6yY9YPZStyRm13B+pRu1O5kZs+MnBMs9dPMsm4nA==", + "version": "0.13.47", + "resolved": "https://registry.npmjs.org/@scalar/components/-/components-0.13.47.tgz", + "integrity": "sha512-e88mKKsCEspd06bpPQPnhtEvCo/jjoFFOX9yUSV9sr0sWFZHi0ihq1zvnpLKpULyS+C5zzyoN/tGVhmaYpXgyg==", "dev": true, "license": "MIT", "dependencies": { @@ -1835,8 +1835,8 @@ "@floating-ui/vue": "^1.0.2", "@headlessui/vue": "^1.7.20", "@scalar/code-highlight": "0.0.27", - "@scalar/themes": "0.9.85", - "@scalar/use-hooks": "0.1.39", + "@scalar/themes": "0.9.86", + "@scalar/use-hooks": "0.1.40", "@scalar/use-toasts": "0.7.9", "@vueuse/core": "^10.10.0", "cva": "1.0.0-beta.2", @@ -1888,13 +1888,13 @@ } }, "node_modules/@scalar/import": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@scalar/import/-/import-0.3.12.tgz", - "integrity": "sha512-1UKTcqsvv3lMZPU7Dh1a3fxq+G/EyGCXulqeGPYsABggWg6tbztbKnaCjOgWglC5Nag+1YMHzxw6IvhlrS325g==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@scalar/import/-/import-0.3.13.tgz", + "integrity": "sha512-ooKyRxwtvMpxBnoLt9mSJF8er5rCR6RzGJaIMRCj7ViN776eY4mbLiYcXre/LO8XfLSHb1p7XbNDhfFyTHaUlw==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/oas-utils": "0.2.129", + "@scalar/oas-utils": "0.2.130", "@scalar/openapi-parser": "0.10.14", "yaml": "^2.4.5" }, @@ -1903,17 +1903,17 @@ } }, "node_modules/@scalar/oas-utils": { - "version": "0.2.129", - "resolved": "https://registry.npmjs.org/@scalar/oas-utils/-/oas-utils-0.2.129.tgz", - "integrity": "sha512-SN7LJ7GRJea+1uR2b4g5e3fFuDwQMeh7OZpwWawTX9u1DqnSFLqV/MyfYgTKcqdkRcQdJPY9k/7Lg0wKLVtKIw==", + "version": "0.2.130", + "resolved": "https://registry.npmjs.org/@scalar/oas-utils/-/oas-utils-0.2.130.tgz", + "integrity": "sha512-sVpdc3+3c/WiNrKEIwzJ+ml2ZQBjarMOTDJCM/IrvYhrJE0nHrdkzxlJgNPi++vJbVl0saYt8LhEItALv7NziA==", "dev": true, "license": "MIT", "dependencies": { "@hyperjump/json-schema": "^1.9.6", "@scalar/object-utils": "1.1.13", "@scalar/openapi-types": "0.2.0", - "@scalar/themes": "0.9.85", - "@scalar/types": "0.1.6", + "@scalar/themes": "0.9.86", + "@scalar/types": "0.1.7", "flatted": "^3.3.1", "microdiff": "^1.4.0", "nanoid": "^5.1.5", @@ -1972,13 +1972,13 @@ } }, "node_modules/@scalar/postman-to-openapi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@scalar/postman-to-openapi/-/postman-to-openapi-0.2.2.tgz", - "integrity": "sha512-8v4996pX2h3K8DjPORcA8G2SwEToEfGBuVH6/bQPVoVrpkgoiYMtylza/qz4+UqoD+K80k3TneYE2ce3lXV6lg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@scalar/postman-to-openapi/-/postman-to-openapi-0.2.3.tgz", + "integrity": "sha512-/I5QbDFy+Sh29EIEgub/ztI+1eNtHRn+mln726hR+uWOyVyaDk0FfNC0R4XOn8SsDNyu5eqPbXBb9vceTVG6jQ==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/oas-utils": "0.2.129", + "@scalar/oas-utils": "0.2.130", "@scalar/openapi-types": "0.2.0" }, "engines": { @@ -1999,22 +1999,22 @@ } }, "node_modules/@scalar/themes": { - "version": "0.9.85", - "resolved": "https://registry.npmjs.org/@scalar/themes/-/themes-0.9.85.tgz", - "integrity": "sha512-mqqUZQAXJ7zwOKGY+V8m4yTRx03Mv89qhsWxLwrVA3UnH5IP+hD02WmvMAlUmp7zGPkQstmNQNf0oS6Vpzzbfg==", + "version": "0.9.86", + "resolved": "https://registry.npmjs.org/@scalar/themes/-/themes-0.9.86.tgz", + "integrity": "sha512-QUHo9g5oSWi+0Lm1vJY9TaMZRau8LHg+vte7q5BVTBnu6NuQfigCaN+ouQ73FqIVd96TwMO6Db+dilK1B+9row==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/types": "0.1.6" + "@scalar/types": "0.1.7" }, "engines": { "node": ">=18" } }, "node_modules/@scalar/types": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.1.6.tgz", - "integrity": "sha512-4GQ9VwyZm5WiOsinCIioGfByQWI+K8cY/jce9EoaJ906mXOyHfwp6lQF/ddnEJ4ptkflKkGdEQ6jm+6PnwlB5w==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@scalar/types/-/types-0.1.7.tgz", + "integrity": "sha512-irIDYzTQG2KLvFbuTI8k2Pz/R4JR+zUUSykVTbEMatkzMmVFnn1VzNSMlODbadycwZunbnL2tA27AXed9URVjw==", "dev": true, "license": "MIT", "dependencies": { @@ -2029,9 +2029,9 @@ } }, "node_modules/@scalar/use-codemirror": { - "version": "0.11.91", - "resolved": "https://registry.npmjs.org/@scalar/use-codemirror/-/use-codemirror-0.11.91.tgz", - "integrity": "sha512-u/ueIRfZffk2lAO0y43mJYyZ1UwQlpCfXvmzxR4zI3OUaEian7SviG7za5u9+vDONr3MoXK7jpYcnHFJtSjnIA==", + "version": "0.11.92", + "resolved": "https://registry.npmjs.org/@scalar/use-codemirror/-/use-codemirror-0.11.92.tgz", + "integrity": "sha512-WDd50xGLV+q1T36cKzmhqYP+TyHe4MOW0tIiu09ed9aMXGUk6kQgnf0J3rYPsGeNj9IkVG3znZ7oYT3QplLGVA==", "dev": true, "license": "MIT", "dependencies": { @@ -2050,7 +2050,7 @@ "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.2", "@replit/codemirror-css-color-picker": "^6.3.0", - "@scalar/components": "0.13.46", + "@scalar/components": "0.13.47", "codemirror": "^6.0.0", "style-mod": "^4.1.2", "vue": "^3.5.12" @@ -2060,13 +2060,13 @@ } }, "node_modules/@scalar/use-hooks": { - "version": "0.1.39", - "resolved": "https://registry.npmjs.org/@scalar/use-hooks/-/use-hooks-0.1.39.tgz", - "integrity": "sha512-YjImUPiNVaUSKOEerNvw+Cg0T/EbG+6E/mQuvhBbbXOZQP3H4033tADWiNekj2lcX4/3gGMxk7xbnAWNz1vbvQ==", + "version": "0.1.40", + "resolved": "https://registry.npmjs.org/@scalar/use-hooks/-/use-hooks-0.1.40.tgz", + "integrity": "sha512-z8qtgIcW9Z3PCrP2cbKG+D2EVhpNgl1N0ucGtDg5SMl/fvCyXNfqB9j+u3ygxkouatfQ9zRZuhxreNMkW9/H5g==", "dev": true, "license": "MIT", "dependencies": { - "@scalar/themes": "0.9.85", + "@scalar/themes": "0.9.86", "@scalar/use-toasts": "0.7.9", "@vueuse/core": "^10.10.0", "vue": "^3.5.12", @@ -2133,9 +2133,9 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.20.5", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.5.tgz", - "integrity": "sha512-zT/97KvVUo19jEGZa972ls7KICjPCB53j54TVxnEFT5VEwL16G+YFqRVwJbfxh7AmS7/Ptr1rKF7Qt4FBMDNlw==", + "version": "2.20.7", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.7.tgz", + "integrity": "sha512-dVbLMubpJJSLI4OYB+yWYNHGAhgc2bVevWuBjDj8jFUXIJOAnLwYP3vsmtcgoxNGUXoq0rHS5f7MFCsryb6nzg==", "dev": true, "license": "MIT", "dependencies": { @@ -2204,9 +2204,9 @@ } }, "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2214,45 +2214,46 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz", - "integrity": "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", + "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", "dev": true, "license": "MIT", "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", - "tailwindcss": "4.1.3" + "tailwindcss": "4.1.4" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz", - "integrity": "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", + "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.3", - "@tailwindcss/oxide-darwin-arm64": "4.1.3", - "@tailwindcss/oxide-darwin-x64": "4.1.3", - "@tailwindcss/oxide-freebsd-x64": "4.1.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.3", - "@tailwindcss/oxide-linux-x64-musl": "4.1.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.3" + "@tailwindcss/oxide-android-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-x64": "4.1.4", + "@tailwindcss/oxide-freebsd-x64": "4.1.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-x64-musl": "4.1.4", + "@tailwindcss/oxide-wasm32-wasi": "4.1.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz", - "integrity": "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", + "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", "cpu": [ "arm64" ], @@ -2267,9 +2268,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz", - "integrity": "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", + "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", "cpu": [ "arm64" ], @@ -2284,9 +2285,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz", - "integrity": "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", + "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", "cpu": [ "x64" ], @@ -2301,9 +2302,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz", - "integrity": "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", + "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", "cpu": [ "x64" ], @@ -2318,9 +2319,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz", - "integrity": "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", + "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", "cpu": [ "arm" ], @@ -2335,9 +2336,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz", - "integrity": "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", + "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", "cpu": [ "arm64" ], @@ -2352,9 +2353,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz", - "integrity": "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", + "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", "cpu": [ "arm64" ], @@ -2369,9 +2370,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz", - "integrity": "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", + "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", "cpu": [ "x64" ], @@ -2386,9 +2387,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz", - "integrity": "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", + "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", "cpu": [ "x64" ], @@ -2402,10 +2403,40 @@ "node": ">= 10" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", + "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@emnapi/wasi-threads": "^1.0.1", + "@napi-rs/wasm-runtime": "^0.2.8", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz", - "integrity": "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", + "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", "cpu": [ "arm64" ], @@ -2420,9 +2451,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz", - "integrity": "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", + "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", "cpu": [ "x64" ], @@ -2437,15 +2468,15 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.3.tgz", - "integrity": "sha512-lUI/QaDxLtlV52Lho6pu07CG9pSnRYLOPmKGIQjyHdTBagemc6HmgZxyjGAQ/5HMPrNeWBfTVIpQl0/jLXvWHQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.4.tgz", + "integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.3", - "@tailwindcss/oxide": "4.1.3", - "tailwindcss": "4.1.3" + "@tailwindcss/node": "4.1.4", + "@tailwindcss/oxide": "4.1.4", + "tailwindcss": "4.1.4" }, "peerDependencies": { "vite": "^5.2.0 || ^6" @@ -2552,17 +2583,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", - "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/type-utils": "8.29.1", - "@typescript-eslint/utils": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2582,16 +2613,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", - "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4" }, "engines": { @@ -2607,14 +2638,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", - "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1" + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2625,14 +2656,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", - "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.29.1", - "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, @@ -2649,9 +2680,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", - "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", "dev": true, "license": "MIT", "engines": { @@ -2663,14 +2694,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", - "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/visitor-keys": "8.29.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2716,16 +2747,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", - "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.29.1", - "@typescript-eslint/types": "8.29.1", - "@typescript-eslint/typescript-estree": "8.29.1" + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2740,13 +2771,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", - "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/types": "8.30.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -6927,9 +6958,9 @@ } }, "node_modules/rollup": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz", - "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "dev": true, "license": "MIT", "dependencies": { @@ -6943,26 +6974,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.39.0", - "@rollup/rollup-android-arm64": "4.39.0", - "@rollup/rollup-darwin-arm64": "4.39.0", - "@rollup/rollup-darwin-x64": "4.39.0", - "@rollup/rollup-freebsd-arm64": "4.39.0", - "@rollup/rollup-freebsd-x64": "4.39.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", - "@rollup/rollup-linux-arm-musleabihf": "4.39.0", - "@rollup/rollup-linux-arm64-gnu": "4.39.0", - "@rollup/rollup-linux-arm64-musl": "4.39.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-gnu": "4.39.0", - "@rollup/rollup-linux-riscv64-musl": "4.39.0", - "@rollup/rollup-linux-s390x-gnu": "4.39.0", - "@rollup/rollup-linux-x64-gnu": "4.39.0", - "@rollup/rollup-linux-x64-musl": "4.39.0", - "@rollup/rollup-win32-arm64-msvc": "4.39.0", - "@rollup/rollup-win32-ia32-msvc": "4.39.0", - "@rollup/rollup-win32-x64-msvc": "4.39.0", + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" } }, @@ -7188,9 +7219,9 @@ } }, "node_modules/svelte": { - "version": "5.25.12", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.25.12.tgz", - "integrity": "sha512-4Y3mRN4fuZicNwBeb7sPPEUmiNIoN4lwf2NWD6CJdtYM3xVoOvjXhHQayIRbE0pTHG0mgk88n8WZvuOiNbtD8Q==", + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.27.0.tgz", + "integrity": "sha512-Uai13Ydt1ZE+bUHme6b9U38PCYVNCqBRoBMkUKbFbKiD7kHWjdUUrklYAQZJxyKK81qII4mrBwe/YmvEMSlC9w==", "dev": true, "license": "MIT", "dependencies": { @@ -7214,9 +7245,9 @@ } }, "node_modules/svelte-check": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.5.tgz", - "integrity": "sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.1.6.tgz", + "integrity": "sha512-P7w/6tdSfk3zEVvfsgrp3h3DFC75jCdZjTQvgGJtjPORs1n7/v2VMPIoty3PWv7jnfEm3x0G/p9wH4pecTb0Wg==", "dev": true, "license": "MIT", "dependencies": { @@ -7316,9 +7347,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz", - "integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", + "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", "dev": true, "license": "MIT" }, @@ -7441,9 +7472,9 @@ } }, "node_modules/type-fest": { - "version": "4.39.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.39.1.tgz", - "integrity": "sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==", + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", + "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -7468,15 +7499,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.29.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", - "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.29.1", - "@typescript-eslint/parser": "8.29.1", - "@typescript-eslint/utils": "8.29.1" + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7820,9 +7851,9 @@ } }, "node_modules/vue-sonner": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-1.3.0.tgz", - "integrity": "sha512-jAodBy4Mri8rQjVZGQAPs4ZYymc1ywPiwfa81qU0fFl+Suk7U8NaOxIDdI1oBGLeQJqRZi/oxNIuhCLqsBmOwg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-1.3.2.tgz", + "integrity": "sha512-UbZ48E9VIya3ToiRHAZUbodKute/z/M1iT8/3fU8zEbwBRE11AKuHikssv18LMk2gTTr6eMQT4qf6JoLHWuj/A==", "dev": true, "license": "MIT" }, diff --git a/client/package.json b/client/package.json index f27c090..6ce61f7 100644 --- a/client/package.json +++ b/client/package.json @@ -19,12 +19,12 @@ "@eslint/compat": "^1.2.8", "@eslint/js": "^9.18.0", "@lucide/svelte": "^0.479.0", - "@scalar/api-reference": "^1.28.17", + "@scalar/api-reference": "^1.28.19", "@simplewebauthn/browser": "^13.1.0", "@sveltejs/adapter-static": "^3.0.8", - "@sveltejs/kit": "^2.20.5", + "@sveltejs/kit": "^2.20.7", "@sveltejs/vite-plugin-svelte": "^5.0.3", - "@tailwindcss/vite": "^4.1.3", + "@tailwindcss/vite": "^4.1.4", "bits-ui": "^1.3.19", "cbor2": "^1.12.0", "clsx": "^2.1.1", @@ -35,14 +35,14 @@ "prettier": "^3.5.3", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", - "svelte": "^5.25.12", - "svelte-check": "^4.1.5", + "svelte": "^5.27.0", + "svelte-check": "^4.1.6", "svelte-sonner": "^0.3.28", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.0.13", "tw-animate-css": "^1.2.5", "typescript": "^5.8.3", - "typescript-eslint": "^8.29.1", + "typescript-eslint": "^8.30.1", "vite": "^6.2.6" } } diff --git a/client/src/lib/services/item/v1/item_pb.ts b/client/src/lib/connect/item/v1/item_pb.ts similarity index 68% rename from client/src/lib/services/item/v1/item_pb.ts rename to client/src/lib/connect/item/v1/item_pb.ts index 0fa9365..adea77a 100644 --- a/client/src/lib/services/item/v1/item_pb.ts +++ b/client/src/lib/connect/item/v1/item_pb.ts @@ -12,16 +12,16 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file item/v1/item.proto. */ export const file_item_v1_item: GenFile = /*@__PURE__*/ - fileDesc("ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEinAEKBEl0ZW0SDwoCaWQYASABKANIAIgBARIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg0KBXByaWNlGAQgASgCEhAKCHF1YW50aXR5GAUgASgFEi4KBWFkZGVkGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEgBiAEBQgUKA19pZEIICgZfYWRkZWQiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKAMiLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKAVIA4gBARITCgZvZmZzZXQYBSABKAVIBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAMiMAoRQ3JlYXRlSXRlbVJlcXVlc3QSGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIxChJDcmVhdGVJdGVtUmVzcG9uc2USGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIwChFVcGRhdGVJdGVtUmVxdWVzdBIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIjEKElVwZGF0ZUl0ZW1SZXNwb25zZRIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgDIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKdAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL2l0ZW0vdjE7aXRlbXYxogIDSVhYqgIHSXRlbS5WMcoCB0l0ZW1cVjHiAhNJdGVtXFYxXEdQQk1ldGFkYXRh6gIISXRlbTo6VjFiBnByb3RvMw", [file_google_protobuf_timestamp]); + fileDesc("ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEigQEKBEl0ZW0SCgoCaWQYASABKAMSDAoEbmFtZRgCIAEoCRIpCgVhZGRlZBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASEwoLZGVzY3JpcHRpb24YBCABKAkSDQoFcHJpY2UYBSABKAISEAoIcXVhbnRpdHkYBiABKAUiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKAMiLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKAVIA4gBARITCgZvZmZzZXQYBSABKAVIBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAMiVwoRQ3JlYXRlSXRlbVJlcXVlc3QSDAoEbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCRINCgVwcmljZRgDIAEoAhIQCghxdWFudGl0eRgEIAEoBSJLChJDcmVhdGVJdGVtUmVzcG9uc2USCgoCaWQYASABKAMSKQoFYWRkZWQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIqcBChFVcGRhdGVJdGVtUmVxdWVzdBIKCgJpZBgBIAEoAxIRCgRuYW1lGAIgASgJSACIAQESGAoLZGVzY3JpcHRpb24YAyABKAlIAYgBARISCgVwcmljZRgEIAEoAkgCiAEBEhUKCHF1YW50aXR5GAUgASgFSAOIAQFCBwoFX25hbWVCDgoMX2Rlc2NyaXB0aW9uQggKBl9wcmljZUILCglfcXVhbnRpdHkiFAoSVXBkYXRlSXRlbVJlc3BvbnNlIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgDIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKcAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkVnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL2Nvbm5lY3QvaXRlbS92MTtpdGVtdjGiAgNJWFiqAgdJdGVtLlYxygIHSXRlbVxWMeICE0l0ZW1cVjFcR1BCTWV0YWRhdGHqAghJdGVtOjpWMWIGcHJvdG8z", [file_google_protobuf_timestamp]); /** * @generated from message item.v1.Item */ export type Item = Message<"item.v1.Item"> & { /** - * @generated from field: optional int64 id = 1; + * @generated from field: int64 id = 1; */ - id?: bigint; + id: bigint; /** * @generated from field: string name = 2; @@ -29,24 +29,24 @@ export type Item = Message<"item.v1.Item"> & { name: string; /** - * @generated from field: string description = 3; + * @generated from field: google.protobuf.Timestamp added = 3; + */ + added?: Timestamp; + + /** + * @generated from field: string description = 4; */ description: string; /** - * @generated from field: float price = 4; + * @generated from field: float price = 5; */ price: number; /** - * @generated from field: int32 quantity = 5; + * @generated from field: int32 quantity = 6; */ quantity: number; - - /** - * @generated from field: optional google.protobuf.Timestamp added = 6; - */ - added?: Timestamp; }; /** @@ -154,9 +154,24 @@ export const GetItemsResponseSchema: GenMessage = /*@__PURE__* */ export type CreateItemRequest = Message<"item.v1.CreateItemRequest"> & { /** - * @generated from field: item.v1.Item item = 1; + * @generated from field: string name = 1; */ - item?: Item; + name: string; + + /** + * @generated from field: string description = 2; + */ + description: string; + + /** + * @generated from field: float price = 3; + */ + price: number; + + /** + * @generated from field: int32 quantity = 4; + */ + quantity: number; }; /** @@ -171,9 +186,14 @@ export const CreateItemRequestSchema: GenMessage = /*@__PURE_ */ export type CreateItemResponse = Message<"item.v1.CreateItemResponse"> & { /** - * @generated from field: item.v1.Item item = 1; + * @generated from field: int64 id = 1; */ - item?: Item; + id: bigint; + + /** + * @generated from field: google.protobuf.Timestamp added = 2; + */ + added?: Timestamp; }; /** @@ -188,9 +208,29 @@ export const CreateItemResponseSchema: GenMessage = /*@__PUR */ export type UpdateItemRequest = Message<"item.v1.UpdateItemRequest"> & { /** - * @generated from field: item.v1.Item item = 1; + * @generated from field: int64 id = 1; */ - item?: Item; + id: bigint; + + /** + * @generated from field: optional string name = 2; + */ + name?: string; + + /** + * @generated from field: optional string description = 3; + */ + description?: string; + + /** + * @generated from field: optional float price = 4; + */ + price?: number; + + /** + * @generated from field: optional int32 quantity = 5; + */ + quantity?: number; }; /** @@ -204,10 +244,6 @@ export const UpdateItemRequestSchema: GenMessage = /*@__PURE_ * @generated from message item.v1.UpdateItemResponse */ export type UpdateItemResponse = Message<"item.v1.UpdateItemResponse"> & { - /** - * @generated from field: item.v1.Item item = 1; - */ - item?: Item; }; /** diff --git a/client/src/lib/services/user/v1/auth_pb.ts b/client/src/lib/connect/user/v1/auth_pb.ts similarity index 94% rename from client/src/lib/services/user/v1/auth_pb.ts rename to client/src/lib/connect/user/v1/auth_pb.ts index c5ad0fd..028d827 100644 --- a/client/src/lib/services/user/v1/auth_pb.ts +++ b/client/src/lib/connect/user/v1/auth_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file user/v1/auth.proto. */ export const file_user_v1_auth: GenFile = /*@__PURE__*/ - fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UywQEKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z"); + fileDesc("ChJ1c2VyL3YxL2F1dGgucHJvdG8SB3VzZXIudjEiMgoMTG9naW5SZXF1ZXN0EhAKCHVzZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJIh4KDUxvZ2luUmVzcG9uc2USDQoFdG9rZW4YASABKAkiTQoNU2lnblVwUmVxdWVzdBIQCgh1c2VybmFtZRgBIAEoCRIQCghwYXNzd29yZBgCIAEoCRIYChBjb25maXJtX3Bhc3N3b3JkGAMgASgJIhAKDlNpZ25VcFJlc3BvbnNlIg8KDUxvZ291dFJlcXVlc3QiEAoOTG9nb3V0UmVzcG9uc2UywQEKC0F1dGhTZXJ2aWNlEjgKBUxvZ2luEhUudXNlci52MS5Mb2dpblJlcXVlc3QaFi51c2VyLnYxLkxvZ2luUmVzcG9uc2UiABI7CgZTaWduVXASFi51c2VyLnYxLlNpZ25VcFJlcXVlc3QaFy51c2VyLnYxLlNpZ25VcFJlc3BvbnNlIgASOwoGTG9nb3V0EhYudXNlci52MS5Mb2dvdXRSZXF1ZXN0GhcudXNlci52MS5Mb2dvdXRSZXNwb25zZSIAQpwBCgtjb20udXNlci52MUIJQXV0aFByb3RvUAFaRWdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvY29ubmVjdC91c2VyL3YxO3VzZXJ2MaICA1VYWKoCB1VzZXIuVjHKAgdVc2VyXFYx4gITVXNlclxWMVxHUEJNZXRhZGF0YeoCCFVzZXI6OlYxYgZwcm90bzM"); /** * @generated from message user.v1.LoginRequest diff --git a/client/src/lib/services/user/v1/user_pb.ts b/client/src/lib/connect/user/v1/user_pb.ts similarity index 96% rename from client/src/lib/services/user/v1/user_pb.ts rename to client/src/lib/connect/user/v1/user_pb.ts index 289401a..83bda51 100644 --- a/client/src/lib/services/user/v1/user_pb.ts +++ b/client/src/lib/connect/user/v1/user_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file user/v1/user.proto. */ export const file_user_v1_user: GenFile = /*@__PURE__*/ - fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiXAoEVXNlchIKCgJpZBgBIAEoAxIQCgh1c2VybmFtZRgCIAEoCRIfChJwcm9maWxlX3BpY3R1cmVfaWQYAyABKANIAIgBAUIVChNfcHJvZmlsZV9waWN0dXJlX2lkIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlcjLPAgoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAQp0BCgtjb20udXNlci52MUIJVXNlclByb3RvUAFaRmdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvc2VydmljZXMvdXNlci92MTt1c2VydjGiAgNVWFiqAgdVc2VyLlYxygIHVXNlclxWMeICE1VzZXJcVjFcR1BCTWV0YWRhdGHqAghVc2VyOjpWMWIGcHJvdG8z"); + fileDesc("ChJ1c2VyL3YxL3VzZXIucHJvdG8SB3VzZXIudjEiXAoEVXNlchIKCgJpZBgBIAEoAxIQCgh1c2VybmFtZRgCIAEoCRIfChJwcm9maWxlX3BpY3R1cmVfaWQYAyABKANIAIgBAUIVChNfcHJvZmlsZV9waWN0dXJlX2lkIhAKDkdldFVzZXJSZXF1ZXN0Ii4KD0dldFVzZXJSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIl0KFVVwZGF0ZVBhc3N3b3JkUmVxdWVzdBIUCgxvbGRfcGFzc3dvcmQYASABKAkSFAoMbmV3X3Bhc3N3b3JkGAIgASgJEhgKEGNvbmZpcm1fcGFzc3dvcmQYAyABKAkiNQoWVXBkYXRlUGFzc3dvcmRSZXNwb25zZRIbCgR1c2VyGAEgASgLMg0udXNlci52MS5Vc2VyIj4KEEdldEFQSUtleVJlcXVlc3QSEAoIcGFzc3dvcmQYASABKAkSGAoQY29uZmlybV9wYXNzd29yZBgCIAEoCSIgChFHZXRBUElLZXlSZXNwb25zZRILCgNrZXkYASABKAkiPgobVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXF1ZXN0EhEKCWZpbGVfbmFtZRgBIAEoCRIMCgRkYXRhGAIgASgMIjsKHFVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVzcG9uc2USGwoEdXNlchgBIAEoCzINLnVzZXIudjEuVXNlcjLPAgoLVXNlclNlcnZpY2USPgoHR2V0VXNlchIXLnVzZXIudjEuR2V0VXNlclJlcXVlc3QaGC51c2VyLnYxLkdldFVzZXJSZXNwb25zZSIAElMKDlVwZGF0ZVBhc3N3b3JkEh4udXNlci52MS5VcGRhdGVQYXNzd29yZFJlcXVlc3QaHy51c2VyLnYxLlVwZGF0ZVBhc3N3b3JkUmVzcG9uc2UiABJECglHZXRBUElLZXkSGS51c2VyLnYxLkdldEFQSUtleVJlcXVlc3QaGi51c2VyLnYxLkdldEFQSUtleVJlc3BvbnNlIgASZQoUVXBkYXRlUHJvZmlsZVBpY3R1cmUSJC51c2VyLnYxLlVwZGF0ZVByb2ZpbGVQaWN0dXJlUmVxdWVzdBolLnVzZXIudjEuVXBkYXRlUHJvZmlsZVBpY3R1cmVSZXNwb25zZSIAQpwBCgtjb20udXNlci52MUIJVXNlclByb3RvUAFaRWdpdGh1Yi5jb20vc3BvdGRlbW80L3RyZXZzdGFjay9zZXJ2ZXIvaW50ZXJuYWwvY29ubmVjdC91c2VyL3YxO3VzZXJ2MaICA1VYWKoCB1VzZXIuVjHKAgdVc2VyXFYx4gITVXNlclxWMVxHUEJNZXRhZGF0YeoCCFVzZXI6OlYxYgZwcm90bzM"); /** * @generated from message user.v1.User diff --git a/client/src/lib/sharedState.svelte.ts b/client/src/lib/sharedState.svelte.ts index e25009e..8f507bc 100644 --- a/client/src/lib/sharedState.svelte.ts +++ b/client/src/lib/sharedState.svelte.ts @@ -1,4 +1,4 @@ -import type { User } from './services/user/v1/user_pb'; +import type { User } from './connect/user/v1/user_pb'; export const userState: { user: User | undefined } = $state({ user: undefined diff --git a/client/src/lib/transport.ts b/client/src/lib/transport.ts index 96e46dd..1e12234 100644 --- a/client/src/lib/transport.ts +++ b/client/src/lib/transport.ts @@ -1,8 +1,8 @@ import { createConnectTransport } from '@connectrpc/connect-web'; import { Code, ConnectError, createClient, type Interceptor } from '@connectrpc/connect'; -import { AuthService } from '$lib/services/user/v1/auth_pb'; -import { UserService } from '$lib/services/user/v1/user_pb'; -import { ItemService } from '$lib/services/item/v1/item_pb'; +import { AuthService } from '$lib/connect/user/v1/auth_pb'; +import { UserService } from '$lib/connect/user/v1/user_pb'; +import { ItemService } from '$lib/connect/item/v1/item_pb'; import { goto } from '$app/navigation'; const redirector: Interceptor = (next) => async (req) => { diff --git a/client/src/routes/(app)/items/+page.svelte b/client/src/routes/(app)/items/+page.svelte index 3edf13e..e6f61c9 100644 --- a/client/src/routes/(app)/items/+page.svelte +++ b/client/src/routes/(app)/items/+page.svelte @@ -10,7 +10,7 @@ import Input from '$lib/ui/Input.svelte'; import Select from '$lib/ui/Select.svelte'; import { SvelteMap } from 'svelte/reactivity'; - import type { Item } from '$lib/services/item/v1/item_pb'; + import type { Item } from '$lib/connect/item/v1/item_pb'; import Pagination from '$lib/ui/Pagination.svelte'; // Config @@ -113,21 +113,17 @@ const quantity = formData.get('quantity')?.toString(); try { - const response = await ItemClient.updateItem({ - item: { - id: item.id, - name: name, - description: description, - price: parseFloat(price ?? '0'), - quantity: parseInt(quantity ?? '0') - } + await ItemClient.updateItem({ + id: item.id, + name: name, + description: description, + price: price ? parseFloat(price) : undefined, + quantity: quantity ? parseInt(quantity) : undefined }); - if (response.item && item.id) { - toast.success(`item "${name}" saved`); - editsOpen.set(item.id, false); - await updateItems(); - } + toast.success(`item "${name}" saved`); + editsOpen.set(item.id, false); + await updateItems(); } catch (err) { const error = ConnectError.from(err); toast.error(error.rawMessage); @@ -341,21 +337,17 @@ const quantity = formData.get('quantity')?.toString(); try { - const response = await ItemClient.createItem({ - item: { - name: name, - description: description, - price: parseFloat(price ?? '0'), - quantity: parseInt(quantity ?? '0') - } + await ItemClient.createItem({ + name: name, + description: description, + price: parseFloat(price ?? '0'), + quantity: parseInt(quantity ?? '0') }); - if (response.item) { - form.reset(); - toast.success(`item "${name}" added`); - addedOpen = false; - await updateItems(); - } + form.reset(); + toast.success(`item "${name}" added`); + addedOpen = false; + await updateItems(); } catch (err) { const error = ConnectError.from(err); toast.error(error.rawMessage); diff --git a/client/src/routes/auth/+page.svelte b/client/src/routes/auth/+page.svelte index 750d24d..bee9a75 100644 --- a/client/src/routes/auth/+page.svelte +++ b/client/src/routes/auth/+page.svelte @@ -105,11 +105,11 @@
- +
- +
diff --git a/client/static/openapi/openapi.yaml b/client/static/openapi/openapi.yaml index 5667407..12746f0 100644 --- a/client/static/openapi/openapi.yaml +++ b/client/static/openapi/openapi.yaml @@ -111,17 +111,34 @@ components: item.v1.CreateItemRequest: type: object properties: - item: - title: item - $ref: '#/components/schemas/item.v1.Item' + name: + type: string + title: name + description: + type: string + title: description + price: + type: number + title: price + format: float + quantity: + type: integer + title: quantity + format: int32 title: CreateItemRequest additionalProperties: false item.v1.CreateItemResponse: type: object properties: - item: - title: item - $ref: '#/components/schemas/item.v1.Item' + id: + type: + - integer + - string + title: id + format: int64 + added: + title: added + $ref: '#/components/schemas/google.protobuf.Timestamp' title: CreateItemResponse additionalProperties: false item.v1.DeleteItemRequest: @@ -210,10 +227,12 @@ components: - string title: id format: int64 - nullable: true name: type: string title: name + added: + title: added + $ref: '#/components/schemas/google.protobuf.Timestamp' description: type: string title: description @@ -225,26 +244,39 @@ components: type: integer title: quantity format: int32 - added: - title: added - nullable: true - $ref: '#/components/schemas/google.protobuf.Timestamp' title: Item additionalProperties: false item.v1.UpdateItemRequest: type: object properties: - item: - title: item - $ref: '#/components/schemas/item.v1.Item' + id: + type: + - integer + - string + title: id + format: int64 + name: + type: string + title: name + nullable: true + description: + type: string + title: description + nullable: true + price: + type: number + title: price + format: float + nullable: true + quantity: + type: integer + title: quantity + format: int32 + nullable: true title: UpdateItemRequest additionalProperties: false item.v1.UpdateItemResponse: type: object - properties: - item: - title: item - $ref: '#/components/schemas/item.v1.Item' title: UpdateItemResponse additionalProperties: false connect-protocol-version: diff --git a/docker-compose-example.yaml b/docker-compose-example.yaml deleted file mode 100644 index 363d6cc..0000000 --- a/docker-compose-example.yaml +++ /dev/null @@ -1,43 +0,0 @@ -services: - trevstack: - container_name: trevstack - build: - context: . - ports: - - "${PORT}:${PORT}" - environment: - - DB_TYPE=postgres - - DB_USER=${DB_USER} - - DB_PASS=${DB_PASS} - - DB_HOST=trevstack-db - - DB_PORT=5432 - - DB_NAME=${DB_NAME} - - PORT=${PORT} - - KEY=${KEY} - depends_on: - trevstack-db: - condition: service_healthy - links: - - trevstack-db - restart: unless-stopped - - trevstack-db: - container_name: trevstack-db - image: postgres - environment: - - POSTGRES_USER=${DB_USER} - - POSTGRES_PASSWORD=${DB_PASS} - - POSTGRES_DB=${DB_NAME} - volumes: - - trevstackdata:/var/lib/postgresql/data - healthcheck: - test: "pg_isready -U postgres" - interval: 5s - timeout: 5s - retries: 5 - restart: unless-stopped - -volumes: - trevstackdata: - name: trevstackdata - external: true \ No newline at end of file diff --git a/flake.lock b/flake.lock index d466f6c..44b95dc 100644 --- a/flake.lock +++ b/flake.lock @@ -1,89 +1,12 @@ { "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "gitignore_2": { - "inputs": { - "nixpkgs": [ - "treli", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1744098102, - "narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=", + "lastModified": 1744463964, + "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7", + "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650", "type": "github" }, "original": { @@ -111,54 +34,20 @@ }, "root": { "inputs": { - "flake-utils": "flake-utils", - "gitignore": "gitignore", "nixpkgs": "nixpkgs", "treli": "treli" } }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, "treli": { "inputs": { - "flake-utils": "flake-utils_2", - "gitignore": "gitignore_2", "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1744262571, - "narHash": "sha256-zYYx5DCQuyGsEKGStakQW1eSXPofRA3LeufIEVhE/4Q=", + "lastModified": 1744776944, + "narHash": "sha256-wdmVDDyz6aZpFai2P0PBb2sMRBTgnaURylJw8wis6ks=", "owner": "spotdemo4", "repo": "treli", - "rev": "00b55f3cdc82e61a6c4f46c6cb745c71203ccde3", + "rev": "eefe10c1ba62a577592ff25e6e5b274215c1d8db", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3cd8cfc..14adb65 100644 --- a/flake.nix +++ b/flake.nix @@ -3,224 +3,114 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; treli.url = "github:spotdemo4/treli"; - gitignore = { - url = "github:hercules-ci/gitignore.nix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; }; - outputs = { self, nixpkgs, flake-utils, gitignore, treli }: - flake-utils.lib.eachDefaultSystem (system: + outputs = { + nixpkgs, + treli, + ... + }: let + pname = "trevstack"; + version = "0.0.11"; - let - pname = "trevstack"; - version = "0.0.11"; - - pkgs = import nixpkgs { - inherit system; - config.allowUnfree = true; - }; - - protoc-gen-connect-openapi = pkgs.buildGoModule { - name = "protoc-gen-connect-openapi"; - src = pkgs.fetchFromGitHub { - owner = "sudorandom"; - repo = "protoc-gen-connect-openapi"; - rev = "v0.16.1"; - sha256 = "sha256-3XBQCc9H9N/AZm/8J5bJRgBhVtoZKFvbdTB+glHxYdA="; - }; - vendorHash = "sha256-CIiG/XhV8xxjYY0sZcSvIFcJ1Wh8LyDDwqem2cSSwBA="; - nativeCheckInputs = with pkgs; [ less ]; - }; - - bobgen = pkgs.buildGoModule { - name = "bobgen"; - src = pkgs.fetchFromGitHub { - owner = "stephenafamo"; - repo = "bob"; - rev = "v0.31.0"; - sha256 = "sha256-APAckQ+EDAu459NTPXUISLIrcAcX3aQ5B/jrMUEW0EY="; - }; - vendorHash = "sha256-3blGiSxlKpWH8k0acAXXks8nCdnoWmXLmzPStJmmGcM="; - subPackages = [ - "gen/bobgen-sql" - ]; - }; - - client = pkgs.buildNpmPackage { - pname = "${pname}-client"; - inherit version; - src = gitignore.lib.gitignoreSource ./client; - npmDepsHash = "sha256-Mu04whysDA1U5wvECJJ+KopGfSzTPR/OhWz9cjTRIfU="; - nodejs = pkgs.nodejs_22; - npmFlags = [ "--legacy-peer-deps" ]; - - installPhase = '' - cp -r build "$out" - chmod -R u+w "$out" - ''; - }; - - server = pkgs.buildGoModule { - inherit client pname version; - src = gitignore.lib.gitignoreSource ./server; - vendorHash = "sha256-YmMKl9X1kVz6dk/JOSi2jghCUKObUKdm2O+JpO9PDCA="; - env.CGO_ENABLED = 0; - - preBuild = '' - cp -r ${client} client - ''; - }; - - in - { - devShells.default = pkgs.mkShell { - packages = with pkgs; [ + supportedSystems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forSystem = f: + nixpkgs.lib.genAttrs supportedSystems ( + system: + f { + inherit system; + pkgs = import nixpkgs { + inherit system; + }; + } + ); + in { + devShells = forSystem ({pkgs, ...}: { + default = pkgs.mkShell { + packages = with pkgs; + [ git nix-update treli.packages."${system}".default - sqlite - # Go backend + # Server go gotools gopls revive - bobgen + sqlc + + # database + sqlite dbmate - - # Protobuf middleware + sqlfluff + + # Protobuf buf protoc-gen-go protoc-gen-connect-go protoc-gen-es - protoc-gen-connect-openapi + (buildGoModule { + name = "protoc-gen-connect-openapi"; + src = pkgs.fetchFromGitHub { + owner = "sudorandom"; + repo = "protoc-gen-connect-openapi"; + rev = "v0.16.1"; + sha256 = "sha256-3XBQCc9H9N/AZm/8J5bJRgBhVtoZKFvbdTB+glHxYdA="; + }; + vendorHash = "sha256-CIiG/XhV8xxjYY0sZcSvIFcJ1Wh8LyDDwqem2cSSwBA="; + nativeCheckInputs = with pkgs; [less]; + }) - # Svelte frontend + # Client nodejs_22 + ] + # Use .scripts + ++ map ( + x: ( + pkgs.writeShellApplication { + name = "${pname}-${(lib.nameFromURL (baseNameOf x) ".")}"; + text = builtins.readFile x; + } + ) + ) (pkgs.lib.filesystem.listFilesRecursive ./.scripts); + }; + }); - # Update - (writeShellApplication { - name = "ts-update"; + formatter = forSystem ({pkgs, ...}: pkgs.alejandra); - text = '' - git_root=$(git rev-parse --show-toplevel) + packages = forSystem ({pkgs, ...}: rec { + default = trevstack; - cd "''${git_root}/client" - npm update --save - if ! git diff --exit-code package.json package-lock.json; then - git add package-lock.json - git add package.json - git commit -m "build(client): updated npm dependencies" - fi + trevstack-client = pkgs.buildNpmPackage { + pname = "${pname}-client"; + inherit version; + src = ./client; + npmDepsHash = "sha256-Mu04whysDA1U5wvECJJ+KopGfSzTPR/OhWz9cjTRIfU="; + nodejs = pkgs.nodejs_22; + npmFlags = ["--legacy-peer-deps"]; - cd "''${git_root}/server" - go get -u - go mod tidy - if ! git diff --exit-code go.mod go.sum; then - git add go.mod - git add go.sum - git commit -m "build(server): updated go dependencies" - fi + installPhase = '' + cp -r build "$out" + chmod -R u+w "$out" + ''; + }; - cd "''${git_root}" - nix-update --flake --version=skip --subpackage client default - if ! git diff --exit-code flake.nix; then - git add flake.nix - git commit -m "build(nix): updated nix hashes" - fi - ''; - }) + trevstack = pkgs.buildGoModule { + inherit trevstack-client pname version; + src = ./server; + vendorHash = "sha256-YmMKl9X1kVz6dk/JOSi2jghCUKObUKdm2O+JpO9PDCA="; - # Bump version - (writeShellApplication { - name = "ts-bump"; - - text = '' - git_root=$(git rev-parse --show-toplevel) - next_version=$(echo "${version}" | awk -F. -v OFS=. '{$NF += 1 ; print}') - - cd "''${git_root}/client" - npm version "''${next_version}" - git add package-lock.json - git add package.json - - cd "''${git_root}" - nix-update --flake --version "''${next_version}" --subpackage client default - git add flake.nix - git commit -m "bump: v${version} -> v''${next_version}" - git push origin main - - git tag -a "v''${next_version}" -m "bump: v${version} -> v''${next_version}" - git push origin "v''${next_version}" - ''; - }) - - # Lint - (writeShellApplication { - name = "ts-lint"; - - text = '' - git_root=$(git rev-parse --show-toplevel) - - if [ -n "''${1:-}" ]; then - cd "''${git_root}/client" - npm run format - fi - - cd "''${git_root}" - echo "Linting protobuf" - buf lint - - cd "''${git_root}/client" - echo "Linting client" - npm run check - npm run lint - - cd "''${git_root}/server" - echo "Linting server" - revive -config revive.toml -set_exit_status ./... - ''; - }) - - # Build - (writeShellApplication { - name = "ts-build"; - - text = '' - git_root=$(git rev-parse --show-toplevel) - - cd "''${git_root}" - echo "Building client" - nix build .#trevstack-client - cp -a result/. server/client - chmod -R u+w server/client - - cd "''${git_root}/server" - echo "Building ${pname}-windows-amd64-${version}.exe" - GOOS=windows GOARCH=amd64 go build -o "../build/${pname}-windows-amd64-${version}.exe" . - - echo "Building ${pname}-linux-amd64-${version}" - GOOS=linux GOARCH=amd64 go build -o "../build/${pname}-linux-amd64-${version}" . - - echo "Building ${pname}-linux-amd64-${version}" - GOOS=linux GOARCH=arm64 go build -o "../build/${pname}-linux-arm64-${version}" . - - echo "Building ${pname}-linux-arm-${version}" - GOOS=linux GOARCH=arm go build -o "../build/${pname}-linux-arm-${version}" . - ''; - }) - ]; - }; - - packages = rec { - default = trevstack; - - trevstack = server; - trevstack-client = client; - }; - } - ); + preBuild = '' + cp -r ${trevstack-client} client + ''; + }; + }); + }; } diff --git a/base.openapi.yaml b/openapi.base.yaml similarity index 100% rename from base.openapi.yaml rename to openapi.base.yaml diff --git a/proto/item/v1/item.proto b/proto/item/v1/item.proto index dbbbd0a..4b728d3 100644 --- a/proto/item/v1/item.proto +++ b/proto/item/v1/item.proto @@ -5,12 +5,12 @@ package item.v1; import "google/protobuf/timestamp.proto"; message Item { - optional int64 id = 1; + int64 id = 1; string name = 2; - string description = 3; - float price = 4; - int32 quantity = 5; - optional google.protobuf.Timestamp added = 6; + google.protobuf.Timestamp added = 3; + string description = 4; + float price = 5; + int32 quantity = 6; } service ItemService { @@ -40,19 +40,25 @@ message GetItemsResponse { int64 count = 2; } -message CreateItemRequest { - Item item = 1; +message CreateItemRequest { + string name = 1; + string description = 2; + float price = 3; + int32 quantity = 4; } message CreateItemResponse { - Item item = 1; + int64 id = 1; + google.protobuf.Timestamp added = 2; } message UpdateItemRequest { - Item item = 1; -} -message UpdateItemResponse { - Item item = 1; + int64 id = 1; + optional string name = 2; + optional string description = 3; + optional float price = 4; + optional int32 quantity = 5; } +message UpdateItemResponse {} message DeleteItemRequest { int64 id = 1; diff --git a/server/.gitignore b/server/.gitignore index 59296a4..c4763ad 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,2 +1,3 @@ /client/ -/tmp/ \ No newline at end of file +/tmp/ +/build/ \ No newline at end of file diff --git a/server/db/.sqlfluff b/server/db/.sqlfluff new file mode 100644 index 0000000..a95fb57 --- /dev/null +++ b/server/db/.sqlfluff @@ -0,0 +1,2 @@ +[sqlfluff] +dialect = sqlite \ No newline at end of file diff --git a/server/db/.sqlfluffignore b/server/db/.sqlfluffignore new file mode 100644 index 0000000..3ef401c --- /dev/null +++ b/server/db/.sqlfluffignore @@ -0,0 +1 @@ +/schema.sql \ No newline at end of file diff --git a/server/db/migrations/20250410195416_init.sql b/server/db/migrations/20250410195416_init.sql index 2c1c8f4..db194f7 100644 --- a/server/db/migrations/20250410195416_init.sql +++ b/server/db/migrations/20250410195416_init.sql @@ -1,23 +1,23 @@ -- migrate:up -CREATE TABLE user( +CREATE TABLE user ( id INTEGER PRIMARY KEY NOT NULL, username TEXT NOT NULL, password TEXT NOT NULL, profile_picture_id INTEGER, - FOREIGN KEY(profile_picture_id) REFERENCES file(id) + FOREIGN KEY (profile_picture_id) REFERENCES file (id) ); -CREATE TABLE file( +CREATE TABLE file ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, data BLOB NOT NULL, user_id INTEGER NOT NULL, - FOREIGN KEY(user_id) REFERENCES user(id) + FOREIGN KEY (user_id) REFERENCES user (id) ); -CREATE TABLE item( +CREATE TABLE item ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, added DATETIME NOT NULL, @@ -26,10 +26,10 @@ CREATE TABLE item( quantity INTEGER NOT NULL, user_id INTEGER NOT NULL, - FOREIGN KEY(user_id) REFERENCES user(id) + FOREIGN KEY (user_id) REFERENCES user (id) ); -- migrate:down -drop table user; -drop table file; -drop table item; +DROP TABLE user; +DROP TABLE file; +DROP TABLE item; diff --git a/server/db/queries/file.sql b/server/db/queries/file.sql new file mode 100644 index 0000000..80bc50b --- /dev/null +++ b/server/db/queries/file.sql @@ -0,0 +1,41 @@ +-- name: GetFile :one +SELECT + id, + name, + data, + user_id +FROM file +WHERE + id = @id + AND + user_id = @user_id +LIMIT 1; + +-- name: InsertFile :one +INSERT INTO file ( + name, + data, + user_id +) VALUES ( + @name, + @data, + @user_id +) +RETURNING id; + +-- name: UpdateFile :exec +UPDATE file +SET + name = COALESCE(sqlc.narg('name'), name), + data = COALESCE(sqlc.narg('data'), data) +WHERE + id = @id + AND + user_id = @user_id; + +-- name: DeleteFile :exec +DELETE FROM file +WHERE + id = @id + AND + user_id = @user_id; diff --git a/server/db/queries/item.sql b/server/db/queries/item.sql new file mode 100644 index 0000000..0e860be --- /dev/null +++ b/server/db/queries/item.sql @@ -0,0 +1,92 @@ +-- name: GetItem :one +SELECT + id, + name, + added, + description, + price, + quantity, + user_id +FROM item +WHERE + id = @id + AND + user_id = @user_id +LIMIT 1; + +-- name: GetItems :many +SELECT + id, + name, + added, + description, + price, + quantity, + user_id +FROM item +WHERE + user_id = @user_id + AND + ( + (name LIKE sqlc.narg('name') OR sqlc.narg('name') IS NULL) + AND + (added >= sqlc.narg('start') OR sqlc.narg('start') IS NULL) + AND + (added <= sqlc.narg('end') OR sqlc.narg('end') IS NULL) + ) +LIMIT + @limit + OFFSET + @offset; + +-- name: GetItemsCount :one +SELECT COUNT(id) +FROM item +WHERE + user_id = @user_id + AND + ( + (name LIKE sqlc.narg('name') OR sqlc.narg('name') IS NULL) + AND + (added >= sqlc.narg('start') OR sqlc.narg('start') IS NULL) + AND + (added <= sqlc.narg('end') OR sqlc.narg('end') IS NULL) + ) +LIMIT 1; + +-- name: InsertItem :one +INSERT INTO item ( + name, + added, + description, + price, + quantity, + user_id +) VALUES ( + @name, + @added, + @description, + @price, + @quantity, + @user_id +) +RETURNING id; + +-- name: UpdateItem :exec +UPDATE item +SET + name = COALESCE(sqlc.narg('name'), name), + description = COALESCE(sqlc.narg('description'), description), + price = COALESCE(sqlc.narg('price'), price), + quantity = COALESCE(sqlc.narg('quantity'), quantity) +WHERE + id = @id + AND + user_id = @user_id; + +-- name: DeleteItem :exec +DELETE FROM item +WHERE + id = @id + AND + user_id = @user_id; diff --git a/server/db/queries/user.sql b/server/db/queries/user.sql new file mode 100644 index 0000000..5153da1 --- /dev/null +++ b/server/db/queries/user.sql @@ -0,0 +1,46 @@ +-- name: GetUser :one +SELECT + id, + username, + password, + profile_picture_id +FROM user +WHERE + id = @id +LIMIT 1; + +-- name: GetUserbyUsername :one +SELECT + id, + username, + password, + profile_picture_id +FROM user +WHERE + username = @username +LIMIT 1; + +-- name: InsertUser :one +INSERT INTO user ( + username, + password +) VALUES ( + @username, + @password +) +RETURNING id; + +-- name: UpdateUser :exec +UPDATE user +SET + username = COALESCE(sqlc.narg('username'), username), + password = COALESCE(sqlc.narg('password'), password), + profile_picture_id = COALESCE( + sqlc.narg('profile_picture_id'), + profile_picture_id + ) +WHERE id = @id; + +-- name: DeleteUser :exec +DELETE FROM user +WHERE id = @id; diff --git a/server/db/schema.sql b/server/db/schema.sql index 178d3c1..7575031 100644 --- a/server/db/schema.sql +++ b/server/db/schema.sql @@ -1,21 +1,21 @@ CREATE TABLE IF NOT EXISTS "schema_migrations" (version varchar(128) primary key); -CREATE TABLE user( +CREATE TABLE user ( id INTEGER PRIMARY KEY NOT NULL, username TEXT NOT NULL, password TEXT NOT NULL, profile_picture_id INTEGER, - FOREIGN KEY(profile_picture_id) REFERENCES file(id) + FOREIGN KEY (profile_picture_id) REFERENCES file (id) ); -CREATE TABLE file( +CREATE TABLE file ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, data BLOB NOT NULL, user_id INTEGER NOT NULL, - FOREIGN KEY(user_id) REFERENCES user(id) + FOREIGN KEY (user_id) REFERENCES user (id) ); -CREATE TABLE item( +CREATE TABLE item ( id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, added DATETIME NOT NULL, @@ -24,7 +24,7 @@ CREATE TABLE item( quantity INTEGER NOT NULL, user_id INTEGER NOT NULL, - FOREIGN KEY(user_id) REFERENCES user(id) + FOREIGN KEY (user_id) REFERENCES user (id) ); -- Dbmate schema migrations INSERT INTO "schema_migrations" (version) VALUES diff --git a/server/go.mod b/server/go.mod index 0192629..38f14b9 100644 --- a/server/go.mod +++ b/server/go.mod @@ -5,15 +5,11 @@ go 1.24.1 require ( connectrpc.com/connect v1.18.1 connectrpc.com/cors v0.1.0 - github.com/aarondl/opt v0.0.0-20240623220848-083f18ab9536 github.com/amacneil/dbmate/v2 v2.26.0 github.com/golang-jwt/jwt/v5 v5.2.2 - github.com/jaswdr/faker/v2 v2.3.3 github.com/joho/godotenv v1.5.1 - github.com/lib/pq v1.10.9 github.com/rs/cors v1.11.1 github.com/spotdemo4/dbmate-sqlite-modernc v0.0.2 - github.com/stephenafamo/bob v0.31.0 golang.org/x/crypto v0.37.0 golang.org/x/net v0.39.0 golang.org/x/time v0.11.0 @@ -22,14 +18,12 @@ require ( ) require ( - github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect - github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/stephenafamo/scan v0.6.2 // indirect golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect diff --git a/server/go.sum b/server/go.sum index ba1958f..e761c44 100644 --- a/server/go.sum +++ b/server/go.sum @@ -4,15 +4,8 @@ connectrpc.com/cors v0.1.0 h1:f3gTXJyDZPrDIZCQ567jxfD9PAIpopHiRDnJRt3QuOQ= connectrpc.com/cors v0.1.0/go.mod h1:v8SJZCPfHtGH1zsm+Ttajpozd4cYIUryl4dFB6QEpfg= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf h1:+edM69bH/X6JpYPmJYBRLanAMe1V5yRXYU3hHUovGcE= -github.com/aarondl/json v0.0.0-20221020222930-8b0db17ef1bf/go.mod h1:FZqLhJSj2tg0ZN48GB1zvj00+ZYcHPqgsC7yzcgCq6k= -github.com/aarondl/opt v0.0.0-20240623220848-083f18ab9536 h1:vhpjulzH5Tr4S3uJ3Y/9pNL481kPq5ERj13ceAW0/uE= -github.com/aarondl/opt v0.0.0-20240623220848-083f18ab9536/go.mod h1:l4/5NZtYd/SIohsFhaJQQe+sPOTG22furpZ5FvcYOzk= github.com/amacneil/dbmate/v2 v2.26.0 h1:74ykEWh0V41BU3wesgmTowMn/2x9rUfIxIG+Q+vuUm0= github.com/amacneil/dbmate/v2 v2.26.0/go.mod h1:cnjZKm5x/gKMLPfExXbEDUUQi1Sv5UqY3AIgbnxql84= -github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= -github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -27,8 +20,6 @@ github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17k github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/jaswdr/faker/v2 v2.3.3 h1:0mA+B5YGjqgpOPdDY/72d6pDv7Z/5t6F1XzIfkUfgC4= -github.com/jaswdr/faker/v2 v2.3.3/go.mod h1:ROK8xwQV0hYOLDUtxCQgHGcl10jbVzIvqHxcIDdwY2Q= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -41,24 +32,12 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmYrbRiUgv+g37W5suLLLxwwniTSc= -github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494/go.mod h1:yipyliwI08eQ6XwDm1fEwKPdF/xdbkiHtrU+1Hg+vc4= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/spotdemo4/dbmate-sqlite-modernc v0.0.2 h1:537TA0HvjoWJxPzAhP+t2Gt82NpxGhCdmDsJpta7sdc= github.com/spotdemo4/dbmate-sqlite-modernc v0.0.2/go.mod h1:JwuvDDgb1VLGyHgSsQN3I+dx6QkEGk86o6Yc8vxzG/4= -github.com/stephenafamo/bob v0.31.0 h1:Nx80wK0N+gTZEdRCkmAzoYTJ3L3DPIF7Zt+Br3wwRJA= -github.com/stephenafamo/bob v0.31.0/go.mod h1:CU2OifyJmHdGvNPEGhZE2p92KirNfFiQm9g2hr9f/cY= -github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97 h1:XItoZNmhOih06TC02jK7l3wlpZ0XT/sPQYutDcGOQjg= -github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97/go.mod h1:bM3Vmw1IakoaXocHmMIGgJFYob0vuK+CFWiJHQvz0jQ= -github.com/stephenafamo/scan v0.6.2 h1:mEjx1P1MuimqALCXfZEV8+KAiVcByrgngqKatgHag9I= -github.com/stephenafamo/scan v0.6.2/go.mod h1:FhIUJ8pLNyex36xGFiazDJJ5Xry0UkAi+RkWRrEcRMg= -github.com/stephenafamo/sqlparser v0.0.0-20241111104950-b04fa8a26c9c h1:JFga++XBnZG2xlnvQyHJkeBWZ9G9mGdtgvLeSRbp/BA= -github.com/stephenafamo/sqlparser v0.0.0-20241111104950-b04fa8a26c9c/go.mod h1:4iveRk8mkzQZxDuK/W0MGLrGmu/igyDYWNDD4a6v0r0= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w= @@ -84,8 +63,6 @@ golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= diff --git a/server/internal/services/item/v1/item.pb.go b/server/internal/connect/item/v1/item.pb.go similarity index 58% rename from server/internal/services/item/v1/item.pb.go rename to server/internal/connect/item/v1/item.pb.go index 3a27404..27b1954 100644 --- a/server/internal/services/item/v1/item.pb.go +++ b/server/internal/connect/item/v1/item.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.5 +// protoc-gen-go v1.36.6 // protoc (unknown) // source: item/v1/item.proto @@ -24,12 +24,12 @@ const ( type Item struct { state protoimpl.MessageState `protogen:"open.v1"` - Id *int64 `protobuf:"varint,1,opt,name=id,proto3,oneof" json:"id,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Price float32 `protobuf:"fixed32,4,opt,name=price,proto3" json:"price,omitempty"` - Quantity int32 `protobuf:"varint,5,opt,name=quantity,proto3" json:"quantity,omitempty"` - Added *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=added,proto3,oneof" json:"added,omitempty"` + Added *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=added,proto3" json:"added,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Price float32 `protobuf:"fixed32,5,opt,name=price,proto3" json:"price,omitempty"` + Quantity int32 `protobuf:"varint,6,opt,name=quantity,proto3" json:"quantity,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -65,8 +65,8 @@ func (*Item) Descriptor() ([]byte, []int) { } func (x *Item) GetId() int64 { - if x != nil && x.Id != nil { - return *x.Id + if x != nil { + return x.Id } return 0 } @@ -78,6 +78,13 @@ func (x *Item) GetName() string { return "" } +func (x *Item) GetAdded() *timestamppb.Timestamp { + if x != nil { + return x.Added + } + return nil +} + func (x *Item) GetDescription() string { if x != nil { return x.Description @@ -99,13 +106,6 @@ func (x *Item) GetQuantity() int32 { return 0 } -func (x *Item) GetAdded() *timestamppb.Timestamp { - if x != nil { - return x.Added - } - return nil -} - type GetItemRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` @@ -324,7 +324,10 @@ func (x *GetItemsResponse) GetCount() int64 { type CreateItemRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Price float32 `protobuf:"fixed32,3,opt,name=price,proto3" json:"price,omitempty"` + Quantity int32 `protobuf:"varint,4,opt,name=quantity,proto3" json:"quantity,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -359,16 +362,38 @@ func (*CreateItemRequest) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{5} } -func (x *CreateItemRequest) GetItem() *Item { +func (x *CreateItemRequest) GetName() string { if x != nil { - return x.Item + return x.Name } - return nil + return "" +} + +func (x *CreateItemRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *CreateItemRequest) GetPrice() float32 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *CreateItemRequest) GetQuantity() int32 { + if x != nil { + return x.Quantity + } + return 0 } type CreateItemResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Added *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=added,proto3" json:"added,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -403,16 +428,27 @@ func (*CreateItemResponse) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{6} } -func (x *CreateItemResponse) GetItem() *Item { +func (x *CreateItemResponse) GetId() int64 { if x != nil { - return x.Item + return x.Id + } + return 0 +} + +func (x *CreateItemResponse) GetAdded() *timestamppb.Timestamp { + if x != nil { + return x.Added } return nil } type UpdateItemRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name *string `protobuf:"bytes,2,opt,name=name,proto3,oneof" json:"name,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Price *float32 `protobuf:"fixed32,4,opt,name=price,proto3,oneof" json:"price,omitempty"` + Quantity *int32 `protobuf:"varint,5,opt,name=quantity,proto3,oneof" json:"quantity,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -447,16 +483,43 @@ func (*UpdateItemRequest) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{7} } -func (x *UpdateItemRequest) GetItem() *Item { +func (x *UpdateItemRequest) GetId() int64 { if x != nil { - return x.Item + return x.Id } - return nil + return 0 +} + +func (x *UpdateItemRequest) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *UpdateItemRequest) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *UpdateItemRequest) GetPrice() float32 { + if x != nil && x.Price != nil { + return *x.Price + } + return 0 +} + +func (x *UpdateItemRequest) GetQuantity() int32 { + if x != nil && x.Quantity != nil { + return *x.Quantity + } + return 0 } type UpdateItemResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -491,13 +554,6 @@ func (*UpdateItemResponse) Descriptor() ([]byte, []int) { return file_item_v1_item_proto_rawDescGZIP(), []int{8} } -func (x *UpdateItemResponse) GetItem() *Item { - if x != nil { - return x.Item - } - return nil -} - type DeleteItemRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` @@ -580,103 +636,66 @@ func (*DeleteItemResponse) Descriptor() ([]byte, []int) { var File_item_v1_item_proto protoreflect.FileDescriptor -var file_item_v1_item_proto_rawDesc = string([]byte{ - 0x0a, 0x12, 0x69, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcb, - 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x13, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x02, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, - 0x01, 0x52, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f, - 0x69, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x22, 0x20, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x34, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, - 0x69, 0x74, 0x65, 0x6d, 0x22, 0x82, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, - 0x31, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x88, - 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, - 0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, - 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x04, 0x52, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x09, - 0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x4d, 0x0a, 0x10, 0x47, 0x65, 0x74, - 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, - 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, - 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, - 0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x36, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, - 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, - 0x22, 0x37, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, - 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x36, 0x0a, 0x11, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, - 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x22, 0x37, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, - 0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xeb, 0x02, 0x0a, 0x0b, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, - 0x12, 0x17, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x74, - 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x69, 0x74, 0x65, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, - 0x73, 0x12, 0x18, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, - 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x69, 0x74, - 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, - 0x1a, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x74, - 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x74, 0x65, 0x6d, - 0x2e, 0x76, 0x31, 0x42, 0x09, 0x49, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, - 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x69, 0x74, 0x65, 0x6d, 0x2f, 0x76, - 0x31, 0x3b, 0x69, 0x74, 0x65, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x49, 0x58, 0x58, 0xaa, 0x02, - 0x07, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x49, 0x74, 0x65, 0x6d, 0x5c, - 0x56, 0x31, 0xe2, 0x02, 0x13, 0x49, 0x74, 0x65, 0x6d, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x49, 0x74, 0x65, 0x6d, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -}) +const file_item_v1_item_proto_rawDesc = "" + + "\n" + + "\x12item/v1/item.proto\x12\aitem.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb0\x01\n" + + "\x04Item\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x120\n" + + "\x05added\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x05added\x12 \n" + + "\vdescription\x18\x04 \x01(\tR\vdescription\x12\x14\n" + + "\x05price\x18\x05 \x01(\x02R\x05price\x12\x1a\n" + + "\bquantity\x18\x06 \x01(\x05R\bquantity\" \n" + + "\x0eGetItemRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\"4\n" + + "\x0fGetItemResponse\x12!\n" + + "\x04item\x18\x01 \x01(\v2\r.item.v1.ItemR\x04item\"\x82\x02\n" + + "\x0fGetItemsRequest\x125\n" + + "\x05start\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampH\x00R\x05start\x88\x01\x01\x121\n" + + "\x03end\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampH\x01R\x03end\x88\x01\x01\x12\x1b\n" + + "\x06filter\x18\x03 \x01(\tH\x02R\x06filter\x88\x01\x01\x12\x19\n" + + "\x05limit\x18\x04 \x01(\x05H\x03R\x05limit\x88\x01\x01\x12\x1b\n" + + "\x06offset\x18\x05 \x01(\x05H\x04R\x06offset\x88\x01\x01B\b\n" + + "\x06_startB\x06\n" + + "\x04_endB\t\n" + + "\a_filterB\b\n" + + "\x06_limitB\t\n" + + "\a_offset\"M\n" + + "\x10GetItemsResponse\x12#\n" + + "\x05items\x18\x01 \x03(\v2\r.item.v1.ItemR\x05items\x12\x14\n" + + "\x05count\x18\x02 \x01(\x03R\x05count\"{\n" + + "\x11CreateItemRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" + + "\vdescription\x18\x02 \x01(\tR\vdescription\x12\x14\n" + + "\x05price\x18\x03 \x01(\x02R\x05price\x12\x1a\n" + + "\bquantity\x18\x04 \x01(\x05R\bquantity\"V\n" + + "\x12CreateItemResponse\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\x120\n" + + "\x05added\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x05added\"\xcf\x01\n" + + "\x11UpdateItemRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\x12\x17\n" + + "\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12%\n" + + "\vdescription\x18\x03 \x01(\tH\x01R\vdescription\x88\x01\x01\x12\x19\n" + + "\x05price\x18\x04 \x01(\x02H\x02R\x05price\x88\x01\x01\x12\x1f\n" + + "\bquantity\x18\x05 \x01(\x05H\x03R\bquantity\x88\x01\x01B\a\n" + + "\x05_nameB\x0e\n" + + "\f_descriptionB\b\n" + + "\x06_priceB\v\n" + + "\t_quantity\"\x14\n" + + "\x12UpdateItemResponse\"#\n" + + "\x11DeleteItemRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\"\x14\n" + + "\x12DeleteItemResponse2\xeb\x02\n" + + "\vItemService\x12>\n" + + "\aGetItem\x12\x17.item.v1.GetItemRequest\x1a\x18.item.v1.GetItemResponse\"\x00\x12A\n" + + "\bGetItems\x12\x18.item.v1.GetItemsRequest\x1a\x19.item.v1.GetItemsResponse\"\x00\x12G\n" + + "\n" + + "CreateItem\x12\x1a.item.v1.CreateItemRequest\x1a\x1b.item.v1.CreateItemResponse\"\x00\x12G\n" + + "\n" + + "UpdateItem\x12\x1a.item.v1.UpdateItemRequest\x1a\x1b.item.v1.UpdateItemResponse\"\x00\x12G\n" + + "\n" + + "DeleteItem\x12\x1a.item.v1.DeleteItemRequest\x1a\x1b.item.v1.DeleteItemResponse\"\x00B\x9c\x01\n" + + "\vcom.item.v1B\tItemProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/item/v1;itemv1\xa2\x02\x03IXX\xaa\x02\aItem.V1\xca\x02\aItem\\V1\xe2\x02\x13Item\\V1\\GPBMetadata\xea\x02\bItem::V1b\x06proto3" var ( file_item_v1_item_proto_rawDescOnce sync.Once @@ -711,25 +730,22 @@ var file_item_v1_item_proto_depIdxs = []int32{ 11, // 2: item.v1.GetItemsRequest.start:type_name -> google.protobuf.Timestamp 11, // 3: item.v1.GetItemsRequest.end:type_name -> google.protobuf.Timestamp 0, // 4: item.v1.GetItemsResponse.items:type_name -> item.v1.Item - 0, // 5: item.v1.CreateItemRequest.item:type_name -> item.v1.Item - 0, // 6: item.v1.CreateItemResponse.item:type_name -> item.v1.Item - 0, // 7: item.v1.UpdateItemRequest.item:type_name -> item.v1.Item - 0, // 8: item.v1.UpdateItemResponse.item:type_name -> item.v1.Item - 1, // 9: item.v1.ItemService.GetItem:input_type -> item.v1.GetItemRequest - 3, // 10: item.v1.ItemService.GetItems:input_type -> item.v1.GetItemsRequest - 5, // 11: item.v1.ItemService.CreateItem:input_type -> item.v1.CreateItemRequest - 7, // 12: item.v1.ItemService.UpdateItem:input_type -> item.v1.UpdateItemRequest - 9, // 13: item.v1.ItemService.DeleteItem:input_type -> item.v1.DeleteItemRequest - 2, // 14: item.v1.ItemService.GetItem:output_type -> item.v1.GetItemResponse - 4, // 15: item.v1.ItemService.GetItems:output_type -> item.v1.GetItemsResponse - 6, // 16: item.v1.ItemService.CreateItem:output_type -> item.v1.CreateItemResponse - 8, // 17: item.v1.ItemService.UpdateItem:output_type -> item.v1.UpdateItemResponse - 10, // 18: item.v1.ItemService.DeleteItem:output_type -> item.v1.DeleteItemResponse - 14, // [14:19] is the sub-list for method output_type - 9, // [9:14] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 11, // 5: item.v1.CreateItemResponse.added:type_name -> google.protobuf.Timestamp + 1, // 6: item.v1.ItemService.GetItem:input_type -> item.v1.GetItemRequest + 3, // 7: item.v1.ItemService.GetItems:input_type -> item.v1.GetItemsRequest + 5, // 8: item.v1.ItemService.CreateItem:input_type -> item.v1.CreateItemRequest + 7, // 9: item.v1.ItemService.UpdateItem:input_type -> item.v1.UpdateItemRequest + 9, // 10: item.v1.ItemService.DeleteItem:input_type -> item.v1.DeleteItemRequest + 2, // 11: item.v1.ItemService.GetItem:output_type -> item.v1.GetItemResponse + 4, // 12: item.v1.ItemService.GetItems:output_type -> item.v1.GetItemsResponse + 6, // 13: item.v1.ItemService.CreateItem:output_type -> item.v1.CreateItemResponse + 8, // 14: item.v1.ItemService.UpdateItem:output_type -> item.v1.UpdateItemResponse + 10, // 15: item.v1.ItemService.DeleteItem:output_type -> item.v1.DeleteItemResponse + 11, // [11:16] is the sub-list for method output_type + 6, // [6:11] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_item_v1_item_proto_init() } @@ -737,8 +753,8 @@ func file_item_v1_item_proto_init() { if File_item_v1_item_proto != nil { return } - file_item_v1_item_proto_msgTypes[0].OneofWrappers = []any{} file_item_v1_item_proto_msgTypes[3].OneofWrappers = []any{} + file_item_v1_item_proto_msgTypes[7].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/server/internal/services/item/v1/itemv1connect/item.connect.go b/server/internal/connect/item/v1/itemv1connect/item.connect.go similarity index 99% rename from server/internal/services/item/v1/itemv1connect/item.connect.go rename to server/internal/connect/item/v1/itemv1connect/item.connect.go index 4b6297b..ac7c3dc 100644 --- a/server/internal/services/item/v1/itemv1connect/item.connect.go +++ b/server/internal/connect/item/v1/itemv1connect/item.connect.go @@ -8,7 +8,7 @@ import ( connect "connectrpc.com/connect" context "context" errors "errors" - v1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1" + v1 "github.com/spotdemo4/trevstack/server/internal/connect/item/v1" http "net/http" strings "strings" ) diff --git a/server/internal/services/user/v1/auth.pb.go b/server/internal/connect/user/v1/auth.pb.go similarity index 70% rename from server/internal/services/user/v1/auth.pb.go rename to server/internal/connect/user/v1/auth.pb.go index 761a7a6..831c880 100644 --- a/server/internal/services/user/v1/auth.pb.go +++ b/server/internal/connect/user/v1/auth.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.5 +// protoc-gen-go v1.36.6 // protoc (unknown) // source: user/v1/auth.proto @@ -287,51 +287,26 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) { var File_user_v1_auth_proto protoreflect.FileDescriptor -var file_user_v1_auth_proto_rawDesc = string([]byte{ - 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x22, 0x46, 0x0a, - 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x25, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x72, 0x0a, 0x0d, - 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x22, 0x10, 0x0a, 0x0e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc1, 0x01, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x15, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x3b, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x06, - 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, - 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, - 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, - 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, - 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, - 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c, - 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -}) +const file_user_v1_auth_proto_rawDesc = "" + + "\n" + + "\x12user/v1/auth.proto\x12\auser.v1\"F\n" + + "\fLoginRequest\x12\x1a\n" + + "\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" + + "\bpassword\x18\x02 \x01(\tR\bpassword\"%\n" + + "\rLoginResponse\x12\x14\n" + + "\x05token\x18\x01 \x01(\tR\x05token\"r\n" + + "\rSignUpRequest\x12\x1a\n" + + "\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" + + "\bpassword\x18\x02 \x01(\tR\bpassword\x12)\n" + + "\x10confirm_password\x18\x03 \x01(\tR\x0fconfirmPassword\"\x10\n" + + "\x0eSignUpResponse\"\x0f\n" + + "\rLogoutRequest\"\x10\n" + + "\x0eLogoutResponse2\xc1\x01\n" + + "\vAuthService\x128\n" + + "\x05Login\x12\x15.user.v1.LoginRequest\x1a\x16.user.v1.LoginResponse\"\x00\x12;\n" + + "\x06SignUp\x12\x16.user.v1.SignUpRequest\x1a\x17.user.v1.SignUpResponse\"\x00\x12;\n" + + "\x06Logout\x12\x16.user.v1.LogoutRequest\x1a\x17.user.v1.LogoutResponse\"\x00B\x9c\x01\n" + + "\vcom.user.v1B\tAuthProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/user/v1;userv1\xa2\x02\x03UXX\xaa\x02\aUser.V1\xca\x02\aUser\\V1\xe2\x02\x13User\\V1\\GPBMetadata\xea\x02\bUser::V1b\x06proto3" var ( file_user_v1_auth_proto_rawDescOnce sync.Once diff --git a/server/internal/services/user/v1/user.pb.go b/server/internal/connect/user/v1/user.pb.go similarity index 68% rename from server/internal/services/user/v1/user.pb.go rename to server/internal/connect/user/v1/user.pb.go index e21b671..bf6acda 100644 --- a/server/internal/services/user/v1/user.pb.go +++ b/server/internal/connect/user/v1/user.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.5 +// protoc-gen-go v1.36.6 // protoc (unknown) // source: user/v1/user.proto @@ -459,84 +459,39 @@ func (x *UpdateProfilePictureResponse) GetUser() *User { var File_user_v1_user_proto protoreflect.FileDescriptor -var file_user_v1_user_proto_rawDesc = string([]byte{ - 0x0a, 0x12, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x22, 0x7c, 0x0a, - 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x31, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x69, 0x63, - 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, - 0x10, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x49, - 0x64, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x70, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, - 0x0c, 0x6f, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x6c, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, - 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x5f, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3b, - 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x10, 0x47, - 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x25, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4e, 0x0a, - 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, - 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x41, 0x0a, - 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, - 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, - 0x32, 0xcf, 0x02, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x53, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, - 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, - 0x65, 0x79, 0x12, 0x19, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, - 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x50, 0x49, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x14, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x24, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, - 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x50, 0x69, 0x63, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, - 0x76, 0x31, 0x42, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f, 0x74, - 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2f, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, - 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x55, 0x58, 0x58, 0xaa, 0x02, 0x07, - 0x55, 0x73, 0x65, 0x72, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x13, 0x55, 0x73, 0x65, 0x72, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x55, 0x73, 0x65, 0x72, 0x3a, 0x3a, - 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -}) +const file_user_v1_user_proto_rawDesc = "" + + "\n" + + "\x12user/v1/user.proto\x12\auser.v1\"|\n" + + "\x04User\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id\x12\x1a\n" + + "\busername\x18\x02 \x01(\tR\busername\x121\n" + + "\x12profile_picture_id\x18\x03 \x01(\x03H\x00R\x10profilePictureId\x88\x01\x01B\x15\n" + + "\x13_profile_picture_id\"\x10\n" + + "\x0eGetUserRequest\"4\n" + + "\x0fGetUserResponse\x12!\n" + + "\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user\"\x88\x01\n" + + "\x15UpdatePasswordRequest\x12!\n" + + "\fold_password\x18\x01 \x01(\tR\voldPassword\x12!\n" + + "\fnew_password\x18\x02 \x01(\tR\vnewPassword\x12)\n" + + "\x10confirm_password\x18\x03 \x01(\tR\x0fconfirmPassword\";\n" + + "\x16UpdatePasswordResponse\x12!\n" + + "\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user\"Y\n" + + "\x10GetAPIKeyRequest\x12\x1a\n" + + "\bpassword\x18\x01 \x01(\tR\bpassword\x12)\n" + + "\x10confirm_password\x18\x02 \x01(\tR\x0fconfirmPassword\"%\n" + + "\x11GetAPIKeyResponse\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\"N\n" + + "\x1bUpdateProfilePictureRequest\x12\x1b\n" + + "\tfile_name\x18\x01 \x01(\tR\bfileName\x12\x12\n" + + "\x04data\x18\x02 \x01(\fR\x04data\"A\n" + + "\x1cUpdateProfilePictureResponse\x12!\n" + + "\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user2\xcf\x02\n" + + "\vUserService\x12>\n" + + "\aGetUser\x12\x17.user.v1.GetUserRequest\x1a\x18.user.v1.GetUserResponse\"\x00\x12S\n" + + "\x0eUpdatePassword\x12\x1e.user.v1.UpdatePasswordRequest\x1a\x1f.user.v1.UpdatePasswordResponse\"\x00\x12D\n" + + "\tGetAPIKey\x12\x19.user.v1.GetAPIKeyRequest\x1a\x1a.user.v1.GetAPIKeyResponse\"\x00\x12e\n" + + "\x14UpdateProfilePicture\x12$.user.v1.UpdateProfilePictureRequest\x1a%.user.v1.UpdateProfilePictureResponse\"\x00B\x9c\x01\n" + + "\vcom.user.v1B\tUserProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/user/v1;userv1\xa2\x02\x03UXX\xaa\x02\aUser.V1\xca\x02\aUser\\V1\xe2\x02\x13User\\V1\\GPBMetadata\xea\x02\bUser::V1b\x06proto3" var ( file_user_v1_user_proto_rawDescOnce sync.Once diff --git a/server/internal/services/user/v1/userv1connect/auth.connect.go b/server/internal/connect/user/v1/userv1connect/auth.connect.go similarity index 99% rename from server/internal/services/user/v1/userv1connect/auth.connect.go rename to server/internal/connect/user/v1/userv1connect/auth.connect.go index 6d42016..23dcc78 100644 --- a/server/internal/services/user/v1/userv1connect/auth.connect.go +++ b/server/internal/connect/user/v1/userv1connect/auth.connect.go @@ -8,7 +8,7 @@ import ( connect "connectrpc.com/connect" context "context" errors "errors" - v1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" + v1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1" http "net/http" strings "strings" ) diff --git a/server/internal/services/user/v1/userv1connect/user.connect.go b/server/internal/connect/user/v1/userv1connect/user.connect.go similarity index 99% rename from server/internal/services/user/v1/userv1connect/user.connect.go rename to server/internal/connect/user/v1/userv1connect/user.connect.go index 7f25e60..3f61b1c 100644 --- a/server/internal/services/user/v1/userv1connect/user.connect.go +++ b/server/internal/connect/user/v1/userv1connect/user.connect.go @@ -8,7 +8,7 @@ import ( connect "connectrpc.com/connect" context "context" errors "errors" - v1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" + v1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1" http "net/http" strings "strings" ) diff --git a/server/internal/database/migrate.go b/server/internal/database/migrate.go index e7e3c86..78ee37c 100644 --- a/server/internal/database/migrate.go +++ b/server/internal/database/migrate.go @@ -9,12 +9,17 @@ import ( _ "github.com/spotdemo4/dbmate-sqlite-modernc/pkg/driver/sqlite" // Modernc sqlite ) -func Migrate(url *url.URL, dbFS *embed.FS) error { +func Migrate(dsn string, dbFS *embed.FS) error { if dbFS == nil { return nil } - db := dbmate.New(url) + dburl, err := url.Parse(dsn) + if err != nil { + return err + } + + db := dbmate.New(dburl) db.Driver() db.FS = dbFS diff --git a/server/internal/database/postgres.go b/server/internal/database/postgres.go deleted file mode 100644 index e621dcf..0000000 --- a/server/internal/database/postgres.go +++ /dev/null @@ -1,74 +0,0 @@ -package database - -import ( - "database/sql" - "fmt" - "net/url" - "runtime" - - _ "github.com/lib/pq" // Postgres - "github.com/stephenafamo/bob" -) - -func NewPostgresConnection(url *url.URL) (*bob.DB, error) { - db, err := sql.Open("postgres", postgresConnectionString(url)) - if err != nil { - return nil, err - } - - bobdb := bob.NewDB(db) - - return &bobdb, nil -} - -func postgresConnectionString(u *url.URL) string { - hostname := u.Hostname() - port := u.Port() - query := u.Query() - - // support socket parameter for consistency with mysql - if query.Get("socket") != "" { - query.Set("host", query.Get("socket")) - query.Del("socket") - } - - // default hostname - if hostname == "" && query.Get("host") == "" { - switch runtime.GOOS { - case "linux": - query.Set("host", "/var/run/postgresql") - case "darwin", "freebsd", "dragonfly", "openbsd", "netbsd": - query.Set("host", "/tmp") - default: - hostname = "localhost" - } - } - - // host param overrides url hostname - if query.Get("host") != "" { - hostname = "" - } - - // always specify a port - if query.Get("port") != "" { - port = query.Get("port") - query.Del("port") - } - if port == "" { - switch u.Scheme { - case "redshift": - port = "5439" - default: - port = "5432" - } - } - - // generate output URL - out, _ := url.Parse(u.String()) - // force scheme back to postgres if there was another postgres-compatible scheme - out.Scheme = "postgres" - out.Host = fmt.Sprintf("%s:%s", hostname, port) - out.RawQuery = query.Encode() - - return out.String() -} diff --git a/server/internal/database/sqlite.go b/server/internal/database/sqlite.go index 41f38a2..d288f9e 100644 --- a/server/internal/database/sqlite.go +++ b/server/internal/database/sqlite.go @@ -2,65 +2,28 @@ package database import ( "database/sql" - "net/url" - "regexp" + "errors" + "strings" - "github.com/stephenafamo/bob" + "github.com/spotdemo4/trevstack/server/internal/sqlc" _ "modernc.org/sqlite" // Sqlite ) -func NewSQLiteConnection(url *url.URL) (*bob.DB, error) { - db, err := sql.Open("sqlite", sqliteConnectionString(url)) +func New(dsn string) (*sqlc.Queries, *sql.DB, error) { + // Validate dsn + sp := strings.Split(dsn, ":") + if len(sp) != 2 { + return nil, nil, errors.New("invalid dsn") + } + + // Open db + db, err := sql.Open("sqlite", sp[1]) if err != nil { - return nil, err + return nil, nil, err } - // Create new bob db - bobdb := bob.NewDB(db) + // Create new sqlc connection + sqlc := sqlc.New(db) - return &bobdb, nil -} - -// ConnectionString converts a URL into a valid connection string -func sqliteConnectionString(u *url.URL) string { - // duplicate URL and remove scheme - newURL := *u - newURL.Scheme = "" - - if newURL.Opaque == "" && newURL.Path != "" { - // When the DSN is in the form "scheme:/absolute/path" or - // "scheme://absolute/path" or "scheme:///absolute/path", url.Parse - // will consider the file path as : - // - "absolute" as the hostname - // - "path" (and the rest until "?") as the URL path. - // Instead, when the DSN is in the form "scheme:", the (relative) file - // path is stored in the "Opaque" field. - // See: https://pkg.go.dev/net/url#URL - // - // While Opaque is not escaped, the URL Path is. So, if .Path contains - // the file path, we need to un-escape it, and rebuild the full path. - - newURL.Opaque = "//" + newURL.Host + mustUnescapePath(newURL.Path) - newURL.Path = "" - } - - // trim duplicate leading slashes - str := regexp.MustCompile("^//+").ReplaceAllString(newURL.String(), "/") - - return str -} - -// MustUnescapePath unescapes a URL path, and panics if it fails. -// It is used during in cases where we are parsing a generated path. -func mustUnescapePath(s string) string { - if s == "" { - panic("missing path") - } - - path, err := url.PathUnescape(s) - if err != nil { - panic(err) - } - - return path + return sqlc, db, nil } diff --git a/server/internal/handlers/file/file.go b/server/internal/handlers/file/file.go index e4b8acd..072c1d3 100644 --- a/server/internal/handlers/file/file.go +++ b/server/internal/handlers/file/file.go @@ -1,7 +1,6 @@ package file import ( - "context" "database/sql" "errors" "net/http" @@ -9,13 +8,11 @@ import ( "strings" "github.com/spotdemo4/trevstack/server/internal/interceptors" - "github.com/spotdemo4/trevstack/server/internal/models" - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/dialect/sqlite" + "github.com/spotdemo4/trevstack/server/internal/sqlc" ) type FileHandler struct { - db *bob.DB + db *sqlc.Queries key []byte } @@ -45,12 +42,10 @@ func (h *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Get the file from the database - file, err := models.Files.Query( - sqlite.WhereAnd( - models.SelectWhere.Files.ID.EQ(int64(id)), - models.SelectWhere.Files.UserID.EQ(userid), - ), - ).One(context.Background(), h.db) + file, err := h.db.GetFile(r.Context(), sqlc.GetFileParams{ + ID: int64(id), + UserID: userid, + }) if err != nil { if errors.Is(err, sql.ErrNoRows) { http.Error(w, "Not Found", http.StatusNotFound) @@ -64,7 +59,7 @@ func (h *FileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write(file.Data) } -func NewFileHandler(db *bob.DB, key string) http.Handler { +func NewFileHandler(db *sqlc.Queries, key string) http.Handler { return interceptors.WithAuthRedirect( &FileHandler{ db: db, diff --git a/server/internal/handlers/item/v1/item.go b/server/internal/handlers/item/v1/item.go index 42d495e..5fe51ce 100644 --- a/server/internal/handlers/item/v1/item.go +++ b/server/internal/handlers/item/v1/item.go @@ -8,32 +8,29 @@ import ( "time" "connectrpc.com/connect" - "github.com/aarondl/opt/omit" + itemv1 "github.com/spotdemo4/trevstack/server/internal/connect/item/v1" + "github.com/spotdemo4/trevstack/server/internal/connect/item/v1/itemv1connect" "github.com/spotdemo4/trevstack/server/internal/interceptors" - "github.com/spotdemo4/trevstack/server/internal/models" - itemv1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1" - "github.com/spotdemo4/trevstack/server/internal/services/item/v1/itemv1connect" - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/dialect/sqlite" - "github.com/stephenafamo/bob/dialect/sqlite/sm" + "github.com/spotdemo4/trevstack/server/internal/sqlc" + "github.com/spotdemo4/trevstack/server/internal/util" "google.golang.org/protobuf/types/known/timestamppb" ) -func itemToConnect(item *models.Item) *itemv1.Item { +func itemToConnect(item sqlc.Item) *itemv1.Item { timestamp := timestamppb.New(item.Added) return &itemv1.Item{ - Id: &item.ID, + Id: item.ID, Name: item.Name, Description: item.Description, - Price: item.Price, + Price: float32(item.Price), Quantity: int32(item.Quantity), Added: timestamp, } } type Handler struct { - db *bob.DB + db *sqlc.Queries key []byte } @@ -44,12 +41,10 @@ func (h *Handler) GetItem(ctx context.Context, req *connect.Request[itemv1.GetIt } // Get item - item, err := models.Items.Query( - sqlite.WhereAnd( - models.SelectWhere.Items.ID.EQ(req.Msg.Id), - models.SelectWhere.Items.UserID.EQ(userid), - ), - ).One(ctx, h.db) + item, err := h.db.GetItem(ctx, sqlc.GetItemParams{ + ID: req.Msg.Id, + UserID: userid, + }) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeNotFound, err) @@ -70,34 +65,26 @@ func (h *Handler) GetItems(ctx context.Context, req *connect.Request[itemv1.GetI return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated")) } - // Filters - query := models.Items.Query(models.SelectWhere.Items.UserID.EQ(userid)) - countQuery := models.Items.Query(models.SelectWhere.Items.UserID.EQ(userid)) - - // Counted filters - if req.Msg.Start != nil { - query.Apply(models.SelectWhere.Items.Added.GTE(req.Msg.Start.AsTime())) - countQuery.Apply(models.SelectWhere.Items.Added.GTE(req.Msg.Start.AsTime())) - } - if req.Msg.End != nil { - query.Apply(models.SelectWhere.Items.Added.LTE(req.Msg.End.AsTime())) - countQuery.Apply(models.SelectWhere.Items.Added.LTE(req.Msg.End.AsTime())) - } - if req.Msg.Filter != nil && *req.Msg.Filter != "" { - query.Apply(models.SelectWhere.Items.Name.Like("%" + *req.Msg.Filter + "%")) - countQuery.Apply(models.SelectWhere.Items.Name.Like(*req.Msg.Filter)) - } - - // Uncounted filters - if req.Msg.Limit != nil { - query.Apply(sm.Limit(*req.Msg.Limit)) - } + // Verify + offset := 0 if req.Msg.Offset != nil { - query.Apply(sm.Offset(*req.Msg.Offset)) + offset = int(*req.Msg.Offset) } - // Get items & count - items, err := query.All(ctx, h.db) + limit := 10 + if req.Msg.Limit != nil { + limit = int(*req.Msg.Limit) + } + + // Get items + items, err := h.db.GetItems(ctx, sqlc.GetItemsParams{ + UserID: userid, + Name: util.NullLike(req.Msg.Filter), + Start: util.NullTimestamp(req.Msg.Start), + End: util.NullTimestamp(req.Msg.End), + Offset: int64(offset), + Limit: int64(limit), + }) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeNotFound, err) @@ -106,12 +93,18 @@ func (h *Handler) GetItems(ctx context.Context, req *connect.Request[itemv1.GetI return nil, connect.NewError(connect.CodeInternal, err) } - count, err := query.Count(ctx, h.db) + // Get items count + count, err := h.db.GetItemsCount(ctx, sqlc.GetItemsCountParams{ + UserID: userid, + Name: util.NullLike(req.Msg.Filter), + Start: util.NullTimestamp(req.Msg.Start), + End: util.NullTimestamp(req.Msg.End), + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } - // Convert to connect v1 items + // Convert to connect items resItems := []*itemv1.Item{} for _, item := range items { resItems = append(resItems, itemToConnect(item)) @@ -130,20 +123,24 @@ func (h *Handler) CreateItem(ctx context.Context, req *connect.Request[itemv1.Cr return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated")) } - item, err := models.Items.Insert(&models.ItemSetter{ - Name: omit.From(req.Msg.Item.Name), - Description: omit.From(req.Msg.Item.Description), - Price: omit.From(req.Msg.Item.Price), - Quantity: omit.From(int64(req.Msg.Item.Quantity)), - Added: omit.From(time.Now()), - UserID: omit.From(userid), - }).One(ctx, h.db) + time := time.Now() + + // Insert item + id, err := h.db.InsertItem(ctx, sqlc.InsertItemParams{ + Name: req.Msg.Name, + Added: time, + Description: req.Msg.Description, + Price: float64(req.Msg.Price), + Quantity: int64(req.Msg.Quantity), + UserID: userid, + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } res := connect.NewResponse(&itemv1.CreateItemResponse{ - Item: itemToConnect(item), + Id: id, + Added: timestamppb.New(time), }) return res, nil } @@ -154,34 +151,23 @@ func (h *Handler) UpdateItem(ctx context.Context, req *connect.Request[itemv1.Up return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated")) } - // Validate - if req.Msg.Item.Id == nil { - return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("id is required")) - } - // Update item - item, err := models.Items.Update( - // Set col - models.ItemSetter{ - Name: omit.From(req.Msg.Item.Name), - Description: omit.From(req.Msg.Item.Description), - Price: omit.From(req.Msg.Item.Price), - Quantity: omit.From(int64(req.Msg.Item.Quantity)), - }.UpdateMod(), + err := h.db.UpdateItem(ctx, sqlc.UpdateItemParams{ + // set + Name: req.Msg.Name, + Description: req.Msg.Description, + Price: util.NullFloat64(req.Msg.Price), + Quantity: util.NullInt64(req.Msg.Quantity), - // Where - sqlite.WhereAnd( - models.UpdateWhere.Items.ID.EQ(*req.Msg.Item.Id), - models.UpdateWhere.Items.UserID.EQ(userid), - ), - ).One(ctx, h.db) + // where + ID: req.Msg.Id, + UserID: userid, + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } - res := connect.NewResponse(&itemv1.UpdateItemResponse{ - Item: itemToConnect(item), - }) + res := connect.NewResponse(&itemv1.UpdateItemResponse{}) return res, nil } @@ -192,12 +178,10 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De } // Delete item - _, err := models.Items.Delete( - sqlite.WhereAnd( - models.DeleteWhere.Items.ID.EQ(req.Msg.Id), - models.DeleteWhere.Items.UserID.EQ(userid), - ), - ).Exec(ctx, h.db) + err := h.db.DeleteItem(ctx, sqlc.DeleteItemParams{ + ID: req.Msg.Id, + UserID: userid, + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -206,7 +190,7 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De return res, nil } -func NewHandler(db *bob.DB, key string) (string, http.Handler) { +func NewHandler(db *sqlc.Queries, key string) (string, http.Handler) { interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key)) return itemv1connect.NewItemServiceHandler( diff --git a/server/internal/handlers/user/v1/auth.go b/server/internal/handlers/user/v1/auth.go index 5d72315..edc7a29 100644 --- a/server/internal/handlers/user/v1/auth.go +++ b/server/internal/handlers/user/v1/auth.go @@ -11,26 +11,22 @@ import ( _ "crypto/sha256" // Crypto "connectrpc.com/connect" - "github.com/aarondl/opt/omit" "github.com/golang-jwt/jwt/v5" + userv1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1" + "github.com/spotdemo4/trevstack/server/internal/connect/user/v1/userv1connect" "github.com/spotdemo4/trevstack/server/internal/interceptors" - "github.com/spotdemo4/trevstack/server/internal/models" - userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" - "github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect" - "github.com/stephenafamo/bob" + "github.com/spotdemo4/trevstack/server/internal/sqlc" "golang.org/x/crypto/bcrypt" ) type AuthHandler struct { - db *bob.DB + db *sqlc.Queries key []byte } func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) { // Get user - user, err := models.Users.Query( - models.SelectWhere.Users.Username.EQ(req.Msg.Username), - ).One(ctx, h.db) + user, err := h.db.GetUserbyUsername(ctx, req.Msg.Username) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodePermissionDenied, err) @@ -47,7 +43,7 @@ func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.Log // Generate JWT t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ Issuer: "trevstack", - Subject: strconv.FormatUint(uint64(user.ID), 10), + Subject: strconv.FormatInt(user.ID, 10), IssuedAt: &jwt.NumericDate{ Time: time.Now(), }, @@ -80,16 +76,13 @@ func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.Log func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.SignUpRequest]) (*connect.Response[userv1.SignUpResponse], error) { // Get user - user, err := models.Users.Query( - models.SelectWhere.Users.Username.EQ(req.Msg.Username), - ).One(ctx, h.db) + _, err := h.db.GetUserbyUsername(ctx, req.Msg.Username) if err != nil { if !errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeInternal, err) } - } - if user != nil { - return nil, connect.NewError(connect.CodeAlreadyExists, errors.New("username already exists")) + } else { + return nil, connect.NewError(connect.CodeAlreadyExists, err) } // Check if confirmation passwords match @@ -104,10 +97,10 @@ func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.Si } // Create user - _, err = models.Users.Insert(&models.UserSetter{ - Username: omit.From(req.Msg.Username), - Password: omit.From(string(hash)), - }).One(ctx, h.db) + _, err = h.db.InsertUser(ctx, sqlc.InsertUserParams{ + Username: req.Msg.Username, + Password: string(hash), + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -221,7 +214,7 @@ func (h *AuthHandler) Logout(_ context.Context, _ *connect.Request[userv1.Logout // return res, nil // } -func NewAuthHandler(db *bob.DB, key string) (string, http.Handler) { +func NewAuthHandler(db *sqlc.Queries, key string) (string, http.Handler) { interceptors := connect.WithInterceptors(interceptors.NewRateLimitInterceptor(key)) return userv1connect.NewAuthServiceHandler( diff --git a/server/internal/handlers/user/v1/user.go b/server/internal/handlers/user/v1/user.go index 91defae..930a47e 100644 --- a/server/internal/handlers/user/v1/user.go +++ b/server/internal/handlers/user/v1/user.go @@ -9,27 +9,25 @@ import ( "time" "connectrpc.com/connect" - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" "github.com/golang-jwt/jwt/v5" + userv1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1" + "github.com/spotdemo4/trevstack/server/internal/connect/user/v1/userv1connect" "github.com/spotdemo4/trevstack/server/internal/interceptors" - "github.com/spotdemo4/trevstack/server/internal/models" - userv1 "github.com/spotdemo4/trevstack/server/internal/services/user/v1" - "github.com/spotdemo4/trevstack/server/internal/services/user/v1/userv1connect" - "github.com/stephenafamo/bob" + "github.com/spotdemo4/trevstack/server/internal/sqlc" + "github.com/spotdemo4/trevstack/server/internal/util" "golang.org/x/crypto/bcrypt" ) -func userToConnect(item *models.User) *userv1.User { +func userToConnect(item sqlc.User) *userv1.User { return &userv1.User{ Id: item.ID, Username: item.Username, - ProfilePictureId: item.ProfilePictureID.Ptr(), + ProfilePictureId: item.ProfilePictureID, } } type Handler struct { - db *bob.DB + db *sqlc.Queries key []byte } @@ -40,9 +38,7 @@ func (h *Handler) GetUser(ctx context.Context, _ *connect.Request[userv1.GetUser } // Get user - user, err := models.Users.Query( - models.SelectWhere.Users.ID.EQ(userid), - ).One(ctx, h.db) + user, err := h.db.GetUser(ctx, userid) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeNotFound, err) @@ -64,9 +60,7 @@ func (h *Handler) UpdatePassword(ctx context.Context, req *connect.Request[userv } // Get user - user, err := models.Users.Query( - models.SelectWhere.Users.ID.EQ(userid), - ).One(ctx, h.db) + user, err := h.db.GetUser(ctx, userid) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeNotFound, err) @@ -90,8 +84,9 @@ func (h *Handler) UpdatePassword(ctx context.Context, req *connect.Request[userv } // Update password - err = user.Update(ctx, h.db, &models.UserSetter{ - Password: omit.From(string(hash)), + err = h.db.UpdateUser(ctx, sqlc.UpdateUserParams{ + Password: util.ToPointer(string(hash)), + ID: userid, }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) @@ -108,9 +103,7 @@ func (h *Handler) GetAPIKey(ctx context.Context, req *connect.Request[userv1.Get } // Get user - user, err := models.Users.Query( - models.SelectWhere.Users.ID.EQ(userid), - ).One(ctx, h.db) + user, err := h.db.GetUser(ctx, userid) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeNotFound, err) @@ -159,19 +152,17 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request } // Save bytes into file - file, err := models.Files.Insert(&models.FileSetter{ - Name: omit.From(req.Msg.FileName), - Data: omit.From(req.Msg.Data), - UserID: omit.From(userid), - }).One(ctx, h.db) + fileID, err := h.db.InsertFile(ctx, sqlc.InsertFileParams{ + Name: req.Msg.FileName, + Data: req.Msg.Data, + UserID: userid, + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } // Get user - user, err := models.Users.Query( - models.SelectWhere.Users.ID.EQ(userid), - ).One(ctx, h.db) + user, err := h.db.GetUser(ctx, userid) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, connect.NewError(connect.CodeNotFound, err) @@ -180,25 +171,24 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request return nil, connect.NewError(connect.CodeInternal, err) } - // Get old profile picture ID - var ppid *int64 - if user.ProfilePictureID.Ptr() != nil { - ppid = user.ProfilePictureID.Ptr() - } - // Update user profile picture - err = user.Update(ctx, h.db, &models.UserSetter{ - ProfilePictureID: omitnull.From(file.ID), + err = h.db.UpdateUser(ctx, sqlc.UpdateUserParams{ + // set + ProfilePictureID: &fileID, + + // where + ID: userid, }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } // Delete old profile picture if exists - if ppid != nil { - _, err = models.Files.Delete( - models.DeleteWhere.Files.ID.EQ(*ppid), - ).Exec(ctx, h.db) + if user.ProfilePictureID != nil { + err = h.db.DeleteFile(ctx, sqlc.DeleteFileParams{ + ID: *user.ProfilePictureID, + UserID: userid, + }) if err != nil { return nil, connect.NewError(connect.CodeInternal, err) } @@ -272,7 +262,7 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request // return nil // } -func NewHandler(db *bob.DB, key string) (string, http.Handler) { +func NewHandler(db *sqlc.Queries, key string) (string, http.Handler) { interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key)) return userv1connect.NewUserServiceHandler( diff --git a/server/internal/models/bob_main.bob.go b/server/internal/models/bob_main.bob.go deleted file mode 100644 index 0fb3827..0000000 --- a/server/internal/models/bob_main.bob.go +++ /dev/null @@ -1,170 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package models - -import ( - "hash/maphash" - "strings" - - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/clause" - "github.com/stephenafamo/bob/dialect/sqlite" - "github.com/stephenafamo/bob/dialect/sqlite/dialect" - sqliteDriver "modernc.org/sqlite" -) - -var TableNames = struct { - Files string - Items string - SchemaMigrations string - Users string -}{ - Files: "file", - Items: "item", - SchemaMigrations: "schema_migrations", - Users: "user", -} - -var ColumnNames = struct { - Files fileColumnNames - Items itemColumnNames - SchemaMigrations schemaMigrationColumnNames - Users userColumnNames -}{ - Files: fileColumnNames{ - ID: "id", - Name: "name", - Data: "data", - UserID: "user_id", - }, - Items: itemColumnNames{ - ID: "id", - Name: "name", - Added: "added", - Description: "description", - Price: "price", - Quantity: "quantity", - UserID: "user_id", - }, - SchemaMigrations: schemaMigrationColumnNames{ - Version: "version", - }, - Users: userColumnNames{ - ID: "id", - Username: "username", - Password: "password", - ProfilePictureID: "profile_picture_id", - }, -} - -var ( - SelectWhere = Where[*dialect.SelectQuery]() - InsertWhere = Where[*dialect.InsertQuery]() - UpdateWhere = Where[*dialect.UpdateQuery]() - DeleteWhere = Where[*dialect.DeleteQuery]() -) - -func Where[Q sqlite.Filterable]() struct { - Files fileWhere[Q] - Items itemWhere[Q] - SchemaMigrations schemaMigrationWhere[Q] - Users userWhere[Q] -} { - return struct { - Files fileWhere[Q] - Items itemWhere[Q] - SchemaMigrations schemaMigrationWhere[Q] - Users userWhere[Q] - }{ - Files: buildFileWhere[Q](FileColumns), - Items: buildItemWhere[Q](ItemColumns), - SchemaMigrations: buildSchemaMigrationWhere[Q](SchemaMigrationColumns), - Users: buildUserWhere[Q](UserColumns), - } -} - -var ( - SelectJoins = getJoins[*dialect.SelectQuery] - UpdateJoins = getJoins[*dialect.UpdateQuery] -) - -type joinSet[Q interface{ aliasedAs(string) Q }] struct { - InnerJoin Q - LeftJoin Q - RightJoin Q -} - -func (j joinSet[Q]) AliasedAs(alias string) joinSet[Q] { - return joinSet[Q]{ - InnerJoin: j.InnerJoin.aliasedAs(alias), - LeftJoin: j.LeftJoin.aliasedAs(alias), - RightJoin: j.RightJoin.aliasedAs(alias), - } -} - -type joins[Q dialect.Joinable] struct { - Files joinSet[fileJoins[Q]] - Items joinSet[itemJoins[Q]] - Users joinSet[userJoins[Q]] -} - -func buildJoinSet[Q interface{ aliasedAs(string) Q }, C any, F func(C, string) Q](c C, f F) joinSet[Q] { - return joinSet[Q]{ - InnerJoin: f(c, clause.InnerJoin), - LeftJoin: f(c, clause.LeftJoin), - RightJoin: f(c, clause.RightJoin), - } -} - -func getJoins[Q dialect.Joinable]() joins[Q] { - return joins[Q]{ - Files: buildJoinSet[fileJoins[Q]](FileColumns, buildFileJoins), - Items: buildJoinSet[itemJoins[Q]](ItemColumns, buildItemJoins), - Users: buildJoinSet[userJoins[Q]](UserColumns, buildUserJoins), - } -} - -type modAs[Q any, C interface{ AliasedAs(string) C }] struct { - c C - f func(C) bob.Mod[Q] -} - -func (m modAs[Q, C]) Apply(q Q) { - m.f(m.c).Apply(q) -} - -func (m modAs[Q, C]) AliasedAs(alias string) bob.Mod[Q] { - m.c = m.c.AliasedAs(alias) - return m -} - -func randInt() int64 { - out := int64(new(maphash.Hash).Sum64()) - - if out < 0 { - return -out % 10000 - } - - return out % 10000 -} - -// ErrUniqueConstraint captures all unique constraint errors by explicitly leaving `s` empty. -var ErrUniqueConstraint = &UniqueConstraintError{s: ""} - -type UniqueConstraintError struct { - // s is a string uniquely identifying the constraint in the raw error message returned from the database. - s string -} - -func (e *UniqueConstraintError) Error() string { - return e.s -} - -func (e *UniqueConstraintError) Is(target error) bool { - err, ok := target.(*sqliteDriver.Error) - if !ok { - return false - } - return err.Code() == 2067 && strings.Contains(err.Error(), e.s) -} diff --git a/server/internal/models/bob_main_test.bob.go b/server/internal/models/bob_main_test.bob.go deleted file mode 100644 index 5de8927..0000000 --- a/server/internal/models/bob_main_test.bob.go +++ /dev/null @@ -1,18 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package models - -import "github.com/stephenafamo/bob" - -// Make sure the type File runs hooks after queries -var _ bob.HookableType = &File{} - -// Make sure the type Item runs hooks after queries -var _ bob.HookableType = &Item{} - -// Make sure the type SchemaMigration runs hooks after queries -var _ bob.HookableType = &SchemaMigration{} - -// Make sure the type User runs hooks after queries -var _ bob.HookableType = &User{} diff --git a/server/internal/models/factory/bobfactory_context.bob.go b/server/internal/models/factory/bobfactory_context.bob.go deleted file mode 100644 index 8dd23dd..0000000 --- a/server/internal/models/factory/bobfactory_context.bob.go +++ /dev/null @@ -1,37 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "context" - - models "github.com/spotdemo4/trevstack/server/internal/models" -) - -type contextKey string - -var ( - fileCtx = newContextual[*models.File]("file") - itemCtx = newContextual[*models.Item]("item") - schemaMigrationCtx = newContextual[*models.SchemaMigration]("schemaMigration") - userCtx = newContextual[*models.User]("user") -) - -// Contextual is a convienience wrapper around context.WithValue and context.Value -type contextual[V any] struct { - key contextKey -} - -func newContextual[V any](key string) contextual[V] { - return contextual[V]{key: contextKey(key)} -} - -func (k contextual[V]) WithValue(ctx context.Context, val V) context.Context { - return context.WithValue(ctx, k.key, val) -} - -func (k contextual[V]) Value(ctx context.Context) (V, bool) { - v, ok := ctx.Value(k.key).(V) - return v, ok -} diff --git a/server/internal/models/factory/bobfactory_main.bob.go b/server/internal/models/factory/bobfactory_main.bob.go deleted file mode 100644 index e10ccad..0000000 --- a/server/internal/models/factory/bobfactory_main.bob.go +++ /dev/null @@ -1,95 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -type Factory struct { - baseFileMods FileModSlice - baseItemMods ItemModSlice - baseSchemaMigrationMods SchemaMigrationModSlice - baseUserMods UserModSlice -} - -func New() *Factory { - return &Factory{} -} - -func (f *Factory) NewFile(mods ...FileMod) *FileTemplate { - o := &FileTemplate{f: f} - - if f != nil { - f.baseFileMods.Apply(o) - } - - FileModSlice(mods).Apply(o) - - return o -} - -func (f *Factory) NewItem(mods ...ItemMod) *ItemTemplate { - o := &ItemTemplate{f: f} - - if f != nil { - f.baseItemMods.Apply(o) - } - - ItemModSlice(mods).Apply(o) - - return o -} - -func (f *Factory) NewSchemaMigration(mods ...SchemaMigrationMod) *SchemaMigrationTemplate { - o := &SchemaMigrationTemplate{f: f} - - if f != nil { - f.baseSchemaMigrationMods.Apply(o) - } - - SchemaMigrationModSlice(mods).Apply(o) - - return o -} - -func (f *Factory) NewUser(mods ...UserMod) *UserTemplate { - o := &UserTemplate{f: f} - - if f != nil { - f.baseUserMods.Apply(o) - } - - UserModSlice(mods).Apply(o) - - return o -} - -func (f *Factory) ClearBaseFileMods() { - f.baseFileMods = nil -} - -func (f *Factory) AddBaseFileMod(mods ...FileMod) { - f.baseFileMods = append(f.baseFileMods, mods...) -} - -func (f *Factory) ClearBaseItemMods() { - f.baseItemMods = nil -} - -func (f *Factory) AddBaseItemMod(mods ...ItemMod) { - f.baseItemMods = append(f.baseItemMods, mods...) -} - -func (f *Factory) ClearBaseSchemaMigrationMods() { - f.baseSchemaMigrationMods = nil -} - -func (f *Factory) AddBaseSchemaMigrationMod(mods ...SchemaMigrationMod) { - f.baseSchemaMigrationMods = append(f.baseSchemaMigrationMods, mods...) -} - -func (f *Factory) ClearBaseUserMods() { - f.baseUserMods = nil -} - -func (f *Factory) AddBaseUserMod(mods ...UserMod) { - f.baseUserMods = append(f.baseUserMods, mods...) -} diff --git a/server/internal/models/factory/bobfactory_random.bob.go b/server/internal/models/factory/bobfactory_random.bob.go deleted file mode 100644 index 5b34317..0000000 --- a/server/internal/models/factory/bobfactory_random.bob.go +++ /dev/null @@ -1,56 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "strings" - "time" - - "github.com/jaswdr/faker/v2" -) - -var defaultFaker = faker.New() - -func random___byte(f *faker.Faker) []byte { - if f == nil { - f = &defaultFaker - } - - return []byte(random_string(f)) -} - -func random_float32(f *faker.Faker) float32 { - if f == nil { - f = &defaultFaker - } - - return f.Float32(10, -1_000_000, 1_000_000) -} - -func random_int64(f *faker.Faker) int64 { - if f == nil { - f = &defaultFaker - } - - return f.Int64() -} - -func random_string(f *faker.Faker) string { - if f == nil { - f = &defaultFaker - } - - return strings.Join(f.Lorem().Words(f.IntBetween(1, 5)), " ") -} - -func random_time_Time(f *faker.Faker) time.Time { - if f == nil { - f = &defaultFaker - } - - year := time.Hour * 24 * 365 - min := time.Now().Add(-year) - max := time.Now().Add(year) - return f.Time().TimeBetween(min, max) -} diff --git a/server/internal/models/factory/bobfactory_random_test.bob.go b/server/internal/models/factory/bobfactory_random_test.bob.go deleted file mode 100644 index d50429f..0000000 --- a/server/internal/models/factory/bobfactory_random_test.bob.go +++ /dev/null @@ -1,64 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "bytes" - "testing" -) - -func TestRandom_int64(t *testing.T) { - t.Parallel() - - val1 := random_int64(nil) - val2 := random_int64(nil) - - if val1 == val2 { - t.Fatalf("random_int64() returned the same value twice: %v", val1) - } -} - -func TestRandom_string(t *testing.T) { - t.Parallel() - - val1 := random_string(nil) - val2 := random_string(nil) - - if val1 == val2 { - t.Fatalf("random_string() returned the same value twice: %v", val1) - } -} - -func TestRandom___byte(t *testing.T) { - t.Parallel() - - val1 := random___byte(nil) - val2 := random___byte(nil) - - if bytes.Equal(val1, val2) { - t.Fatalf("random___byte() returned the same value twice: %v", val1) - } -} - -func TestRandom_time_Time(t *testing.T) { - t.Parallel() - - val1 := random_time_Time(nil) - val2 := random_time_Time(nil) - - if val1.Equal(val2) { - t.Fatalf("random_time_Time() returned the same value twice: %v", val1) - } -} - -func TestRandom_float32(t *testing.T) { - t.Parallel() - - val1 := random_float32(nil) - val2 := random_float32(nil) - - if val1 == val2 { - t.Fatalf("random_float32() returned the same value twice: %v", val1) - } -} diff --git a/server/internal/models/factory/file.bob.go b/server/internal/models/factory/file.bob.go deleted file mode 100644 index 33b284f..0000000 --- a/server/internal/models/factory/file.bob.go +++ /dev/null @@ -1,527 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "context" - "testing" - - "github.com/aarondl/opt/null" - "github.com/aarondl/opt/omit" - "github.com/jaswdr/faker/v2" - models "github.com/spotdemo4/trevstack/server/internal/models" - "github.com/stephenafamo/bob" -) - -type FileMod interface { - Apply(*FileTemplate) -} - -type FileModFunc func(*FileTemplate) - -func (f FileModFunc) Apply(n *FileTemplate) { - f(n) -} - -type FileModSlice []FileMod - -func (mods FileModSlice) Apply(n *FileTemplate) { - for _, f := range mods { - f.Apply(n) - } -} - -// FileTemplate is an object representing the database table. -// all columns are optional and should be set by mods -type FileTemplate struct { - ID func() int64 - Name func() string - Data func() []byte - UserID func() int64 - - r fileR - f *Factory -} - -type fileR struct { - User *fileRUserR - ProfilePictureUsers []*fileRProfilePictureUsersR -} - -type fileRUserR struct { - o *UserTemplate -} -type fileRProfilePictureUsersR struct { - number int - o *UserTemplate -} - -// Apply mods to the FileTemplate -func (o *FileTemplate) Apply(mods ...FileMod) { - for _, mod := range mods { - mod.Apply(o) - } -} - -// toModel returns an *models.File -// this does nothing with the relationship templates -func (o FileTemplate) toModel() *models.File { - m := &models.File{} - - if o.ID != nil { - m.ID = o.ID() - } - if o.Name != nil { - m.Name = o.Name() - } - if o.Data != nil { - m.Data = o.Data() - } - if o.UserID != nil { - m.UserID = o.UserID() - } - - return m -} - -// toModels returns an models.FileSlice -// this does nothing with the relationship templates -func (o FileTemplate) toModels(number int) models.FileSlice { - m := make(models.FileSlice, number) - - for i := range m { - m[i] = o.toModel() - } - - return m -} - -// setModelRels creates and sets the relationships on *models.File -// according to the relationships in the template. Nothing is inserted into the db -func (t FileTemplate) setModelRels(o *models.File) { - if t.r.User != nil { - rel := t.r.User.o.toModel() - rel.R.Files = append(rel.R.Files, o) - o.UserID = rel.ID - o.R.User = rel - } - - if t.r.ProfilePictureUsers != nil { - rel := models.UserSlice{} - for _, r := range t.r.ProfilePictureUsers { - related := r.o.toModels(r.number) - for _, rel := range related { - rel.ProfilePictureID = null.From(o.ID) - rel.R.ProfilePictureFile = o - } - rel = append(rel, related...) - } - o.R.ProfilePictureUsers = rel - } -} - -// BuildSetter returns an *models.FileSetter -// this does nothing with the relationship templates -func (o FileTemplate) BuildSetter() *models.FileSetter { - m := &models.FileSetter{} - - if o.ID != nil { - m.ID = omit.From(o.ID()) - } - if o.Name != nil { - m.Name = omit.From(o.Name()) - } - if o.Data != nil { - m.Data = omit.From(o.Data()) - } - if o.UserID != nil { - m.UserID = omit.From(o.UserID()) - } - - return m -} - -// BuildManySetter returns an []*models.FileSetter -// this does nothing with the relationship templates -func (o FileTemplate) BuildManySetter(number int) []*models.FileSetter { - m := make([]*models.FileSetter, number) - - for i := range m { - m[i] = o.BuildSetter() - } - - return m -} - -// Build returns an *models.File -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use FileTemplate.Create -func (o FileTemplate) Build() *models.File { - m := o.toModel() - o.setModelRels(m) - - return m -} - -// BuildMany returns an models.FileSlice -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use FileTemplate.CreateMany -func (o FileTemplate) BuildMany(number int) models.FileSlice { - m := make(models.FileSlice, number) - - for i := range m { - m[i] = o.Build() - } - - return m -} - -func ensureCreatableFile(m *models.FileSetter) { - if m.Name.IsUnset() { - m.Name = omit.From(random_string(nil)) - } - if m.Data.IsUnset() { - m.Data = omit.From(random___byte(nil)) - } - if m.UserID.IsUnset() { - m.UserID = omit.From(random_int64(nil)) - } -} - -// insertOptRels creates and inserts any optional the relationships on *models.File -// according to the relationships in the template. -// any required relationship should have already exist on the model -func (o *FileTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.File) (context.Context, error) { - var err error - - if o.r.ProfilePictureUsers != nil { - for _, r := range o.r.ProfilePictureUsers { - var rel1 models.UserSlice - ctx, rel1, err = r.o.createMany(ctx, exec, r.number) - if err != nil { - return ctx, err - } - - err = m.AttachProfilePictureUsers(ctx, exec, rel1...) - if err != nil { - return ctx, err - } - } - } - - return ctx, err -} - -// Create builds a file and inserts it into the database -// Relations objects are also inserted and placed in the .R field -func (o *FileTemplate) Create(ctx context.Context, exec bob.Executor) (*models.File, error) { - _, m, err := o.create(ctx, exec) - return m, err -} - -// MustCreate builds a file and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o *FileTemplate) MustCreate(ctx context.Context, exec bob.Executor) *models.File { - _, m, err := o.create(ctx, exec) - if err != nil { - panic(err) - } - return m -} - -// CreateOrFail builds a file and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o *FileTemplate) CreateOrFail(ctx context.Context, tb testing.TB, exec bob.Executor) *models.File { - tb.Helper() - _, m, err := o.create(ctx, exec) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// create builds a file and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted model -func (o *FileTemplate) create(ctx context.Context, exec bob.Executor) (context.Context, *models.File, error) { - var err error - opt := o.BuildSetter() - ensureCreatableFile(opt) - - var rel0 *models.User - if o.r.User == nil { - var ok bool - rel0, ok = userCtx.Value(ctx) - if !ok { - FileMods.WithNewUser().Apply(o) - } - } - if o.r.User != nil { - ctx, rel0, err = o.r.User.o.create(ctx, exec) - if err != nil { - return ctx, nil, err - } - } - opt.UserID = omit.From(rel0.ID) - - m, err := models.Files.Insert(opt).One(ctx, exec) - if err != nil { - return ctx, nil, err - } - ctx = fileCtx.WithValue(ctx, m) - - m.R.User = rel0 - - ctx, err = o.insertOptRels(ctx, exec, m) - return ctx, m, err -} - -// CreateMany builds multiple files and inserts them into the database -// Relations objects are also inserted and placed in the .R field -func (o FileTemplate) CreateMany(ctx context.Context, exec bob.Executor, number int) (models.FileSlice, error) { - _, m, err := o.createMany(ctx, exec, number) - return m, err -} - -// MustCreateMany builds multiple files and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o FileTemplate) MustCreateMany(ctx context.Context, exec bob.Executor, number int) models.FileSlice { - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - panic(err) - } - return m -} - -// CreateManyOrFail builds multiple files and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o FileTemplate) CreateManyOrFail(ctx context.Context, tb testing.TB, exec bob.Executor, number int) models.FileSlice { - tb.Helper() - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// createMany builds multiple files and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted models -func (o FileTemplate) createMany(ctx context.Context, exec bob.Executor, number int) (context.Context, models.FileSlice, error) { - var err error - m := make(models.FileSlice, number) - - for i := range m { - ctx, m[i], err = o.create(ctx, exec) - if err != nil { - return ctx, nil, err - } - } - - return ctx, m, nil -} - -// File has methods that act as mods for the FileTemplate -var FileMods fileMods - -type fileMods struct{} - -func (m fileMods) RandomizeAllColumns(f *faker.Faker) FileMod { - return FileModSlice{ - FileMods.RandomID(f), - FileMods.RandomName(f), - FileMods.RandomData(f), - FileMods.RandomUserID(f), - } -} - -// Set the model columns to this value -func (m fileMods) ID(val int64) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.ID = func() int64 { return val } - }) -} - -// Set the Column from the function -func (m fileMods) IDFunc(f func() int64) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.ID = f - }) -} - -// Clear any values for the column -func (m fileMods) UnsetID() FileMod { - return FileModFunc(func(o *FileTemplate) { - o.ID = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m fileMods) RandomID(f *faker.Faker) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.ID = func() int64 { - return random_int64(f) - } - }) -} - -// Set the model columns to this value -func (m fileMods) Name(val string) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Name = func() string { return val } - }) -} - -// Set the Column from the function -func (m fileMods) NameFunc(f func() string) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Name = f - }) -} - -// Clear any values for the column -func (m fileMods) UnsetName() FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Name = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m fileMods) RandomName(f *faker.Faker) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Name = func() string { - return random_string(f) - } - }) -} - -// Set the model columns to this value -func (m fileMods) Data(val []byte) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Data = func() []byte { return val } - }) -} - -// Set the Column from the function -func (m fileMods) DataFunc(f func() []byte) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Data = f - }) -} - -// Clear any values for the column -func (m fileMods) UnsetData() FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Data = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m fileMods) RandomData(f *faker.Faker) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.Data = func() []byte { - return random___byte(f) - } - }) -} - -// Set the model columns to this value -func (m fileMods) UserID(val int64) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.UserID = func() int64 { return val } - }) -} - -// Set the Column from the function -func (m fileMods) UserIDFunc(f func() int64) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.UserID = f - }) -} - -// Clear any values for the column -func (m fileMods) UnsetUserID() FileMod { - return FileModFunc(func(o *FileTemplate) { - o.UserID = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m fileMods) RandomUserID(f *faker.Faker) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.UserID = func() int64 { - return random_int64(f) - } - }) -} - -func (m fileMods) WithUser(rel *UserTemplate) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.r.User = &fileRUserR{ - o: rel, - } - }) -} - -func (m fileMods) WithNewUser(mods ...UserMod) FileMod { - return FileModFunc(func(o *FileTemplate) { - related := o.f.NewUser(mods...) - - m.WithUser(related).Apply(o) - }) -} - -func (m fileMods) WithoutUser() FileMod { - return FileModFunc(func(o *FileTemplate) { - o.r.User = nil - }) -} - -func (m fileMods) WithProfilePictureUsers(number int, related *UserTemplate) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.r.ProfilePictureUsers = []*fileRProfilePictureUsersR{{ - number: number, - o: related, - }} - }) -} - -func (m fileMods) WithNewProfilePictureUsers(number int, mods ...UserMod) FileMod { - return FileModFunc(func(o *FileTemplate) { - related := o.f.NewUser(mods...) - m.WithProfilePictureUsers(number, related).Apply(o) - }) -} - -func (m fileMods) AddProfilePictureUsers(number int, related *UserTemplate) FileMod { - return FileModFunc(func(o *FileTemplate) { - o.r.ProfilePictureUsers = append(o.r.ProfilePictureUsers, &fileRProfilePictureUsersR{ - number: number, - o: related, - }) - }) -} - -func (m fileMods) AddNewProfilePictureUsers(number int, mods ...UserMod) FileMod { - return FileModFunc(func(o *FileTemplate) { - related := o.f.NewUser(mods...) - m.AddProfilePictureUsers(number, related).Apply(o) - }) -} - -func (m fileMods) WithoutProfilePictureUsers() FileMod { - return FileModFunc(func(o *FileTemplate) { - o.r.ProfilePictureUsers = nil - }) -} diff --git a/server/internal/models/factory/item.bob.go b/server/internal/models/factory/item.bob.go deleted file mode 100644 index 9df96be..0000000 --- a/server/internal/models/factory/item.bob.go +++ /dev/null @@ -1,582 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "context" - "testing" - "time" - - "github.com/aarondl/opt/omit" - "github.com/jaswdr/faker/v2" - models "github.com/spotdemo4/trevstack/server/internal/models" - "github.com/stephenafamo/bob" -) - -type ItemMod interface { - Apply(*ItemTemplate) -} - -type ItemModFunc func(*ItemTemplate) - -func (f ItemModFunc) Apply(n *ItemTemplate) { - f(n) -} - -type ItemModSlice []ItemMod - -func (mods ItemModSlice) Apply(n *ItemTemplate) { - for _, f := range mods { - f.Apply(n) - } -} - -// ItemTemplate is an object representing the database table. -// all columns are optional and should be set by mods -type ItemTemplate struct { - ID func() int64 - Name func() string - Added func() time.Time - Description func() string - Price func() float32 - Quantity func() int64 - UserID func() int64 - - r itemR - f *Factory -} - -type itemR struct { - User *itemRUserR -} - -type itemRUserR struct { - o *UserTemplate -} - -// Apply mods to the ItemTemplate -func (o *ItemTemplate) Apply(mods ...ItemMod) { - for _, mod := range mods { - mod.Apply(o) - } -} - -// toModel returns an *models.Item -// this does nothing with the relationship templates -func (o ItemTemplate) toModel() *models.Item { - m := &models.Item{} - - if o.ID != nil { - m.ID = o.ID() - } - if o.Name != nil { - m.Name = o.Name() - } - if o.Added != nil { - m.Added = o.Added() - } - if o.Description != nil { - m.Description = o.Description() - } - if o.Price != nil { - m.Price = o.Price() - } - if o.Quantity != nil { - m.Quantity = o.Quantity() - } - if o.UserID != nil { - m.UserID = o.UserID() - } - - return m -} - -// toModels returns an models.ItemSlice -// this does nothing with the relationship templates -func (o ItemTemplate) toModels(number int) models.ItemSlice { - m := make(models.ItemSlice, number) - - for i := range m { - m[i] = o.toModel() - } - - return m -} - -// setModelRels creates and sets the relationships on *models.Item -// according to the relationships in the template. Nothing is inserted into the db -func (t ItemTemplate) setModelRels(o *models.Item) { - if t.r.User != nil { - rel := t.r.User.o.toModel() - rel.R.Items = append(rel.R.Items, o) - o.UserID = rel.ID - o.R.User = rel - } -} - -// BuildSetter returns an *models.ItemSetter -// this does nothing with the relationship templates -func (o ItemTemplate) BuildSetter() *models.ItemSetter { - m := &models.ItemSetter{} - - if o.ID != nil { - m.ID = omit.From(o.ID()) - } - if o.Name != nil { - m.Name = omit.From(o.Name()) - } - if o.Added != nil { - m.Added = omit.From(o.Added()) - } - if o.Description != nil { - m.Description = omit.From(o.Description()) - } - if o.Price != nil { - m.Price = omit.From(o.Price()) - } - if o.Quantity != nil { - m.Quantity = omit.From(o.Quantity()) - } - if o.UserID != nil { - m.UserID = omit.From(o.UserID()) - } - - return m -} - -// BuildManySetter returns an []*models.ItemSetter -// this does nothing with the relationship templates -func (o ItemTemplate) BuildManySetter(number int) []*models.ItemSetter { - m := make([]*models.ItemSetter, number) - - for i := range m { - m[i] = o.BuildSetter() - } - - return m -} - -// Build returns an *models.Item -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use ItemTemplate.Create -func (o ItemTemplate) Build() *models.Item { - m := o.toModel() - o.setModelRels(m) - - return m -} - -// BuildMany returns an models.ItemSlice -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use ItemTemplate.CreateMany -func (o ItemTemplate) BuildMany(number int) models.ItemSlice { - m := make(models.ItemSlice, number) - - for i := range m { - m[i] = o.Build() - } - - return m -} - -func ensureCreatableItem(m *models.ItemSetter) { - if m.Name.IsUnset() { - m.Name = omit.From(random_string(nil)) - } - if m.Added.IsUnset() { - m.Added = omit.From(random_time_Time(nil)) - } - if m.Description.IsUnset() { - m.Description = omit.From(random_string(nil)) - } - if m.Price.IsUnset() { - m.Price = omit.From(random_float32(nil)) - } - if m.Quantity.IsUnset() { - m.Quantity = omit.From(random_int64(nil)) - } - if m.UserID.IsUnset() { - m.UserID = omit.From(random_int64(nil)) - } -} - -// insertOptRels creates and inserts any optional the relationships on *models.Item -// according to the relationships in the template. -// any required relationship should have already exist on the model -func (o *ItemTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.Item) (context.Context, error) { - var err error - - return ctx, err -} - -// Create builds a item and inserts it into the database -// Relations objects are also inserted and placed in the .R field -func (o *ItemTemplate) Create(ctx context.Context, exec bob.Executor) (*models.Item, error) { - _, m, err := o.create(ctx, exec) - return m, err -} - -// MustCreate builds a item and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o *ItemTemplate) MustCreate(ctx context.Context, exec bob.Executor) *models.Item { - _, m, err := o.create(ctx, exec) - if err != nil { - panic(err) - } - return m -} - -// CreateOrFail builds a item and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o *ItemTemplate) CreateOrFail(ctx context.Context, tb testing.TB, exec bob.Executor) *models.Item { - tb.Helper() - _, m, err := o.create(ctx, exec) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// create builds a item and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted model -func (o *ItemTemplate) create(ctx context.Context, exec bob.Executor) (context.Context, *models.Item, error) { - var err error - opt := o.BuildSetter() - ensureCreatableItem(opt) - - var rel0 *models.User - if o.r.User == nil { - var ok bool - rel0, ok = userCtx.Value(ctx) - if !ok { - ItemMods.WithNewUser().Apply(o) - } - } - if o.r.User != nil { - ctx, rel0, err = o.r.User.o.create(ctx, exec) - if err != nil { - return ctx, nil, err - } - } - opt.UserID = omit.From(rel0.ID) - - m, err := models.Items.Insert(opt).One(ctx, exec) - if err != nil { - return ctx, nil, err - } - ctx = itemCtx.WithValue(ctx, m) - - m.R.User = rel0 - - ctx, err = o.insertOptRels(ctx, exec, m) - return ctx, m, err -} - -// CreateMany builds multiple items and inserts them into the database -// Relations objects are also inserted and placed in the .R field -func (o ItemTemplate) CreateMany(ctx context.Context, exec bob.Executor, number int) (models.ItemSlice, error) { - _, m, err := o.createMany(ctx, exec, number) - return m, err -} - -// MustCreateMany builds multiple items and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o ItemTemplate) MustCreateMany(ctx context.Context, exec bob.Executor, number int) models.ItemSlice { - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - panic(err) - } - return m -} - -// CreateManyOrFail builds multiple items and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o ItemTemplate) CreateManyOrFail(ctx context.Context, tb testing.TB, exec bob.Executor, number int) models.ItemSlice { - tb.Helper() - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// createMany builds multiple items and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted models -func (o ItemTemplate) createMany(ctx context.Context, exec bob.Executor, number int) (context.Context, models.ItemSlice, error) { - var err error - m := make(models.ItemSlice, number) - - for i := range m { - ctx, m[i], err = o.create(ctx, exec) - if err != nil { - return ctx, nil, err - } - } - - return ctx, m, nil -} - -// Item has methods that act as mods for the ItemTemplate -var ItemMods itemMods - -type itemMods struct{} - -func (m itemMods) RandomizeAllColumns(f *faker.Faker) ItemMod { - return ItemModSlice{ - ItemMods.RandomID(f), - ItemMods.RandomName(f), - ItemMods.RandomAdded(f), - ItemMods.RandomDescription(f), - ItemMods.RandomPrice(f), - ItemMods.RandomQuantity(f), - ItemMods.RandomUserID(f), - } -} - -// Set the model columns to this value -func (m itemMods) ID(val int64) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.ID = func() int64 { return val } - }) -} - -// Set the Column from the function -func (m itemMods) IDFunc(f func() int64) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.ID = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetID() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.ID = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomID(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.ID = func() int64 { - return random_int64(f) - } - }) -} - -// Set the model columns to this value -func (m itemMods) Name(val string) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Name = func() string { return val } - }) -} - -// Set the Column from the function -func (m itemMods) NameFunc(f func() string) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Name = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetName() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Name = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomName(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Name = func() string { - return random_string(f) - } - }) -} - -// Set the model columns to this value -func (m itemMods) Added(val time.Time) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = func() time.Time { return val } - }) -} - -// Set the Column from the function -func (m itemMods) AddedFunc(f func() time.Time) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetAdded() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomAdded(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Added = func() time.Time { - return random_time_Time(f) - } - }) -} - -// Set the model columns to this value -func (m itemMods) Description(val string) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Description = func() string { return val } - }) -} - -// Set the Column from the function -func (m itemMods) DescriptionFunc(f func() string) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Description = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetDescription() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Description = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomDescription(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Description = func() string { - return random_string(f) - } - }) -} - -// Set the model columns to this value -func (m itemMods) Price(val float32) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Price = func() float32 { return val } - }) -} - -// Set the Column from the function -func (m itemMods) PriceFunc(f func() float32) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Price = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetPrice() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Price = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomPrice(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Price = func() float32 { - return random_float32(f) - } - }) -} - -// Set the model columns to this value -func (m itemMods) Quantity(val int64) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Quantity = func() int64 { return val } - }) -} - -// Set the Column from the function -func (m itemMods) QuantityFunc(f func() int64) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Quantity = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetQuantity() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Quantity = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomQuantity(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.Quantity = func() int64 { - return random_int64(f) - } - }) -} - -// Set the model columns to this value -func (m itemMods) UserID(val int64) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.UserID = func() int64 { return val } - }) -} - -// Set the Column from the function -func (m itemMods) UserIDFunc(f func() int64) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.UserID = f - }) -} - -// Clear any values for the column -func (m itemMods) UnsetUserID() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.UserID = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m itemMods) RandomUserID(f *faker.Faker) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.UserID = func() int64 { - return random_int64(f) - } - }) -} - -func (m itemMods) WithUser(rel *UserTemplate) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.r.User = &itemRUserR{ - o: rel, - } - }) -} - -func (m itemMods) WithNewUser(mods ...UserMod) ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - related := o.f.NewUser(mods...) - - m.WithUser(related).Apply(o) - }) -} - -func (m itemMods) WithoutUser() ItemMod { - return ItemModFunc(func(o *ItemTemplate) { - o.r.User = nil - }) -} diff --git a/server/internal/models/factory/schema_migrations.bob.go b/server/internal/models/factory/schema_migrations.bob.go deleted file mode 100644 index 7f4c735..0000000 --- a/server/internal/models/factory/schema_migrations.bob.go +++ /dev/null @@ -1,276 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "context" - "testing" - - "github.com/aarondl/opt/omit" - "github.com/jaswdr/faker/v2" - models "github.com/spotdemo4/trevstack/server/internal/models" - "github.com/stephenafamo/bob" -) - -type SchemaMigrationMod interface { - Apply(*SchemaMigrationTemplate) -} - -type SchemaMigrationModFunc func(*SchemaMigrationTemplate) - -func (f SchemaMigrationModFunc) Apply(n *SchemaMigrationTemplate) { - f(n) -} - -type SchemaMigrationModSlice []SchemaMigrationMod - -func (mods SchemaMigrationModSlice) Apply(n *SchemaMigrationTemplate) { - for _, f := range mods { - f.Apply(n) - } -} - -// SchemaMigrationTemplate is an object representing the database table. -// all columns are optional and should be set by mods -type SchemaMigrationTemplate struct { - Version func() string - - f *Factory -} - -// Apply mods to the SchemaMigrationTemplate -func (o *SchemaMigrationTemplate) Apply(mods ...SchemaMigrationMod) { - for _, mod := range mods { - mod.Apply(o) - } -} - -// toModel returns an *models.SchemaMigration -// this does nothing with the relationship templates -func (o SchemaMigrationTemplate) toModel() *models.SchemaMigration { - m := &models.SchemaMigration{} - - if o.Version != nil { - m.Version = o.Version() - } - - return m -} - -// toModels returns an models.SchemaMigrationSlice -// this does nothing with the relationship templates -func (o SchemaMigrationTemplate) toModels(number int) models.SchemaMigrationSlice { - m := make(models.SchemaMigrationSlice, number) - - for i := range m { - m[i] = o.toModel() - } - - return m -} - -// setModelRels creates and sets the relationships on *models.SchemaMigration -// according to the relationships in the template. Nothing is inserted into the db -func (t SchemaMigrationTemplate) setModelRels(o *models.SchemaMigration) {} - -// BuildSetter returns an *models.SchemaMigrationSetter -// this does nothing with the relationship templates -func (o SchemaMigrationTemplate) BuildSetter() *models.SchemaMigrationSetter { - m := &models.SchemaMigrationSetter{} - - if o.Version != nil { - m.Version = omit.From(o.Version()) - } - - return m -} - -// BuildManySetter returns an []*models.SchemaMigrationSetter -// this does nothing with the relationship templates -func (o SchemaMigrationTemplate) BuildManySetter(number int) []*models.SchemaMigrationSetter { - m := make([]*models.SchemaMigrationSetter, number) - - for i := range m { - m[i] = o.BuildSetter() - } - - return m -} - -// Build returns an *models.SchemaMigration -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use SchemaMigrationTemplate.Create -func (o SchemaMigrationTemplate) Build() *models.SchemaMigration { - m := o.toModel() - o.setModelRels(m) - - return m -} - -// BuildMany returns an models.SchemaMigrationSlice -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use SchemaMigrationTemplate.CreateMany -func (o SchemaMigrationTemplate) BuildMany(number int) models.SchemaMigrationSlice { - m := make(models.SchemaMigrationSlice, number) - - for i := range m { - m[i] = o.Build() - } - - return m -} - -func ensureCreatableSchemaMigration(m *models.SchemaMigrationSetter) { - if m.Version.IsUnset() { - m.Version = omit.From(random_string(nil)) - } -} - -// insertOptRels creates and inserts any optional the relationships on *models.SchemaMigration -// according to the relationships in the template. -// any required relationship should have already exist on the model -func (o *SchemaMigrationTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.SchemaMigration) (context.Context, error) { - var err error - - return ctx, err -} - -// Create builds a schemaMigration and inserts it into the database -// Relations objects are also inserted and placed in the .R field -func (o *SchemaMigrationTemplate) Create(ctx context.Context, exec bob.Executor) (*models.SchemaMigration, error) { - _, m, err := o.create(ctx, exec) - return m, err -} - -// MustCreate builds a schemaMigration and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o *SchemaMigrationTemplate) MustCreate(ctx context.Context, exec bob.Executor) *models.SchemaMigration { - _, m, err := o.create(ctx, exec) - if err != nil { - panic(err) - } - return m -} - -// CreateOrFail builds a schemaMigration and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o *SchemaMigrationTemplate) CreateOrFail(ctx context.Context, tb testing.TB, exec bob.Executor) *models.SchemaMigration { - tb.Helper() - _, m, err := o.create(ctx, exec) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// create builds a schemaMigration and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted model -func (o *SchemaMigrationTemplate) create(ctx context.Context, exec bob.Executor) (context.Context, *models.SchemaMigration, error) { - var err error - opt := o.BuildSetter() - ensureCreatableSchemaMigration(opt) - - m, err := models.SchemaMigrations.Insert(opt).One(ctx, exec) - if err != nil { - return ctx, nil, err - } - ctx = schemaMigrationCtx.WithValue(ctx, m) - - ctx, err = o.insertOptRels(ctx, exec, m) - return ctx, m, err -} - -// CreateMany builds multiple schemaMigrations and inserts them into the database -// Relations objects are also inserted and placed in the .R field -func (o SchemaMigrationTemplate) CreateMany(ctx context.Context, exec bob.Executor, number int) (models.SchemaMigrationSlice, error) { - _, m, err := o.createMany(ctx, exec, number) - return m, err -} - -// MustCreateMany builds multiple schemaMigrations and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o SchemaMigrationTemplate) MustCreateMany(ctx context.Context, exec bob.Executor, number int) models.SchemaMigrationSlice { - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - panic(err) - } - return m -} - -// CreateManyOrFail builds multiple schemaMigrations and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o SchemaMigrationTemplate) CreateManyOrFail(ctx context.Context, tb testing.TB, exec bob.Executor, number int) models.SchemaMigrationSlice { - tb.Helper() - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// createMany builds multiple schemaMigrations and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted models -func (o SchemaMigrationTemplate) createMany(ctx context.Context, exec bob.Executor, number int) (context.Context, models.SchemaMigrationSlice, error) { - var err error - m := make(models.SchemaMigrationSlice, number) - - for i := range m { - ctx, m[i], err = o.create(ctx, exec) - if err != nil { - return ctx, nil, err - } - } - - return ctx, m, nil -} - -// SchemaMigration has methods that act as mods for the SchemaMigrationTemplate -var SchemaMigrationMods schemaMigrationMods - -type schemaMigrationMods struct{} - -func (m schemaMigrationMods) RandomizeAllColumns(f *faker.Faker) SchemaMigrationMod { - return SchemaMigrationModSlice{ - SchemaMigrationMods.RandomVersion(f), - } -} - -// Set the model columns to this value -func (m schemaMigrationMods) Version(val string) SchemaMigrationMod { - return SchemaMigrationModFunc(func(o *SchemaMigrationTemplate) { - o.Version = func() string { return val } - }) -} - -// Set the Column from the function -func (m schemaMigrationMods) VersionFunc(f func() string) SchemaMigrationMod { - return SchemaMigrationModFunc(func(o *SchemaMigrationTemplate) { - o.Version = f - }) -} - -// Clear any values for the column -func (m schemaMigrationMods) UnsetVersion() SchemaMigrationMod { - return SchemaMigrationModFunc(func(o *SchemaMigrationTemplate) { - o.Version = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m schemaMigrationMods) RandomVersion(f *faker.Faker) SchemaMigrationMod { - return SchemaMigrationModFunc(func(o *SchemaMigrationTemplate) { - o.Version = func() string { - return random_string(f) - } - }) -} diff --git a/server/internal/models/factory/user.bob.go b/server/internal/models/factory/user.bob.go deleted file mode 100644 index 044f859..0000000 --- a/server/internal/models/factory/user.bob.go +++ /dev/null @@ -1,598 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package factory - -import ( - "context" - "testing" - - "github.com/aarondl/opt/null" - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" - "github.com/jaswdr/faker/v2" - models "github.com/spotdemo4/trevstack/server/internal/models" - "github.com/stephenafamo/bob" -) - -type UserMod interface { - Apply(*UserTemplate) -} - -type UserModFunc func(*UserTemplate) - -func (f UserModFunc) Apply(n *UserTemplate) { - f(n) -} - -type UserModSlice []UserMod - -func (mods UserModSlice) Apply(n *UserTemplate) { - for _, f := range mods { - f.Apply(n) - } -} - -// UserTemplate is an object representing the database table. -// all columns are optional and should be set by mods -type UserTemplate struct { - ID func() int64 - Username func() string - Password func() string - ProfilePictureID func() null.Val[int64] - - r userR - f *Factory -} - -type userR struct { - Files []*userRFilesR - Items []*userRItemsR - ProfilePictureFile *userRProfilePictureFileR -} - -type userRFilesR struct { - number int - o *FileTemplate -} -type userRItemsR struct { - number int - o *ItemTemplate -} -type userRProfilePictureFileR struct { - o *FileTemplate -} - -// Apply mods to the UserTemplate -func (o *UserTemplate) Apply(mods ...UserMod) { - for _, mod := range mods { - mod.Apply(o) - } -} - -// toModel returns an *models.User -// this does nothing with the relationship templates -func (o UserTemplate) toModel() *models.User { - m := &models.User{} - - if o.ID != nil { - m.ID = o.ID() - } - if o.Username != nil { - m.Username = o.Username() - } - if o.Password != nil { - m.Password = o.Password() - } - if o.ProfilePictureID != nil { - m.ProfilePictureID = o.ProfilePictureID() - } - - return m -} - -// toModels returns an models.UserSlice -// this does nothing with the relationship templates -func (o UserTemplate) toModels(number int) models.UserSlice { - m := make(models.UserSlice, number) - - for i := range m { - m[i] = o.toModel() - } - - return m -} - -// setModelRels creates and sets the relationships on *models.User -// according to the relationships in the template. Nothing is inserted into the db -func (t UserTemplate) setModelRels(o *models.User) { - if t.r.Files != nil { - rel := models.FileSlice{} - for _, r := range t.r.Files { - related := r.o.toModels(r.number) - for _, rel := range related { - rel.UserID = o.ID - rel.R.User = o - } - rel = append(rel, related...) - } - o.R.Files = rel - } - - if t.r.Items != nil { - rel := models.ItemSlice{} - for _, r := range t.r.Items { - related := r.o.toModels(r.number) - for _, rel := range related { - rel.UserID = o.ID - rel.R.User = o - } - rel = append(rel, related...) - } - o.R.Items = rel - } - - if t.r.ProfilePictureFile != nil { - rel := t.r.ProfilePictureFile.o.toModel() - rel.R.ProfilePictureUsers = append(rel.R.ProfilePictureUsers, o) - o.ProfilePictureID = null.From(rel.ID) - o.R.ProfilePictureFile = rel - } -} - -// BuildSetter returns an *models.UserSetter -// this does nothing with the relationship templates -func (o UserTemplate) BuildSetter() *models.UserSetter { - m := &models.UserSetter{} - - if o.ID != nil { - m.ID = omit.From(o.ID()) - } - if o.Username != nil { - m.Username = omit.From(o.Username()) - } - if o.Password != nil { - m.Password = omit.From(o.Password()) - } - if o.ProfilePictureID != nil { - m.ProfilePictureID = omitnull.FromNull(o.ProfilePictureID()) - } - - return m -} - -// BuildManySetter returns an []*models.UserSetter -// this does nothing with the relationship templates -func (o UserTemplate) BuildManySetter(number int) []*models.UserSetter { - m := make([]*models.UserSetter, number) - - for i := range m { - m[i] = o.BuildSetter() - } - - return m -} - -// Build returns an *models.User -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use UserTemplate.Create -func (o UserTemplate) Build() *models.User { - m := o.toModel() - o.setModelRels(m) - - return m -} - -// BuildMany returns an models.UserSlice -// Related objects are also created and placed in the .R field -// NOTE: Objects are not inserted into the database. Use UserTemplate.CreateMany -func (o UserTemplate) BuildMany(number int) models.UserSlice { - m := make(models.UserSlice, number) - - for i := range m { - m[i] = o.Build() - } - - return m -} - -func ensureCreatableUser(m *models.UserSetter) { - if m.Username.IsUnset() { - m.Username = omit.From(random_string(nil)) - } - if m.Password.IsUnset() { - m.Password = omit.From(random_string(nil)) - } -} - -// insertOptRels creates and inserts any optional the relationships on *models.User -// according to the relationships in the template. -// any required relationship should have already exist on the model -func (o *UserTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *models.User) (context.Context, error) { - var err error - - if o.r.Files != nil { - for _, r := range o.r.Files { - var rel0 models.FileSlice - ctx, rel0, err = r.o.createMany(ctx, exec, r.number) - if err != nil { - return ctx, err - } - - err = m.AttachFiles(ctx, exec, rel0...) - if err != nil { - return ctx, err - } - } - } - - if o.r.Items != nil { - for _, r := range o.r.Items { - var rel1 models.ItemSlice - ctx, rel1, err = r.o.createMany(ctx, exec, r.number) - if err != nil { - return ctx, err - } - - err = m.AttachItems(ctx, exec, rel1...) - if err != nil { - return ctx, err - } - } - } - - if o.r.ProfilePictureFile != nil { - var rel2 *models.File - ctx, rel2, err = o.r.ProfilePictureFile.o.create(ctx, exec) - if err != nil { - return ctx, err - } - err = m.AttachProfilePictureFile(ctx, exec, rel2) - if err != nil { - return ctx, err - } - } - - return ctx, err -} - -// Create builds a user and inserts it into the database -// Relations objects are also inserted and placed in the .R field -func (o *UserTemplate) Create(ctx context.Context, exec bob.Executor) (*models.User, error) { - _, m, err := o.create(ctx, exec) - return m, err -} - -// MustCreate builds a user and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o *UserTemplate) MustCreate(ctx context.Context, exec bob.Executor) *models.User { - _, m, err := o.create(ctx, exec) - if err != nil { - panic(err) - } - return m -} - -// CreateOrFail builds a user and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o *UserTemplate) CreateOrFail(ctx context.Context, tb testing.TB, exec bob.Executor) *models.User { - tb.Helper() - _, m, err := o.create(ctx, exec) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// create builds a user and inserts it into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted model -func (o *UserTemplate) create(ctx context.Context, exec bob.Executor) (context.Context, *models.User, error) { - var err error - opt := o.BuildSetter() - ensureCreatableUser(opt) - - m, err := models.Users.Insert(opt).One(ctx, exec) - if err != nil { - return ctx, nil, err - } - ctx = userCtx.WithValue(ctx, m) - - ctx, err = o.insertOptRels(ctx, exec, m) - return ctx, m, err -} - -// CreateMany builds multiple users and inserts them into the database -// Relations objects are also inserted and placed in the .R field -func (o UserTemplate) CreateMany(ctx context.Context, exec bob.Executor, number int) (models.UserSlice, error) { - _, m, err := o.createMany(ctx, exec, number) - return m, err -} - -// MustCreateMany builds multiple users and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// panics if an error occurs -func (o UserTemplate) MustCreateMany(ctx context.Context, exec bob.Executor, number int) models.UserSlice { - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - panic(err) - } - return m -} - -// CreateManyOrFail builds multiple users and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// It calls `tb.Fatal(err)` on the test/benchmark if an error occurs -func (o UserTemplate) CreateManyOrFail(ctx context.Context, tb testing.TB, exec bob.Executor, number int) models.UserSlice { - tb.Helper() - _, m, err := o.createMany(ctx, exec, number) - if err != nil { - tb.Fatal(err) - return nil - } - return m -} - -// createMany builds multiple users and inserts them into the database -// Relations objects are also inserted and placed in the .R field -// this returns a context that includes the newly inserted models -func (o UserTemplate) createMany(ctx context.Context, exec bob.Executor, number int) (context.Context, models.UserSlice, error) { - var err error - m := make(models.UserSlice, number) - - for i := range m { - ctx, m[i], err = o.create(ctx, exec) - if err != nil { - return ctx, nil, err - } - } - - return ctx, m, nil -} - -// User has methods that act as mods for the UserTemplate -var UserMods userMods - -type userMods struct{} - -func (m userMods) RandomizeAllColumns(f *faker.Faker) UserMod { - return UserModSlice{ - UserMods.RandomID(f), - UserMods.RandomUsername(f), - UserMods.RandomPassword(f), - UserMods.RandomProfilePictureID(f), - } -} - -// Set the model columns to this value -func (m userMods) ID(val int64) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ID = func() int64 { return val } - }) -} - -// Set the Column from the function -func (m userMods) IDFunc(f func() int64) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ID = f - }) -} - -// Clear any values for the column -func (m userMods) UnsetID() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ID = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m userMods) RandomID(f *faker.Faker) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ID = func() int64 { - return random_int64(f) - } - }) -} - -// Set the model columns to this value -func (m userMods) Username(val string) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Username = func() string { return val } - }) -} - -// Set the Column from the function -func (m userMods) UsernameFunc(f func() string) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Username = f - }) -} - -// Clear any values for the column -func (m userMods) UnsetUsername() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Username = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m userMods) RandomUsername(f *faker.Faker) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Username = func() string { - return random_string(f) - } - }) -} - -// Set the model columns to this value -func (m userMods) Password(val string) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Password = func() string { return val } - }) -} - -// Set the Column from the function -func (m userMods) PasswordFunc(f func() string) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Password = f - }) -} - -// Clear any values for the column -func (m userMods) UnsetPassword() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Password = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m userMods) RandomPassword(f *faker.Faker) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.Password = func() string { - return random_string(f) - } - }) -} - -// Set the model columns to this value -func (m userMods) ProfilePictureID(val null.Val[int64]) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ProfilePictureID = func() null.Val[int64] { return val } - }) -} - -// Set the Column from the function -func (m userMods) ProfilePictureIDFunc(f func() null.Val[int64]) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ProfilePictureID = f - }) -} - -// Clear any values for the column -func (m userMods) UnsetProfilePictureID() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ProfilePictureID = nil - }) -} - -// Generates a random value for the column using the given faker -// if faker is nil, a default faker is used -func (m userMods) RandomProfilePictureID(f *faker.Faker) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.ProfilePictureID = func() null.Val[int64] { - if f == nil { - f = &defaultFaker - } - - if f.Bool() { - return null.FromPtr[int64](nil) - } - - return null.From(random_int64(f)) - } - }) -} - -func (m userMods) WithProfilePictureFile(rel *FileTemplate) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.ProfilePictureFile = &userRProfilePictureFileR{ - o: rel, - } - }) -} - -func (m userMods) WithNewProfilePictureFile(mods ...FileMod) UserMod { - return UserModFunc(func(o *UserTemplate) { - related := o.f.NewFile(mods...) - - m.WithProfilePictureFile(related).Apply(o) - }) -} - -func (m userMods) WithoutProfilePictureFile() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.ProfilePictureFile = nil - }) -} - -func (m userMods) WithFiles(number int, related *FileTemplate) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.Files = []*userRFilesR{{ - number: number, - o: related, - }} - }) -} - -func (m userMods) WithNewFiles(number int, mods ...FileMod) UserMod { - return UserModFunc(func(o *UserTemplate) { - related := o.f.NewFile(mods...) - m.WithFiles(number, related).Apply(o) - }) -} - -func (m userMods) AddFiles(number int, related *FileTemplate) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.Files = append(o.r.Files, &userRFilesR{ - number: number, - o: related, - }) - }) -} - -func (m userMods) AddNewFiles(number int, mods ...FileMod) UserMod { - return UserModFunc(func(o *UserTemplate) { - related := o.f.NewFile(mods...) - m.AddFiles(number, related).Apply(o) - }) -} - -func (m userMods) WithoutFiles() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.Files = nil - }) -} - -func (m userMods) WithItems(number int, related *ItemTemplate) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.Items = []*userRItemsR{{ - number: number, - o: related, - }} - }) -} - -func (m userMods) WithNewItems(number int, mods ...ItemMod) UserMod { - return UserModFunc(func(o *UserTemplate) { - related := o.f.NewItem(mods...) - m.WithItems(number, related).Apply(o) - }) -} - -func (m userMods) AddItems(number int, related *ItemTemplate) UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.Items = append(o.r.Items, &userRItemsR{ - number: number, - o: related, - }) - }) -} - -func (m userMods) AddNewItems(number int, mods ...ItemMod) UserMod { - return UserModFunc(func(o *UserTemplate) { - related := o.f.NewItem(mods...) - m.AddItems(number, related).Apply(o) - }) -} - -func (m userMods) WithoutItems() UserMod { - return UserModFunc(func(o *UserTemplate) { - o.r.Items = nil - }) -} diff --git a/server/internal/models/file.bob.go b/server/internal/models/file.bob.go deleted file mode 100644 index de27c5f..0000000 --- a/server/internal/models/file.bob.go +++ /dev/null @@ -1,851 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package models - -import ( - "context" - "database/sql" - "errors" - "fmt" - "io" - - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/dialect/sqlite" - "github.com/stephenafamo/bob/dialect/sqlite/dialect" - "github.com/stephenafamo/bob/dialect/sqlite/dm" - "github.com/stephenafamo/bob/dialect/sqlite/sm" - "github.com/stephenafamo/bob/dialect/sqlite/um" - "github.com/stephenafamo/bob/expr" - "github.com/stephenafamo/bob/mods" - "github.com/stephenafamo/bob/orm" -) - -// File is an object representing the database table. -type File struct { - ID int64 `db:"id,pk" ` - Name string `db:"name" ` - Data []byte `db:"data" ` - UserID int64 `db:"user_id" ` - - R fileR `db:"-" ` -} - -// FileSlice is an alias for a slice of pointers to File. -// This should almost always be used instead of []*File. -type FileSlice []*File - -// Files contains methods to work with the file table -var Files = sqlite.NewTablex[*File, FileSlice, *FileSetter]("", "file") - -// FilesQuery is a query on the file table -type FilesQuery = *sqlite.ViewQuery[*File, FileSlice] - -// fileR is where relationships are stored. -type fileR struct { - User *User // fk_file_0 - ProfilePictureUsers UserSlice // fk_user_0 -} - -type fileColumnNames struct { - ID string - Name string - Data string - UserID string -} - -var FileColumns = buildFileColumns("file") - -type fileColumns struct { - tableAlias string - ID sqlite.Expression - Name sqlite.Expression - Data sqlite.Expression - UserID sqlite.Expression -} - -func (c fileColumns) Alias() string { - return c.tableAlias -} - -func (fileColumns) AliasedAs(alias string) fileColumns { - return buildFileColumns(alias) -} - -func buildFileColumns(alias string) fileColumns { - return fileColumns{ - tableAlias: alias, - ID: sqlite.Quote(alias, "id"), - Name: sqlite.Quote(alias, "name"), - Data: sqlite.Quote(alias, "data"), - UserID: sqlite.Quote(alias, "user_id"), - } -} - -type fileWhere[Q sqlite.Filterable] struct { - ID sqlite.WhereMod[Q, int64] - Name sqlite.WhereMod[Q, string] - Data sqlite.WhereMod[Q, []byte] - UserID sqlite.WhereMod[Q, int64] -} - -func (fileWhere[Q]) AliasedAs(alias string) fileWhere[Q] { - return buildFileWhere[Q](buildFileColumns(alias)) -} - -func buildFileWhere[Q sqlite.Filterable](cols fileColumns) fileWhere[Q] { - return fileWhere[Q]{ - ID: sqlite.Where[Q, int64](cols.ID), - Name: sqlite.Where[Q, string](cols.Name), - Data: sqlite.Where[Q, []byte](cols.Data), - UserID: sqlite.Where[Q, int64](cols.UserID), - } -} - -var FileErrors = &fileErrors{ - ErrUniquePkMainFile: &UniqueConstraintError{s: "pk_main_file"}, -} - -type fileErrors struct { - ErrUniquePkMainFile *UniqueConstraintError -} - -// FileSetter is used for insert/upsert/update operations -// All values are optional, and do not have to be set -// Generated columns are not included -type FileSetter struct { - ID omit.Val[int64] `db:"id,pk" ` - Name omit.Val[string] `db:"name" ` - Data omit.Val[[]byte] `db:"data" ` - UserID omit.Val[int64] `db:"user_id" ` -} - -func (s FileSetter) SetColumns() []string { - vals := make([]string, 0, 4) - if !s.ID.IsUnset() { - vals = append(vals, "id") - } - - if !s.Name.IsUnset() { - vals = append(vals, "name") - } - - if !s.Data.IsUnset() { - vals = append(vals, "data") - } - - if !s.UserID.IsUnset() { - vals = append(vals, "user_id") - } - - return vals -} - -func (s FileSetter) Overwrite(t *File) { - if !s.ID.IsUnset() { - t.ID, _ = s.ID.Get() - } - if !s.Name.IsUnset() { - t.Name, _ = s.Name.Get() - } - if !s.Data.IsUnset() { - t.Data, _ = s.Data.Get() - } - if !s.UserID.IsUnset() { - t.UserID, _ = s.UserID.Get() - } -} - -func (s *FileSetter) Apply(q *dialect.InsertQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Files.BeforeInsertHooks.RunHooks(ctx, exec, s) - }) - - if len(q.Table.Columns) == 0 { - q.Table.Columns = s.SetColumns() - } - - q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 0, 4) - if !s.ID.IsUnset() { - vals = append(vals, sqlite.Arg(s.ID)) - } - - if !s.Name.IsUnset() { - vals = append(vals, sqlite.Arg(s.Name)) - } - - if !s.Data.IsUnset() { - vals = append(vals, sqlite.Arg(s.Data)) - } - - if !s.UserID.IsUnset() { - vals = append(vals, sqlite.Arg(s.UserID)) - } - - return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") - })) -} - -func (s FileSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return um.Set(s.Expressions()...) -} - -func (s FileSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 4) - - if !s.ID.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "id")...), - sqlite.Arg(s.ID), - }}) - } - - if !s.Name.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "name")...), - sqlite.Arg(s.Name), - }}) - } - - if !s.Data.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "data")...), - sqlite.Arg(s.Data), - }}) - } - - if !s.UserID.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "user_id")...), - sqlite.Arg(s.UserID), - }}) - } - - return exprs -} - -// FindFile retrieves a single record by primary key -// If cols is empty Find will return all columns. -func FindFile(ctx context.Context, exec bob.Executor, IDPK int64, cols ...string) (*File, error) { - if len(cols) == 0 { - return Files.Query( - SelectWhere.Files.ID.EQ(IDPK), - ).One(ctx, exec) - } - - return Files.Query( - SelectWhere.Files.ID.EQ(IDPK), - sm.Columns(Files.Columns().Only(cols...)), - ).One(ctx, exec) -} - -// FileExists checks the presence of a single record by primary key -func FileExists(ctx context.Context, exec bob.Executor, IDPK int64) (bool, error) { - return Files.Query( - SelectWhere.Files.ID.EQ(IDPK), - ).Exists(ctx, exec) -} - -// AfterQueryHook is called after File is retrieved from the database -func (o *File) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = Files.AfterSelectHooks.RunHooks(ctx, exec, FileSlice{o}) - case bob.QueryTypeInsert: - ctx, err = Files.AfterInsertHooks.RunHooks(ctx, exec, FileSlice{o}) - case bob.QueryTypeUpdate: - ctx, err = Files.AfterUpdateHooks.RunHooks(ctx, exec, FileSlice{o}) - case bob.QueryTypeDelete: - ctx, err = Files.AfterDeleteHooks.RunHooks(ctx, exec, FileSlice{o}) - } - - return err -} - -// PrimaryKeyVals returns the primary key values of the File -func (o *File) PrimaryKeyVals() bob.Expression { - return sqlite.Arg(o.ID) -} - -func (o *File) pkEQ() dialect.Expression { - return sqlite.Quote("file", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) - })) -} - -// Update uses an executor to update the File -func (o *File) Update(ctx context.Context, exec bob.Executor, s *FileSetter) error { - v, err := Files.Update(s.UpdateMod(), um.Where(o.pkEQ())).One(ctx, exec) - if err != nil { - return err - } - - o.R = v.R - *o = *v - - return nil -} - -// Delete deletes a single File record with an executor -func (o *File) Delete(ctx context.Context, exec bob.Executor) error { - _, err := Files.Delete(dm.Where(o.pkEQ())).Exec(ctx, exec) - return err -} - -// Reload refreshes the File using the executor -func (o *File) Reload(ctx context.Context, exec bob.Executor) error { - o2, err := Files.Query( - SelectWhere.Files.ID.EQ(o.ID), - ).One(ctx, exec) - if err != nil { - return err - } - o2.R = o.R - *o = *o2 - - return nil -} - -// AfterQueryHook is called after FileSlice is retrieved from the database -func (o FileSlice) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = Files.AfterSelectHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeInsert: - ctx, err = Files.AfterInsertHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeUpdate: - ctx, err = Files.AfterUpdateHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeDelete: - ctx, err = Files.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err -} - -func (o FileSlice) pkIN() dialect.Expression { - if len(o) == 0 { - return sqlite.Raw("NULL") - } - - return sqlite.Quote("file", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - pkPairs := make([]bob.Expression, len(o)) - for i, row := range o { - pkPairs[i] = row.PrimaryKeyVals() - } - return bob.ExpressSlice(ctx, w, d, start, pkPairs, "", ", ", "") - })) -} - -// copyMatchingRows finds models in the given slice that have the same primary key -// then it first copies the existing relationships from the old model to the new model -// and then replaces the old model in the slice with the new model -func (o FileSlice) copyMatchingRows(from ...*File) { - for i, old := range o { - for _, new := range from { - if new.ID != old.ID { - continue - } - new.R = old.R - o[i] = new - break - } - } -} - -// UpdateMod modifies an update query with "WHERE primary_key IN (o...)" -func (o FileSlice) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return bob.ModFunc[*dialect.UpdateQuery](func(q *dialect.UpdateQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Files.BeforeUpdateHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *File: - o.copyMatchingRows(retrieved) - case []*File: - o.copyMatchingRows(retrieved...) - case FileSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a File or a slice of File - // then run the AfterUpdateHooks on the slice - _, err = Files.AfterUpdateHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -// DeleteMod modifies an delete query with "WHERE primary_key IN (o...)" -func (o FileSlice) DeleteMod() bob.Mod[*dialect.DeleteQuery] { - return bob.ModFunc[*dialect.DeleteQuery](func(q *dialect.DeleteQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Files.BeforeDeleteHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *File: - o.copyMatchingRows(retrieved) - case []*File: - o.copyMatchingRows(retrieved...) - case FileSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a File or a slice of File - // then run the AfterDeleteHooks on the slice - _, err = Files.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -func (o FileSlice) UpdateAll(ctx context.Context, exec bob.Executor, vals FileSetter) error { - if len(o) == 0 { - return nil - } - - _, err := Files.Update(vals.UpdateMod(), o.UpdateMod()).All(ctx, exec) - return err -} - -func (o FileSlice) DeleteAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - _, err := Files.Delete(o.DeleteMod()).Exec(ctx, exec) - return err -} - -func (o FileSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - o2, err := Files.Query(sm.Where(o.pkIN())).All(ctx, exec) - if err != nil { - return err - } - - o.copyMatchingRows(o2...) - - return nil -} - -type fileJoins[Q dialect.Joinable] struct { - typ string - User func(context.Context) modAs[Q, userColumns] - ProfilePictureUsers func(context.Context) modAs[Q, userColumns] -} - -func (j fileJoins[Q]) aliasedAs(alias string) fileJoins[Q] { - return buildFileJoins[Q](buildFileColumns(alias), j.typ) -} - -func buildFileJoins[Q dialect.Joinable](cols fileColumns, typ string) fileJoins[Q] { - return fileJoins[Q]{ - typ: typ, - User: filesJoinUser[Q](cols, typ), - ProfilePictureUsers: filesJoinProfilePictureUsers[Q](cols, typ), - } -} - -func filesJoinUser[Q dialect.Joinable](from fileColumns, typ string) func(context.Context) modAs[Q, userColumns] { - return func(ctx context.Context) modAs[Q, userColumns] { - return modAs[Q, userColumns]{ - c: UserColumns, - f: func(to userColumns) bob.Mod[Q] { - mods := make(mods.QueryMods[Q], 0, 1) - - { - mods = append(mods, dialect.Join[Q](typ, Users.Name().As(to.Alias())).On( - to.ID.EQ(from.UserID), - )) - } - - return mods - }, - } - } -} - -func filesJoinProfilePictureUsers[Q dialect.Joinable](from fileColumns, typ string) func(context.Context) modAs[Q, userColumns] { - return func(ctx context.Context) modAs[Q, userColumns] { - return modAs[Q, userColumns]{ - c: UserColumns, - f: func(to userColumns) bob.Mod[Q] { - mods := make(mods.QueryMods[Q], 0, 1) - - { - mods = append(mods, dialect.Join[Q](typ, Users.Name().As(to.Alias())).On( - to.ProfilePictureID.EQ(from.ID), - )) - } - - return mods - }, - } - } -} - -// User starts a query for related objects on user -func (o *File) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { - return Users.Query(append(mods, - sm.Where(UserColumns.ID.EQ(sqlite.Arg(o.UserID))), - )...) -} - -func (os FileSlice) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { - PKArgs := make([]bob.Expression, len(os)) - for i, o := range os { - PKArgs[i] = sqlite.ArgGroup(o.UserID) - } - - return Users.Query(append(mods, - sm.Where(sqlite.Group(UserColumns.ID).In(PKArgs...)), - )...) -} - -// ProfilePictureUsers starts a query for related objects on user -func (o *File) ProfilePictureUsers(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { - return Users.Query(append(mods, - sm.Where(UserColumns.ProfilePictureID.EQ(sqlite.Arg(o.ID))), - )...) -} - -func (os FileSlice) ProfilePictureUsers(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { - PKArgs := make([]bob.Expression, len(os)) - for i, o := range os { - PKArgs[i] = sqlite.ArgGroup(o.ID) - } - - return Users.Query(append(mods, - sm.Where(sqlite.Group(UserColumns.ProfilePictureID).In(PKArgs...)), - )...) -} - -func (o *File) Preload(name string, retrieved any) error { - if o == nil { - return nil - } - - switch name { - case "User": - rel, ok := retrieved.(*User) - if !ok { - return fmt.Errorf("file cannot load %T as %q", retrieved, name) - } - - o.R.User = rel - - if rel != nil { - rel.R.Files = FileSlice{o} - } - return nil - case "ProfilePictureUsers": - rels, ok := retrieved.(UserSlice) - if !ok { - return fmt.Errorf("file cannot load %T as %q", retrieved, name) - } - - o.R.ProfilePictureUsers = rels - - for _, rel := range rels { - if rel != nil { - rel.R.ProfilePictureFile = o - } - } - return nil - default: - return fmt.Errorf("file has no relationship %q", name) - } -} - -func PreloadFileUser(opts ...sqlite.PreloadOption) sqlite.Preloader { - return sqlite.Preload[*User, UserSlice](orm.Relationship{ - Name: "User", - Sides: []orm.RelSide{ - { - From: TableNames.Files, - To: TableNames.Users, - FromColumns: []string{ - ColumnNames.Files.UserID, - }, - ToColumns: []string{ - ColumnNames.Users.ID, - }, - }, - }, - }, Users.Columns().Names(), opts...) -} - -func ThenLoadFileUser(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { - return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { - loader, isLoader := retrieved.(interface { - LoadFileUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error - }) - if !isLoader { - return fmt.Errorf("object %T cannot load FileUser", retrieved) - } - - err := loader.LoadFileUser(ctx, exec, queryMods...) - - // Don't cause an issue due to missing relationships - if errors.Is(err, sql.ErrNoRows) { - return nil - } - - return err - }) -} - -// LoadFileUser loads the file's User into the .R struct -func (o *File) LoadFileUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if o == nil { - return nil - } - - // Reset the relationship - o.R.User = nil - - related, err := o.User(mods...).One(ctx, exec) - if err != nil { - return err - } - - related.R.Files = FileSlice{o} - - o.R.User = related - return nil -} - -// LoadFileUser loads the file's User into the .R struct -func (os FileSlice) LoadFileUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if len(os) == 0 { - return nil - } - - users, err := os.User(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, o := range os { - for _, rel := range users { - if o.UserID != rel.ID { - continue - } - - rel.R.Files = append(rel.R.Files, o) - - o.R.User = rel - break - } - } - - return nil -} - -func ThenLoadFileProfilePictureUsers(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { - return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { - loader, isLoader := retrieved.(interface { - LoadFileProfilePictureUsers(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error - }) - if !isLoader { - return fmt.Errorf("object %T cannot load FileProfilePictureUsers", retrieved) - } - - err := loader.LoadFileProfilePictureUsers(ctx, exec, queryMods...) - - // Don't cause an issue due to missing relationships - if errors.Is(err, sql.ErrNoRows) { - return nil - } - - return err - }) -} - -// LoadFileProfilePictureUsers loads the file's ProfilePictureUsers into the .R struct -func (o *File) LoadFileProfilePictureUsers(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if o == nil { - return nil - } - - // Reset the relationship - o.R.ProfilePictureUsers = nil - - related, err := o.ProfilePictureUsers(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, rel := range related { - rel.R.ProfilePictureFile = o - } - - o.R.ProfilePictureUsers = related - return nil -} - -// LoadFileProfilePictureUsers loads the file's ProfilePictureUsers into the .R struct -func (os FileSlice) LoadFileProfilePictureUsers(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if len(os) == 0 { - return nil - } - - users, err := os.ProfilePictureUsers(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, o := range os { - o.R.ProfilePictureUsers = nil - } - - for _, o := range os { - for _, rel := range users { - if o.ID != rel.ProfilePictureID.GetOrZero() { - continue - } - - rel.R.ProfilePictureFile = o - - o.R.ProfilePictureUsers = append(o.R.ProfilePictureUsers, rel) - } - } - - return nil -} - -func attachFileUser0(ctx context.Context, exec bob.Executor, count int, file0 *File, user1 *User) (*File, error) { - setter := &FileSetter{ - UserID: omit.From(user1.ID), - } - - err := file0.Update(ctx, exec, setter) - if err != nil { - return nil, fmt.Errorf("attachFileUser0: %w", err) - } - - return file0, nil -} - -func (file0 *File) InsertUser(ctx context.Context, exec bob.Executor, related *UserSetter) error { - user1, err := Users.Insert(related).One(ctx, exec) - if err != nil { - return fmt.Errorf("inserting related objects: %w", err) - } - - _, err = attachFileUser0(ctx, exec, 1, file0, user1) - if err != nil { - return err - } - - file0.R.User = user1 - - user1.R.Files = append(user1.R.Files, file0) - - return nil -} - -func (file0 *File) AttachUser(ctx context.Context, exec bob.Executor, user1 *User) error { - var err error - - _, err = attachFileUser0(ctx, exec, 1, file0, user1) - if err != nil { - return err - } - - file0.R.User = user1 - - user1.R.Files = append(user1.R.Files, file0) - - return nil -} - -func insertFileProfilePictureUsers0(ctx context.Context, exec bob.Executor, users1 []*UserSetter, file0 *File) (UserSlice, error) { - for i := range users1 { - users1[i].ProfilePictureID = omitnull.From(file0.ID) - } - - ret, err := Users.Insert(bob.ToMods(users1...)).All(ctx, exec) - if err != nil { - return ret, fmt.Errorf("insertFileProfilePictureUsers0: %w", err) - } - - return ret, nil -} - -func attachFileProfilePictureUsers0(ctx context.Context, exec bob.Executor, count int, users1 UserSlice, file0 *File) (UserSlice, error) { - setter := &UserSetter{ - ProfilePictureID: omitnull.From(file0.ID), - } - - err := users1.UpdateAll(ctx, exec, *setter) - if err != nil { - return nil, fmt.Errorf("attachFileProfilePictureUsers0: %w", err) - } - - return users1, nil -} - -func (file0 *File) InsertProfilePictureUsers(ctx context.Context, exec bob.Executor, related ...*UserSetter) error { - if len(related) == 0 { - return nil - } - - var err error - - users1, err := insertFileProfilePictureUsers0(ctx, exec, related, file0) - if err != nil { - return err - } - - file0.R.ProfilePictureUsers = append(file0.R.ProfilePictureUsers, users1...) - - for _, rel := range users1 { - rel.R.ProfilePictureFile = file0 - } - return nil -} - -func (file0 *File) AttachProfilePictureUsers(ctx context.Context, exec bob.Executor, related ...*User) error { - if len(related) == 0 { - return nil - } - - var err error - users1 := UserSlice(related) - - _, err = attachFileProfilePictureUsers0(ctx, exec, len(related), users1, file0) - if err != nil { - return err - } - - file0.R.ProfilePictureUsers = append(file0.R.ProfilePictureUsers, users1...) - - for _, rel := range related { - rel.R.ProfilePictureFile = file0 - } - - return nil -} diff --git a/server/internal/models/item.bob.go b/server/internal/models/item.bob.go deleted file mode 100644 index 379207c..0000000 --- a/server/internal/models/item.bob.go +++ /dev/null @@ -1,732 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package models - -import ( - "context" - "database/sql" - "errors" - "fmt" - "io" - "time" - - "github.com/aarondl/opt/omit" - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/dialect/sqlite" - "github.com/stephenafamo/bob/dialect/sqlite/dialect" - "github.com/stephenafamo/bob/dialect/sqlite/dm" - "github.com/stephenafamo/bob/dialect/sqlite/sm" - "github.com/stephenafamo/bob/dialect/sqlite/um" - "github.com/stephenafamo/bob/expr" - "github.com/stephenafamo/bob/mods" - "github.com/stephenafamo/bob/orm" -) - -// Item is an object representing the database table. -type Item struct { - ID int64 `db:"id,pk" ` - Name string `db:"name" ` - Added time.Time `db:"added" ` - Description string `db:"description" ` - Price float32 `db:"price" ` - Quantity int64 `db:"quantity" ` - UserID int64 `db:"user_id" ` - - R itemR `db:"-" ` -} - -// ItemSlice is an alias for a slice of pointers to Item. -// This should almost always be used instead of []*Item. -type ItemSlice []*Item - -// Items contains methods to work with the item table -var Items = sqlite.NewTablex[*Item, ItemSlice, *ItemSetter]("", "item") - -// ItemsQuery is a query on the item table -type ItemsQuery = *sqlite.ViewQuery[*Item, ItemSlice] - -// itemR is where relationships are stored. -type itemR struct { - User *User // fk_item_0 -} - -type itemColumnNames struct { - ID string - Name string - Added string - Description string - Price string - Quantity string - UserID string -} - -var ItemColumns = buildItemColumns("item") - -type itemColumns struct { - tableAlias string - ID sqlite.Expression - Name sqlite.Expression - Added sqlite.Expression - Description sqlite.Expression - Price sqlite.Expression - Quantity sqlite.Expression - UserID sqlite.Expression -} - -func (c itemColumns) Alias() string { - return c.tableAlias -} - -func (itemColumns) AliasedAs(alias string) itemColumns { - return buildItemColumns(alias) -} - -func buildItemColumns(alias string) itemColumns { - return itemColumns{ - tableAlias: alias, - ID: sqlite.Quote(alias, "id"), - Name: sqlite.Quote(alias, "name"), - Added: sqlite.Quote(alias, "added"), - Description: sqlite.Quote(alias, "description"), - Price: sqlite.Quote(alias, "price"), - Quantity: sqlite.Quote(alias, "quantity"), - UserID: sqlite.Quote(alias, "user_id"), - } -} - -type itemWhere[Q sqlite.Filterable] struct { - ID sqlite.WhereMod[Q, int64] - Name sqlite.WhereMod[Q, string] - Added sqlite.WhereMod[Q, time.Time] - Description sqlite.WhereMod[Q, string] - Price sqlite.WhereMod[Q, float32] - Quantity sqlite.WhereMod[Q, int64] - UserID sqlite.WhereMod[Q, int64] -} - -func (itemWhere[Q]) AliasedAs(alias string) itemWhere[Q] { - return buildItemWhere[Q](buildItemColumns(alias)) -} - -func buildItemWhere[Q sqlite.Filterable](cols itemColumns) itemWhere[Q] { - return itemWhere[Q]{ - ID: sqlite.Where[Q, int64](cols.ID), - Name: sqlite.Where[Q, string](cols.Name), - Added: sqlite.Where[Q, time.Time](cols.Added), - Description: sqlite.Where[Q, string](cols.Description), - Price: sqlite.Where[Q, float32](cols.Price), - Quantity: sqlite.Where[Q, int64](cols.Quantity), - UserID: sqlite.Where[Q, int64](cols.UserID), - } -} - -var ItemErrors = &itemErrors{ - ErrUniquePkMainItem: &UniqueConstraintError{s: "pk_main_item"}, -} - -type itemErrors struct { - ErrUniquePkMainItem *UniqueConstraintError -} - -// ItemSetter is used for insert/upsert/update operations -// All values are optional, and do not have to be set -// Generated columns are not included -type ItemSetter struct { - ID omit.Val[int64] `db:"id,pk" ` - Name omit.Val[string] `db:"name" ` - Added omit.Val[time.Time] `db:"added" ` - Description omit.Val[string] `db:"description" ` - Price omit.Val[float32] `db:"price" ` - Quantity omit.Val[int64] `db:"quantity" ` - UserID omit.Val[int64] `db:"user_id" ` -} - -func (s ItemSetter) SetColumns() []string { - vals := make([]string, 0, 7) - if !s.ID.IsUnset() { - vals = append(vals, "id") - } - - if !s.Name.IsUnset() { - vals = append(vals, "name") - } - - if !s.Added.IsUnset() { - vals = append(vals, "added") - } - - if !s.Description.IsUnset() { - vals = append(vals, "description") - } - - if !s.Price.IsUnset() { - vals = append(vals, "price") - } - - if !s.Quantity.IsUnset() { - vals = append(vals, "quantity") - } - - if !s.UserID.IsUnset() { - vals = append(vals, "user_id") - } - - return vals -} - -func (s ItemSetter) Overwrite(t *Item) { - if !s.ID.IsUnset() { - t.ID, _ = s.ID.Get() - } - if !s.Name.IsUnset() { - t.Name, _ = s.Name.Get() - } - if !s.Added.IsUnset() { - t.Added, _ = s.Added.Get() - } - if !s.Description.IsUnset() { - t.Description, _ = s.Description.Get() - } - if !s.Price.IsUnset() { - t.Price, _ = s.Price.Get() - } - if !s.Quantity.IsUnset() { - t.Quantity, _ = s.Quantity.Get() - } - if !s.UserID.IsUnset() { - t.UserID, _ = s.UserID.Get() - } -} - -func (s *ItemSetter) Apply(q *dialect.InsertQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Items.BeforeInsertHooks.RunHooks(ctx, exec, s) - }) - - if len(q.Table.Columns) == 0 { - q.Table.Columns = s.SetColumns() - } - - q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 0, 7) - if !s.ID.IsUnset() { - vals = append(vals, sqlite.Arg(s.ID)) - } - - if !s.Name.IsUnset() { - vals = append(vals, sqlite.Arg(s.Name)) - } - - if !s.Added.IsUnset() { - vals = append(vals, sqlite.Arg(s.Added)) - } - - if !s.Description.IsUnset() { - vals = append(vals, sqlite.Arg(s.Description)) - } - - if !s.Price.IsUnset() { - vals = append(vals, sqlite.Arg(s.Price)) - } - - if !s.Quantity.IsUnset() { - vals = append(vals, sqlite.Arg(s.Quantity)) - } - - if !s.UserID.IsUnset() { - vals = append(vals, sqlite.Arg(s.UserID)) - } - - return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") - })) -} - -func (s ItemSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return um.Set(s.Expressions()...) -} - -func (s ItemSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 7) - - if !s.ID.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "id")...), - sqlite.Arg(s.ID), - }}) - } - - if !s.Name.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "name")...), - sqlite.Arg(s.Name), - }}) - } - - if !s.Added.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "added")...), - sqlite.Arg(s.Added), - }}) - } - - if !s.Description.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "description")...), - sqlite.Arg(s.Description), - }}) - } - - if !s.Price.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "price")...), - sqlite.Arg(s.Price), - }}) - } - - if !s.Quantity.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "quantity")...), - sqlite.Arg(s.Quantity), - }}) - } - - if !s.UserID.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "user_id")...), - sqlite.Arg(s.UserID), - }}) - } - - return exprs -} - -// FindItem retrieves a single record by primary key -// If cols is empty Find will return all columns. -func FindItem(ctx context.Context, exec bob.Executor, IDPK int64, cols ...string) (*Item, error) { - if len(cols) == 0 { - return Items.Query( - SelectWhere.Items.ID.EQ(IDPK), - ).One(ctx, exec) - } - - return Items.Query( - SelectWhere.Items.ID.EQ(IDPK), - sm.Columns(Items.Columns().Only(cols...)), - ).One(ctx, exec) -} - -// ItemExists checks the presence of a single record by primary key -func ItemExists(ctx context.Context, exec bob.Executor, IDPK int64) (bool, error) { - return Items.Query( - SelectWhere.Items.ID.EQ(IDPK), - ).Exists(ctx, exec) -} - -// AfterQueryHook is called after Item is retrieved from the database -func (o *Item) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = Items.AfterSelectHooks.RunHooks(ctx, exec, ItemSlice{o}) - case bob.QueryTypeInsert: - ctx, err = Items.AfterInsertHooks.RunHooks(ctx, exec, ItemSlice{o}) - case bob.QueryTypeUpdate: - ctx, err = Items.AfterUpdateHooks.RunHooks(ctx, exec, ItemSlice{o}) - case bob.QueryTypeDelete: - ctx, err = Items.AfterDeleteHooks.RunHooks(ctx, exec, ItemSlice{o}) - } - - return err -} - -// PrimaryKeyVals returns the primary key values of the Item -func (o *Item) PrimaryKeyVals() bob.Expression { - return sqlite.Arg(o.ID) -} - -func (o *Item) pkEQ() dialect.Expression { - return sqlite.Quote("item", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) - })) -} - -// Update uses an executor to update the Item -func (o *Item) Update(ctx context.Context, exec bob.Executor, s *ItemSetter) error { - v, err := Items.Update(s.UpdateMod(), um.Where(o.pkEQ())).One(ctx, exec) - if err != nil { - return err - } - - o.R = v.R - *o = *v - - return nil -} - -// Delete deletes a single Item record with an executor -func (o *Item) Delete(ctx context.Context, exec bob.Executor) error { - _, err := Items.Delete(dm.Where(o.pkEQ())).Exec(ctx, exec) - return err -} - -// Reload refreshes the Item using the executor -func (o *Item) Reload(ctx context.Context, exec bob.Executor) error { - o2, err := Items.Query( - SelectWhere.Items.ID.EQ(o.ID), - ).One(ctx, exec) - if err != nil { - return err - } - o2.R = o.R - *o = *o2 - - return nil -} - -// AfterQueryHook is called after ItemSlice is retrieved from the database -func (o ItemSlice) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = Items.AfterSelectHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeInsert: - ctx, err = Items.AfterInsertHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeUpdate: - ctx, err = Items.AfterUpdateHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeDelete: - ctx, err = Items.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err -} - -func (o ItemSlice) pkIN() dialect.Expression { - if len(o) == 0 { - return sqlite.Raw("NULL") - } - - return sqlite.Quote("item", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - pkPairs := make([]bob.Expression, len(o)) - for i, row := range o { - pkPairs[i] = row.PrimaryKeyVals() - } - return bob.ExpressSlice(ctx, w, d, start, pkPairs, "", ", ", "") - })) -} - -// copyMatchingRows finds models in the given slice that have the same primary key -// then it first copies the existing relationships from the old model to the new model -// and then replaces the old model in the slice with the new model -func (o ItemSlice) copyMatchingRows(from ...*Item) { - for i, old := range o { - for _, new := range from { - if new.ID != old.ID { - continue - } - new.R = old.R - o[i] = new - break - } - } -} - -// UpdateMod modifies an update query with "WHERE primary_key IN (o...)" -func (o ItemSlice) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return bob.ModFunc[*dialect.UpdateQuery](func(q *dialect.UpdateQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Items.BeforeUpdateHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *Item: - o.copyMatchingRows(retrieved) - case []*Item: - o.copyMatchingRows(retrieved...) - case ItemSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a Item or a slice of Item - // then run the AfterUpdateHooks on the slice - _, err = Items.AfterUpdateHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -// DeleteMod modifies an delete query with "WHERE primary_key IN (o...)" -func (o ItemSlice) DeleteMod() bob.Mod[*dialect.DeleteQuery] { - return bob.ModFunc[*dialect.DeleteQuery](func(q *dialect.DeleteQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Items.BeforeDeleteHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *Item: - o.copyMatchingRows(retrieved) - case []*Item: - o.copyMatchingRows(retrieved...) - case ItemSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a Item or a slice of Item - // then run the AfterDeleteHooks on the slice - _, err = Items.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -func (o ItemSlice) UpdateAll(ctx context.Context, exec bob.Executor, vals ItemSetter) error { - if len(o) == 0 { - return nil - } - - _, err := Items.Update(vals.UpdateMod(), o.UpdateMod()).All(ctx, exec) - return err -} - -func (o ItemSlice) DeleteAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - _, err := Items.Delete(o.DeleteMod()).Exec(ctx, exec) - return err -} - -func (o ItemSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - o2, err := Items.Query(sm.Where(o.pkIN())).All(ctx, exec) - if err != nil { - return err - } - - o.copyMatchingRows(o2...) - - return nil -} - -type itemJoins[Q dialect.Joinable] struct { - typ string - User func(context.Context) modAs[Q, userColumns] -} - -func (j itemJoins[Q]) aliasedAs(alias string) itemJoins[Q] { - return buildItemJoins[Q](buildItemColumns(alias), j.typ) -} - -func buildItemJoins[Q dialect.Joinable](cols itemColumns, typ string) itemJoins[Q] { - return itemJoins[Q]{ - typ: typ, - User: itemsJoinUser[Q](cols, typ), - } -} - -func itemsJoinUser[Q dialect.Joinable](from itemColumns, typ string) func(context.Context) modAs[Q, userColumns] { - return func(ctx context.Context) modAs[Q, userColumns] { - return modAs[Q, userColumns]{ - c: UserColumns, - f: func(to userColumns) bob.Mod[Q] { - mods := make(mods.QueryMods[Q], 0, 1) - - { - mods = append(mods, dialect.Join[Q](typ, Users.Name().As(to.Alias())).On( - to.ID.EQ(from.UserID), - )) - } - - return mods - }, - } - } -} - -// User starts a query for related objects on user -func (o *Item) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { - return Users.Query(append(mods, - sm.Where(UserColumns.ID.EQ(sqlite.Arg(o.UserID))), - )...) -} - -func (os ItemSlice) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery { - PKArgs := make([]bob.Expression, len(os)) - for i, o := range os { - PKArgs[i] = sqlite.ArgGroup(o.UserID) - } - - return Users.Query(append(mods, - sm.Where(sqlite.Group(UserColumns.ID).In(PKArgs...)), - )...) -} - -func (o *Item) Preload(name string, retrieved any) error { - if o == nil { - return nil - } - - switch name { - case "User": - rel, ok := retrieved.(*User) - if !ok { - return fmt.Errorf("item cannot load %T as %q", retrieved, name) - } - - o.R.User = rel - - if rel != nil { - rel.R.Items = ItemSlice{o} - } - return nil - default: - return fmt.Errorf("item has no relationship %q", name) - } -} - -func PreloadItemUser(opts ...sqlite.PreloadOption) sqlite.Preloader { - return sqlite.Preload[*User, UserSlice](orm.Relationship{ - Name: "User", - Sides: []orm.RelSide{ - { - From: TableNames.Items, - To: TableNames.Users, - FromColumns: []string{ - ColumnNames.Items.UserID, - }, - ToColumns: []string{ - ColumnNames.Users.ID, - }, - }, - }, - }, Users.Columns().Names(), opts...) -} - -func ThenLoadItemUser(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { - return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { - loader, isLoader := retrieved.(interface { - LoadItemUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error - }) - if !isLoader { - return fmt.Errorf("object %T cannot load ItemUser", retrieved) - } - - err := loader.LoadItemUser(ctx, exec, queryMods...) - - // Don't cause an issue due to missing relationships - if errors.Is(err, sql.ErrNoRows) { - return nil - } - - return err - }) -} - -// LoadItemUser loads the item's User into the .R struct -func (o *Item) LoadItemUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if o == nil { - return nil - } - - // Reset the relationship - o.R.User = nil - - related, err := o.User(mods...).One(ctx, exec) - if err != nil { - return err - } - - related.R.Items = ItemSlice{o} - - o.R.User = related - return nil -} - -// LoadItemUser loads the item's User into the .R struct -func (os ItemSlice) LoadItemUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if len(os) == 0 { - return nil - } - - users, err := os.User(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, o := range os { - for _, rel := range users { - if o.UserID != rel.ID { - continue - } - - rel.R.Items = append(rel.R.Items, o) - - o.R.User = rel - break - } - } - - return nil -} - -func attachItemUser0(ctx context.Context, exec bob.Executor, count int, item0 *Item, user1 *User) (*Item, error) { - setter := &ItemSetter{ - UserID: omit.From(user1.ID), - } - - err := item0.Update(ctx, exec, setter) - if err != nil { - return nil, fmt.Errorf("attachItemUser0: %w", err) - } - - return item0, nil -} - -func (item0 *Item) InsertUser(ctx context.Context, exec bob.Executor, related *UserSetter) error { - user1, err := Users.Insert(related).One(ctx, exec) - if err != nil { - return fmt.Errorf("inserting related objects: %w", err) - } - - _, err = attachItemUser0(ctx, exec, 1, item0, user1) - if err != nil { - return err - } - - item0.R.User = user1 - - user1.R.Items = append(user1.R.Items, item0) - - return nil -} - -func (item0 *Item) AttachUser(ctx context.Context, exec bob.Executor, user1 *User) error { - var err error - - _, err = attachItemUser0(ctx, exec, 1, item0, user1) - if err != nil { - return err - } - - item0.R.User = user1 - - user1.R.Items = append(user1.R.Items, item0) - - return nil -} diff --git a/server/internal/models/schema_migrations.bob.go b/server/internal/models/schema_migrations.bob.go deleted file mode 100644 index 6614148..0000000 --- a/server/internal/models/schema_migrations.bob.go +++ /dev/null @@ -1,361 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package models - -import ( - "context" - "io" - - "github.com/aarondl/opt/omit" - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/dialect/sqlite" - "github.com/stephenafamo/bob/dialect/sqlite/dialect" - "github.com/stephenafamo/bob/dialect/sqlite/dm" - "github.com/stephenafamo/bob/dialect/sqlite/sm" - "github.com/stephenafamo/bob/dialect/sqlite/um" - "github.com/stephenafamo/bob/expr" -) - -// SchemaMigration is an object representing the database table. -type SchemaMigration struct { - Version string `db:"version,pk" ` -} - -// SchemaMigrationSlice is an alias for a slice of pointers to SchemaMigration. -// This should almost always be used instead of []*SchemaMigration. -type SchemaMigrationSlice []*SchemaMigration - -// SchemaMigrations contains methods to work with the schema_migrations table -var SchemaMigrations = sqlite.NewTablex[*SchemaMigration, SchemaMigrationSlice, *SchemaMigrationSetter]("", "schema_migrations") - -// SchemaMigrationsQuery is a query on the schema_migrations table -type SchemaMigrationsQuery = *sqlite.ViewQuery[*SchemaMigration, SchemaMigrationSlice] - -type schemaMigrationColumnNames struct { - Version string -} - -var SchemaMigrationColumns = buildSchemaMigrationColumns("schema_migrations") - -type schemaMigrationColumns struct { - tableAlias string - Version sqlite.Expression -} - -func (c schemaMigrationColumns) Alias() string { - return c.tableAlias -} - -func (schemaMigrationColumns) AliasedAs(alias string) schemaMigrationColumns { - return buildSchemaMigrationColumns(alias) -} - -func buildSchemaMigrationColumns(alias string) schemaMigrationColumns { - return schemaMigrationColumns{ - tableAlias: alias, - Version: sqlite.Quote(alias, "version"), - } -} - -type schemaMigrationWhere[Q sqlite.Filterable] struct { - Version sqlite.WhereMod[Q, string] -} - -func (schemaMigrationWhere[Q]) AliasedAs(alias string) schemaMigrationWhere[Q] { - return buildSchemaMigrationWhere[Q](buildSchemaMigrationColumns(alias)) -} - -func buildSchemaMigrationWhere[Q sqlite.Filterable](cols schemaMigrationColumns) schemaMigrationWhere[Q] { - return schemaMigrationWhere[Q]{ - Version: sqlite.Where[Q, string](cols.Version), - } -} - -var SchemaMigrationErrors = &schemaMigrationErrors{ - ErrUniqueSqliteAutoindexSchemaMigrations1: &UniqueConstraintError{s: "sqlite_autoindex_schema_migrations_1"}, -} - -type schemaMigrationErrors struct { - ErrUniqueSqliteAutoindexSchemaMigrations1 *UniqueConstraintError -} - -// SchemaMigrationSetter is used for insert/upsert/update operations -// All values are optional, and do not have to be set -// Generated columns are not included -type SchemaMigrationSetter struct { - Version omit.Val[string] `db:"version,pk" ` -} - -func (s SchemaMigrationSetter) SetColumns() []string { - vals := make([]string, 0, 1) - if !s.Version.IsUnset() { - vals = append(vals, "version") - } - - return vals -} - -func (s SchemaMigrationSetter) Overwrite(t *SchemaMigration) { - if !s.Version.IsUnset() { - t.Version, _ = s.Version.Get() - } -} - -func (s *SchemaMigrationSetter) Apply(q *dialect.InsertQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return SchemaMigrations.BeforeInsertHooks.RunHooks(ctx, exec, s) - }) - - if len(q.Table.Columns) == 0 { - q.Table.Columns = s.SetColumns() - } - - q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 0, 1) - if !s.Version.IsUnset() { - vals = append(vals, sqlite.Arg(s.Version)) - } - - return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") - })) -} - -func (s SchemaMigrationSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return um.Set(s.Expressions()...) -} - -func (s SchemaMigrationSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 1) - - if !s.Version.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "version")...), - sqlite.Arg(s.Version), - }}) - } - - return exprs -} - -// FindSchemaMigration retrieves a single record by primary key -// If cols is empty Find will return all columns. -func FindSchemaMigration(ctx context.Context, exec bob.Executor, VersionPK string, cols ...string) (*SchemaMigration, error) { - if len(cols) == 0 { - return SchemaMigrations.Query( - SelectWhere.SchemaMigrations.Version.EQ(VersionPK), - ).One(ctx, exec) - } - - return SchemaMigrations.Query( - SelectWhere.SchemaMigrations.Version.EQ(VersionPK), - sm.Columns(SchemaMigrations.Columns().Only(cols...)), - ).One(ctx, exec) -} - -// SchemaMigrationExists checks the presence of a single record by primary key -func SchemaMigrationExists(ctx context.Context, exec bob.Executor, VersionPK string) (bool, error) { - return SchemaMigrations.Query( - SelectWhere.SchemaMigrations.Version.EQ(VersionPK), - ).Exists(ctx, exec) -} - -// AfterQueryHook is called after SchemaMigration is retrieved from the database -func (o *SchemaMigration) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = SchemaMigrations.AfterSelectHooks.RunHooks(ctx, exec, SchemaMigrationSlice{o}) - case bob.QueryTypeInsert: - ctx, err = SchemaMigrations.AfterInsertHooks.RunHooks(ctx, exec, SchemaMigrationSlice{o}) - case bob.QueryTypeUpdate: - ctx, err = SchemaMigrations.AfterUpdateHooks.RunHooks(ctx, exec, SchemaMigrationSlice{o}) - case bob.QueryTypeDelete: - ctx, err = SchemaMigrations.AfterDeleteHooks.RunHooks(ctx, exec, SchemaMigrationSlice{o}) - } - - return err -} - -// PrimaryKeyVals returns the primary key values of the SchemaMigration -func (o *SchemaMigration) PrimaryKeyVals() bob.Expression { - return sqlite.Arg(o.Version) -} - -func (o *SchemaMigration) pkEQ() dialect.Expression { - return sqlite.Quote("schema_migrations", "version").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) - })) -} - -// Update uses an executor to update the SchemaMigration -func (o *SchemaMigration) Update(ctx context.Context, exec bob.Executor, s *SchemaMigrationSetter) error { - v, err := SchemaMigrations.Update(s.UpdateMod(), um.Where(o.pkEQ())).One(ctx, exec) - if err != nil { - return err - } - - *o = *v - - return nil -} - -// Delete deletes a single SchemaMigration record with an executor -func (o *SchemaMigration) Delete(ctx context.Context, exec bob.Executor) error { - _, err := SchemaMigrations.Delete(dm.Where(o.pkEQ())).Exec(ctx, exec) - return err -} - -// Reload refreshes the SchemaMigration using the executor -func (o *SchemaMigration) Reload(ctx context.Context, exec bob.Executor) error { - o2, err := SchemaMigrations.Query( - SelectWhere.SchemaMigrations.Version.EQ(o.Version), - ).One(ctx, exec) - if err != nil { - return err - } - - *o = *o2 - - return nil -} - -// AfterQueryHook is called after SchemaMigrationSlice is retrieved from the database -func (o SchemaMigrationSlice) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = SchemaMigrations.AfterSelectHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeInsert: - ctx, err = SchemaMigrations.AfterInsertHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeUpdate: - ctx, err = SchemaMigrations.AfterUpdateHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeDelete: - ctx, err = SchemaMigrations.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err -} - -func (o SchemaMigrationSlice) pkIN() dialect.Expression { - if len(o) == 0 { - return sqlite.Raw("NULL") - } - - return sqlite.Quote("schema_migrations", "version").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - pkPairs := make([]bob.Expression, len(o)) - for i, row := range o { - pkPairs[i] = row.PrimaryKeyVals() - } - return bob.ExpressSlice(ctx, w, d, start, pkPairs, "", ", ", "") - })) -} - -// copyMatchingRows finds models in the given slice that have the same primary key -// then it first copies the existing relationships from the old model to the new model -// and then replaces the old model in the slice with the new model -func (o SchemaMigrationSlice) copyMatchingRows(from ...*SchemaMigration) { - for i, old := range o { - for _, new := range from { - if new.Version != old.Version { - continue - } - - o[i] = new - break - } - } -} - -// UpdateMod modifies an update query with "WHERE primary_key IN (o...)" -func (o SchemaMigrationSlice) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return bob.ModFunc[*dialect.UpdateQuery](func(q *dialect.UpdateQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return SchemaMigrations.BeforeUpdateHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *SchemaMigration: - o.copyMatchingRows(retrieved) - case []*SchemaMigration: - o.copyMatchingRows(retrieved...) - case SchemaMigrationSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a SchemaMigration or a slice of SchemaMigration - // then run the AfterUpdateHooks on the slice - _, err = SchemaMigrations.AfterUpdateHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -// DeleteMod modifies an delete query with "WHERE primary_key IN (o...)" -func (o SchemaMigrationSlice) DeleteMod() bob.Mod[*dialect.DeleteQuery] { - return bob.ModFunc[*dialect.DeleteQuery](func(q *dialect.DeleteQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return SchemaMigrations.BeforeDeleteHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *SchemaMigration: - o.copyMatchingRows(retrieved) - case []*SchemaMigration: - o.copyMatchingRows(retrieved...) - case SchemaMigrationSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a SchemaMigration or a slice of SchemaMigration - // then run the AfterDeleteHooks on the slice - _, err = SchemaMigrations.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -func (o SchemaMigrationSlice) UpdateAll(ctx context.Context, exec bob.Executor, vals SchemaMigrationSetter) error { - if len(o) == 0 { - return nil - } - - _, err := SchemaMigrations.Update(vals.UpdateMod(), o.UpdateMod()).All(ctx, exec) - return err -} - -func (o SchemaMigrationSlice) DeleteAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - _, err := SchemaMigrations.Delete(o.DeleteMod()).Exec(ctx, exec) - return err -} - -func (o SchemaMigrationSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - o2, err := SchemaMigrations.Query(sm.Where(o.pkIN())).All(ctx, exec) - if err != nil { - return err - } - - o.copyMatchingRows(o2...) - - return nil -} diff --git a/server/internal/models/user.bob.go b/server/internal/models/user.bob.go deleted file mode 100644 index 96e9ca9..0000000 --- a/server/internal/models/user.bob.go +++ /dev/null @@ -1,1046 +0,0 @@ -// Code generated by BobGen sql (devel). DO NOT EDIT. -// This file is meant to be re-generated in place and/or deleted at any time. - -package models - -import ( - "context" - "database/sql" - "errors" - "fmt" - "io" - - "github.com/aarondl/opt/null" - "github.com/aarondl/opt/omit" - "github.com/aarondl/opt/omitnull" - "github.com/stephenafamo/bob" - "github.com/stephenafamo/bob/dialect/sqlite" - "github.com/stephenafamo/bob/dialect/sqlite/dialect" - "github.com/stephenafamo/bob/dialect/sqlite/dm" - "github.com/stephenafamo/bob/dialect/sqlite/sm" - "github.com/stephenafamo/bob/dialect/sqlite/um" - "github.com/stephenafamo/bob/expr" - "github.com/stephenafamo/bob/mods" - "github.com/stephenafamo/bob/orm" -) - -// User is an object representing the database table. -type User struct { - ID int64 `db:"id,pk" ` - Username string `db:"username" ` - Password string `db:"password" ` - ProfilePictureID null.Val[int64] `db:"profile_picture_id" ` - - R userR `db:"-" ` -} - -// UserSlice is an alias for a slice of pointers to User. -// This should almost always be used instead of []*User. -type UserSlice []*User - -// Users contains methods to work with the user table -var Users = sqlite.NewTablex[*User, UserSlice, *UserSetter]("", "user") - -// UsersQuery is a query on the user table -type UsersQuery = *sqlite.ViewQuery[*User, UserSlice] - -// userR is where relationships are stored. -type userR struct { - Files FileSlice // fk_file_0 - Items ItemSlice // fk_item_0 - ProfilePictureFile *File // fk_user_0 -} - -type userColumnNames struct { - ID string - Username string - Password string - ProfilePictureID string -} - -var UserColumns = buildUserColumns("user") - -type userColumns struct { - tableAlias string - ID sqlite.Expression - Username sqlite.Expression - Password sqlite.Expression - ProfilePictureID sqlite.Expression -} - -func (c userColumns) Alias() string { - return c.tableAlias -} - -func (userColumns) AliasedAs(alias string) userColumns { - return buildUserColumns(alias) -} - -func buildUserColumns(alias string) userColumns { - return userColumns{ - tableAlias: alias, - ID: sqlite.Quote(alias, "id"), - Username: sqlite.Quote(alias, "username"), - Password: sqlite.Quote(alias, "password"), - ProfilePictureID: sqlite.Quote(alias, "profile_picture_id"), - } -} - -type userWhere[Q sqlite.Filterable] struct { - ID sqlite.WhereMod[Q, int64] - Username sqlite.WhereMod[Q, string] - Password sqlite.WhereMod[Q, string] - ProfilePictureID sqlite.WhereNullMod[Q, int64] -} - -func (userWhere[Q]) AliasedAs(alias string) userWhere[Q] { - return buildUserWhere[Q](buildUserColumns(alias)) -} - -func buildUserWhere[Q sqlite.Filterable](cols userColumns) userWhere[Q] { - return userWhere[Q]{ - ID: sqlite.Where[Q, int64](cols.ID), - Username: sqlite.Where[Q, string](cols.Username), - Password: sqlite.Where[Q, string](cols.Password), - ProfilePictureID: sqlite.WhereNull[Q, int64](cols.ProfilePictureID), - } -} - -var UserErrors = &userErrors{ - ErrUniquePkMainUser: &UniqueConstraintError{s: "pk_main_user"}, -} - -type userErrors struct { - ErrUniquePkMainUser *UniqueConstraintError -} - -// UserSetter is used for insert/upsert/update operations -// All values are optional, and do not have to be set -// Generated columns are not included -type UserSetter struct { - ID omit.Val[int64] `db:"id,pk" ` - Username omit.Val[string] `db:"username" ` - Password omit.Val[string] `db:"password" ` - ProfilePictureID omitnull.Val[int64] `db:"profile_picture_id" ` -} - -func (s UserSetter) SetColumns() []string { - vals := make([]string, 0, 4) - if !s.ID.IsUnset() { - vals = append(vals, "id") - } - - if !s.Username.IsUnset() { - vals = append(vals, "username") - } - - if !s.Password.IsUnset() { - vals = append(vals, "password") - } - - if !s.ProfilePictureID.IsUnset() { - vals = append(vals, "profile_picture_id") - } - - return vals -} - -func (s UserSetter) Overwrite(t *User) { - if !s.ID.IsUnset() { - t.ID, _ = s.ID.Get() - } - if !s.Username.IsUnset() { - t.Username, _ = s.Username.Get() - } - if !s.Password.IsUnset() { - t.Password, _ = s.Password.Get() - } - if !s.ProfilePictureID.IsUnset() { - t.ProfilePictureID, _ = s.ProfilePictureID.GetNull() - } -} - -func (s *UserSetter) Apply(q *dialect.InsertQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Users.BeforeInsertHooks.RunHooks(ctx, exec, s) - }) - - if len(q.Table.Columns) == 0 { - q.Table.Columns = s.SetColumns() - } - - q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 0, 4) - if !s.ID.IsUnset() { - vals = append(vals, sqlite.Arg(s.ID)) - } - - if !s.Username.IsUnset() { - vals = append(vals, sqlite.Arg(s.Username)) - } - - if !s.Password.IsUnset() { - vals = append(vals, sqlite.Arg(s.Password)) - } - - if !s.ProfilePictureID.IsUnset() { - vals = append(vals, sqlite.Arg(s.ProfilePictureID)) - } - - return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") - })) -} - -func (s UserSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return um.Set(s.Expressions()...) -} - -func (s UserSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 4) - - if !s.ID.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "id")...), - sqlite.Arg(s.ID), - }}) - } - - if !s.Username.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "username")...), - sqlite.Arg(s.Username), - }}) - } - - if !s.Password.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "password")...), - sqlite.Arg(s.Password), - }}) - } - - if !s.ProfilePictureID.IsUnset() { - exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ - sqlite.Quote(append(prefix, "profile_picture_id")...), - sqlite.Arg(s.ProfilePictureID), - }}) - } - - return exprs -} - -// FindUser retrieves a single record by primary key -// If cols is empty Find will return all columns. -func FindUser(ctx context.Context, exec bob.Executor, IDPK int64, cols ...string) (*User, error) { - if len(cols) == 0 { - return Users.Query( - SelectWhere.Users.ID.EQ(IDPK), - ).One(ctx, exec) - } - - return Users.Query( - SelectWhere.Users.ID.EQ(IDPK), - sm.Columns(Users.Columns().Only(cols...)), - ).One(ctx, exec) -} - -// UserExists checks the presence of a single record by primary key -func UserExists(ctx context.Context, exec bob.Executor, IDPK int64) (bool, error) { - return Users.Query( - SelectWhere.Users.ID.EQ(IDPK), - ).Exists(ctx, exec) -} - -// AfterQueryHook is called after User is retrieved from the database -func (o *User) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = Users.AfterSelectHooks.RunHooks(ctx, exec, UserSlice{o}) - case bob.QueryTypeInsert: - ctx, err = Users.AfterInsertHooks.RunHooks(ctx, exec, UserSlice{o}) - case bob.QueryTypeUpdate: - ctx, err = Users.AfterUpdateHooks.RunHooks(ctx, exec, UserSlice{o}) - case bob.QueryTypeDelete: - ctx, err = Users.AfterDeleteHooks.RunHooks(ctx, exec, UserSlice{o}) - } - - return err -} - -// PrimaryKeyVals returns the primary key values of the User -func (o *User) PrimaryKeyVals() bob.Expression { - return sqlite.Arg(o.ID) -} - -func (o *User) pkEQ() dialect.Expression { - return sqlite.Quote("user", "id").EQ(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - return o.PrimaryKeyVals().WriteSQL(ctx, w, d, start) - })) -} - -// Update uses an executor to update the User -func (o *User) Update(ctx context.Context, exec bob.Executor, s *UserSetter) error { - v, err := Users.Update(s.UpdateMod(), um.Where(o.pkEQ())).One(ctx, exec) - if err != nil { - return err - } - - o.R = v.R - *o = *v - - return nil -} - -// Delete deletes a single User record with an executor -func (o *User) Delete(ctx context.Context, exec bob.Executor) error { - _, err := Users.Delete(dm.Where(o.pkEQ())).Exec(ctx, exec) - return err -} - -// Reload refreshes the User using the executor -func (o *User) Reload(ctx context.Context, exec bob.Executor) error { - o2, err := Users.Query( - SelectWhere.Users.ID.EQ(o.ID), - ).One(ctx, exec) - if err != nil { - return err - } - o2.R = o.R - *o = *o2 - - return nil -} - -// AfterQueryHook is called after UserSlice is retrieved from the database -func (o UserSlice) AfterQueryHook(ctx context.Context, exec bob.Executor, queryType bob.QueryType) error { - var err error - - switch queryType { - case bob.QueryTypeSelect: - ctx, err = Users.AfterSelectHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeInsert: - ctx, err = Users.AfterInsertHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeUpdate: - ctx, err = Users.AfterUpdateHooks.RunHooks(ctx, exec, o) - case bob.QueryTypeDelete: - ctx, err = Users.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err -} - -func (o UserSlice) pkIN() dialect.Expression { - if len(o) == 0 { - return sqlite.Raw("NULL") - } - - return sqlite.Quote("user", "id").In(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - pkPairs := make([]bob.Expression, len(o)) - for i, row := range o { - pkPairs[i] = row.PrimaryKeyVals() - } - return bob.ExpressSlice(ctx, w, d, start, pkPairs, "", ", ", "") - })) -} - -// copyMatchingRows finds models in the given slice that have the same primary key -// then it first copies the existing relationships from the old model to the new model -// and then replaces the old model in the slice with the new model -func (o UserSlice) copyMatchingRows(from ...*User) { - for i, old := range o { - for _, new := range from { - if new.ID != old.ID { - continue - } - new.R = old.R - o[i] = new - break - } - } -} - -// UpdateMod modifies an update query with "WHERE primary_key IN (o...)" -func (o UserSlice) UpdateMod() bob.Mod[*dialect.UpdateQuery] { - return bob.ModFunc[*dialect.UpdateQuery](func(q *dialect.UpdateQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Users.BeforeUpdateHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *User: - o.copyMatchingRows(retrieved) - case []*User: - o.copyMatchingRows(retrieved...) - case UserSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a User or a slice of User - // then run the AfterUpdateHooks on the slice - _, err = Users.AfterUpdateHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -// DeleteMod modifies an delete query with "WHERE primary_key IN (o...)" -func (o UserSlice) DeleteMod() bob.Mod[*dialect.DeleteQuery] { - return bob.ModFunc[*dialect.DeleteQuery](func(q *dialect.DeleteQuery) { - q.AppendHooks(func(ctx context.Context, exec bob.Executor) (context.Context, error) { - return Users.BeforeDeleteHooks.RunHooks(ctx, exec, o) - }) - - q.AppendLoader(bob.LoaderFunc(func(ctx context.Context, exec bob.Executor, retrieved any) error { - var err error - switch retrieved := retrieved.(type) { - case *User: - o.copyMatchingRows(retrieved) - case []*User: - o.copyMatchingRows(retrieved...) - case UserSlice: - o.copyMatchingRows(retrieved...) - default: - // If the retrieved value is not a User or a slice of User - // then run the AfterDeleteHooks on the slice - _, err = Users.AfterDeleteHooks.RunHooks(ctx, exec, o) - } - - return err - })) - - q.AppendWhere(o.pkIN()) - }) -} - -func (o UserSlice) UpdateAll(ctx context.Context, exec bob.Executor, vals UserSetter) error { - if len(o) == 0 { - return nil - } - - _, err := Users.Update(vals.UpdateMod(), o.UpdateMod()).All(ctx, exec) - return err -} - -func (o UserSlice) DeleteAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - _, err := Users.Delete(o.DeleteMod()).Exec(ctx, exec) - return err -} - -func (o UserSlice) ReloadAll(ctx context.Context, exec bob.Executor) error { - if len(o) == 0 { - return nil - } - - o2, err := Users.Query(sm.Where(o.pkIN())).All(ctx, exec) - if err != nil { - return err - } - - o.copyMatchingRows(o2...) - - return nil -} - -type userJoins[Q dialect.Joinable] struct { - typ string - Files func(context.Context) modAs[Q, fileColumns] - Items func(context.Context) modAs[Q, itemColumns] - ProfilePictureFile func(context.Context) modAs[Q, fileColumns] -} - -func (j userJoins[Q]) aliasedAs(alias string) userJoins[Q] { - return buildUserJoins[Q](buildUserColumns(alias), j.typ) -} - -func buildUserJoins[Q dialect.Joinable](cols userColumns, typ string) userJoins[Q] { - return userJoins[Q]{ - typ: typ, - Files: usersJoinFiles[Q](cols, typ), - Items: usersJoinItems[Q](cols, typ), - ProfilePictureFile: usersJoinProfilePictureFile[Q](cols, typ), - } -} - -func usersJoinFiles[Q dialect.Joinable](from userColumns, typ string) func(context.Context) modAs[Q, fileColumns] { - return func(ctx context.Context) modAs[Q, fileColumns] { - return modAs[Q, fileColumns]{ - c: FileColumns, - f: func(to fileColumns) bob.Mod[Q] { - mods := make(mods.QueryMods[Q], 0, 1) - - { - mods = append(mods, dialect.Join[Q](typ, Files.Name().As(to.Alias())).On( - to.UserID.EQ(from.ID), - )) - } - - return mods - }, - } - } -} - -func usersJoinItems[Q dialect.Joinable](from userColumns, typ string) func(context.Context) modAs[Q, itemColumns] { - return func(ctx context.Context) modAs[Q, itemColumns] { - return modAs[Q, itemColumns]{ - c: ItemColumns, - f: func(to itemColumns) bob.Mod[Q] { - mods := make(mods.QueryMods[Q], 0, 1) - - { - mods = append(mods, dialect.Join[Q](typ, Items.Name().As(to.Alias())).On( - to.UserID.EQ(from.ID), - )) - } - - return mods - }, - } - } -} - -func usersJoinProfilePictureFile[Q dialect.Joinable](from userColumns, typ string) func(context.Context) modAs[Q, fileColumns] { - return func(ctx context.Context) modAs[Q, fileColumns] { - return modAs[Q, fileColumns]{ - c: FileColumns, - f: func(to fileColumns) bob.Mod[Q] { - mods := make(mods.QueryMods[Q], 0, 1) - - { - mods = append(mods, dialect.Join[Q](typ, Files.Name().As(to.Alias())).On( - to.ID.EQ(from.ProfilePictureID), - )) - } - - return mods - }, - } - } -} - -// Files starts a query for related objects on file -func (o *User) Files(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { - return Files.Query(append(mods, - sm.Where(FileColumns.UserID.EQ(sqlite.Arg(o.ID))), - )...) -} - -func (os UserSlice) Files(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { - PKArgs := make([]bob.Expression, len(os)) - for i, o := range os { - PKArgs[i] = sqlite.ArgGroup(o.ID) - } - - return Files.Query(append(mods, - sm.Where(sqlite.Group(FileColumns.UserID).In(PKArgs...)), - )...) -} - -// Items starts a query for related objects on item -func (o *User) Items(mods ...bob.Mod[*dialect.SelectQuery]) ItemsQuery { - return Items.Query(append(mods, - sm.Where(ItemColumns.UserID.EQ(sqlite.Arg(o.ID))), - )...) -} - -func (os UserSlice) Items(mods ...bob.Mod[*dialect.SelectQuery]) ItemsQuery { - PKArgs := make([]bob.Expression, len(os)) - for i, o := range os { - PKArgs[i] = sqlite.ArgGroup(o.ID) - } - - return Items.Query(append(mods, - sm.Where(sqlite.Group(ItemColumns.UserID).In(PKArgs...)), - )...) -} - -// ProfilePictureFile starts a query for related objects on file -func (o *User) ProfilePictureFile(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { - return Files.Query(append(mods, - sm.Where(FileColumns.ID.EQ(sqlite.Arg(o.ProfilePictureID))), - )...) -} - -func (os UserSlice) ProfilePictureFile(mods ...bob.Mod[*dialect.SelectQuery]) FilesQuery { - PKArgs := make([]bob.Expression, len(os)) - for i, o := range os { - PKArgs[i] = sqlite.ArgGroup(o.ProfilePictureID) - } - - return Files.Query(append(mods, - sm.Where(sqlite.Group(FileColumns.ID).In(PKArgs...)), - )...) -} - -func (o *User) Preload(name string, retrieved any) error { - if o == nil { - return nil - } - - switch name { - case "Files": - rels, ok := retrieved.(FileSlice) - if !ok { - return fmt.Errorf("user cannot load %T as %q", retrieved, name) - } - - o.R.Files = rels - - for _, rel := range rels { - if rel != nil { - rel.R.User = o - } - } - return nil - case "Items": - rels, ok := retrieved.(ItemSlice) - if !ok { - return fmt.Errorf("user cannot load %T as %q", retrieved, name) - } - - o.R.Items = rels - - for _, rel := range rels { - if rel != nil { - rel.R.User = o - } - } - return nil - case "ProfilePictureFile": - rel, ok := retrieved.(*File) - if !ok { - return fmt.Errorf("user cannot load %T as %q", retrieved, name) - } - - o.R.ProfilePictureFile = rel - - if rel != nil { - rel.R.ProfilePictureUsers = UserSlice{o} - } - return nil - default: - return fmt.Errorf("user has no relationship %q", name) - } -} - -func ThenLoadUserFiles(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { - return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { - loader, isLoader := retrieved.(interface { - LoadUserFiles(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error - }) - if !isLoader { - return fmt.Errorf("object %T cannot load UserFiles", retrieved) - } - - err := loader.LoadUserFiles(ctx, exec, queryMods...) - - // Don't cause an issue due to missing relationships - if errors.Is(err, sql.ErrNoRows) { - return nil - } - - return err - }) -} - -// LoadUserFiles loads the user's Files into the .R struct -func (o *User) LoadUserFiles(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if o == nil { - return nil - } - - // Reset the relationship - o.R.Files = nil - - related, err := o.Files(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, rel := range related { - rel.R.User = o - } - - o.R.Files = related - return nil -} - -// LoadUserFiles loads the user's Files into the .R struct -func (os UserSlice) LoadUserFiles(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if len(os) == 0 { - return nil - } - - files, err := os.Files(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, o := range os { - o.R.Files = nil - } - - for _, o := range os { - for _, rel := range files { - if o.ID != rel.UserID { - continue - } - - rel.R.User = o - - o.R.Files = append(o.R.Files, rel) - } - } - - return nil -} - -func ThenLoadUserItems(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { - return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { - loader, isLoader := retrieved.(interface { - LoadUserItems(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error - }) - if !isLoader { - return fmt.Errorf("object %T cannot load UserItems", retrieved) - } - - err := loader.LoadUserItems(ctx, exec, queryMods...) - - // Don't cause an issue due to missing relationships - if errors.Is(err, sql.ErrNoRows) { - return nil - } - - return err - }) -} - -// LoadUserItems loads the user's Items into the .R struct -func (o *User) LoadUserItems(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if o == nil { - return nil - } - - // Reset the relationship - o.R.Items = nil - - related, err := o.Items(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, rel := range related { - rel.R.User = o - } - - o.R.Items = related - return nil -} - -// LoadUserItems loads the user's Items into the .R struct -func (os UserSlice) LoadUserItems(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if len(os) == 0 { - return nil - } - - items, err := os.Items(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, o := range os { - o.R.Items = nil - } - - for _, o := range os { - for _, rel := range items { - if o.ID != rel.UserID { - continue - } - - rel.R.User = o - - o.R.Items = append(o.R.Items, rel) - } - } - - return nil -} - -func PreloadUserProfilePictureFile(opts ...sqlite.PreloadOption) sqlite.Preloader { - return sqlite.Preload[*File, FileSlice](orm.Relationship{ - Name: "ProfilePictureFile", - Sides: []orm.RelSide{ - { - From: TableNames.Users, - To: TableNames.Files, - FromColumns: []string{ - ColumnNames.Users.ProfilePictureID, - }, - ToColumns: []string{ - ColumnNames.Files.ID, - }, - }, - }, - }, Files.Columns().Names(), opts...) -} - -func ThenLoadUserProfilePictureFile(queryMods ...bob.Mod[*dialect.SelectQuery]) sqlite.Loader { - return sqlite.Loader(func(ctx context.Context, exec bob.Executor, retrieved any) error { - loader, isLoader := retrieved.(interface { - LoadUserProfilePictureFile(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error - }) - if !isLoader { - return fmt.Errorf("object %T cannot load UserProfilePictureFile", retrieved) - } - - err := loader.LoadUserProfilePictureFile(ctx, exec, queryMods...) - - // Don't cause an issue due to missing relationships - if errors.Is(err, sql.ErrNoRows) { - return nil - } - - return err - }) -} - -// LoadUserProfilePictureFile loads the user's ProfilePictureFile into the .R struct -func (o *User) LoadUserProfilePictureFile(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if o == nil { - return nil - } - - // Reset the relationship - o.R.ProfilePictureFile = nil - - related, err := o.ProfilePictureFile(mods...).One(ctx, exec) - if err != nil { - return err - } - - related.R.ProfilePictureUsers = UserSlice{o} - - o.R.ProfilePictureFile = related - return nil -} - -// LoadUserProfilePictureFile loads the user's ProfilePictureFile into the .R struct -func (os UserSlice) LoadUserProfilePictureFile(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error { - if len(os) == 0 { - return nil - } - - files, err := os.ProfilePictureFile(mods...).All(ctx, exec) - if err != nil { - return err - } - - for _, o := range os { - for _, rel := range files { - if o.ProfilePictureID.GetOrZero() != rel.ID { - continue - } - - rel.R.ProfilePictureUsers = append(rel.R.ProfilePictureUsers, o) - - o.R.ProfilePictureFile = rel - break - } - } - - return nil -} - -func insertUserFiles0(ctx context.Context, exec bob.Executor, files1 []*FileSetter, user0 *User) (FileSlice, error) { - for i := range files1 { - files1[i].UserID = omit.From(user0.ID) - } - - ret, err := Files.Insert(bob.ToMods(files1...)).All(ctx, exec) - if err != nil { - return ret, fmt.Errorf("insertUserFiles0: %w", err) - } - - return ret, nil -} - -func attachUserFiles0(ctx context.Context, exec bob.Executor, count int, files1 FileSlice, user0 *User) (FileSlice, error) { - setter := &FileSetter{ - UserID: omit.From(user0.ID), - } - - err := files1.UpdateAll(ctx, exec, *setter) - if err != nil { - return nil, fmt.Errorf("attachUserFiles0: %w", err) - } - - return files1, nil -} - -func (user0 *User) InsertFiles(ctx context.Context, exec bob.Executor, related ...*FileSetter) error { - if len(related) == 0 { - return nil - } - - var err error - - files1, err := insertUserFiles0(ctx, exec, related, user0) - if err != nil { - return err - } - - user0.R.Files = append(user0.R.Files, files1...) - - for _, rel := range files1 { - rel.R.User = user0 - } - return nil -} - -func (user0 *User) AttachFiles(ctx context.Context, exec bob.Executor, related ...*File) error { - if len(related) == 0 { - return nil - } - - var err error - files1 := FileSlice(related) - - _, err = attachUserFiles0(ctx, exec, len(related), files1, user0) - if err != nil { - return err - } - - user0.R.Files = append(user0.R.Files, files1...) - - for _, rel := range related { - rel.R.User = user0 - } - - return nil -} - -func insertUserItems0(ctx context.Context, exec bob.Executor, items1 []*ItemSetter, user0 *User) (ItemSlice, error) { - for i := range items1 { - items1[i].UserID = omit.From(user0.ID) - } - - ret, err := Items.Insert(bob.ToMods(items1...)).All(ctx, exec) - if err != nil { - return ret, fmt.Errorf("insertUserItems0: %w", err) - } - - return ret, nil -} - -func attachUserItems0(ctx context.Context, exec bob.Executor, count int, items1 ItemSlice, user0 *User) (ItemSlice, error) { - setter := &ItemSetter{ - UserID: omit.From(user0.ID), - } - - err := items1.UpdateAll(ctx, exec, *setter) - if err != nil { - return nil, fmt.Errorf("attachUserItems0: %w", err) - } - - return items1, nil -} - -func (user0 *User) InsertItems(ctx context.Context, exec bob.Executor, related ...*ItemSetter) error { - if len(related) == 0 { - return nil - } - - var err error - - items1, err := insertUserItems0(ctx, exec, related, user0) - if err != nil { - return err - } - - user0.R.Items = append(user0.R.Items, items1...) - - for _, rel := range items1 { - rel.R.User = user0 - } - return nil -} - -func (user0 *User) AttachItems(ctx context.Context, exec bob.Executor, related ...*Item) error { - if len(related) == 0 { - return nil - } - - var err error - items1 := ItemSlice(related) - - _, err = attachUserItems0(ctx, exec, len(related), items1, user0) - if err != nil { - return err - } - - user0.R.Items = append(user0.R.Items, items1...) - - for _, rel := range related { - rel.R.User = user0 - } - - return nil -} - -func attachUserProfilePictureFile0(ctx context.Context, exec bob.Executor, count int, user0 *User, file1 *File) (*User, error) { - setter := &UserSetter{ - ProfilePictureID: omitnull.From(file1.ID), - } - - err := user0.Update(ctx, exec, setter) - if err != nil { - return nil, fmt.Errorf("attachUserProfilePictureFile0: %w", err) - } - - return user0, nil -} - -func (user0 *User) InsertProfilePictureFile(ctx context.Context, exec bob.Executor, related *FileSetter) error { - file1, err := Files.Insert(related).One(ctx, exec) - if err != nil { - return fmt.Errorf("inserting related objects: %w", err) - } - - _, err = attachUserProfilePictureFile0(ctx, exec, 1, user0, file1) - if err != nil { - return err - } - - user0.R.ProfilePictureFile = file1 - - file1.R.ProfilePictureUsers = append(file1.R.ProfilePictureUsers, user0) - - return nil -} - -func (user0 *User) AttachProfilePictureFile(ctx context.Context, exec bob.Executor, file1 *File) error { - var err error - - _, err = attachUserProfilePictureFile0(ctx, exec, 1, user0, file1) - if err != nil { - return err - } - - user0.R.ProfilePictureFile = file1 - - file1.R.ProfilePictureUsers = append(file1.R.ProfilePictureUsers, user0) - - return nil -} diff --git a/server/internal/sqlc/db.go b/server/internal/sqlc/db.go new file mode 100644 index 0000000..8277a70 --- /dev/null +++ b/server/internal/sqlc/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 + +package sqlc + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/server/internal/sqlc/file.sql.go b/server/internal/sqlc/file.sql.go new file mode 100644 index 0000000..843e345 --- /dev/null +++ b/server/internal/sqlc/file.sql.go @@ -0,0 +1,113 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 +// source: file.sql + +package sqlc + +import ( + "context" +) + +const deleteFile = `-- name: DeleteFile :exec +DELETE FROM file +WHERE + id = ?1 + AND + user_id = ?2 +` + +type DeleteFileParams struct { + ID int64 + UserID int64 +} + +func (q *Queries) DeleteFile(ctx context.Context, arg DeleteFileParams) error { + _, err := q.db.ExecContext(ctx, deleteFile, arg.ID, arg.UserID) + return err +} + +const getFile = `-- name: GetFile :one +SELECT + id, + name, + data, + user_id +FROM file +WHERE + id = ?1 + AND + user_id = ?2 +LIMIT 1 +` + +type GetFileParams struct { + ID int64 + UserID int64 +} + +func (q *Queries) GetFile(ctx context.Context, arg GetFileParams) (File, error) { + row := q.db.QueryRowContext(ctx, getFile, arg.ID, arg.UserID) + var i File + err := row.Scan( + &i.ID, + &i.Name, + &i.Data, + &i.UserID, + ) + return i, err +} + +const insertFile = `-- name: InsertFile :one +INSERT INTO file ( + name, + data, + user_id +) VALUES ( + ?1, + ?2, + ?3 +) +RETURNING id +` + +type InsertFileParams struct { + Name string + Data []byte + UserID int64 +} + +func (q *Queries) InsertFile(ctx context.Context, arg InsertFileParams) (int64, error) { + row := q.db.QueryRowContext(ctx, insertFile, arg.Name, arg.Data, arg.UserID) + var id int64 + err := row.Scan(&id) + return id, err +} + +const updateFile = `-- name: UpdateFile :exec +UPDATE file +SET + name = COALESCE(?1, name), + data = COALESCE(?2, data) +WHERE + id = ?3 + AND + user_id = ?4 +` + +type UpdateFileParams struct { + Name *string + Data []byte + ID int64 + UserID int64 +} + +func (q *Queries) UpdateFile(ctx context.Context, arg UpdateFileParams) error { + _, err := q.db.ExecContext(ctx, updateFile, + arg.Name, + arg.Data, + arg.ID, + arg.UserID, + ) + return err +} diff --git a/server/internal/sqlc/item.sql.go b/server/internal/sqlc/item.sql.go new file mode 100644 index 0000000..6074ad8 --- /dev/null +++ b/server/internal/sqlc/item.sql.go @@ -0,0 +1,250 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 +// source: item.sql + +package sqlc + +import ( + "context" + "time" +) + +const deleteItem = `-- name: DeleteItem :exec +DELETE FROM item +WHERE + id = ?1 + AND + user_id = ?2 +` + +type DeleteItemParams struct { + ID int64 + UserID int64 +} + +func (q *Queries) DeleteItem(ctx context.Context, arg DeleteItemParams) error { + _, err := q.db.ExecContext(ctx, deleteItem, arg.ID, arg.UserID) + return err +} + +const getItem = `-- name: GetItem :one +SELECT + id, + name, + added, + description, + price, + quantity, + user_id +FROM item +WHERE + id = ?1 + AND + user_id = ?2 +LIMIT 1 +` + +type GetItemParams struct { + ID int64 + UserID int64 +} + +func (q *Queries) GetItem(ctx context.Context, arg GetItemParams) (Item, error) { + row := q.db.QueryRowContext(ctx, getItem, arg.ID, arg.UserID) + var i Item + err := row.Scan( + &i.ID, + &i.Name, + &i.Added, + &i.Description, + &i.Price, + &i.Quantity, + &i.UserID, + ) + return i, err +} + +const getItems = `-- name: GetItems :many +SELECT + id, + name, + added, + description, + price, + quantity, + user_id +FROM item +WHERE + user_id = ?1 + AND + ( + (name LIKE ?2 OR ?2 IS NULL) + AND + (added >= ?3 OR ?3 IS NULL) + AND + (added <= ?4 OR ?4 IS NULL) + ) +LIMIT + ?6 + OFFSET + ?5 +` + +type GetItemsParams struct { + UserID int64 + Name *string + Start *time.Time + End *time.Time + Offset int64 + Limit int64 +} + +func (q *Queries) GetItems(ctx context.Context, arg GetItemsParams) ([]Item, error) { + rows, err := q.db.QueryContext(ctx, getItems, + arg.UserID, + arg.Name, + arg.Start, + arg.End, + arg.Offset, + arg.Limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Item + for rows.Next() { + var i Item + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Added, + &i.Description, + &i.Price, + &i.Quantity, + &i.UserID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getItemsCount = `-- name: GetItemsCount :one +SELECT COUNT(id) +FROM item +WHERE + user_id = ?1 + AND + ( + (name LIKE ?2 OR ?2 IS NULL) + AND + (added >= ?3 OR ?3 IS NULL) + AND + (added <= ?4 OR ?4 IS NULL) + ) +LIMIT 1 +` + +type GetItemsCountParams struct { + UserID int64 + Name *string + Start *time.Time + End *time.Time +} + +func (q *Queries) GetItemsCount(ctx context.Context, arg GetItemsCountParams) (int64, error) { + row := q.db.QueryRowContext(ctx, getItemsCount, + arg.UserID, + arg.Name, + arg.Start, + arg.End, + ) + var count int64 + err := row.Scan(&count) + return count, err +} + +const insertItem = `-- name: InsertItem :one +INSERT INTO item ( + name, + added, + description, + price, + quantity, + user_id +) VALUES ( + ?1, + ?2, + ?3, + ?4, + ?5, + ?6 +) +RETURNING id +` + +type InsertItemParams struct { + Name string + Added time.Time + Description string + Price float64 + Quantity int64 + UserID int64 +} + +func (q *Queries) InsertItem(ctx context.Context, arg InsertItemParams) (int64, error) { + row := q.db.QueryRowContext(ctx, insertItem, + arg.Name, + arg.Added, + arg.Description, + arg.Price, + arg.Quantity, + arg.UserID, + ) + var id int64 + err := row.Scan(&id) + return id, err +} + +const updateItem = `-- name: UpdateItem :exec +UPDATE item +SET + name = COALESCE(?1, name), + description = COALESCE(?2, description), + price = COALESCE(?3, price), + quantity = COALESCE(?4, quantity) +WHERE + id = ?5 + AND + user_id = ?6 +` + +type UpdateItemParams struct { + Name *string + Description *string + Price *float64 + Quantity *int64 + ID int64 + UserID int64 +} + +func (q *Queries) UpdateItem(ctx context.Context, arg UpdateItemParams) error { + _, err := q.db.ExecContext(ctx, updateItem, + arg.Name, + arg.Description, + arg.Price, + arg.Quantity, + arg.ID, + arg.UserID, + ) + return err +} diff --git a/server/internal/sqlc/models.go b/server/internal/sqlc/models.go new file mode 100644 index 0000000..d5cbf77 --- /dev/null +++ b/server/internal/sqlc/models.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 + +package sqlc + +import ( + "time" +) + +type File struct { + ID int64 + Name string + Data []byte + UserID int64 +} + +type Item struct { + ID int64 + Name string + Added time.Time + Description string + Price float64 + Quantity int64 + UserID int64 +} + +type SchemaMigration struct { + Version string +} + +type User struct { + ID int64 + Username string + Password string + ProfilePictureID *int64 +} diff --git a/server/internal/sqlc/user.sql.go b/server/internal/sqlc/user.sql.go new file mode 100644 index 0000000..885c5cf --- /dev/null +++ b/server/internal/sqlc/user.sql.go @@ -0,0 +1,120 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 +// source: user.sql + +package sqlc + +import ( + "context" +) + +const deleteUser = `-- name: DeleteUser :exec +DELETE FROM user +WHERE id = ?1 +` + +func (q *Queries) DeleteUser(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteUser, id) + return err +} + +const getUser = `-- name: GetUser :one +SELECT + id, + username, + password, + profile_picture_id +FROM user +WHERE + id = ?1 +LIMIT 1 +` + +func (q *Queries) GetUser(ctx context.Context, id int64) (User, error) { + row := q.db.QueryRowContext(ctx, getUser, id) + var i User + err := row.Scan( + &i.ID, + &i.Username, + &i.Password, + &i.ProfilePictureID, + ) + return i, err +} + +const getUserbyUsername = `-- name: GetUserbyUsername :one +SELECT + id, + username, + password, + profile_picture_id +FROM user +WHERE + username = ?1 +LIMIT 1 +` + +func (q *Queries) GetUserbyUsername(ctx context.Context, username string) (User, error) { + row := q.db.QueryRowContext(ctx, getUserbyUsername, username) + var i User + err := row.Scan( + &i.ID, + &i.Username, + &i.Password, + &i.ProfilePictureID, + ) + return i, err +} + +const insertUser = `-- name: InsertUser :one +INSERT INTO user ( + username, + password +) VALUES ( + ?1, + ?2 +) +RETURNING id +` + +type InsertUserParams struct { + Username string + Password string +} + +func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (int64, error) { + row := q.db.QueryRowContext(ctx, insertUser, arg.Username, arg.Password) + var id int64 + err := row.Scan(&id) + return id, err +} + +const updateUser = `-- name: UpdateUser :exec +UPDATE user +SET + username = COALESCE(?1, username), + password = COALESCE(?2, password), + profile_picture_id = COALESCE( + ?3, + profile_picture_id + ) +WHERE id = ?4 +` + +type UpdateUserParams struct { + Username *string + Password *string + ProfilePictureID *int64 + ID int64 +} + +func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error { + _, err := q.db.ExecContext(ctx, updateUser, + arg.Username, + arg.Password, + arg.ProfilePictureID, + arg.ID, + ) + return err +} diff --git a/server/internal/util/pointers.go b/server/internal/util/pointers.go new file mode 100644 index 0000000..59ed55d --- /dev/null +++ b/server/internal/util/pointers.go @@ -0,0 +1,17 @@ +package util + +func DerefOrEmpty[T any](val *T) T { + if val == nil { + var empty T + return empty + } + return *val +} + +func IsNotNil[T any](val *T) bool { + return val != nil +} + +func ToPointer[T any](val T) *T { + return &val +} diff --git a/server/internal/util/sql.go b/server/internal/util/sql.go new file mode 100644 index 0000000..8055fc9 --- /dev/null +++ b/server/internal/util/sql.go @@ -0,0 +1,44 @@ +package util + +import ( + "fmt" + "time" + + "google.golang.org/protobuf/types/known/timestamppb" +) + +func NullLike(s *string) *string { + if s == nil { + return nil + } + + ts := fmt.Sprintf("%%%s%%", *s) + return &ts +} + +func NullTimestamp(ts *timestamppb.Timestamp) *time.Time { + if ts == nil { + return nil + } + + t := ts.AsTime() + return &t +} + +func NullFloat64(f *float32) *float64 { + if f == nil { + return nil + } + + f64 := float64(*f) + return &f64 +} + +func NullInt64(i *int32) *int64 { + if i == nil { + return nil + } + + i64 := int64(*i) + return &i64 +} diff --git a/server/main.go b/server/main.go index 684a0ce..7df1e90 100644 --- a/server/main.go +++ b/server/main.go @@ -9,15 +9,12 @@ import ( "log" "log/slog" "net/http" - "net/url" "os" "os/signal" - "strings" "syscall" "time" "github.com/joho/godotenv" - "github.com/stephenafamo/bob" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" @@ -33,7 +30,7 @@ var clientFS *embed.FS var dbFS *embed.FS func main() { - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{})) slog.SetDefault(logger) // Get env @@ -43,44 +40,27 @@ func main() { } // Migrate database - err = database.Migrate(env.DatabaseUrl, dbFS) + err = database.Migrate(env.DatabaseURL, dbFS) if err != nil { log.Fatal(err.Error()) } // Get database - db := &bob.DB{} - switch env.DatabaseType { - case "postgres": - log.Println("Using Postgres") - - db, err = database.NewPostgresConnection(env.DatabaseUrl) - if err != nil { - log.Fatalf("failed to connect to postgres: %v", err) - } - - case "sqlite", "sqlite3": - log.Println("Using SQLite") - - db, err = database.NewSQLiteConnection(env.DatabaseUrl) - if err != nil { - log.Fatalf("failed to connect to sqlite: %v", err) - } - - default: - log.Fatal("DB_TYPE must be either postgres or sqlite") + sqlc, db, err := database.New(env.DatabaseURL) + if err != nil { + log.Fatalf("failed to connect to database: %s", err.Error()) } // Serve GRPC Handlers api := http.NewServeMux() - api.Handle(interceptors.WithCORS(user.NewAuthHandler(db, env.Key))) - api.Handle(interceptors.WithCORS(user.NewHandler(db, env.Key))) - api.Handle(interceptors.WithCORS(item.NewHandler(db, env.Key))) + api.Handle(interceptors.WithCORS(user.NewAuthHandler(sqlc, env.Key))) + api.Handle(interceptors.WithCORS(user.NewHandler(sqlc, env.Key))) + api.Handle(interceptors.WithCORS(item.NewHandler(sqlc, env.Key))) // Serve web interface mux := http.NewServeMux() mux.Handle("/", client.NewClientHandler(env.Key, clientFS)) - mux.Handle("/file/", file.NewFileHandler(db, env.Key)) + mux.Handle("/file/", file.NewFileHandler(sqlc, env.Key)) mux.Handle("/grpc/", http.StripPrefix("/grpc", api)) // Start server @@ -114,10 +94,9 @@ func main() { } type env struct { - Port string - Key string - DatabaseType string - DatabaseUrl *url.URL + Port string + Key string + DatabaseURL string } func getEnv() (*env, error) { @@ -128,8 +107,9 @@ func getEnv() (*env, error) { // Create env := env{ - Port: os.Getenv("PORT"), - Key: os.Getenv("KEY"), + Port: os.Getenv("PORT"), + Key: os.Getenv("KEY"), + DatabaseURL: os.Getenv("DATABASE_URL"), } // Validate @@ -139,20 +119,9 @@ func getEnv() (*env, error) { if env.Key == "" { return nil, errors.New("env 'key' not found") } - - // Validate DATABASE_URL - dbstr := os.Getenv("DATABASE_URL") - if dbstr == "" { + if env.DatabaseURL == "" { return nil, errors.New("env 'DATABASE_URL' not found") } - dbsp := strings.Split(dbstr, ":") - dburl, err := url.Parse(dbstr) - if err != nil || len(dbsp) < 2 { - return nil, errors.New("env 'DATABASE_URL' formatted incorrectly") - } - env.DatabaseType = dbsp[0] - env.DatabaseUrl = dburl - return &env, nil } diff --git a/server/sqlc.yaml b/server/sqlc.yaml new file mode 100644 index 0000000..dc4593b --- /dev/null +++ b/server/sqlc.yaml @@ -0,0 +1,14 @@ +version: "2" +sql: + - engine: "sqlite" + queries: "db/queries" + schema: "db/schema.sql" + gen: + go: + package: "sqlc" + out: "internal/sqlc" + emit_pointers_for_null_types: true + database: + uri: ${DATABASE_URL} + rules: + - sqlc/db-prepare \ No newline at end of file