mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-24 22:39:14 +01:00
Compare commits
250 Commits
fix/variou
...
v0.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9841513cd | ||
|
|
eeedb94716 | ||
|
|
bf6151ed61 | ||
|
|
101e6b0802 | ||
|
|
c844fcd185 | ||
|
|
2111ee182d | ||
|
|
0980ee41cf | ||
|
|
0c2181fce9 | ||
|
|
7d4e5d17fc | ||
|
|
ea79d2d279 | ||
|
|
36d64737e0 | ||
|
|
0acb9316f9 | ||
|
|
1233606d19 | ||
|
|
8d98780a7c | ||
|
|
b81bac0bf7 | ||
|
|
ada49f6957 | ||
|
|
919671d9c9 | ||
|
|
dd986e4481 | ||
|
|
829458562f | ||
|
|
e652507714 | ||
|
|
b6c4d8c966 | ||
|
|
96b0c8606c | ||
|
|
69a25e3aba | ||
|
|
320ca2d48a | ||
|
|
421c57267b | ||
|
|
6fd8457e5a | ||
|
|
c2511d56d7 | ||
|
|
fbb17d66aa | ||
|
|
98429ae8c2 | ||
|
|
69adbacd25 | ||
|
|
43ff04317a | ||
|
|
361c2121d8 | ||
|
|
0041c277ad | ||
|
|
f621d3ad5d | ||
|
|
af9aa239af | ||
|
|
f8a2c70f2c | ||
|
|
89b68da86e | ||
|
|
03e9596b7a | ||
|
|
90535c8691 | ||
|
|
b7fc0e903c | ||
|
|
eb420d50b1 | ||
|
|
8f3a081670 | ||
|
|
2867a05c92 | ||
|
|
77b4d594af | ||
|
|
8f4fed1df9 | ||
|
|
b65da05d11 | ||
|
|
c2c8441625 | ||
|
|
a1eb6e314c | ||
|
|
b0c9dcdeee | ||
|
|
0a609adb0b | ||
|
|
19ee6c2687 | ||
|
|
b77c9be36f | ||
|
|
be2910e0df | ||
|
|
749ee367d4 | ||
|
|
c36ce7ac2d | ||
|
|
d0128c7bd7 | ||
|
|
f91b33db38 | ||
|
|
cf166ac641 | ||
|
|
ae86e09970 | ||
|
|
a7e0d2d127 | ||
|
|
9866fdddb8 | ||
|
|
ff9759ab20 | ||
|
|
b655cfab28 | ||
|
|
4c9ddac395 | ||
|
|
c708b1759e | ||
|
|
3ed50f5a1b | ||
|
|
e6d77b3f72 | ||
|
|
747815982e | ||
|
|
7e23a75908 | ||
|
|
4c8a97fe0d | ||
|
|
87b07ab47d | ||
|
|
cf8000d8ee | ||
|
|
31b10194f1 | ||
|
|
3d748021dc | ||
|
|
0f8e1fee35 | ||
|
|
8d18b06012 | ||
|
|
6a32762c98 | ||
|
|
2311eda44b | ||
|
|
5b52aa8abf | ||
|
|
aace77ec40 | ||
|
|
c55d421326 | ||
|
|
5ca8e0c35d | ||
|
|
aba853d598 | ||
|
|
2c8fd163ee | ||
|
|
d9497bd69e | ||
|
|
893f3f6df6 | ||
|
|
ca55e5ba94 | ||
|
|
7753213657 | ||
|
|
b3f7b59243 | ||
|
|
18a9b21a87 | ||
|
|
853b473668 | ||
|
|
40905bc100 | ||
|
|
8be9df2b8d | ||
|
|
bd9eb69313 | ||
|
|
fa31bb2448 | ||
|
|
fcd33e59d9 | ||
|
|
84aee20208 | ||
|
|
2b79788fbe | ||
|
|
ce923a5b4c | ||
|
|
e83ff89c9d | ||
|
|
c71c4f3b16 | ||
|
|
33661587fd | ||
|
|
27b72603e1 | ||
|
|
a4d91adc1e | ||
|
|
03df23d97c | ||
|
|
5e83b28ff5 | ||
|
|
d759fad40c | ||
|
|
1cc9bf459a | ||
|
|
0c535aa8d8 | ||
|
|
3eb4c21263 | ||
|
|
19c53dce2f | ||
|
|
3967bc86d0 | ||
|
|
2b6e42b11f | ||
|
|
d48de6b9f6 | ||
|
|
477a5fce97 | ||
|
|
65efe7f575 | ||
|
|
1b77b15db4 | ||
|
|
fc3bc3f010 | ||
|
|
81e76d9dd4 | ||
|
|
bd1a241be1 | ||
|
|
bc4c0c0b89 | ||
|
|
c7936fc478 | ||
|
|
09ee4fef3a | ||
|
|
d6b0062ae9 | ||
|
|
df26a3eeb7 | ||
|
|
798d873f76 | ||
|
|
a26ea3bac5 | ||
|
|
812b464bc7 | ||
|
|
e1f34c2507 | ||
|
|
79be938531 | ||
|
|
e3ddc68eb2 | ||
|
|
eb9175ab27 | ||
|
|
54b7b2c35f | ||
|
|
ab98870350 | ||
|
|
13c437c418 | ||
|
|
ba2e66a014 | ||
|
|
1adf24e109 | ||
|
|
742ece7923 | ||
|
|
f74736b369 | ||
|
|
3a3be89b90 | ||
|
|
4082092560 | ||
|
|
31839eb444 | ||
|
|
6e203e7833 | ||
|
|
0ef9d0deb8 | ||
|
|
c71f077466 | ||
|
|
36e13ab03b | ||
|
|
c0f19cdf05 | ||
|
|
b20c88e256 | ||
|
|
ce16b37b97 | ||
|
|
6ed1893bed | ||
|
|
bd9ea571aa | ||
|
|
10d9c4fbcc | ||
|
|
eff5db3664 | ||
|
|
f8a6160039 | ||
|
|
4375ff8bd6 | ||
|
|
9bee6e9863 | ||
|
|
b42d20b6a6 | ||
|
|
ba1e3a905b | ||
|
|
973d5ca97d | ||
|
|
57baf22534 | ||
|
|
c615707cbc | ||
|
|
992eea3278 | ||
|
|
03288b52ee | ||
|
|
a274bbcf64 | ||
|
|
64e9889d41 | ||
|
|
58e80ab3e0 | ||
|
|
94e81d14fa | ||
|
|
0b021e898f | ||
|
|
be8d6e8235 | ||
|
|
d3dcb599ca | ||
|
|
0cbe516ca3 | ||
|
|
26911e9530 | ||
|
|
5524dfa83d | ||
|
|
21d54eb2ac | ||
|
|
4152199f3b | ||
|
|
1d40251f29 | ||
|
|
97e137c411 | ||
|
|
0de6c2338d | ||
|
|
a2479155b2 | ||
|
|
e2dace75f4 | ||
|
|
ef9a7cd811 | ||
|
|
964270e054 | ||
|
|
6b9cdf3294 | ||
|
|
6fd228f1f4 | ||
|
|
0eb0b283b2 | ||
|
|
4b071bccda | ||
|
|
65a7947932 | ||
|
|
a3e607a887 | ||
|
|
feab9f4c46 | ||
|
|
fe5622d62a | ||
|
|
e759f2817e | ||
|
|
60cc5c2710 | ||
|
|
25ccd678c9 | ||
|
|
a77b4cbe71 | ||
|
|
aae32b0d74 | ||
|
|
a94b43a19e | ||
|
|
9a4c2df552 | ||
|
|
d5b89a755e | ||
|
|
a9acf62d93 | ||
|
|
c896e198dd | ||
|
|
c538518b4b | ||
|
|
f66d14eeea | ||
|
|
bc8feac83c | ||
|
|
40a98bcf30 | ||
|
|
045e91d9ac | ||
|
|
a80ab0f3e9 | ||
|
|
e5d209d407 | ||
|
|
ef1531e561 | ||
|
|
4dd036abb2 | ||
|
|
81e909ccfb | ||
|
|
0cb9d2a8e4 | ||
|
|
cb16c0e829 | ||
|
|
9e067ee230 | ||
|
|
8b53d40a2a | ||
|
|
4c0ad7a5d8 | ||
|
|
66e25ba068 | ||
|
|
56c98e6e3a | ||
|
|
01f305a98e | ||
|
|
e14cdaccdd | ||
|
|
4ece25b58d | ||
|
|
c1957bb927 | ||
|
|
636ca155e5 | ||
|
|
17a5b43609 | ||
|
|
b2b3ccf923 | ||
|
|
2272c7eb6b | ||
|
|
181c324dd4 | ||
|
|
89912b18d7 | ||
|
|
85f2af4bc3 | ||
|
|
21ad5a32c1 | ||
|
|
4b51a4ad9a | ||
|
|
dd7e634b69 | ||
|
|
351ec64bbc | ||
|
|
3a758e012f | ||
|
|
c16084d99f | ||
|
|
5591267124 | ||
|
|
d46c16f01f | ||
|
|
18c22e8a68 | ||
|
|
64b3ac3e94 | ||
|
|
c36b9dcf5d | ||
|
|
d3b6c93b63 | ||
|
|
3b862e36c8 | ||
|
|
f3bb86d905 | ||
|
|
4dd925caf0 | ||
|
|
ced5aef6d1 | ||
|
|
6a853c07a0 | ||
|
|
f0b9a0fce4 | ||
|
|
00f09fec2f | ||
|
|
6e1863b515 | ||
|
|
dfe2084c74 | ||
|
|
0825f055a7 |
@@ -35,6 +35,6 @@
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node",
|
||||
"features": {
|
||||
"golang": "1.19"
|
||||
"golang": "1.21"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,3 +22,4 @@
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
README.md
|
||||
!Dockerfile.rootless
|
||||
|
||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1 +1 @@
|
||||
github: [hay-kot]
|
||||
github: [tankerkiller125,katosdev]
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -2,6 +2,7 @@
|
||||
name: "Bug Report"
|
||||
description: "Submit a bug report for the current release"
|
||||
labels: ["bug"]
|
||||
projects: ["sysadminsmedia/2"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: checks
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
3
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,7 +1,8 @@
|
||||
---
|
||||
name: "Feature Request"
|
||||
description: "Submit a feature request for the current release"
|
||||
labels: ["feature-request"]
|
||||
labels: ["enhancement"]
|
||||
projects: ["sysadminsmedia/2"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem-statement
|
||||
|
||||
113
.github/workflows/docker-publish-rootless.yaml
vendored
Normal file
113
.github/workflows/docker-publish-rootless.yaml
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
name: Docker publish rootless
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '00 6 * * *'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
|
||||
jobs:
|
||||
build-rootless:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
# Set up BuildKit Docker container builder to be able to build
|
||||
# multi-platform images and export cache
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: metadata
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=schedule,pattern=nightly
|
||||
flavor: |
|
||||
suffix=-rootless,onlatest=true
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
# Sign the resulting Docker image digest except on PRs.
|
||||
# This will only write to the public Rekor transparency log when the Docker
|
||||
# repository is public to avoid leaking data. If you would like to publish
|
||||
# transparency data even for private images, pass --force to cosign below.
|
||||
# https://github.com/sigstore/cosign
|
||||
- name: Sign the published Docker image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
env:
|
||||
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
|
||||
TAGS: ${{ steps.metadata.outputs.tags }}
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
# This step uses the identity token to provision an ephemeral certificate
|
||||
# against the sigstore community Fulcio instance.
|
||||
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
||||
|
||||
- name: Attest
|
||||
uses: actions/attest-build-provenance@v1
|
||||
id: attest
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
subject-digest: ${{ steps.build-and-push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
111
.github/workflows/docker-publish.yaml
vendored
Normal file
111
.github/workflows/docker-publish.yaml
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
name: Docker publish
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '00 6 * * *'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
# Set up BuildKit Docker container builder to be able to build
|
||||
# multi-platform images and export cache
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=schedule,pattern=nightly
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
# Sign the resulting Docker image digest except on PRs.
|
||||
# This will only write to the public Rekor transparency log when the Docker
|
||||
# repository is public to avoid leaking data. If you would like to publish
|
||||
# transparency data even for private images, pass --force to cosign below.
|
||||
# https://github.com/sigstore/cosign
|
||||
- name: Sign the published Docker image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
env:
|
||||
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
# This step uses the identity token to provision an ephemeral certificate
|
||||
# against the sigstore community Fulcio instance.
|
||||
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
||||
|
||||
- name: Attest
|
||||
uses: actions/attest-build-provenance@v1
|
||||
id: attest
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
subject-digest: ${{ steps.build-and-push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
8
.github/workflows/partial-backend.yaml
vendored
8
.github/workflows/partial-backend.yaml
vendored
@@ -7,12 +7,12 @@ jobs:
|
||||
Go:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||
version: latest
|
||||
|
||||
14
.github/workflows/partial-frontend.yaml
vendored
14
.github/workflows/partial-frontend.yaml
vendored
@@ -9,11 +9,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
- uses: pnpm/action-setup@v3.0.0
|
||||
with:
|
||||
version: 6.0.2
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -44,15 +44,15 @@ jobs:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- uses: pnpm/action-setup@v2.2.4
|
||||
- uses: pnpm/action-setup@v3.0.0
|
||||
with:
|
||||
version: 6.0.2
|
||||
|
||||
|
||||
41
.github/workflows/partial-publish.yaml
vendored
41
.github/workflows/partial-publish.yaml
vendored
@@ -20,22 +20,22 @@ jobs:
|
||||
name: "Publish Homebox"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: all
|
||||
|
||||
- name: install buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
install: true
|
||||
|
||||
@@ -44,23 +44,46 @@ jobs:
|
||||
env:
|
||||
CR_PAT: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: build nightly the image
|
||||
- name: build nightly image
|
||||
if: ${{ inputs.release == false }}
|
||||
run: |
|
||||
docker build --push --no-cache \
|
||||
--tag=ghcr.io/hay-kot/homebox:${{ inputs.tag }} \
|
||||
--tag=ghcr.io/sysadminsmedia/homebox:${{ inputs.tag }} \
|
||||
--build-arg=COMMIT=$(git rev-parse HEAD) \
|
||||
--build-arg=BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
|
||||
--platform=linux/amd64,linux/arm64,linux/arm/v7 .
|
||||
|
||||
- name: build nightly-rootless image
|
||||
if: ${{ inputs.release == false }}
|
||||
run: |
|
||||
docker build --push --no-cache \
|
||||
--tag=ghcr.io/sysadminsmedia/homebox:${{ inputs.tag }}-rootless \
|
||||
--build-arg=COMMIT=$(git rev-parse HEAD) \
|
||||
--build-arg=BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
|
||||
--file Dockerfile.rootless \
|
||||
--platform=linux/amd64,linux/arm64,linux/arm/v7 .
|
||||
|
||||
- name: build release tagged the image
|
||||
if: ${{ inputs.release == true }}
|
||||
run: |
|
||||
docker build --push --no-cache \
|
||||
--tag ghcr.io/hay-kot/homebox:nightly \
|
||||
--tag ghcr.io/hay-kot/homebox:latest \
|
||||
--tag ghcr.io/hay-kot/homebox:${{ inputs.tag }} \
|
||||
--tag ghcr.io/sysadminsmedia/homebox:nightly \
|
||||
--tag ghcr.io/sysadminsmedia/homebox:latest \
|
||||
--tag ghcr.io/sysadminsmedia/homebox:${{ inputs.tag }} \
|
||||
--build-arg VERSION=${{ inputs.tag }} \
|
||||
--build-arg COMMIT=$(git rev-parse HEAD) \
|
||||
--build-arg BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
|
||||
--platform linux/amd64,linux/arm64,linux/arm/v7 .
|
||||
|
||||
- name: build release tagged the rootless image
|
||||
if: ${{ inputs.release == true }}
|
||||
run: |
|
||||
docker build --push --no-cache \
|
||||
--tag ghcr.io/sysadminsmedia/homebox:nightly-rootless \
|
||||
--tag ghcr.io/sysadminsmedia/homebox:latest-rootless \
|
||||
--tag ghcr.io/sysadminsmedia/homebox:${{ inputs.tag }}-rootless \
|
||||
--build-arg VERSION=${{ inputs.tag }} \
|
||||
--build-arg COMMIT=$(git rev-parse HEAD) \
|
||||
--build-arg BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
|
||||
--platform linux/amd64,linux/arm64,linux/arm/v7 \
|
||||
--file Dockerfile.rootless .
|
||||
|
||||
31
.github/workflows/publish.yaml
vendored
31
.github/workflows/publish.yaml
vendored
@@ -4,9 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
@@ -16,41 +13,17 @@ jobs:
|
||||
name: "Deploy Nightly to Fly.io"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@master
|
||||
- run: flyctl deploy --remote-only
|
||||
|
||||
publish-nightly:
|
||||
name: "Publish Nightly"
|
||||
if: github.event_name != 'release'
|
||||
uses: hay-kot/homebox/.github/workflows/partial-publish.yaml@main
|
||||
uses: sysadminsmedia/homebox/.github/workflows/partial-publish.yaml@main
|
||||
with:
|
||||
tag: nightly
|
||||
secrets:
|
||||
GH_TOKEN: ${{ secrets.CR_PAT }}
|
||||
|
||||
publish-tag:
|
||||
name: "Publish Tag"
|
||||
if: github.event_name == 'release'
|
||||
uses: hay-kot/homebox/.github/workflows/partial-publish.yaml@main
|
||||
with:
|
||||
release: true
|
||||
tag: ${{ github.event.release.tag_name }}
|
||||
secrets:
|
||||
GH_TOKEN: ${{ secrets.CR_PAT }}
|
||||
|
||||
deploy-docs:
|
||||
name: Deploy docs
|
||||
needs:
|
||||
- publish-tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CONFIG_FILE: docs/mkdocs.yml
|
||||
EXTRA_PACKAGES: build-base
|
||||
|
||||
38
.github/workflows/tag.yaml
vendored
38
.github/workflows/tag.yaml
vendored
@@ -11,23 +11,23 @@ env:
|
||||
jobs:
|
||||
backend-tests:
|
||||
name: "Backend Server Tests"
|
||||
uses: hay-kot/homebox/.github/workflows/partial-backend.yaml@main
|
||||
uses: sysadminsmedia/homebox/.github/workflows/partial-backend.yaml@main
|
||||
|
||||
frontend-tests:
|
||||
name: "Frontend and End-to-End Tests"
|
||||
uses: hay-kot/homebox/.github/workflows/partial-frontend.yaml@main
|
||||
|
||||
uses: sysadminsmedia/homebox/.github/workflows/partial-frontend.yaml@main
|
||||
|
||||
goreleaser:
|
||||
name: goreleaser
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
cp -r ./.output/public ../backend/app/api/static/
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
workdir: "backend"
|
||||
distribution: goreleaser
|
||||
@@ -49,3 +49,29 @@ jobs:
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish-tag:
|
||||
name: "Publish Tag"
|
||||
uses: sysadminsmedia/homebox/.github/workflows/partial-publish.yaml@main
|
||||
with:
|
||||
release: true
|
||||
tag: ${{ github.ref_name }}
|
||||
secrets:
|
||||
GH_TOKEN: ${{ secrets.CR_PAT }}
|
||||
|
||||
deploy-docs:
|
||||
name: Deploy docs
|
||||
needs:
|
||||
- publish-tag
|
||||
- goreleaser
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CONFIG_FILE: docs/mkdocs.yml
|
||||
EXTRA_PACKAGES: build-base
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -53,4 +53,7 @@ dist/
|
||||
|
||||
# Nuxt Publish Dir
|
||||
backend/app/api/static/public/*
|
||||
!backend/app/api/static/public/.gitkeep
|
||||
!backend/app/api/static/public/.gitkeep
|
||||
backend/api
|
||||
|
||||
docs/.vitepress/cache/
|
||||
@@ -3,7 +3,7 @@ package schema
|
||||
import (
|
||||
"entgo.io/ent"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/schema/mixins"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/schema/mixins"
|
||||
)
|
||||
|
||||
type {{ .Scaffold.model }} struct {
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -16,7 +16,7 @@
|
||||
"editor.formatOnSave": false,
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Contributing
|
||||
|
||||
## We Develop with Github
|
||||
## We Develop with GitHub
|
||||
|
||||
We use github to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
|
||||
## Branch Flow
|
||||
|
||||
We use the `main` branch as the development branch. All PRs should be made to the `main` branch from a feature branch. To create a pull request you can use the following steps:
|
||||
We use the `main` branch as the development branch. All PRs should be made to the `main` branch from a feature branch. To create a pull request, you can use the following steps:
|
||||
|
||||
1. Fork the repository and create a new branch from `main`.
|
||||
2. If you've added code that should be tested, add tests.
|
||||
3. If you've changed API's, update the documentation.
|
||||
3. If you've changed APIs, update the documentation.
|
||||
4. Ensure that the test suite and linters pass
|
||||
5. Issue your pull request
|
||||
|
||||
@@ -18,7 +18,7 @@ We use the `main` branch as the development branch. All PRs should be made to th
|
||||
|
||||
### Prerequisites
|
||||
|
||||
There is a devcontainer available for this project. If you are using VSCode, you can use the devcontainer to get started. If you are not using VSCode, you can need to ensure that you have the following tools installed:
|
||||
There is a devcontainer available for this project. If you are using VSCode, you can use the devcontainer to get started. If you are not using VSCode, you need to ensure that you have the following tools installed:
|
||||
|
||||
- [Go 1.19+](https://golang.org/doc/install)
|
||||
- [Swaggo](https://github.com/swaggo/swag)
|
||||
@@ -31,27 +31,27 @@ If you're using `taskfile` you can run `task --list-all` for a list of all comma
|
||||
|
||||
### Setup
|
||||
|
||||
If you're using the taskfile you can use the `task setup` command to run the required setup commands. Otherwise you can review the commands required in the `Taskfile.yml` file.
|
||||
If you're using the taskfile, you can use the `task setup` command to run the required setup commands. Otherwise, you can review the commands required in the `Taskfile.yml` file.
|
||||
|
||||
Note that when installing dependencies with pnpm you must use the `--shamefully-hoist` flag. If you don't use this flag you will get an error when running the the frontend server.
|
||||
Note that when installing dependencies with pnpm you must use the `--shamefully-hoist` flag. If you don't use this flag, you will get an error when running the frontend server.
|
||||
|
||||
### API Development Notes
|
||||
|
||||
start command `task go:run`
|
||||
|
||||
1. API Server does not auto reload. You'll need to restart the server after making changes.
|
||||
2. Unit tests should be written in Go, however end-to-end or user story tests should be written in TypeScript using the client library in the frontend directory.
|
||||
2. Unit tests should be written in Go, however, end-to-end or user story tests should be written in TypeScript using the client library in the frontend directory.
|
||||
|
||||
### Frontend Development Notes
|
||||
|
||||
start command `task: ui:dev`
|
||||
|
||||
1. The frontend is a Vue 3 app with Nuxt.js that uses Tailwind and DaisyUI for styling.
|
||||
2. We're using Vitest for our automated testing. you can run these with `task ui:watch`.
|
||||
3. Tests require the API server to be running and in some cases the first run will fail due to a race condition. If this happens just run the tests again and they should pass.
|
||||
2. We're using Vitest for our automated testing. You can run these with `task ui:watch`.
|
||||
3. Tests require the API server to be running, and in some cases the first run will fail due to a race condition. If this happens, just run the tests again and they should pass.
|
||||
|
||||
## Publishing Release
|
||||
|
||||
Create a new tag in github with the version number vX.X.X. This will trigger a new release to be created.
|
||||
Create a new tag in GitHub with the version number vX.X.X. This will trigger a new release to be created.
|
||||
|
||||
Test -> Goreleaser -> Publish Release -> Trigger Docker Builds -> Deploy Docs + Fly.io Demo
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
# Build Nuxt
|
||||
FROM node:17-alpine as frontend-builder
|
||||
FROM node:18-alpine as frontend-builder
|
||||
WORKDIR /app
|
||||
RUN npm install -g pnpm
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||
@@ -32,7 +32,7 @@ FROM alpine:latest
|
||||
|
||||
ENV HBOX_MODE=production
|
||||
ENV HBOX_STORAGE_DATA=/data/
|
||||
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1
|
||||
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_pragma=busy_timeout=2000&_pragma=journal_mode=WAL&_fk=1
|
||||
|
||||
RUN apk --no-cache add ca-certificates
|
||||
RUN mkdir /app
|
||||
@@ -41,7 +41,7 @@ COPY --from=builder /go/bin/api /app
|
||||
RUN chmod +x /app/api
|
||||
|
||||
LABEL Name=homebox Version=0.0.1
|
||||
LABEL org.opencontainers.image.source="https://github.com/hay-kot/homebox"
|
||||
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"
|
||||
EXPOSE 7745
|
||||
WORKDIR /app
|
||||
VOLUME [ "/data" ]
|
||||
|
||||
53
Dockerfile.rootless
Normal file
53
Dockerfile.rootless
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
# Build Nuxt
|
||||
FROM node:18-alpine as frontend-builder
|
||||
WORKDIR /app
|
||||
RUN npm install -g pnpm
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||
RUN pnpm install --frozen-lockfile --shamefully-hoist
|
||||
COPY frontend .
|
||||
RUN pnpm build
|
||||
|
||||
# Build API
|
||||
FROM golang:alpine AS builder
|
||||
ARG BUILD_TIME
|
||||
ARG COMMIT
|
||||
ARG VERSION
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --update git build-base gcc g++
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY ./backend .
|
||||
RUN go get -d -v ./...
|
||||
RUN rm -rf ./app/api/public
|
||||
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build \
|
||||
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
|
||||
-o /go/bin/api \
|
||||
-v ./app/api/*.go && \
|
||||
chmod +x /go/bin/api && \
|
||||
# create a directory so that we can copy it in the next stage
|
||||
mkdir /data
|
||||
|
||||
# Production Stage
|
||||
FROM gcr.io/distroless/static
|
||||
|
||||
ENV HBOX_MODE=production
|
||||
ENV HBOX_STORAGE_DATA=/data/
|
||||
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1
|
||||
|
||||
# Copy the binary and the (empty) /data dir and
|
||||
# change the ownership to the low-privileged user
|
||||
COPY --from=builder --chown=nonroot /go/bin/api /app
|
||||
COPY --from=builder --chown=nonroot /data /data
|
||||
|
||||
LABEL Name=homebox Version=0.0.1
|
||||
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"
|
||||
EXPOSE 7745
|
||||
VOLUME [ "/data" ]
|
||||
|
||||
# Drop root and run as low-privileged user
|
||||
USER nonroot
|
||||
ENTRYPOINT [ "/app" ]
|
||||
CMD [ "/data/config.yml" ]
|
||||
30
README.md
30
README.md
@@ -1,30 +1,50 @@
|
||||
<div align="center">
|
||||
<img src="/docs/docs/assets/img/lilbox.svg" height="200"/>
|
||||
<img src="/docs/public/lilbox.svg" height="200"/>
|
||||
</div>
|
||||
|
||||
<h1 align="center" style="margin-top: -10px"> HomeBox </h1>
|
||||
<p align="center" style="width: 100;">
|
||||
<a href="https://hay-kot.github.io/homebox/">Docs</a>
|
||||
<a href="https://homebox.sysadminsmedia.com">Docs</a>
|
||||
|
|
||||
<a href="https://homebox.fly.dev">Demo</a>
|
||||
|
|
||||
<a href="https://discord.gg/tuncmNrE4z">Discord</a>
|
||||
</p>
|
||||
|
||||
## What is HomeBox
|
||||
Homebox is the inventory and organization system built for the Home User! With a focus on simplicity and ease of use, Homebox is the perfect solution for your home inventory, organization, and management needs. While developing this project, I've tried to keep the following principles in mind:
|
||||
|
||||
- _Simple_ - Homebox is designed to be simple and easy to use. No complicated setup or configuration required. Use either a single docker container, or deploy yourself by compiling the binary for your platform of choice.
|
||||
- _Blazingly Fast_ - Homebox is written in Go, which makes it extremely fast and requires minimal resources to deploy. In general idle memory usage is less than 50MB for the whole container.
|
||||
- _Portable_ - Homebox is designed to be portable and run on anywhere. We use SQLite and an embedded Web UI to make it easy to deploy, use, and backup.
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
[Configuration & Docker Compose](https://hay-kot.github.io/homebox/quick-start)
|
||||
[Configuration & Docker Compose](https://homebox.sysadminsmedia.com/quick-start.html)
|
||||
|
||||
```bash
|
||||
# If using the rootless image, ensure data
|
||||
# folder has correct permissions
|
||||
mkdir -p /path/to/data/folder
|
||||
chown 65532:65532 -R /path/to/data/folder
|
||||
docker run -d \
|
||||
--name homebox \
|
||||
--restart unless-stopped \
|
||||
--publish 3100:7745 \
|
||||
--env TZ=Europe/Bucharest \
|
||||
--volume /path/to/data/folder/:/data \
|
||||
ghcr.io/hay-kot/homebox:latest
|
||||
ghcr.io/sysadminsmedia/homebox:latest
|
||||
# ghcr.io/sysadminsmedia/homebox:latest-rootless
|
||||
```
|
||||
|
||||
## Credits
|
||||
<!-- CONTRIBUTING -->
|
||||
## Contributing
|
||||
|
||||
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
||||
|
||||
If you are not a coder, you can still contribute financially. Financial contributions help me prioritize working on this project over others and helps me know that there is a real demand for project development.
|
||||
|
||||
## Credits
|
||||
- Original project by [@hay-kot](https://github.com/hay-kot)
|
||||
- Logo by [@lakotelman](https://github.com/lakotelman)
|
||||
|
||||
47
Taskfile.yml
47
Taskfile.yml
@@ -1,6 +1,7 @@
|
||||
version: "3"
|
||||
|
||||
env:
|
||||
HBOX_LOG_LEVEL: debug
|
||||
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_pragma=busy_timeout=1000&_pragma=journal_mode=WAL&_fk=1
|
||||
HBOX_OPTIONS_ALLOW_REGISTRATION: true
|
||||
UNSAFE_DISABLE_PASSWORD_PROJECTION: "yes_i_am_sure"
|
||||
@@ -12,15 +13,25 @@ tasks:
|
||||
- cd backend && go mod tidy
|
||||
- cd frontend && pnpm install --shamefully-hoist
|
||||
|
||||
generate:
|
||||
desc: |
|
||||
Generates collateral files from the backend project
|
||||
including swagger docs and typescripts type for the frontend
|
||||
deps:
|
||||
- db:generate
|
||||
swag:
|
||||
desc: Generate swagger docs
|
||||
dir: backend/app/api/static/
|
||||
vars:
|
||||
API: "../"
|
||||
INTERNAL: "../../../internal"
|
||||
PKGS: "../../../pkgs"
|
||||
cmds:
|
||||
- swag fmt --dir={{ .API }}
|
||||
- swag init --dir={{ .API }},{{ .INTERNAL }}/core/services,{{ .INTERNAL }}/data/repo --parseDependency
|
||||
sources:
|
||||
- "./backend/app/api/**/*"
|
||||
- "./backend/internal/data/**"
|
||||
- "./backend/internal/core/services/**/*"
|
||||
- "./backend/app/tools/typegen/main.go"
|
||||
|
||||
typescript-types:
|
||||
desc: Generates typescript types from swagger definition
|
||||
cmds:
|
||||
- cd backend/app/api/static && swag fmt --dir=../
|
||||
- cd backend/app/api/static && swag init --dir=../,../../../internal,../../../pkgs
|
||||
- |
|
||||
npx swagger-typescript-api \
|
||||
--no-client \
|
||||
@@ -28,12 +39,17 @@ tasks:
|
||||
--path ./backend/app/api/static/docs/swagger.json \
|
||||
--output ./frontend/lib/api/types
|
||||
- go run ./backend/app/tools/typegen/main.go ./frontend/lib/api/types/data-contracts.ts
|
||||
- cp ./backend/app/api/static/docs/swagger.json docs/docs/api/openapi-2.0.json
|
||||
sources:
|
||||
- "./backend/app/api/**/*"
|
||||
- "./backend/internal/data/**"
|
||||
- "./backend/internal/core/services/**/*"
|
||||
- "./backend/app/tools/typegen/main.go"
|
||||
- ./backend/app/tools/typegen/main.go
|
||||
- ./backend/app/api/static/docs/swagger.json
|
||||
|
||||
generate:
|
||||
deps:
|
||||
- db:generate
|
||||
cmds:
|
||||
- task: swag
|
||||
- task: typescript-types
|
||||
- cp ./backend/app/api/static/docs/swagger.json docs/docs/api/openapi-2.0.json
|
||||
|
||||
go:run:
|
||||
desc: Starts the backend api server (depends on generate task)
|
||||
@@ -87,8 +103,7 @@ tasks:
|
||||
dir: backend/internal/
|
||||
cmds:
|
||||
- |
|
||||
go generate ./... \
|
||||
--template=./data/ent/schema/templates/has_id.tmpl
|
||||
go generate ./...
|
||||
sources:
|
||||
- "./backend/internal/data/ent/schema/**/*"
|
||||
|
||||
@@ -139,4 +154,4 @@ tasks:
|
||||
- task: go:all
|
||||
- task: ui:check
|
||||
- task: ui:fix
|
||||
- task: test:ci
|
||||
- task: test:ci
|
||||
|
||||
74
backend/.golangci.yml
Normal file
74
backend/.golangci.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
skip-dirs:
|
||||
- internal/data/ent.*
|
||||
linters-settings:
|
||||
goconst:
|
||||
min-len: 5
|
||||
min-occurrences: 5
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
revive:
|
||||
ignore-generated-header: false
|
||||
severity: warning
|
||||
confidence: 3
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: io/util
|
||||
desc: |
|
||||
Deprecated: As of Go 1.16, the same functionality is now provided by
|
||||
package io or package os, and those implementations should be
|
||||
preferred in new code. See the specific function documentation for
|
||||
details.
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- ruleguard
|
||||
testifylint:
|
||||
enable-all: true
|
||||
tagalign:
|
||||
order:
|
||||
- json
|
||||
- schema
|
||||
- yaml
|
||||
- yml
|
||||
- toml
|
||||
- validate
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- depguard
|
||||
- dogsled
|
||||
- errcheck
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- gochecknoinits
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goprintffuncname
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- tagalign
|
||||
- testifylint
|
||||
- typecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
- whitespace
|
||||
- zerologlint
|
||||
- sqlclosecheck
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
fix: true
|
||||
@@ -1,23 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/config"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/mailer"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/mailer"
|
||||
)
|
||||
|
||||
type app struct {
|
||||
conf *config.Config
|
||||
mailer mailer.Mailer
|
||||
db *ent.Client
|
||||
server *server.Server
|
||||
repos *repo.AllRepos
|
||||
services *services.AllServices
|
||||
bus *eventbus.EventBus
|
||||
}
|
||||
|
||||
func new(conf *config.Config) *app {
|
||||
@@ -35,13 +33,3 @@ func new(conf *config.Config) *app {
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (a *app) startBgTask(t time.Duration, fn func()) {
|
||||
timer := time.NewTimer(t)
|
||||
|
||||
for {
|
||||
timer.Reset(t)
|
||||
a.server.Background(fn)
|
||||
<-timer.C
|
||||
}
|
||||
}
|
||||
|
||||
37
backend/app/api/bgrunner.go
Normal file
37
backend/app/api/bgrunner.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BackgroundTask struct {
|
||||
name string
|
||||
Interval time.Duration
|
||||
Fn func(context.Context)
|
||||
}
|
||||
|
||||
func (tsk *BackgroundTask) Name() string {
|
||||
return tsk.name
|
||||
}
|
||||
|
||||
func NewTask(name string, interval time.Duration, fn func(context.Context)) *BackgroundTask {
|
||||
return &BackgroundTask{
|
||||
Interval: interval,
|
||||
Fn: fn,
|
||||
}
|
||||
}
|
||||
|
||||
func (tsk *BackgroundTask) Start(ctx context.Context) error {
|
||||
timer := time.NewTimer(tsk.Interval)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-timer.C:
|
||||
timer.Reset(tsk.Interval)
|
||||
tsk.Fn(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
)
|
||||
|
||||
func (a *app) SetupDemo() {
|
||||
@@ -15,9 +16,12 @@ func (a *app) SetupDemo() {
|
||||
,Office,IOT;Home Assistant; Z-Wave,1,Zooz 110v Power Switch,"Zooz Z-Wave Plus Power Switch ZEN15 for 110V AC Units, Sump Pumps, Humidifiers, and More",,,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
|
||||
,Downstairs,IOT;Home Assistant; Z-Wave,1,Ecolink Z-Wave PIR Motion Sensor,"Ecolink Z-Wave PIR Motion Detector Pet Immune, White (PIRZWAVE2.5-ECO)",,,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,,,,,,
|
||||
,Entry,IOT;Home Assistant; Z-Wave,1,Yale Security Touchscreen Deadbolt,"Yale Security YRD226-ZW2-619 YRD226ZW2619 Touchscreen Deadbolt, Satin Nickel",,,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,,,,,,
|
||||
,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,
|
||||
,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,
|
||||
`
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
registration := services.UserRegistration{
|
||||
Email: "demo@example.com",
|
||||
Name: "Demo",
|
||||
@@ -25,21 +29,34 @@ func (a *app) SetupDemo() {
|
||||
}
|
||||
|
||||
// First check if we've already setup a demo user and skip if so
|
||||
_, err := a.services.User.Login(context.Background(), registration.Email, registration.Password, false)
|
||||
log.Debug().Msg("Checking if demo user already exists")
|
||||
_, err := a.services.User.Login(ctx, registration.Email, registration.Password, false)
|
||||
if err == nil {
|
||||
log.Info().Msg("Demo user already exists, skipping setup")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = a.services.User.RegisterUser(context.Background(), registration)
|
||||
log.Debug().Msg("Demo user does not exist, setting up demo")
|
||||
_, err = a.services.User.RegisterUser(ctx, registration)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to register demo user")
|
||||
log.Fatal().Msg("Failed to setup demo")
|
||||
}
|
||||
|
||||
token, _ := a.services.User.Login(context.Background(), registration.Email, registration.Password, false)
|
||||
self, _ := a.services.User.GetSelf(context.Background(), token.Raw)
|
||||
token, err := a.services.User.Login(ctx, registration.Email, registration.Password, false)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to login demo user")
|
||||
log.Fatal().Msg("Failed to setup demo")
|
||||
return
|
||||
}
|
||||
self, err := a.services.User.GetSelf(ctx, token.Raw)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to get self")
|
||||
log.Fatal().Msg("Failed to setup demo")
|
||||
return
|
||||
}
|
||||
|
||||
_, err = a.services.Items.CsvImport(context.Background(), self.GroupID, strings.NewReader(csvText))
|
||||
_, err = a.services.Items.CsvImport(ctx, self.GroupID, strings.NewReader(csvText))
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to import CSV")
|
||||
log.Fatal().Msg("Failed to setup demo")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package debughandlers provides handlers for debugging.
|
||||
package debughandlers
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
// Package v1 provides the API handlers for version 1 of the API.
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
|
||||
"github.com/olahol/melody"
|
||||
)
|
||||
|
||||
type Results[T any] struct {
|
||||
@@ -43,12 +51,20 @@ func WithRegistration(allowRegistration bool) func(*V1Controller) {
|
||||
}
|
||||
}
|
||||
|
||||
func WithSecureCookies(secure bool) func(*V1Controller) {
|
||||
return func(ctrl *V1Controller) {
|
||||
ctrl.cookieSecure = secure
|
||||
}
|
||||
}
|
||||
|
||||
type V1Controller struct {
|
||||
cookieSecure bool
|
||||
repo *repo.AllRepos
|
||||
svc *services.AllServices
|
||||
maxUploadSize int64
|
||||
isDemo bool
|
||||
allowRegistration bool
|
||||
bus *eventbus.EventBus
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -60,7 +76,7 @@ type (
|
||||
BuildTime string `json:"buildTime"`
|
||||
}
|
||||
|
||||
ApiSummary struct {
|
||||
APISummary struct {
|
||||
Healthy bool `json:"health"`
|
||||
Versions []string `json:"versions"`
|
||||
Title string `json:"title"`
|
||||
@@ -71,17 +87,18 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func BaseUrlFunc(prefix string) func(s string) string {
|
||||
func BaseURLFunc(prefix string) func(s string) string {
|
||||
return func(s string) string {
|
||||
return prefix + "/v1" + s
|
||||
}
|
||||
}
|
||||
|
||||
func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, options ...func(*V1Controller)) *V1Controller {
|
||||
func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, bus *eventbus.EventBus, options ...func(*V1Controller)) *V1Controller {
|
||||
ctrl := &V1Controller{
|
||||
repo: repos,
|
||||
svc: svc,
|
||||
allowRegistration: true,
|
||||
bus: bus,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
@@ -96,11 +113,11 @@ func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, options ..
|
||||
// @Summary Application Info
|
||||
// @Tags Base
|
||||
// @Produce json
|
||||
// @Success 200 {object} ApiSummary
|
||||
// @Success 200 {object} APISummary
|
||||
// @Router /v1/status [GET]
|
||||
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
return server.JSON(w, http.StatusOK, ApiSummary{
|
||||
return server.JSON(w, http.StatusOK, APISummary{
|
||||
Healthy: ready(),
|
||||
Title: "Homebox",
|
||||
Message: "Track, Manage, and Organize your Things",
|
||||
@@ -110,3 +127,87 @@ func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.Hand
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// HandleCurrency godoc
|
||||
//
|
||||
// @Summary Currency
|
||||
// @Tags Base
|
||||
// @Produce json
|
||||
// @Success 200 {object} currencies.Currency
|
||||
// @Router /v1/currency [GET]
|
||||
func (ctrl *V1Controller) HandleCurrency() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
// Set Cache for 10 Minutes
|
||||
w.Header().Set("Cache-Control", "max-age=600")
|
||||
|
||||
return server.JSON(w, http.StatusOK, ctrl.svc.Currencies.Slice())
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) HandleCacheWS() errchain.HandlerFunc {
|
||||
type eventMsg struct {
|
||||
Event string `json:"event"`
|
||||
}
|
||||
|
||||
m := melody.New()
|
||||
|
||||
m.HandleConnect(func(s *melody.Session) {
|
||||
auth := services.NewContext(s.Request.Context())
|
||||
s.Set("gid", auth.GID)
|
||||
})
|
||||
|
||||
factory := func(e string) func(data any) {
|
||||
return func(data any) {
|
||||
eventData, ok := data.(eventbus.GroupMutationEvent)
|
||||
if !ok {
|
||||
log.Log().Msgf("invalid event data: %v", data)
|
||||
return
|
||||
}
|
||||
|
||||
msg := &eventMsg{Event: e}
|
||||
|
||||
jsonBytes, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
log.Log().Msgf("error marshling event data %v: %v", data, err)
|
||||
return
|
||||
}
|
||||
|
||||
_ = m.BroadcastFilter(jsonBytes, func(s *melody.Session) bool {
|
||||
groupIDStr, ok := s.Get("gid")
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
GID := groupIDStr.(uuid.UUID)
|
||||
return GID == eventData.GID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.bus.Subscribe(eventbus.EventLabelMutation, factory("label.mutation"))
|
||||
ctrl.bus.Subscribe(eventbus.EventLocationMutation, factory("location.mutation"))
|
||||
ctrl.bus.Subscribe(eventbus.EventItemMutation, factory("item.mutation"))
|
||||
|
||||
// Persistent asynchronous ticker that keeps all websocket connections alive with periodic pings.
|
||||
go func() {
|
||||
const interval = 10 * time.Second
|
||||
|
||||
ping := time.NewTicker(interval)
|
||||
defer ping.Stop()
|
||||
|
||||
for range ping.C {
|
||||
msg := &eventMsg{Event: "ping"}
|
||||
|
||||
pingBytes, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
log.Log().Msgf("error marshaling ping: %v", err)
|
||||
} else {
|
||||
_ = m.Broadcast(pingBytes)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
return m.HandleRequest(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
// routeID extracts the ID from the request URL. If the ID is not in a valid
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
type ActionAmountResult struct {
|
||||
@@ -68,3 +68,16 @@ func (ctrl *V1Controller) HandleEnsureImportRefs() errchain.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemDateZeroOut() errchain.HandlerFunc {
|
||||
return actionHandlerFactory("zero out date time", ctrl.repo.Items.ZeroOutTimeFields)
|
||||
}
|
||||
|
||||
// HandleSetPrimaryPhotos godoc
|
||||
//
|
||||
// @Summary Set Primary Photos
|
||||
// @Description Sets the first photo of each item as the primary photo
|
||||
// @Tags Actions
|
||||
// @Produce json
|
||||
// @Success 200 {object} ActionAmountResult
|
||||
// @Router /v1/actions/set-primary-photos [Post]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleSetPrimaryPhotos() errchain.HandlerFunc {
|
||||
return actionHandlerFactory("ensure asset IDs", ctrl.repo.Items.SetPrimaryPhotos)
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -27,17 +27,17 @@ import (
|
||||
func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
assetIdParam := chi.URLParam(r, "id")
|
||||
assetIdParam = strings.ReplaceAll(assetIdParam, "-", "") // Remove dashes
|
||||
assetIDParam := chi.URLParam(r, "id")
|
||||
assetIDParam = strings.ReplaceAll(assetIDParam, "-", "") // Remove dashes
|
||||
// Convert the asset ID to an int64
|
||||
assetId, err := strconv.ParseInt(assetIdParam, 10, 64)
|
||||
assetID, err := strconv.ParseInt(assetIDParam, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pageParam := r.URL.Query().Get("page")
|
||||
var page int64 = -1
|
||||
if pageParam != "" {
|
||||
page, err = strconv.ParseInt(pageParam, 10, 64)
|
||||
page, err = strconv.ParseInt(pageParam, 10, 32)
|
||||
if err != nil {
|
||||
return server.JSON(w, http.StatusBadRequest, "Invalid page number")
|
||||
}
|
||||
@@ -46,13 +46,13 @@ func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
|
||||
pageSizeParam := r.URL.Query().Get("pageSize")
|
||||
var pageSize int64 = -1
|
||||
if pageSizeParam != "" {
|
||||
pageSize, err = strconv.ParseInt(pageSizeParam, 10, 64)
|
||||
pageSize, err = strconv.ParseInt(pageSizeParam, 10, 32)
|
||||
if err != nil {
|
||||
return server.JSON(w, http.StatusBadRequest, "Invalid page size")
|
||||
}
|
||||
}
|
||||
|
||||
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetId), int(page), int(pageSize))
|
||||
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetID), int(page), int(pageSize))
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get item")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
|
||||
@@ -3,14 +3,21 @@ package v1
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
const (
|
||||
cookieNameToken = "hb.auth.token"
|
||||
cookieNameRemember = "hb.auth.remember"
|
||||
cookieNameSession = "hb.auth.session"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -27,6 +34,49 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
type CookieContents struct {
|
||||
Token string
|
||||
ExpiresAt time.Time
|
||||
Remember bool
|
||||
}
|
||||
|
||||
func GetCookies(r *http.Request) (*CookieContents, error) {
|
||||
cookie, err := r.Cookie(cookieNameToken)
|
||||
if err != nil {
|
||||
return nil, errors.New("authorization cookie is required")
|
||||
}
|
||||
|
||||
rememberCookie, err := r.Cookie(cookieNameRemember)
|
||||
if err != nil {
|
||||
return nil, errors.New("remember cookie is required")
|
||||
}
|
||||
|
||||
return &CookieContents{
|
||||
Token: cookie.Value,
|
||||
ExpiresAt: cookie.Expires,
|
||||
Remember: rememberCookie.Value == "true",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AuthProvider is an interface that can be implemented by any authentication provider.
|
||||
// to extend authentication methods for the API.
|
||||
type AuthProvider interface {
|
||||
// Name returns the name of the authentication provider. This should be a unique name.
|
||||
// that is URL friendly.
|
||||
//
|
||||
// Example: "local", "ldap"
|
||||
Name() string
|
||||
// Authenticate is called when a user attempts to login to the API. The implementation
|
||||
// should return an error if the user cannot be authenticated. If an error is returned
|
||||
// the API controller will return a vague error message to the user.
|
||||
//
|
||||
// Authenticate should do the following:
|
||||
//
|
||||
// 1. Ensure that the user exists within the database (either create, or get)
|
||||
// 2. On successful authentication, they must set the user cookies.
|
||||
Authenticate(w http.ResponseWriter, r *http.Request) (services.UserAuthTokenDetail, error)
|
||||
}
|
||||
|
||||
// HandleAuthLogin godoc
|
||||
//
|
||||
// @Summary User Login
|
||||
@@ -35,52 +85,42 @@ type (
|
||||
// @Accept application/json
|
||||
// @Param username formData string false "string" example(admin@admin.com)
|
||||
// @Param password formData string false "string" example(admin)
|
||||
// @Param payload body LoginForm true "Login Data"
|
||||
// @Param payload body LoginForm true "Login Data"
|
||||
// @Param provider query string false "auth provider"
|
||||
// @Produce json
|
||||
// @Success 200 {object} TokenResponse
|
||||
// @Router /v1/users/login [POST]
|
||||
func (ctrl *V1Controller) HandleAuthLogin() errchain.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleAuthLogin(ps ...AuthProvider) errchain.HandlerFunc {
|
||||
if len(ps) == 0 {
|
||||
panic("no auth providers provided")
|
||||
}
|
||||
|
||||
providers := make(map[string]AuthProvider)
|
||||
for _, p := range ps {
|
||||
log.Info().Str("name", p.Name()).Msg("registering auth provider")
|
||||
providers[p.Name()] = p
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
loginForm := &LoginForm{}
|
||||
|
||||
switch r.Header.Get("Content-Type") {
|
||||
case "application/x-www-form-urlencoded":
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
return errors.New("failed to parse form")
|
||||
}
|
||||
|
||||
loginForm.Username = r.PostFormValue("username")
|
||||
loginForm.Password = r.PostFormValue("password")
|
||||
loginForm.StayLoggedIn = r.PostFormValue("stayLoggedIn") == "true"
|
||||
case "application/json":
|
||||
err := server.Decode(r, loginForm)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to decode login form")
|
||||
return errors.New("failed to decode login form")
|
||||
}
|
||||
default:
|
||||
return server.JSON(w, http.StatusBadRequest, errors.New("invalid content type"))
|
||||
// Extract provider query
|
||||
provider := r.URL.Query().Get("provider")
|
||||
if provider == "" {
|
||||
provider = "local"
|
||||
}
|
||||
|
||||
if loginForm.Username == "" || loginForm.Password == "" {
|
||||
return validate.NewFieldErrors(
|
||||
validate.FieldError{
|
||||
Field: "username",
|
||||
Error: "username or password is empty",
|
||||
},
|
||||
validate.FieldError{
|
||||
Field: "password",
|
||||
Error: "username or password is empty",
|
||||
},
|
||||
)
|
||||
// Get the provider
|
||||
p, ok := providers[provider]
|
||||
if !ok {
|
||||
return validate.NewRequestError(errors.New("invalid auth provider"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
newToken, err := ctrl.svc.User.Login(r.Context(), strings.ToLower(loginForm.Username), loginForm.Password, loginForm.StayLoggedIn)
|
||||
newToken, err := p.Authenticate(w, r)
|
||||
if err != nil {
|
||||
return validate.NewRequestError(errors.New("authentication failed"), http.StatusInternalServerError)
|
||||
log.Err(err).Msg("failed to authenticate")
|
||||
return server.JSON(w, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true)
|
||||
return server.JSON(w, http.StatusOK, TokenResponse{
|
||||
Token: "Bearer " + newToken.Raw,
|
||||
ExpiresAt: newToken.ExpiresAt,
|
||||
@@ -108,11 +148,12 @@ func (ctrl *V1Controller) HandleAuthLogout() errchain.HandlerFunc {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
ctrl.unsetCookies(w, noPort(r.Host))
|
||||
return server.JSON(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleAuthLogout godoc
|
||||
// HandleAuthRefresh godoc
|
||||
//
|
||||
// @Summary User Token Refresh
|
||||
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.
|
||||
@@ -133,6 +174,78 @@ func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
|
||||
return validate.NewUnauthorizedError()
|
||||
}
|
||||
|
||||
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, false)
|
||||
return server.JSON(w, http.StatusOK, newToken)
|
||||
}
|
||||
}
|
||||
|
||||
func noPort(host string) string {
|
||||
return strings.Split(host, ":")[0]
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string, expires time.Time, remember bool) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameRemember,
|
||||
Value: strconv.FormatBool(remember),
|
||||
Expires: expires,
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: true,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
// Set HTTP only cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameToken,
|
||||
Value: token,
|
||||
Expires: expires,
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: true,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
// Set Fake Session cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameSession,
|
||||
Value: "true",
|
||||
Expires: expires,
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: false,
|
||||
Path: "/",
|
||||
})
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameToken,
|
||||
Value: "",
|
||||
Expires: time.Unix(0, 0),
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: true,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameRemember,
|
||||
Value: "false",
|
||||
Expires: time.Unix(0, 0),
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: true,
|
||||
Path: "/",
|
||||
})
|
||||
|
||||
// Set Fake Session cookie
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieNameSession,
|
||||
Value: "false",
|
||||
Expires: time.Unix(0, 0),
|
||||
Domain: domain,
|
||||
Secure: ctrl.cookieSecure,
|
||||
HttpOnly: false,
|
||||
Path: "/",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
type (
|
||||
GroupInvitationCreate struct {
|
||||
Uses int `json:"uses" validate:"required,min=1,max=100"`
|
||||
Uses int `json:"uses" validate:"required,min=1,max=100"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
@@ -52,6 +53,14 @@ func (ctrl *V1Controller) HandleGroupGet() errchain.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleGroupUpdate() errchain.HandlerFunc {
|
||||
fn := func(r *http.Request, body repo.GroupUpdate) (repo.Group, error) {
|
||||
auth := services.NewContext(r.Context())
|
||||
|
||||
ok := ctrl.svc.Currencies.IsSupported(body.Currency)
|
||||
if !ok {
|
||||
return repo.Group{}, validate.NewFieldErrors(
|
||||
validate.NewFieldError("currency", "currency '"+body.Currency+"' is not supported"),
|
||||
)
|
||||
}
|
||||
|
||||
return ctrl.svc.Group.UpdateGroup(auth, body)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
// HandleItemsGetAll godoc
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
// @Param pageSize query int false "items per page"
|
||||
// @Param labels query []string false "label Ids" collectionFormat(multi)
|
||||
// @Param locations query []string false "location Ids" collectionFormat(multi)
|
||||
// @Param parentIds query []string false "parent Ids" collectionFormat(multi)
|
||||
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
|
||||
// @Router /v1/items [GET]
|
||||
// @Security Bearer
|
||||
@@ -56,8 +57,10 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: queryUUIDList(params, "locations"),
|
||||
LabelIDs: queryUUIDList(params, "labels"),
|
||||
ParentItemIDs: queryUUIDList(params, "parentIds"),
|
||||
IncludeArchived: queryBool(params.Get("includeArchived")),
|
||||
Fields: filterFieldItems(params["fields"]),
|
||||
OrderBy: params.Get("orderBy"),
|
||||
}
|
||||
|
||||
if strings.HasPrefix(v.Search, "#") {
|
||||
@@ -90,6 +93,48 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// HandleItemFullPath godoc
|
||||
//
|
||||
// @Summary Get the full path of an item
|
||||
// @Tags Items
|
||||
// @Produce json
|
||||
// @Param id path string true "Item ID"
|
||||
// @Success 200 {object} []repo.ItemPath
|
||||
// @Router /v1/items/{id}/path [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemFullPath() errchain.HandlerFunc {
|
||||
fn := func(r *http.Request, ID uuid.UUID) ([]repo.ItemPath, error) {
|
||||
auth := services.NewContext(r.Context())
|
||||
item, err := ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paths, err := ctrl.repo.Locations.PathForLoc(auth, auth.GID, item.Location.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if item.Parent != nil {
|
||||
paths = append(paths, repo.ItemPath{
|
||||
Type: repo.ItemTypeItem,
|
||||
ID: item.Parent.ID,
|
||||
Name: item.Parent.Name,
|
||||
})
|
||||
}
|
||||
|
||||
paths = append(paths, repo.ItemPath{
|
||||
Type: repo.ItemTypeItem,
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
})
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
return adapters.CommandID("id", fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleItemsCreate godoc
|
||||
//
|
||||
// @Summary Create Item
|
||||
@@ -166,6 +211,32 @@ func (ctrl *V1Controller) HandleItemUpdate() errchain.HandlerFunc {
|
||||
return adapters.ActionID("id", fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleItemPatch godocs
|
||||
//
|
||||
// @Summary Update Item
|
||||
// @Tags Items
|
||||
// @Produce json
|
||||
// @Param id path string true "Item ID"
|
||||
// @Param payload body repo.ItemPatch true "Item Data"
|
||||
// @Success 200 {object} repo.ItemOut
|
||||
// @Router /v1/items/{id} [Patch]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemPatch() errchain.HandlerFunc {
|
||||
fn := func(r *http.Request, ID uuid.UUID, body repo.ItemPatch) (repo.ItemOut, error) {
|
||||
auth := services.NewContext(r.Context())
|
||||
|
||||
body.ID = ID
|
||||
err := ctrl.repo.Items.Patch(auth, auth.GID, ID, body)
|
||||
if err != nil {
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
|
||||
return ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
|
||||
}
|
||||
|
||||
return adapters.ActionID("id", fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleGetAllCustomFieldNames godocs
|
||||
//
|
||||
// @Summary Get All Custom Field Names
|
||||
@@ -203,8 +274,7 @@ func (ctrl *V1Controller) HandleGetAllCustomFieldValues() errchain.HandlerFunc {
|
||||
return ctrl.repo.Items.GetAllCustomFieldValues(auth, auth.GID, q.Field)
|
||||
}
|
||||
|
||||
return adapters.Action(fn, http.StatusOK)
|
||||
|
||||
return adapters.Query(fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleItemsImport godocs
|
||||
@@ -261,7 +331,9 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
|
||||
|
||||
w.Header().Set("Content-Type", "text/tsv")
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=homebox-items.tsv")
|
||||
|
||||
writer := csv.NewWriter(w)
|
||||
writer.Comma = '\t'
|
||||
return writer.WriteAll(csvData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,16 @@ package v1
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -29,7 +31,7 @@ type (
|
||||
// @Param type formData string true "Type of file"
|
||||
// @Param name formData string true "name of the file including extension"
|
||||
// @Success 200 {object} repo.ItemOut
|
||||
// @Failure 422 {object} mid.ErrorResponse
|
||||
// @Failure 422 {object} validate.ErrorResponse
|
||||
// @Router /v1/items/{id}/attachments [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
|
||||
@@ -38,7 +40,6 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to parse multipart form")
|
||||
return validate.NewRequestError(errors.New("failed to parse multipart form"), http.StatusBadRequest)
|
||||
|
||||
}
|
||||
|
||||
errs := validate.NewFieldErrors()
|
||||
@@ -67,7 +68,15 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
|
||||
|
||||
attachmentType := r.FormValue("type")
|
||||
if attachmentType == "" {
|
||||
attachmentType = attachment.TypeAttachment.String()
|
||||
// Attempt to auto-detect the type of the file
|
||||
ext := filepath.Ext(attachmentName)
|
||||
|
||||
switch strings.ToLower(ext) {
|
||||
case ".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tiff":
|
||||
attachmentType = attachment.TypePhoto.String()
|
||||
default:
|
||||
attachmentType = attachment.TypeAttachment.String()
|
||||
}
|
||||
}
|
||||
|
||||
id, err := ctrl.routeID(r)
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
// HandleLabelsGetAll godoc
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
// HandleLocationTreeQuery
|
||||
// HandleLocationTreeQuery godoc
|
||||
//
|
||||
// @Summary Get Locations Tree
|
||||
// @Tags Locations
|
||||
@@ -28,7 +28,7 @@ func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
|
||||
return adapters.Query(fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleLocationGetAll
|
||||
// HandleLocationGetAll godoc
|
||||
//
|
||||
// @Summary Get All Locations
|
||||
// @Tags Locations
|
||||
@@ -46,7 +46,7 @@ func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
|
||||
return adapters.Query(fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleLocationCreate
|
||||
// HandleLocationCreate godoc
|
||||
//
|
||||
// @Summary Create Location
|
||||
// @Tags Locations
|
||||
@@ -64,7 +64,7 @@ func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
|
||||
return adapters.Action(fn, http.StatusCreated)
|
||||
}
|
||||
|
||||
// HandleLocationDelete
|
||||
// HandleLocationDelete godoc
|
||||
//
|
||||
// @Summary Delete Location
|
||||
// @Tags Locations
|
||||
@@ -83,7 +83,7 @@ func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
|
||||
return adapters.CommandID("id", fn, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// HandleLocationGet
|
||||
// HandleLocationGet godoc
|
||||
//
|
||||
// @Summary Get Location
|
||||
// @Tags Locations
|
||||
@@ -101,7 +101,7 @@ func (ctrl *V1Controller) HandleLocationGet() errchain.HandlerFunc {
|
||||
return adapters.CommandID("id", fn, http.StatusOK)
|
||||
}
|
||||
|
||||
// HandleLocationUpdate
|
||||
// HandleLocationUpdate godoc
|
||||
//
|
||||
// @Summary Update Location
|
||||
// @Tags Locations
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
// HandleMaintenanceGetLog godoc
|
||||
// HandleMaintenanceLogGet godoc
|
||||
//
|
||||
// @Summary Get Maintenance Log
|
||||
// @Tags Maintenance
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
|
||||
"github.com/containrrr/shoutrrr"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
// HandleGetUserNotifiers godoc
|
||||
|
||||
@@ -5,9 +5,10 @@ import (
|
||||
"image/png"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
"github.com/yeqown/go-qrcode/v2"
|
||||
"github.com/yeqown/go-qrcode/writer/standard"
|
||||
|
||||
@@ -43,7 +44,12 @@ func (ctrl *V1Controller) HandleGenerateQRCode() errchain.HandlerFunc {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
qrc, err := qrcode.New(q.Data)
|
||||
decodedStr, err := url.QueryUnescape(q.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
qrc, err := qrcode.New(decodedStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
)
|
||||
|
||||
// HandleBillOfMaterialsExport godoc
|
||||
@@ -24,8 +23,8 @@ func (ctrl *V1Controller) HandleBillOfMaterialsExport() errchain.HandlerFunc {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/tsv")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=bill-of-materials.tsv")
|
||||
w.Header().Set("Content-Type", "text/csv")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=bill-of-materials.csv")
|
||||
_, err = w.Write(csv)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/adapters"
|
||||
)
|
||||
|
||||
// HandleGroupGet godoc
|
||||
// HandleGroupStatisticsLocations godoc
|
||||
//
|
||||
// @Summary Get Location Statistics
|
||||
// @Tags Statistics
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
// HandleUserRegistration godoc
|
||||
|
||||
@@ -2,11 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/config"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
)
|
||||
|
||||
// setupLogger initializes the zerolog config
|
||||
@@ -18,24 +17,8 @@ func (a *app) setupLogger() {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger()
|
||||
}
|
||||
|
||||
log.Level(getLevel(a.conf.Log.Level))
|
||||
}
|
||||
|
||||
func getLevel(l string) zerolog.Level {
|
||||
switch strings.ToLower(l) {
|
||||
case "debug":
|
||||
return zerolog.DebugLevel
|
||||
case "info":
|
||||
return zerolog.InfoLevel
|
||||
case "warn":
|
||||
return zerolog.WarnLevel
|
||||
case "error":
|
||||
return zerolog.ErrorLevel
|
||||
case "fatal":
|
||||
return zerolog.FatalLevel
|
||||
case "panic":
|
||||
return zerolog.PanicLevel
|
||||
default:
|
||||
return zerolog.InfoLevel
|
||||
level, err := zerolog.ParseLevel(a.conf.Log.Level)
|
||||
if err == nil {
|
||||
zerolog.SetGlobalLevel(level)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -13,20 +14,21 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/app/api/static/docs"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/migrations"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/config"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/mid"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/safeserve/server"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/hay-kot/httpkit/graceful"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/rs/zerolog/pkgerrors"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/currencies"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/migrations"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/config"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/web/mid"
|
||||
|
||||
_ "github.com/hay-kot/homebox/backend/pkgs/cgofreesqlite"
|
||||
_ "github.com/sysadminsmedia/homebox/backend/pkgs/cgofreesqlite"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -35,11 +37,19 @@ var (
|
||||
buildTime = "now"
|
||||
)
|
||||
|
||||
func build() string {
|
||||
short := commit
|
||||
if len(short) > 7 {
|
||||
short = short[:7]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s, commit %s, built at %s", version, short, buildTime)
|
||||
}
|
||||
|
||||
// @title Homebox API
|
||||
// @version 1.0
|
||||
// @description Track, Manage, and Organize your Things.
|
||||
// @contact.name Don't
|
||||
// @license.name MIT
|
||||
// @BasePath /api
|
||||
// @securityDefinitions.apikey Bearer
|
||||
// @in header
|
||||
@@ -48,13 +58,11 @@ var (
|
||||
func main() {
|
||||
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
||||
|
||||
cfg, err := config.New()
|
||||
cfg, err := config.New(build(), "Homebox inventory management system")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
docs.SwaggerInfo.Host = cfg.Swagger.Host
|
||||
|
||||
if err := run(cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -72,12 +80,12 @@ func run(cfg *config.Config) error {
|
||||
log.Fatal().Err(err).Msg("failed to create data directory")
|
||||
}
|
||||
|
||||
c, err := ent.Open("sqlite3", cfg.Storage.SqliteUrl)
|
||||
c, err := ent.Open("sqlite3", cfg.Storage.SqliteURL)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Str("driver", "sqlite").
|
||||
Str("url", cfg.Storage.SqliteUrl).
|
||||
Str("url", cfg.Storage.SqliteURL).
|
||||
Msg("failed opening connection to sqlite")
|
||||
}
|
||||
defer func(c *ent.Client) {
|
||||
@@ -110,7 +118,7 @@ func run(cfg *config.Config) error {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Str("driver", "sqlite").
|
||||
Str("url", cfg.Storage.SqliteUrl).
|
||||
Str("url", cfg.Storage.SqliteURL).
|
||||
Msg("failed creating schema resources")
|
||||
}
|
||||
|
||||
@@ -120,11 +128,40 @@ func run(cfg *config.Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
collectFuncs := []currencies.CollectorFunc{
|
||||
currencies.CollectDefaults(),
|
||||
}
|
||||
|
||||
if cfg.Options.CurrencyConfig != "" {
|
||||
log.Info().
|
||||
Str("path", cfg.Options.CurrencyConfig).
|
||||
Msg("loading currency config file")
|
||||
|
||||
content, err := os.ReadFile(cfg.Options.CurrencyConfig)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Str("path", cfg.Options.CurrencyConfig).
|
||||
Msg("failed to read currency config file")
|
||||
}
|
||||
|
||||
collectFuncs = append(collectFuncs, currencies.CollectJSON(bytes.NewReader(content)))
|
||||
}
|
||||
|
||||
currencies, err := currencies.CollectionCurrencies(collectFuncs...)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Msg("failed to collect currencies")
|
||||
}
|
||||
|
||||
app.bus = eventbus.New()
|
||||
app.db = c
|
||||
app.repos = repo.New(c, cfg.Storage.Data)
|
||||
app.repos = repo.New(c, app.bus, cfg.Storage.Data)
|
||||
app.services = services.New(
|
||||
app.repos,
|
||||
services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID),
|
||||
services.WithCurrencies(currencies),
|
||||
)
|
||||
|
||||
// =========================================================================
|
||||
@@ -141,36 +178,63 @@ func run(cfg *config.Config) error {
|
||||
middleware.StripSlashes,
|
||||
)
|
||||
|
||||
chain := errchain.New(mid.Errors(app.server, logger))
|
||||
chain := errchain.New(mid.Errors(logger))
|
||||
|
||||
app.mountRoutes(router, chain, app.repos)
|
||||
|
||||
app.server = server.NewServer(
|
||||
server.WithHost(app.conf.Web.Host),
|
||||
server.WithPort(app.conf.Web.Port),
|
||||
)
|
||||
log.Info().Msgf("Starting HTTP Server on %s:%s", app.server.Host, app.server.Port)
|
||||
runner := graceful.NewRunner()
|
||||
|
||||
runner.AddFunc("server", func(ctx context.Context) error {
|
||||
httpserver := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%s", cfg.Web.Host, cfg.Web.Port),
|
||||
Handler: router,
|
||||
ReadTimeout: cfg.Web.ReadTimeout,
|
||||
WriteTimeout: cfg.Web.WriteTimeout,
|
||||
IdleTimeout: cfg.Web.IdleTimeout,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = httpserver.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
log.Info().Msgf("Server is running on %s:%s", cfg.Web.Host, cfg.Web.Port)
|
||||
return httpserver.ListenAndServe()
|
||||
})
|
||||
|
||||
// =========================================================================
|
||||
// Start Reoccurring Tasks
|
||||
|
||||
go app.startBgTask(time.Duration(24)*time.Hour, func() {
|
||||
_, err := app.repos.AuthTokens.PurgeExpiredTokens(context.Background())
|
||||
runner.AddFunc("eventbus", app.bus.Run)
|
||||
|
||||
runner.AddFunc("seed_database", func(ctx context.Context) error {
|
||||
// TODO: Remove through external API that does setup
|
||||
if cfg.Demo {
|
||||
log.Info().Msg("Running in demo mode, creating demo data")
|
||||
app.SetupDemo()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
runner.AddPlugin(NewTask("purge-tokens", time.Duration(24)*time.Hour, func(ctx context.Context) {
|
||||
_, err := app.repos.AuthTokens.PurgeExpiredTokens(ctx)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to purge expired tokens")
|
||||
}
|
||||
})
|
||||
go app.startBgTask(time.Duration(24)*time.Hour, func() {
|
||||
_, err := app.repos.Groups.InvitationPurge(context.Background())
|
||||
}))
|
||||
|
||||
runner.AddPlugin(NewTask("purge-invitations", time.Duration(24)*time.Hour, func(ctx context.Context) {
|
||||
_, err := app.repos.Groups.InvitationPurge(ctx)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("failed to purge expired invitations")
|
||||
}
|
||||
})
|
||||
go app.startBgTask(time.Duration(1)*time.Hour, func() {
|
||||
}))
|
||||
|
||||
runner.AddPlugin(NewTask("send-notifications", time.Duration(1)*time.Hour, func(ctx context.Context) {
|
||||
now := time.Now()
|
||||
|
||||
if now.Hour() == 8 {
|
||||
@@ -182,22 +246,27 @@ func run(cfg *config.Config) error {
|
||||
Msg("failed to send notifiers")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: Remove through external API that does setup
|
||||
if cfg.Demo {
|
||||
log.Info().Msg("Running in demo mode, creating demo data")
|
||||
app.SetupDemo()
|
||||
}
|
||||
}))
|
||||
|
||||
if cfg.Debug.Enabled {
|
||||
debugrouter := app.debugRouter()
|
||||
go func() {
|
||||
if err := http.ListenAndServe(":"+cfg.Debug.Port, debugrouter); err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to start debug server")
|
||||
runner.AddFunc("debug", func(ctx context.Context) error {
|
||||
debugserver := http.Server{
|
||||
Addr: fmt.Sprintf("%s:%s", cfg.Web.Host, cfg.Debug.Port),
|
||||
Handler: app.debugRouter(),
|
||||
ReadTimeout: cfg.Web.ReadTimeout,
|
||||
WriteTimeout: cfg.Web.WriteTimeout,
|
||||
IdleTimeout: cfg.Web.IdleTimeout,
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = debugserver.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
log.Info().Msgf("Debug server is running on %s:%s", cfg.Web.Host, cfg.Debug.Port)
|
||||
return debugserver.ListenAndServe()
|
||||
})
|
||||
}
|
||||
|
||||
return app.server.Start(router)
|
||||
return runner.Start(context.Background())
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
v1 "github.com/sysadminsmedia/homebox/backend/app/api/handlers/v1"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
type tokenHasKey struct {
|
||||
@@ -94,20 +96,6 @@ func getQuery(r *http.Request) (string, error) {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func getCookie(r *http.Request) (string, error) {
|
||||
cookie, err := r.Cookie("hb.auth.token")
|
||||
if err != nil {
|
||||
return "", errors.New("access_token cookie is required")
|
||||
}
|
||||
|
||||
token, err := url.QueryUnescape(cookie.Value)
|
||||
if err != nil {
|
||||
return "", errors.New("access_token cookie is required")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// mwAuthToken is a middleware that will check the database for a stateful token
|
||||
// and attach it's user to the request context, or return an appropriate error.
|
||||
// Authorization support is by token via Headers or Query Parameter
|
||||
@@ -115,26 +103,35 @@ func getCookie(r *http.Request) (string, error) {
|
||||
// Example:
|
||||
// - header = "Bearer 1234567890"
|
||||
// - query = "?access_token=1234567890"
|
||||
// - cookie = hb.auth.token = 1234567890
|
||||
func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
|
||||
return errchain.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
keyFuncs := [...]KeyFunc{
|
||||
getBearer,
|
||||
getCookie,
|
||||
getQuery,
|
||||
}
|
||||
|
||||
var requestToken string
|
||||
for _, keyFunc := range keyFuncs {
|
||||
token, err := keyFunc(r)
|
||||
if err == nil {
|
||||
requestToken = token
|
||||
break
|
||||
|
||||
// We ignore the error to allow the next strategy to be attempted
|
||||
{
|
||||
cookies, _ := v1.GetCookies(r)
|
||||
if cookies != nil {
|
||||
requestToken = cookies.Token
|
||||
}
|
||||
}
|
||||
|
||||
if requestToken == "" {
|
||||
return validate.NewRequestError(errors.New("Authorization header or query is required"), http.StatusUnauthorized)
|
||||
keyFuncs := [...]KeyFunc{
|
||||
getBearer,
|
||||
getQuery,
|
||||
}
|
||||
|
||||
for _, keyFunc := range keyFuncs {
|
||||
token, err := keyFunc(r)
|
||||
if err == nil {
|
||||
requestToken = token
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if requestToken == "" {
|
||||
return validate.NewRequestError(errors.New("authorization header or query is required"), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
requestToken = strings.TrimPrefix(requestToken, "Bearer ")
|
||||
@@ -144,7 +141,11 @@ func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
|
||||
usr, err := a.services.User.GetSelf(r.Context(), requestToken)
|
||||
// Check the database for the token
|
||||
if err != nil {
|
||||
return validate.NewRequestError(errors.New("valid authorization header is required"), http.StatusUnauthorized)
|
||||
if ent.IsNotFound(err) {
|
||||
return validate.NewRequestError(errors.New("valid authorization token is required"), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken))
|
||||
|
||||
2
backend/app/api/providers/doc.go
Normal file
2
backend/app/api/providers/doc.go
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package providers provides a authentication abstraction for the backend.
|
||||
package providers
|
||||
55
backend/app/api/providers/extractors.go
Normal file
55
backend/app/api/providers/extractors.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/hay-kot/httpkit/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
type LoginForm struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
StayLoggedIn bool `json:"stayLoggedIn"`
|
||||
}
|
||||
|
||||
func getLoginForm(r *http.Request) (LoginForm, error) {
|
||||
loginForm := LoginForm{}
|
||||
|
||||
switch r.Header.Get("Content-Type") {
|
||||
case "application/x-www-form-urlencoded":
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
return loginForm, errors.New("failed to parse form")
|
||||
}
|
||||
|
||||
loginForm.Username = r.PostFormValue("username")
|
||||
loginForm.Password = r.PostFormValue("password")
|
||||
loginForm.StayLoggedIn = r.PostFormValue("stayLoggedIn") == "true"
|
||||
case "application/json":
|
||||
err := server.Decode(r, &loginForm)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to decode login form")
|
||||
return loginForm, errors.New("failed to decode login form")
|
||||
}
|
||||
default:
|
||||
return loginForm, errors.New("invalid content type")
|
||||
}
|
||||
|
||||
if loginForm.Username == "" || loginForm.Password == "" {
|
||||
return loginForm, validate.NewFieldErrors(
|
||||
validate.FieldError{
|
||||
Field: "username",
|
||||
Error: "username or password is empty",
|
||||
},
|
||||
validate.FieldError{
|
||||
Field: "password",
|
||||
Error: "username or password is empty",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return loginForm, nil
|
||||
}
|
||||
30
backend/app/api/providers/local.go
Normal file
30
backend/app/api/providers/local.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
)
|
||||
|
||||
type LocalProvider struct {
|
||||
service *services.UserService
|
||||
}
|
||||
|
||||
func NewLocalProvider(service *services.UserService) *LocalProvider {
|
||||
return &LocalProvider{
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *LocalProvider) Name() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
func (p *LocalProvider) Authenticate(w http.ResponseWriter, r *http.Request) (services.UserAuthTokenDetail, error) {
|
||||
loginForm, err := getLoginForm(r)
|
||||
if err != nil {
|
||||
return services.UserAuthTokenDetail{}, err
|
||||
}
|
||||
|
||||
return p.service.Login(r.Context(), loginForm.Username, loginForm.Password, loginForm.StayLoggedIn)
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
@@ -11,13 +10,14 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers"
|
||||
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1"
|
||||
_ "github.com/hay-kot/homebox/backend/app/api/static/docs"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/safeserve/errchain"
|
||||
httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
httpSwagger "github.com/swaggo/http-swagger/v2" // http-swagger middleware
|
||||
"github.com/sysadminsmedia/homebox/backend/app/api/handlers/debughandlers"
|
||||
v1 "github.com/sysadminsmedia/homebox/backend/app/api/handlers/v1"
|
||||
"github.com/sysadminsmedia/homebox/backend/app/api/providers"
|
||||
_ "github.com/sysadminsmedia/homebox/backend/app/api/static/docs"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
const prefix = "/api"
|
||||
@@ -41,17 +41,18 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
registerMimes()
|
||||
|
||||
r.Get("/swagger/*", httpSwagger.Handler(
|
||||
httpSwagger.URL(fmt.Sprintf("%s://%s/swagger/doc.json", a.conf.Swagger.Scheme, a.conf.Swagger.Host)),
|
||||
httpSwagger.URL("/swagger/doc.json"),
|
||||
))
|
||||
|
||||
// =========================================================================
|
||||
// API Version 1
|
||||
|
||||
v1Base := v1.BaseUrlFunc(prefix)
|
||||
v1Base := v1.BaseURLFunc(prefix)
|
||||
|
||||
v1Ctrl := v1.NewControllerV1(
|
||||
a.services,
|
||||
a.repos,
|
||||
a.bus,
|
||||
v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize),
|
||||
v1.WithRegistration(a.conf.Options.AllowRegistration),
|
||||
v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode
|
||||
@@ -63,14 +64,21 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
BuildTime: buildTime,
|
||||
})))
|
||||
|
||||
r.Get(v1Base("/currencies"), chain.ToHandlerFunc(v1Ctrl.HandleCurrency()))
|
||||
|
||||
providers := []v1.AuthProvider{
|
||||
providers.NewLocalProvider(a.services.User),
|
||||
}
|
||||
|
||||
r.Post(v1Base("/users/register"), chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration()))
|
||||
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin()))
|
||||
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providers...)))
|
||||
|
||||
userMW := []errchain.Middleware{
|
||||
a.mwAuthToken,
|
||||
a.mwRoles(RoleModeOr, authroles.RoleUser.String()),
|
||||
}
|
||||
|
||||
r.Get(v1Base("/ws/events"), chain.ToHandlerFunc(v1Ctrl.HandleCacheWS(), userMW...))
|
||||
r.Get(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelf(), userMW...))
|
||||
r.Put(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfUpdate(), userMW...))
|
||||
r.Delete(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfDelete(), userMW...))
|
||||
@@ -91,6 +99,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
r.Post(v1Base("/actions/ensure-asset-ids"), chain.ToHandlerFunc(v1Ctrl.HandleEnsureAssetID(), userMW...))
|
||||
r.Post(v1Base("/actions/zero-item-time-fields"), chain.ToHandlerFunc(v1Ctrl.HandleItemDateZeroOut(), userMW...))
|
||||
r.Post(v1Base("/actions/ensure-import-refs"), chain.ToHandlerFunc(v1Ctrl.HandleEnsureImportRefs(), userMW...))
|
||||
r.Post(v1Base("/actions/set-primary-photos"), chain.ToHandlerFunc(v1Ctrl.HandleSetPrimaryPhotos(), userMW...))
|
||||
|
||||
r.Get(v1Base("/locations"), chain.ToHandlerFunc(v1Ctrl.HandleLocationGetAll(), userMW...))
|
||||
r.Post(v1Base("/locations"), chain.ToHandlerFunc(v1Ctrl.HandleLocationCreate(), userMW...))
|
||||
@@ -113,7 +122,9 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
r.Get(v1Base("/items/fields/values"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldValues(), userMW...))
|
||||
|
||||
r.Get(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemGet(), userMW...))
|
||||
r.Get(v1Base("/items/{id}/path"), chain.ToHandlerFunc(v1Ctrl.HandleItemFullPath(), userMW...))
|
||||
r.Put(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemUpdate(), userMW...))
|
||||
r.Patch(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemPatch(), userMW...))
|
||||
r.Delete(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemDelete(), userMW...))
|
||||
|
||||
r.Post(v1Base("/items/{id}/attachments"), chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentCreate(), userMW...))
|
||||
@@ -125,7 +136,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
r.Put(v1Base("/items/{id}/maintenance/{entry_id}"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryUpdate(), userMW...))
|
||||
r.Delete(v1Base("/items/{id}/maintenance/{entry_id}"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryDelete(), userMW...))
|
||||
|
||||
r.Get(v1Base("/asset/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleAssetGet(), userMW...))
|
||||
r.Get(v1Base("/assets/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleAssetGet(), userMW...))
|
||||
|
||||
// Notifiers
|
||||
r.Get(v1Base("/notifiers"), chain.ToHandlerFunc(v1Ctrl.HandleGetUserNotifiers(), userMW...))
|
||||
@@ -153,7 +164,6 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
||||
r.Get(v1Base("/reporting/bill-of-materials"), chain.ToHandlerFunc(v1Ctrl.HandleBillOfMaterialsExport(), userMW...))
|
||||
|
||||
r.NotFound(chain.ToHandlerFunc(notFoundHandler()))
|
||||
|
||||
}
|
||||
|
||||
func registerMimes() {
|
||||
@@ -176,7 +186,7 @@ func notFoundHandler() errchain.HandlerFunc {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
stat, _ := f.Stat()
|
||||
if stat.IsDir() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Package docs GENERATED BY SWAG; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag
|
||||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
@@ -13,9 +12,6 @@ const docTemplate = `{
|
||||
"contact": {
|
||||
"name": "Don't"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
@@ -71,6 +67,31 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/actions/set-primary-photos": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sets the first photo of each item as the primary photo",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Actions"
|
||||
],
|
||||
"summary": "Set Primary Photos",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ActionAmountResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/actions/zero-item-time-fields": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -129,6 +150,25 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/currency": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Base"
|
||||
],
|
||||
"summary": "Currency",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/currencies.Currency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -389,6 +429,16 @@ const docTemplate = `{
|
||||
"description": "location Ids",
|
||||
"name": "locations",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "parent Ids",
|
||||
"name": "parentIds",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -638,6 +688,46 @@ const docTemplate = `{
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Update Item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Item Data",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemPatch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/attachments": {
|
||||
@@ -694,7 +784,7 @@ const docTemplate = `{
|
||||
"422": {
|
||||
"description": "Unprocessable Entity",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/mid.ErrorResponse"
|
||||
"$ref": "#/definitions/validate.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -927,6 +1017,42 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/path": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Get the full path of an item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemPath"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/labels": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1513,7 +1639,7 @@ const docTemplate = `{
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ApiSummary"
|
||||
"$ref": "#/definitions/v1.APISummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1584,6 +1710,12 @@ const docTemplate = `{
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.LoginForm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "auth provider",
|
||||
"name": "provider",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1762,17 +1894,20 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"mid.ErrorResponse": {
|
||||
"currencies.Currency": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1856,6 +1991,9 @@ const docTemplate = `{
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1867,6 +2005,9 @@ const docTemplate = `{
|
||||
"repo.ItemAttachmentUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1945,12 +2086,6 @@ const docTemplate = `{
|
||||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1966,6 +2101,9 @@ const docTemplate = `{
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"insured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1981,9 +2119,13 @@ const docTemplate = `{
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
}
|
||||
],
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
"x-omitempty": true
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string"
|
||||
@@ -1999,9 +2141,13 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
],
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
"x-omitempty": true
|
||||
},
|
||||
"purchaseFrom": {
|
||||
"type": "string"
|
||||
@@ -2045,6 +2191,33 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemPatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"quantity": {
|
||||
"type": "integer",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemPath": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/repo.ItemType"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2060,6 +2233,9 @@ const docTemplate = `{
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"insured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -2071,9 +2247,13 @@ const docTemplate = `{
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
}
|
||||
],
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
"x-omitempty": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
@@ -2090,6 +2270,17 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"location",
|
||||
"item"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ItemTypeLocation",
|
||||
"ItemTypeItem"
|
||||
]
|
||||
},
|
||||
"repo.ItemUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2182,7 +2373,6 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"warrantyExpires": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2219,12 +2409,6 @@ const docTemplate = `{
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2286,12 +2470,6 @@ const docTemplate = `{
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2368,7 +2546,6 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
},
|
||||
"cost": {
|
||||
@@ -2385,7 +2562,6 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"scheduledDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2397,7 +2573,6 @@ const docTemplate = `{
|
||||
],
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
},
|
||||
"cost": {
|
||||
@@ -2411,7 +2586,6 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"scheduledDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2420,7 +2594,6 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
},
|
||||
"cost": {
|
||||
@@ -2434,7 +2607,6 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"scheduledDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2670,15 +2842,7 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ApiSummary": {
|
||||
"v1.APISummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowRegistration": {
|
||||
@@ -2707,6 +2871,14 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Build": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2803,6 +2975,17 @@ const docTemplate = `{
|
||||
"properties": {
|
||||
"item": {}
|
||||
}
|
||||
},
|
||||
"validate.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
@@ -2825,6 +3008,8 @@ var SwaggerInfo = &swag.Spec{
|
||||
Description: "Track, Manage, and Organize your Things.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
"contact": {
|
||||
"name": "Don't"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"basePath": "/api",
|
||||
@@ -63,6 +60,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/actions/set-primary-photos": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sets the first photo of each item as the primary photo",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Actions"
|
||||
],
|
||||
"summary": "Set Primary Photos",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ActionAmountResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/actions/zero-item-time-fields": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -121,6 +143,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/currency": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Base"
|
||||
],
|
||||
"summary": "Currency",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/currencies.Currency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -381,6 +422,16 @@
|
||||
"description": "location Ids",
|
||||
"name": "locations",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "parent Ids",
|
||||
"name": "parentIds",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -630,6 +681,46 @@
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Update Item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Item Data",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemPatch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.ItemOut"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/attachments": {
|
||||
@@ -686,7 +777,7 @@
|
||||
"422": {
|
||||
"description": "Unprocessable Entity",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/mid.ErrorResponse"
|
||||
"$ref": "#/definitions/validate.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -919,6 +1010,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items/{id}/path": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Items"
|
||||
],
|
||||
"summary": "Get the full path of an item",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Item ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemPath"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/labels": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1505,7 +1632,7 @@
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ApiSummary"
|
||||
"$ref": "#/definitions/v1.APISummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1576,6 +1703,12 @@
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.LoginForm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "auth provider",
|
||||
"name": "provider",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1754,17 +1887,20 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"mid.ErrorResponse": {
|
||||
"currencies.Currency": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1848,6 +1984,9 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1859,6 +1998,9 @@
|
||||
"repo.ItemAttachmentUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"primary": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1937,12 +2079,6 @@
|
||||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1958,6 +2094,9 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"insured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1973,9 +2112,13 @@
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
}
|
||||
],
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
"x-omitempty": true
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string"
|
||||
@@ -1991,9 +2134,13 @@
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
],
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
"x-omitempty": true
|
||||
},
|
||||
"purchaseFrom": {
|
||||
"type": "string"
|
||||
@@ -2037,6 +2184,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemPatch": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"quantity": {
|
||||
"type": "integer",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemPath": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/repo.ItemType"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2052,6 +2226,9 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageId": {
|
||||
"type": "string"
|
||||
},
|
||||
"insured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -2063,9 +2240,13 @@
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
}
|
||||
],
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
"x-omitempty": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
@@ -2082,6 +2263,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.ItemType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"location",
|
||||
"item"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ItemTypeLocation",
|
||||
"ItemTypeItem"
|
||||
]
|
||||
},
|
||||
"repo.ItemUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2174,7 +2366,6 @@
|
||||
"type": "string"
|
||||
},
|
||||
"warrantyExpires": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2211,12 +2402,6 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2278,12 +2463,6 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2360,7 +2539,6 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
},
|
||||
"cost": {
|
||||
@@ -2377,7 +2555,6 @@
|
||||
"type": "string"
|
||||
},
|
||||
"scheduledDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2389,7 +2566,6 @@
|
||||
],
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
},
|
||||
"cost": {
|
||||
@@ -2403,7 +2579,6 @@
|
||||
"type": "string"
|
||||
},
|
||||
"scheduledDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2412,7 +2587,6 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completedDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
},
|
||||
"cost": {
|
||||
@@ -2426,7 +2600,6 @@
|
||||
"type": "string"
|
||||
},
|
||||
"scheduledDate": {
|
||||
"description": "Sold",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
@@ -2662,15 +2835,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ApiSummary": {
|
||||
"v1.APISummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowRegistration": {
|
||||
@@ -2699,6 +2864,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Build": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2795,6 +2968,17 @@
|
||||
"properties": {
|
||||
"item": {}
|
||||
}
|
||||
},
|
||||
"validate.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
basePath: /api
|
||||
definitions:
|
||||
mid.ErrorResponse:
|
||||
currencies.Currency:
|
||||
properties:
|
||||
error:
|
||||
code:
|
||||
type: string
|
||||
local:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
symbol:
|
||||
type: string
|
||||
fields:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
repo.DocumentOut:
|
||||
properties:
|
||||
@@ -61,6 +63,8 @@ definitions:
|
||||
$ref: '#/definitions/repo.DocumentOut'
|
||||
id:
|
||||
type: string
|
||||
primary:
|
||||
type: boolean
|
||||
type:
|
||||
type: string
|
||||
updatedAt:
|
||||
@@ -68,6 +72,8 @@ definitions:
|
||||
type: object
|
||||
repo.ItemAttachmentUpdate:
|
||||
properties:
|
||||
primary:
|
||||
type: boolean
|
||||
title:
|
||||
type: string
|
||||
type:
|
||||
@@ -121,10 +127,6 @@ definitions:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemAttachment'
|
||||
type: array
|
||||
children:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
@@ -135,6 +137,8 @@ definitions:
|
||||
type: array
|
||||
id:
|
||||
type: string
|
||||
imageId:
|
||||
type: string
|
||||
insured:
|
||||
type: boolean
|
||||
labels:
|
||||
@@ -145,7 +149,8 @@ definitions:
|
||||
description: Warranty
|
||||
type: boolean
|
||||
location:
|
||||
$ref: '#/definitions/repo.LocationSummary'
|
||||
allOf:
|
||||
- $ref: '#/definitions/repo.LocationSummary'
|
||||
description: Edges
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
@@ -159,7 +164,8 @@ definitions:
|
||||
description: Extras
|
||||
type: string
|
||||
parent:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
allOf:
|
||||
- $ref: '#/definitions/repo.ItemSummary'
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
purchaseFrom:
|
||||
@@ -191,6 +197,24 @@ definitions:
|
||||
warrantyExpires:
|
||||
type: string
|
||||
type: object
|
||||
repo.ItemPatch:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
quantity:
|
||||
type: integer
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
type: object
|
||||
repo.ItemPath:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
$ref: '#/definitions/repo.ItemType'
|
||||
type: object
|
||||
repo.ItemSummary:
|
||||
properties:
|
||||
archived:
|
||||
@@ -201,6 +225,8 @@ definitions:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
imageId:
|
||||
type: string
|
||||
insured:
|
||||
type: boolean
|
||||
labels:
|
||||
@@ -208,7 +234,8 @@ definitions:
|
||||
$ref: '#/definitions/repo.LabelSummary'
|
||||
type: array
|
||||
location:
|
||||
$ref: '#/definitions/repo.LocationSummary'
|
||||
allOf:
|
||||
- $ref: '#/definitions/repo.LocationSummary'
|
||||
description: Edges
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
@@ -222,6 +249,14 @@ definitions:
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
repo.ItemType:
|
||||
enum:
|
||||
- location
|
||||
- item
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- ItemTypeLocation
|
||||
- ItemTypeItem
|
||||
repo.ItemUpdate:
|
||||
properties:
|
||||
archived:
|
||||
@@ -287,7 +322,6 @@ definitions:
|
||||
warrantyDetails:
|
||||
type: string
|
||||
warrantyExpires:
|
||||
description: Sold
|
||||
type: string
|
||||
type: object
|
||||
repo.LabelCreate:
|
||||
@@ -312,10 +346,6 @@ definitions:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
name:
|
||||
type: string
|
||||
updatedAt:
|
||||
@@ -356,10 +386,6 @@ definitions:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
name:
|
||||
type: string
|
||||
parent:
|
||||
@@ -410,7 +436,6 @@ definitions:
|
||||
repo.MaintenanceEntry:
|
||||
properties:
|
||||
completedDate:
|
||||
description: Sold
|
||||
type: string
|
||||
cost:
|
||||
example: "0"
|
||||
@@ -422,13 +447,11 @@ definitions:
|
||||
name:
|
||||
type: string
|
||||
scheduledDate:
|
||||
description: Sold
|
||||
type: string
|
||||
type: object
|
||||
repo.MaintenanceEntryCreate:
|
||||
properties:
|
||||
completedDate:
|
||||
description: Sold
|
||||
type: string
|
||||
cost:
|
||||
example: "0"
|
||||
@@ -438,7 +461,6 @@ definitions:
|
||||
name:
|
||||
type: string
|
||||
scheduledDate:
|
||||
description: Sold
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
@@ -446,7 +468,6 @@ definitions:
|
||||
repo.MaintenanceEntryUpdate:
|
||||
properties:
|
||||
completedDate:
|
||||
description: Sold
|
||||
type: string
|
||||
cost:
|
||||
example: "0"
|
||||
@@ -456,7 +477,6 @@ definitions:
|
||||
name:
|
||||
type: string
|
||||
scheduledDate:
|
||||
description: Sold
|
||||
type: string
|
||||
type: object
|
||||
repo.MaintenanceLog:
|
||||
@@ -611,12 +631,7 @@ definitions:
|
||||
token:
|
||||
type: string
|
||||
type: object
|
||||
v1.ActionAmountResult:
|
||||
properties:
|
||||
completed:
|
||||
type: integer
|
||||
type: object
|
||||
v1.ApiSummary:
|
||||
v1.APISummary:
|
||||
properties:
|
||||
allowRegistration:
|
||||
type: boolean
|
||||
@@ -635,6 +650,11 @@ definitions:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
v1.ActionAmountResult:
|
||||
properties:
|
||||
completed:
|
||||
type: integer
|
||||
type: object
|
||||
v1.Build:
|
||||
properties:
|
||||
buildTime:
|
||||
@@ -698,12 +718,17 @@ definitions:
|
||||
properties:
|
||||
item: {}
|
||||
type: object
|
||||
validate.ErrorResponse:
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
fields:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact:
|
||||
name: Don't
|
||||
description: Track, Manage, and Organize your Things.
|
||||
license:
|
||||
name: MIT
|
||||
title: Homebox API
|
||||
version: "1.0"
|
||||
paths:
|
||||
@@ -737,6 +762,21 @@ paths:
|
||||
summary: Ensures Import Refs
|
||||
tags:
|
||||
- Actions
|
||||
/v1/actions/set-primary-photos:
|
||||
post:
|
||||
description: Sets the first photo of each item as the primary photo
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/v1.ActionAmountResult'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Set Primary Photos
|
||||
tags:
|
||||
- Actions
|
||||
/v1/actions/zero-item-time-fields:
|
||||
post:
|
||||
description: Resets all item date fields to the beginning of the day
|
||||
@@ -772,6 +812,18 @@ paths:
|
||||
summary: Get Item by Asset ID
|
||||
tags:
|
||||
- Items
|
||||
/v1/currency:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/currencies.Currency'
|
||||
summary: Currency
|
||||
tags:
|
||||
- Base
|
||||
/v1/groups:
|
||||
get:
|
||||
produces:
|
||||
@@ -925,6 +977,13 @@ paths:
|
||||
type: string
|
||||
name: locations
|
||||
type: array
|
||||
- collectionFormat: multi
|
||||
description: parent Ids
|
||||
in: query
|
||||
items:
|
||||
type: string
|
||||
name: parentIds
|
||||
type: array
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@@ -994,6 +1053,31 @@ paths:
|
||||
summary: Get Item
|
||||
tags:
|
||||
- Items
|
||||
patch:
|
||||
parameters:
|
||||
- description: Item ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Item Data
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/repo.ItemPatch'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/repo.ItemOut'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Update Item
|
||||
tags:
|
||||
- Items
|
||||
put:
|
||||
parameters:
|
||||
- description: Item ID
|
||||
@@ -1052,7 +1136,7 @@ paths:
|
||||
"422":
|
||||
description: Unprocessable Entity
|
||||
schema:
|
||||
$ref: '#/definitions/mid.ErrorResponse'
|
||||
$ref: '#/definitions/validate.ErrorResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Create Item Attachment
|
||||
@@ -1197,6 +1281,28 @@ paths:
|
||||
summary: Update Maintenance Entry
|
||||
tags:
|
||||
- Maintenance
|
||||
/v1/items/{id}/path:
|
||||
get:
|
||||
parameters:
|
||||
- description: Item ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemPath'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get the full path of an item
|
||||
tags:
|
||||
- Items
|
||||
/v1/items/export:
|
||||
get:
|
||||
responses:
|
||||
@@ -1614,7 +1720,7 @@ paths:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/v1.ApiSummary'
|
||||
$ref: '#/definitions/v1.APISummary'
|
||||
summary: Application Info
|
||||
tags:
|
||||
- Base
|
||||
@@ -1657,6 +1763,10 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/v1.LoginForm'
|
||||
- description: auth provider
|
||||
in: query
|
||||
name: provider
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/migrate"
|
||||
|
||||
atlas "ariga.io/atlas/sql/migrate"
|
||||
_ "ariga.io/atlas/sql/sqlite"
|
||||
|
||||
@@ -54,6 +54,7 @@ func main() {
|
||||
NewReReplace(` Services`, " "),
|
||||
NewReReplace(` V1`, " "),
|
||||
NewReReplace(`\?:`, ":"),
|
||||
NewReReplace(`(\w+):\s(.*null.*)`, "$1?: $2"), // make null union types optional
|
||||
NewReDate("createdAt"),
|
||||
NewReDate("updatedAt"),
|
||||
NewReDate("soldTime"),
|
||||
|
||||
104
backend/go.mod
104
backend/go.mod
@@ -1,74 +1,78 @@
|
||||
module github.com/hay-kot/homebox/backend
|
||||
module github.com/sysadminsmedia/homebox/backend
|
||||
|
||||
go 1.20
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.10.0
|
||||
entgo.io/ent v0.11.10
|
||||
github.com/ardanlabs/conf/v3 v3.1.5
|
||||
github.com/containrrr/shoutrrr v0.7.1
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/go-playground/validator/v10 v10.12.0
|
||||
github.com/gocarina/gocsv v0.0.0-20230226133904-70c27cb2918a
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/hay-kot/safeserve v0.0.1
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
ariga.io/atlas v0.19.1
|
||||
entgo.io/ent v0.12.5
|
||||
github.com/ardanlabs/conf/v3 v3.1.7
|
||||
github.com/containrrr/shoutrrr v0.8.0
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-playground/validator/v10 v10.18.0
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/schema v1.2.1
|
||||
github.com/hay-kot/httpkit v0.0.9
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/olahol/melody v1.1.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.29.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/swaggo/http-swagger v1.3.4
|
||||
github.com/swaggo/swag v1.8.11
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.1
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.1
|
||||
golang.org/x/crypto v0.7.0
|
||||
modernc.org/sqlite v1.21.0
|
||||
github.com/rs/zerolog v1.32.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/swaggo/http-swagger/v2 v2.0.2
|
||||
github.com/swaggo/swag v1.16.3
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.2
|
||||
github.com/yeqown/go-qrcode/writer/standard v1.2.2
|
||||
golang.org/x/crypto v0.19.0
|
||||
modernc.org/sqlite v1.29.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/fogleman/gg v1.3.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.7 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.9 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.15.0 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.19.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/leodido/go-urn v1.2.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/swaggo/files v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/yeqown/reedsolomon v1.0.0 // indirect
|
||||
github.com/zclconf/go-cty v1.12.1 // indirect
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
golang.org/x/tools v0.6.1-0.20230222164832-25d2519c8696 // indirect
|
||||
github.com/zclconf/go-cty v1.14.1 // indirect
|
||||
golang.org/x/image v0.14.0 // indirect
|
||||
golang.org/x/mod v0.15.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.22.3 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
1333
backend/go.sum
1333
backend/go.sum
File diff suppressed because it is too large
Load Diff
104
backend/internal/core/currencies/currencies.go
Normal file
104
backend/internal/core/currencies/currencies.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Package currencies provides a shared definition of currencies. This uses a global
|
||||
// variable to hold the currencies.
|
||||
package currencies
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:embed currencies.json
|
||||
var defaults []byte
|
||||
|
||||
type CollectorFunc func() ([]Currency, error)
|
||||
|
||||
func CollectJSON(reader io.Reader) CollectorFunc {
|
||||
return func() ([]Currency, error) {
|
||||
var currencies []Currency
|
||||
err := json.NewDecoder(reader).Decode(¤cies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return currencies, nil
|
||||
}
|
||||
}
|
||||
|
||||
func CollectDefaults() CollectorFunc {
|
||||
return CollectJSON(bytes.NewReader(defaults))
|
||||
}
|
||||
|
||||
func CollectionCurrencies(collectors ...CollectorFunc) ([]Currency, error) {
|
||||
out := make([]Currency, 0, len(collectors))
|
||||
for i := range collectors {
|
||||
c, err := collectors[i]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out = append(out, c...)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type Currency struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Local string `json:"local"`
|
||||
Symbol string `json:"symbol"`
|
||||
}
|
||||
|
||||
type CurrencyRegistry struct {
|
||||
mu sync.RWMutex
|
||||
registry map[string]Currency
|
||||
}
|
||||
|
||||
func NewCurrencyService(currencies []Currency) *CurrencyRegistry {
|
||||
registry := make(map[string]Currency, len(currencies))
|
||||
for i := range currencies {
|
||||
registry[currencies[i].Code] = currencies[i]
|
||||
}
|
||||
|
||||
return &CurrencyRegistry{
|
||||
registry: registry,
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CurrencyRegistry) Slice() []Currency {
|
||||
cs.mu.RLock()
|
||||
defer cs.mu.RUnlock()
|
||||
|
||||
out := make([]Currency, 0, len(cs.registry))
|
||||
for key := range cs.registry {
|
||||
out = append(out, cs.registry[key])
|
||||
}
|
||||
|
||||
slices.SortFunc(out, func(a, b Currency) int {
|
||||
if a.Name < b.Name {
|
||||
return -1
|
||||
}
|
||||
|
||||
if a.Name > b.Name {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (cs *CurrencyRegistry) IsSupported(code string) bool {
|
||||
upper := strings.ToUpper(code)
|
||||
|
||||
cs.mu.RLock()
|
||||
defer cs.mu.RUnlock()
|
||||
_, ok := cs.registry[upper]
|
||||
return ok
|
||||
}
|
||||
638
backend/internal/core/currencies/currencies.json
Normal file
638
backend/internal/core/currencies/currencies.json
Normal file
@@ -0,0 +1,638 @@
|
||||
[
|
||||
{
|
||||
"code": "USD",
|
||||
"local": "United States",
|
||||
"symbol": "$",
|
||||
"name": "United States Dollar"
|
||||
},
|
||||
{
|
||||
"code": "AED",
|
||||
"local": "United Arab Emirates",
|
||||
"symbol": "د.إ",
|
||||
"name": "United Arab Emirates Dirham"
|
||||
},
|
||||
{
|
||||
"code": "AFN",
|
||||
"local": "Afghanistan",
|
||||
"symbol": "؋",
|
||||
"name": "Afghan Afghani"
|
||||
},
|
||||
{
|
||||
"code": "ALL",
|
||||
"local": "Albania",
|
||||
"symbol": "L",
|
||||
"name": "Albanian Lek"
|
||||
},
|
||||
{
|
||||
"code": "AMD",
|
||||
"local": "Armenia",
|
||||
"symbol": "֏",
|
||||
"name": "Armenian Dram"
|
||||
},
|
||||
{
|
||||
"code": "ANG",
|
||||
"local": "Netherlands Antilles",
|
||||
"symbol": "ƒ",
|
||||
"name": "Netherlands Antillean Guilder"
|
||||
},
|
||||
{
|
||||
"code": "AOA",
|
||||
"local": "Angola",
|
||||
"symbol": "Kz",
|
||||
"name": "Angolan Kwanza"
|
||||
},
|
||||
{
|
||||
"code": "ARS",
|
||||
"local": "Argentina",
|
||||
"symbol": "$",
|
||||
"name": "Argentine Peso"
|
||||
},
|
||||
{
|
||||
"code": "AUD",
|
||||
"local": "Australia",
|
||||
"symbol": "A$",
|
||||
"name": "Australian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "AWG",
|
||||
"local": "Aruba",
|
||||
"symbol": "ƒ",
|
||||
"name": "Aruban Florin"
|
||||
},
|
||||
{
|
||||
"code": "AZN",
|
||||
"local": "Azerbaijan",
|
||||
"symbol": "₼",
|
||||
"name": "Azerbaijani Manat"
|
||||
},
|
||||
{
|
||||
"code": "BAM",
|
||||
"local": "Bosnia and Herzegovina",
|
||||
"symbol": "KM",
|
||||
"name": "Bosnia and Herzegovina Convertible Mark"
|
||||
},
|
||||
{
|
||||
"code": "BBD",
|
||||
"local": "Barbados",
|
||||
"symbol": "Bds$",
|
||||
"name": "Barbadian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BDT",
|
||||
"local": "Bangladesh",
|
||||
"symbol": "৳",
|
||||
"name": "Bangladeshi Taka"
|
||||
},
|
||||
{
|
||||
"code": "BGN",
|
||||
"local": "Bulgaria",
|
||||
"symbol": "лв",
|
||||
"name": "Bulgarian lev"
|
||||
},
|
||||
{
|
||||
"code": "BHD",
|
||||
"local": "Bahrain",
|
||||
"symbol": "ب.د",
|
||||
"name": "Bahraini Dinar"
|
||||
},
|
||||
{
|
||||
"code": "BIF",
|
||||
"local": "Burundi",
|
||||
"symbol": "FBu",
|
||||
"name": "Burundian Franc"
|
||||
},
|
||||
{
|
||||
"code": "BMD",
|
||||
"local": "Bermuda",
|
||||
"symbol": "BD$",
|
||||
"name": "Bermudian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BND",
|
||||
"local": "Brunei",
|
||||
"symbol": "B$",
|
||||
"name": "Brunei Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BOB",
|
||||
"local": "Bolivia",
|
||||
"symbol": "Bs.",
|
||||
"name": "Bolivian Boliviano"
|
||||
},
|
||||
{
|
||||
"code": "BRL",
|
||||
"local": "Brazil",
|
||||
"symbol": "R$",
|
||||
"name": "Brazilian Real"
|
||||
},
|
||||
{
|
||||
"code": "BSD",
|
||||
"local": "Bahamas",
|
||||
"symbol": "B$",
|
||||
"name": "Bahamian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BTN",
|
||||
"local": "Bhutan",
|
||||
"symbol": "Nu.",
|
||||
"name": "Bhutanese Ngultrum"
|
||||
},
|
||||
{
|
||||
"code": "BWP",
|
||||
"local": "Botswana",
|
||||
"symbol": "P",
|
||||
"name": "Botswana Pula"
|
||||
},
|
||||
{
|
||||
"code": "BYN",
|
||||
"local": "Belarus",
|
||||
"symbol": "Br",
|
||||
"name": "Belarusian Ruble"
|
||||
},
|
||||
{
|
||||
"code": "BZD",
|
||||
"local": "Belize",
|
||||
"symbol": "BZ$",
|
||||
"name": "Belize Dollar"
|
||||
},
|
||||
{
|
||||
"code": "CAD",
|
||||
"local": "Canada",
|
||||
"symbol": "C$",
|
||||
"name": "Canadian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "CDF",
|
||||
"local": "Democratic Republic of the Congo",
|
||||
"symbol": "FC",
|
||||
"name": "Congolese Franc"
|
||||
},
|
||||
{
|
||||
"code": "CHF",
|
||||
"local": "Switzerland",
|
||||
"symbol": "CHF",
|
||||
"name": "Swiss Franc"
|
||||
},
|
||||
{
|
||||
"code": "CLP",
|
||||
"local": "Chile",
|
||||
"symbol": "CL$",
|
||||
"name": "Chilean Peso"
|
||||
},
|
||||
{
|
||||
"code": "CNY",
|
||||
"local": "China",
|
||||
"symbol": "¥",
|
||||
"name": "Chinese Yuan"
|
||||
},
|
||||
{
|
||||
"code": "COP",
|
||||
"local": "Colombia",
|
||||
"symbol": "COL$",
|
||||
"name": "Colombian Peso"
|
||||
},
|
||||
{
|
||||
"code": "CRC",
|
||||
"local": "Costa Rica",
|
||||
"symbol": "₡",
|
||||
"name": "Costa Rican Colón"
|
||||
},
|
||||
{
|
||||
"code": "CUP",
|
||||
"local": "Cuba",
|
||||
"symbol": "₱",
|
||||
"name": "Cuban Peso"
|
||||
},
|
||||
{
|
||||
"code": "CVE",
|
||||
"local": "Cape Verde",
|
||||
"symbol": "$",
|
||||
"name": "Cape Verdean Escudo"
|
||||
},
|
||||
{
|
||||
"code": "CZK",
|
||||
"local": "Czech Republic",
|
||||
"symbol": "Kč",
|
||||
"name": "Czech Koruna"
|
||||
},
|
||||
{
|
||||
"code": "DJF",
|
||||
"local": "Djibouti",
|
||||
"symbol": "Fdj",
|
||||
"name": "Djiboutian Franc"
|
||||
},
|
||||
{
|
||||
"code": "DKK",
|
||||
"local": "Denmark",
|
||||
"symbol": "kr",
|
||||
"name": "Danish Krone"
|
||||
},
|
||||
{
|
||||
"code": "DOP",
|
||||
"local": "Dominican Republic",
|
||||
"symbol": "RD$",
|
||||
"name": "Dominican Peso"
|
||||
},
|
||||
{
|
||||
"code": "DZD",
|
||||
"local": "Algeria",
|
||||
"symbol": "د.ج",
|
||||
"name": "Algerian Dinar"
|
||||
},
|
||||
{
|
||||
"code": "EGP",
|
||||
"local": "Egypt",
|
||||
"symbol": "£",
|
||||
"name": "Egyptian Pound"
|
||||
},
|
||||
{
|
||||
"code": "ERN",
|
||||
"local": "Eritrea",
|
||||
"symbol": "Nfk",
|
||||
"name": "Eritrean Nakfa"
|
||||
},
|
||||
{
|
||||
"code": "ETB",
|
||||
"local": "Ethiopia",
|
||||
"symbol": "Br",
|
||||
"name": "Ethiopian Birr"
|
||||
},
|
||||
{
|
||||
"code": "EUR",
|
||||
"local": "Eurozone",
|
||||
"symbol": "€",
|
||||
"name": "Euro"
|
||||
},
|
||||
{
|
||||
"code": "FJD",
|
||||
"local": "Fiji",
|
||||
"symbol": "FJ$",
|
||||
"name": "Fijian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "FKP",
|
||||
"local": "Falkland Islands",
|
||||
"symbol": "£",
|
||||
"name": "Falkland Islands Pound"
|
||||
},
|
||||
{
|
||||
"code": "FOK",
|
||||
"local": "Faroe Islands",
|
||||
"symbol": "kr",
|
||||
"name": "Faroese Króna"
|
||||
},
|
||||
{
|
||||
"code": "GBP",
|
||||
"local": "United Kingdom",
|
||||
"symbol": "£",
|
||||
"name": "British Pound Sterling"
|
||||
},
|
||||
{
|
||||
"code": "GEL",
|
||||
"local": "Georgia",
|
||||
"symbol": "₾",
|
||||
"name": "Georgian Lari"
|
||||
},
|
||||
{
|
||||
"code": "GGP",
|
||||
"local": "Guernsey",
|
||||
"symbol": "£",
|
||||
"name": "Guernsey Pound"
|
||||
},
|
||||
{
|
||||
"code": "GHS",
|
||||
"local": "Ghana",
|
||||
"symbol": "GH₵",
|
||||
"name": "Ghanaian Cedi"
|
||||
},
|
||||
{
|
||||
"code": "GIP",
|
||||
"local": "Gibraltar",
|
||||
"symbol": "£",
|
||||
"name": "Gibraltar Pound"
|
||||
},
|
||||
{
|
||||
"code": "GMD",
|
||||
"local": "Gambia",
|
||||
"symbol": "D",
|
||||
"name": "Gambian Dalasi"
|
||||
},
|
||||
{
|
||||
"code": "GNF",
|
||||
"local": "Guinea",
|
||||
"symbol": "FG",
|
||||
"name": "Guinean Franc"
|
||||
},
|
||||
{
|
||||
"code": "GTQ",
|
||||
"local": "Guatemala",
|
||||
"symbol": "Q",
|
||||
"name": "Guatemalan Quetzal"
|
||||
},
|
||||
{
|
||||
"code": "GYD",
|
||||
"local": "Guyana",
|
||||
"symbol": "GY$",
|
||||
"name": "Guyanese Dollar"
|
||||
},
|
||||
{
|
||||
"code": "HKD",
|
||||
"local": "Hong Kong",
|
||||
"symbol": "HK$",
|
||||
"name": "Hong Kong Dollar"
|
||||
},
|
||||
{
|
||||
"code": "HNL",
|
||||
"local": "Honduras",
|
||||
"symbol": "L",
|
||||
"name": "Honduran Lempira"
|
||||
},
|
||||
{
|
||||
"code": "HRK",
|
||||
"local": "Croatia",
|
||||
"symbol": "kn",
|
||||
"name": "Croatian Kuna"
|
||||
},
|
||||
{
|
||||
"code": "HTG",
|
||||
"local": "Haiti",
|
||||
"symbol": "G",
|
||||
"name": "Haitian Gourde"
|
||||
},
|
||||
{
|
||||
"code": "HUF",
|
||||
"local": "Hungary",
|
||||
"symbol": "Ft",
|
||||
"name": "Hungarian Forint"
|
||||
},
|
||||
{
|
||||
"code": "IDR",
|
||||
"local": "Indonesia",
|
||||
"symbol": "Rp",
|
||||
"name": "Indonesian Rupiah"
|
||||
},
|
||||
{
|
||||
"code": "ILS",
|
||||
"local": "Israel",
|
||||
"symbol": "₪",
|
||||
"name": "Israeli New Shekel"
|
||||
},
|
||||
{
|
||||
"code": "IMP",
|
||||
"local": "Isle of Man",
|
||||
"symbol": "£",
|
||||
"name": "Manx Pound"
|
||||
},
|
||||
{
|
||||
"code": "INR",
|
||||
"local": "India",
|
||||
"symbol": "₹",
|
||||
"name": "Indian Rupee"
|
||||
},
|
||||
{
|
||||
"code": "IQD",
|
||||
"local": "Iraq",
|
||||
"symbol": "ع.د",
|
||||
"name": "Iraqi Dinar"
|
||||
},
|
||||
{
|
||||
"code": "IRR",
|
||||
"local": "Iran",
|
||||
"symbol": "﷼",
|
||||
"name": "Iranian Rial"
|
||||
},
|
||||
{
|
||||
"code": "ISK",
|
||||
"local": "Iceland",
|
||||
"symbol": "kr",
|
||||
"name": "Icelandic Króna"
|
||||
},
|
||||
{
|
||||
"code": "JEP",
|
||||
"local": "Jersey",
|
||||
"symbol": "£",
|
||||
"name": "Jersey Pound"
|
||||
},
|
||||
{
|
||||
"code": "JMD",
|
||||
"local": "Jamaica",
|
||||
"symbol": "J$",
|
||||
"name": "Jamaican Dollar"
|
||||
},
|
||||
{
|
||||
"code": "JOD",
|
||||
"local": "Jordan",
|
||||
"symbol": "د.ا",
|
||||
"name": "Jordanian Dinar"
|
||||
},
|
||||
{
|
||||
"code": "JPY",
|
||||
"local": "Japan",
|
||||
"symbol": "¥",
|
||||
"name": "Japanese Yen"
|
||||
},
|
||||
{
|
||||
"code": "KES",
|
||||
"local": "Kenya",
|
||||
"symbol": "KSh",
|
||||
"name": "Kenyan Shilling"
|
||||
},
|
||||
{
|
||||
"code": "KGS",
|
||||
"local": "Kyrgyzstan",
|
||||
"symbol": "с",
|
||||
"name": "Kyrgyzstani Som"
|
||||
},
|
||||
{
|
||||
"code": "KHR",
|
||||
"local": "Cambodia",
|
||||
"symbol": "៛",
|
||||
"name": "Cambodian Riel"
|
||||
},
|
||||
{
|
||||
"code": "KID",
|
||||
"local": "Kiribati",
|
||||
"symbol": "$",
|
||||
"name": "Kiribati Dollar"
|
||||
},
|
||||
{
|
||||
"code": "KMF",
|
||||
"local": "Comoros",
|
||||
"symbol": "CF",
|
||||
"name": "Comorian Franc"
|
||||
},
|
||||
{
|
||||
"code": "KRW",
|
||||
"local": "South Korea",
|
||||
"symbol": "₩",
|
||||
"name": "South Korean Won"
|
||||
},
|
||||
{
|
||||
"code": "KWD",
|
||||
"local": "Kuwait",
|
||||
"symbol": "د.ك",
|
||||
"name": "Kuwaiti Dinar"
|
||||
},
|
||||
{
|
||||
"code": "KYD",
|
||||
"local": "Cayman Islands",
|
||||
"symbol": "CI$",
|
||||
"name": "Cayman Islands Dollar"
|
||||
},
|
||||
{
|
||||
"code": "KZT",
|
||||
"local": "Kazakhstan",
|
||||
"symbol": "₸",
|
||||
"name": "Kazakhstani Tenge"
|
||||
},
|
||||
{
|
||||
"code": "LAK",
|
||||
"local": "Laos",
|
||||
"symbol": "₭",
|
||||
"name": "Lao Kip"
|
||||
},
|
||||
{
|
||||
"code": "LBP",
|
||||
"local": "Lebanon",
|
||||
"symbol": "ل.ل",
|
||||
"name": "Lebanese Pound"
|
||||
},
|
||||
{
|
||||
"code": "LKR",
|
||||
"local": "Sri Lanka",
|
||||
"symbol": "₨",
|
||||
"name": "Sri Lankan Rupee"
|
||||
},
|
||||
{
|
||||
"code": "LRD",
|
||||
"local": "Liberia",
|
||||
"symbol": "L$",
|
||||
"name": "Liberian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "LSL",
|
||||
"local": "Lesotho",
|
||||
"symbol": "M",
|
||||
"name": "Lesotho Loti"
|
||||
},
|
||||
{
|
||||
"code": "LYD",
|
||||
"local": "Libya",
|
||||
"symbol": "ل.د",
|
||||
"name": "Libyan Dinar"
|
||||
},
|
||||
{
|
||||
"code": "MAD",
|
||||
"local": "Morocco",
|
||||
"symbol": "د.م.",
|
||||
"name": "Moroccan Dirham"
|
||||
},
|
||||
{
|
||||
"code": "MDL",
|
||||
"local": "Moldova",
|
||||
"symbol": "lei",
|
||||
"name": "Moldovan Leu"
|
||||
},
|
||||
{
|
||||
"code": "MGA",
|
||||
"local": "Madagascar",
|
||||
"symbol": "Ar",
|
||||
"name": "Malagasy Ariary"
|
||||
},
|
||||
{
|
||||
"code": "MKD",
|
||||
"local": "North Macedonia",
|
||||
"symbol": "ден",
|
||||
"name": "Macedonian Denar"
|
||||
},
|
||||
{
|
||||
"code": "MMK",
|
||||
"local": "Myanmar",
|
||||
"symbol": "K",
|
||||
"name": "Myanmar Kyat"
|
||||
},
|
||||
{
|
||||
"code": "MNT",
|
||||
"local": "Mongolia",
|
||||
"symbol": "₮",
|
||||
"name": "Mongolian Tugrik"
|
||||
},
|
||||
{
|
||||
"code": "MOP",
|
||||
"local": "Macau",
|
||||
"symbol": "MOP$",
|
||||
"name": "Macanese Pataca"
|
||||
},
|
||||
{
|
||||
"code": "MRU",
|
||||
"local": "Mauritania",
|
||||
"symbol": "UM",
|
||||
"name": "Mauritanian Ouguiya"
|
||||
},
|
||||
{
|
||||
"code": "MUR",
|
||||
"local": "Mauritius",
|
||||
"symbol": "₨",
|
||||
"name": "Mauritian Rupee"
|
||||
},
|
||||
{
|
||||
"code": "MVR",
|
||||
"local": "Maldives",
|
||||
"symbol": "Rf",
|
||||
"name": "Maldivian Rufiyaa"
|
||||
},
|
||||
{
|
||||
"code": "MWK",
|
||||
"local": "Malawi",
|
||||
"symbol": "MK",
|
||||
"name": "Malawian Kwacha"
|
||||
},
|
||||
{
|
||||
"code": "MXN",
|
||||
"local": "Mexico",
|
||||
"symbol": "Mex$",
|
||||
"name": "Mexican Peso"
|
||||
},
|
||||
{
|
||||
"code": "MYR",
|
||||
"local": "Malaysia",
|
||||
"symbol": "RM",
|
||||
"name": "Malaysian Ringgit"
|
||||
},
|
||||
{
|
||||
"code": "MZN",
|
||||
"local": "Mozambique",
|
||||
"symbol": "MT",
|
||||
"name": "Mozambican Metical"
|
||||
},
|
||||
{
|
||||
"code": "NAD",
|
||||
"local": "Namibia",
|
||||
"symbol": "N$",
|
||||
"name": "Namibian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "NGN",
|
||||
"local": "Nigeria",
|
||||
"symbol": "₦",
|
||||
"name": "Nigerian Naira"
|
||||
},
|
||||
{
|
||||
"code": "NIO",
|
||||
"local": "Nicaragua",
|
||||
"symbol": "C$",
|
||||
"name": "Nicaraguan Córdoba"
|
||||
},
|
||||
{
|
||||
"code": "NOK",
|
||||
"local": "Norway",
|
||||
"symbol": "kr",
|
||||
"name": "Norwegian Krone"
|
||||
},
|
||||
{
|
||||
"code": "UAH",
|
||||
"local": "Ukraine",
|
||||
"symbol": "₴",
|
||||
"name": "Ukrainian Hryvnia"
|
||||
}
|
||||
]
|
||||
@@ -1,7 +1,9 @@
|
||||
// Package services provides the core business logic for the application.
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/currencies"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
type AllServices struct {
|
||||
@@ -9,12 +11,14 @@ type AllServices struct {
|
||||
Group *GroupService
|
||||
Items *ItemService
|
||||
BackgroundService *BackgroundService
|
||||
Currencies *currencies.CurrencyRegistry
|
||||
}
|
||||
|
||||
type OptionsFunc func(*options)
|
||||
|
||||
type options struct {
|
||||
autoIncrementAssetID bool
|
||||
currencies []currencies.Currency
|
||||
}
|
||||
|
||||
func WithAutoIncrementAssetID(v bool) func(*options) {
|
||||
@@ -23,13 +27,27 @@ func WithAutoIncrementAssetID(v bool) func(*options) {
|
||||
}
|
||||
}
|
||||
|
||||
func WithCurrencies(v []currencies.Currency) func(*options) {
|
||||
return func(o *options) {
|
||||
o.currencies = v
|
||||
}
|
||||
}
|
||||
|
||||
func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
|
||||
if repos == nil {
|
||||
panic("repos cannot be nil")
|
||||
}
|
||||
|
||||
defaultCurrencies, err := currencies.CollectionCurrencies(
|
||||
currencies.CollectDefaults(),
|
||||
)
|
||||
if err != nil {
|
||||
panic("failed to collect default currencies")
|
||||
}
|
||||
|
||||
options := &options{
|
||||
autoIncrementAssetID: true,
|
||||
currencies: defaultCurrencies,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@@ -44,5 +62,6 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
|
||||
autoIncrementAssetID: options.autoIncrementAssetID,
|
||||
},
|
||||
BackgroundService: &BackgroundService{repos},
|
||||
Currencies: currencies.NewCurrencyService(options.currencies),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
type contextKeys struct {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
func Test_SetAuthContext(t *testing.T) {
|
||||
|
||||
@@ -6,14 +6,17 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/faker"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/currencies"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/faker"
|
||||
)
|
||||
|
||||
var (
|
||||
fk = faker.NewFaker()
|
||||
fk = faker.NewFaker()
|
||||
tbus = eventbus.New()
|
||||
|
||||
tCtx = Context{}
|
||||
tClient *ent.Client
|
||||
@@ -58,9 +61,14 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
tClient = client
|
||||
tRepos = repo.New(tClient, os.TempDir()+"/homebox")
|
||||
tSvc = New(tRepos)
|
||||
defer client.Close()
|
||||
tRepos = repo.New(tClient, tbus, os.TempDir()+"/homebox")
|
||||
|
||||
defaults, _ := currencies.CollectionCurrencies(
|
||||
currencies.CollectDefaults(),
|
||||
)
|
||||
|
||||
tSvc = New(tRepos, WithCurrencies(defaults))
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
bootstrap()
|
||||
tCtx = Context{
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
Import Ref,Location,Labels,Quantity,Name,Description,Insured,Serial Number,Mode Number,Manufacturer,Notes,Purchase From,Purchased Price,Purchased Time,Lifetime Warranty,Warranty Expires,Warranty Details,Sold To,Sold Price,Sold Time,Sold Notes
|
||||
A,Garage,IOT;Home Assistant; Z-Wave,1,Zooz Universal Relay ZEN17,Description 1,TRUE,,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,10/13/2021,,,,10/13/2021,
|
||||
B,Living Room,IOT;Home Assistant; Z-Wave,1,Zooz Motion Sensor,Description 2,FALSE,,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,10/15/2021,,,,10/15/2021,
|
||||
C,Office,IOT;Home Assistant; Z-Wave,1,Zooz 110v Power Switch,Description 3,TRUE,,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,10/13/2021,,,,10/13/2021,
|
||||
D,Downstairs,IOT;Home Assistant; Z-Wave,1,Ecolink Z-Wave PIR Motion Sensor,Description 4,FALSE,,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,10/21/2020,,,,10/21/2020,
|
||||
E,Entry,IOT;Home Assistant; Z-Wave,1,Yale Security Touchscreen Deadbolt,Description 5,TRUE,,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,10/14/2020,,,,10/14/2020,
|
||||
F,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,Description 6,FALSE,,39351,Honeywell,,Amazon,65.98,09/30/2020,,09/30/2020,,,,09/30/2020,
|
||||
|
@@ -1,7 +0,0 @@
|
||||
Import Ref Location Labels Quantity Name Description Insured Serial Number Mode Number Manufacturer Notes Purchase From Purchased Price Purchased Time Lifetime Warranty Warranty Expires Warranty Details Sold To Sold Price Sold Time Sold Notes
|
||||
A Garage IOT;Home Assistant; Z-Wave 1 Zooz Universal Relay ZEN17 Description 1 TRUE ZEN17 Zooz Amazon 39.95 10/13/2021 10/13/2021 10/13/2021
|
||||
B Living Room IOT;Home Assistant; Z-Wave 1 Zooz Motion Sensor Description 2 FALSE ZSE18 Zooz Amazon 29.95 10/15/2021 10/15/2021 10/15/2021
|
||||
C Office IOT;Home Assistant; Z-Wave 1 Zooz 110v Power Switch Description 3 TRUE ZEN15 Zooz Amazon 39.95 10/13/2021 10/13/2021 10/13/2021
|
||||
D Downstairs IOT;Home Assistant; Z-Wave 1 Ecolink Z-Wave PIR Motion Sensor Description 4 FALSE PIRZWAVE2.5-ECO Ecolink Amazon 35.58 10/21/2020 10/21/2020 10/21/2020
|
||||
E Entry IOT;Home Assistant; Z-Wave 1 Yale Security Touchscreen Deadbolt Description 5 TRUE YRD226ZW2619 Yale Amazon 120.39 10/14/2020 10/14/2020 10/14/2020
|
||||
F Kitchen IOT;Home Assistant; Z-Wave 1 Smart Rocker Light Dimmer Description 6 FALSE 39351 Honeywell Amazon 65.98 09/30/2020 09/30/2020 09/30/2020
|
||||
|
@@ -2,8 +2,8 @@ package reporting
|
||||
|
||||
import (
|
||||
"github.com/gocarina/gocsv"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/types"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
|
||||
)
|
||||
|
||||
// =================================================================================================
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Package eventbus provides an interface for event bus.
|
||||
package eventbus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Event string
|
||||
|
||||
const (
|
||||
EventLabelMutation Event = "label.mutation"
|
||||
EventLocationMutation Event = "location.mutation"
|
||||
EventItemMutation Event = "item.mutation"
|
||||
)
|
||||
|
||||
type GroupMutationEvent struct {
|
||||
GID uuid.UUID
|
||||
}
|
||||
|
||||
type eventData struct {
|
||||
event Event
|
||||
data any
|
||||
}
|
||||
|
||||
type EventBus struct {
|
||||
started bool
|
||||
ch chan eventData
|
||||
|
||||
mu sync.RWMutex
|
||||
subscribers map[Event][]func(any)
|
||||
}
|
||||
|
||||
func New() *EventBus {
|
||||
return &EventBus{
|
||||
ch: make(chan eventData, 100),
|
||||
subscribers: map[Event][]func(any){
|
||||
EventLabelMutation: {},
|
||||
EventLocationMutation: {},
|
||||
EventItemMutation: {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EventBus) Run(ctx context.Context) error {
|
||||
if e.started {
|
||||
panic("event bus already started")
|
||||
}
|
||||
|
||||
e.started = true
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case event := <-e.ch:
|
||||
e.mu.RLock()
|
||||
arr, ok := e.subscribers[event.event]
|
||||
e.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, fn := range arr {
|
||||
fn(event.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EventBus) Publish(event Event, data any) {
|
||||
e.ch <- eventData{
|
||||
event: event,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EventBus) Subscribe(event Event, fn func(any)) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
arr, ok := e.subscribers[event]
|
||||
if !ok {
|
||||
panic("event not found")
|
||||
}
|
||||
|
||||
e.subscribers[event] = append(arr, fn)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package reporting provides a way to import CSV files into the database.
|
||||
package reporting
|
||||
|
||||
import (
|
||||
|
||||
@@ -3,8 +3,8 @@ package reporting
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/types"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
|
||||
)
|
||||
|
||||
type ExportItemFields struct {
|
||||
@@ -83,3 +83,13 @@ func parseLocationString(s string) LocationString {
|
||||
func (csf LocationString) String() string {
|
||||
return strings.Join(csf, " / ")
|
||||
}
|
||||
|
||||
func fromPathSlice(s []repo.ItemPath) LocationString {
|
||||
v := make(LocationString, len(s))
|
||||
|
||||
for i := range s {
|
||||
v[i] = s[i].Name
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package reporting
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
@@ -8,9 +9,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/types"
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
|
||||
)
|
||||
|
||||
// IOSheet is the representation of a CSV/TSV sheet that is used for importing/exporting
|
||||
@@ -150,8 +152,8 @@ func (s *IOSheet) Read(data io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes the sheet to a writer.
|
||||
func (s *IOSheet) ReadItems(items []repo.ItemOut) {
|
||||
// ReadItems writes the sheet to a writer.
|
||||
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
|
||||
s.Rows = make([]ExportTSVRow, len(items))
|
||||
|
||||
extraHeaders := map[string]struct{}{}
|
||||
@@ -160,7 +162,15 @@ func (s *IOSheet) ReadItems(items []repo.ItemOut) {
|
||||
item := items[i]
|
||||
|
||||
// TODO: Support fetching nested locations
|
||||
locString := LocationString{item.Location.Name}
|
||||
locID := item.Location.ID
|
||||
|
||||
locPaths, err := repos.Locations.PathForLoc(context.Background(), GID, locID)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not get location path")
|
||||
return err
|
||||
}
|
||||
|
||||
locString := fromPathSlice(locPaths)
|
||||
|
||||
labelString := make([]string, len(item.Labels))
|
||||
|
||||
@@ -238,9 +248,11 @@ func (s *IOSheet) ReadItems(items []repo.ItemOut) {
|
||||
for _, h := range customHeaders {
|
||||
s.headers = append(s.headers, "HB.field."+h)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Writes the current sheet to a writer in TSV format.
|
||||
// TSV writes the current sheet to a writer in TSV format.
|
||||
func (s *IOSheet) TSV() ([][]string, error) {
|
||||
memcsv := make([][]string, len(s.Rows)+1)
|
||||
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,12 +21,6 @@ var (
|
||||
|
||||
//go:embed .testdata/import/types.csv
|
||||
customTypesImportCSV []byte
|
||||
|
||||
//go:embed .testdata/import.csv
|
||||
CSVData_Comma []byte
|
||||
|
||||
//go:embed .testdata/import.tsv
|
||||
CSVData_Tab []byte
|
||||
)
|
||||
|
||||
func TestSheet_Read(t *testing.T) {
|
||||
@@ -109,9 +104,9 @@ func TestSheet_Read(t *testing.T) {
|
||||
|
||||
switch {
|
||||
case tt.wantErr:
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
default:
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, tt.want, sheet.Rows)
|
||||
}
|
||||
})
|
||||
@@ -189,7 +184,7 @@ func Test_determineSeparator(t *testing.T) {
|
||||
{
|
||||
name: "comma",
|
||||
args: args{
|
||||
data: CSVData_Comma,
|
||||
data: []byte("a,b,c"),
|
||||
},
|
||||
want: ',',
|
||||
wantErr: false,
|
||||
@@ -197,7 +192,7 @@ func Test_determineSeparator(t *testing.T) {
|
||||
{
|
||||
name: "tab",
|
||||
args: args{
|
||||
data: CSVData_Tab,
|
||||
data: []byte("a\tb\tc"),
|
||||
},
|
||||
want: '\t',
|
||||
wantErr: false,
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containrrr/shoutrrr"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/types"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
|
||||
)
|
||||
|
||||
type BackgroundService struct {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/hasher"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/hasher"
|
||||
)
|
||||
|
||||
type GroupService struct {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services/reporting"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -32,7 +32,7 @@ func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut,
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
|
||||
item.AssetID = repo.AssetID(highest + 1)
|
||||
item.AssetID = highest + 1
|
||||
}
|
||||
|
||||
return svc.repo.Items.Create(ctx, ctx.GID, item)
|
||||
@@ -53,7 +53,7 @@ func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int,
|
||||
for _, item := range items {
|
||||
highest++
|
||||
|
||||
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, repo.AssetID(highest))
|
||||
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, highest)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -337,7 +337,10 @@ func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]strin
|
||||
|
||||
sheet := reporting.IOSheet{}
|
||||
|
||||
sheet.ReadItems(items)
|
||||
err = sheet.ReadItems(ctx, items, GID, svc.repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sheet.TSV()
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentId uuid.UUID) (*ent.Document, error) {
|
||||
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
|
||||
func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentID uuid.UUID) (*ent.Document, error) {
|
||||
attachment, err := svc.repo.Attachments.Get(ctx, attachmentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -21,9 +21,9 @@ func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentId uuid.UU
|
||||
return attachment.Edges.Document, nil
|
||||
}
|
||||
|
||||
func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
|
||||
func (svc *ItemService) AttachmentUpdate(ctx Context, itemID uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
|
||||
// Update Attachment
|
||||
attachment, err := svc.repo.Attachments.Update(ctx, data.ID, attachment.Type(data.Type))
|
||||
attachment, err := svc.repo.Attachments.Update(ctx, data.ID, data)
|
||||
if err != nil {
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
@@ -35,15 +35,15 @@ func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *re
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
|
||||
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
|
||||
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
|
||||
}
|
||||
|
||||
// AttachmentAdd adds an attachment to an item by creating an entry in the Documents table and linking it to the Attachment
|
||||
// Table and Items table. The file provided via the reader is stored on the file system based on the provided
|
||||
// relative path during construction of the service.
|
||||
func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
|
||||
func (svc *ItemService) AttachmentAdd(ctx Context, itemID uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
|
||||
// Get the Item
|
||||
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
|
||||
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
|
||||
if err != nil {
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
@@ -56,29 +56,29 @@ func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename st
|
||||
}
|
||||
|
||||
// Create the attachment
|
||||
_, err = svc.repo.Attachments.Create(ctx, itemId, doc.ID, attachmentType)
|
||||
_, err = svc.repo.Attachments.Create(ctx, itemID, doc.ID, attachmentType)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create attachment")
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
|
||||
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
|
||||
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
|
||||
}
|
||||
|
||||
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemId, attachmentId uuid.UUID) error {
|
||||
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemID, attachmentID uuid.UUID) error {
|
||||
// Get the Item
|
||||
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemId)
|
||||
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
|
||||
attachment, err := svc.repo.Attachments.Get(ctx, attachmentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the attachment
|
||||
err = svc.repo.Attachments.Delete(ctx, attachmentId)
|
||||
err = svc.repo.Attachments.Delete(ctx, attachmentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
func TestItemService_AddAttachment(t *testing.T) {
|
||||
@@ -23,7 +24,7 @@ func TestItemService_AddAttachment(t *testing.T) {
|
||||
Description: "test",
|
||||
Name: "test",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, loc)
|
||||
|
||||
itmC := repo.ItemCreate{
|
||||
@@ -33,11 +34,11 @@ func TestItemService_AddAttachment(t *testing.T) {
|
||||
}
|
||||
|
||||
itm, err := svc.repo.Items.Create(context.Background(), tGroup.ID, itmC)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, itm)
|
||||
t.Cleanup(func() {
|
||||
err := svc.repo.Items.Delete(context.Background(), itm.ID)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
contents := fk.Str(1000)
|
||||
@@ -45,7 +46,7 @@ func TestItemService_AddAttachment(t *testing.T) {
|
||||
|
||||
// Setup
|
||||
afterAttachment, err := svc.AttachmentAdd(tCtx, itm.ID, "testfile.txt", "attachment", reader)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, afterAttachment)
|
||||
|
||||
// Check that the file exists
|
||||
@@ -56,6 +57,6 @@ func TestItemService_AddAttachment(t *testing.T) {
|
||||
|
||||
// Check that the file contents are correct
|
||||
bts, err := os.ReadFile(storedPath)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, contents, string(bts))
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/hasher"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/pkgs/hasher"
|
||||
)
|
||||
|
||||
var (
|
||||
oneWeek = time.Hour * 24 * 7
|
||||
ErrorInvalidLogin = errors.New("invalid username or password")
|
||||
ErrorInvalidToken = errors.New("invalid token")
|
||||
ErrorTokenIdMismatch = errors.New("token id mismatch")
|
||||
ErrorTokenIDMismatch = errors.New("token id mismatch")
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
@@ -92,9 +92,11 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
|
||||
if err != nil {
|
||||
return repo.UserOut{}, err
|
||||
}
|
||||
log.Debug().Msg("user created")
|
||||
|
||||
// Create the default labels and locations for the group.
|
||||
if creatingGroup {
|
||||
log.Debug().Msg("creating default labels")
|
||||
for _, label := range defaultLabels() {
|
||||
_, err := svc.repos.Labels.Create(ctx, usr.GroupID, label)
|
||||
if err != nil {
|
||||
@@ -102,6 +104,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug().Msg("creating default locations")
|
||||
for _, location := range defaultLocations() {
|
||||
_, err := svc.repos.Locations.Create(ctx, usr.GroupID, location)
|
||||
if err != nil {
|
||||
@@ -112,6 +115,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
|
||||
|
||||
// Decrement the invitation token if it was used.
|
||||
if token.ID != uuid.Nil {
|
||||
log.Debug().Msg("decrementing invitation token")
|
||||
err = svc.repos.Groups.InvitationUpdate(ctx, token.ID, token.Uses-1)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Failed to update invitation token")
|
||||
@@ -134,13 +138,13 @@ func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data repo.
|
||||
return repo.UserOut{}, err
|
||||
}
|
||||
|
||||
return svc.repos.Users.GetOneId(ctx, ID)
|
||||
return svc.repos.Users.GetOneID(ctx, ID)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// User Authentication
|
||||
|
||||
func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID, extendedSession bool) (UserAuthTokenDetail, error) {
|
||||
func (svc *UserService) createSessionToken(ctx context.Context, userID uuid.UUID, extendedSession bool) (UserAuthTokenDetail, error) {
|
||||
attachmentToken := hasher.GenerateToken()
|
||||
|
||||
expiresAt := time.Now().Add(oneWeek)
|
||||
@@ -149,7 +153,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID
|
||||
}
|
||||
|
||||
attachmentData := repo.UserAuthTokenCreate{
|
||||
UserID: userId,
|
||||
UserID: userID,
|
||||
TokenHash: attachmentToken.Hash,
|
||||
ExpiresAt: expiresAt,
|
||||
}
|
||||
@@ -161,7 +165,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID
|
||||
|
||||
userToken := hasher.GenerateToken()
|
||||
data := repo.UserAuthTokenCreate{
|
||||
UserID: userId,
|
||||
UserID: userID,
|
||||
TokenHash: userToken.Hash,
|
||||
ExpiresAt: expiresAt,
|
||||
}
|
||||
@@ -218,7 +222,7 @@ func (svc *UserService) DeleteSelf(ctx context.Context, ID uuid.UUID) error {
|
||||
}
|
||||
|
||||
func (svc *UserService) ChangePassword(ctx Context, current string, new string) (ok bool) {
|
||||
usr, err := svc.repos.Users.GetOneId(ctx, ctx.UID)
|
||||
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
func defaultLocations() []repo.LocationCreate {
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/document"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/item"
|
||||
)
|
||||
|
||||
// Attachment is the model entity for the Attachment schema.
|
||||
@@ -25,11 +26,14 @@ type Attachment struct {
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
// Type holds the value of the "type" field.
|
||||
Type attachment.Type `json:"type,omitempty"`
|
||||
// Primary holds the value of the "primary" field.
|
||||
Primary bool `json:"primary,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the AttachmentQuery when eager-loading is set.
|
||||
Edges AttachmentEdges `json:"edges"`
|
||||
document_attachments *uuid.UUID
|
||||
item_attachments *uuid.UUID
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// AttachmentEdges holds the relations/edges for other nodes in the graph.
|
||||
@@ -74,6 +78,8 @@ func (*Attachment) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case attachment.FieldPrimary:
|
||||
values[i] = new(sql.NullBool)
|
||||
case attachment.FieldType:
|
||||
values[i] = new(sql.NullString)
|
||||
case attachment.FieldCreatedAt, attachment.FieldUpdatedAt:
|
||||
@@ -85,7 +91,7 @@ func (*Attachment) scanValues(columns []string) ([]any, error) {
|
||||
case attachment.ForeignKeys[1]: // item_attachments
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected column %q for type Attachment", columns[i])
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
@@ -123,6 +129,12 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
|
||||
} else if value.Valid {
|
||||
a.Type = attachment.Type(value.String)
|
||||
}
|
||||
case attachment.FieldPrimary:
|
||||
if value, ok := values[i].(*sql.NullBool); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field primary", values[i])
|
||||
} else if value.Valid {
|
||||
a.Primary = value.Bool
|
||||
}
|
||||
case attachment.ForeignKeys[0]:
|
||||
if value, ok := values[i].(*sql.NullScanner); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field document_attachments", values[i])
|
||||
@@ -137,11 +149,19 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
|
||||
a.item_attachments = new(uuid.UUID)
|
||||
*a.item_attachments = *value.S.(*uuid.UUID)
|
||||
}
|
||||
default:
|
||||
a.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the Attachment.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (a *Attachment) Value(name string) (ent.Value, error) {
|
||||
return a.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryItem queries the "item" edge of the Attachment entity.
|
||||
func (a *Attachment) QueryItem() *ItemQuery {
|
||||
return NewAttachmentClient(a.config).QueryItem(a)
|
||||
@@ -183,6 +203,9 @@ func (a *Attachment) String() string {
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("type=")
|
||||
builder.WriteString(fmt.Sprintf("%v", a.Type))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("primary=")
|
||||
builder.WriteString(fmt.Sprintf("%v", a.Primary))
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -20,6 +22,8 @@ const (
|
||||
FieldUpdatedAt = "updated_at"
|
||||
// FieldType holds the string denoting the type field in the database.
|
||||
FieldType = "type"
|
||||
// FieldPrimary holds the string denoting the primary field in the database.
|
||||
FieldPrimary = "primary"
|
||||
// EdgeItem holds the string denoting the item edge name in mutations.
|
||||
EdgeItem = "item"
|
||||
// EdgeDocument holds the string denoting the document edge name in mutations.
|
||||
@@ -48,6 +52,7 @@ var Columns = []string{
|
||||
FieldCreatedAt,
|
||||
FieldUpdatedAt,
|
||||
FieldType,
|
||||
FieldPrimary,
|
||||
}
|
||||
|
||||
// ForeignKeys holds the SQL foreign-keys that are owned by the "attachments"
|
||||
@@ -79,6 +84,8 @@ var (
|
||||
DefaultUpdatedAt func() time.Time
|
||||
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
|
||||
UpdateDefaultUpdatedAt func() time.Time
|
||||
// DefaultPrimary holds the default value on creation for the "primary" field.
|
||||
DefaultPrimary bool
|
||||
// DefaultID holds the default value on creation for the "id" field.
|
||||
DefaultID func() uuid.UUID
|
||||
)
|
||||
@@ -111,3 +118,59 @@ func TypeValidator(_type Type) error {
|
||||
return fmt.Errorf("attachment: invalid enum value for type field: %q", _type)
|
||||
}
|
||||
}
|
||||
|
||||
// OrderOption defines the ordering options for the Attachment queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatedAt orders the results by the created_at field.
|
||||
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUpdatedAt orders the results by the updated_at field.
|
||||
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByType orders the results by the type field.
|
||||
func ByType(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldType, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByPrimary orders the results by the primary field.
|
||||
func ByPrimary(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldPrimary, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByItemField orders the results by item field.
|
||||
func ByItemField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newItemStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
|
||||
// ByDocumentField orders the results by document field.
|
||||
func ByDocumentField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newDocumentStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
func newItemStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(ItemInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn),
|
||||
)
|
||||
}
|
||||
func newDocumentStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(DocumentInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -66,6 +66,11 @@ func UpdatedAt(v time.Time) predicate.Attachment {
|
||||
return predicate.Attachment(sql.FieldEQ(FieldUpdatedAt, v))
|
||||
}
|
||||
|
||||
// Primary applies equality check predicate on the "primary" field. It's identical to PrimaryEQ.
|
||||
func Primary(v bool) predicate.Attachment {
|
||||
return predicate.Attachment(sql.FieldEQ(FieldPrimary, v))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
func CreatedAtEQ(v time.Time) predicate.Attachment {
|
||||
return predicate.Attachment(sql.FieldEQ(FieldCreatedAt, v))
|
||||
@@ -166,6 +171,16 @@ func TypeNotIn(vs ...Type) predicate.Attachment {
|
||||
return predicate.Attachment(sql.FieldNotIn(FieldType, vs...))
|
||||
}
|
||||
|
||||
// PrimaryEQ applies the EQ predicate on the "primary" field.
|
||||
func PrimaryEQ(v bool) predicate.Attachment {
|
||||
return predicate.Attachment(sql.FieldEQ(FieldPrimary, v))
|
||||
}
|
||||
|
||||
// PrimaryNEQ applies the NEQ predicate on the "primary" field.
|
||||
func PrimaryNEQ(v bool) predicate.Attachment {
|
||||
return predicate.Attachment(sql.FieldNEQ(FieldPrimary, v))
|
||||
}
|
||||
|
||||
// HasItem applies the HasEdge predicate on the "item" edge.
|
||||
func HasItem() predicate.Attachment {
|
||||
return predicate.Attachment(func(s *sql.Selector) {
|
||||
@@ -180,11 +195,7 @@ func HasItem() predicate.Attachment {
|
||||
// HasItemWith applies the HasEdge predicate on the "item" edge with a given conditions (other predicates).
|
||||
func HasItemWith(preds ...predicate.Item) predicate.Attachment {
|
||||
return predicate.Attachment(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(ItemInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn),
|
||||
)
|
||||
step := newItemStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
@@ -207,11 +218,7 @@ func HasDocument() predicate.Attachment {
|
||||
// HasDocumentWith applies the HasEdge predicate on the "document" edge with a given conditions (other predicates).
|
||||
func HasDocumentWith(preds ...predicate.Document) predicate.Attachment {
|
||||
return predicate.Attachment(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(DocumentInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn),
|
||||
)
|
||||
step := newDocumentStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
@@ -222,32 +229,15 @@ func HasDocumentWith(preds ...predicate.Document) predicate.Attachment {
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.Attachment) predicate.Attachment {
|
||||
return predicate.Attachment(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for _, p := range predicates {
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.Attachment(sql.AndPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.Attachment) predicate.Attachment {
|
||||
return predicate.Attachment(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for i, p := range predicates {
|
||||
if i > 0 {
|
||||
s1.Or()
|
||||
}
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.Attachment(sql.OrPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.Attachment) predicate.Attachment {
|
||||
return predicate.Attachment(func(s *sql.Selector) {
|
||||
p(s.Not())
|
||||
})
|
||||
return predicate.Attachment(sql.NotPredicates(p))
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/document"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/item"
|
||||
)
|
||||
|
||||
// AttachmentCreate is the builder for creating a Attachment entity.
|
||||
@@ -65,6 +65,20 @@ func (ac *AttachmentCreate) SetNillableType(a *attachment.Type) *AttachmentCreat
|
||||
return ac
|
||||
}
|
||||
|
||||
// SetPrimary sets the "primary" field.
|
||||
func (ac *AttachmentCreate) SetPrimary(b bool) *AttachmentCreate {
|
||||
ac.mutation.SetPrimary(b)
|
||||
return ac
|
||||
}
|
||||
|
||||
// SetNillablePrimary sets the "primary" field if the given value is not nil.
|
||||
func (ac *AttachmentCreate) SetNillablePrimary(b *bool) *AttachmentCreate {
|
||||
if b != nil {
|
||||
ac.SetPrimary(*b)
|
||||
}
|
||||
return ac
|
||||
}
|
||||
|
||||
// SetID sets the "id" field.
|
||||
func (ac *AttachmentCreate) SetID(u uuid.UUID) *AttachmentCreate {
|
||||
ac.mutation.SetID(u)
|
||||
@@ -109,7 +123,7 @@ func (ac *AttachmentCreate) Mutation() *AttachmentMutation {
|
||||
// Save creates the Attachment in the database.
|
||||
func (ac *AttachmentCreate) Save(ctx context.Context) (*Attachment, error) {
|
||||
ac.defaults()
|
||||
return withHooks[*Attachment, AttachmentMutation](ctx, ac.sqlSave, ac.mutation, ac.hooks)
|
||||
return withHooks(ctx, ac.sqlSave, ac.mutation, ac.hooks)
|
||||
}
|
||||
|
||||
// SaveX calls Save and panics if Save returns an error.
|
||||
@@ -148,6 +162,10 @@ func (ac *AttachmentCreate) defaults() {
|
||||
v := attachment.DefaultType
|
||||
ac.mutation.SetType(v)
|
||||
}
|
||||
if _, ok := ac.mutation.Primary(); !ok {
|
||||
v := attachment.DefaultPrimary
|
||||
ac.mutation.SetPrimary(v)
|
||||
}
|
||||
if _, ok := ac.mutation.ID(); !ok {
|
||||
v := attachment.DefaultID()
|
||||
ac.mutation.SetID(v)
|
||||
@@ -170,6 +188,9 @@ func (ac *AttachmentCreate) check() error {
|
||||
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "Attachment.type": %w`, err)}
|
||||
}
|
||||
}
|
||||
if _, ok := ac.mutation.Primary(); !ok {
|
||||
return &ValidationError{Name: "primary", err: errors.New(`ent: missing required field "Attachment.primary"`)}
|
||||
}
|
||||
if _, ok := ac.mutation.ItemID(); !ok {
|
||||
return &ValidationError{Name: "item", err: errors.New(`ent: missing required edge "Attachment.item"`)}
|
||||
}
|
||||
@@ -223,6 +244,10 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
|
||||
_spec.SetField(attachment.FieldType, field.TypeEnum, value)
|
||||
_node.Type = value
|
||||
}
|
||||
if value, ok := ac.mutation.Primary(); ok {
|
||||
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value)
|
||||
_node.Primary = value
|
||||
}
|
||||
if nodes := ac.mutation.ItemIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
@@ -263,11 +288,15 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
|
||||
// AttachmentCreateBulk is the builder for creating many Attachment entities in bulk.
|
||||
type AttachmentCreateBulk struct {
|
||||
config
|
||||
err error
|
||||
builders []*AttachmentCreate
|
||||
}
|
||||
|
||||
// Save creates the Attachment entities in the database.
|
||||
func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error) {
|
||||
if acb.err != nil {
|
||||
return nil, acb.err
|
||||
}
|
||||
specs := make([]*sqlgraph.CreateSpec, len(acb.builders))
|
||||
nodes := make([]*Attachment, len(acb.builders))
|
||||
mutators := make([]Mutator, len(acb.builders))
|
||||
@@ -284,8 +313,8 @@ func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error
|
||||
return nil, err
|
||||
}
|
||||
builder.mutation = mutation
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
var err error
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
if i < len(mutators)-1 {
|
||||
_, err = mutators[i+1].Mutate(root, acb.builders[i+1].mutation)
|
||||
} else {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AttachmentDelete is the builder for deleting a Attachment entity.
|
||||
@@ -27,7 +27,7 @@ func (ad *AttachmentDelete) Where(ps ...predicate.Attachment) *AttachmentDelete
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func (ad *AttachmentDelete) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks[int, AttachmentMutation](ctx, ad.sqlExec, ad.mutation, ad.hooks)
|
||||
return withHooks(ctx, ad.sqlExec, ad.mutation, ad.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
|
||||
@@ -11,17 +11,17 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/document"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/item"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AttachmentQuery is the builder for querying Attachment entities.
|
||||
type AttachmentQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []OrderFunc
|
||||
order []attachment.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.Attachment
|
||||
withItem *ItemQuery
|
||||
@@ -58,7 +58,7 @@ func (aq *AttachmentQuery) Unique(unique bool) *AttachmentQuery {
|
||||
}
|
||||
|
||||
// Order specifies how the records should be ordered.
|
||||
func (aq *AttachmentQuery) Order(o ...OrderFunc) *AttachmentQuery {
|
||||
func (aq *AttachmentQuery) Order(o ...attachment.OrderOption) *AttachmentQuery {
|
||||
aq.order = append(aq.order, o...)
|
||||
return aq
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func (aq *AttachmentQuery) Clone() *AttachmentQuery {
|
||||
return &AttachmentQuery{
|
||||
config: aq.config,
|
||||
ctx: aq.ctx.Clone(),
|
||||
order: append([]OrderFunc{}, aq.order...),
|
||||
order: append([]attachment.OrderOption{}, aq.order...),
|
||||
inters: append([]Interceptor{}, aq.inters...),
|
||||
predicates: append([]predicate.Attachment{}, aq.predicates...),
|
||||
withItem: aq.withItem.Clone(),
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/document"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/item"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AttachmentUpdate is the builder for updating Attachment entities.
|
||||
@@ -51,6 +51,20 @@ func (au *AttachmentUpdate) SetNillableType(a *attachment.Type) *AttachmentUpdat
|
||||
return au
|
||||
}
|
||||
|
||||
// SetPrimary sets the "primary" field.
|
||||
func (au *AttachmentUpdate) SetPrimary(b bool) *AttachmentUpdate {
|
||||
au.mutation.SetPrimary(b)
|
||||
return au
|
||||
}
|
||||
|
||||
// SetNillablePrimary sets the "primary" field if the given value is not nil.
|
||||
func (au *AttachmentUpdate) SetNillablePrimary(b *bool) *AttachmentUpdate {
|
||||
if b != nil {
|
||||
au.SetPrimary(*b)
|
||||
}
|
||||
return au
|
||||
}
|
||||
|
||||
// SetItemID sets the "item" edge to the Item entity by ID.
|
||||
func (au *AttachmentUpdate) SetItemID(id uuid.UUID) *AttachmentUpdate {
|
||||
au.mutation.SetItemID(id)
|
||||
@@ -93,7 +107,7 @@ func (au *AttachmentUpdate) ClearDocument() *AttachmentUpdate {
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (au *AttachmentUpdate) Save(ctx context.Context) (int, error) {
|
||||
au.defaults()
|
||||
return withHooks[int, AttachmentMutation](ctx, au.sqlSave, au.mutation, au.hooks)
|
||||
return withHooks(ctx, au.sqlSave, au.mutation, au.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
@@ -160,6 +174,9 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
if value, ok := au.mutation.GetType(); ok {
|
||||
_spec.SetField(attachment.FieldType, field.TypeEnum, value)
|
||||
}
|
||||
if value, ok := au.mutation.Primary(); ok {
|
||||
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value)
|
||||
}
|
||||
if au.mutation.ItemCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
@@ -258,6 +275,20 @@ func (auo *AttachmentUpdateOne) SetNillableType(a *attachment.Type) *AttachmentU
|
||||
return auo
|
||||
}
|
||||
|
||||
// SetPrimary sets the "primary" field.
|
||||
func (auo *AttachmentUpdateOne) SetPrimary(b bool) *AttachmentUpdateOne {
|
||||
auo.mutation.SetPrimary(b)
|
||||
return auo
|
||||
}
|
||||
|
||||
// SetNillablePrimary sets the "primary" field if the given value is not nil.
|
||||
func (auo *AttachmentUpdateOne) SetNillablePrimary(b *bool) *AttachmentUpdateOne {
|
||||
if b != nil {
|
||||
auo.SetPrimary(*b)
|
||||
}
|
||||
return auo
|
||||
}
|
||||
|
||||
// SetItemID sets the "item" edge to the Item entity by ID.
|
||||
func (auo *AttachmentUpdateOne) SetItemID(id uuid.UUID) *AttachmentUpdateOne {
|
||||
auo.mutation.SetItemID(id)
|
||||
@@ -313,7 +344,7 @@ func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *Attachme
|
||||
// Save executes the query and returns the updated Attachment entity.
|
||||
func (auo *AttachmentUpdateOne) Save(ctx context.Context) (*Attachment, error) {
|
||||
auo.defaults()
|
||||
return withHooks[*Attachment, AttachmentMutation](ctx, auo.sqlSave, auo.mutation, auo.hooks)
|
||||
return withHooks(ctx, auo.sqlSave, auo.mutation, auo.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
@@ -397,6 +428,9 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
|
||||
if value, ok := auo.mutation.GetType(); ok {
|
||||
_spec.SetField(attachment.FieldType, field.TypeEnum, value)
|
||||
}
|
||||
if value, ok := auo.mutation.Primary(); ok {
|
||||
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value)
|
||||
}
|
||||
if auo.mutation.ItemCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
Rel: sqlgraph.M2O,
|
||||
|
||||
@@ -6,10 +6,11 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
)
|
||||
|
||||
// AuthRoles is the model entity for the AuthRoles schema.
|
||||
@@ -23,6 +24,7 @@ type AuthRoles struct {
|
||||
// The values are being populated by the AuthRolesQuery when eager-loading is set.
|
||||
Edges AuthRolesEdges `json:"edges"`
|
||||
auth_tokens_roles *uuid.UUID
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// AuthRolesEdges holds the relations/edges for other nodes in the graph.
|
||||
@@ -59,7 +61,7 @@ func (*AuthRoles) scanValues(columns []string) ([]any, error) {
|
||||
case authroles.ForeignKeys[0]: // auth_tokens_roles
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected column %q for type AuthRoles", columns[i])
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
@@ -92,11 +94,19 @@ func (ar *AuthRoles) assignValues(columns []string, values []any) error {
|
||||
ar.auth_tokens_roles = new(uuid.UUID)
|
||||
*ar.auth_tokens_roles = *value.S.(*uuid.UUID)
|
||||
}
|
||||
default:
|
||||
ar.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the AuthRoles.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (ar *AuthRoles) Value(name string) (ent.Value, error) {
|
||||
return ar.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryToken queries the "token" edge of the AuthRoles entity.
|
||||
func (ar *AuthRoles) QueryToken() *AuthTokensQuery {
|
||||
return NewAuthRolesClient(ar.config).QueryToken(ar)
|
||||
|
||||
@@ -4,6 +4,9 @@ package authroles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -79,3 +82,30 @@ func RoleValidator(r Role) error {
|
||||
return fmt.Errorf("authroles: invalid enum value for role field: %q", r)
|
||||
}
|
||||
}
|
||||
|
||||
// OrderOption defines the ordering options for the AuthRoles queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByRole orders the results by the role field.
|
||||
func ByRole(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldRole, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByTokenField orders the results by token field.
|
||||
func ByTokenField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newTokenStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
func newTokenStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(TokenInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package authroles
|
||||
import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -87,11 +87,7 @@ func HasToken() predicate.AuthRoles {
|
||||
// HasTokenWith applies the HasEdge predicate on the "token" edge with a given conditions (other predicates).
|
||||
func HasTokenWith(preds ...predicate.AuthTokens) predicate.AuthRoles {
|
||||
return predicate.AuthRoles(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(TokenInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
|
||||
)
|
||||
step := newTokenStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
@@ -102,32 +98,15 @@ func HasTokenWith(preds ...predicate.AuthTokens) predicate.AuthRoles {
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.AuthRoles) predicate.AuthRoles {
|
||||
return predicate.AuthRoles(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for _, p := range predicates {
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.AuthRoles(sql.AndPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.AuthRoles) predicate.AuthRoles {
|
||||
return predicate.AuthRoles(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for i, p := range predicates {
|
||||
if i > 0 {
|
||||
s1.Or()
|
||||
}
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.AuthRoles(sql.OrPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.AuthRoles) predicate.AuthRoles {
|
||||
return predicate.AuthRoles(func(s *sql.Selector) {
|
||||
p(s.Not())
|
||||
})
|
||||
return predicate.AuthRoles(sql.NotPredicates(p))
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
)
|
||||
|
||||
// AuthRolesCreate is the builder for creating a AuthRoles entity.
|
||||
@@ -62,7 +62,7 @@ func (arc *AuthRolesCreate) Mutation() *AuthRolesMutation {
|
||||
// Save creates the AuthRoles in the database.
|
||||
func (arc *AuthRolesCreate) Save(ctx context.Context) (*AuthRoles, error) {
|
||||
arc.defaults()
|
||||
return withHooks[*AuthRoles, AuthRolesMutation](ctx, arc.sqlSave, arc.mutation, arc.hooks)
|
||||
return withHooks(ctx, arc.sqlSave, arc.mutation, arc.hooks)
|
||||
}
|
||||
|
||||
// SaveX calls Save and panics if Save returns an error.
|
||||
@@ -158,11 +158,15 @@ func (arc *AuthRolesCreate) createSpec() (*AuthRoles, *sqlgraph.CreateSpec) {
|
||||
// AuthRolesCreateBulk is the builder for creating many AuthRoles entities in bulk.
|
||||
type AuthRolesCreateBulk struct {
|
||||
config
|
||||
err error
|
||||
builders []*AuthRolesCreate
|
||||
}
|
||||
|
||||
// Save creates the AuthRoles entities in the database.
|
||||
func (arcb *AuthRolesCreateBulk) Save(ctx context.Context) ([]*AuthRoles, error) {
|
||||
if arcb.err != nil {
|
||||
return nil, arcb.err
|
||||
}
|
||||
specs := make([]*sqlgraph.CreateSpec, len(arcb.builders))
|
||||
nodes := make([]*AuthRoles, len(arcb.builders))
|
||||
mutators := make([]Mutator, len(arcb.builders))
|
||||
@@ -179,8 +183,8 @@ func (arcb *AuthRolesCreateBulk) Save(ctx context.Context) ([]*AuthRoles, error)
|
||||
return nil, err
|
||||
}
|
||||
builder.mutation = mutation
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
var err error
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
if i < len(mutators)-1 {
|
||||
_, err = mutators[i+1].Mutate(root, arcb.builders[i+1].mutation)
|
||||
} else {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AuthRolesDelete is the builder for deleting a AuthRoles entity.
|
||||
@@ -27,7 +27,7 @@ func (ard *AuthRolesDelete) Where(ps ...predicate.AuthRoles) *AuthRolesDelete {
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func (ard *AuthRolesDelete) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks[int, AuthRolesMutation](ctx, ard.sqlExec, ard.mutation, ard.hooks)
|
||||
return withHooks(ctx, ard.sqlExec, ard.mutation, ard.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
|
||||
@@ -11,16 +11,16 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AuthRolesQuery is the builder for querying AuthRoles entities.
|
||||
type AuthRolesQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []OrderFunc
|
||||
order []authroles.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.AuthRoles
|
||||
withToken *AuthTokensQuery
|
||||
@@ -56,7 +56,7 @@ func (arq *AuthRolesQuery) Unique(unique bool) *AuthRolesQuery {
|
||||
}
|
||||
|
||||
// Order specifies how the records should be ordered.
|
||||
func (arq *AuthRolesQuery) Order(o ...OrderFunc) *AuthRolesQuery {
|
||||
func (arq *AuthRolesQuery) Order(o ...authroles.OrderOption) *AuthRolesQuery {
|
||||
arq.order = append(arq.order, o...)
|
||||
return arq
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func (arq *AuthRolesQuery) Clone() *AuthRolesQuery {
|
||||
return &AuthRolesQuery{
|
||||
config: arq.config,
|
||||
ctx: arq.ctx.Clone(),
|
||||
order: append([]OrderFunc{}, arq.order...),
|
||||
order: append([]authroles.OrderOption{}, arq.order...),
|
||||
inters: append([]Interceptor{}, arq.inters...),
|
||||
predicates: append([]predicate.AuthRoles{}, arq.predicates...),
|
||||
withToken: arq.withToken.Clone(),
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AuthRolesUpdate is the builder for updating AuthRoles entities.
|
||||
@@ -75,7 +75,7 @@ func (aru *AuthRolesUpdate) ClearToken() *AuthRolesUpdate {
|
||||
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (aru *AuthRolesUpdate) Save(ctx context.Context) (int, error) {
|
||||
return withHooks[int, AuthRolesMutation](ctx, aru.sqlSave, aru.mutation, aru.hooks)
|
||||
return withHooks(ctx, aru.sqlSave, aru.mutation, aru.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
@@ -233,7 +233,7 @@ func (aruo *AuthRolesUpdateOne) Select(field string, fields ...string) *AuthRole
|
||||
|
||||
// Save executes the query and returns the updated AuthRoles entity.
|
||||
func (aruo *AuthRolesUpdateOne) Save(ctx context.Context) (*AuthRoles, error) {
|
||||
return withHooks[*AuthRoles, AuthRolesMutation](ctx, aruo.sqlSave, aruo.mutation, aruo.hooks)
|
||||
return withHooks(ctx, aruo.sqlSave, aruo.mutation, aruo.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokens is the model entity for the AuthTokens schema.
|
||||
@@ -31,6 +32,7 @@ type AuthTokens struct {
|
||||
// The values are being populated by the AuthTokensQuery when eager-loading is set.
|
||||
Edges AuthTokensEdges `json:"edges"`
|
||||
user_auth_tokens *uuid.UUID
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// AuthTokensEdges holds the relations/edges for other nodes in the graph.
|
||||
@@ -84,7 +86,7 @@ func (*AuthTokens) scanValues(columns []string) ([]any, error) {
|
||||
case authtokens.ForeignKeys[0]: // user_auth_tokens
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected column %q for type AuthTokens", columns[i])
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
@@ -135,11 +137,19 @@ func (at *AuthTokens) assignValues(columns []string, values []any) error {
|
||||
at.user_auth_tokens = new(uuid.UUID)
|
||||
*at.user_auth_tokens = *value.S.(*uuid.UUID)
|
||||
}
|
||||
default:
|
||||
at.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the AuthTokens.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (at *AuthTokens) Value(name string) (ent.Value, error) {
|
||||
return at.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryUser queries the "user" edge of the AuthTokens entity.
|
||||
func (at *AuthTokens) QueryUser() *UserQuery {
|
||||
return NewAuthTokensClient(at.config).QueryUser(at)
|
||||
|
||||
@@ -5,6 +5,8 @@ package authtokens
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -85,3 +87,54 @@ var (
|
||||
// DefaultID holds the default value on creation for the "id" field.
|
||||
DefaultID func() uuid.UUID
|
||||
)
|
||||
|
||||
// OrderOption defines the ordering options for the AuthTokens queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatedAt orders the results by the created_at field.
|
||||
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUpdatedAt orders the results by the updated_at field.
|
||||
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByExpiresAt orders the results by the expires_at field.
|
||||
func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldExpiresAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUserField orders the results by user field.
|
||||
func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
|
||||
// ByRolesField orders the results by roles field.
|
||||
func ByRolesField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newRolesStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
func newUserStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(UserInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
|
||||
)
|
||||
}
|
||||
func newRolesStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(RolesInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -250,11 +250,7 @@ func HasUser() predicate.AuthTokens {
|
||||
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
|
||||
func HasUserWith(preds ...predicate.User) predicate.AuthTokens {
|
||||
return predicate.AuthTokens(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(UserInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
|
||||
)
|
||||
step := newUserStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
@@ -277,11 +273,7 @@ func HasRoles() predicate.AuthTokens {
|
||||
// HasRolesWith applies the HasEdge predicate on the "roles" edge with a given conditions (other predicates).
|
||||
func HasRolesWith(preds ...predicate.AuthRoles) predicate.AuthTokens {
|
||||
return predicate.AuthTokens(func(s *sql.Selector) {
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(RolesInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
|
||||
)
|
||||
step := newRolesStep()
|
||||
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
|
||||
for _, p := range preds {
|
||||
p(s)
|
||||
@@ -292,32 +284,15 @@ func HasRolesWith(preds ...predicate.AuthRoles) predicate.AuthTokens {
|
||||
|
||||
// And groups predicates with the AND operator between them.
|
||||
func And(predicates ...predicate.AuthTokens) predicate.AuthTokens {
|
||||
return predicate.AuthTokens(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for _, p := range predicates {
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.AuthTokens(sql.AndPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Or groups predicates with the OR operator between them.
|
||||
func Or(predicates ...predicate.AuthTokens) predicate.AuthTokens {
|
||||
return predicate.AuthTokens(func(s *sql.Selector) {
|
||||
s1 := s.Clone().SetP(nil)
|
||||
for i, p := range predicates {
|
||||
if i > 0 {
|
||||
s1.Or()
|
||||
}
|
||||
p(s1)
|
||||
}
|
||||
s.Where(s1.P())
|
||||
})
|
||||
return predicate.AuthTokens(sql.OrPredicates(predicates...))
|
||||
}
|
||||
|
||||
// Not applies the not operator on the given predicate.
|
||||
func Not(p predicate.AuthTokens) predicate.AuthTokens {
|
||||
return predicate.AuthTokens(func(s *sql.Selector) {
|
||||
p(s.Not())
|
||||
})
|
||||
return predicate.AuthTokens(sql.NotPredicates(p))
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokensCreate is the builder for creating a AuthTokens entity.
|
||||
@@ -131,7 +131,7 @@ func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation {
|
||||
// Save creates the AuthTokens in the database.
|
||||
func (atc *AuthTokensCreate) Save(ctx context.Context) (*AuthTokens, error) {
|
||||
atc.defaults()
|
||||
return withHooks[*AuthTokens, AuthTokensMutation](ctx, atc.sqlSave, atc.mutation, atc.hooks)
|
||||
return withHooks(ctx, atc.sqlSave, atc.mutation, atc.hooks)
|
||||
}
|
||||
|
||||
// SaveX calls Save and panics if Save returns an error.
|
||||
@@ -280,11 +280,15 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
|
||||
// AuthTokensCreateBulk is the builder for creating many AuthTokens entities in bulk.
|
||||
type AuthTokensCreateBulk struct {
|
||||
config
|
||||
err error
|
||||
builders []*AuthTokensCreate
|
||||
}
|
||||
|
||||
// Save creates the AuthTokens entities in the database.
|
||||
func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, error) {
|
||||
if atcb.err != nil {
|
||||
return nil, atcb.err
|
||||
}
|
||||
specs := make([]*sqlgraph.CreateSpec, len(atcb.builders))
|
||||
nodes := make([]*AuthTokens, len(atcb.builders))
|
||||
mutators := make([]Mutator, len(atcb.builders))
|
||||
@@ -301,8 +305,8 @@ func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, erro
|
||||
return nil, err
|
||||
}
|
||||
builder.mutation = mutation
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
var err error
|
||||
nodes[i], specs[i] = builder.createSpec()
|
||||
if i < len(mutators)-1 {
|
||||
_, err = mutators[i+1].Mutate(root, atcb.builders[i+1].mutation)
|
||||
} else {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AuthTokensDelete is the builder for deleting a AuthTokens entity.
|
||||
@@ -27,7 +27,7 @@ func (atd *AuthTokensDelete) Where(ps ...predicate.AuthTokens) *AuthTokensDelete
|
||||
|
||||
// Exec executes the deletion query and returns how many vertices were deleted.
|
||||
func (atd *AuthTokensDelete) Exec(ctx context.Context) (int, error) {
|
||||
return withHooks[int, AuthTokensMutation](ctx, atd.sqlExec, atd.mutation, atd.hooks)
|
||||
return withHooks(ctx, atd.sqlExec, atd.mutation, atd.hooks)
|
||||
}
|
||||
|
||||
// ExecX is like Exec, but panics if an error occurs.
|
||||
|
||||
@@ -12,17 +12,17 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokensQuery is the builder for querying AuthTokens entities.
|
||||
type AuthTokensQuery struct {
|
||||
config
|
||||
ctx *QueryContext
|
||||
order []OrderFunc
|
||||
order []authtokens.OrderOption
|
||||
inters []Interceptor
|
||||
predicates []predicate.AuthTokens
|
||||
withUser *UserQuery
|
||||
@@ -59,7 +59,7 @@ func (atq *AuthTokensQuery) Unique(unique bool) *AuthTokensQuery {
|
||||
}
|
||||
|
||||
// Order specifies how the records should be ordered.
|
||||
func (atq *AuthTokensQuery) Order(o ...OrderFunc) *AuthTokensQuery {
|
||||
func (atq *AuthTokensQuery) Order(o ...authtokens.OrderOption) *AuthTokensQuery {
|
||||
atq.order = append(atq.order, o...)
|
||||
return atq
|
||||
}
|
||||
@@ -297,7 +297,7 @@ func (atq *AuthTokensQuery) Clone() *AuthTokensQuery {
|
||||
return &AuthTokensQuery{
|
||||
config: atq.config,
|
||||
ctx: atq.ctx.Clone(),
|
||||
order: append([]OrderFunc{}, atq.order...),
|
||||
order: append([]authtokens.OrderOption{}, atq.order...),
|
||||
inters: append([]Interceptor{}, atq.inters...),
|
||||
predicates: append([]predicate.AuthTokens{}, atq.predicates...),
|
||||
withUser: atq.withUser.Clone(),
|
||||
@@ -494,7 +494,7 @@ func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery
|
||||
}
|
||||
query.withFKs = true
|
||||
query.Where(predicate.AuthRoles(func(s *sql.Selector) {
|
||||
s.Where(sql.InValues(authtokens.RolesColumn, fks...))
|
||||
s.Where(sql.InValues(s.C(authtokens.RolesColumn), fks...))
|
||||
}))
|
||||
neighbors, err := query.All(ctx)
|
||||
if err != nil {
|
||||
@@ -507,7 +507,7 @@ func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery
|
||||
}
|
||||
node, ok := nodeids[*fk]
|
||||
if !ok {
|
||||
return fmt.Errorf(`unexpected foreign-key "auth_tokens_roles" returned %v for node %v`, *fk, n.ID)
|
||||
return fmt.Errorf(`unexpected referenced foreign-key "auth_tokens_roles" returned %v for node %v`, *fk, n.ID)
|
||||
}
|
||||
assign(node, n)
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokensUpdate is the builder for updating AuthTokens entities.
|
||||
@@ -115,7 +115,7 @@ func (atu *AuthTokensUpdate) ClearRoles() *AuthTokensUpdate {
|
||||
// Save executes the query and returns the number of nodes affected by the update operation.
|
||||
func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) {
|
||||
atu.defaults()
|
||||
return withHooks[int, AuthTokensMutation](ctx, atu.sqlSave, atu.mutation, atu.hooks)
|
||||
return withHooks(ctx, atu.sqlSave, atu.mutation, atu.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
@@ -341,7 +341,7 @@ func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTok
|
||||
// Save executes the query and returns the updated AuthTokens entity.
|
||||
func (atuo *AuthTokensUpdateOne) Save(ctx context.Context) (*AuthTokens, error) {
|
||||
atuo.defaults()
|
||||
return withHooks[*AuthTokens, AuthTokensMutation](ctx, atuo.sqlSave, atuo.mutation, atuo.hooks)
|
||||
return withHooks(ctx, atuo.sqlSave, atuo.mutation, atuo.hooks)
|
||||
}
|
||||
|
||||
// SaveX is like Save, but panics if an error occurs.
|
||||
|
||||
@@ -7,27 +7,28 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/migrate"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/itemfield"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/maintenanceentry"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/notifier"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authroles"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/document"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/group"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/item"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/itemfield"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/label"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/location"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/maintenanceentry"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/notifier"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// Client is the client that holds all ent builders.
|
||||
@@ -65,9 +66,7 @@ type Client struct {
|
||||
|
||||
// NewClient creates a new client configured with the given options.
|
||||
func NewClient(opts ...Option) *Client {
|
||||
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
|
||||
cfg.options(opts...)
|
||||
client := &Client{config: cfg}
|
||||
client := &Client{config: newConfig(opts...)}
|
||||
client.init()
|
||||
return client
|
||||
}
|
||||
@@ -107,6 +106,13 @@ type (
|
||||
Option func(*config)
|
||||
)
|
||||
|
||||
// newConfig creates a new config for the client.
|
||||
func newConfig(opts ...Option) config {
|
||||
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
|
||||
cfg.options(opts...)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// options applies the options on the config object.
|
||||
func (c *config) options(opts ...Option) {
|
||||
for _, opt := range opts {
|
||||
@@ -154,11 +160,14 @@ func Open(driverName, dataSourceName string, options ...Option) (*Client, error)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrTxStarted is returned when trying to start a new transaction from a transactional client.
|
||||
var ErrTxStarted = errors.New("ent: cannot start a transaction within a transaction")
|
||||
|
||||
// Tx returns a new transactional client. The provided context
|
||||
// is used until the transaction is committed or rolled back.
|
||||
func (c *Client) Tx(ctx context.Context) (*Tx, error) {
|
||||
if _, ok := c.driver.(*txDriver); ok {
|
||||
return nil, errors.New("ent: cannot start a transaction within a transaction")
|
||||
return nil, ErrTxStarted
|
||||
}
|
||||
tx, err := newTx(ctx, c.driver)
|
||||
if err != nil {
|
||||
@@ -330,6 +339,21 @@ func (c *AttachmentClient) CreateBulk(builders ...*AttachmentCreate) *Attachment
|
||||
return &AttachmentCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *AttachmentClient) MapCreateBulk(slice any, setFunc func(*AttachmentCreate, int)) *AttachmentCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &AttachmentCreateBulk{err: fmt.Errorf("calling to AttachmentClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*AttachmentCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &AttachmentCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Attachment.
|
||||
func (c *AttachmentClient) Update() *AttachmentUpdate {
|
||||
mutation := newAttachmentMutation(c.config, OpUpdate)
|
||||
@@ -480,6 +504,21 @@ func (c *AuthRolesClient) CreateBulk(builders ...*AuthRolesCreate) *AuthRolesCre
|
||||
return &AuthRolesCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *AuthRolesClient) MapCreateBulk(slice any, setFunc func(*AuthRolesCreate, int)) *AuthRolesCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &AuthRolesCreateBulk{err: fmt.Errorf("calling to AuthRolesClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*AuthRolesCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &AuthRolesCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for AuthRoles.
|
||||
func (c *AuthRolesClient) Update() *AuthRolesUpdate {
|
||||
mutation := newAuthRolesMutation(c.config, OpUpdate)
|
||||
@@ -614,6 +653,21 @@ func (c *AuthTokensClient) CreateBulk(builders ...*AuthTokensCreate) *AuthTokens
|
||||
return &AuthTokensCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *AuthTokensClient) MapCreateBulk(slice any, setFunc func(*AuthTokensCreate, int)) *AuthTokensCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &AuthTokensCreateBulk{err: fmt.Errorf("calling to AuthTokensClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*AuthTokensCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &AuthTokensCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for AuthTokens.
|
||||
func (c *AuthTokensClient) Update() *AuthTokensUpdate {
|
||||
mutation := newAuthTokensMutation(c.config, OpUpdate)
|
||||
@@ -764,6 +818,21 @@ func (c *DocumentClient) CreateBulk(builders ...*DocumentCreate) *DocumentCreate
|
||||
return &DocumentCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *DocumentClient) MapCreateBulk(slice any, setFunc func(*DocumentCreate, int)) *DocumentCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &DocumentCreateBulk{err: fmt.Errorf("calling to DocumentClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*DocumentCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &DocumentCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Document.
|
||||
func (c *DocumentClient) Update() *DocumentUpdate {
|
||||
mutation := newDocumentMutation(c.config, OpUpdate)
|
||||
@@ -914,6 +983,21 @@ func (c *GroupClient) CreateBulk(builders ...*GroupCreate) *GroupCreateBulk {
|
||||
return &GroupCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *GroupClient) MapCreateBulk(slice any, setFunc func(*GroupCreate, int)) *GroupCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &GroupCreateBulk{err: fmt.Errorf("calling to GroupClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*GroupCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &GroupCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Group.
|
||||
func (c *GroupClient) Update() *GroupUpdate {
|
||||
mutation := newGroupMutation(c.config, OpUpdate)
|
||||
@@ -1144,6 +1228,21 @@ func (c *GroupInvitationTokenClient) CreateBulk(builders ...*GroupInvitationToke
|
||||
return &GroupInvitationTokenCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *GroupInvitationTokenClient) MapCreateBulk(slice any, setFunc func(*GroupInvitationTokenCreate, int)) *GroupInvitationTokenCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &GroupInvitationTokenCreateBulk{err: fmt.Errorf("calling to GroupInvitationTokenClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*GroupInvitationTokenCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &GroupInvitationTokenCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for GroupInvitationToken.
|
||||
func (c *GroupInvitationTokenClient) Update() *GroupInvitationTokenUpdate {
|
||||
mutation := newGroupInvitationTokenMutation(c.config, OpUpdate)
|
||||
@@ -1278,6 +1377,21 @@ func (c *ItemClient) CreateBulk(builders ...*ItemCreate) *ItemCreateBulk {
|
||||
return &ItemCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *ItemClient) MapCreateBulk(slice any, setFunc func(*ItemCreate, int)) *ItemCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &ItemCreateBulk{err: fmt.Errorf("calling to ItemClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*ItemCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &ItemCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Item.
|
||||
func (c *ItemClient) Update() *ItemUpdate {
|
||||
mutation := newItemMutation(c.config, OpUpdate)
|
||||
@@ -1524,6 +1638,21 @@ func (c *ItemFieldClient) CreateBulk(builders ...*ItemFieldCreate) *ItemFieldCre
|
||||
return &ItemFieldCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *ItemFieldClient) MapCreateBulk(slice any, setFunc func(*ItemFieldCreate, int)) *ItemFieldCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &ItemFieldCreateBulk{err: fmt.Errorf("calling to ItemFieldClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*ItemFieldCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &ItemFieldCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for ItemField.
|
||||
func (c *ItemFieldClient) Update() *ItemFieldUpdate {
|
||||
mutation := newItemFieldMutation(c.config, OpUpdate)
|
||||
@@ -1658,6 +1787,21 @@ func (c *LabelClient) CreateBulk(builders ...*LabelCreate) *LabelCreateBulk {
|
||||
return &LabelCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *LabelClient) MapCreateBulk(slice any, setFunc func(*LabelCreate, int)) *LabelCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &LabelCreateBulk{err: fmt.Errorf("calling to LabelClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*LabelCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &LabelCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Label.
|
||||
func (c *LabelClient) Update() *LabelUpdate {
|
||||
mutation := newLabelMutation(c.config, OpUpdate)
|
||||
@@ -1808,6 +1952,21 @@ func (c *LocationClient) CreateBulk(builders ...*LocationCreate) *LocationCreate
|
||||
return &LocationCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *LocationClient) MapCreateBulk(slice any, setFunc func(*LocationCreate, int)) *LocationCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &LocationCreateBulk{err: fmt.Errorf("calling to LocationClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*LocationCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &LocationCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Location.
|
||||
func (c *LocationClient) Update() *LocationUpdate {
|
||||
mutation := newLocationMutation(c.config, OpUpdate)
|
||||
@@ -1990,6 +2149,21 @@ func (c *MaintenanceEntryClient) CreateBulk(builders ...*MaintenanceEntryCreate)
|
||||
return &MaintenanceEntryCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *MaintenanceEntryClient) MapCreateBulk(slice any, setFunc func(*MaintenanceEntryCreate, int)) *MaintenanceEntryCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &MaintenanceEntryCreateBulk{err: fmt.Errorf("calling to MaintenanceEntryClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*MaintenanceEntryCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &MaintenanceEntryCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for MaintenanceEntry.
|
||||
func (c *MaintenanceEntryClient) Update() *MaintenanceEntryUpdate {
|
||||
mutation := newMaintenanceEntryMutation(c.config, OpUpdate)
|
||||
@@ -2124,6 +2298,21 @@ func (c *NotifierClient) CreateBulk(builders ...*NotifierCreate) *NotifierCreate
|
||||
return &NotifierCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *NotifierClient) MapCreateBulk(slice any, setFunc func(*NotifierCreate, int)) *NotifierCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &NotifierCreateBulk{err: fmt.Errorf("calling to NotifierClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*NotifierCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &NotifierCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for Notifier.
|
||||
func (c *NotifierClient) Update() *NotifierUpdate {
|
||||
mutation := newNotifierMutation(c.config, OpUpdate)
|
||||
@@ -2274,6 +2463,21 @@ func (c *UserClient) CreateBulk(builders ...*UserCreate) *UserCreateBulk {
|
||||
return &UserCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
|
||||
// a builder and applies setFunc on it.
|
||||
func (c *UserClient) MapCreateBulk(slice any, setFunc func(*UserCreate, int)) *UserCreateBulk {
|
||||
rv := reflect.ValueOf(slice)
|
||||
if rv.Kind() != reflect.Slice {
|
||||
return &UserCreateBulk{err: fmt.Errorf("calling to UserClient.MapCreateBulk with wrong type %T, need slice", slice)}
|
||||
}
|
||||
builders := make([]*UserCreate, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
builders[i] = c.Create()
|
||||
setFunc(builders[i], i)
|
||||
}
|
||||
return &UserCreateBulk{config: c.config, builders: builders}
|
||||
}
|
||||
|
||||
// Update returns an update builder for User.
|
||||
func (c *UserClient) Update() *UserUpdate {
|
||||
mutation := newUserMutation(c.config, OpUpdate)
|
||||
|
||||
@@ -7,10 +7,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/document"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/data/ent/group"
|
||||
)
|
||||
|
||||
// Document is the model entity for the Document schema.
|
||||
@@ -30,6 +31,7 @@ type Document struct {
|
||||
// The values are being populated by the DocumentQuery when eager-loading is set.
|
||||
Edges DocumentEdges `json:"edges"`
|
||||
group_documents *uuid.UUID
|
||||
selectValues sql.SelectValues
|
||||
}
|
||||
|
||||
// DocumentEdges holds the relations/edges for other nodes in the graph.
|
||||
@@ -79,7 +81,7 @@ func (*Document) scanValues(columns []string) ([]any, error) {
|
||||
case document.ForeignKeys[0]: // group_documents
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected column %q for type Document", columns[i])
|
||||
values[i] = new(sql.UnknownType)
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
@@ -130,11 +132,19 @@ func (d *Document) assignValues(columns []string, values []any) error {
|
||||
d.group_documents = new(uuid.UUID)
|
||||
*d.group_documents = *value.S.(*uuid.UUID)
|
||||
}
|
||||
default:
|
||||
d.selectValues.Set(columns[i], values[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value returns the ent.Value that was dynamically selected and assigned to the Document.
|
||||
// This includes values selected through modifiers, order, etc.
|
||||
func (d *Document) Value(name string) (ent.Value, error) {
|
||||
return d.selectValues.Get(name)
|
||||
}
|
||||
|
||||
// QueryGroup queries the "group" edge of the Document entity.
|
||||
func (d *Document) QueryGroup() *GroupQuery {
|
||||
return NewDocumentClient(d.config).QueryGroup(d)
|
||||
|
||||
@@ -5,6 +5,8 @@ package document
|
||||
import (
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -87,3 +89,66 @@ var (
|
||||
// DefaultID holds the default value on creation for the "id" field.
|
||||
DefaultID func() uuid.UUID
|
||||
)
|
||||
|
||||
// OrderOption defines the ordering options for the Document queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
// ByID orders the results by the id field.
|
||||
func ByID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatedAt orders the results by the created_at field.
|
||||
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByUpdatedAt orders the results by the updated_at field.
|
||||
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByTitle orders the results by the title field.
|
||||
func ByTitle(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldTitle, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByPath orders the results by the path field.
|
||||
func ByPath(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldPath, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByGroupField orders the results by group field.
|
||||
func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newGroupStep(), sql.OrderByField(field, opts...))
|
||||
}
|
||||
}
|
||||
|
||||
// ByAttachmentsCount orders the results by attachments count.
|
||||
func ByAttachmentsCount(opts ...sql.OrderTermOption) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborsCount(s, newAttachmentsStep(), opts...)
|
||||
}
|
||||
}
|
||||
|
||||
// ByAttachments orders the results by attachments terms.
|
||||
func ByAttachments(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
|
||||
return func(s *sql.Selector) {
|
||||
sqlgraph.OrderByNeighborTerms(s, newAttachmentsStep(), append([]sql.OrderTerm{term}, terms...)...)
|
||||
}
|
||||
}
|
||||
func newGroupStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(GroupInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
|
||||
)
|
||||
}
|
||||
func newAttachmentsStep() *sqlgraph.Step {
|
||||
return sqlgraph.NewStep(
|
||||
sqlgraph.From(Table, FieldID),
|
||||
sqlgraph.To(AttachmentsInverseTable, FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user