mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 21:33:02 +01:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af1ab9d1af | ||
|
|
69e5a877c0 | ||
|
|
5c0d161eb4 | ||
|
|
91cd0d1bca | ||
|
|
b12011f4a6 | ||
|
|
0223fbbb3f | ||
|
|
a709d38946 | ||
|
|
bd2eb8b5ac | ||
|
|
5a015b9581 | ||
|
|
552cb0bf53 | ||
|
|
a93f4ff1ad | ||
|
|
3d283c2d81 | ||
|
|
ea1d92207a | ||
|
|
bcd826ed4f | ||
|
|
499bb90b09 | ||
|
|
4f00822849 | ||
| ff28175838 | |||
|
|
0f482aebad | ||
|
|
76fe0d3522 | ||
|
|
4995e04cf0 | ||
|
|
76123e00d6 | ||
|
|
0b7de9557d | ||
|
|
0f25983278 | ||
|
|
40a093656b | ||
|
|
8b8a96f93b | ||
|
|
7b9c3d52cd | ||
|
|
a0198fb66f | ||
|
|
04eb136ab0 | ||
|
|
16f3fb19e8 | ||
|
|
6a1ffd7700 | ||
|
|
fff0f4344c | ||
|
|
24dc182c0e | ||
|
|
b4be462ce5 | ||
| 9ed618d45e | |||
| e79905b608 | |||
| e929c38e37 | |||
| e406bb2d04 | |||
|
|
a3f4c97049 | ||
|
|
51c21edb67 | ||
|
|
892635b5e8 | ||
|
|
c9e603c1c2 | ||
|
|
02bc58ed20 | ||
|
|
7931003cb7 | ||
|
|
79c811f65e | ||
|
|
07c0193e55 | ||
|
|
87e2464599 | ||
|
|
30abdd4d36 | ||
|
|
8a57ca41bf | ||
|
|
b57efa02ff | ||
|
|
d7bf64742e | ||
|
|
7e150aa8d7 | ||
|
|
4d916a69af | ||
|
|
7096616414 | ||
|
|
2292d72802 | ||
|
|
a8d21f0465 | ||
|
|
53d542413b | ||
|
|
aaeac8ca9d | ||
|
|
a780c6fac4 | ||
|
|
2a54933cef | ||
|
|
fa67e7d09f | ||
|
|
add57a8962 | ||
|
|
22c76c52a2 | ||
|
|
dd1c09fe0c | ||
|
|
b4f7d2152a | ||
|
|
25e1c41335 | ||
|
|
bde7ccbb2c |
@@ -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.21"
|
||||
"ghcr.io/devcontainers/features/go:1": "1.21"
|
||||
}
|
||||
}
|
||||
|
||||
16
.github/pull_request_template.md
vendored
16
.github/pull_request_template.md
vendored
@@ -55,18 +55,4 @@ _(fill-in or delete this section)_
|
||||
|
||||
<!--
|
||||
Describe how you tested this change.
|
||||
-->
|
||||
|
||||
## Release Notes
|
||||
|
||||
_(REQUIRED)_
|
||||
<!--
|
||||
If this PR makes user facing changes, please describe them here. This
|
||||
description will be copied into the release notes/changelog, whenever the
|
||||
next version is released. Keep this section short, and focus on high level
|
||||
changes.
|
||||
Put your text between the block. To omit notes, use NONE within the block.
|
||||
-->
|
||||
|
||||
```release-note
|
||||
```
|
||||
-->
|
||||
47
.github/workflows/binaries-publish.yaml
vendored
Normal file
47
.github/workflows/binaries-publish.yaml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Publish Release Binaries
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ 'v*.*.*' ]
|
||||
|
||||
jobs:
|
||||
# backend-tests:
|
||||
# name: "Backend Server Tests"
|
||||
# uses: sysadminsmedia/homebox/.github/workflows/partial-backend.yaml@main
|
||||
|
||||
# frontend-tests:
|
||||
# name: "Frontend and End-to-End Tests"
|
||||
# uses: sysadminsmedia/homebox/.github/workflows/partial-frontend.yaml@main
|
||||
|
||||
goreleaser:
|
||||
name: goreleaser
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7.30.1
|
||||
|
||||
- name: Build Frontend and Copy to Backend
|
||||
working-directory: frontend
|
||||
run: |
|
||||
pnpm install --shamefully-hoist
|
||||
pnpm run build
|
||||
cp -r ./.output/public ../backend/app/api/static/
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
workdir: "backend"
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
55
.github/workflows/docker-publish-rootless.yaml
vendored
55
.github/workflows/docker-publish-rootless.yaml
vendored
@@ -5,10 +5,25 @@ on:
|
||||
- cron: '00 6 * * *'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile.rootless'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows'
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile.rootless'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows'
|
||||
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
@@ -33,25 +48,17 @@ jobs:
|
||||
- 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
|
||||
uses: docker/setup-buildx-action@v3.0.0 # 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
|
||||
uses: docker/login-action@v3.0.0 # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -61,7 +68,7 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: metadata
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||
uses: docker/metadata-action@v5.0.0 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
@@ -78,30 +85,18 @@ jobs:
|
||||
# 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
|
||||
uses: docker/build-push-action@v5.0.0 # 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}
|
||||
# cache-from: type=gha
|
||||
# cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
VERSION=${{ github.ref_name }}
|
||||
COMMIT=${{ github.sha }}
|
||||
|
||||
- name: Attest
|
||||
uses: actions/attest-build-provenance@v1
|
||||
@@ -110,4 +105,4 @@ jobs:
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
subject-digest: ${{ steps.build-and-push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
push-to-registry: true
|
||||
|
||||
54
.github/workflows/docker-publish.yaml
vendored
54
.github/workflows/docker-publish.yaml
vendored
@@ -5,10 +5,24 @@ on:
|
||||
- cron: '00 6 * * *'
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile.rootless'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows'
|
||||
# Publish semver tags as releases.
|
||||
tags: [ 'v*.*.*' ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'Dockerfile'
|
||||
- 'Dockerfile.rootless'
|
||||
- '.dockerignore'
|
||||
- '.github/workflows'
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
@@ -33,25 +47,17 @@ jobs:
|
||||
- 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
|
||||
uses: docker/setup-buildx-action@v3.0.0 # 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
|
||||
uses: docker/login-action@v3.0.0 # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -61,7 +67,7 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||
uses: docker/metadata-action@v5.0.0 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
@@ -76,30 +82,18 @@ jobs:
|
||||
# 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
|
||||
uses: docker/build-push-action@v5.0.0 # 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}
|
||||
# cache-from: type=gha
|
||||
# cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
VERSION=${{ github.ref_name }}
|
||||
COMMIT=${{ github.sha }}
|
||||
|
||||
- name: Attest
|
||||
uses: actions/attest-build-provenance@v1
|
||||
@@ -108,4 +102,4 @@ jobs:
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
subject-digest: ${{ steps.build-and-push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
push-to-registry: true
|
||||
|
||||
89
.github/workflows/partial-publish.yaml
vendored
89
.github/workflows/partial-publish.yaml
vendored
@@ -1,89 +0,0 @@
|
||||
name: Frontend / E2E
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
release:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
secrets:
|
||||
GH_TOKEN:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: "Publish Homebox"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
image: tonistiigi/binfmt:latest
|
||||
platforms: all
|
||||
|
||||
- name: install buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: login to container registry
|
||||
run: docker login ghcr.io --username hay-kot --password $CR_PAT
|
||||
env:
|
||||
CR_PAT: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: build nightly image
|
||||
if: ${{ inputs.release == false }}
|
||||
run: |
|
||||
docker build --push --no-cache \
|
||||
--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/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 .
|
||||
29
.github/workflows/publish.yaml
vendored
29
.github/workflows/publish.yaml
vendored
@@ -1,29 +0,0 @@
|
||||
name: Publish Dockers
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: "Deploy Nightly to Fly.io"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- 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: sysadminsmedia/homebox/.github/workflows/partial-publish.yaml@main
|
||||
with:
|
||||
tag: nightly
|
||||
secrets:
|
||||
GH_TOKEN: ${{ secrets.CR_PAT }}
|
||||
|
||||
|
||||
4
.github/workflows/pull-requests.yaml
vendored
4
.github/workflows/pull-requests.yaml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
|
||||
jobs:
|
||||
backend-tests:
|
||||
name: "Backend Server Tests"
|
||||
|
||||
77
.github/workflows/tag.yaml
vendored
77
.github/workflows/tag.yaml
vendored
@@ -1,77 +0,0 @@
|
||||
name: Publish Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
|
||||
jobs:
|
||||
backend-tests:
|
||||
name: "Backend Server Tests"
|
||||
uses: sysadminsmedia/homebox/.github/workflows/partial-backend.yaml@main
|
||||
|
||||
frontend-tests:
|
||||
name: "Frontend and End-to-End Tests"
|
||||
uses: sysadminsmedia/homebox/.github/workflows/partial-frontend.yaml@main
|
||||
|
||||
goreleaser:
|
||||
name: goreleaser
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 7.30.1
|
||||
|
||||
- name: Build Frontend and Copy to Backend
|
||||
working-directory: frontend
|
||||
run: |
|
||||
pnpm install --shamefully-hoist
|
||||
pnpm run build
|
||||
cp -r ./.output/public ../backend/app/api/static/
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
workdir: "backend"
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
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
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -48,7 +48,7 @@ dist
|
||||
|
||||
.pnpm-store
|
||||
backend/app/api/app
|
||||
backend/app/api/__debug_bin
|
||||
backend/app/api/__debug_bin*
|
||||
dist/
|
||||
|
||||
# Nuxt Publish Dir
|
||||
|
||||
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -25,6 +25,7 @@
|
||||
"HBOX_STORAGE_DATA": "${workspaceRoot}/backend/.data",
|
||||
"HBOX_STORAGE_SQLITE_URL": "${workspaceRoot}/backend/.data/homebox.db?_fk=1"
|
||||
},
|
||||
"console": "integratedTerminal",
|
||||
},
|
||||
{
|
||||
"name": "Launch Frontend",
|
||||
@@ -38,10 +39,11 @@
|
||||
"cwd": "${workspaceFolder}/frontend",
|
||||
"serverReadyAction": {
|
||||
"action": "debugWithChrome",
|
||||
"pattern": "Local: http://localhost:([0-9]+)",
|
||||
"pattern": "Local: +http://localhost:([0-9]+)",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"webRoot": "${workspaceFolder}/frontend"
|
||||
}
|
||||
},
|
||||
"console": "integratedTerminal",
|
||||
}
|
||||
]
|
||||
}
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[support@sysadminemedia.com](mailto:support@sysadminemedia.com).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
[Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[FAQ](https://www.contributor-covenant.org/faq). Translations are available at
|
||||
[Translations](https://www.contributor-covenant.org/translations).
|
||||
10
Dockerfile
10
Dockerfile
@@ -1,6 +1,6 @@
|
||||
|
||||
# Build Nuxt
|
||||
FROM node:18-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 ./
|
||||
@@ -27,6 +27,8 @@ RUN CGO_ENABLED=0 GOOS=linux go build \
|
||||
-o /go/bin/api \
|
||||
-v ./app/api/*.go
|
||||
|
||||
FROM gcr.io/distroless/java:latest
|
||||
|
||||
# Production Stage
|
||||
FROM alpine:latest
|
||||
|
||||
@@ -39,11 +41,17 @@ RUN mkdir /app
|
||||
COPY --from=builder /go/bin/api /app
|
||||
|
||||
RUN chmod +x /app/api
|
||||
RUN apk add --no-cache wget
|
||||
|
||||
LABEL Name=homebox Version=0.0.1
|
||||
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"
|
||||
EXPOSE 7745
|
||||
WORKDIR /app
|
||||
HEALTHCHECK --interval=30s \
|
||||
--timeout=5s \
|
||||
--start-period=5s \
|
||||
--retries=3 \
|
||||
CMD [ "/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:7745/api/v1/status" ]
|
||||
VOLUME [ "/data" ]
|
||||
|
||||
ENTRYPOINT [ "/app/api" ]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
# Build Nuxt
|
||||
FROM node:18-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 ./
|
||||
@@ -13,6 +13,7 @@ FROM golang:alpine AS builder
|
||||
ARG BUILD_TIME
|
||||
ARG COMMIT
|
||||
ARG VERSION
|
||||
ARG BUSYBOX_VERSION=1.36.1-r31
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --update git build-base gcc g++
|
||||
@@ -30,8 +31,10 @@ RUN CGO_ENABLED=0 GOOS=linux go build \
|
||||
# create a directory so that we can copy it in the next stage
|
||||
mkdir /data
|
||||
|
||||
FROM gcr.io/distroless/java:latest
|
||||
|
||||
# Production Stage
|
||||
FROM gcr.io/distroless/static
|
||||
FROM gcr.io/distroless/static:latest
|
||||
|
||||
ENV HBOX_MODE=production
|
||||
ENV HBOX_STORAGE_DATA=/data/
|
||||
@@ -42,9 +45,16 @@ ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1
|
||||
COPY --from=builder --chown=nonroot /go/bin/api /app
|
||||
COPY --from=builder --chown=nonroot /data /data
|
||||
|
||||
RUN apk add --no-cache wget
|
||||
|
||||
LABEL Name=homebox Version=0.0.1
|
||||
LABEL org.opencontainers.image.source="https://github.com/sysadminsmedia/homebox"
|
||||
EXPOSE 7745
|
||||
HEALTHCHECK --interval=30s \
|
||||
--timeout=5s \
|
||||
--start-period=5s \
|
||||
--retries=3 \
|
||||
CMD [ "/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:7745/api/v1/status" ]
|
||||
VOLUME [ "/data" ]
|
||||
|
||||
# Drop root and run as low-privileged user
|
||||
|
||||
@@ -8,10 +8,11 @@
|
||||
|
|
||||
<a href="https://homebox.fly.dev">Demo</a>
|
||||
|
|
||||
<a href="https://discord.gg/tuncmNrE4z">Discord</a>
|
||||
<a href="https://discord.gg/aY4DCkpNA9">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.
|
||||
@@ -21,7 +22,7 @@ Homebox is the inventory and organization system built for the Home User! With a
|
||||
|
||||
## Quick Start
|
||||
|
||||
[Configuration & Docker Compose](https://homebox.sysadminsmedia.com/quick-start.html)
|
||||
[Configuration & Docker Compose](https://homebox.sysadminsmedia.com/en/quick-start.html)
|
||||
|
||||
```bash
|
||||
# If using the rootless image, ensure data
|
||||
@@ -39,6 +40,7 @@ docker run -d \
|
||||
```
|
||||
|
||||
<!-- 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**.
|
||||
@@ -46,5 +48,6 @@ Contributions are what make the open source community such an amazing place to l
|
||||
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)
|
||||
|
||||
@@ -6,4 +6,6 @@ Since this software is still considered beta/WIP support is always only given fo
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please open a normal public issue if you have any security related concerns.
|
||||
Please open a normal public issue for minor security issues or general security inquires.
|
||||
|
||||
For major or critical security issues, please open a private github security issue.
|
||||
@@ -30,7 +30,7 @@ func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
|
||||
assetIDParam := chi.URLParam(r, "id")
|
||||
assetIDParam = strings.ReplaceAll(assetIDParam, "-", "") // Remove dashes
|
||||
// Convert the asset ID to an int64
|
||||
assetID, err := strconv.ParseInt(assetIDParam, 10, 32)
|
||||
assetID, err := strconv.ParseInt(assetIDParam, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -57,6 +58,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: queryUUIDList(params, "locations"),
|
||||
LabelIDs: queryUUIDList(params, "labels"),
|
||||
NegateLabels: queryBool(params.Get("negateLabels")),
|
||||
ParentItemIDs: queryUUIDList(params, "parentIds"),
|
||||
IncludeArchived: queryBool(params.Get("includeArchived")),
|
||||
Fields: filterFieldItems(params["fields"]),
|
||||
@@ -80,6 +82,14 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GID, extractQuery(r))
|
||||
totalPrice := new(big.Int)
|
||||
for _, item := range items.Items {
|
||||
totalPrice.Add(totalPrice, big.NewInt(int64(item.PurchasePrice*100)))
|
||||
}
|
||||
|
||||
totalPriceFloat := new(big.Float).SetInt(totalPrice)
|
||||
totalPriceFloat.Quo(totalPriceFloat, big.NewFloat(100))
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return server.JSON(w, http.StatusOK, repo.PaginationResult[repo.ItemSummary]{
|
||||
@@ -323,17 +333,17 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
csvData, err := ctrl.svc.Items.ExportTSV(r.Context(), ctx.GID)
|
||||
csvData, err := ctrl.svc.Items.ExportCSV(r.Context(), ctx.GID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to export items")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/tsv")
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=homebox-items.tsv")
|
||||
w.Header().Set("Content-Type", "text/csv")
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=homebox-items.csv")
|
||||
|
||||
writer := csv.NewWriter(w)
|
||||
writer.Comma = '\t'
|
||||
writer.Comma = ','
|
||||
return writer.WriteAll(csvData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -83,6 +85,32 @@ func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
|
||||
return adapters.CommandID("id", fn, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) GetLocationWithPrice(auth context.Context, GID uuid.UUID, ID uuid.UUID) (repo.LocationOut, error) {
|
||||
var location, err = ctrl.repo.Locations.GetOneByGroup(auth, GID, ID)
|
||||
|
||||
// Add direct child items price
|
||||
totalPrice := new(big.Int)
|
||||
items, err := ctrl.repo.Items.QueryByGroup(auth, GID, repo.ItemQuery{LocationIDs: []uuid.UUID{ID}})
|
||||
for _, item := range items.Items {
|
||||
totalPrice.Add(totalPrice, big.NewInt(int64(item.PurchasePrice*100)))
|
||||
}
|
||||
|
||||
totalPriceFloat := new(big.Float).SetInt(totalPrice)
|
||||
totalPriceFloat.Quo(totalPriceFloat, big.NewFloat(100))
|
||||
location.TotalPrice, _ = totalPriceFloat.Float64()
|
||||
|
||||
// Add price from child locations
|
||||
for _, childLocation := range location.Children {
|
||||
var childLocation, err = ctrl.GetLocationWithPrice(auth, GID, childLocation.ID)
|
||||
if err != nil {
|
||||
return repo.LocationOut{}, err
|
||||
}
|
||||
location.TotalPrice += childLocation.TotalPrice
|
||||
}
|
||||
|
||||
return location, err
|
||||
}
|
||||
|
||||
// HandleLocationGet godoc
|
||||
//
|
||||
// @Summary Get Location
|
||||
@@ -95,7 +123,9 @@ func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleLocationGet() errchain.HandlerFunc {
|
||||
fn := func(r *http.Request, ID uuid.UUID) (repo.LocationOut, error) {
|
||||
auth := services.NewContext(r.Context())
|
||||
return ctrl.repo.Locations.GetOneByGroup(auth, auth.GID, ID)
|
||||
var location, err = ctrl.GetLocationWithPrice(auth, auth.GID, ID)
|
||||
|
||||
return location, err
|
||||
}
|
||||
|
||||
return adapters.CommandID("id", fn, http.StatusOK)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HandleBillOfMaterialsExport godoc
|
||||
@@ -18,7 +18,7 @@ func (ctrl *V1Controller) HandleBillOfMaterialsExport() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
actor := services.UseUserCtx(r.Context())
|
||||
|
||||
csv, err := ctrl.svc.Items.ExportBillOfMaterialsTSV(r.Context(), actor.GroupID)
|
||||
csv, err := ctrl.svc.Items.ExportBillOfMaterialsCSV(r.Context(), actor.GroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2469,6 +2469,9 @@
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"totalPrice": {
|
||||
"type": "number"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -2707,6 +2710,9 @@
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalPrice": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2989,4 +2995,4 @@
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
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/gorilla/schema v1.4.1
|
||||
github.com/hay-kot/httpkit v0.0.9
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/olahol/melody v1.1.4
|
||||
@@ -24,7 +24,7 @@ require (
|
||||
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
|
||||
golang.org/x/crypto v0.23.0
|
||||
modernc.org/sqlite v1.29.2
|
||||
)
|
||||
|
||||
@@ -62,12 +62,12 @@ require (
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/yeqown/reedsolomon v1.0.0 // 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
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
ariga.io/atlas v0.19.0 h1:gilVpXabeiGhGI9lj/rQURkXBemnloc41RGOtwVLNc4=
|
||||
ariga.io/atlas v0.19.0/go.mod h1:uj3pm+hUTVN/X5yfdBexHlZv+1Xu5u5ZbZx7+CDavNU=
|
||||
ariga.io/atlas v0.19.1 h1:QzBHkakwzEhmPWOzNhw8Yr/Bbicj6Iq5hwEoNI/Jr9A=
|
||||
ariga.io/atlas v0.19.1/go.mod h1:VPlcXdd4w2KqKnH54yEZcry79UAhpaWaxEsmn5JRNoE=
|
||||
entgo.io/ent v0.12.5 h1:KREM5E4CSoej4zeGa88Ou/gfturAnpUv0mzAjch1sj4=
|
||||
@@ -76,20 +74,14 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM=
|
||||
github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
|
||||
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
|
||||
github.com/hay-kot/httpkit v0.0.6 h1:BidC4UrkS7zRhoTdpKLeF8ODJPKcOZkJ2tk2t2ZIQjQ=
|
||||
github.com/hay-kot/httpkit v0.0.6/go.mod h1:1s/OJwWRyH6tBtTw76jTp6kwBYvjswziXaokPQH7eKQ=
|
||||
github.com/hay-kot/httpkit v0.0.7 h1:KxGi+MwXFavfFUfJEMpye5cnMef9TlFu3v7UZipUB8U=
|
||||
github.com/hay-kot/httpkit v0.0.7/go.mod h1:AD22YluZrvBDxmtB3Pw2SOyp3A2PZqcmBZa0+COrhoU=
|
||||
github.com/hay-kot/httpkit v0.0.8 h1:n+Z5z35YZcdD9cGwbnIPRbrgDw9LY6lqakH4zYr5z+A=
|
||||
github.com/hay-kot/httpkit v0.0.8/go.mod h1:AD22YluZrvBDxmtB3Pw2SOyp3A2PZqcmBZa0+COrhoU=
|
||||
github.com/hay-kot/httpkit v0.0.9 h1:hu2TPY9awmIYWXxWGubaXl2U61pPvaVsm9YwboBRGu0=
|
||||
github.com/hay-kot/httpkit v0.0.9/go.mod h1:AD22YluZrvBDxmtB3Pw2SOyp3A2PZqcmBZa0+COrhoU=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
@@ -169,25 +161,25 @@ github.com/yeqown/reedsolomon v1.0.0 h1:x1h/Ej/uJnNu8jaX7GLHBWmZKCAWjEJTetkqaabr
|
||||
github.com/yeqown/reedsolomon v1.0.0/go.mod h1:P76zpcn2TCuL0ul1Fso373qHRc69LKwAw/Iy6g1WiiM=
|
||||
github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA=
|
||||
github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -17,6 +17,18 @@
|
||||
"symbol": "؋",
|
||||
"name": "Afghan Afghani"
|
||||
},
|
||||
{
|
||||
"code": "XCD",
|
||||
"local": "Eastern Carribean",
|
||||
"symbol": "$",
|
||||
"name": "Eastern Carribean Dollar"
|
||||
},
|
||||
{
|
||||
"code": "XOF",
|
||||
"local": "CFA Franc",
|
||||
"symbol": "CFA",
|
||||
"name": "CFA Franc"
|
||||
},
|
||||
{
|
||||
"code": "ALL",
|
||||
"local": "Albania",
|
||||
@@ -167,12 +179,24 @@
|
||||
"symbol": "FC",
|
||||
"name": "Congolese Franc"
|
||||
},
|
||||
{
|
||||
"code": "XAF",
|
||||
"local": "CFA",
|
||||
"symbol": "FCFA",
|
||||
"name": "CFA Franc BEAC"
|
||||
},
|
||||
{
|
||||
"code": "CHF",
|
||||
"local": "Switzerland",
|
||||
"symbol": "CHF",
|
||||
"name": "Swiss Franc"
|
||||
},
|
||||
{
|
||||
"code": "NZD",
|
||||
"local": "New Zealand",
|
||||
"symbol": "NZ$",
|
||||
"name": "New Zealand Dollar"
|
||||
},
|
||||
{
|
||||
"code": "CLP",
|
||||
"local": "Chile",
|
||||
|
||||
@@ -20,9 +20,9 @@ type BillOfMaterialsEntry struct {
|
||||
TotalPrice float64 `csv:"Total Price"`
|
||||
}
|
||||
|
||||
// BillOfMaterialsTSV returns a byte slice of the Bill of Materials for a given GID in TSV format
|
||||
// BillOfMaterialsCSV returns a byte slice of the Bill of Materials for a given GID in CSV format
|
||||
// See BillOfMaterialsEntry for the format of the output
|
||||
func BillOfMaterialsTSV(entities []repo.ItemOut) ([]byte, error) {
|
||||
func BillOfMaterialsCSV(entities []repo.ItemOut) ([]byte, error) {
|
||||
bomEntries := make([]BillOfMaterialsEntry, len(entities))
|
||||
for i, entity := range entities {
|
||||
bomEntries[i] = BillOfMaterialsEntry{
|
||||
|
||||
@@ -12,7 +12,7 @@ type ExportItemFields struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type ExportTSVRow struct {
|
||||
type ExportCSVRow struct {
|
||||
ImportRef string `csv:"HB.import_ref"`
|
||||
Location LocationString `csv:"HB.location"`
|
||||
LabelStr LabelString `csv:"HB.labels"`
|
||||
|
||||
@@ -19,12 +19,12 @@ import (
|
||||
// items from homebox. It is used to read/write the data from/to a CSV/TSV file given
|
||||
// the standard format of the file.
|
||||
//
|
||||
// See ExportTSVRow for the format of the data in the sheet.
|
||||
// See ExportCSVRow for the format of the data in the sheet.
|
||||
type IOSheet struct {
|
||||
headers []string
|
||||
custom []int
|
||||
index map[string]int
|
||||
Rows []ExportTSVRow
|
||||
Rows []ExportCSVRow
|
||||
}
|
||||
|
||||
func (s *IOSheet) indexHeaders() {
|
||||
@@ -70,16 +70,16 @@ func (s *IOSheet) Read(data io.Reader) error {
|
||||
}
|
||||
|
||||
s.headers = sheet[0]
|
||||
s.Rows = make([]ExportTSVRow, len(sheet)-1)
|
||||
s.Rows = make([]ExportCSVRow, len(sheet)-1)
|
||||
|
||||
for i, row := range sheet[1:] {
|
||||
if len(row) != len(s.headers) {
|
||||
return fmt.Errorf("row has %d columns, expected %d", len(row), len(s.headers))
|
||||
}
|
||||
|
||||
rowData := ExportTSVRow{}
|
||||
rowData := ExportCSVRow{}
|
||||
|
||||
st := reflect.TypeOf(ExportTSVRow{})
|
||||
st := reflect.TypeOf(ExportCSVRow{})
|
||||
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
field := st.Field(i)
|
||||
@@ -154,7 +154,7 @@ func (s *IOSheet) Read(data io.Reader) error {
|
||||
|
||||
// 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))
|
||||
s.Rows = make([]ExportCSVRow, len(items))
|
||||
|
||||
extraHeaders := map[string]struct{}{}
|
||||
|
||||
@@ -189,7 +189,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
|
||||
}
|
||||
}
|
||||
|
||||
s.Rows[i] = ExportTSVRow{
|
||||
s.Rows[i] = ExportCSVRow{
|
||||
// fill struct
|
||||
Location: locString,
|
||||
LabelStr: labelString,
|
||||
@@ -232,7 +232,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
|
||||
|
||||
sort.Strings(customHeaders)
|
||||
|
||||
st := reflect.TypeOf(ExportTSVRow{})
|
||||
st := reflect.TypeOf(ExportCSVRow{})
|
||||
|
||||
// Write headers
|
||||
for i := 0; i < st.NumField(); i++ {
|
||||
@@ -252,8 +252,8 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
|
||||
return nil
|
||||
}
|
||||
|
||||
// TSV writes the current sheet to a writer in TSV format.
|
||||
func (s *IOSheet) TSV() ([][]string, error) {
|
||||
// CSV writes the current sheet to a 2d array, for compatibility with TSV/CSV files.
|
||||
func (s *IOSheet) CSV() ([][]string, error) {
|
||||
memcsv := make([][]string, len(s.Rows)+1)
|
||||
|
||||
memcsv[0] = s.headers
|
||||
|
||||
@@ -27,13 +27,13 @@ func TestSheet_Read(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
want []ExportTSVRow
|
||||
want []ExportCSVRow
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "minimal import",
|
||||
data: minimalImportCSV,
|
||||
want: []ExportTSVRow{
|
||||
want: []ExportCSVRow{
|
||||
{Location: LocationString{"loc"}, Name: "Item 1", Quantity: 1, Description: "Description 1"},
|
||||
{Location: LocationString{"loc"}, Name: "Item 2", Quantity: 2, Description: "Description 2"},
|
||||
{Location: LocationString{"loc"}, Name: "Item 3", Quantity: 3, Description: "Description 3"},
|
||||
@@ -42,7 +42,7 @@ func TestSheet_Read(t *testing.T) {
|
||||
{
|
||||
name: "custom field import",
|
||||
data: customFieldImportCSV,
|
||||
want: []ExportTSVRow{
|
||||
want: []ExportCSVRow{
|
||||
{
|
||||
Location: LocationString{"loc"}, Name: "Item 1", Quantity: 1, Description: "Description 1",
|
||||
Fields: []ExportItemFields{
|
||||
@@ -72,7 +72,7 @@ func TestSheet_Read(t *testing.T) {
|
||||
{
|
||||
name: "custom types import",
|
||||
data: customTypesImportCSV,
|
||||
want: []ExportTSVRow{
|
||||
want: []ExportCSVRow{
|
||||
{
|
||||
Name: "Item 1",
|
||||
AssetID: repo.AssetID(1),
|
||||
|
||||
@@ -329,7 +329,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
|
||||
return finished, nil
|
||||
}
|
||||
|
||||
func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
|
||||
func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
|
||||
items, err := svc.repo.Items.GetAll(ctx, GID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -342,14 +342,14 @@ func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sheet.TSV()
|
||||
return sheet.CSV()
|
||||
}
|
||||
|
||||
func (svc *ItemService) ExportBillOfMaterialsTSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
|
||||
func (svc *ItemService) ExportBillOfMaterialsCSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
|
||||
items, err := svc.repo.Items.GetAll(ctx, GID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reporting.BillOfMaterialsTSV(items)
|
||||
return reporting.BillOfMaterialsCSV(items)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type (
|
||||
AssetID AssetID `json:"assetId"`
|
||||
LocationIDs []uuid.UUID `json:"locationIds"`
|
||||
LabelIDs []uuid.UUID `json:"labelIds"`
|
||||
NegateLabels bool `json:"negateLabels"`
|
||||
ParentItemIDs []uuid.UUID `json:"parentIds"`
|
||||
SortBy string `json:"sortBy"`
|
||||
IncludeArchived bool `json:"includeArchived"`
|
||||
@@ -365,10 +366,17 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite
|
||||
if len(q.LabelIDs) > 0 {
|
||||
labelPredicates := make([]predicate.Item, 0, len(q.LabelIDs))
|
||||
for _, l := range q.LabelIDs {
|
||||
labelPredicates = append(labelPredicates, item.HasLabelWith(label.ID(l)))
|
||||
if !q.NegateLabels {
|
||||
labelPredicates = append(labelPredicates, item.HasLabelWith(label.ID(l)))
|
||||
} else {
|
||||
labelPredicates = append(labelPredicates, item.Not(item.HasLabelWith(label.ID(l))))
|
||||
}
|
||||
}
|
||||
if !q.NegateLabels {
|
||||
andPredicates = append(andPredicates, item.Or(labelPredicates...))
|
||||
} else {
|
||||
andPredicates = append(andPredicates, item.And(labelPredicates...))
|
||||
}
|
||||
|
||||
andPredicates = append(andPredicates, item.Or(labelPredicates...))
|
||||
}
|
||||
|
||||
if len(q.LocationIDs) > 0 {
|
||||
|
||||
@@ -49,6 +49,7 @@ type (
|
||||
Parent *LocationSummary `json:"parent,omitempty"`
|
||||
LocationSummary
|
||||
Children []LocationSummary `json:"children"`
|
||||
TotalPrice float64 `json:"totalPrice"`
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -5,6 +5,17 @@ export default defineConfig({
|
||||
title: "HomeBox",
|
||||
description: "A simple home inventory management software",
|
||||
lastUpdated: true,
|
||||
sitemap: {
|
||||
hostname: 'https://homebox.sysadminsmedia.com',
|
||||
},
|
||||
|
||||
locales: {
|
||||
en: {
|
||||
label: 'English',
|
||||
lang: 'en',
|
||||
}
|
||||
},
|
||||
|
||||
themeConfig: {
|
||||
logo: '/lilbox.svg',
|
||||
|
||||
@@ -16,28 +27,38 @@ export default defineConfig({
|
||||
},
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'API', link: 'https://redocly.github.io/redoc/?url=https://raw.githubusercontent.com/sysadminsmedia/homebox/main/docs/docs/api/openapi-2.0.json' }
|
||||
],
|
||||
|
||||
sidebar: [
|
||||
{
|
||||
text: 'Getting Started',
|
||||
items: [
|
||||
{ text: 'Quick Start', link: '/quick-start' },
|
||||
{ text: 'Tips and Tricks', link: '/tips-tricks' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{ text: 'Import CSV', link: '/import-csv' },
|
||||
{ text: 'Build from Source', link: '/build' }
|
||||
]
|
||||
},
|
||||
],
|
||||
sidebar: {
|
||||
'/en/': [
|
||||
{
|
||||
text: 'Getting Started',
|
||||
items: [
|
||||
{ text: 'Quick Start', link: '/en/quick-start' },
|
||||
{ text: 'Tips and Tricks', link: '/en/tips-tricks' }
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Advanced',
|
||||
items: [
|
||||
{ text: 'Import CSV', link: '/en/import-csv' },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: 'Contributing',
|
||||
items: [
|
||||
{ text: 'Get Started', link: '/en/contribute/get-started' },
|
||||
{ text: 'Bounty Program', link: '/en/contribute/bounty' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/sysadminsmedia/homebox' }
|
||||
{ icon: 'discord', link: 'https://discord.gg/aY4DCkpNA9' },
|
||||
{ icon: 'github', link: 'https://github.com/sysadminsmedia/homebox' },
|
||||
{ icon: 'mastodon', link: 'https://noc.social/@sysadminszone' },
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Building The Binary
|
||||
|
||||
This document describes how to build the project from source code.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
TODO
|
||||
|
||||
## Building
|
||||
|
||||
TODO
|
||||
|
||||
## Running
|
||||
|
||||
TODO
|
||||
@@ -1683,14 +1683,12 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"example": "admin@admin.com",
|
||||
"description": "string",
|
||||
"name": "username",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"example": "admin",
|
||||
"description": "string",
|
||||
"name": "password",
|
||||
"in": "formData"
|
||||
@@ -2469,6 +2467,9 @@
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"totalPrice": {
|
||||
"type": "number"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -2707,6 +2708,9 @@
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalPrice": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2989,4 +2993,4 @@
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
docs/en/contribute/bounty.md
Normal file
19
docs/en/contribute/bounty.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Bounty Program
|
||||
|
||||
## About
|
||||
As part of our commitment to open source, and building an active community around Homebox (and hopefully active pool of developers), we are enabling bounties on issues.
|
||||
|
||||
After digging through several platforms, we ended up settling on [boss.dev](https://www.boss.dev/) as it has some of the lowest fees we could possibly find for any of these platforms other than spinning one up ourselves (which we currently aren't in a position to do).
|
||||
|
||||
While it's not the perfect solution, we think it's about the best one we could find at the moment to lower the rates as much as possible to make sure everyone get's the highest payouts possible. (Some we found were as high as a combined 16%!!!)
|
||||
|
||||
We hope that by enabling bounties on issues, people who have the means and want certain features implemented quicker can now sponsor issues, and in turn everyone contributing code can potentially earn some money for their hard work.
|
||||
|
||||
## Contributor
|
||||
As a contributor wanting to accept money from bounties all you need to do is simply register for an account via GitHub, and attach a bank account (or debit card in the USA).
|
||||
|
||||
## Sponsor
|
||||
Sign in with a GitHub account, and then attach a credit card to your account.
|
||||
|
||||
## Commands to use boss.dev
|
||||
There is documentation on their website regarding commands that you can put in comments to use the bounty system. [boss.dev Documentation](https://www.boss.dev/doc)
|
||||
69
docs/en/contribute/get-started.md
Normal file
69
docs/en/contribute/get-started.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Getting Started With Contributing
|
||||
|
||||
## Get Started
|
||||
|
||||
### 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 need to ensure that you have the following tools installed:
|
||||
|
||||
- [Go 1.19+](https://golang.org/doc/install)
|
||||
- [Swaggo](https://github.com/swaggo/swag)
|
||||
- [Node.js 16+](https://nodejs.org/en/download/)
|
||||
- [pnpm](https://pnpm.io/installation)
|
||||
- [Taskfile](https://taskfile.dev/#/installation) (Optional but recommended)
|
||||
- For code generation, you'll need to have `python3` available on your path. In most cases, this is already installed and available.
|
||||
|
||||
If you're using `taskfile` you can run `task --list-all` for a list of all commands and their descriptions.
|
||||
|
||||
### 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.
|
||||
|
||||
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.
|
||||
|
||||
test command `task go:test`
|
||||
|
||||
lint command `task go:lint`
|
||||
|
||||
swagger update command `task swag`
|
||||
|
||||
### 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.
|
||||
|
||||
fix/lint code `task ui:fix`
|
||||
|
||||
type checking `task ui:check`
|
||||
|
||||
## Documentation
|
||||
We use [Vitepress](https://vitepress.dev/) for the web documentation of homebox. Anyone is welcome to contribute the documentation if they wish.
|
||||
For documentation contributions you only need NodeJS and PNPM.
|
||||
|
||||
::: info Notes
|
||||
- Languages are seperated by folder (e.g `/en`, `/fr`, etc.)
|
||||
- The Sidebar must be updated on a per language basis
|
||||
- Each languages files can be named independently (slugs can match the language)
|
||||
- The `public/_redirects` file is used to redirect the default to english
|
||||
- Redirects can also be configured per language by adding `Language=` after the redirect code
|
||||
:::
|
||||
|
||||
## Branch Flow
|
||||
We use the `main` branch as the development branch. All PRs should be made to the `main` branch form a feature branch.
|
||||
To create a pull request you can use the following steps:
|
||||
|
||||
1. Fork the repo and create a new branch from `main`
|
||||
2. If you added code that should be tested, add tests
|
||||
3. If you've changed APIs update the documentation
|
||||
4. Ensure that the test suite and linters pass
|
||||
5. Create your PR
|
||||
|
||||
@@ -11,10 +11,10 @@ hero:
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Quick Start
|
||||
link: /quick-start
|
||||
link: /en/quick-start
|
||||
- theme: alt
|
||||
text: Tips and Tricks
|
||||
link: /tips-tricks
|
||||
link: /en/tips-tricks
|
||||
|
||||
features:
|
||||
- title: Add/Update/Delete Items
|
||||
@@ -24,7 +24,7 @@ features:
|
||||
- title: CSV Import/Export
|
||||
details: Import a CSV file to quickly get started with existing information, or export to save information
|
||||
- title: Custom Report
|
||||
details: Export bill of mertials, or generate QR codes for items
|
||||
details: Export bill of materials, or generate QR codes for items
|
||||
- title: Custom labeling and locations
|
||||
details: Use custom labels and locations to organize items
|
||||
- title: Multi-Tenant Support
|
||||
@@ -52,4 +52,4 @@ That's a fair point. If your needs can be fulfilled by a Spreadsheet, I'd sugges
|
||||
|
||||
### Snipe-It?
|
||||
|
||||
Snipe-It is the gold standard for IT management. If your use-case is to manage consumables and IT physical infrastructure, I highly suggest you look at Snipe-It over Homebox, it's just more purpose built for that use case. Homebox is, in contrast, purpose built for the home user, which means that we try to focus on keeping things simple and easy to use. Lowering the friction for creating items and managing them is a key goal of Homebox which means you lose out on some of the more advanced features. In most cases, this is a good trade-off.
|
||||
Snipe-It is the gold standard for IT management. If your use-case is to manage consumables and IT physical infrastructure, I highly suggest you look at Snipe-It over Homebox, it's just more purpose built for that use case. Homebox is, in contrast, purpose built for the home user, which means that we try to focus on keeping things simple and easy to use. Lowering the friction for creating items and managing them is a key goal of Homebox which means you lose out on some of the more advanced features. In most cases, this is a good trade-off.
|
||||
@@ -46,11 +46,14 @@ volumes:
|
||||
homebox-data:
|
||||
driver: local
|
||||
```
|
||||
|
||||
::: info
|
||||
If you use the `rootless` image, and instead of using named volumes you would prefer using a hostMount directly (e.g., `volumes: [ /path/to/data/folder:/data ]`) you need to `chown` the chosen directory in advance to the `65532` user (as shown in the Docker example above).
|
||||
:::
|
||||
|
||||
::: warning
|
||||
If you have previously set up docker compose with the `HBOX_WEB_READ_TIMEOUT`, `HBOX_WEB_WRITE_TIMEOUT`, or `HBOX_IDLE_TIMEOUT` options, and you were previously using the hay-kot image, please note that you will have to add an `s` for seconds or `m` for minutes to the end of the integers. A dependency update removed the defaultation to seconds and it now requires an explicit duration time.
|
||||
:::
|
||||
|
||||
## Env Variables & Configuration
|
||||
|
||||
| Variable | Default | Description |
|
||||
@@ -59,15 +62,15 @@ If you use the `rootless` image, and instead of using named volumes you would pr
|
||||
| HBOX_WEB_PORT | 7745 | port to run the web server on, if you're using docker do not change this |
|
||||
| HBOX_WEB_HOST | | host to run the web server on, if you're using docker do not change this |
|
||||
| HBOX_OPTIONS_ALLOW_REGISTRATION | true | allow users to register themselves |
|
||||
| HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID | true | auto increments the asset_id field for new items |
|
||||
| HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID | true | auto-increments the asset_id field for new items |
|
||||
| HBOX_OPTIONS_CURRENCY_CONFIG | | json configuration file containing additional currencie |
|
||||
| HBOX_WEB_MAX_UPLOAD_SIZE | 10 | maximum file upload size supported in MB |
|
||||
| HBOX_WEB_READ_TIMEOUT | 10 | Read timeout of HTTP sever |
|
||||
| HBOX_WEB_WRITE_TIMEOUT | 10 | Write timeout of HTTP server |
|
||||
| HBOX_WEB_IDLE_TIMEOUT | 30 | Idle timeout of HTTP server |
|
||||
| HBOX_WEB_READ_TIMEOUT | 10s | Read timeout of HTTP sever |
|
||||
| HBOX_WEB_WRITE_TIMEOUT | 10s | Write timeout of HTTP server |
|
||||
| HBOX_WEB_IDLE_TIMEOUT | 30s | Idle timeout of HTTP server |
|
||||
| HBOX_STORAGE_DATA | /data/ | path to the data directory, do not change this if you're using docker |
|
||||
| HBOX_STORAGE_SQLITE_URL | /data/homebox.db?_fk=1 | sqlite database url, if you're using docker do not change this |
|
||||
| HBOX_LOG_LEVEL | info | log level to use, can be one of: trace, debug, info, warn, error, critical |
|
||||
| HBOX_LOG_LEVEL | info | log level to use, can be one of trace, debug, info, warn, error, critical |
|
||||
| HBOX_LOG_FORMAT | text | log format to use, can be one of: text, json |
|
||||
| HBOX_MAILER_HOST | | email host to use, if not set no email provider will be used |
|
||||
| HBOX_MAILER_PORT | 587 | email port to use |
|
||||
@@ -17,7 +17,7 @@ Homebox Custom Fields also have special support for URLs. Provide a URL (`https:
|
||||
|
||||
## Managing Asset IDs
|
||||
|
||||
Homebox provides the option to auto-set asset IDs, this is the default behavior. These can be used for tracking assets with printable tags or labels. You can disable this behavior via a command line flag or ENV variable. See [configuration](/quick-start#env-variables-configuration) for more details.
|
||||
Homebox provides the option to auto-set asset IDs, this is the default behavior. These can be used for tracking assets with printable tags or labels. You can disable this behavior via a command line flag or ENV variable. See [configuration](/en/quick-start.md#env-variables-configuration) for more details.
|
||||
|
||||
Example ID: `000-001`
|
||||
|
||||
4
docs/public/_redirects
Normal file
4
docs/public/_redirects
Normal file
@@ -0,0 +1,4 @@
|
||||
/ /en/ 302
|
||||
|
||||
# This is an example for a french redirect
|
||||
# /* /fr/:splat 302 Language=fr
|
||||
44
fly.toml
44
fly.toml
@@ -1,44 +0,0 @@
|
||||
# fly.toml file generated for homebox on 2022-09-08T16:00:08-08:00
|
||||
|
||||
app = "homebox"
|
||||
kill_signal = "SIGINT"
|
||||
kill_timeout = 5
|
||||
processes = []
|
||||
|
||||
[build.args]
|
||||
COMMIT = "HEAD"
|
||||
VERSION = "nightly"
|
||||
|
||||
[env]
|
||||
PORT = "7745"
|
||||
HBOX_DEMO = "true"
|
||||
|
||||
[experimental]
|
||||
allowed_public_ports = []
|
||||
auto_rollback = true
|
||||
|
||||
[[services]]
|
||||
http_checks = []
|
||||
internal_port = 7745
|
||||
processes = ["app"]
|
||||
protocol = "tcp"
|
||||
script_checks = []
|
||||
[services.concurrency]
|
||||
hard_limit = 25
|
||||
soft_limit = 20
|
||||
type = "connections"
|
||||
|
||||
[[services.ports]]
|
||||
force_https = true
|
||||
handlers = ["http"]
|
||||
port = 80
|
||||
|
||||
[[services.ports]]
|
||||
handlers = ["tls", "http"]
|
||||
port = 443
|
||||
|
||||
[[services.tcp_checks]]
|
||||
grace_period = "1s"
|
||||
interval = "15s"
|
||||
restart_limit = 0
|
||||
timeout = "2s"
|
||||
@@ -6,6 +6,10 @@
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* transparent subtle scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 0.2em;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
/>
|
||||
<Currency v-else-if="detail.type == 'currency'" :amount="detail.text" />
|
||||
<template v-else-if="detail.type === 'link'">
|
||||
<div class="tooltip tooltip-primary tooltip-right" :data-tip="detail.href">
|
||||
<div class="tooltip tooltip-primary tooltip-top" :data-tip="detail.href">
|
||||
<a class="btn btn-primary btn-xs" :href="detail.href" target="_blank">
|
||||
<MdiOpenInNew class="mr-2 swap-on" />
|
||||
{{ detail.text }}
|
||||
|
||||
@@ -26,3 +26,9 @@
|
||||
subtitle: undefined,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
[data-theme="homebox"] .stat-title {
|
||||
color: hsl(0 0% 90/0.6);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,6 +23,7 @@ export type ItemsQuery = {
|
||||
pageSize?: number;
|
||||
locations?: string[];
|
||||
labels?: string[];
|
||||
negateLabels?: boolean;
|
||||
parentIds?: string[];
|
||||
q?: string;
|
||||
fields?: string[];
|
||||
|
||||
@@ -232,6 +232,7 @@ export interface LocationOut {
|
||||
id: string;
|
||||
name: string;
|
||||
parent: LocationSummary;
|
||||
totalPrice: number;
|
||||
updatedAt: Date | string;
|
||||
}
|
||||
|
||||
@@ -329,6 +330,7 @@ export interface PaginationResultItemSummary {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
totalPrice: number;
|
||||
}
|
||||
|
||||
export interface TotalsByOrganizer {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const ctx = useAuthContext();
|
||||
const api = useUserApi();
|
||||
const redirectTo = useState("authRedirect");
|
||||
|
||||
if (!ctx.isAuthorized()) {
|
||||
if (window.location.pathname !== "/") {
|
||||
console.debug("[middleware/auth] isAuthorized returned false, redirecting to /");
|
||||
redirectTo.value = window.location.pathname;
|
||||
return navigateTo("/");
|
||||
}
|
||||
}
|
||||
@@ -15,6 +17,7 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
if (error) {
|
||||
if (window.location.pathname !== "/") {
|
||||
console.debug("[middleware/user] user is null and fetch failed, redirecting to /");
|
||||
redirectTo.value = window.location.pathname;
|
||||
return navigateTo("/");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
|
||||
const loading = ref(false);
|
||||
const loginPassword = ref("");
|
||||
const redirectTo = useState("authRedirect");
|
||||
|
||||
async function login() {
|
||||
loading.value = true;
|
||||
@@ -116,7 +117,8 @@
|
||||
|
||||
toast.success("Logged in successfully");
|
||||
|
||||
navigateTo("/home");
|
||||
navigateTo(redirectTo.value || "/home");
|
||||
redirectTo.value = null;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
const advanced = useRouteQuery("advanced", false);
|
||||
const includeArchived = useRouteQuery("archived", false);
|
||||
const fieldSelector = useRouteQuery("fieldSelector", false);
|
||||
const negateLabels = useRouteQuery("negateLabels", false);
|
||||
|
||||
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
|
||||
const hasNext = computed(() => page.value * pageSize.value < total.value);
|
||||
@@ -162,6 +163,12 @@
|
||||
}
|
||||
});
|
||||
|
||||
watch(negateLabels, (newV, oldV) => {
|
||||
if (newV !== oldV) {
|
||||
search();
|
||||
}
|
||||
});
|
||||
|
||||
async function fetchValues(field: string): Promise<string[]> {
|
||||
if (fieldValuesCache.value[field]) {
|
||||
return fieldValuesCache.value[field];
|
||||
@@ -193,6 +200,7 @@
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
includeArchived: includeArchived.value ? "true" : "false",
|
||||
negateLabels: negateLabels.value ? "true" : "false",
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -219,6 +227,7 @@
|
||||
q: query.value || "",
|
||||
locations: locIDs.value,
|
||||
labels: labIDs.value,
|
||||
negateLabels: negateLabels.value,
|
||||
includeArchived: includeArchived.value,
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
@@ -268,6 +277,7 @@
|
||||
advanced: "true",
|
||||
archived: includeArchived.value ? "true" : "false",
|
||||
fieldSelector: fieldSelector.value ? "true" : "false",
|
||||
negateLabels: negateLabels.value ? "true" : "false",
|
||||
pageSize: pageSize.value,
|
||||
page: page.value,
|
||||
q: query.value,
|
||||
@@ -359,6 +369,10 @@
|
||||
<input v-model="fieldSelector" type="checkbox" class="toggle toggle-sm toggle-primary" />
|
||||
<span class="label-text ml-4"> Field Selector </span>
|
||||
</label>
|
||||
<label class="label cursor-pointer mr-auto">
|
||||
<input v-model="negateLabels" type="checkbox" class="toggle toggle-sm toggle-primary" />
|
||||
<span class="label-text ml-4"> Negate selected labels </span>
|
||||
</label>
|
||||
<hr class="my-2" />
|
||||
<BaseButton class="btn-block btn-sm" @click="reset"> Reset Search</BaseButton>
|
||||
</div>
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
return [];
|
||||
}
|
||||
|
||||
return resp.data.items;
|
||||
return resp.data;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -115,8 +115,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl pb-1">
|
||||
<h1 class="text-2xl pb-1 flex items-center gap-3">
|
||||
{{ label ? label.name : "" }}
|
||||
|
||||
<div
|
||||
v-if="items && items.totalPrice"
|
||||
class="text-xs bg-secondary text-secondary-content rounded-full px-2 py-1"
|
||||
>
|
||||
<div>
|
||||
<Currency :amount="items.totalPrice" />
|
||||
</div>
|
||||
</div>
|
||||
</h1>
|
||||
<div class="flex gap-1 flex-wrap text-xs">
|
||||
<div>
|
||||
@@ -144,7 +153,7 @@
|
||||
<Markdown v-if="label && label.description" class="text-base" :source="label.description"> </Markdown>
|
||||
</div>
|
||||
<section v-if="label && items">
|
||||
<ItemViewSelectable :items="items" />
|
||||
<ItemViewSelectable :items="items.items" />
|
||||
</section>
|
||||
</BaseContainer>
|
||||
</BaseContainer>
|
||||
|
||||
@@ -138,8 +138,17 @@
|
||||
<li>{{ location.name }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h1 class="text-2xl pb-1">
|
||||
<h1 class="text-2xl pb-1 flex items-center gap-3">
|
||||
{{ location ? location.name : "" }}
|
||||
|
||||
<div
|
||||
v-if="location && location.totalPrice"
|
||||
class="text-xs bg-secondary text-secondary-content rounded-full px-2 py-1"
|
||||
>
|
||||
<div>
|
||||
<Currency :amount="location.totalPrice" />
|
||||
</div>
|
||||
</div>
|
||||
</h1>
|
||||
<div class="flex gap-1 flex-wrap text-xs">
|
||||
<div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</DetailAction>
|
||||
<DetailAction @action="getBillOfMaterials()">
|
||||
<template #title>Bill of Materials</template>
|
||||
Generates a TSV (Tab Separated Values) file that can be imported into a spreadsheet program. This is a
|
||||
Generates a CSV (Comma Separated Values) file that can be imported into a spreadsheet program. This is a
|
||||
summary of your inventory with basic item and pricing information.
|
||||
<template #button> Generate BOM </template>
|
||||
</DetailAction>
|
||||
@@ -45,7 +45,7 @@
|
||||
Imports the standard CSV format for Homebox. This will <b>not</b> overwrite any existing items in your
|
||||
inventory. It will only add new items.
|
||||
</DetailAction>
|
||||
<DetailAction @action="getExportTSV()">
|
||||
<DetailAction @action="getExportCSV()">
|
||||
<template #title>Export Inventory</template>
|
||||
Exports the standard CSV format for Homebox. This will export all items in your inventory.
|
||||
</DetailAction>
|
||||
@@ -78,7 +78,7 @@
|
||||
Resets the time value for all date time fields in your inventory to the beginning of the date. This is to
|
||||
fix a bug that was introduced early on in the development of the site that caused the time value to be
|
||||
stored with the time which caused issues with date fields displaying accurate values.
|
||||
<a class="link" href="https://github.com/sysadminsmedia/homebox/issues/236" target="_blank">
|
||||
<a class="link" href="https://github.com/hay-kot/homebox/issues/236" target="_blank">
|
||||
See Github Issue #236 for more details.
|
||||
</a>
|
||||
</DetailAction>
|
||||
@@ -86,7 +86,7 @@
|
||||
<template #title> Set Primary Photos </template>
|
||||
In version v0.10.0 of Homebox, the primary image field was added to attachments of type photo. This action
|
||||
will set the primary image field to the first image in the attachments array in the database, if it is not
|
||||
already set. <a class="link" href="https://github.com/sysadminsmedia/homebox/pull/576">See GitHub PR #576</a>
|
||||
already set. <a class="link" href="https://github.com/hay-kot/homebox/pull/576">See GitHub PR #576</a>
|
||||
</DetailAction>
|
||||
</div>
|
||||
</BaseCard>
|
||||
@@ -123,7 +123,7 @@
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
|
||||
function getExportTSV() {
|
||||
function getExportCSV() {
|
||||
const url = api.items.exportURL();
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
|
||||
3206
frontend/pnpm-lock.yaml
generated
3206
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -14,5 +14,6 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.2.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.1.4+sha512.9df9cf27c91715646c7d675d1c9c8e41f6fce88246f1318c1aa6a1ed1aeb3c4f032fcdf4ba63cc69c4fe6d634279176b5358727d8f2cc1e65b65f43ce2f8bfb0"
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user