Compare commits

..

11 commits

Author SHA1 Message Date
Hayden
e96da19d2b fix upload size 2022-10-06 15:24:14 -05:00
Hayden
68a27b7f82 rename repository factory 2022-10-06 15:02:15 -05:00
Hayden
1827c61314 remove console.logs 2022-10-03 21:45:12 -08:00
Hayden
76c6d25850 implement UI for url and join 2022-10-03 21:36:51 -08:00
Hayden
505099ee26 api refactor / registration tests 2022-10-03 20:09:53 -08:00
Hayden
a67316a889 fix types 2022-10-03 20:09:26 -08:00
Hayden
1143246816 refactor server to support variable options 2022-10-03 15:43:36 -08:00
Hayden
245591cb23 (wip) init token APIs 2022-10-01 14:02:14 -08:00
Hayden
c4916ea762 basic layout 2022-10-01 12:59:18 -08:00
Hayden
d6c1b789cf lowercase buttons by default 2022-10-01 12:57:51 -08:00
Hayden
72324b8439 add user profiles and theme selectors 2022-10-01 12:13:53 -08:00
502 changed files with 31301 additions and 68900 deletions

View file

@ -1,5 +0,0 @@
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
ARG VARIANT=16-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT}
RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin

View file

@ -1,40 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/typescript-node
{
"name": "Node.js & TypeScript",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local on arm64/Apple Silicon.
"args": {
"VARIANT": "18-bullseye"
}
},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"dbaeumer.vscode-eslint"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
7745,
3000
],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "go install github.com/go-task/task/v3/cmd/task@latest && npm install -g pnpm && task setup",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
"features": {
"golang": "1.21"
}
}

View file

@ -22,4 +22,3 @@
**/secrets.dev.yaml **/secrets.dev.yaml
**/values.dev.yaml **/values.dev.yaml
README.md README.md
!Dockerfile.rootless

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
github: [hay-kot]

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View file

@ -1,61 +0,0 @@
---
name: "Bug Report"
description: "Submit a bug report for the current release"
labels: ["bug"]
body:
- type: checkboxes
id: checks
attributes:
label: First Check
description: Please confirm and check all the following options.
options:
- label: This is not a feature request
required: true
- label: I added a very descriptive title to this issue.
required: true
- label: I used the GitHub search to find a similar issue and didn't find it.
required: true
- label: I searched the documentation, with the integrated search.
required: true
- label: I already read the docs and didn't find an answer.
required: true
- type: input
id: homebox-version
attributes:
label: Homebox Version
validations:
required: true
- type: textarea
id: description
attributes:
label: What is the issue you are experiencing?
placeholder: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: reproduction
attributes:
description: If you do not provide a way to reproduce the issue, your issue will likely be closed.
label: How can the maintainer reproduce the issue?
placeholder: A clear step-by-step guide on how to reproduce the issue.
validations:
required: true
- type: dropdown
id: os
attributes:
label: Deployment
description: What Deployment system are you using?
multiple: true
options:
- Docker (Linux)
- Docker (Windows)
- Docker (Synology)
- Unraid
- Other
validations:
required: true
- type: textarea
id: os-details
attributes:
label: Deployment Details
description: You can add more details about your operating system here, in particular if you chose "Other". If you are experiencing issues with deployment, please provide your docker-compose or docker commands

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -1,37 +0,0 @@
---
name: "Feature Request"
description: "Submit a feature request for the current release"
labels: ["feature-request"]
body:
- type: textarea
id: problem-statement
attributes:
label: What is the problem you are trying to solve with this feature?
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
- type: textarea
id: feature-solution
attributes:
label: What is the solution you are proposing?
placeholder: A clear and concise description of what you want to happen.
- type: textarea
id: feature-alternatives
attributes:
label: What alternatives have you considered?
placeholder: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: feature-details
attributes:
label: Additional context
placeholder: Add any other context or screenshots about the feature request here.
- type: checkboxes
id: checks
attributes:
label: Contributions
description: Please confirm the following
options:
- label: I have searched through existing issues and feature requests to see if my idea has already been proposed.
required: true
- label: If this feature is accepted, I would be willing to help implement and maintain this feature.
required: false
- label: If this feature is accepted, I'm willing to sponsor the development of this feature.
required: false

31
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,31 @@
version: 2
updates:
# Fetch and update latest `npm` packages
- package-ecosystem: npm
directory: "/frontend"
schedule:
interval: daily
time: "00:00"
open-pull-requests-limit: 10
reviewers:
- hay-kot
assignees:
- hay-kot
commit-message:
prefix: fix
prefix-development: chore
include: scope
- package-ecosystem: gomod
directory: backend
schedule:
interval: daily
time: "00:00"
open-pull-requests-limit: 10
reviewers:
- hay-kot
assignees:
- hay-kot
commit-message:
prefix: fix
prefix-development: chore
include: scope

View file

@ -7,20 +7,18 @@ jobs:
Go: Go:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v2
with: with:
go-version: "1.21" go-version: 1.19
- name: Install Task - name: Install Task
uses: arduino/setup-task@v1 uses: arduino/setup-task@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v4 uses: golangci/golangci-lint-action@v3
with: with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version # 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 version: latest
@ -30,7 +28,7 @@ jobs:
args: --timeout=6m args: --timeout=6m
- name: Build API - name: Build API
run: task go:build run: task api:build
- name: Test - name: Test
run: task go:coverage run: task api:coverage

View file

@ -4,55 +4,27 @@ on:
workflow_call: workflow_call:
jobs: jobs:
lint: Frontend:
name: Lint
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: pnpm/action-setup@v3.0.0
with:
version: 6.0.2
- name: Install dependencies
run: pnpm install --shamefully-hoist
working-directory: frontend
- name: Run Lint
run: pnpm run lint:ci
working-directory: frontend
- name: Run Typecheck
run: pnpm run typecheck
working-directory: frontend
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Task - name: Install Task
uses: arduino/setup-task@v1 uses: arduino/setup-task@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v2
with: with:
go-version: "1.21" go-version: 1.19
- uses: actions/setup-node@v4 - uses: actions/setup-node@v3
with: with:
node-version: 18 node-version: 18
- uses: pnpm/action-setup@v3.0.0 - uses: pnpm/action-setup@v2.2.2
with: with:
version: 6.0.2 version: 6.0.2
@ -60,5 +32,9 @@ jobs:
run: pnpm install run: pnpm install
working-directory: frontend working-directory: frontend
- name: Run linter 👀
run: pnpm lint
working-directory: "frontend"
- name: Run Integration Tests - name: Run Integration Tests
run: task test:ci run: task test:ci

View file

@ -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/hay-kot/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/hay-kot/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 }} \
--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/hay-kot/homebox:nightly-rootless \
--tag ghcr.io/hay-kot/homebox:latest-rootless \
--tag ghcr.io/hay-kot/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 .

View file

@ -1,4 +1,4 @@
name: Publish Dockers name: Build Nightly
on: on:
push: push:
@ -9,21 +9,76 @@ env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
jobs: jobs:
backend-tests:
name: "Backend Server Tests"
uses: hay-kot/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
deploy: deploy:
name: "Deploy Nightly to Fly.io" name: "Deploy Nightly to Fly.io"
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
- backend-tests
- frontend-tests
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- uses: superfly/flyctl-actions/setup-flyctl@master - uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only - run: flyctl deploy --remote-only
publish-nightly: publish:
name: "Publish Nightly" name: "Publish Nightly"
if: github.event_name != 'release' needs:
uses: hay-kot/homebox/.github/workflows/partial-publish.yaml@main - backend-tests
with: - frontend-tests
tag: nightly runs-on: ubuntu-latest
secrets: steps:
GH_TOKEN: ${{ secrets.CR_PAT }} - uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.19
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: install buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
install: true
- name: login to container registry
run: docker login ghcr.io --username hay-kot --password $CR_PAT
env:
CR_PAT: ${{ secrets.CR_PAT }}
- name: build the image
run: |
docker build --push \
--tag ghcr.io/hay-kot/homebox:nightly \
--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 .
deploy-docs:
name: Deploy docs
needs:
- publish
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v2
- 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

View file

@ -8,8 +8,8 @@ on:
jobs: jobs:
backend-tests: backend-tests:
name: "Backend Server Tests" name: "Backend Server Tests"
uses: ./.github/workflows/partial-backend.yaml uses: hay-kot/homebox/.github/workflows/partial-backend.yaml@main
frontend-tests: frontend-tests:
name: "Frontend and End-to-End Tests" name: "Frontend and End-to-End Tests"
uses: ./.github/workflows/partial-frontend.yaml uses: hay-kot/homebox/.github/workflows/partial-frontend.yaml@main

View file

@ -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: hay-kot/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
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: hay-kot/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

12
.gitignore vendored
View file

@ -3,6 +3,7 @@ backend/.data/*
config.yml config.yml
homebox.db homebox.db
.idea .idea
.DS_Store .DS_Store
test-mailer.json test-mailer.json
node_modules node_modules
@ -31,7 +32,6 @@ node_modules
go.work go.work
.task/ .task/
backend/.env backend/.env
build/*
# Output Directory for Nuxt/Frontend during build step # Output Directory for Nuxt/Frontend during build step
backend/app/api/public/* backend/app/api/public/*
@ -45,13 +45,3 @@ node_modules
.output .output
.env .env
dist dist
.pnpm-store
backend/app/api/app
backend/app/api/__debug_bin
dist/
# Nuxt Publish Dir
backend/app/api/static/public/*
!backend/app/api/static/public/.gitkeep
backend/api

View file

@ -1,33 +0,0 @@
---
# yaml-language-server: $schema=https://hay-kot.github.io/scaffold/schema.json
messages:
pre: |
# Ent Model Generation
With Boilerplate!
post: |
Complete!
questions:
- name: "model"
prompt:
message: "What is the name of the model? (PascalCase)"
required: true
- name: "by_group"
prompt:
confirm: "Include a Group Edge? (group_id -> id)"
required: true
rewrites:
- from: 'templates/model.go'
to: 'backend/internal/data/ent/schema/{{ lower .Scaffold.model }}.go'
inject:
- name: "Insert Groups Edge"
path: 'backend/internal/data/ent/schema/group.go'
at: // $scaffold_edge
template: |
{{- if .Scaffold.by_group -}}
owned("{{ lower .Scaffold.model }}s", {{ .Scaffold.model }}.Type),
{{- end -}}

View file

@ -1,40 +0,0 @@
package schema
import (
"entgo.io/ent"
"github.com/hay-kot/homebox/backend/internal/data/ent/schema/mixins"
)
type {{ .Scaffold.model }} struct {
ent.Schema
}
func ({{ .Scaffold.model }}) Mixin() []ent.Mixin {
return []ent.Mixin{
mixins.BaseMixin{},
{{- if .Scaffold.by_group }}
GroupMixin{ref: "{{ snakecase .Scaffold.model }}s"},
{{- end }}
}
}
// Fields of the {{ .Scaffold.model }}.
func ({{ .Scaffold.model }}) Fields() []ent.Field {
return []ent.Field{
// field.String("name").
}
}
// Edges of the {{ .Scaffold.model }}.
func ({{ .Scaffold.model }}) Edges() []ent.Edge {
return []ent.Edge{
// edge.From("group", Group.Type).
}
}
func ({{ .Scaffold.model }}) Indexes() []ent.Index {
return []ent.Index{
// index.Fields("token"),
}
}

47
.vscode/launch.json vendored
View file

@ -1,47 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"compounds": [
{
"name": "Full Stack",
"configurations": ["Launch Backend", "Launch Frontend"],
"stopAll": true
}
],
"configurations": [
{
"name": "Launch Backend",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceRoot}/backend/app/api/",
"args": [],
"env": {
"HBOX_DEMO": "true",
"HBOX_LOG_LEVEL": "debug",
"HBOX_DEBUG_ENABLED": "true",
"HBOX_STORAGE_DATA": "${workspaceRoot}/backend/.data",
"HBOX_STORAGE_SQLITE_URL": "${workspaceRoot}/backend/.data/homebox.db?_fk=1"
},
},
{
"name": "Launch Frontend",
"type": "node",
"request": "launch",
"runtimeExecutable": "pnpm",
"runtimeArgs": [
"run",
"dev"
],
"cwd": "${workspaceFolder}/frontend",
"serverReadyAction": {
"action": "debugWithChrome",
"pattern": "Local: http://localhost:([0-9]+)",
"uriFormat": "http://localhost:%s",
"webRoot": "${workspaceFolder}/frontend"
}
}
]
}

28
.vscode/settings.json vendored
View file

@ -1,4 +1,7 @@
{ {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"yaml.schemas": { "yaml.schemas": {
"https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml" "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
}, },
@ -7,28 +10,5 @@
"package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig, pnpm-lock.yaml, postcss.config.js, tailwind.config.js", "package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig, pnpm-lock.yaml, postcss.config.js, tailwind.config.js",
"docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml", "docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml",
"README.md": "LICENSE, SECURITY.md" "README.md": "LICENSE, SECURITY.md"
}, }
"cSpell.words": [
"debughandlers",
"Homebox"
],
// use ESLint to format code on save
"editor.formatOnSave": false,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"eslint.format.enable": true,
"css.validate": false,
"tailwindCSS.includeLanguages": {
"vue": "html",
"vue-html": "html"
},
"editor.quickSuggestions": {
"strings": true
},
"tailwindCSS.experimental.configFile": "./frontend/tailwind.config.js"
} }

View file

@ -1,57 +0,0 @@
# Contributing
## We Develop with GitHub
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:
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 APIs, update the documentation.
4. Ensure that the test suite and linters pass
5. Issue your pull request
## How To 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.
### 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.
## Publishing Release
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

View file

@ -1,48 +1,48 @@
# Build Nuxt # Build Nuxt
FROM r.batts.cloud/nodejs:18 as frontend-builder FROM node:17-alpine as frontend-builder
WORKDIR /app WORKDIR /app
RUN npm install -g pnpm@latest-9 RUN npm install -g pnpm
COPY frontend/package.json frontend/pnpm-lock.yaml ./ COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --shamefully-hoist RUN pnpm install --frozen-lockfile --shamefully-hoist
COPY frontend . COPY frontend .
RUN pnpm build RUN pnpm build
# Build API # Build API
FROM r.batts.cloud/golang:1.24 AS builder FROM golang:alpine AS builder
ARG BUILD_TIME ARG BUILD_TIME
ARG COMMIT ARG COMMIT
ARG VERSION RUN apk update && \
RUN apt update && \ apk upgrade && \
apt install -y git build-essential gcc g++ apk add --update git build-base gcc g++
WORKDIR /go/src/app WORKDIR /go/src/app
COPY ./backend . COPY ./backend .
RUN go get -d -v ./... RUN go get -d -v ./...
RUN rm -rf ./app/api/public RUN rm -rf ./app/api/public
COPY --from=frontend-builder /app/.output/public ./app/api/static/public COPY --from=frontend-builder /app/.output/public ./app/api/public
RUN CGO_ENABLED=0 GOOS=linux go build \ RUN CGO_ENABLED=1 GOOS=linux go build \
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \ -ldflags "-s -w -X main.Commit=$COMMIT -X main.BuildTime=$BUILD_TIME" \
-o /go/bin/api \ -o /go/bin/api \
-v ./app/api/*.go -v ./app/api/*.go
# Production Stage # Production Stage
FROM r.batts.cloud/debian:trixie FROM alpine:latest
ENV HBOX_MODE=production ENV HBOX_MODE=production
ENV HBOX_STORAGE_DATA=/data/ ENV HBOX_STORAGE_DATA=/data/
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_pragma=busy_timeout=2000&_pragma=journal_mode=WAL&_fk=1 ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1
RUN mkdir /app RUN apk --no-cache add ca-certificates
COPY --from=builder /go/bin/api /app RUN mkdir /app
COPY --from=builder /go/bin/api /app
RUN chmod +x /app/api
RUN chmod +x /app/api
LABEL Name=homebox Version=0.0.1
LABEL org.opencontainers.image.source="https://github.com/hay-kot/homebox" LABEL Name=homebox Version=0.0.1
EXPOSE 7745 EXPOSE 7745
WORKDIR /app WORKDIR /app
VOLUME [ "/data" ] VOLUME [ "/data" ]
ENTRYPOINT [ "/app/api" ] ENTRYPOINT [ "/app/api" ]
CMD [ "/data/config.yml" ] CMD [ "/data/config.yml" ]

View file

@ -1,53 +0,0 @@
# Build Nuxt
FROM node:17-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/hay-kot/homebox"
EXPOSE 7745
VOLUME [ "/data" ]
# Drop root and run as low-privileged user
USER nonroot
ENTRYPOINT [ "/app" ]
CMD [ "/data/config.yml" ]

View file

@ -16,28 +16,12 @@
[Configuration & Docker Compose](https://hay-kot.github.io/homebox/quick-start) [Configuration & Docker Compose](https://hay-kot.github.io/homebox/quick-start)
```bash ```bash
# If using the rootless image, ensure data docker run --name=homebox \
# folder has correct permissions --restart=always \
mkdir -p /path/to/data/folder --publish=3100:7745 \
chown 65532:65532 -R /path/to/data/folder ghcr.io/hay-kot/homebox:nightly
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/hay-kot/homebox:latest-rootless
``` ```
<!-- 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.
<a href="https://www.buymeacoffee.com/haykot" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 107px !important;" ></a>
## Credits ## Credits
- Logo by [@lakotelman](https://github.com/lakotelman) - Logo by [@lakotelman](https://github.com/lakotelman)

View file

@ -1,145 +1,63 @@
version: "3" version: "3"
env: env:
HBOX_LOG_LEVEL: debug HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_fk=1
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"
tasks: tasks:
setup: generate:
desc: Install development dependencies desc: |
cmds: Generates collateral files from the backend project
- go install github.com/swaggo/swag/cmd/swag@latest including swagger docs and typescripts type for the frontend
- cd backend && go mod tidy deps:
- cd frontend && pnpm install --shamefully-hoist - 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: cmds:
- cd backend/app/api/ && swag fmt
- cd backend/app/api/ && swag init --dir=./,../../internal,../../pkgs
- | - |
npx swagger-typescript-api \ npx swagger-typescript-api \
--no-client \ --no-client \
--modular \ --modular \
--path ./backend/app/api/static/docs/swagger.json \ --path ./backend/app/api/docs/swagger.json \
--output ./frontend/lib/api/types --output ./frontend/lib/api/types
- go run ./backend/app/tools/typegen/main.go ./frontend/lib/api/types/data-contracts.ts
python3 ./scripts/process-types.py ./frontend/lib/api/types/data-contracts.ts
sources: sources:
- ./backend/app/tools/typegen/main.go - "./backend/app/api/**/*"
- ./backend/app/api/static/docs/swagger.json - "./backend/app/internal/types/**/*"
- "./scripts/process-types.py"
generates:
- "./frontend/lib/api/types/data-contracts.ts"
- "./backend/app/api/docs/swagger.json"
- "./backend/app/api/docs/swagger.yaml"
generate: api:
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) desc: Starts the backend api server (depends on generate task)
dir: backend
deps: deps:
- generate - generate
cmds: cmds:
- go run ./app/api/ {{ .CLI_ARGS }} - cd backend && go run ./app/api/ {{ .CLI_ARGS }}
silent: false silent: false
go:test: api:build:
desc: Runs all go tests using gotestsum - supports passing gotestsum args
dir: backend
cmds: cmds:
- gotestsum {{ .CLI_ARGS }} ./... - cd backend && go build ./app/api/
go:coverage:
desc: Runs all go tests with -race flag and generates a coverage report
dir: backend
cmds:
- go test -race -coverprofile=coverage.out -covermode=atomic ./app/... ./internal/... ./pkgs/... -v -cover
silent: true silent: true
go:tidy: api:test:
desc: Runs go mod tidy on the backend
dir: backend
cmds: cmds:
- go mod tidy - cd backend && go test ./app/api/
silent: true
go:lint: api:watch:
desc: Runs golangci-lint
dir: backend
cmds: cmds:
- golangci-lint run ./... - cd backend && gotestsum --watch ./...
go:all: api:coverage:
desc: Runs all go test and lint related tasks
cmds: cmds:
- task: go:tidy - cd backend && go test -race -coverprofile=coverage.out -covermode=atomic ./app/... ./internal/... ./pkgs/... -v -cover
- task: go:lint silent: true
- task: go:test
go:build:
desc: Builds the backend binary
dir: backend
cmds:
- go build -o ../build/backend ./app/api
db:generate:
desc: Run Entgo.io Code Generation
dir: backend/internal/
cmds:
- |
go generate ./...
sources:
- "./backend/internal/data/ent/schema/**/*"
db:migration:
desc: Runs the database diff engine to generate a SQL migration files
deps:
- db:generate
cmds:
- cd backend && go run app/tools/migrations/main.go {{ .CLI_ARGS }}
ui:watch:
desc: Starts the vitest test runner in watch mode
dir: frontend
cmds:
- pnpm run test:watch
ui:dev:
desc: Run frontend development server
dir: frontend
cmds:
- pnpm dev
ui:fix:
desc: Runs prettier and eslint on the frontend
dir: frontend
cmds:
- pnpm run lint:fix
ui:check:
desc: Runs type checking
dir: frontend
cmds:
- pnpm run typecheck
test:ci: test:ci:
desc: Runs end-to-end test on a live server (only for use in CI)
cmds: cmds:
- cd backend && go build ./app/api - cd backend && go build ./app/api
- backend/api & - backend/api &
@ -147,11 +65,30 @@ tasks:
- cd frontend && pnpm run test:ci - cd frontend && pnpm run test:ci
silent: true silent: true
pr: frontend:watch:
desc: Runs all tasks required for a PR desc: Starts the vitest test runner in watch mode
cmds: cmds:
- task: generate - cd frontend && pnpm vitest --watch
- task: go:all
- task: ui:check frontend:
- task: ui:fix desc: Run frontend development server
- task: test:ci cmds:
- cd frontend && pnpm dev
db:generate:
desc: Run Entgo.io Code Generation
cmds:
- |
cd backend && go generate ./... \
--template=ent/schema/templates/has_id.tmpl
sources:
- "./backend/ent/schema/**/*"
generates:
- "./backend/ent/"
db:migration:
desc: Runs the database diff engine to generate a SQL migration files
deps:
- db:generate
cmds:
- cd backend && go run app/migrations/main.go {{ .CLI_ARGS }}

2
backend/.gitignore vendored
View file

@ -1,2 +0,0 @@
dist/

View file

@ -1,74 +0,0 @@
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

View file

@ -1,54 +0,0 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# you may remove this if you don't need go generate
- go generate ./...
builds:
- main: ./app/api
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- "386"
- arm
- arm64
ignore:
- goos: windows
goarch: arm
- goos: windows
goarch: "386"
archives:
- format: tar.gz
# this name template makes the OS and Arch compatible with the results of uname.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

View file

@ -1,21 +1,23 @@
package main package main
import ( import (
"github.com/hay-kot/homebox/backend/internal/core/services" "time"
"github.com/hay-kot/homebox/backend/internal/core/services/reporting/eventbus"
"github.com/hay-kot/homebox/backend/internal/data/ent" "github.com/hay-kot/homebox/backend/ent"
"github.com/hay-kot/homebox/backend/internal/data/repo" "github.com/hay-kot/homebox/backend/internal/config"
"github.com/hay-kot/homebox/backend/internal/sys/config" "github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/mailer" "github.com/hay-kot/homebox/backend/pkgs/mailer"
"github.com/hay-kot/homebox/backend/pkgs/server"
) )
type app struct { type app struct {
conf *config.Config conf *config.Config
mailer mailer.Mailer mailer mailer.Mailer
db *ent.Client db *ent.Client
server *server.Server
repos *repo.AllRepos repos *repo.AllRepos
services *services.AllServices services *services.AllServices
bus *eventbus.EventBus
} }
func new(conf *config.Config) *app { func new(conf *config.Config) *app {
@ -33,3 +35,10 @@ func new(conf *config.Config) *app {
return s return s
} }
func (a *app) startBgTask(t time.Duration, fn func()) {
for {
a.server.Background(fn)
time.Sleep(t)
}
}

View file

@ -1,37 +0,0 @@
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)
}
}
}

View file

@ -1,66 +0,0 @@
package main
import (
"context"
"strings"
"time"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/rs/zerolog/log"
)
func (a *app) SetupDemo() {
csvText := `HB.import_ref,HB.location,HB.labels,HB.quantity,HB.name,HB.description,HB.insured,HB.serial_number,HB.model_number,HB.manufacturer,HB.notes,HB.purchase_from,HB.purchase_price,HB.purchase_time,HB.lifetime_warranty,HB.warranty_expires,HB.warranty_details,HB.sold_to,HB.sold_price,HB.sold_time,HB.sold_notes
,Garage,IOT;Home Assistant; Z-Wave,1,Zooz Universal Relay ZEN17,"Zooz 700 Series Z-Wave Universal Relay ZEN17 for Awnings, Garage Doors, Sprinklers, and More | 2 NO-C-NC Relays (20A, 10A) | Signal Repeater | Hub Required (Compatible with SmartThings and Hubitat)",,,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
,Living Room,IOT;Home Assistant; Z-Wave,1,Zooz Motion Sensor,"Zooz Z-Wave Plus S2 Motion Sensor ZSE18 with Magnetic Mount, Works with Vera and SmartThings",,,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,,,,,,
,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,,,,,,,
`
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
registration := services.UserRegistration{
Email: "demo@example.com",
Name: "Demo",
Password: "demo",
}
// First check if we've already setup a demo user and skip if so
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
}
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, 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(ctx, self.GroupID, strings.NewReader(csvText))
if err != nil {
log.Err(err).Msg("Failed to import CSV")
log.Fatal().Msg("Failed to setup demo")
}
log.Info().Msg("Demo setup complete")
}

1654
backend/app/api/docs/docs.go Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,17 +0,0 @@
// Package debughandlers provides handlers for debugging.
package debughandlers
import (
"expvar"
"net/http"
"net/http/pprof"
)
func New(mux *http.ServeMux) {
mux.HandleFunc("/debug/pprof", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
mux.Handle("/debug/vars", expvar.Handler())
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -1,213 +0,0 @@
// Package v1 provides the API handlers for version 1 of the API.
package v1
import (
"encoding/json"
"net/http"
"time"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/core/services/reporting/eventbus"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
"github.com/olahol/melody"
)
type Results[T any] struct {
Items []T `json:"items"`
}
func WrapResults[T any](items []T) Results[T] {
return Results[T]{Items: items}
}
type Wrapped struct {
Item interface{} `json:"item"`
}
func Wrap(v any) Wrapped {
return Wrapped{Item: v}
}
func WithMaxUploadSize(maxUploadSize int64) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.maxUploadSize = maxUploadSize
}
}
func WithDemoStatus(demoStatus bool) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.isDemo = demoStatus
}
}
func WithRegistration(allowRegistration bool) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.allowRegistration = allowRegistration
}
}
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 (
ReadyFunc func() bool
Build struct {
Version string `json:"version"`
Commit string `json:"commit"`
BuildTime string `json:"buildTime"`
}
APISummary struct {
Healthy bool `json:"health"`
Versions []string `json:"versions"`
Title string `json:"title"`
Message string `json:"message"`
Build Build `json:"build"`
Demo bool `json:"demo"`
AllowRegistration bool `json:"allowRegistration"`
}
)
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, bus *eventbus.EventBus, options ...func(*V1Controller)) *V1Controller {
ctrl := &V1Controller{
repo: repos,
svc: svc,
allowRegistration: true,
bus: bus,
}
for _, opt := range options {
opt(ctrl)
}
return ctrl
}
// HandleBase godoc
//
// @Summary Application Info
// @Tags Base
// @Produce json
// @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{
Healthy: ready(),
Title: "Homebox",
Message: "Track, Manage, and Organize your Things",
Build: build,
Demo: ctrl.isDemo,
AllowRegistration: ctrl.allowRegistration,
})
}
}
// 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)
}
}

View file

@ -1,27 +0,0 @@
package v1
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
)
// routeID extracts the ID from the request URL. If the ID is not in a valid
// format, an error is returned. If a error is returned, it can be directly returned
// from the handler. the validate.ErrInvalidID error is known by the error middleware
// and will be handled accordingly.
//
// Example: /api/v1/ac614db5-d8b8-4659-9b14-6e913a6eb18a -> uuid.UUID{ac614db5-d8b8-4659-9b14-6e913a6eb18a}
func (ctrl *V1Controller) routeID(r *http.Request) (uuid.UUID, error) {
return ctrl.routeUUID(r, "id")
}
func (ctrl *V1Controller) routeUUID(r *http.Request, key string) (uuid.UUID, error) {
ID, err := uuid.Parse(chi.URLParam(r, key))
if err != nil {
return uuid.Nil, validate.NewRouteKeyError(key)
}
return ID, nil
}

View file

@ -1,36 +0,0 @@
package v1
import (
"net/url"
"strconv"
"github.com/google/uuid"
)
func queryUUIDList(params url.Values, key string) []uuid.UUID {
var ids []uuid.UUID
for _, id := range params[key] {
uid, err := uuid.Parse(id)
if err != nil {
continue
}
ids = append(ids, uid)
}
return ids
}
func queryIntOrNegativeOne(s string) int {
i, err := strconv.Atoi(s)
if err != nil {
return -1
}
return i
}
func queryBool(s string) bool {
b, err := strconv.ParseBool(s)
if err != nil {
return false
}
return b
}

View file

@ -1,83 +0,0 @@
package v1
import (
"context"
"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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
type ActionAmountResult struct {
Completed int `json:"completed"`
}
func actionHandlerFactory(ref string, fn func(context.Context, uuid.UUID) (int, error)) errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
totalCompleted, err := fn(ctx, ctx.GID)
if err != nil {
log.Err(err).Str("action_ref", ref).Msg("failed to run action")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, ActionAmountResult{Completed: totalCompleted})
}
}
// HandleEnsureAssetID godoc
//
// @Summary Ensure Asset IDs
// @Description Ensures all items in the database have an asset ID
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/ensure-asset-ids [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleEnsureAssetID() errchain.HandlerFunc {
return actionHandlerFactory("ensure asset IDs", ctrl.svc.Items.EnsureAssetID)
}
// HandleEnsureImportRefs godoc
//
// @Summary Ensures Import Refs
// @Description Ensures all items in the database have an import ref
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/ensure-import-refs [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleEnsureImportRefs() errchain.HandlerFunc {
return actionHandlerFactory("ensure import refs", ctrl.svc.Items.EnsureImportRef)
}
// HandleItemDateZeroOut godoc
//
// @Summary Zero Out Time Fields
// @Description Resets all item date fields to the beginning of the day
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/zero-item-time-fields [Post]
// @Security Bearer
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)
}

View file

@ -1,62 +0,0 @@
package v1
import (
"net/http"
"strconv"
"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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
// HandleAssetGet godocs
//
// @Summary Get Item by Asset ID
// @Tags Items
// @Produce json
// @Param id path string true "Asset ID"
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
// @Router /v1/assets/{id} [GET]
// @Security Bearer
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
// Convert the asset ID to an int64
assetID, err := strconv.ParseInt(assetIDParam, 10, 64)
if err != nil {
return err
}
pageParam := r.URL.Query().Get("page")
var page int64 = -1
if pageParam != "" {
page, err = strconv.ParseInt(pageParam, 10, 64)
if err != nil {
return server.JSON(w, http.StatusBadRequest, "Invalid page number")
}
}
pageSizeParam := r.URL.Query().Get("pageSize")
var pageSize int64 = -1
if pageSizeParam != "" {
pageSize, err = strconv.ParseInt(pageSizeParam, 10, 64)
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))
if err != nil {
log.Err(err).Msg("failed to get item")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, items)
}
}

View file

@ -1,251 +0,0 @@
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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
const (
cookieNameToken = "hb.auth.token"
cookieNameRemember = "hb.auth.remember"
cookieNameSession = "hb.auth.session"
)
type (
TokenResponse struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expiresAt"`
AttachmentToken string `json:"attachmentToken"`
}
LoginForm struct {
Username string `json:"username"`
Password string `json:"password"`
StayLoggedIn bool `json:"stayLoggedIn"`
}
)
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
// @Tags Authentication
// @Accept x-www-form-urlencoded
// @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 provider query string false "auth provider"
// @Produce json
// @Success 200 {object} TokenResponse
// @Router /v1/users/login [POST]
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 {
// Extract provider query
provider := r.URL.Query().Get("provider")
if provider == "" {
provider = "local"
}
// Get the provider
p, ok := providers[provider]
if !ok {
return validate.NewRequestError(errors.New("invalid auth provider"), http.StatusBadRequest)
}
newToken, err := p.Authenticate(w, r)
if err != nil {
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,
AttachmentToken: newToken.AttachmentToken,
})
}
}
// HandleAuthLogout godoc
//
// @Summary User Logout
// @Tags Authentication
// @Success 204
// @Router /v1/users/logout [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthLogout() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
token := services.UseTokenCtx(r.Context())
if token == "" {
return validate.NewRequestError(errors.New("no token within request context"), http.StatusUnauthorized)
}
err := ctrl.svc.User.Logout(r.Context(), token)
if err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
ctrl.unsetCookies(w, noPort(r.Host))
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleAuthRefresh godoc
//
// @Summary User Token Refresh
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.
// @Description This does not validate that the user still exists within the database.
// @Tags Authentication
// @Success 200
// @Router /v1/users/refresh [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
requestToken := services.UseTokenCtx(r.Context())
if requestToken == "" {
return validate.NewRequestError(errors.New("no token within request context"), http.StatusUnauthorized)
}
newToken, err := ctrl.svc.User.RenewToken(r.Context(), requestToken)
if err != nil {
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: "/",
})
}

View file

@ -1,97 +0,0 @@
package v1
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/httpkit/errchain"
)
type (
GroupInvitationCreate struct {
Uses int `json:"uses" validate:"required,min=1,max=100"`
ExpiresAt time.Time `json:"expiresAt"`
}
GroupInvitation struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expiresAt"`
Uses int `json:"uses"`
}
)
// HandleGroupGet godoc
//
// @Summary Get Group
// @Tags Group
// @Produce json
// @Success 200 {object} repo.Group
// @Router /v1/groups [Get]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupGet() errchain.HandlerFunc {
fn := func(r *http.Request) (repo.Group, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.GroupByID(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
}
// HandleGroupUpdate godoc
//
// @Summary Update Group
// @Tags Group
// @Produce json
// @Param payload body repo.GroupUpdate true "User Data"
// @Success 200 {object} repo.Group
// @Router /v1/groups [Put]
// @Security Bearer
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)
}
return adapters.Action(fn, http.StatusOK)
}
// HandleGroupInvitationsCreate godoc
//
// @Summary Create Group Invitation
// @Tags Group
// @Produce json
// @Param payload body GroupInvitationCreate true "User Data"
// @Success 200 {object} GroupInvitation
// @Router /v1/groups/invitations [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupInvitationsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, body GroupInvitationCreate) (GroupInvitation, error) {
if body.ExpiresAt.IsZero() {
body.ExpiresAt = time.Now().Add(time.Hour * 24)
}
auth := services.NewContext(r.Context())
token, err := ctrl.svc.Group.NewInvitation(auth, body.Uses, body.ExpiresAt)
return GroupInvitation{
Token: token,
ExpiresAt: body.ExpiresAt,
Uses: body.Uses,
}, err
}
return adapters.Action(fn, http.StatusCreated)
}

View file

@ -1,339 +0,0 @@
package v1
import (
"database/sql"
"encoding/csv"
"errors"
"net/http"
"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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
// HandleItemsGetAll godoc
//
// @Summary Query All Items
// @Tags Items
// @Produce json
// @Param q query string false "search string"
// @Param page query int false "page number"
// @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
func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
extractQuery := func(r *http.Request) repo.ItemQuery {
params := r.URL.Query()
filterFieldItems := func(raw []string) []repo.FieldQuery {
var items []repo.FieldQuery
for _, v := range raw {
parts := strings.SplitN(v, "=", 2)
if len(parts) == 2 {
items = append(items, repo.FieldQuery{
Name: parts[0],
Value: parts[1],
})
}
}
return items
}
v := repo.ItemQuery{
Page: queryIntOrNegativeOne(params.Get("page")),
PageSize: queryIntOrNegativeOne(params.Get("pageSize")),
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, "#") {
aidStr := strings.TrimPrefix(v.Search, "#")
aid, ok := repo.ParseAssetID(aidStr)
if ok {
v.Search = ""
v.AssetID = aid
}
}
return v
}
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GID, extractQuery(r))
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return server.JSON(w, http.StatusOK, repo.PaginationResult[repo.ItemSummary]{
Items: []repo.ItemSummary{},
})
}
log.Err(err).Msg("failed to get items")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, items)
}
}
// 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
// @Tags Items
// @Produce json
// @Param payload body repo.ItemCreate true "Item Data"
// @Success 201 {object} repo.ItemSummary
// @Router /v1/items [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, body repo.ItemCreate) (repo.ItemOut, error) {
return ctrl.svc.Items.Create(services.NewContext(r.Context()), body)
}
return adapters.Action(fn, http.StatusCreated)
}
// HandleItemGet godocs
//
// @Summary Get Item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.ItemOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
}
// HandleItemDelete godocs
//
// @Summary Delete Item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 204
// @Router /v1/items/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Items.DeleteByGroup(auth, auth.GID, ID)
return nil, err
}
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleItemUpdate godocs
//
// @Summary Update Item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Param payload body repo.ItemUpdate true "Item Data"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, body repo.ItemUpdate) (repo.ItemOut, error) {
auth := services.NewContext(r.Context())
body.ID = ID
return ctrl.repo.Items.UpdateByGroup(auth, auth.GID, body)
}
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
// @Tags Items
// @Produce json
// @Success 200
// @Router /v1/items/fields [GET]
// @Success 200 {object} []string
// @Security Bearer
func (ctrl *V1Controller) HandleGetAllCustomFieldNames() errchain.HandlerFunc {
fn := func(r *http.Request) ([]string, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetAllCustomFieldNames(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
}
// HandleGetAllCustomFieldValues godocs
//
// @Summary Get All Custom Field Values
// @Tags Items
// @Produce json
// @Success 200
// @Router /v1/items/fields/values [GET]
// @Success 200 {object} []string
// @Security Bearer
func (ctrl *V1Controller) HandleGetAllCustomFieldValues() errchain.HandlerFunc {
type query struct {
Field string `schema:"field" validate:"required"`
}
fn := func(r *http.Request, q query) ([]string, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetAllCustomFieldValues(auth, auth.GID, q.Field)
}
return adapters.Query(fn, http.StatusOK)
}
// HandleItemsImport godocs
//
// @Summary Import Items
// @Tags Items
// @Produce json
// @Success 204
// @Param csv formData file true "Image to upload"
// @Router /v1/items/import [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsImport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
file, _, err := r.FormFile("csv")
if err != nil {
log.Err(err).Msg("failed to get file from form")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
user := services.UseUserCtx(r.Context())
_, err = ctrl.svc.Items.CsvImport(r.Context(), user.GroupID, file)
if err != nil {
log.Err(err).Msg("failed to import items")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleItemsExport godocs
//
// @Summary Export Items
// @Tags Items
// @Success 200 {string} string "text/csv"
// @Router /v1/items/export [GET]
// @Security Bearer
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)
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")
writer := csv.NewWriter(w)
writer.Comma = '\t'
return writer.WriteAll(csvData)
}
}

View file

@ -1,199 +0,0 @@
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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
type (
ItemAttachmentToken struct {
Token string `json:"token"`
}
)
// HandleItemAttachmentCreate godocs
//
// @Summary Create Item Attachment
// @Tags Items Attachments
// @Produce json
// @Param id path string true "Item ID"
// @Param file formData file true "File attachment"
// @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} validate.ErrorResponse
// @Router /v1/items/{id}/attachments [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
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()
file, _, err := r.FormFile("file")
if err != nil {
switch {
case errors.Is(err, http.ErrMissingFile):
log.Debug().Msg("file for attachment is missing")
errs = errs.Append("file", "file is required")
default:
log.Err(err).Msg("failed to get file from form")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
}
attachmentName := r.FormValue("name")
if attachmentName == "" {
log.Debug().Msg("failed to get name from form")
errs = errs.Append("name", "name is required")
}
if !errs.Nil() {
return server.JSON(w, http.StatusUnprocessableEntity, errs)
}
attachmentType := r.FormValue("type")
if attachmentType == "" {
// 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)
if err != nil {
return err
}
ctx := services.NewContext(r.Context())
item, err := ctrl.svc.Items.AttachmentAdd(
ctx,
id,
attachmentName,
attachment.Type(attachmentType),
file,
)
if err != nil {
log.Err(err).Msg("failed to add attachment")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusCreated, item)
}
}
// HandleItemAttachmentGet godocs
//
// @Summary Get Item Attachment
// @Tags Items Attachments
// @Produce application/octet-stream
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 200 {object} ItemAttachmentToken
// @Router /v1/items/{id}/attachments/{attachment_id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentGet() errchain.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
// HandleItemAttachmentDelete godocs
//
// @Summary Delete Item Attachment
// @Tags Items Attachments
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 204
// @Router /v1/items/{id}/attachments/{attachment_id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDelete() errchain.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
// HandleItemAttachmentUpdate godocs
//
// @Summary Update Item Attachment
// @Tags Items Attachments
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Param payload body repo.ItemAttachmentUpdate true "Attachment Update"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id}/attachments/{attachment_id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentUpdate() errchain.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) error {
ID, err := ctrl.routeID(r)
if err != nil {
return err
}
attachmentID, err := ctrl.routeUUID(r, "attachment_id")
if err != nil {
return err
}
ctx := services.NewContext(r.Context())
switch r.Method {
case http.MethodGet:
doc, err := ctrl.svc.Items.AttachmentPath(r.Context(), attachmentID)
if err != nil {
log.Err(err).Msg("failed to get attachment path")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
http.ServeFile(w, r, doc.Path)
return nil
// Delete Attachment Handler
case http.MethodDelete:
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentID)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
// Update Attachment Handler
case http.MethodPut:
var attachment repo.ItemAttachmentUpdate
err = server.Decode(r, &attachment)
if err != nil {
log.Err(err).Msg("failed to decode attachment")
return validate.NewRequestError(err, http.StatusBadRequest)
}
attachment.ID = attachmentID
val, err := ctrl.svc.Items.AttachmentUpdate(ctx, ID, &attachment)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, val)
}
return nil
}

View file

@ -1,102 +0,0 @@
package v1
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/httpkit/errchain"
)
// HandleLabelsGetAll godoc
//
// @Summary Get All Labels
// @Tags Labels
// @Produce json
// @Success 200 {object} []repo.LabelOut
// @Router /v1/labels [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsGetAll() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.LabelSummary, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.GetAll(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
}
// HandleLabelsCreate godoc
//
// @Summary Create Label
// @Tags Labels
// @Produce json
// @Param payload body repo.LabelCreate true "Label Data"
// @Success 200 {object} repo.LabelSummary
// @Router /v1/labels [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, data repo.LabelCreate) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.Create(auth, auth.GID, data)
}
return adapters.Action(fn, http.StatusCreated)
}
// HandleLabelDelete godocs
//
// @Summary Delete Label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 204
// @Router /v1/labels/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Labels.DeleteByGroup(auth, auth.GID, ID)
return nil, err
}
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleLabelGet godocs
//
// @Summary Get Label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.GetOneByGroup(auth, auth.GID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
}
// HandleLabelUpdate godocs
//
// @Summary Update Label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, data repo.LabelUpdate) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
data.ID = ID
return ctrl.repo.Labels.UpdateByGroup(auth, auth.GID, data)
}
return adapters.ActionID("id", fn, http.StatusOK)
}

View file

@ -1,122 +0,0 @@
package v1
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/httpkit/errchain"
)
// HandleLocationTreeQuery godoc
//
// @Summary Get Locations Tree
// @Tags Locations
// @Produce json
// @Param withItems query bool false "include items in response tree"
// @Success 200 {object} []repo.TreeItem
// @Router /v1/locations/tree [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
fn := func(r *http.Request, query repo.TreeQuery) ([]repo.TreeItem, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.Tree(auth, auth.GID, query)
}
return adapters.Query(fn, http.StatusOK)
}
// HandleLocationGetAll godoc
//
// @Summary Get All Locations
// @Tags Locations
// @Produce json
// @Param filterChildren query bool false "Filter locations with parents"
// @Success 200 {object} []repo.LocationOutCount
// @Router /v1/locations [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
fn := func(r *http.Request, q repo.LocationQuery) ([]repo.LocationOutCount, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.GetAll(auth, auth.GID, q)
}
return adapters.Query(fn, http.StatusOK)
}
// HandleLocationCreate godoc
//
// @Summary Create Location
// @Tags Locations
// @Produce json
// @Param payload body repo.LocationCreate true "Location Data"
// @Success 200 {object} repo.LocationSummary
// @Router /v1/locations [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
fn := func(r *http.Request, createData repo.LocationCreate) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.Create(auth, auth.GID, createData)
}
return adapters.Action(fn, http.StatusCreated)
}
// HandleLocationDelete godoc
//
// @Summary Delete Location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 204
// @Router /v1/locations/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Locations.DeleteByGroup(auth, auth.GID, ID)
return nil, err
}
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleLocationGet godoc
//
// @Summary Get Location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [GET]
// @Security Bearer
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)
}
return adapters.CommandID("id", fn, http.StatusOK)
}
// HandleLocationUpdate godoc
//
// @Summary Update Location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Param payload body repo.LocationUpdate true "Location Data"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, body repo.LocationUpdate) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
body.ID = ID
return ctrl.repo.Locations.UpdateByGroup(auth, auth.GID, ID, body)
}
return adapters.ActionID("id", fn, http.StatusOK)
}

View file

@ -1,82 +0,0 @@
package v1
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/httpkit/errchain"
)
// HandleMaintenanceLogGet godoc
//
// @Summary Get Maintenance Log
// @Tags Maintenance
// @Produce json
// @Success 200 {object} repo.MaintenanceLog
// @Router /v1/items/{id}/maintenance [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleMaintenanceLogGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, q repo.MaintenanceLogQuery) (repo.MaintenanceLog, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.GetLog(auth, auth.GID, ID, q)
}
return adapters.QueryID("id", fn, http.StatusOK)
}
// HandleMaintenanceEntryCreate godoc
//
// @Summary Create Maintenance Entry
// @Tags Maintenance
// @Produce json
// @Param payload body repo.MaintenanceEntryCreate true "Entry Data"
// @Success 201 {object} repo.MaintenanceEntry
// @Router /v1/items/{id}/maintenance [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleMaintenanceEntryCreate() errchain.HandlerFunc {
fn := func(r *http.Request, itemID uuid.UUID, body repo.MaintenanceEntryCreate) (repo.MaintenanceEntry, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.Create(auth, itemID, body)
}
return adapters.ActionID("id", fn, http.StatusCreated)
}
// HandleMaintenanceEntryDelete godoc
//
// @Summary Delete Maintenance Entry
// @Tags Maintenance
// @Produce json
// @Success 204
// @Router /v1/items/{id}/maintenance/{entry_id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleMaintenanceEntryDelete() errchain.HandlerFunc {
fn := func(r *http.Request, entryID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.MaintEntry.Delete(auth, entryID)
return nil, err
}
return adapters.CommandID("entry_id", fn, http.StatusNoContent)
}
// HandleMaintenanceEntryUpdate godoc
//
// @Summary Update Maintenance Entry
// @Tags Maintenance
// @Produce json
// @Param payload body repo.MaintenanceEntryUpdate true "Entry Data"
// @Success 200 {object} repo.MaintenanceEntry
// @Router /v1/items/{id}/maintenance/{entry_id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleMaintenanceEntryUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, entryID uuid.UUID, body repo.MaintenanceEntryUpdate) (repo.MaintenanceEntry, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.Update(auth, entryID, body)
}
return adapters.ActionID("entry_id", fn, http.StatusOK)
}

View file

@ -1,105 +0,0 @@
package v1
import (
"net/http"
"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/httpkit/errchain"
)
// HandleGetUserNotifiers godoc
//
// @Summary Get Notifiers
// @Tags Notifiers
// @Produce json
// @Success 200 {object} []repo.NotifierOut
// @Router /v1/notifiers [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGetUserNotifiers() errchain.HandlerFunc {
fn := func(r *http.Request, _ struct{}) ([]repo.NotifierOut, error) {
user := services.UseUserCtx(r.Context())
return ctrl.repo.Notifiers.GetByUser(r.Context(), user.ID)
}
return adapters.Query(fn, http.StatusOK)
}
// HandleCreateNotifier godoc
//
// @Summary Create Notifier
// @Tags Notifiers
// @Produce json
// @Param payload body repo.NotifierCreate true "Notifier Data"
// @Success 200 {object} repo.NotifierOut
// @Router /v1/notifiers [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleCreateNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, in repo.NotifierCreate) (repo.NotifierOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Notifiers.Create(auth, auth.GID, auth.UID, in)
}
return adapters.Action(fn, http.StatusCreated)
}
// HandleDeleteNotifier godocs
//
// @Summary Delete a Notifier
// @Tags Notifiers
// @Param id path string true "Notifier ID"
// @Success 204
// @Router /v1/notifiers/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleDeleteNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
return nil, ctrl.repo.Notifiers.Delete(auth, auth.UID, ID)
}
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleUpdateNotifier godocs
//
// @Summary Update Notifier
// @Tags Notifiers
// @Param id path string true "Notifier ID"
// @Param payload body repo.NotifierUpdate true "Notifier Data"
// @Success 200 {object} repo.NotifierOut
// @Router /v1/notifiers/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUpdateNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, in repo.NotifierUpdate) (repo.NotifierOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Notifiers.Update(auth, auth.UID, ID, in)
}
return adapters.ActionID("id", fn, http.StatusOK)
}
// HandlerNotifierTest godoc
//
// @Summary Test Notifier
// @Tags Notifiers
// @Produce json
// @Param id path string true "Notifier ID"
// @Param url query string true "URL"
// @Success 204
// @Router /v1/notifiers/test [POST]
// @Security Bearer
func (ctrl *V1Controller) HandlerNotifierTest() errchain.HandlerFunc {
type body struct {
URL string `json:"url" validate:"required"`
}
fn := func(r *http.Request, q body) (any, error) {
err := shoutrrr.Send(q.URL, "Test message from Homebox")
return nil, err
}
return adapters.Action(fn, http.StatusOK)
}

View file

@ -1,72 +0,0 @@
package v1
import (
"bytes"
"image/png"
"io"
"net/http"
"net/url"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/httpkit/errchain"
"github.com/yeqown/go-qrcode/v2"
"github.com/yeqown/go-qrcode/writer/standard"
_ "embed"
)
//go:embed assets/QRIcon.png
var qrcodeLogo []byte
// HandleGenerateQRCode godoc
//
// @Summary Create QR Code
// @Tags Items
// @Produce json
// @Param data query string false "data to be encoded into qrcode"
// @Success 200 {string} string "image/jpeg"
// @Router /v1/qrcode [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGenerateQRCode() errchain.HandlerFunc {
type query struct {
// 4,296 characters is the maximum length of a QR code
Data string `schema:"data" validate:"required,max=4296"`
}
return func(w http.ResponseWriter, r *http.Request) error {
q, err := adapters.DecodeQuery[query](r)
if err != nil {
return err
}
image, err := png.Decode(bytes.NewReader(qrcodeLogo))
if err != nil {
panic(err)
}
decodedStr, err := url.QueryUnescape(q.Data)
if err != nil {
return err
}
qrc, err := qrcode.New(decodedStr)
if err != nil {
return err
}
toWriteCloser := struct {
io.Writer
io.Closer
}{
Writer: w,
Closer: io.NopCloser(nil),
}
qrwriter := standard.NewWithWriter(toWriteCloser, standard.WithLogoImage(image))
// Return the QR code as a jpeg image
w.Header().Set("Content-Type", "image/jpeg")
w.Header().Set("Content-Disposition", "attachment; filename=qrcode.jpg")
return qrc.Save(qrwriter)
}
}

View file

@ -1,32 +0,0 @@
package v1
import (
"net/http"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/httpkit/errchain"
)
// HandleBillOfMaterialsExport godoc
//
// @Summary Export Bill of Materials
// @Tags Reporting
// @Produce json
// @Success 200 {string} string "text/csv"
// @Router /v1/reporting/bill-of-materials [GET]
// @Security Bearer
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)
if err != nil {
return err
}
w.Header().Set("Content-Type", "text/tsv")
w.Header().Set("Content-Disposition", "attachment; filename=bill-of-materials.tsv")
_, err = w.Write(csv)
return err
}
}

View file

@ -1,104 +0,0 @@
package v1
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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
)
// HandleGroupStatisticsLocations godoc
//
// @Summary Get Location Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} []repo.TotalsByOrganizer
// @Router /v1/groups/statistics/locations [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatisticsLocations() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.TotalsByOrganizer, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsLocationsByPurchasePrice(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
}
// HandleGroupStatisticsLabels godoc
//
// @Summary Get Label Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} []repo.TotalsByOrganizer
// @Router /v1/groups/statistics/labels [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatisticsLabels() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.TotalsByOrganizer, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsLabelsByPurchasePrice(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
}
// HandleGroupStatistics godoc
//
// @Summary Get Group Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} repo.GroupStatistics
// @Router /v1/groups/statistics [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatistics() errchain.HandlerFunc {
fn := func(r *http.Request) (repo.GroupStatistics, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsGroup(auth, auth.GID)
}
return adapters.Command(fn, http.StatusOK)
}
// HandleGroupStatisticsPriceOverTime godoc
//
// @Summary Get Purchase Price Statistics
// @Tags Statistics
// @Produce json
// @Success 200 {object} repo.ValueOverTime
// @Param start query string false "start date"
// @Param end query string false "end date"
// @Router /v1/groups/statistics/purchase-price [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupStatisticsPriceOverTime() errchain.HandlerFunc {
parseDate := func(datestr string, defaultDate time.Time) (time.Time, error) {
if datestr == "" {
return defaultDate, nil
}
return time.Parse("2006-01-02", datestr)
}
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
startDate, err := parseDate(r.URL.Query().Get("start"), time.Now().AddDate(0, -1, 0))
if err != nil {
return validate.NewRequestError(err, http.StatusBadRequest)
}
endDate, err := parseDate(r.URL.Query().Get("end"), time.Now())
if err != nil {
return validate.NewRequestError(err, http.StatusBadRequest)
}
stats, err := ctrl.repo.Groups.StatsPurchasePrice(ctx, ctx.GID, startDate, endDate)
if err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, stats)
}
}

View file

@ -1,154 +0,0 @@
package v1
import (
"fmt"
"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/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
// HandleUserRegistration godoc
//
// @Summary Register New User
// @Tags User
// @Produce json
// @Param payload body services.UserRegistration true "User Data"
// @Success 204
// @Router /v1/users/register [Post]
func (ctrl *V1Controller) HandleUserRegistration() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
regData := services.UserRegistration{}
if err := server.Decode(r, &regData); err != nil {
log.Err(err).Msg("failed to decode user registration data")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
if !ctrl.allowRegistration && regData.GroupToken == "" {
return validate.NewRequestError(fmt.Errorf("user registration disabled"), http.StatusForbidden)
}
_, err := ctrl.svc.User.RegisterUser(r.Context(), regData)
if err != nil {
log.Err(err).Msg("failed to register user")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleUserSelf godoc
//
// @Summary Get User Self
// @Tags User
// @Produce json
// @Success 200 {object} Wrapped{item=repo.UserOut}
// @Router /v1/users/self [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelf() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
token := services.UseTokenCtx(r.Context())
usr, err := ctrl.svc.User.GetSelf(r.Context(), token)
if usr.ID == uuid.Nil || err != nil {
log.Err(err).Msg("failed to get user")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, Wrap(usr))
}
}
// HandleUserSelfUpdate godoc
//
// @Summary Update Account
// @Tags User
// @Produce json
// @Param payload body repo.UserUpdate true "User Data"
// @Success 200 {object} Wrapped{item=repo.UserUpdate}
// @Router /v1/users/self [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfUpdate() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
updateData := repo.UserUpdate{}
if err := server.Decode(r, &updateData); err != nil {
log.Err(err).Msg("failed to decode user update data")
return validate.NewRequestError(err, http.StatusBadRequest)
}
actor := services.UseUserCtx(r.Context())
newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData)
if err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusOK, Wrap(newData))
}
}
// HandleUserSelfDelete godoc
//
// @Summary Delete Account
// @Tags User
// @Produce json
// @Success 204
// @Router /v1/users/self [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfDelete() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
if ctrl.isDemo {
return validate.NewRequestError(nil, http.StatusForbidden)
}
actor := services.UseUserCtx(r.Context())
if err := ctrl.svc.User.DeleteSelf(r.Context(), actor.ID); err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
}
}
type (
ChangePassword struct {
Current string `json:"current,omitempty"`
New string `json:"new,omitempty"`
}
)
// HandleUserSelfChangePassword godoc
//
// @Summary Change Password
// @Tags User
// @Success 204
// @Param payload body ChangePassword true "Password Payload"
// @Router /v1/users/change-password [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
if ctrl.isDemo {
return validate.NewRequestError(nil, http.StatusForbidden)
}
var cp ChangePassword
err := server.Decode(r, &cp)
if err != nil {
log.Err(err).Msg("user failed to change password")
}
ctx := services.NewContext(r.Context())
ok := ctrl.svc.User.ChangePassword(ctx, cp.Current, cp.New)
if !ok {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
}
}

View file

@ -2,8 +2,9 @@ package main
import ( import (
"os" "os"
"strings"
"github.com/hay-kot/homebox/backend/internal/sys/config" "github.com/hay-kot/homebox/backend/internal/config"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -14,11 +15,27 @@ func (a *app) setupLogger() {
// Logger Init // Logger Init
// zerolog.TimeFieldFormat = zerolog.TimeFormatUnix // zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
if a.conf.Log.Format != config.LogFormatJSON { if a.conf.Log.Format != config.LogFormatJSON {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger() log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
} }
level, err := zerolog.ParseLevel(a.conf.Log.Level) log.Level(getLevel(a.conf.Log.Level))
if err == nil { }
zerolog.SetGlobalLevel(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
} }
} }

View file

@ -1,68 +1,53 @@
package main package main
import ( import (
"bytes"
"context" "context"
"fmt"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"time" "time"
atlas "ariga.io/atlas/sql/migrate" atlas "ariga.io/atlas/sql/migrate"
"entgo.io/ent/dialect/sql/schema" "entgo.io/ent/dialect/sql/schema"
"github.com/go-chi/chi/v5" "github.com/hay-kot/homebox/backend/app/api/docs"
"github.com/go-chi/chi/v5/middleware" "github.com/hay-kot/homebox/backend/ent"
"github.com/hay-kot/homebox/backend/internal/config"
"github.com/hay-kot/homebox/backend/internal/core/currencies" "github.com/hay-kot/homebox/backend/internal/migrations"
"github.com/hay-kot/homebox/backend/internal/core/services" "github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/core/services/reporting/eventbus" "github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/internal/data/ent" "github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/hay-kot/homebox/backend/internal/data/migrations" _ "github.com/mattn/go-sqlite3"
"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/httpkit/errchain"
"github.com/hay-kot/httpkit/graceful"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/rs/zerolog/pkgerrors"
_ "github.com/hay-kot/homebox/backend/pkgs/cgofreesqlite"
) )
var ( var (
version = "nightly" Version = "0.1.0"
commit = "HEAD" Commit = "HEAD"
buildTime = "now" BuildTime = "now"
) )
func build() string { // @title Go API Templates
short := commit // @version 1.0
if len(short) > 7 { // @description This is a simple Rest API Server Template that implements some basic User and Authentication patterns to help you get started and bootstrap your next project!.
short = short[:7] // @contact.name Don't
// @license.name MIT
// @BasePath /api
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description "Type 'Bearer TOKEN' to correctly set the API Key"
func main() {
path := ""
if len(os.Args) > 1 {
path = os.Args[1]
} }
return fmt.Sprintf("%s, commit %s, built at %s", version, short, buildTime) cfg, err := config.NewConfig(path)
}
// @title Homebox API
// @version 1.0
// @description Track, Manage, and Organize your Things.
// @contact.name Don't
// @BasePath /api
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description "Type 'Bearer TOKEN' to correctly set the API Key"
func main() {
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
cfg, err := config.New(build(), "Homebox inventory management system")
if err != nil { if err != nil {
panic(err) panic(err)
} }
docs.SwaggerInfo.Host = cfg.Swagger.Host
if err := run(cfg); err != nil { if err := run(cfg); err != nil {
panic(err) panic(err)
} }
@ -75,17 +60,17 @@ func run(cfg *config.Config) error {
// ========================================================================= // =========================================================================
// Initialize Database & Repos // Initialize Database & Repos
err := os.MkdirAll(cfg.Storage.Data, 0o755) err := os.MkdirAll(cfg.Storage.Data, 0755)
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("failed to create data directory") 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 { if err != nil {
log.Fatal(). log.Fatal().
Err(err). Err(err).
Str("driver", "sqlite"). Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteURL). Str("url", cfg.Storage.SqliteUrl).
Msg("failed opening connection to sqlite") Msg("failed opening connection to sqlite")
} }
defer func(c *ent.Client) { defer func(c *ent.Client) {
@ -118,7 +103,7 @@ func run(cfg *config.Config) error {
log.Fatal(). log.Fatal().
Err(err). Err(err).
Str("driver", "sqlite"). Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteURL). Str("url", cfg.Storage.SqliteUrl).
Msg("failed creating schema resources") Msg("failed creating schema resources")
} }
@ -128,145 +113,36 @@ func run(cfg *config.Config) error {
return err 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.db = c
app.repos = repo.New(c, app.bus, cfg.Storage.Data) app.repos = repo.New(c, cfg.Storage.Data)
app.services = services.New( app.services = services.NewServices(app.repos)
app.repos,
services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID),
services.WithCurrencies(currencies),
)
// ========================================================================= // =========================================================================
// Start Server // Start Server
app.server = server.NewServer(
logger := log.With().Caller().Logger() server.WithHost(app.conf.Web.Host),
server.WithPort(app.conf.Web.Port),
router := chi.NewMux()
router.Use(
middleware.RequestID,
middleware.RealIP,
mid.Logger(logger),
middleware.Recoverer,
middleware.StripSlashes,
) )
chain := errchain.New(mid.Errors(logger)) routes := app.newRouter(app.repos)
app.mountRoutes(router, chain, app.repos) if app.conf.Mode != config.ModeDevelopment {
app.logRoutes(routes)
}
runner := graceful.NewRunner() log.Info().Msgf("Starting HTTP Server on %s:%s", app.server.Host, app.server.Port)
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 // Start Reoccurring Tasks
runner.AddFunc("eventbus", app.bus.Run) go app.startBgTask(time.Duration(24)*time.Hour, func() {
_, err := app.repos.AuthTokens.PurgeExpiredTokens(context.Background())
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 { if err != nil {
log.Error(). log.Error().
Err(err). Err(err).
Msg("failed to purge expired tokens") Msg("failed to purge expired tokens")
} }
})) })
runner.AddPlugin(NewTask("purge-invitations", time.Duration(24)*time.Hour, func(ctx context.Context) { return app.server.Start(routes)
_, err := app.repos.Groups.InvitationPurge(ctx)
if err != nil {
log.Error().
Err(err).
Msg("failed to purge expired invitations")
}
}))
runner.AddPlugin(NewTask("send-notifications", time.Duration(1)*time.Hour, func(ctx context.Context) {
now := time.Now()
if now.Hour() == 8 {
fmt.Println("run notifiers")
err := app.services.BackgroundService.SendNotifiersToday(context.Background())
if err != nil {
log.Error().
Err(err).
Msg("failed to send notifiers")
}
}
}))
if cfg.Debug.Enabled {
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 runner.Start(context.Background())
} }

View file

@ -1,154 +1,160 @@
package main package main
import ( import (
"context" "fmt"
"errors"
"net/http" "net/http"
"net/url"
"strings" "strings"
"time"
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1" "github.com/go-chi/chi/v5"
"github.com/hay-kot/homebox/backend/internal/core/services" "github.com/go-chi/chi/v5/middleware"
"github.com/hay-kot/homebox/backend/internal/data/ent" "github.com/hay-kot/homebox/backend/internal/config"
"github.com/hay-kot/homebox/backend/internal/sys/validate" "github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/httpkit/errchain" "github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
) )
type tokenHasKey struct { func (a *app) setGlobalMiddleware(r *chi.Mux) {
key string // =========================================================================
} // Middleware
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(mwStripTrailingSlash)
var hashedToken = tokenHasKey{key: "hashedToken"} // Use struct logger in production for requests, but use
// pretty console logger in development.
type RoleMode int if a.conf.Mode == config.ModeDevelopment {
r.Use(a.mwSummaryLogger)
const ( } else {
RoleModeOr RoleMode = 0 r.Use(a.mwStructLogger)
RoleModeAnd RoleMode = 1
)
// mwRoles is a middleware that will validate the required roles are met. All roles
// are required to be met for the request to be allowed. If the user does not have
// the required roles, a 403 Forbidden will be returned.
//
// WARNING: This middleware _MUST_ be called after mwAuthToken or else it will panic
func (a *app) mwRoles(rm RoleMode, required ...string) errchain.Middleware {
return func(next errchain.Handler) errchain.Handler {
return errchain.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
maybeToken := ctx.Value(hashedToken)
if maybeToken == nil {
panic("mwRoles: token not found in context, you must call mwAuthToken before mwRoles")
}
token := maybeToken.(string)
roles, err := a.repos.AuthTokens.GetRoles(r.Context(), token)
if err != nil {
return err
}
outer:
switch rm {
case RoleModeOr:
for _, role := range required {
if roles.Contains(role) {
break outer
}
}
return validate.NewRequestError(errors.New("Forbidden"), http.StatusForbidden)
case RoleModeAnd:
for _, req := range required {
if !roles.Contains(req) {
return validate.NewRequestError(errors.New("Unauthorized"), http.StatusForbidden)
}
}
}
return next.ServeHTTP(w, r)
})
} }
} r.Use(middleware.Recoverer)
type KeyFunc func(r *http.Request) (string, error) // Set a timeout value on the request context (ctx), that will signal
// through ctx.Done() that the request has timed out and further
func getBearer(r *http.Request) (string, error) { // processing should be stopped.
auth := r.Header.Get("Authorization") r.Use(middleware.Timeout(60 * time.Second))
if auth == "" {
return "", errors.New("authorization header is required")
}
return auth, nil
}
func getQuery(r *http.Request) (string, error) {
token := r.URL.Query().Get("access_token")
if token == "" {
return "", errors.New("access_token query is required")
}
token, err := url.QueryUnescape(token)
if err != nil {
return "", errors.New("access_token query is required")
}
return token, nil
} }
// mwAuthToken is a middleware that will check the database for a stateful token // 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. // and attach it to the request context with the user, or return a 401 if it doesn't exist.
// Authorization support is by token via Headers or Query Parameter func (a *app) mwAuthToken(next http.Handler) http.Handler {
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Example: requestToken := r.Header.Get("Authorization")
// - header = "Bearer 1234567890"
// - query = "?access_token=1234567890"
func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
return errchain.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
var requestToken string
// We ignore the error to allow the next strategy to be attempted
{
cookies, _ := v1.GetCookies(r)
if cookies != nil {
requestToken = cookies.Token
}
}
if requestToken == "" { if requestToken == "" {
keyFuncs := [...]KeyFunc{ server.RespondUnauthorized(w)
getBearer, return
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 ") requestToken = strings.TrimPrefix(requestToken, "Bearer ")
r = r.WithContext(context.WithValue(r.Context(), hashedToken, requestToken))
usr, err := a.services.User.GetSelf(r.Context(), requestToken) usr, err := a.services.User.GetSelf(r.Context(), requestToken)
// Check the database for the token
if err != nil {
if ent.IsNotFound(err) {
return validate.NewRequestError(errors.New("valid authorization token is required"), http.StatusUnauthorized)
}
return err // Check the database for the token
if err != nil {
server.RespondUnauthorized(w)
return
} }
r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken)) r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken))
return next.ServeHTTP(w, r)
next.ServeHTTP(w, r)
})
}
// mwAdminOnly is a middleware that extends the mwAuthToken middleware to only allow
// requests from superusers.
// func (a *app) mwAdminOnly(next http.Handler) http.Handler {
// mw := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// usr := services.UseUserCtx(r.Context())
// if !usr.IsSuperuser {
// server.RespondUnauthorized(w)
// return
// }
// next.ServeHTTP(w, r)
// })
// return a.mwAuthToken(mw)
// }
// mqStripTrailingSlash is a middleware that will strip trailing slashes from the request path.
func mwStripTrailingSlash(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
next.ServeHTTP(w, r)
})
}
type StatusRecorder struct {
http.ResponseWriter
Status int
}
func (r *StatusRecorder) WriteHeader(status int) {
r.Status = status
r.ResponseWriter.WriteHeader(status)
}
func (a *app) mwStructLogger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
record := &StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
next.ServeHTTP(record, r)
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto)
log.Info().
Str("id", middleware.GetReqID(r.Context())).
Str("url", url).
Str("method", r.Method).
Str("remote_addr", r.RemoteAddr).
Int("status", record.Status).
Msg(url)
})
}
func (a *app) mwSummaryLogger(next http.Handler) http.Handler {
bold := func(s string) string { return "\033[1m" + s + "\033[0m" }
orange := func(s string) string { return "\033[33m" + s + "\033[0m" }
aqua := func(s string) string { return "\033[36m" + s + "\033[0m" }
red := func(s string) string { return "\033[31m" + s + "\033[0m" }
green := func(s string) string { return "\033[32m" + s + "\033[0m" }
fmtCode := func(code int) string {
switch {
case code >= 500:
return red(fmt.Sprintf("%d", code))
case code >= 400:
return orange(fmt.Sprintf("%d", code))
case code >= 300:
return aqua(fmt.Sprintf("%d", code))
default:
return green(fmt.Sprintf("%d", code))
}
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
record := &StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
next.ServeHTTP(record, r) // Blocks until the next handler returns.
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto)
log.Info().
Msgf("%s %s %s",
bold(orange(""+r.Method+"")),
aqua(url),
bold(fmtCode(record.Status)),
)
}) })
} }

View file

@ -1,2 +0,0 @@
// Package providers provides a authentication abstraction for the backend.
package providers

View file

@ -1,55 +0,0 @@
package providers
import (
"errors"
"net/http"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
)
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
}

View file

@ -1,30 +0,0 @@
package providers
import (
"net/http"
"github.com/hay-kot/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)
}

View file

@ -3,6 +3,7 @@ package main
import ( import (
"embed" "embed"
"errors" "errors"
"fmt"
"io" "io"
"mime" "mime"
"net/http" "net/http"
@ -10,14 +11,11 @@ import (
"path/filepath" "path/filepath"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers" _ "github.com/hay-kot/homebox/backend/app/api/docs"
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1" v1 "github.com/hay-kot/homebox/backend/app/api/v1"
"github.com/hay-kot/homebox/backend/app/api/providers" "github.com/hay-kot/homebox/backend/internal/repo"
_ "github.com/hay-kot/homebox/backend/app/api/static/docs" "github.com/rs/zerolog/log"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles" httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/httpkit/errchain"
httpSwagger "github.com/swaggo/http-swagger/v2" // http-swagger middleware
) )
const prefix = "/api" const prefix = "/api"
@ -25,145 +23,100 @@ const prefix = "/api"
var ( var (
ErrDir = errors.New("path is dir") ErrDir = errors.New("path is dir")
//go:embed all:static/public/* //go:embed all:public/*
public embed.FS public embed.FS
) )
func (a *app) debugRouter() *http.ServeMux {
dbg := http.NewServeMux()
debughandlers.New(dbg)
return dbg
}
// registerRoutes registers all the routes for the API // registerRoutes registers all the routes for the API
func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllRepos) { func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux {
registerMimes() registerMimes()
r := chi.NewRouter()
a.setGlobalMiddleware(r)
r.Get("/swagger/*", httpSwagger.Handler( r.Get("/swagger/*", httpSwagger.Handler(
httpSwagger.URL("/swagger/doc.json"), httpSwagger.URL(fmt.Sprintf("%s://%s/swagger/doc.json", a.conf.Swagger.Scheme, a.conf.Swagger.Host)),
)) ))
// ========================================================================= // =========================================================================
// API Version 1 // API Version 1
v1Base := v1.BaseURLFunc(prefix) v1Base := v1.BaseUrlFunc(prefix)
v1Ctrl := v1.NewControllerV1(a.services, v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize))
{
r.Get(v1Base("/status"), v1Ctrl.HandleBase(func() bool { return true }, v1.Build{
Version: Version,
Commit: Commit,
BuildTime: BuildTime,
}))
v1Ctrl := v1.NewControllerV1( r.Post(v1Base("/users/register"), v1Ctrl.HandleUserRegistration())
a.services, r.Post(v1Base("/users/login"), v1Ctrl.HandleAuthLogin())
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
)
r.Get(v1Base("/status"), chain.ToHandlerFunc(v1Ctrl.HandleBase(func() bool { return true }, v1.Build{ // Attachment download URl needs a `token` query param to be passed in the request.
Version: version, // and also needs to be outside of the `auth` middleware.
Commit: commit, r.Get(v1Base("/items/{id}/attachments/download"), v1Ctrl.HandleItemAttachmentDownload())
BuildTime: buildTime,
})))
r.Get(v1Base("/currencies"), chain.ToHandlerFunc(v1Ctrl.HandleCurrency())) r.Group(func(r chi.Router) {
r.Use(a.mwAuthToken)
r.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf())
r.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate())
r.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete())
r.Put(v1Base("/users/self/password"), v1Ctrl.HandleUserUpdatePassword())
r.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout())
r.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh())
providers := []v1.AuthProvider{ r.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate())
providers.NewLocalProvider(a.services.User),
r.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll())
r.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate())
r.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet())
r.Put(v1Base("/locations/{id}"), v1Ctrl.HandleLocationUpdate())
r.Delete(v1Base("/locations/{id}"), v1Ctrl.HandleLocationDelete())
r.Get(v1Base("/labels"), v1Ctrl.HandleLabelsGetAll())
r.Post(v1Base("/labels"), v1Ctrl.HandleLabelsCreate())
r.Get(v1Base("/labels/{id}"), v1Ctrl.HandleLabelGet())
r.Put(v1Base("/labels/{id}"), v1Ctrl.HandleLabelUpdate())
r.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete())
r.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll())
r.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport())
r.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate())
r.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet())
r.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate())
r.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete())
r.Post(v1Base("/items/{id}/attachments"), v1Ctrl.HandleItemAttachmentCreate())
r.Get(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentToken())
r.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate())
r.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete())
})
} }
r.Post(v1Base("/users/register"), chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration())) r.NotFound(notFoundHandler())
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providers...))) return r
}
userMW := []errchain.Middleware{ // logRoutes logs the routes of the server that are registered within Server.registerRoutes(). This is useful for debugging.
a.mwAuthToken, // See https://github.com/go-chi/chi/issues/332 for details and inspiration.
a.mwRoles(RoleModeOr, authroles.RoleUser.String()), func (a *app) logRoutes(r *chi.Mux) {
desiredSpaces := 10
walkFunc := func(method string, route string, handler http.Handler, middleware ...func(http.Handler) http.Handler) error {
text := "[" + method + "]"
for len(text) < desiredSpaces {
text = text + " "
}
fmt.Printf("Registered Route: %s%s\n", text, route)
return nil
} }
r.Get(v1Base("/ws/events"), chain.ToHandlerFunc(v1Ctrl.HandleCacheWS(), userMW...)) if err := chi.Walk(r, walkFunc); err != nil {
r.Get(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelf(), userMW...)) fmt.Printf("Logging err: %s\n", err.Error())
r.Put(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfUpdate(), userMW...))
r.Delete(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfDelete(), userMW...))
r.Post(v1Base("/users/logout"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogout(), userMW...))
r.Get(v1Base("/users/refresh"), chain.ToHandlerFunc(v1Ctrl.HandleAuthRefresh(), userMW...))
r.Put(v1Base("/users/self/change-password"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfChangePassword(), userMW...))
r.Post(v1Base("/groups/invitations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupInvitationsCreate(), userMW...))
r.Get(v1Base("/groups/statistics"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatistics(), userMW...))
r.Get(v1Base("/groups/statistics/purchase-price"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatisticsPriceOverTime(), userMW...))
r.Get(v1Base("/groups/statistics/locations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatisticsLocations(), userMW...))
r.Get(v1Base("/groups/statistics/labels"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatisticsLabels(), userMW...))
// TODO: I don't like /groups being the URL for users
r.Get(v1Base("/groups"), chain.ToHandlerFunc(v1Ctrl.HandleGroupGet(), userMW...))
r.Put(v1Base("/groups"), chain.ToHandlerFunc(v1Ctrl.HandleGroupUpdate(), userMW...))
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...))
r.Get(v1Base("/locations/tree"), chain.ToHandlerFunc(v1Ctrl.HandleLocationTreeQuery(), userMW...))
r.Get(v1Base("/locations/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLocationGet(), userMW...))
r.Put(v1Base("/locations/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLocationUpdate(), userMW...))
r.Delete(v1Base("/locations/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLocationDelete(), userMW...))
r.Get(v1Base("/labels"), chain.ToHandlerFunc(v1Ctrl.HandleLabelsGetAll(), userMW...))
r.Post(v1Base("/labels"), chain.ToHandlerFunc(v1Ctrl.HandleLabelsCreate(), userMW...))
r.Get(v1Base("/labels/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLabelGet(), userMW...))
r.Put(v1Base("/labels/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLabelUpdate(), userMW...))
r.Delete(v1Base("/labels/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleLabelDelete(), userMW...))
r.Get(v1Base("/items"), chain.ToHandlerFunc(v1Ctrl.HandleItemsGetAll(), userMW...))
r.Post(v1Base("/items"), chain.ToHandlerFunc(v1Ctrl.HandleItemsCreate(), userMW...))
r.Post(v1Base("/items/import"), chain.ToHandlerFunc(v1Ctrl.HandleItemsImport(), userMW...))
r.Get(v1Base("/items/export"), chain.ToHandlerFunc(v1Ctrl.HandleItemsExport(), userMW...))
r.Get(v1Base("/items/fields"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldNames(), userMW...))
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...))
r.Put(v1Base("/items/{id}/attachments/{attachment_id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentUpdate(), userMW...))
r.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentDelete(), userMW...))
r.Get(v1Base("/items/{id}/maintenance"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceLogGet(), userMW...))
r.Post(v1Base("/items/{id}/maintenance"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryCreate(), userMW...))
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("/assets/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleAssetGet(), userMW...))
// Notifiers
r.Get(v1Base("/notifiers"), chain.ToHandlerFunc(v1Ctrl.HandleGetUserNotifiers(), userMW...))
r.Post(v1Base("/notifiers"), chain.ToHandlerFunc(v1Ctrl.HandleCreateNotifier(), userMW...))
r.Put(v1Base("/notifiers/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleUpdateNotifier(), userMW...))
r.Delete(v1Base("/notifiers/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleDeleteNotifier(), userMW...))
r.Post(v1Base("/notifiers/test"), chain.ToHandlerFunc(v1Ctrl.HandlerNotifierTest(), userMW...))
// Asset-Like endpoints
assetMW := []errchain.Middleware{
a.mwAuthToken,
a.mwRoles(RoleModeOr, authroles.RoleUser.String(), authroles.RoleAttachments.String()),
} }
r.Get(
v1Base("/qrcode"),
chain.ToHandlerFunc(v1Ctrl.HandleGenerateQRCode(), assetMW...),
)
r.Get(
v1Base("/items/{id}/attachments/{attachment_id}"),
chain.ToHandlerFunc(v1Ctrl.HandleItemAttachmentGet(), assetMW...),
)
// Reporting Services
r.Get(v1Base("/reporting/bill-of-materials"), chain.ToHandlerFunc(v1Ctrl.HandleBillOfMaterialsExport(), userMW...))
r.NotFound(chain.ToHandlerFunc(notFoundHandler()))
} }
func registerMimes() { func registerMimes() {
@ -180,13 +133,13 @@ func registerMimes() {
// notFoundHandler perform the main logic around handling the internal SPA embed and ensuring that // notFoundHandler perform the main logic around handling the internal SPA embed and ensuring that
// the client side routing is handled correctly. // the client side routing is handled correctly.
func notFoundHandler() errchain.HandlerFunc { func notFoundHandler() http.HandlerFunc {
tryRead := func(fs embed.FS, prefix, requestedPath string, w http.ResponseWriter) error { tryRead := func(fs embed.FS, prefix, requestedPath string, w http.ResponseWriter) error {
f, err := fs.Open(path.Join(prefix, requestedPath)) f, err := fs.Open(path.Join(prefix, requestedPath))
if err != nil { if err != nil {
return err return err
} }
defer func() { _ = f.Close() }() defer f.Close()
stat, _ := f.Stat() stat, _ := f.Stat()
if stat.IsDir() { if stat.IsDir() {
@ -199,16 +152,17 @@ func notFoundHandler() errchain.HandlerFunc {
return err return err
} }
return func(w http.ResponseWriter, r *http.Request) error { return func(w http.ResponseWriter, r *http.Request) {
err := tryRead(public, "static/public", r.URL.Path, w) err := tryRead(public, "public", r.URL.Path, w)
if err != nil { if err == nil {
// Fallback to the index.html file. return
// should succeed in all cases. }
err = tryRead(public, "static/public", "index.html", w) log.Debug().
if err != nil { Str("path", r.URL.Path).
return err Msg("served from embed not found - serving index.html")
} err = tryRead(public, "public", "index.html", w)
if err != nil {
panic(err)
} }
return nil
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
package v1
import (
"net/http"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
)
func WithMaxUploadSize(maxUploadSize int64) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.maxUploadSize = maxUploadSize
}
}
type V1Controller struct {
svc *services.AllServices
maxUploadSize int64
}
type (
Build struct {
Version string `json:"version"`
Commit string `json:"commit"`
BuildTime string `json:"buildTime"`
}
ApiSummary struct {
Healthy bool `json:"health"`
Versions []string `json:"versions"`
Title string `json:"title"`
Message string `json:"message"`
Build Build
}
)
func BaseUrlFunc(prefix string) func(s string) string {
v1Base := prefix + "/v1"
prefixFunc := func(s string) string {
return v1Base + s
}
return prefixFunc
}
func NewControllerV1(svc *services.AllServices, options ...func(*V1Controller)) *V1Controller {
ctrl := &V1Controller{
svc: svc,
}
return ctrl
}
type ReadyFunc func() bool
// HandleBase godoc
// @Summary Retrieves the basic information about the API
// @Tags Base
// @Produce json
// @Success 200 {object} ApiSummary
// @Router /v1/status [GET]
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
server.Respond(w, http.StatusOK, ApiSummary{
Healthy: ready(),
Title: "Go API Template",
Message: "Welcome to the Go API Template Application!",
Build: build,
})
}
}

View file

@ -0,0 +1,34 @@
package v1
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
/*
This is where we put partial snippets/functions for actions that are commonly
used within the controller class. This _hopefully_ helps with code duplication
and makes it a little more consistent when error handling and logging.
*/
// partialParseIdAndUser parses the ID from the requests URL and pulls the user
// from the context. If either of these fail, it will return an error. When an error
// occurs it will also write the error to the response. As such, if an error is returned
// from this function you can return immediately without writing to the response.
func (ctrl *V1Controller) partialParseIdAndUser(w http.ResponseWriter, r *http.Request) (uuid.UUID, *repo.UserOut, error) {
uid, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
log.Err(err).Msg("failed to parse id")
server.RespondError(w, http.StatusBadRequest, err)
return uuid.Nil, &repo.UserOut{}, err
}
user := services.UseUserCtx(r.Context())
return uid, user, nil
}

View file

@ -0,0 +1,133 @@
package v1
import (
"errors"
"net/http"
"time"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
type (
TokenResponse struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expiresAt"`
}
LoginForm struct {
Username string `json:"username"`
Password string `json:"password"`
}
)
// HandleAuthLogin godoc
// @Summary User Login
// @Tags Authentication
// @Accept x-www-form-urlencoded
// @Accept application/json
// @Param username formData string false "string" example(admin@admin.com)
// @Param password formData string false "string" example(admin)
// @Produce json
// @Success 200 {object} TokenResponse
// @Router /v1/users/login [POST]
func (ctrl *V1Controller) HandleAuthLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
loginForm := &LoginForm{}
if r.Header.Get("Content-Type") == server.ContentFormUrlEncoded {
err := r.ParseForm()
if err != nil {
server.Respond(w, http.StatusBadRequest, server.Wrap(err))
log.Error().Err(err).Msg("failed to parse form")
return
}
loginForm.Username = r.PostFormValue("username")
loginForm.Password = r.PostFormValue("password")
} else if r.Header.Get("Content-Type") == server.ContentJSON {
err := server.Decode(r, loginForm)
if err != nil {
log.Err(err).Msg("failed to decode login form")
server.Respond(w, http.StatusBadRequest, server.Wrap(err))
return
}
} else {
server.Respond(w, http.StatusBadRequest, errors.New("invalid content type"))
return
}
if loginForm.Username == "" || loginForm.Password == "" {
server.RespondError(w, http.StatusBadRequest, errors.New("username and password are required"))
return
}
newToken, err := ctrl.svc.User.Login(r.Context(), loginForm.Username, loginForm.Password)
if err != nil {
server.RespondError(w, http.StatusUnauthorized, err)
return
}
server.Respond(w, http.StatusOK, TokenResponse{
Token: "Bearer " + newToken.Raw,
ExpiresAt: newToken.ExpiresAt,
})
}
}
// HandleAuthLogout godoc
// @Summary User Logout
// @Tags Authentication
// @Success 204
// @Router /v1/users/logout [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthLogout() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := services.UseTokenCtx(r.Context())
if token == "" {
server.RespondError(w, http.StatusUnauthorized, errors.New("no token within request context"))
return
}
err := ctrl.svc.User.Logout(r.Context(), token)
if err != nil {
server.RespondError(w, http.StatusInternalServerError, err)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleAuthLogout godoc
// @Summary User Token Refresh
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.
// @Description This does not validate that the user still exists within the database.
// @Tags Authentication
// @Success 200
// @Router /v1/users/refresh [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleAuthRefresh() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
requestToken := services.UseTokenCtx(r.Context())
if requestToken == "" {
server.RespondError(w, http.StatusUnauthorized, errors.New("no user token found"))
return
}
newToken, err := ctrl.svc.User.RenewToken(r.Context(), requestToken)
if err != nil {
server.RespondUnauthorized(w)
return
}
server.Respond(w, http.StatusOK, newToken)
}
}

View file

@ -0,0 +1,62 @@
package v1
import (
"net/http"
"time"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
type (
GroupInvitationCreate struct {
Uses int `json:"uses"`
ExpiresAt time.Time `json:"expiresAt"`
}
GroupInvitation struct {
Token string `json:"token"`
ExpiresAt time.Time `json:"expiresAt"`
Uses int `json:"uses"`
}
)
// HandleUserSelf godoc
// @Summary Get the current user
// @Tags User
// @Produce json
// @Param payload body GroupInvitationCreate true "User Data"
// @Success 200 {object} GroupInvitation
// @Router /v1/groups/invitations [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleGroupInvitationsCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data := GroupInvitationCreate{}
if err := server.Decode(r, &data); err != nil {
log.Err(err).Msg("failed to decode user registration data")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
if data.ExpiresAt.IsZero() {
data.ExpiresAt = time.Now().Add(time.Hour * 24)
}
ctx := services.NewContext(r.Context())
token, err := ctrl.svc.User.NewInvitation(ctx, data.Uses, data.ExpiresAt)
if err != nil {
log.Err(err).Msg("failed to create new token")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
server.Respond(w, http.StatusCreated, GroupInvitation{
Token: token,
ExpiresAt: data.ExpiresAt,
Uses: data.Uses,
})
}
}

View file

@ -0,0 +1,189 @@
package v1
import (
"encoding/csv"
"net/http"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleItemsGetAll godoc
// @Summary Get All Items
// @Tags Items
// @Produce json
// @Success 200 {object} server.Results{items=[]repo.ItemSummary}
// @Router /v1/items [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user := services.UseUserCtx(r.Context())
items, err := ctrl.svc.Items.GetAll(r.Context(), user.GroupID)
if err != nil {
log.Err(err).Msg("failed to get items")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, server.Results{Items: items})
}
}
// HandleItemsCreate godoc
// @Summary Create a new item
// @Tags Items
// @Produce json
// @Param payload body repo.ItemCreate true "Item Data"
// @Success 200 {object} repo.ItemSummary
// @Router /v1/items [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
createData := repo.ItemCreate{}
if err := server.Decode(r, &createData); err != nil {
log.Err(err).Msg("failed to decode request body")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
user := services.UseUserCtx(r.Context())
item, err := ctrl.svc.Items.Create(r.Context(), user.GroupID, createData)
if err != nil {
log.Err(err).Msg("failed to create item")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusCreated, item)
}
}
// HandleItemDelete godocs
// @Summary deletes a item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 204
// @Router /v1/items/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
err = ctrl.svc.Items.Delete(r.Context(), user.GroupID, uid)
if err != nil {
log.Err(err).Msg("failed to delete item")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleItemGet godocs
// @Summary Gets a item and fields
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
items, err := ctrl.svc.Items.GetOne(r.Context(), user.GroupID, uid)
if err != nil {
log.Err(err).Msg("failed to get item")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, items)
}
}
// HandleItemUpdate godocs
// @Summary updates a item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Param payload body repo.ItemUpdate true "Item Data"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemUpdate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
body := repo.ItemUpdate{}
if err := server.Decode(r, &body); err != nil {
log.Err(err).Msg("failed to decode request body")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
body.ID = uid
result, err := ctrl.svc.Items.Update(r.Context(), user.GroupID, body)
if err != nil {
log.Err(err).Msg("failed to update item")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, result)
}
}
// HandleItemsImport godocs
// @Summary imports items into the database
// @Tags Items
// @Produce json
// @Success 204
// @Param csv formData file true "Image to upload"
// @Router /v1/items/import [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleItemsImport() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
server.RespondServerError(w)
return
}
file, _, err := r.FormFile("csv")
if err != nil {
log.Err(err).Msg("failed to get file from form")
server.RespondServerError(w)
return
}
reader := csv.NewReader(file)
data, err := reader.ReadAll()
if err != nil {
log.Err(err).Msg("failed to read csv")
server.RespondServerError(w)
return
}
user := services.UseUserCtx(r.Context())
err = ctrl.svc.Items.CsvImport(r.Context(), user.GroupID, data)
if err != nil {
log.Err(err).Msg("failed to import items")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}

View file

@ -0,0 +1,242 @@
package v1
import (
"errors"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
type (
ItemAttachmentToken struct {
Token string `json:"token"`
}
)
// HandleItemsImport godocs
// @Summary imports items into the database
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Param file formData file true "File attachment"
// @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} []server.ValidationError
// @Router /v1/items/{id}/attachments [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
server.RespondError(w, http.StatusBadRequest, errors.New("failed to parse multipart form"))
return
}
errs := make(server.ValidationErrors, 0)
file, _, err := r.FormFile("file")
if err != nil {
switch {
case errors.Is(err, http.ErrMissingFile):
log.Debug().Msg("file for attachment is missing")
errs = errs.Append("file", "file is required")
default:
log.Err(err).Msg("failed to get file from form")
server.RespondServerError(w)
return
}
}
attachmentName := r.FormValue("name")
if attachmentName == "" {
log.Debug().Msg("failed to get name from form")
errs = errs.Append("name", "name is required")
}
if errs.HasErrors() {
server.Respond(w, http.StatusUnprocessableEntity, errs)
return
}
attachmentType := r.FormValue("type")
if attachmentType == "" {
attachmentType = attachment.TypeAttachment.String()
}
id, _, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
ctx := services.NewContext(r.Context())
item, err := ctrl.svc.Items.AttachmentAdd(
ctx,
id,
attachmentName,
attachment.Type(attachmentType),
file,
)
if err != nil {
log.Err(err).Msg("failed to add attachment")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusCreated, item)
}
}
// HandleItemAttachmentGet godocs
// @Summary retrieves an attachment for an item
// @Tags Items
// @Produce application/octet-stream
// @Param id path string true "Item ID"
// @Param token query string true "Attachment token"
// @Success 200
// @Router /v1/items/{id}/attachments/download [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDownload() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := server.GetParam(r, "token", "")
doc, err := ctrl.svc.Items.AttachmentPath(r.Context(), token)
if err != nil {
log.Err(err).Msg("failed to get attachment")
server.RespondServerError(w)
return
}
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", doc.Title))
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeFile(w, r, doc.Path)
}
}
// HandleItemAttachmentToken godocs
// @Summary retrieves an attachment for an item
// @Tags Items
// @Produce application/octet-stream
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 200 {object} ItemAttachmentToken
// @Router /v1/items/{id}/attachments/{attachment_id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentToken() http.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
// HandleItemAttachmentDelete godocs
// @Summary retrieves an attachment for an item
// @Tags Items
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Success 204
// @Router /v1/items/{id}/attachments/{attachment_id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDelete() http.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
// HandleItemAttachmentUpdate godocs
// @Summary retrieves an attachment for an item
// @Tags Items
// @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID"
// @Param payload body repo.ItemAttachmentUpdate true "Attachment Update"
// @Success 200 {object} repo.ItemOut
// @Router /v1/items/{id}/attachments/{attachment_id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentUpdate() http.HandlerFunc {
return ctrl.handleItemAttachmentsHandler
}
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
attachmentId, err := uuid.Parse(chi.URLParam(r, "attachment_id"))
if err != nil {
log.Err(err).Msg("failed to parse attachment_id param")
server.RespondError(w, http.StatusBadRequest, err)
return
}
ctx := services.NewContext(r.Context())
switch r.Method {
// Token Handler
case http.MethodGet:
token, err := ctrl.svc.Items.AttachmentToken(ctx, uid, attachmentId)
if err != nil {
switch err {
case services.ErrNotFound:
log.Err(err).
Str("id", attachmentId.String()).
Msg("failed to find attachment with id")
server.RespondError(w, http.StatusNotFound, err)
case services.ErrFileNotFound:
log.Err(err).
Str("id", attachmentId.String()).
Msg("failed to find file path for attachment with id")
log.Warn().Msg("attachment with no file path removed from database")
server.RespondError(w, http.StatusNotFound, err)
default:
log.Err(err).Msg("failed to get attachment")
server.RespondServerError(w)
return
}
}
server.Respond(w, http.StatusOK, ItemAttachmentToken{Token: token})
// Delete Attachment Handler
case http.MethodDelete:
err = ctrl.svc.Items.AttachmentDelete(r.Context(), user.GroupID, uid, attachmentId)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusNoContent, nil)
// Update Attachment Handler
case http.MethodPut:
var attachment repo.ItemAttachmentUpdate
err = server.Decode(r, &attachment)
if err != nil {
log.Err(err).Msg("failed to decode attachment")
server.RespondError(w, http.StatusBadRequest, err)
return
}
attachment.ID = attachmentId
val, err := ctrl.svc.Items.AttachmentUpdate(ctx, uid, &attachment)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, val)
}
}

View file

@ -0,0 +1,150 @@
package v1
import (
"net/http"
"github.com/hay-kot/homebox/backend/ent"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleLabelsGetAll godoc
// @Summary Get All Labels
// @Tags Labels
// @Produce json
// @Success 200 {object} server.Results{items=[]repo.LabelOut}
// @Router /v1/labels [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsGetAll() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user := services.UseUserCtx(r.Context())
labels, err := ctrl.svc.Labels.GetAll(r.Context(), user.GroupID)
if err != nil {
log.Err(err).Msg("error getting labels")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, server.Results{Items: labels})
}
}
// HandleLabelsCreate godoc
// @Summary Create a new label
// @Tags Labels
// @Produce json
// @Param payload body repo.LabelCreate true "Label Data"
// @Success 200 {object} repo.LabelSummary
// @Router /v1/labels [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
createData := repo.LabelCreate{}
if err := server.Decode(r, &createData); err != nil {
log.Err(err).Msg("error decoding label create data")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
user := services.UseUserCtx(r.Context())
label, err := ctrl.svc.Labels.Create(r.Context(), user.GroupID, createData)
if err != nil {
log.Err(err).Msg("error creating label")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusCreated, label)
}
}
// HandleLabelDelete godocs
// @Summary deletes a label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 204
// @Router /v1/labels/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
err = ctrl.svc.Labels.Delete(r.Context(), user.GroupID, uid)
if err != nil {
log.Err(err).Msg("error deleting label")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleLabelGet godocs
// @Summary Gets a label and fields
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
labels, err := ctrl.svc.Labels.Get(r.Context(), user.GroupID, uid)
if err != nil {
if ent.IsNotFound(err) {
log.Err(err).
Str("id", uid.String()).
Msg("label not found")
server.RespondError(w, http.StatusNotFound, err)
return
}
log.Err(err).Msg("error getting label")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, labels)
}
}
// HandleLabelUpdate godocs
// @Summary updates a label
// @Tags Labels
// @Produce json
// @Param id path string true "Label ID"
// @Success 200 {object} repo.LabelOut
// @Router /v1/labels/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLabelUpdate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
body := repo.LabelUpdate{}
if err := server.Decode(r, &body); err != nil {
log.Err(err).Msg("error decoding label update data")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
body.ID = uid
result, err := ctrl.svc.Labels.Update(r.Context(), user.GroupID, body)
if err != nil {
log.Err(err).Msg("error updating label")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, result)
}
}

View file

@ -0,0 +1,157 @@
package v1
import (
"net/http"
"github.com/hay-kot/homebox/backend/ent"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleLocationGetAll godoc
// @Summary Get All Locations
// @Tags Locations
// @Produce json
// @Success 200 {object} server.Results{items=[]repo.LocationOutCount}
// @Router /v1/locations [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationGetAll() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user := services.UseUserCtx(r.Context())
locations, err := ctrl.svc.Location.GetAll(r.Context(), user.GroupID)
if err != nil {
log.Err(err).Msg("failed to get locations")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, server.Results{Items: locations})
}
}
// HandleLocationCreate godoc
// @Summary Create a new location
// @Tags Locations
// @Produce json
// @Param payload body repo.LocationCreate true "Location Data"
// @Success 200 {object} repo.LocationSummary
// @Router /v1/locations [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
createData := repo.LocationCreate{}
if err := server.Decode(r, &createData); err != nil {
log.Err(err).Msg("failed to decode location create data")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
user := services.UseUserCtx(r.Context())
location, err := ctrl.svc.Location.Create(r.Context(), user.GroupID, createData)
if err != nil {
log.Err(err).Msg("failed to create location")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusCreated, location)
}
}
// HandleLocationDelete godocs
// @Summary deletes a location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 204
// @Router /v1/locations/{id} [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
err = ctrl.svc.Location.Delete(r.Context(), user.GroupID, uid)
if err != nil {
log.Err(err).Msg("failed to delete location")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleLocationGet godocs
// @Summary Gets a location and fields
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
location, err := ctrl.svc.Location.GetOne(r.Context(), user.GroupID, uid)
if err != nil {
if ent.IsNotFound(err) {
log.Err(err).
Str("id", uid.String()).
Str("gid", user.GroupID.String()).
Msg("location not found")
server.RespondError(w, http.StatusNotFound, err)
return
}
log.Err(err).
Str("id", uid.String()).
Str("gid", user.GroupID.String()).
Msg("failed to get location")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, location)
}
}
// HandleLocationUpdate godocs
// @Summary updates a location
// @Tags Locations
// @Produce json
// @Param id path string true "Location ID"
// @Success 200 {object} repo.LocationOut
// @Router /v1/locations/{id} [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
body := repo.LocationUpdate{}
if err := server.Decode(r, &body); err != nil {
log.Err(err).Msg("failed to decode location update data")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
body.ID = uid
result, err := ctrl.svc.Location.Update(r.Context(), user.GroupID, body)
if err != nil {
log.Err(err).Msg("failed to update location")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, result)
}
}

View file

@ -0,0 +1,121 @@
package v1
import (
"net/http"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/repo"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/pkgs/server"
"github.com/rs/zerolog/log"
)
// HandleUserSelf godoc
// @Summary Get the current user
// @Tags User
// @Produce json
// @Param payload body services.UserRegistration true "User Data"
// @Success 204
// @Router /v1/users/register [Post]
func (ctrl *V1Controller) HandleUserRegistration() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
regData := services.UserRegistration{}
if err := server.Decode(r, &regData); err != nil {
log.Err(err).Msg("failed to decode user registration data")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
_, err := ctrl.svc.User.RegisterUser(r.Context(), regData)
if err != nil {
log.Err(err).Msg("failed to register user")
server.RespondError(w, http.StatusInternalServerError, err)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleUserSelf godoc
// @Summary Get the current user
// @Tags User
// @Produce json
// @Success 200 {object} server.Result{item=repo.UserOut}
// @Router /v1/users/self [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelf() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token := services.UseTokenCtx(r.Context())
usr, err := ctrl.svc.User.GetSelf(r.Context(), token)
if usr.ID == uuid.Nil || err != nil {
log.Err(err).Msg("failed to get user")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, server.Wrap(usr))
}
}
// HandleUserSelfUpdate godoc
// @Summary Update the current user
// @Tags User
// @Produce json
// @Param payload body repo.UserUpdate true "User Data"
// @Success 200 {object} server.Result{item=repo.UserUpdate}
// @Router /v1/users/self [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfUpdate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateData := repo.UserUpdate{}
if err := server.Decode(r, &updateData); err != nil {
log.Err(err).Msg("failed to decode user update data")
server.RespondError(w, http.StatusBadRequest, err)
return
}
actor := services.UseUserCtx(r.Context())
newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData)
if err != nil {
server.RespondError(w, http.StatusInternalServerError, err)
return
}
server.Respond(w, http.StatusOK, server.Wrap(newData))
}
}
// HandleUserUpdatePassword godoc
// @Summary Update the current user's password // TODO:
// @Tags User
// @Produce json
// @Success 204
// @Router /v1/users/self/password [PUT]
// @Security Bearer
func (ctrl *V1Controller) HandleUserUpdatePassword() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
}
}
// HandleUserSelfDelete godoc
// @Summary Deletes the user account
// @Tags User
// @Produce json
// @Success 204
// @Router /v1/users/self [DELETE]
// @Security Bearer
func (ctrl *V1Controller) HandleUserSelfDelete() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
actor := services.UseUserCtx(r.Context())
if err := ctrl.svc.User.DeleteSelf(r.Context(), actor.ID); err != nil {
server.RespondError(w, http.StatusInternalServerError, err)
return
}
server.Respond(w, http.StatusNoContent, nil)
}
}

View file

@ -2,11 +2,10 @@ package main
import ( import (
"context" "context"
"fmt"
"log" "log"
"os" "os"
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate" "github.com/hay-kot/homebox/backend/ent/migrate"
atlas "ariga.io/atlas/sql/migrate" atlas "ariga.io/atlas/sql/migrate"
_ "ariga.io/atlas/sql/sqlite" _ "ariga.io/atlas/sql/sqlite"
@ -18,7 +17,7 @@ import (
func main() { func main() {
ctx := context.Background() ctx := context.Background()
// Create a local migration directory able to understand Atlas migration file format for replay. // Create a local migration directory able to understand Atlas migration file format for replay.
dir, err := atlas.NewLocalDir("internal/data/migrations/migrations") dir, err := atlas.NewLocalDir("internal/migrations/migrations")
if err != nil { if err != nil {
log.Fatalf("failed creating atlas migration directory: %v", err) log.Fatalf("failed creating atlas migration directory: %v", err)
} }
@ -40,6 +39,4 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("failed generating migration file: %v", err) log.Fatalf("failed generating migration file: %v", err)
} }
fmt.Println("Migration file generated successfully.")
} }

View file

@ -1,81 +0,0 @@
package main
import (
"fmt"
"os"
"regexp"
)
type ReReplace struct {
Regex *regexp.Regexp
Text string
}
func NewReReplace(regex string, replace string) ReReplace {
return ReReplace{
Regex: regexp.MustCompile(regex),
Text: replace,
}
}
func NewReDate(dateStr string) ReReplace {
return ReReplace{
Regex: regexp.MustCompile(fmt.Sprintf(`%s: string`, dateStr)),
Text: fmt.Sprintf(`%s: Date | string`, dateStr),
}
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Please provide a file path as an argument")
os.Exit(1)
}
path := os.Args[1]
fmt.Printf("Processing %s\n", path)
if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("File %s does not exist\n", path)
os.Exit(1)
}
text := "/* post-processed by ./scripts/process-types.go */\n"
data, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
text += string(data)
replaces := [...]ReReplace{
NewReReplace(` Repo`, " "),
NewReReplace(` PaginationResultRepo`, " PaginationResult"),
NewReReplace(` Services`, " "),
NewReReplace(` V1`, " "),
NewReReplace(`\?:`, ":"),
NewReReplace(`(\w+):\s(.*null.*)`, "$1?: $2"), // make null union types optional
NewReDate("createdAt"),
NewReDate("updatedAt"),
NewReDate("soldTime"),
NewReDate("purchaseTime"),
NewReDate("warrantyExpires"),
NewReDate("expiresAt"),
NewReDate("date"),
NewReDate("completedDate"),
NewReDate("scheduledDate"),
}
for _, replace := range replaces {
fmt.Printf("Replacing '%v' -> '%s'\n", replace.Regex, replace.Text)
text = replace.Regex.ReplaceAllString(text, replace.Text)
}
err = os.WriteFile(path, []byte(text), 0644)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(0)
}

View file

@ -7,12 +7,11 @@ import (
"strings" "strings"
"time" "time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/ent/item"
) )
// Attachment is the model entity for the Attachment schema. // Attachment is the model entity for the Attachment schema.
@ -26,14 +25,11 @@ type Attachment struct {
UpdatedAt time.Time `json:"updated_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"`
// Type holds the value of the "type" field. // Type holds the value of the "type" field.
Type attachment.Type `json:"type,omitempty"` 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. // Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the AttachmentQuery when eager-loading is set. // The values are being populated by the AttachmentQuery when eager-loading is set.
Edges AttachmentEdges `json:"edges"` Edges AttachmentEdges `json:"edges"`
document_attachments *uuid.UUID document_attachments *uuid.UUID
item_attachments *uuid.UUID item_attachments *uuid.UUID
selectValues sql.SelectValues
} }
// AttachmentEdges holds the relations/edges for other nodes in the graph. // AttachmentEdges holds the relations/edges for other nodes in the graph.
@ -78,8 +74,6 @@ func (*Attachment) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns)) values := make([]any, len(columns))
for i := range columns { for i := range columns {
switch columns[i] { switch columns[i] {
case attachment.FieldPrimary:
values[i] = new(sql.NullBool)
case attachment.FieldType: case attachment.FieldType:
values[i] = new(sql.NullString) values[i] = new(sql.NullString)
case attachment.FieldCreatedAt, attachment.FieldUpdatedAt: case attachment.FieldCreatedAt, attachment.FieldUpdatedAt:
@ -91,7 +85,7 @@ func (*Attachment) scanValues(columns []string) ([]any, error) {
case attachment.ForeignKeys[1]: // item_attachments case attachment.ForeignKeys[1]: // item_attachments
values[i] = &sql.NullScanner{S: new(uuid.UUID)} values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default: default:
values[i] = new(sql.UnknownType) return nil, fmt.Errorf("unexpected column %q for type Attachment", columns[i])
} }
} }
return values, nil return values, nil
@ -129,12 +123,6 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
} else if value.Valid { } else if value.Valid {
a.Type = attachment.Type(value.String) 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]: case attachment.ForeignKeys[0]:
if value, ok := values[i].(*sql.NullScanner); !ok { if value, ok := values[i].(*sql.NullScanner); !ok {
return fmt.Errorf("unexpected type %T for field document_attachments", values[i]) return fmt.Errorf("unexpected type %T for field document_attachments", values[i])
@ -149,34 +137,26 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
a.item_attachments = new(uuid.UUID) a.item_attachments = new(uuid.UUID)
*a.item_attachments = *value.S.(*uuid.UUID) *a.item_attachments = *value.S.(*uuid.UUID)
} }
default:
a.selectValues.Set(columns[i], values[i])
} }
} }
return nil 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. // QueryItem queries the "item" edge of the Attachment entity.
func (a *Attachment) QueryItem() *ItemQuery { func (a *Attachment) QueryItem() *ItemQuery {
return NewAttachmentClient(a.config).QueryItem(a) return (&AttachmentClient{config: a.config}).QueryItem(a)
} }
// QueryDocument queries the "document" edge of the Attachment entity. // QueryDocument queries the "document" edge of the Attachment entity.
func (a *Attachment) QueryDocument() *DocumentQuery { func (a *Attachment) QueryDocument() *DocumentQuery {
return NewAttachmentClient(a.config).QueryDocument(a) return (&AttachmentClient{config: a.config}).QueryDocument(a)
} }
// Update returns a builder for updating this Attachment. // Update returns a builder for updating this Attachment.
// Note that you need to call Attachment.Unwrap() before calling this method if this Attachment // Note that you need to call Attachment.Unwrap() before calling this method if this Attachment
// was returned from a transaction, and the transaction was committed or rolled back. // was returned from a transaction, and the transaction was committed or rolled back.
func (a *Attachment) Update() *AttachmentUpdateOne { func (a *Attachment) Update() *AttachmentUpdateOne {
return NewAttachmentClient(a.config).UpdateOne(a) return (&AttachmentClient{config: a.config}).UpdateOne(a)
} }
// Unwrap unwraps the Attachment entity that was returned from a transaction after it was closed, // Unwrap unwraps the Attachment entity that was returned from a transaction after it was closed,
@ -203,12 +183,15 @@ func (a *Attachment) String() string {
builder.WriteString(", ") builder.WriteString(", ")
builder.WriteString("type=") builder.WriteString("type=")
builder.WriteString(fmt.Sprintf("%v", a.Type)) builder.WriteString(fmt.Sprintf("%v", a.Type))
builder.WriteString(", ")
builder.WriteString("primary=")
builder.WriteString(fmt.Sprintf("%v", a.Primary))
builder.WriteByte(')') builder.WriteByte(')')
return builder.String() return builder.String()
} }
// Attachments is a parsable slice of Attachment. // Attachments is a parsable slice of Attachment.
type Attachments []*Attachment type Attachments []*Attachment
func (a Attachments) config(cfg config) {
for _i := range a {
a[_i].config = cfg
}
}

View file

@ -6,8 +6,6 @@ import (
"fmt" "fmt"
"time" "time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -22,8 +20,6 @@ const (
FieldUpdatedAt = "updated_at" FieldUpdatedAt = "updated_at"
// FieldType holds the string denoting the type field in the database. // FieldType holds the string denoting the type field in the database.
FieldType = "type" 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 holds the string denoting the item edge name in mutations.
EdgeItem = "item" EdgeItem = "item"
// EdgeDocument holds the string denoting the document edge name in mutations. // EdgeDocument holds the string denoting the document edge name in mutations.
@ -52,7 +48,6 @@ var Columns = []string{
FieldCreatedAt, FieldCreatedAt,
FieldUpdatedAt, FieldUpdatedAt,
FieldType, FieldType,
FieldPrimary,
} }
// ForeignKeys holds the SQL foreign-keys that are owned by the "attachments" // ForeignKeys holds the SQL foreign-keys that are owned by the "attachments"
@ -84,8 +79,6 @@ var (
DefaultUpdatedAt func() time.Time DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time 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 holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID DefaultID func() uuid.UUID
) )
@ -102,7 +95,6 @@ const (
TypeManual Type = "manual" TypeManual Type = "manual"
TypeWarranty Type = "warranty" TypeWarranty Type = "warranty"
TypeAttachment Type = "attachment" TypeAttachment Type = "attachment"
TypeReceipt Type = "receipt"
) )
func (_type Type) String() string { func (_type Type) String() string {
@ -112,65 +104,9 @@ func (_type Type) String() string {
// TypeValidator is a validator for the "type" field enum values. It is called by the builders before save. // TypeValidator is a validator for the "type" field enum values. It is called by the builders before save.
func TypeValidator(_type Type) error { func TypeValidator(_type Type) error {
switch _type { switch _type {
case TypePhoto, TypeManual, TypeWarranty, TypeAttachment, TypeReceipt: case TypePhoto, TypeManual, TypeWarranty, TypeAttachment:
return nil return nil
default: default:
return fmt.Errorf("attachment: invalid enum value for type field: %q", _type) 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),
)
}

View file

@ -8,177 +8,256 @@ import (
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// ID filters vertices based on their ID field. // ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.Attachment { func ID(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDEQ applies the EQ predicate on the ID field. // IDEQ applies the EQ predicate on the ID field.
func IDEQ(id uuid.UUID) predicate.Attachment { func IDEQ(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDNEQ applies the NEQ predicate on the ID field. // IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id uuid.UUID) predicate.Attachment { func IDNEQ(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldID), id))
})
} }
// IDIn applies the In predicate on the ID field. // IDIn applies the In predicate on the ID field.
func IDIn(ids ...uuid.UUID) predicate.Attachment { func IDIn(ids ...uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldIn(FieldID, ids...)) return predicate.Attachment(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
})
} }
// IDNotIn applies the NotIn predicate on the ID field. // IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...uuid.UUID) predicate.Attachment { func IDNotIn(ids ...uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldID, ids...)) return predicate.Attachment(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
})
} }
// IDGT applies the GT predicate on the ID field. // IDGT applies the GT predicate on the ID field.
func IDGT(id uuid.UUID) predicate.Attachment { func IDGT(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldGT(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldID), id))
})
} }
// IDGTE applies the GTE predicate on the ID field. // IDGTE applies the GTE predicate on the ID field.
func IDGTE(id uuid.UUID) predicate.Attachment { func IDGTE(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldGTE(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldID), id))
})
} }
// IDLT applies the LT predicate on the ID field. // IDLT applies the LT predicate on the ID field.
func IDLT(id uuid.UUID) predicate.Attachment { func IDLT(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldLT(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldID), id))
})
} }
// IDLTE applies the LTE predicate on the ID field. // IDLTE applies the LTE predicate on the ID field.
func IDLTE(id uuid.UUID) predicate.Attachment { func IDLTE(id uuid.UUID) predicate.Attachment {
return predicate.Attachment(sql.FieldLTE(FieldID, id)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldID), id))
})
} }
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.Attachment { func CreatedAt(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. // UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.Attachment { func UpdatedAt(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
} s.Where(sql.EQ(s.C(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. // CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Attachment { func CreatedAtEQ(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. // CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.Attachment { func CreatedAtNEQ(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtIn applies the In predicate on the "created_at" field. // CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.Attachment { func CreatedAtIn(vs ...time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldIn(FieldCreatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. // CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.Attachment { func CreatedAtNotIn(vs ...time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldCreatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtGT applies the GT predicate on the "created_at" field. // CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.Attachment { func CreatedAtGT(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldGT(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtGTE applies the GTE predicate on the "created_at" field. // CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.Attachment { func CreatedAtGTE(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldGTE(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLT applies the LT predicate on the "created_at" field. // CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.Attachment { func CreatedAtLT(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldLT(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLTE applies the LTE predicate on the "created_at" field. // CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.Attachment { func CreatedAtLTE(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldLTE(FieldCreatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
})
} }
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. // UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.Attachment { func UpdatedAtEQ(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. // UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.Attachment { func UpdatedAtNEQ(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtIn applies the In predicate on the "updated_at" field. // UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.Attachment { func UpdatedAtIn(vs ...time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldIn(FieldUpdatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldUpdatedAt), v...))
})
} }
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. // UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.Attachment { func UpdatedAtNotIn(vs ...time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldUpdatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldUpdatedAt), v...))
})
} }
// UpdatedAtGT applies the GT predicate on the "updated_at" field. // UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.Attachment { func UpdatedAtGT(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldGT(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. // UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.Attachment { func UpdatedAtGTE(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldGTE(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtLT applies the LT predicate on the "updated_at" field. // UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.Attachment { func UpdatedAtLT(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldLT(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. // UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.Attachment { func UpdatedAtLTE(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldLTE(FieldUpdatedAt, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldUpdatedAt), v))
})
} }
// TypeEQ applies the EQ predicate on the "type" field. // TypeEQ applies the EQ predicate on the "type" field.
func TypeEQ(v Type) predicate.Attachment { func TypeEQ(v Type) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldType, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldType), v))
})
} }
// TypeNEQ applies the NEQ predicate on the "type" field. // TypeNEQ applies the NEQ predicate on the "type" field.
func TypeNEQ(v Type) predicate.Attachment { func TypeNEQ(v Type) predicate.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldType, v)) return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldType), v))
})
} }
// TypeIn applies the In predicate on the "type" field. // TypeIn applies the In predicate on the "type" field.
func TypeIn(vs ...Type) predicate.Attachment { func TypeIn(vs ...Type) predicate.Attachment {
return predicate.Attachment(sql.FieldIn(FieldType, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldType), v...))
})
} }
// TypeNotIn applies the NotIn predicate on the "type" field. // TypeNotIn applies the NotIn predicate on the "type" field.
func TypeNotIn(vs ...Type) predicate.Attachment { func TypeNotIn(vs ...Type) predicate.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldType, vs...)) v := make([]any, len(vs))
} for i := range v {
v[i] = vs[i]
// PrimaryEQ applies the EQ predicate on the "primary" field. }
func PrimaryEQ(v bool) predicate.Attachment { return predicate.Attachment(func(s *sql.Selector) {
return predicate.Attachment(sql.FieldEQ(FieldPrimary, v)) s.Where(sql.NotIn(s.C(FieldType), 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. // HasItem applies the HasEdge predicate on the "item" edge.
@ -186,6 +265,7 @@ func HasItem() predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) { return predicate.Attachment(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn), sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -195,7 +275,11 @@ func HasItem() predicate.Attachment {
// HasItemWith applies the HasEdge predicate on the "item" edge with a given conditions (other predicates). // HasItemWith applies the HasEdge predicate on the "item" edge with a given conditions (other predicates).
func HasItemWith(preds ...predicate.Item) predicate.Attachment { func HasItemWith(preds ...predicate.Item) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) { return predicate.Attachment(func(s *sql.Selector) {
step := newItemStep() step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -209,6 +293,7 @@ func HasDocument() predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) { return predicate.Attachment(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn), sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -218,7 +303,11 @@ func HasDocument() predicate.Attachment {
// HasDocumentWith applies the HasEdge predicate on the "document" edge with a given conditions (other predicates). // HasDocumentWith applies the HasEdge predicate on the "document" edge with a given conditions (other predicates).
func HasDocumentWith(preds ...predicate.Document) predicate.Attachment { func HasDocumentWith(preds ...predicate.Document) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) { return predicate.Attachment(func(s *sql.Selector) {
step := newDocumentStep() step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -229,15 +318,32 @@ func HasDocumentWith(preds ...predicate.Document) predicate.Attachment {
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.Attachment) predicate.Attachment { func And(predicates ...predicate.Attachment) predicate.Attachment {
return predicate.Attachment(sql.AndPredicates(predicates...)) return predicate.Attachment(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
} }
// Or groups predicates with the OR operator between them. // Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.Attachment) predicate.Attachment { func Or(predicates ...predicate.Attachment) predicate.Attachment {
return predicate.Attachment(sql.OrPredicates(predicates...)) 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())
})
} }
// Not applies the not operator on the given predicate. // Not applies the not operator on the given predicate.
func Not(p predicate.Attachment) predicate.Attachment { func Not(p predicate.Attachment) predicate.Attachment {
return predicate.Attachment(sql.NotPredicates(p)) return predicate.Attachment(func(s *sql.Selector) {
p(s.Not())
})
} }

View file

@ -11,9 +11,9 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/ent/item"
) )
// AttachmentCreate is the builder for creating a Attachment entity. // AttachmentCreate is the builder for creating a Attachment entity.
@ -65,20 +65,6 @@ func (ac *AttachmentCreate) SetNillableType(a *attachment.Type) *AttachmentCreat
return ac 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. // SetID sets the "id" field.
func (ac *AttachmentCreate) SetID(u uuid.UUID) *AttachmentCreate { func (ac *AttachmentCreate) SetID(u uuid.UUID) *AttachmentCreate {
ac.mutation.SetID(u) ac.mutation.SetID(u)
@ -122,8 +108,50 @@ func (ac *AttachmentCreate) Mutation() *AttachmentMutation {
// Save creates the Attachment in the database. // Save creates the Attachment in the database.
func (ac *AttachmentCreate) Save(ctx context.Context) (*Attachment, error) { func (ac *AttachmentCreate) Save(ctx context.Context) (*Attachment, error) {
var (
err error
node *Attachment
)
ac.defaults() ac.defaults()
return withHooks(ctx, ac.sqlSave, ac.mutation, ac.hooks) if len(ac.hooks) == 0 {
if err = ac.check(); err != nil {
return nil, err
}
node, err = ac.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = ac.check(); err != nil {
return nil, err
}
ac.mutation = mutation
if node, err = ac.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(ac.hooks) - 1; i >= 0; i-- {
if ac.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ac.hooks[i](mut)
}
v, err := mut.Mutate(ctx, ac.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Attachment)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AttachmentMutation", v)
}
node = nv
}
return node, err
} }
// SaveX calls Save and panics if Save returns an error. // SaveX calls Save and panics if Save returns an error.
@ -162,10 +190,6 @@ func (ac *AttachmentCreate) defaults() {
v := attachment.DefaultType v := attachment.DefaultType
ac.mutation.SetType(v) ac.mutation.SetType(v)
} }
if _, ok := ac.mutation.Primary(); !ok {
v := attachment.DefaultPrimary
ac.mutation.SetPrimary(v)
}
if _, ok := ac.mutation.ID(); !ok { if _, ok := ac.mutation.ID(); !ok {
v := attachment.DefaultID() v := attachment.DefaultID()
ac.mutation.SetID(v) ac.mutation.SetID(v)
@ -188,9 +212,6 @@ func (ac *AttachmentCreate) check() error {
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "Attachment.type": %w`, err)} 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 { if _, ok := ac.mutation.ItemID(); !ok {
return &ValidationError{Name: "item", err: errors.New(`ent: missing required edge "Attachment.item"`)} return &ValidationError{Name: "item", err: errors.New(`ent: missing required edge "Attachment.item"`)}
} }
@ -201,9 +222,6 @@ func (ac *AttachmentCreate) check() error {
} }
func (ac *AttachmentCreate) sqlSave(ctx context.Context) (*Attachment, error) { func (ac *AttachmentCreate) sqlSave(ctx context.Context) (*Attachment, error) {
if err := ac.check(); err != nil {
return nil, err
}
_node, _spec := ac.createSpec() _node, _spec := ac.createSpec()
if err := sqlgraph.CreateNode(ctx, ac.driver, _spec); err != nil { if err := sqlgraph.CreateNode(ctx, ac.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) { if sqlgraph.IsConstraintError(err) {
@ -218,36 +236,48 @@ func (ac *AttachmentCreate) sqlSave(ctx context.Context) (*Attachment, error) {
return nil, err return nil, err
} }
} }
ac.mutation.id = &_node.ID
ac.mutation.done = true
return _node, nil return _node, nil
} }
func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) { func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
var ( var (
_node = &Attachment{config: ac.config} _node = &Attachment{config: ac.config}
_spec = sqlgraph.NewCreateSpec(attachment.Table, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID)) _spec = &sqlgraph.CreateSpec{
Table: attachment.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}
) )
if id, ok := ac.mutation.ID(); ok { if id, ok := ac.mutation.ID(); ok {
_node.ID = id _node.ID = id
_spec.ID.Value = &id _spec.ID.Value = &id
} }
if value, ok := ac.mutation.CreatedAt(); ok { if value, ok := ac.mutation.CreatedAt(); ok {
_spec.SetField(attachment.FieldCreatedAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: attachment.FieldCreatedAt,
})
_node.CreatedAt = value _node.CreatedAt = value
} }
if value, ok := ac.mutation.UpdatedAt(); ok { if value, ok := ac.mutation.UpdatedAt(); ok {
_spec.SetField(attachment.FieldUpdatedAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: attachment.FieldUpdatedAt,
})
_node.UpdatedAt = value _node.UpdatedAt = value
} }
if value, ok := ac.mutation.GetType(); ok { if value, ok := ac.mutation.GetType(); ok {
_spec.SetField(attachment.FieldType, field.TypeEnum, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeEnum,
Value: value,
Column: attachment.FieldType,
})
_node.Type = 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 { if nodes := ac.mutation.ItemIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O, Rel: sqlgraph.M2O,
@ -256,7 +286,10 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
Columns: []string{attachment.ItemColumn}, Columns: []string{attachment.ItemColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -273,7 +306,10 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
Columns: []string{attachment.DocumentColumn}, Columns: []string{attachment.DocumentColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -288,15 +324,11 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
// AttachmentCreateBulk is the builder for creating many Attachment entities in bulk. // AttachmentCreateBulk is the builder for creating many Attachment entities in bulk.
type AttachmentCreateBulk struct { type AttachmentCreateBulk struct {
config config
err error
builders []*AttachmentCreate builders []*AttachmentCreate
} }
// Save creates the Attachment entities in the database. // Save creates the Attachment entities in the database.
func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error) { func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error) {
if acb.err != nil {
return nil, acb.err
}
specs := make([]*sqlgraph.CreateSpec, len(acb.builders)) specs := make([]*sqlgraph.CreateSpec, len(acb.builders))
nodes := make([]*Attachment, len(acb.builders)) nodes := make([]*Attachment, len(acb.builders))
mutators := make([]Mutator, len(acb.builders)) mutators := make([]Mutator, len(acb.builders))
@ -313,8 +345,8 @@ func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error
return nil, err return nil, err
} }
builder.mutation = mutation builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec() nodes[i], specs[i] = builder.createSpec()
var err error
if i < len(mutators)-1 { if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, acb.builders[i+1].mutation) _, err = mutators[i+1].Mutate(root, acb.builders[i+1].mutation)
} else { } else {

View file

@ -4,12 +4,13 @@ package ent
import ( import (
"context" "context"
"fmt"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// AttachmentDelete is the builder for deleting a Attachment entity. // AttachmentDelete is the builder for deleting a Attachment entity.
@ -27,7 +28,34 @@ func (ad *AttachmentDelete) Where(ps ...predicate.Attachment) *AttachmentDelete
// Exec executes the deletion query and returns how many vertices were deleted. // Exec executes the deletion query and returns how many vertices were deleted.
func (ad *AttachmentDelete) Exec(ctx context.Context) (int, error) { func (ad *AttachmentDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, ad.sqlExec, ad.mutation, ad.hooks) var (
err error
affected int
)
if len(ad.hooks) == 0 {
affected, err = ad.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
ad.mutation = mutation
affected, err = ad.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(ad.hooks) - 1; i >= 0; i-- {
if ad.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ad.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ad.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
@ -40,7 +68,15 @@ func (ad *AttachmentDelete) ExecX(ctx context.Context) int {
} }
func (ad *AttachmentDelete) sqlExec(ctx context.Context) (int, error) { func (ad *AttachmentDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(attachment.Table, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID)) _spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
}
if ps := ad.mutation.predicates; len(ps) > 0 { if ps := ad.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
@ -52,7 +88,6 @@ func (ad *AttachmentDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) { if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
ad.mutation.done = true
return affected, err return affected, err
} }
@ -61,12 +96,6 @@ type AttachmentDeleteOne struct {
ad *AttachmentDelete ad *AttachmentDelete
} }
// Where appends a list predicates to the AttachmentDelete builder.
func (ado *AttachmentDeleteOne) Where(ps ...predicate.Attachment) *AttachmentDeleteOne {
ado.ad.mutation.Where(ps...)
return ado
}
// Exec executes the deletion query. // Exec executes the deletion query.
func (ado *AttachmentDeleteOne) Exec(ctx context.Context) error { func (ado *AttachmentDeleteOne) Exec(ctx context.Context) error {
n, err := ado.ad.Exec(ctx) n, err := ado.ad.Exec(ctx)
@ -82,7 +111,5 @@ func (ado *AttachmentDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ado *AttachmentDeleteOne) ExecX(ctx context.Context) { func (ado *AttachmentDeleteOne) ExecX(ctx context.Context) {
if err := ado.Exec(ctx); err != nil { ado.ad.ExecX(ctx)
panic(err)
}
} }

View file

@ -11,18 +11,20 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/ent/item"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// AttachmentQuery is the builder for querying Attachment entities. // AttachmentQuery is the builder for querying Attachment entities.
type AttachmentQuery struct { type AttachmentQuery struct {
config config
ctx *QueryContext limit *int
order []attachment.OrderOption offset *int
inters []Interceptor unique *bool
order []OrderFunc
fields []string
predicates []predicate.Attachment predicates []predicate.Attachment
withItem *ItemQuery withItem *ItemQuery
withDocument *DocumentQuery withDocument *DocumentQuery
@ -38,34 +40,34 @@ func (aq *AttachmentQuery) Where(ps ...predicate.Attachment) *AttachmentQuery {
return aq return aq
} }
// Limit the number of records to be returned by this query. // Limit adds a limit step to the query.
func (aq *AttachmentQuery) Limit(limit int) *AttachmentQuery { func (aq *AttachmentQuery) Limit(limit int) *AttachmentQuery {
aq.ctx.Limit = &limit aq.limit = &limit
return aq return aq
} }
// Offset to start from. // Offset adds an offset step to the query.
func (aq *AttachmentQuery) Offset(offset int) *AttachmentQuery { func (aq *AttachmentQuery) Offset(offset int) *AttachmentQuery {
aq.ctx.Offset = &offset aq.offset = &offset
return aq return aq
} }
// Unique configures the query builder to filter duplicate records on query. // Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method. // By default, unique is set to true, and can be disabled using this method.
func (aq *AttachmentQuery) Unique(unique bool) *AttachmentQuery { func (aq *AttachmentQuery) Unique(unique bool) *AttachmentQuery {
aq.ctx.Unique = &unique aq.unique = &unique
return aq return aq
} }
// Order specifies how the records should be ordered. // Order adds an order step to the query.
func (aq *AttachmentQuery) Order(o ...attachment.OrderOption) *AttachmentQuery { func (aq *AttachmentQuery) Order(o ...OrderFunc) *AttachmentQuery {
aq.order = append(aq.order, o...) aq.order = append(aq.order, o...)
return aq return aq
} }
// QueryItem chains the current query on the "item" edge. // QueryItem chains the current query on the "item" edge.
func (aq *AttachmentQuery) QueryItem() *ItemQuery { func (aq *AttachmentQuery) QueryItem() *ItemQuery {
query := (&ItemClient{config: aq.config}).Query() query := &ItemQuery{config: aq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := aq.prepareQuery(ctx); err != nil { if err := aq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
@ -87,7 +89,7 @@ func (aq *AttachmentQuery) QueryItem() *ItemQuery {
// QueryDocument chains the current query on the "document" edge. // QueryDocument chains the current query on the "document" edge.
func (aq *AttachmentQuery) QueryDocument() *DocumentQuery { func (aq *AttachmentQuery) QueryDocument() *DocumentQuery {
query := (&DocumentClient{config: aq.config}).Query() query := &DocumentQuery{config: aq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := aq.prepareQuery(ctx); err != nil { if err := aq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
@ -110,7 +112,7 @@ func (aq *AttachmentQuery) QueryDocument() *DocumentQuery {
// First returns the first Attachment entity from the query. // First returns the first Attachment entity from the query.
// Returns a *NotFoundError when no Attachment was found. // Returns a *NotFoundError when no Attachment was found.
func (aq *AttachmentQuery) First(ctx context.Context) (*Attachment, error) { func (aq *AttachmentQuery) First(ctx context.Context) (*Attachment, error) {
nodes, err := aq.Limit(1).All(setContextOp(ctx, aq.ctx, "First")) nodes, err := aq.Limit(1).All(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -133,7 +135,7 @@ func (aq *AttachmentQuery) FirstX(ctx context.Context) *Attachment {
// Returns a *NotFoundError when no Attachment ID was found. // Returns a *NotFoundError when no Attachment ID was found.
func (aq *AttachmentQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { func (aq *AttachmentQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID var ids []uuid.UUID
if ids, err = aq.Limit(1).IDs(setContextOp(ctx, aq.ctx, "FirstID")); err != nil { if ids, err = aq.Limit(1).IDs(ctx); err != nil {
return return
} }
if len(ids) == 0 { if len(ids) == 0 {
@ -156,7 +158,7 @@ func (aq *AttachmentQuery) FirstIDX(ctx context.Context) uuid.UUID {
// Returns a *NotSingularError when more than one Attachment entity is found. // Returns a *NotSingularError when more than one Attachment entity is found.
// Returns a *NotFoundError when no Attachment entities are found. // Returns a *NotFoundError when no Attachment entities are found.
func (aq *AttachmentQuery) Only(ctx context.Context) (*Attachment, error) { func (aq *AttachmentQuery) Only(ctx context.Context) (*Attachment, error) {
nodes, err := aq.Limit(2).All(setContextOp(ctx, aq.ctx, "Only")) nodes, err := aq.Limit(2).All(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -184,7 +186,7 @@ func (aq *AttachmentQuery) OnlyX(ctx context.Context) *Attachment {
// Returns a *NotFoundError when no entities are found. // Returns a *NotFoundError when no entities are found.
func (aq *AttachmentQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { func (aq *AttachmentQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID var ids []uuid.UUID
if ids, err = aq.Limit(2).IDs(setContextOp(ctx, aq.ctx, "OnlyID")); err != nil { if ids, err = aq.Limit(2).IDs(ctx); err != nil {
return return
} }
switch len(ids) { switch len(ids) {
@ -209,12 +211,10 @@ func (aq *AttachmentQuery) OnlyIDX(ctx context.Context) uuid.UUID {
// All executes the query and returns a list of Attachments. // All executes the query and returns a list of Attachments.
func (aq *AttachmentQuery) All(ctx context.Context) ([]*Attachment, error) { func (aq *AttachmentQuery) All(ctx context.Context) ([]*Attachment, error) {
ctx = setContextOp(ctx, aq.ctx, "All")
if err := aq.prepareQuery(ctx); err != nil { if err := aq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
} }
qr := querierAll[[]*Attachment, *AttachmentQuery]() return aq.sqlAll(ctx)
return withInterceptors[[]*Attachment](ctx, aq, qr, aq.inters)
} }
// AllX is like All, but panics if an error occurs. // AllX is like All, but panics if an error occurs.
@ -227,12 +227,9 @@ func (aq *AttachmentQuery) AllX(ctx context.Context) []*Attachment {
} }
// IDs executes the query and returns a list of Attachment IDs. // IDs executes the query and returns a list of Attachment IDs.
func (aq *AttachmentQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { func (aq *AttachmentQuery) IDs(ctx context.Context) ([]uuid.UUID, error) {
if aq.ctx.Unique == nil && aq.path != nil { var ids []uuid.UUID
aq.Unique(true) if err := aq.Select(attachment.FieldID).Scan(ctx, &ids); err != nil {
}
ctx = setContextOp(ctx, aq.ctx, "IDs")
if err = aq.Select(attachment.FieldID).Scan(ctx, &ids); err != nil {
return nil, err return nil, err
} }
return ids, nil return ids, nil
@ -249,11 +246,10 @@ func (aq *AttachmentQuery) IDsX(ctx context.Context) []uuid.UUID {
// Count returns the count of the given query. // Count returns the count of the given query.
func (aq *AttachmentQuery) Count(ctx context.Context) (int, error) { func (aq *AttachmentQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, aq.ctx, "Count")
if err := aq.prepareQuery(ctx); err != nil { if err := aq.prepareQuery(ctx); err != nil {
return 0, err return 0, err
} }
return withInterceptors[int](ctx, aq, querierCount[*AttachmentQuery](), aq.inters) return aq.sqlCount(ctx)
} }
// CountX is like Count, but panics if an error occurs. // CountX is like Count, but panics if an error occurs.
@ -267,15 +263,10 @@ func (aq *AttachmentQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph. // Exist returns true if the query has elements in the graph.
func (aq *AttachmentQuery) Exist(ctx context.Context) (bool, error) { func (aq *AttachmentQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, aq.ctx, "Exist") if err := aq.prepareQuery(ctx); err != nil {
switch _, err := aq.FirstID(ctx); { return false, err
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
} }
return aq.sqlExist(ctx)
} }
// ExistX is like Exist, but panics if an error occurs. // ExistX is like Exist, but panics if an error occurs.
@ -295,22 +286,23 @@ func (aq *AttachmentQuery) Clone() *AttachmentQuery {
} }
return &AttachmentQuery{ return &AttachmentQuery{
config: aq.config, config: aq.config,
ctx: aq.ctx.Clone(), limit: aq.limit,
order: append([]attachment.OrderOption{}, aq.order...), offset: aq.offset,
inters: append([]Interceptor{}, aq.inters...), order: append([]OrderFunc{}, aq.order...),
predicates: append([]predicate.Attachment{}, aq.predicates...), predicates: append([]predicate.Attachment{}, aq.predicates...),
withItem: aq.withItem.Clone(), withItem: aq.withItem.Clone(),
withDocument: aq.withDocument.Clone(), withDocument: aq.withDocument.Clone(),
// clone intermediate query. // clone intermediate query.
sql: aq.sql.Clone(), sql: aq.sql.Clone(),
path: aq.path, path: aq.path,
unique: aq.unique,
} }
} }
// WithItem tells the query-builder to eager-load the nodes that are connected to // WithItem tells the query-builder to eager-load the nodes that are connected to
// the "item" edge. The optional arguments are used to configure the query builder of the edge. // the "item" edge. The optional arguments are used to configure the query builder of the edge.
func (aq *AttachmentQuery) WithItem(opts ...func(*ItemQuery)) *AttachmentQuery { func (aq *AttachmentQuery) WithItem(opts ...func(*ItemQuery)) *AttachmentQuery {
query := (&ItemClient{config: aq.config}).Query() query := &ItemQuery{config: aq.config}
for _, opt := range opts { for _, opt := range opts {
opt(query) opt(query)
} }
@ -321,7 +313,7 @@ func (aq *AttachmentQuery) WithItem(opts ...func(*ItemQuery)) *AttachmentQuery {
// WithDocument tells the query-builder to eager-load the nodes that are connected to // WithDocument tells the query-builder to eager-load the nodes that are connected to
// the "document" edge. The optional arguments are used to configure the query builder of the edge. // the "document" edge. The optional arguments are used to configure the query builder of the edge.
func (aq *AttachmentQuery) WithDocument(opts ...func(*DocumentQuery)) *AttachmentQuery { func (aq *AttachmentQuery) WithDocument(opts ...func(*DocumentQuery)) *AttachmentQuery {
query := (&DocumentClient{config: aq.config}).Query() query := &DocumentQuery{config: aq.config}
for _, opt := range opts { for _, opt := range opts {
opt(query) opt(query)
} }
@ -344,11 +336,16 @@ func (aq *AttachmentQuery) WithDocument(opts ...func(*DocumentQuery)) *Attachmen
// Aggregate(ent.Count()). // Aggregate(ent.Count()).
// Scan(ctx, &v) // Scan(ctx, &v)
func (aq *AttachmentQuery) GroupBy(field string, fields ...string) *AttachmentGroupBy { func (aq *AttachmentQuery) GroupBy(field string, fields ...string) *AttachmentGroupBy {
aq.ctx.Fields = append([]string{field}, fields...) grbuild := &AttachmentGroupBy{config: aq.config}
grbuild := &AttachmentGroupBy{build: aq} grbuild.fields = append([]string{field}, fields...)
grbuild.flds = &aq.ctx.Fields grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := aq.prepareQuery(ctx); err != nil {
return nil, err
}
return aq.sqlQuery(ctx), nil
}
grbuild.label = attachment.Label grbuild.label = attachment.Label
grbuild.scan = grbuild.Scan grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild return grbuild
} }
@ -365,30 +362,15 @@ func (aq *AttachmentQuery) GroupBy(field string, fields ...string) *AttachmentGr
// Select(attachment.FieldCreatedAt). // Select(attachment.FieldCreatedAt).
// Scan(ctx, &v) // Scan(ctx, &v)
func (aq *AttachmentQuery) Select(fields ...string) *AttachmentSelect { func (aq *AttachmentQuery) Select(fields ...string) *AttachmentSelect {
aq.ctx.Fields = append(aq.ctx.Fields, fields...) aq.fields = append(aq.fields, fields...)
sbuild := &AttachmentSelect{AttachmentQuery: aq} selbuild := &AttachmentSelect{AttachmentQuery: aq}
sbuild.label = attachment.Label selbuild.label = attachment.Label
sbuild.flds, sbuild.scan = &aq.ctx.Fields, sbuild.Scan selbuild.flds, selbuild.scan = &aq.fields, selbuild.Scan
return sbuild return selbuild
}
// Aggregate returns a AttachmentSelect configured with the given aggregations.
func (aq *AttachmentQuery) Aggregate(fns ...AggregateFunc) *AttachmentSelect {
return aq.Select().Aggregate(fns...)
} }
func (aq *AttachmentQuery) prepareQuery(ctx context.Context) error { func (aq *AttachmentQuery) prepareQuery(ctx context.Context) error {
for _, inter := range aq.inters { for _, f := range aq.fields {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, aq); err != nil {
return err
}
}
}
for _, f := range aq.ctx.Fields {
if !attachment.ValidColumn(f) { if !attachment.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
} }
@ -465,9 +447,6 @@ func (aq *AttachmentQuery) loadItem(ctx context.Context, query *ItemQuery, nodes
} }
nodeids[fk] = append(nodeids[fk], nodes[i]) nodeids[fk] = append(nodeids[fk], nodes[i])
} }
if len(ids) == 0 {
return nil
}
query.Where(item.IDIn(ids...)) query.Where(item.IDIn(ids...))
neighbors, err := query.All(ctx) neighbors, err := query.All(ctx)
if err != nil { if err != nil {
@ -497,9 +476,6 @@ func (aq *AttachmentQuery) loadDocument(ctx context.Context, query *DocumentQuer
} }
nodeids[fk] = append(nodeids[fk], nodes[i]) nodeids[fk] = append(nodeids[fk], nodes[i])
} }
if len(ids) == 0 {
return nil
}
query.Where(document.IDIn(ids...)) query.Where(document.IDIn(ids...))
neighbors, err := query.All(ctx) neighbors, err := query.All(ctx)
if err != nil { if err != nil {
@ -519,22 +495,41 @@ func (aq *AttachmentQuery) loadDocument(ctx context.Context, query *DocumentQuer
func (aq *AttachmentQuery) sqlCount(ctx context.Context) (int, error) { func (aq *AttachmentQuery) sqlCount(ctx context.Context) (int, error) {
_spec := aq.querySpec() _spec := aq.querySpec()
_spec.Node.Columns = aq.ctx.Fields _spec.Node.Columns = aq.fields
if len(aq.ctx.Fields) > 0 { if len(aq.fields) > 0 {
_spec.Unique = aq.ctx.Unique != nil && *aq.ctx.Unique _spec.Unique = aq.unique != nil && *aq.unique
} }
return sqlgraph.CountNodes(ctx, aq.driver, _spec) return sqlgraph.CountNodes(ctx, aq.driver, _spec)
} }
func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec { func (aq *AttachmentQuery) sqlExist(ctx context.Context) (bool, error) {
_spec := sqlgraph.NewQuerySpec(attachment.Table, attachment.Columns, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID)) switch _, err := aq.FirstID(ctx); {
_spec.From = aq.sql case IsNotFound(err):
if unique := aq.ctx.Unique; unique != nil { return false, nil
_spec.Unique = *unique case err != nil:
} else if aq.path != nil { return false, fmt.Errorf("ent: check existence: %w", err)
_spec.Unique = true default:
return true, nil
} }
if fields := aq.ctx.Fields; len(fields) > 0 { }
func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
Columns: attachment.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
From: aq.sql,
Unique: true,
}
if unique := aq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := aq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields)) _spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, attachment.FieldID) _spec.Node.Columns = append(_spec.Node.Columns, attachment.FieldID)
for i := range fields { for i := range fields {
@ -550,10 +545,10 @@ func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
} }
} }
} }
if limit := aq.ctx.Limit; limit != nil { if limit := aq.limit; limit != nil {
_spec.Limit = *limit _spec.Limit = *limit
} }
if offset := aq.ctx.Offset; offset != nil { if offset := aq.offset; offset != nil {
_spec.Offset = *offset _spec.Offset = *offset
} }
if ps := aq.order; len(ps) > 0 { if ps := aq.order; len(ps) > 0 {
@ -569,7 +564,7 @@ func (aq *AttachmentQuery) querySpec() *sqlgraph.QuerySpec {
func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector { func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(aq.driver.Dialect()) builder := sql.Dialect(aq.driver.Dialect())
t1 := builder.Table(attachment.Table) t1 := builder.Table(attachment.Table)
columns := aq.ctx.Fields columns := aq.fields
if len(columns) == 0 { if len(columns) == 0 {
columns = attachment.Columns columns = attachment.Columns
} }
@ -578,7 +573,7 @@ func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = aq.sql selector = aq.sql
selector.Select(selector.Columns(columns...)...) selector.Select(selector.Columns(columns...)...)
} }
if aq.ctx.Unique != nil && *aq.ctx.Unique { if aq.unique != nil && *aq.unique {
selector.Distinct() selector.Distinct()
} }
for _, p := range aq.predicates { for _, p := range aq.predicates {
@ -587,12 +582,12 @@ func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range aq.order { for _, p := range aq.order {
p(selector) p(selector)
} }
if offset := aq.ctx.Offset; offset != nil { if offset := aq.offset; offset != nil {
// limit is mandatory for offset clause. We start // limit is mandatory for offset clause. We start
// with default value, and override it below if needed. // with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32) selector.Offset(*offset).Limit(math.MaxInt32)
} }
if limit := aq.ctx.Limit; limit != nil { if limit := aq.limit; limit != nil {
selector.Limit(*limit) selector.Limit(*limit)
} }
return selector return selector
@ -600,8 +595,13 @@ func (aq *AttachmentQuery) sqlQuery(ctx context.Context) *sql.Selector {
// AttachmentGroupBy is the group-by builder for Attachment entities. // AttachmentGroupBy is the group-by builder for Attachment entities.
type AttachmentGroupBy struct { type AttachmentGroupBy struct {
config
selector selector
build *AttachmentQuery fields []string
fns []AggregateFunc
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
} }
// Aggregate adds the given aggregation functions to the group-by query. // Aggregate adds the given aggregation functions to the group-by query.
@ -610,77 +610,74 @@ func (agb *AttachmentGroupBy) Aggregate(fns ...AggregateFunc) *AttachmentGroupBy
return agb return agb
} }
// Scan applies the selector query and scans the result into the given value. // Scan applies the group-by query and scans the result into the given value.
func (agb *AttachmentGroupBy) Scan(ctx context.Context, v any) error { func (agb *AttachmentGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, agb.build.ctx, "GroupBy") query, err := agb.path(ctx)
if err := agb.build.prepareQuery(ctx); err != nil { if err != nil {
return err return err
} }
return scanWithInterceptors[*AttachmentQuery, *AttachmentGroupBy](ctx, agb.build, agb, agb.build.inters, v) agb.sql = query
return agb.sqlScan(ctx, v)
} }
func (agb *AttachmentGroupBy) sqlScan(ctx context.Context, root *AttachmentQuery, v any) error { func (agb *AttachmentGroupBy) sqlScan(ctx context.Context, v any) error {
selector := root.sqlQuery(ctx).Select() for _, f := range agb.fields {
aggregation := make([]string, 0, len(agb.fns)) if !attachment.ValidColumn(f) {
for _, fn := range agb.fns { return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*agb.flds)+len(agb.fns))
for _, f := range *agb.flds {
columns = append(columns, selector.C(f))
} }
columns = append(columns, aggregation...)
selector.Select(columns...)
} }
selector.GroupBy(selector.Columns(*agb.flds...)...) selector := agb.sqlQuery()
if err := selector.Err(); err != nil { if err := selector.Err(); err != nil {
return err return err
} }
rows := &sql.Rows{} rows := &sql.Rows{}
query, args := selector.Query() query, args := selector.Query()
if err := agb.build.driver.Query(ctx, query, args, rows); err != nil { if err := agb.driver.Query(ctx, query, args, rows); err != nil {
return err return err
} }
defer rows.Close() defer rows.Close()
return sql.ScanSlice(rows, v) return sql.ScanSlice(rows, v)
} }
func (agb *AttachmentGroupBy) sqlQuery() *sql.Selector {
selector := agb.sql.Select()
aggregation := make([]string, 0, len(agb.fns))
for _, fn := range agb.fns {
aggregation = append(aggregation, fn(selector))
}
// If no columns were selected in a custom aggregation function, the default
// selection is the fields used for "group-by", and the aggregation functions.
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(agb.fields)+len(agb.fns))
for _, f := range agb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(agb.fields...)...)
}
// AttachmentSelect is the builder for selecting fields of Attachment entities. // AttachmentSelect is the builder for selecting fields of Attachment entities.
type AttachmentSelect struct { type AttachmentSelect struct {
*AttachmentQuery *AttachmentQuery
selector selector
} // intermediate query (i.e. traversal path).
sql *sql.Selector
// Aggregate adds the given aggregation functions to the selector query.
func (as *AttachmentSelect) Aggregate(fns ...AggregateFunc) *AttachmentSelect {
as.fns = append(as.fns, fns...)
return as
} }
// Scan applies the selector query and scans the result into the given value. // Scan applies the selector query and scans the result into the given value.
func (as *AttachmentSelect) Scan(ctx context.Context, v any) error { func (as *AttachmentSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, as.ctx, "Select")
if err := as.prepareQuery(ctx); err != nil { if err := as.prepareQuery(ctx); err != nil {
return err return err
} }
return scanWithInterceptors[*AttachmentQuery, *AttachmentSelect](ctx, as.AttachmentQuery, as, as.inters, v) as.sql = as.AttachmentQuery.sqlQuery(ctx)
return as.sqlScan(ctx, v)
} }
func (as *AttachmentSelect) sqlScan(ctx context.Context, root *AttachmentQuery, v any) error { func (as *AttachmentSelect) sqlScan(ctx context.Context, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(as.fns))
for _, fn := range as.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*as.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{} rows := &sql.Rows{}
query, args := selector.Query() query, args := as.sql.Query()
if err := as.driver.Query(ctx, query, args, rows); err != nil { if err := as.driver.Query(ctx, query, args, rows); err != nil {
return err return err
} }

View file

@ -12,10 +12,10 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/item" "github.com/hay-kot/homebox/backend/ent/item"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// AttachmentUpdate is the builder for updating Attachment entities. // AttachmentUpdate is the builder for updating Attachment entities.
@ -51,20 +51,6 @@ func (au *AttachmentUpdate) SetNillableType(a *attachment.Type) *AttachmentUpdat
return au 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. // SetItemID sets the "item" edge to the Item entity by ID.
func (au *AttachmentUpdate) SetItemID(id uuid.UUID) *AttachmentUpdate { func (au *AttachmentUpdate) SetItemID(id uuid.UUID) *AttachmentUpdate {
au.mutation.SetItemID(id) au.mutation.SetItemID(id)
@ -106,8 +92,41 @@ func (au *AttachmentUpdate) ClearDocument() *AttachmentUpdate {
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (au *AttachmentUpdate) Save(ctx context.Context) (int, error) { func (au *AttachmentUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
au.defaults() au.defaults()
return withHooks(ctx, au.sqlSave, au.mutation, au.hooks) if len(au.hooks) == 0 {
if err = au.check(); err != nil {
return 0, err
}
affected, err = au.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = au.check(); err != nil {
return 0, err
}
au.mutation = mutation
affected, err = au.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(au.hooks) - 1; i >= 0; i-- {
if au.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = au.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, au.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
@ -157,10 +176,16 @@ func (au *AttachmentUpdate) check() error {
} }
func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) { func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := au.check(); err != nil { _spec := &sqlgraph.UpdateSpec{
return n, err Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
Columns: attachment.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
} }
_spec := sqlgraph.NewUpdateSpec(attachment.Table, attachment.Columns, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
if ps := au.mutation.predicates; len(ps) > 0 { if ps := au.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
@ -169,13 +194,18 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
} }
if value, ok := au.mutation.UpdatedAt(); ok { if value, ok := au.mutation.UpdatedAt(); ok {
_spec.SetField(attachment.FieldUpdatedAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: attachment.FieldUpdatedAt,
})
} }
if value, ok := au.mutation.GetType(); ok { if value, ok := au.mutation.GetType(); ok {
_spec.SetField(attachment.FieldType, field.TypeEnum, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
} Type: field.TypeEnum,
if value, ok := au.mutation.Primary(); ok { Value: value,
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value) Column: attachment.FieldType,
})
} }
if au.mutation.ItemCleared() { if au.mutation.ItemCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
@ -185,7 +215,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.ItemColumn}, Columns: []string{attachment.ItemColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -198,7 +231,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.ItemColumn}, Columns: []string{attachment.ItemColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -214,7 +250,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.DocumentColumn}, Columns: []string{attachment.DocumentColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -227,7 +266,10 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{attachment.DocumentColumn}, Columns: []string{attachment.DocumentColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -243,7 +285,6 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
return 0, err return 0, err
} }
au.mutation.done = true
return n, nil return n, nil
} }
@ -275,20 +316,6 @@ func (auo *AttachmentUpdateOne) SetNillableType(a *attachment.Type) *AttachmentU
return auo 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. // SetItemID sets the "item" edge to the Item entity by ID.
func (auo *AttachmentUpdateOne) SetItemID(id uuid.UUID) *AttachmentUpdateOne { func (auo *AttachmentUpdateOne) SetItemID(id uuid.UUID) *AttachmentUpdateOne {
auo.mutation.SetItemID(id) auo.mutation.SetItemID(id)
@ -328,12 +355,6 @@ func (auo *AttachmentUpdateOne) ClearDocument() *AttachmentUpdateOne {
return auo return auo
} }
// Where appends a list predicates to the AttachmentUpdate builder.
func (auo *AttachmentUpdateOne) Where(ps ...predicate.Attachment) *AttachmentUpdateOne {
auo.mutation.Where(ps...)
return auo
}
// Select allows selecting one or more fields (columns) of the returned entity. // Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema. // The default is selecting all fields defined in the entity schema.
func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *AttachmentUpdateOne { func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *AttachmentUpdateOne {
@ -343,8 +364,47 @@ func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *Attachme
// Save executes the query and returns the updated Attachment entity. // Save executes the query and returns the updated Attachment entity.
func (auo *AttachmentUpdateOne) Save(ctx context.Context) (*Attachment, error) { func (auo *AttachmentUpdateOne) Save(ctx context.Context) (*Attachment, error) {
var (
err error
node *Attachment
)
auo.defaults() auo.defaults()
return withHooks(ctx, auo.sqlSave, auo.mutation, auo.hooks) if len(auo.hooks) == 0 {
if err = auo.check(); err != nil {
return nil, err
}
node, err = auo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AttachmentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = auo.check(); err != nil {
return nil, err
}
auo.mutation = mutation
node, err = auo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(auo.hooks) - 1; i >= 0; i-- {
if auo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = auo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, auo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Attachment)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AttachmentMutation", v)
}
node = nv
}
return node, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
@ -394,10 +454,16 @@ func (auo *AttachmentUpdateOne) check() error {
} }
func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment, err error) { func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment, err error) {
if err := auo.check(); err != nil { _spec := &sqlgraph.UpdateSpec{
return _node, err Node: &sqlgraph.NodeSpec{
Table: attachment.Table,
Columns: attachment.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
},
} }
_spec := sqlgraph.NewUpdateSpec(attachment.Table, attachment.Columns, sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID))
id, ok := auo.mutation.ID() id, ok := auo.mutation.ID()
if !ok { if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Attachment.id" for update`)} return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Attachment.id" for update`)}
@ -423,13 +489,18 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
} }
} }
if value, ok := auo.mutation.UpdatedAt(); ok { if value, ok := auo.mutation.UpdatedAt(); ok {
_spec.SetField(attachment.FieldUpdatedAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: attachment.FieldUpdatedAt,
})
} }
if value, ok := auo.mutation.GetType(); ok { if value, ok := auo.mutation.GetType(); ok {
_spec.SetField(attachment.FieldType, field.TypeEnum, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
} Type: field.TypeEnum,
if value, ok := auo.mutation.Primary(); ok { Value: value,
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value) Column: attachment.FieldType,
})
} }
if auo.mutation.ItemCleared() { if auo.mutation.ItemCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
@ -439,7 +510,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.ItemColumn}, Columns: []string{attachment.ItemColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -452,7 +526,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.ItemColumn}, Columns: []string{attachment.ItemColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: item.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -468,7 +545,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.DocumentColumn}, Columns: []string{attachment.DocumentColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -481,7 +561,10 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
Columns: []string{attachment.DocumentColumn}, Columns: []string{attachment.DocumentColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -500,6 +583,5 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
} }
return nil, err return nil, err
} }
auo.mutation.done = true
return _node, nil return _node, nil
} }

View file

@ -7,12 +7,10 @@ import (
"strings" "strings"
"time" "time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/ent/user"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
) )
// AuthTokens is the model entity for the AuthTokens schema. // AuthTokens is the model entity for the AuthTokens schema.
@ -32,18 +30,15 @@ type AuthTokens struct {
// The values are being populated by the AuthTokensQuery when eager-loading is set. // The values are being populated by the AuthTokensQuery when eager-loading is set.
Edges AuthTokensEdges `json:"edges"` Edges AuthTokensEdges `json:"edges"`
user_auth_tokens *uuid.UUID user_auth_tokens *uuid.UUID
selectValues sql.SelectValues
} }
// AuthTokensEdges holds the relations/edges for other nodes in the graph. // AuthTokensEdges holds the relations/edges for other nodes in the graph.
type AuthTokensEdges struct { type AuthTokensEdges struct {
// User holds the value of the user edge. // User holds the value of the user edge.
User *User `json:"user,omitempty"` User *User `json:"user,omitempty"`
// Roles holds the value of the roles edge.
Roles *AuthRoles `json:"roles,omitempty"`
// loadedTypes holds the information for reporting if a // loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not. // type was loaded (or requested) in eager-loading or not.
loadedTypes [2]bool loadedTypes [1]bool
} }
// UserOrErr returns the User value or an error if the edge // UserOrErr returns the User value or an error if the edge
@ -59,19 +54,6 @@ func (e AuthTokensEdges) UserOrErr() (*User, error) {
return nil, &NotLoadedError{edge: "user"} return nil, &NotLoadedError{edge: "user"}
} }
// RolesOrErr returns the Roles value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e AuthTokensEdges) RolesOrErr() (*AuthRoles, error) {
if e.loadedTypes[1] {
if e.Roles == nil {
// Edge was loaded but was not found.
return nil, &NotFoundError{label: authroles.Label}
}
return e.Roles, nil
}
return nil, &NotLoadedError{edge: "roles"}
}
// scanValues returns the types for scanning values from sql.Rows. // scanValues returns the types for scanning values from sql.Rows.
func (*AuthTokens) scanValues(columns []string) ([]any, error) { func (*AuthTokens) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns)) values := make([]any, len(columns))
@ -86,7 +68,7 @@ func (*AuthTokens) scanValues(columns []string) ([]any, error) {
case authtokens.ForeignKeys[0]: // user_auth_tokens case authtokens.ForeignKeys[0]: // user_auth_tokens
values[i] = &sql.NullScanner{S: new(uuid.UUID)} values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default: default:
values[i] = new(sql.UnknownType) return nil, fmt.Errorf("unexpected column %q for type AuthTokens", columns[i])
} }
} }
return values, nil return values, nil
@ -137,34 +119,21 @@ func (at *AuthTokens) assignValues(columns []string, values []any) error {
at.user_auth_tokens = new(uuid.UUID) at.user_auth_tokens = new(uuid.UUID)
*at.user_auth_tokens = *value.S.(*uuid.UUID) *at.user_auth_tokens = *value.S.(*uuid.UUID)
} }
default:
at.selectValues.Set(columns[i], values[i])
} }
} }
return nil 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. // QueryUser queries the "user" edge of the AuthTokens entity.
func (at *AuthTokens) QueryUser() *UserQuery { func (at *AuthTokens) QueryUser() *UserQuery {
return NewAuthTokensClient(at.config).QueryUser(at) return (&AuthTokensClient{config: at.config}).QueryUser(at)
}
// QueryRoles queries the "roles" edge of the AuthTokens entity.
func (at *AuthTokens) QueryRoles() *AuthRolesQuery {
return NewAuthTokensClient(at.config).QueryRoles(at)
} }
// Update returns a builder for updating this AuthTokens. // Update returns a builder for updating this AuthTokens.
// Note that you need to call AuthTokens.Unwrap() before calling this method if this AuthTokens // Note that you need to call AuthTokens.Unwrap() before calling this method if this AuthTokens
// was returned from a transaction, and the transaction was committed or rolled back. // was returned from a transaction, and the transaction was committed or rolled back.
func (at *AuthTokens) Update() *AuthTokensUpdateOne { func (at *AuthTokens) Update() *AuthTokensUpdateOne {
return NewAuthTokensClient(at.config).UpdateOne(at) return (&AuthTokensClient{config: at.config}).UpdateOne(at)
} }
// Unwrap unwraps the AuthTokens entity that was returned from a transaction after it was closed, // Unwrap unwraps the AuthTokens entity that was returned from a transaction after it was closed,
@ -200,3 +169,9 @@ func (at *AuthTokens) String() string {
// AuthTokensSlice is a parsable slice of AuthTokens. // AuthTokensSlice is a parsable slice of AuthTokens.
type AuthTokensSlice []*AuthTokens type AuthTokensSlice []*AuthTokens
func (at AuthTokensSlice) config(cfg config) {
for _i := range at {
at[_i].config = cfg
}
}

View file

@ -5,8 +5,6 @@ package authtokens
import ( import (
"time" "time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -25,8 +23,6 @@ const (
FieldExpiresAt = "expires_at" FieldExpiresAt = "expires_at"
// EdgeUser holds the string denoting the user edge name in mutations. // EdgeUser holds the string denoting the user edge name in mutations.
EdgeUser = "user" EdgeUser = "user"
// EdgeRoles holds the string denoting the roles edge name in mutations.
EdgeRoles = "roles"
// Table holds the table name of the authtokens in the database. // Table holds the table name of the authtokens in the database.
Table = "auth_tokens" Table = "auth_tokens"
// UserTable is the table that holds the user relation/edge. // UserTable is the table that holds the user relation/edge.
@ -36,13 +32,6 @@ const (
UserInverseTable = "users" UserInverseTable = "users"
// UserColumn is the table column denoting the user relation/edge. // UserColumn is the table column denoting the user relation/edge.
UserColumn = "user_auth_tokens" UserColumn = "user_auth_tokens"
// RolesTable is the table that holds the roles relation/edge.
RolesTable = "auth_roles"
// RolesInverseTable is the table name for the AuthRoles entity.
// It exists in this package in order to avoid circular dependency with the "authroles" package.
RolesInverseTable = "auth_roles"
// RolesColumn is the table column denoting the roles relation/edge.
RolesColumn = "auth_tokens_roles"
) )
// Columns holds all SQL columns for authtokens fields. // Columns holds all SQL columns for authtokens fields.
@ -87,54 +76,3 @@ var (
// DefaultID holds the default value on creation for the "id" field. // DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID 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),
)
}

View file

@ -8,232 +8,362 @@ import (
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// ID filters vertices based on their ID field. // ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.AuthTokens { func ID(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDEQ applies the EQ predicate on the ID field. // IDEQ applies the EQ predicate on the ID field.
func IDEQ(id uuid.UUID) predicate.AuthTokens { func IDEQ(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDNEQ applies the NEQ predicate on the ID field. // IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id uuid.UUID) predicate.AuthTokens { func IDNEQ(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldID), id))
})
} }
// IDIn applies the In predicate on the ID field. // IDIn applies the In predicate on the ID field.
func IDIn(ids ...uuid.UUID) predicate.AuthTokens { func IDIn(ids ...uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldID, ids...)) return predicate.AuthTokens(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
})
} }
// IDNotIn applies the NotIn predicate on the ID field. // IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...uuid.UUID) predicate.AuthTokens { func IDNotIn(ids ...uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldID, ids...)) return predicate.AuthTokens(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
})
} }
// IDGT applies the GT predicate on the ID field. // IDGT applies the GT predicate on the ID field.
func IDGT(id uuid.UUID) predicate.AuthTokens { func IDGT(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldID), id))
})
} }
// IDGTE applies the GTE predicate on the ID field. // IDGTE applies the GTE predicate on the ID field.
func IDGTE(id uuid.UUID) predicate.AuthTokens { func IDGTE(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldID), id))
})
} }
// IDLT applies the LT predicate on the ID field. // IDLT applies the LT predicate on the ID field.
func IDLT(id uuid.UUID) predicate.AuthTokens { func IDLT(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldID), id))
})
} }
// IDLTE applies the LTE predicate on the ID field. // IDLTE applies the LTE predicate on the ID field.
func IDLTE(id uuid.UUID) predicate.AuthTokens { func IDLTE(id uuid.UUID) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldID, id)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldID), id))
})
} }
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.AuthTokens { func CreatedAt(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. // UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.AuthTokens { func UpdatedAt(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
} }
// Token applies equality check predicate on the "token" field. It's identical to TokenEQ. // Token applies equality check predicate on the "token" field. It's identical to TokenEQ.
func Token(v []byte) predicate.AuthTokens { func Token(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldToken), v))
})
} }
// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. // ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ.
func ExpiresAt(v time.Time) predicate.AuthTokens { func ExpiresAt(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldExpiresAt), v))
})
} }
// CreatedAtEQ applies the EQ predicate on the "created_at" field. // CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.AuthTokens { func CreatedAtEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. // CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.AuthTokens { func CreatedAtNEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtIn applies the In predicate on the "created_at" field. // CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.AuthTokens { func CreatedAtIn(vs ...time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldCreatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. // CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.AuthTokens { func CreatedAtNotIn(vs ...time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldCreatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtGT applies the GT predicate on the "created_at" field. // CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.AuthTokens { func CreatedAtGT(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtGTE applies the GTE predicate on the "created_at" field. // CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.AuthTokens { func CreatedAtGTE(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLT applies the LT predicate on the "created_at" field. // CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.AuthTokens { func CreatedAtLT(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLTE applies the LTE predicate on the "created_at" field. // CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.AuthTokens { func CreatedAtLTE(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldCreatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
})
} }
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. // UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.AuthTokens { func UpdatedAtEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. // UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.AuthTokens { func UpdatedAtNEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtIn applies the In predicate on the "updated_at" field. // UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.AuthTokens { func UpdatedAtIn(vs ...time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldUpdatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldUpdatedAt), v...))
})
} }
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. // UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.AuthTokens { func UpdatedAtNotIn(vs ...time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldUpdatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldUpdatedAt), v...))
})
} }
// UpdatedAtGT applies the GT predicate on the "updated_at" field. // UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.AuthTokens { func UpdatedAtGT(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. // UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.AuthTokens { func UpdatedAtGTE(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtLT applies the LT predicate on the "updated_at" field. // UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.AuthTokens { func UpdatedAtLT(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. // UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.AuthTokens { func UpdatedAtLTE(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldUpdatedAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldUpdatedAt), v))
})
} }
// TokenEQ applies the EQ predicate on the "token" field. // TokenEQ applies the EQ predicate on the "token" field.
func TokenEQ(v []byte) predicate.AuthTokens { func TokenEQ(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldToken), v))
})
} }
// TokenNEQ applies the NEQ predicate on the "token" field. // TokenNEQ applies the NEQ predicate on the "token" field.
func TokenNEQ(v []byte) predicate.AuthTokens { func TokenNEQ(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldToken), v))
})
} }
// TokenIn applies the In predicate on the "token" field. // TokenIn applies the In predicate on the "token" field.
func TokenIn(vs ...[]byte) predicate.AuthTokens { func TokenIn(vs ...[]byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldToken, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldToken), v...))
})
} }
// TokenNotIn applies the NotIn predicate on the "token" field. // TokenNotIn applies the NotIn predicate on the "token" field.
func TokenNotIn(vs ...[]byte) predicate.AuthTokens { func TokenNotIn(vs ...[]byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldToken, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldToken), v...))
})
} }
// TokenGT applies the GT predicate on the "token" field. // TokenGT applies the GT predicate on the "token" field.
func TokenGT(v []byte) predicate.AuthTokens { func TokenGT(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldToken), v))
})
} }
// TokenGTE applies the GTE predicate on the "token" field. // TokenGTE applies the GTE predicate on the "token" field.
func TokenGTE(v []byte) predicate.AuthTokens { func TokenGTE(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldToken), v))
})
} }
// TokenLT applies the LT predicate on the "token" field. // TokenLT applies the LT predicate on the "token" field.
func TokenLT(v []byte) predicate.AuthTokens { func TokenLT(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldToken), v))
})
} }
// TokenLTE applies the LTE predicate on the "token" field. // TokenLTE applies the LTE predicate on the "token" field.
func TokenLTE(v []byte) predicate.AuthTokens { func TokenLTE(v []byte) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldToken, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldToken), v))
})
} }
// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. // ExpiresAtEQ applies the EQ predicate on the "expires_at" field.
func ExpiresAtEQ(v time.Time) predicate.AuthTokens { func ExpiresAtEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldEQ(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldExpiresAt), v))
})
} }
// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. // ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field.
func ExpiresAtNEQ(v time.Time) predicate.AuthTokens { func ExpiresAtNEQ(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNEQ(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldExpiresAt), v))
})
} }
// ExpiresAtIn applies the In predicate on the "expires_at" field. // ExpiresAtIn applies the In predicate on the "expires_at" field.
func ExpiresAtIn(vs ...time.Time) predicate.AuthTokens { func ExpiresAtIn(vs ...time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldIn(FieldExpiresAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldExpiresAt), v...))
})
} }
// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. // ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field.
func ExpiresAtNotIn(vs ...time.Time) predicate.AuthTokens { func ExpiresAtNotIn(vs ...time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldNotIn(FieldExpiresAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldExpiresAt), v...))
})
} }
// ExpiresAtGT applies the GT predicate on the "expires_at" field. // ExpiresAtGT applies the GT predicate on the "expires_at" field.
func ExpiresAtGT(v time.Time) predicate.AuthTokens { func ExpiresAtGT(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGT(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldExpiresAt), v))
})
} }
// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. // ExpiresAtGTE applies the GTE predicate on the "expires_at" field.
func ExpiresAtGTE(v time.Time) predicate.AuthTokens { func ExpiresAtGTE(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldGTE(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldExpiresAt), v))
})
} }
// ExpiresAtLT applies the LT predicate on the "expires_at" field. // ExpiresAtLT applies the LT predicate on the "expires_at" field.
func ExpiresAtLT(v time.Time) predicate.AuthTokens { func ExpiresAtLT(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLT(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldExpiresAt), v))
})
} }
// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. // ExpiresAtLTE applies the LTE predicate on the "expires_at" field.
func ExpiresAtLTE(v time.Time) predicate.AuthTokens { func ExpiresAtLTE(v time.Time) predicate.AuthTokens {
return predicate.AuthTokens(sql.FieldLTE(FieldExpiresAt, v)) return predicate.AuthTokens(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldExpiresAt), v))
})
} }
// HasUser applies the HasEdge predicate on the "user" edge. // HasUser applies the HasEdge predicate on the "user" edge.
@ -241,6 +371,7 @@ func HasUser() predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) { return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(UserTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn), sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -249,31 +380,12 @@ func HasUser() predicate.AuthTokens {
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). // HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
func HasUserWith(preds ...predicate.User) predicate.AuthTokens { func HasUserWith(preds ...predicate.User) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := newUserStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// HasRoles applies the HasEdge predicate on the "roles" edge.
func HasRoles() predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) { return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn), sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
) )
sqlgraph.HasNeighbors(s, step)
})
}
// 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 := newRolesStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -284,15 +396,32 @@ func HasRolesWith(preds ...predicate.AuthRoles) predicate.AuthTokens {
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.AuthTokens) predicate.AuthTokens { func And(predicates ...predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(sql.AndPredicates(predicates...)) return predicate.AuthTokens(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
} }
// Or groups predicates with the OR operator between them. // Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.AuthTokens) predicate.AuthTokens { func Or(predicates ...predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(sql.OrPredicates(predicates...)) 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())
})
} }
// Not applies the not operator on the given predicate. // Not applies the not operator on the given predicate.
func Not(p predicate.AuthTokens) predicate.AuthTokens { func Not(p predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(sql.NotPredicates(p)) return predicate.AuthTokens(func(s *sql.Selector) {
p(s.Not())
})
} }

View file

@ -11,9 +11,8 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/ent/user"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
) )
// AuthTokensCreate is the builder for creating a AuthTokens entity. // AuthTokensCreate is the builder for creating a AuthTokens entity.
@ -104,25 +103,6 @@ func (atc *AuthTokensCreate) SetUser(u *User) *AuthTokensCreate {
return atc.SetUserID(u.ID) return atc.SetUserID(u.ID)
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by ID.
func (atc *AuthTokensCreate) SetRolesID(id int) *AuthTokensCreate {
atc.mutation.SetRolesID(id)
return atc
}
// SetNillableRolesID sets the "roles" edge to the AuthRoles entity by ID if the given value is not nil.
func (atc *AuthTokensCreate) SetNillableRolesID(id *int) *AuthTokensCreate {
if id != nil {
atc = atc.SetRolesID(*id)
}
return atc
}
// SetRoles sets the "roles" edge to the AuthRoles entity.
func (atc *AuthTokensCreate) SetRoles(a *AuthRoles) *AuthTokensCreate {
return atc.SetRolesID(a.ID)
}
// Mutation returns the AuthTokensMutation object of the builder. // Mutation returns the AuthTokensMutation object of the builder.
func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation { func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation {
return atc.mutation return atc.mutation
@ -130,8 +110,50 @@ func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation {
// Save creates the AuthTokens in the database. // Save creates the AuthTokens in the database.
func (atc *AuthTokensCreate) Save(ctx context.Context) (*AuthTokens, error) { func (atc *AuthTokensCreate) Save(ctx context.Context) (*AuthTokens, error) {
var (
err error
node *AuthTokens
)
atc.defaults() atc.defaults()
return withHooks(ctx, atc.sqlSave, atc.mutation, atc.hooks) if len(atc.hooks) == 0 {
if err = atc.check(); err != nil {
return nil, err
}
node, err = atc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = atc.check(); err != nil {
return nil, err
}
atc.mutation = mutation
if node, err = atc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(atc.hooks) - 1; i >= 0; i-- {
if atc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, atc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthTokens)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthTokensMutation", v)
}
node = nv
}
return node, err
} }
// SaveX calls Save and panics if Save returns an error. // SaveX calls Save and panics if Save returns an error.
@ -194,9 +216,6 @@ func (atc *AuthTokensCreate) check() error {
} }
func (atc *AuthTokensCreate) sqlSave(ctx context.Context) (*AuthTokens, error) { func (atc *AuthTokensCreate) sqlSave(ctx context.Context) (*AuthTokens, error) {
if err := atc.check(); err != nil {
return nil, err
}
_node, _spec := atc.createSpec() _node, _spec := atc.createSpec()
if err := sqlgraph.CreateNode(ctx, atc.driver, _spec); err != nil { if err := sqlgraph.CreateNode(ctx, atc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) { if sqlgraph.IsConstraintError(err) {
@ -211,34 +230,54 @@ func (atc *AuthTokensCreate) sqlSave(ctx context.Context) (*AuthTokens, error) {
return nil, err return nil, err
} }
} }
atc.mutation.id = &_node.ID
atc.mutation.done = true
return _node, nil return _node, nil
} }
func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) { func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
var ( var (
_node = &AuthTokens{config: atc.config} _node = &AuthTokens{config: atc.config}
_spec = sqlgraph.NewCreateSpec(authtokens.Table, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID)) _spec = &sqlgraph.CreateSpec{
Table: authtokens.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
}
) )
if id, ok := atc.mutation.ID(); ok { if id, ok := atc.mutation.ID(); ok {
_node.ID = id _node.ID = id
_spec.ID.Value = &id _spec.ID.Value = &id
} }
if value, ok := atc.mutation.CreatedAt(); ok { if value, ok := atc.mutation.CreatedAt(); ok {
_spec.SetField(authtokens.FieldCreatedAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldCreatedAt,
})
_node.CreatedAt = value _node.CreatedAt = value
} }
if value, ok := atc.mutation.UpdatedAt(); ok { if value, ok := atc.mutation.UpdatedAt(); ok {
_spec.SetField(authtokens.FieldUpdatedAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldUpdatedAt,
})
_node.UpdatedAt = value _node.UpdatedAt = value
} }
if value, ok := atc.mutation.Token(); ok { if value, ok := atc.mutation.Token(); ok {
_spec.SetField(authtokens.FieldToken, field.TypeBytes, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeBytes,
Value: value,
Column: authtokens.FieldToken,
})
_node.Token = value _node.Token = value
} }
if value, ok := atc.mutation.ExpiresAt(); ok { if value, ok := atc.mutation.ExpiresAt(); ok {
_spec.SetField(authtokens.FieldExpiresAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldExpiresAt,
})
_node.ExpiresAt = value _node.ExpiresAt = value
} }
if nodes := atc.mutation.UserIDs(); len(nodes) > 0 { if nodes := atc.mutation.UserIDs(); len(nodes) > 0 {
@ -249,7 +288,10 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
Columns: []string{authtokens.UserColumn}, Columns: []string{authtokens.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -258,37 +300,17 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
_node.user_auth_tokens = &nodes[0] _node.user_auth_tokens = &nodes[0]
_spec.Edges = append(_spec.Edges, edge) _spec.Edges = append(_spec.Edges, edge)
} }
if nodes := atc.mutation.RolesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec return _node, _spec
} }
// AuthTokensCreateBulk is the builder for creating many AuthTokens entities in bulk. // AuthTokensCreateBulk is the builder for creating many AuthTokens entities in bulk.
type AuthTokensCreateBulk struct { type AuthTokensCreateBulk struct {
config config
err error
builders []*AuthTokensCreate builders []*AuthTokensCreate
} }
// Save creates the AuthTokens entities in the database. // Save creates the AuthTokens entities in the database.
func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, error) { func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, error) {
if atcb.err != nil {
return nil, atcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(atcb.builders)) specs := make([]*sqlgraph.CreateSpec, len(atcb.builders))
nodes := make([]*AuthTokens, len(atcb.builders)) nodes := make([]*AuthTokens, len(atcb.builders))
mutators := make([]Mutator, len(atcb.builders)) mutators := make([]Mutator, len(atcb.builders))
@ -305,8 +327,8 @@ func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, erro
return nil, err return nil, err
} }
builder.mutation = mutation builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec() nodes[i], specs[i] = builder.createSpec()
var err error
if i < len(mutators)-1 { if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, atcb.builders[i+1].mutation) _, err = mutators[i+1].Mutate(root, atcb.builders[i+1].mutation)
} else { } else {

View file

@ -4,12 +4,13 @@ package ent
import ( import (
"context" "context"
"fmt"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// AuthTokensDelete is the builder for deleting a AuthTokens entity. // AuthTokensDelete is the builder for deleting a AuthTokens entity.
@ -27,7 +28,34 @@ func (atd *AuthTokensDelete) Where(ps ...predicate.AuthTokens) *AuthTokensDelete
// Exec executes the deletion query and returns how many vertices were deleted. // Exec executes the deletion query and returns how many vertices were deleted.
func (atd *AuthTokensDelete) Exec(ctx context.Context) (int, error) { func (atd *AuthTokensDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, atd.sqlExec, atd.mutation, atd.hooks) var (
err error
affected int
)
if len(atd.hooks) == 0 {
affected, err = atd.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
atd.mutation = mutation
affected, err = atd.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(atd.hooks) - 1; i >= 0; i-- {
if atd.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atd.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, atd.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
@ -40,7 +68,15 @@ func (atd *AuthTokensDelete) ExecX(ctx context.Context) int {
} }
func (atd *AuthTokensDelete) sqlExec(ctx context.Context) (int, error) { func (atd *AuthTokensDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(authtokens.Table, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID)) _spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
if ps := atd.mutation.predicates; len(ps) > 0 { if ps := atd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
@ -52,7 +88,6 @@ func (atd *AuthTokensDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) { if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
atd.mutation.done = true
return affected, err return affected, err
} }
@ -61,12 +96,6 @@ type AuthTokensDeleteOne struct {
atd *AuthTokensDelete atd *AuthTokensDelete
} }
// Where appends a list predicates to the AuthTokensDelete builder.
func (atdo *AuthTokensDeleteOne) Where(ps ...predicate.AuthTokens) *AuthTokensDeleteOne {
atdo.atd.mutation.Where(ps...)
return atdo
}
// Exec executes the deletion query. // Exec executes the deletion query.
func (atdo *AuthTokensDeleteOne) Exec(ctx context.Context) error { func (atdo *AuthTokensDeleteOne) Exec(ctx context.Context) error {
n, err := atdo.atd.Exec(ctx) n, err := atdo.atd.Exec(ctx)
@ -82,7 +111,5 @@ func (atdo *AuthTokensDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (atdo *AuthTokensDeleteOne) ExecX(ctx context.Context) { func (atdo *AuthTokensDeleteOne) ExecX(ctx context.Context) {
if err := atdo.Exec(ctx); err != nil { atdo.atd.ExecX(ctx)
panic(err)
}
} }

View file

@ -4,7 +4,6 @@ package ent
import ( import (
"context" "context"
"database/sql/driver"
"fmt" "fmt"
"math" "math"
@ -12,21 +11,21 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/ent/predicate"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/user"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
) )
// AuthTokensQuery is the builder for querying AuthTokens entities. // AuthTokensQuery is the builder for querying AuthTokens entities.
type AuthTokensQuery struct { type AuthTokensQuery struct {
config config
ctx *QueryContext limit *int
order []authtokens.OrderOption offset *int
inters []Interceptor unique *bool
order []OrderFunc
fields []string
predicates []predicate.AuthTokens predicates []predicate.AuthTokens
withUser *UserQuery withUser *UserQuery
withRoles *AuthRolesQuery
withFKs bool withFKs bool
// intermediate query (i.e. traversal path). // intermediate query (i.e. traversal path).
sql *sql.Selector sql *sql.Selector
@ -39,34 +38,34 @@ func (atq *AuthTokensQuery) Where(ps ...predicate.AuthTokens) *AuthTokensQuery {
return atq return atq
} }
// Limit the number of records to be returned by this query. // Limit adds a limit step to the query.
func (atq *AuthTokensQuery) Limit(limit int) *AuthTokensQuery { func (atq *AuthTokensQuery) Limit(limit int) *AuthTokensQuery {
atq.ctx.Limit = &limit atq.limit = &limit
return atq return atq
} }
// Offset to start from. // Offset adds an offset step to the query.
func (atq *AuthTokensQuery) Offset(offset int) *AuthTokensQuery { func (atq *AuthTokensQuery) Offset(offset int) *AuthTokensQuery {
atq.ctx.Offset = &offset atq.offset = &offset
return atq return atq
} }
// Unique configures the query builder to filter duplicate records on query. // Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method. // By default, unique is set to true, and can be disabled using this method.
func (atq *AuthTokensQuery) Unique(unique bool) *AuthTokensQuery { func (atq *AuthTokensQuery) Unique(unique bool) *AuthTokensQuery {
atq.ctx.Unique = &unique atq.unique = &unique
return atq return atq
} }
// Order specifies how the records should be ordered. // Order adds an order step to the query.
func (atq *AuthTokensQuery) Order(o ...authtokens.OrderOption) *AuthTokensQuery { func (atq *AuthTokensQuery) Order(o ...OrderFunc) *AuthTokensQuery {
atq.order = append(atq.order, o...) atq.order = append(atq.order, o...)
return atq return atq
} }
// QueryUser chains the current query on the "user" edge. // QueryUser chains the current query on the "user" edge.
func (atq *AuthTokensQuery) QueryUser() *UserQuery { func (atq *AuthTokensQuery) QueryUser() *UserQuery {
query := (&UserClient{config: atq.config}).Query() query := &UserQuery{config: atq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil { if err := atq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
@ -86,32 +85,10 @@ func (atq *AuthTokensQuery) QueryUser() *UserQuery {
return query return query
} }
// QueryRoles chains the current query on the "roles" edge.
func (atq *AuthTokensQuery) QueryRoles() *AuthRolesQuery {
query := (&AuthRolesClient{config: atq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := atq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(authtokens.Table, authtokens.FieldID, selector),
sqlgraph.To(authroles.Table, authroles.FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, authtokens.RolesTable, authtokens.RolesColumn),
)
fromU = sqlgraph.SetNeighbors(atq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first AuthTokens entity from the query. // First returns the first AuthTokens entity from the query.
// Returns a *NotFoundError when no AuthTokens was found. // Returns a *NotFoundError when no AuthTokens was found.
func (atq *AuthTokensQuery) First(ctx context.Context) (*AuthTokens, error) { func (atq *AuthTokensQuery) First(ctx context.Context) (*AuthTokens, error) {
nodes, err := atq.Limit(1).All(setContextOp(ctx, atq.ctx, "First")) nodes, err := atq.Limit(1).All(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -134,7 +111,7 @@ func (atq *AuthTokensQuery) FirstX(ctx context.Context) *AuthTokens {
// Returns a *NotFoundError when no AuthTokens ID was found. // Returns a *NotFoundError when no AuthTokens ID was found.
func (atq *AuthTokensQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { func (atq *AuthTokensQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID var ids []uuid.UUID
if ids, err = atq.Limit(1).IDs(setContextOp(ctx, atq.ctx, "FirstID")); err != nil { if ids, err = atq.Limit(1).IDs(ctx); err != nil {
return return
} }
if len(ids) == 0 { if len(ids) == 0 {
@ -157,7 +134,7 @@ func (atq *AuthTokensQuery) FirstIDX(ctx context.Context) uuid.UUID {
// Returns a *NotSingularError when more than one AuthTokens entity is found. // Returns a *NotSingularError when more than one AuthTokens entity is found.
// Returns a *NotFoundError when no AuthTokens entities are found. // Returns a *NotFoundError when no AuthTokens entities are found.
func (atq *AuthTokensQuery) Only(ctx context.Context) (*AuthTokens, error) { func (atq *AuthTokensQuery) Only(ctx context.Context) (*AuthTokens, error) {
nodes, err := atq.Limit(2).All(setContextOp(ctx, atq.ctx, "Only")) nodes, err := atq.Limit(2).All(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -185,7 +162,7 @@ func (atq *AuthTokensQuery) OnlyX(ctx context.Context) *AuthTokens {
// Returns a *NotFoundError when no entities are found. // Returns a *NotFoundError when no entities are found.
func (atq *AuthTokensQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { func (atq *AuthTokensQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID var ids []uuid.UUID
if ids, err = atq.Limit(2).IDs(setContextOp(ctx, atq.ctx, "OnlyID")); err != nil { if ids, err = atq.Limit(2).IDs(ctx); err != nil {
return return
} }
switch len(ids) { switch len(ids) {
@ -210,12 +187,10 @@ func (atq *AuthTokensQuery) OnlyIDX(ctx context.Context) uuid.UUID {
// All executes the query and returns a list of AuthTokensSlice. // All executes the query and returns a list of AuthTokensSlice.
func (atq *AuthTokensQuery) All(ctx context.Context) ([]*AuthTokens, error) { func (atq *AuthTokensQuery) All(ctx context.Context) ([]*AuthTokens, error) {
ctx = setContextOp(ctx, atq.ctx, "All")
if err := atq.prepareQuery(ctx); err != nil { if err := atq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
} }
qr := querierAll[[]*AuthTokens, *AuthTokensQuery]() return atq.sqlAll(ctx)
return withInterceptors[[]*AuthTokens](ctx, atq, qr, atq.inters)
} }
// AllX is like All, but panics if an error occurs. // AllX is like All, but panics if an error occurs.
@ -228,12 +203,9 @@ func (atq *AuthTokensQuery) AllX(ctx context.Context) []*AuthTokens {
} }
// IDs executes the query and returns a list of AuthTokens IDs. // IDs executes the query and returns a list of AuthTokens IDs.
func (atq *AuthTokensQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { func (atq *AuthTokensQuery) IDs(ctx context.Context) ([]uuid.UUID, error) {
if atq.ctx.Unique == nil && atq.path != nil { var ids []uuid.UUID
atq.Unique(true) if err := atq.Select(authtokens.FieldID).Scan(ctx, &ids); err != nil {
}
ctx = setContextOp(ctx, atq.ctx, "IDs")
if err = atq.Select(authtokens.FieldID).Scan(ctx, &ids); err != nil {
return nil, err return nil, err
} }
return ids, nil return ids, nil
@ -250,11 +222,10 @@ func (atq *AuthTokensQuery) IDsX(ctx context.Context) []uuid.UUID {
// Count returns the count of the given query. // Count returns the count of the given query.
func (atq *AuthTokensQuery) Count(ctx context.Context) (int, error) { func (atq *AuthTokensQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, atq.ctx, "Count")
if err := atq.prepareQuery(ctx); err != nil { if err := atq.prepareQuery(ctx); err != nil {
return 0, err return 0, err
} }
return withInterceptors[int](ctx, atq, querierCount[*AuthTokensQuery](), atq.inters) return atq.sqlCount(ctx)
} }
// CountX is like Count, but panics if an error occurs. // CountX is like Count, but panics if an error occurs.
@ -268,15 +239,10 @@ func (atq *AuthTokensQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph. // Exist returns true if the query has elements in the graph.
func (atq *AuthTokensQuery) Exist(ctx context.Context) (bool, error) { func (atq *AuthTokensQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, atq.ctx, "Exist") if err := atq.prepareQuery(ctx); err != nil {
switch _, err := atq.FirstID(ctx); { return false, err
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
} }
return atq.sqlExist(ctx)
} }
// ExistX is like Exist, but panics if an error occurs. // ExistX is like Exist, but panics if an error occurs.
@ -296,22 +262,22 @@ func (atq *AuthTokensQuery) Clone() *AuthTokensQuery {
} }
return &AuthTokensQuery{ return &AuthTokensQuery{
config: atq.config, config: atq.config,
ctx: atq.ctx.Clone(), limit: atq.limit,
order: append([]authtokens.OrderOption{}, atq.order...), offset: atq.offset,
inters: append([]Interceptor{}, atq.inters...), order: append([]OrderFunc{}, atq.order...),
predicates: append([]predicate.AuthTokens{}, atq.predicates...), predicates: append([]predicate.AuthTokens{}, atq.predicates...),
withUser: atq.withUser.Clone(), withUser: atq.withUser.Clone(),
withRoles: atq.withRoles.Clone(),
// clone intermediate query. // clone intermediate query.
sql: atq.sql.Clone(), sql: atq.sql.Clone(),
path: atq.path, path: atq.path,
unique: atq.unique,
} }
} }
// WithUser tells the query-builder to eager-load the nodes that are connected to // WithUser tells the query-builder to eager-load the nodes that are connected to
// the "user" edge. The optional arguments are used to configure the query builder of the edge. // the "user" edge. The optional arguments are used to configure the query builder of the edge.
func (atq *AuthTokensQuery) WithUser(opts ...func(*UserQuery)) *AuthTokensQuery { func (atq *AuthTokensQuery) WithUser(opts ...func(*UserQuery)) *AuthTokensQuery {
query := (&UserClient{config: atq.config}).Query() query := &UserQuery{config: atq.config}
for _, opt := range opts { for _, opt := range opts {
opt(query) opt(query)
} }
@ -319,17 +285,6 @@ func (atq *AuthTokensQuery) WithUser(opts ...func(*UserQuery)) *AuthTokensQuery
return atq return atq
} }
// WithRoles tells the query-builder to eager-load the nodes that are connected to
// the "roles" edge. The optional arguments are used to configure the query builder of the edge.
func (atq *AuthTokensQuery) WithRoles(opts ...func(*AuthRolesQuery)) *AuthTokensQuery {
query := (&AuthRolesClient{config: atq.config}).Query()
for _, opt := range opts {
opt(query)
}
atq.withRoles = query
return atq
}
// GroupBy is used to group vertices by one or more fields/columns. // GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum. // It is often used with aggregate functions, like: count, max, mean, min, sum.
// //
@ -345,11 +300,16 @@ func (atq *AuthTokensQuery) WithRoles(opts ...func(*AuthRolesQuery)) *AuthTokens
// Aggregate(ent.Count()). // Aggregate(ent.Count()).
// Scan(ctx, &v) // Scan(ctx, &v)
func (atq *AuthTokensQuery) GroupBy(field string, fields ...string) *AuthTokensGroupBy { func (atq *AuthTokensQuery) GroupBy(field string, fields ...string) *AuthTokensGroupBy {
atq.ctx.Fields = append([]string{field}, fields...) grbuild := &AuthTokensGroupBy{config: atq.config}
grbuild := &AuthTokensGroupBy{build: atq} grbuild.fields = append([]string{field}, fields...)
grbuild.flds = &atq.ctx.Fields grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
return atq.sqlQuery(ctx), nil
}
grbuild.label = authtokens.Label grbuild.label = authtokens.Label
grbuild.scan = grbuild.Scan grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild return grbuild
} }
@ -366,30 +326,15 @@ func (atq *AuthTokensQuery) GroupBy(field string, fields ...string) *AuthTokensG
// Select(authtokens.FieldCreatedAt). // Select(authtokens.FieldCreatedAt).
// Scan(ctx, &v) // Scan(ctx, &v)
func (atq *AuthTokensQuery) Select(fields ...string) *AuthTokensSelect { func (atq *AuthTokensQuery) Select(fields ...string) *AuthTokensSelect {
atq.ctx.Fields = append(atq.ctx.Fields, fields...) atq.fields = append(atq.fields, fields...)
sbuild := &AuthTokensSelect{AuthTokensQuery: atq} selbuild := &AuthTokensSelect{AuthTokensQuery: atq}
sbuild.label = authtokens.Label selbuild.label = authtokens.Label
sbuild.flds, sbuild.scan = &atq.ctx.Fields, sbuild.Scan selbuild.flds, selbuild.scan = &atq.fields, selbuild.Scan
return sbuild return selbuild
}
// Aggregate returns a AuthTokensSelect configured with the given aggregations.
func (atq *AuthTokensQuery) Aggregate(fns ...AggregateFunc) *AuthTokensSelect {
return atq.Select().Aggregate(fns...)
} }
func (atq *AuthTokensQuery) prepareQuery(ctx context.Context) error { func (atq *AuthTokensQuery) prepareQuery(ctx context.Context) error {
for _, inter := range atq.inters { for _, f := range atq.fields {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, atq); err != nil {
return err
}
}
}
for _, f := range atq.ctx.Fields {
if !authtokens.ValidColumn(f) { if !authtokens.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
} }
@ -409,9 +354,8 @@ func (atq *AuthTokensQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*
nodes = []*AuthTokens{} nodes = []*AuthTokens{}
withFKs = atq.withFKs withFKs = atq.withFKs
_spec = atq.querySpec() _spec = atq.querySpec()
loadedTypes = [2]bool{ loadedTypes = [1]bool{
atq.withUser != nil, atq.withUser != nil,
atq.withRoles != nil,
} }
) )
if atq.withUser != nil { if atq.withUser != nil {
@ -444,12 +388,6 @@ func (atq *AuthTokensQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*
return nil, err return nil, err
} }
} }
if query := atq.withRoles; query != nil {
if err := atq.loadRoles(ctx, query, nodes, nil,
func(n *AuthTokens, e *AuthRoles) { n.Edges.Roles = e }); err != nil {
return nil, err
}
}
return nodes, nil return nodes, nil
} }
@ -466,9 +404,6 @@ func (atq *AuthTokensQuery) loadUser(ctx context.Context, query *UserQuery, node
} }
nodeids[fk] = append(nodeids[fk], nodes[i]) nodeids[fk] = append(nodeids[fk], nodes[i])
} }
if len(ids) == 0 {
return nil
}
query.Where(user.IDIn(ids...)) query.Where(user.IDIn(ids...))
neighbors, err := query.All(ctx) neighbors, err := query.All(ctx)
if err != nil { if err != nil {
@ -485,53 +420,44 @@ func (atq *AuthTokensQuery) loadUser(ctx context.Context, query *UserQuery, node
} }
return nil return nil
} }
func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery, nodes []*AuthTokens, init func(*AuthTokens), assign func(*AuthTokens, *AuthRoles)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[uuid.UUID]*AuthTokens)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
}
query.withFKs = true
query.Where(predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(authtokens.RolesColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.auth_tokens_roles
if fk == nil {
return fmt.Errorf(`foreign-key "auth_tokens_roles" is nil for node %v`, n.ID)
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "auth_tokens_roles" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
return nil
}
func (atq *AuthTokensQuery) sqlCount(ctx context.Context) (int, error) { func (atq *AuthTokensQuery) sqlCount(ctx context.Context) (int, error) {
_spec := atq.querySpec() _spec := atq.querySpec()
_spec.Node.Columns = atq.ctx.Fields _spec.Node.Columns = atq.fields
if len(atq.ctx.Fields) > 0 { if len(atq.fields) > 0 {
_spec.Unique = atq.ctx.Unique != nil && *atq.ctx.Unique _spec.Unique = atq.unique != nil && *atq.unique
} }
return sqlgraph.CountNodes(ctx, atq.driver, _spec) return sqlgraph.CountNodes(ctx, atq.driver, _spec)
} }
func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec { func (atq *AuthTokensQuery) sqlExist(ctx context.Context) (bool, error) {
_spec := sqlgraph.NewQuerySpec(authtokens.Table, authtokens.Columns, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID)) switch _, err := atq.FirstID(ctx); {
_spec.From = atq.sql case IsNotFound(err):
if unique := atq.ctx.Unique; unique != nil { return false, nil
_spec.Unique = *unique case err != nil:
} else if atq.path != nil { return false, fmt.Errorf("ent: check existence: %w", err)
_spec.Unique = true default:
return true, nil
} }
if fields := atq.ctx.Fields; len(fields) > 0 { }
func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
Columns: authtokens.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
From: atq.sql,
Unique: true,
}
if unique := atq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := atq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields)) _spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, authtokens.FieldID) _spec.Node.Columns = append(_spec.Node.Columns, authtokens.FieldID)
for i := range fields { for i := range fields {
@ -547,10 +473,10 @@ func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
} }
} }
} }
if limit := atq.ctx.Limit; limit != nil { if limit := atq.limit; limit != nil {
_spec.Limit = *limit _spec.Limit = *limit
} }
if offset := atq.ctx.Offset; offset != nil { if offset := atq.offset; offset != nil {
_spec.Offset = *offset _spec.Offset = *offset
} }
if ps := atq.order; len(ps) > 0 { if ps := atq.order; len(ps) > 0 {
@ -566,7 +492,7 @@ func (atq *AuthTokensQuery) querySpec() *sqlgraph.QuerySpec {
func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector { func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(atq.driver.Dialect()) builder := sql.Dialect(atq.driver.Dialect())
t1 := builder.Table(authtokens.Table) t1 := builder.Table(authtokens.Table)
columns := atq.ctx.Fields columns := atq.fields
if len(columns) == 0 { if len(columns) == 0 {
columns = authtokens.Columns columns = authtokens.Columns
} }
@ -575,7 +501,7 @@ func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = atq.sql selector = atq.sql
selector.Select(selector.Columns(columns...)...) selector.Select(selector.Columns(columns...)...)
} }
if atq.ctx.Unique != nil && *atq.ctx.Unique { if atq.unique != nil && *atq.unique {
selector.Distinct() selector.Distinct()
} }
for _, p := range atq.predicates { for _, p := range atq.predicates {
@ -584,12 +510,12 @@ func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range atq.order { for _, p := range atq.order {
p(selector) p(selector)
} }
if offset := atq.ctx.Offset; offset != nil { if offset := atq.offset; offset != nil {
// limit is mandatory for offset clause. We start // limit is mandatory for offset clause. We start
// with default value, and override it below if needed. // with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32) selector.Offset(*offset).Limit(math.MaxInt32)
} }
if limit := atq.ctx.Limit; limit != nil { if limit := atq.limit; limit != nil {
selector.Limit(*limit) selector.Limit(*limit)
} }
return selector return selector
@ -597,8 +523,13 @@ func (atq *AuthTokensQuery) sqlQuery(ctx context.Context) *sql.Selector {
// AuthTokensGroupBy is the group-by builder for AuthTokens entities. // AuthTokensGroupBy is the group-by builder for AuthTokens entities.
type AuthTokensGroupBy struct { type AuthTokensGroupBy struct {
config
selector selector
build *AuthTokensQuery fields []string
fns []AggregateFunc
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
} }
// Aggregate adds the given aggregation functions to the group-by query. // Aggregate adds the given aggregation functions to the group-by query.
@ -607,77 +538,74 @@ func (atgb *AuthTokensGroupBy) Aggregate(fns ...AggregateFunc) *AuthTokensGroupB
return atgb return atgb
} }
// Scan applies the selector query and scans the result into the given value. // Scan applies the group-by query and scans the result into the given value.
func (atgb *AuthTokensGroupBy) Scan(ctx context.Context, v any) error { func (atgb *AuthTokensGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, atgb.build.ctx, "GroupBy") query, err := atgb.path(ctx)
if err := atgb.build.prepareQuery(ctx); err != nil { if err != nil {
return err return err
} }
return scanWithInterceptors[*AuthTokensQuery, *AuthTokensGroupBy](ctx, atgb.build, atgb, atgb.build.inters, v) atgb.sql = query
return atgb.sqlScan(ctx, v)
} }
func (atgb *AuthTokensGroupBy) sqlScan(ctx context.Context, root *AuthTokensQuery, v any) error { func (atgb *AuthTokensGroupBy) sqlScan(ctx context.Context, v any) error {
selector := root.sqlQuery(ctx).Select() for _, f := range atgb.fields {
aggregation := make([]string, 0, len(atgb.fns)) if !authtokens.ValidColumn(f) {
for _, fn := range atgb.fns { return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*atgb.flds)+len(atgb.fns))
for _, f := range *atgb.flds {
columns = append(columns, selector.C(f))
} }
columns = append(columns, aggregation...)
selector.Select(columns...)
} }
selector.GroupBy(selector.Columns(*atgb.flds...)...) selector := atgb.sqlQuery()
if err := selector.Err(); err != nil { if err := selector.Err(); err != nil {
return err return err
} }
rows := &sql.Rows{} rows := &sql.Rows{}
query, args := selector.Query() query, args := selector.Query()
if err := atgb.build.driver.Query(ctx, query, args, rows); err != nil { if err := atgb.driver.Query(ctx, query, args, rows); err != nil {
return err return err
} }
defer rows.Close() defer rows.Close()
return sql.ScanSlice(rows, v) return sql.ScanSlice(rows, v)
} }
func (atgb *AuthTokensGroupBy) sqlQuery() *sql.Selector {
selector := atgb.sql.Select()
aggregation := make([]string, 0, len(atgb.fns))
for _, fn := range atgb.fns {
aggregation = append(aggregation, fn(selector))
}
// If no columns were selected in a custom aggregation function, the default
// selection is the fields used for "group-by", and the aggregation functions.
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(atgb.fields)+len(atgb.fns))
for _, f := range atgb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(atgb.fields...)...)
}
// AuthTokensSelect is the builder for selecting fields of AuthTokens entities. // AuthTokensSelect is the builder for selecting fields of AuthTokens entities.
type AuthTokensSelect struct { type AuthTokensSelect struct {
*AuthTokensQuery *AuthTokensQuery
selector selector
} // intermediate query (i.e. traversal path).
sql *sql.Selector
// Aggregate adds the given aggregation functions to the selector query.
func (ats *AuthTokensSelect) Aggregate(fns ...AggregateFunc) *AuthTokensSelect {
ats.fns = append(ats.fns, fns...)
return ats
} }
// Scan applies the selector query and scans the result into the given value. // Scan applies the selector query and scans the result into the given value.
func (ats *AuthTokensSelect) Scan(ctx context.Context, v any) error { func (ats *AuthTokensSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, ats.ctx, "Select")
if err := ats.prepareQuery(ctx); err != nil { if err := ats.prepareQuery(ctx); err != nil {
return err return err
} }
return scanWithInterceptors[*AuthTokensQuery, *AuthTokensSelect](ctx, ats.AuthTokensQuery, ats, ats.inters, v) ats.sql = ats.AuthTokensQuery.sqlQuery(ctx)
return ats.sqlScan(ctx, v)
} }
func (ats *AuthTokensSelect) sqlScan(ctx context.Context, root *AuthTokensQuery, v any) error { func (ats *AuthTokensSelect) sqlScan(ctx context.Context, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(ats.fns))
for _, fn := range ats.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*ats.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{} rows := &sql.Rows{}
query, args := selector.Query() query, args := ats.sql.Query()
if err := ats.driver.Query(ctx, query, args, rows); err != nil { if err := ats.driver.Query(ctx, query, args, rows); err != nil {
return err return err
} }

View file

@ -12,10 +12,9 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles" "github.com/hay-kot/homebox/backend/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/ent/predicate"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/user"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
) )
// AuthTokensUpdate is the builder for updating AuthTokens entities. // AuthTokensUpdate is the builder for updating AuthTokens entities.
@ -76,25 +75,6 @@ func (atu *AuthTokensUpdate) SetUser(u *User) *AuthTokensUpdate {
return atu.SetUserID(u.ID) return atu.SetUserID(u.ID)
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by ID.
func (atu *AuthTokensUpdate) SetRolesID(id int) *AuthTokensUpdate {
atu.mutation.SetRolesID(id)
return atu
}
// SetNillableRolesID sets the "roles" edge to the AuthRoles entity by ID if the given value is not nil.
func (atu *AuthTokensUpdate) SetNillableRolesID(id *int) *AuthTokensUpdate {
if id != nil {
atu = atu.SetRolesID(*id)
}
return atu
}
// SetRoles sets the "roles" edge to the AuthRoles entity.
func (atu *AuthTokensUpdate) SetRoles(a *AuthRoles) *AuthTokensUpdate {
return atu.SetRolesID(a.ID)
}
// Mutation returns the AuthTokensMutation object of the builder. // Mutation returns the AuthTokensMutation object of the builder.
func (atu *AuthTokensUpdate) Mutation() *AuthTokensMutation { func (atu *AuthTokensUpdate) Mutation() *AuthTokensMutation {
return atu.mutation return atu.mutation
@ -106,16 +86,37 @@ func (atu *AuthTokensUpdate) ClearUser() *AuthTokensUpdate {
return atu return atu
} }
// ClearRoles clears the "roles" edge to the AuthRoles entity.
func (atu *AuthTokensUpdate) ClearRoles() *AuthTokensUpdate {
atu.mutation.ClearRoles()
return atu
}
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) { func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
atu.defaults() atu.defaults()
return withHooks(ctx, atu.sqlSave, atu.mutation, atu.hooks) if len(atu.hooks) == 0 {
affected, err = atu.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
atu.mutation = mutation
affected, err = atu.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(atu.hooks) - 1; i >= 0; i-- {
if atu.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atu.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, atu.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
@ -149,7 +150,16 @@ func (atu *AuthTokensUpdate) defaults() {
} }
func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) { func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
_spec := sqlgraph.NewUpdateSpec(authtokens.Table, authtokens.Columns, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID)) _spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
Columns: authtokens.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
if ps := atu.mutation.predicates; len(ps) > 0 { if ps := atu.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
@ -158,13 +168,25 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
} }
if value, ok := atu.mutation.UpdatedAt(); ok { if value, ok := atu.mutation.UpdatedAt(); ok {
_spec.SetField(authtokens.FieldUpdatedAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldUpdatedAt,
})
} }
if value, ok := atu.mutation.Token(); ok { if value, ok := atu.mutation.Token(); ok {
_spec.SetField(authtokens.FieldToken, field.TypeBytes, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeBytes,
Value: value,
Column: authtokens.FieldToken,
})
} }
if value, ok := atu.mutation.ExpiresAt(); ok { if value, ok := atu.mutation.ExpiresAt(); ok {
_spec.SetField(authtokens.FieldExpiresAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldExpiresAt,
})
} }
if atu.mutation.UserCleared() { if atu.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
@ -174,7 +196,10 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authtokens.UserColumn}, Columns: []string{authtokens.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -187,36 +212,10 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{authtokens.UserColumn}, Columns: []string{authtokens.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
}, Type: field.TypeUUID,
} Column: user.FieldID,
for _, k := range nodes { },
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if atu.mutation.RolesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := atu.mutation.RolesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -232,7 +231,6 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
return 0, err return 0, err
} }
atu.mutation.done = true
return n, nil return n, nil
} }
@ -289,25 +287,6 @@ func (atuo *AuthTokensUpdateOne) SetUser(u *User) *AuthTokensUpdateOne {
return atuo.SetUserID(u.ID) return atuo.SetUserID(u.ID)
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by ID.
func (atuo *AuthTokensUpdateOne) SetRolesID(id int) *AuthTokensUpdateOne {
atuo.mutation.SetRolesID(id)
return atuo
}
// SetNillableRolesID sets the "roles" edge to the AuthRoles entity by ID if the given value is not nil.
func (atuo *AuthTokensUpdateOne) SetNillableRolesID(id *int) *AuthTokensUpdateOne {
if id != nil {
atuo = atuo.SetRolesID(*id)
}
return atuo
}
// SetRoles sets the "roles" edge to the AuthRoles entity.
func (atuo *AuthTokensUpdateOne) SetRoles(a *AuthRoles) *AuthTokensUpdateOne {
return atuo.SetRolesID(a.ID)
}
// Mutation returns the AuthTokensMutation object of the builder. // Mutation returns the AuthTokensMutation object of the builder.
func (atuo *AuthTokensUpdateOne) Mutation() *AuthTokensMutation { func (atuo *AuthTokensUpdateOne) Mutation() *AuthTokensMutation {
return atuo.mutation return atuo.mutation
@ -319,18 +298,6 @@ func (atuo *AuthTokensUpdateOne) ClearUser() *AuthTokensUpdateOne {
return atuo return atuo
} }
// ClearRoles clears the "roles" edge to the AuthRoles entity.
func (atuo *AuthTokensUpdateOne) ClearRoles() *AuthTokensUpdateOne {
atuo.mutation.ClearRoles()
return atuo
}
// Where appends a list predicates to the AuthTokensUpdate builder.
func (atuo *AuthTokensUpdateOne) Where(ps ...predicate.AuthTokens) *AuthTokensUpdateOne {
atuo.mutation.Where(ps...)
return atuo
}
// Select allows selecting one or more fields (columns) of the returned entity. // Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema. // The default is selecting all fields defined in the entity schema.
func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTokensUpdateOne { func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTokensUpdateOne {
@ -340,8 +307,41 @@ func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTok
// Save executes the query and returns the updated AuthTokens entity. // Save executes the query and returns the updated AuthTokens entity.
func (atuo *AuthTokensUpdateOne) Save(ctx context.Context) (*AuthTokens, error) { func (atuo *AuthTokensUpdateOne) Save(ctx context.Context) (*AuthTokens, error) {
var (
err error
node *AuthTokens
)
atuo.defaults() atuo.defaults()
return withHooks(ctx, atuo.sqlSave, atuo.mutation, atuo.hooks) if len(atuo.hooks) == 0 {
node, err = atuo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthTokensMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
atuo.mutation = mutation
node, err = atuo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(atuo.hooks) - 1; i >= 0; i-- {
if atuo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = atuo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, atuo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthTokens)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthTokensMutation", v)
}
node = nv
}
return node, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
@ -375,7 +375,16 @@ func (atuo *AuthTokensUpdateOne) defaults() {
} }
func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens, err error) { func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens, err error) {
_spec := sqlgraph.NewUpdateSpec(authtokens.Table, authtokens.Columns, sqlgraph.NewFieldSpec(authtokens.FieldID, field.TypeUUID)) _spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authtokens.Table,
Columns: authtokens.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
id, ok := atuo.mutation.ID() id, ok := atuo.mutation.ID()
if !ok { if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AuthTokens.id" for update`)} return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AuthTokens.id" for update`)}
@ -401,13 +410,25 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
} }
} }
if value, ok := atuo.mutation.UpdatedAt(); ok { if value, ok := atuo.mutation.UpdatedAt(); ok {
_spec.SetField(authtokens.FieldUpdatedAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldUpdatedAt,
})
} }
if value, ok := atuo.mutation.Token(); ok { if value, ok := atuo.mutation.Token(); ok {
_spec.SetField(authtokens.FieldToken, field.TypeBytes, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeBytes,
Value: value,
Column: authtokens.FieldToken,
})
} }
if value, ok := atuo.mutation.ExpiresAt(); ok { if value, ok := atuo.mutation.ExpiresAt(); ok {
_spec.SetField(authtokens.FieldExpiresAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: authtokens.FieldExpiresAt,
})
} }
if atuo.mutation.UserCleared() { if atuo.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
@ -417,7 +438,10 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
Columns: []string{authtokens.UserColumn}, Columns: []string{authtokens.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: user.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -430,36 +454,10 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
Columns: []string{authtokens.UserColumn}, Columns: []string{authtokens.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
}, Type: field.TypeUUID,
} Column: user.FieldID,
for _, k := range nodes { },
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if atuo.mutation.RolesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := atuo.mutation.RolesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(authroles.FieldID, field.TypeInt),
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -478,6 +476,5 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
} }
return nil, err return nil, err
} }
atuo.mutation.done = true
return _node, nil return _node, nil
} }

1601
backend/ent/client.go Normal file

File diff suppressed because it is too large Load diff

69
backend/ent/config.go Normal file
View file

@ -0,0 +1,69 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"entgo.io/ent"
"entgo.io/ent/dialect"
)
// Option function to configure the client.
type Option func(*config)
// Config is the configuration for the client and its builder.
type config struct {
// driver used for executing database requests.
driver dialect.Driver
// debug enable a debug logging.
debug bool
// log used for logging on debug mode.
log func(...any)
// hooks to execute on mutations.
hooks *hooks
}
// hooks per client, for fast access.
type hooks struct {
Attachment []ent.Hook
AuthTokens []ent.Hook
Document []ent.Hook
DocumentToken []ent.Hook
Group []ent.Hook
GroupInvitationToken []ent.Hook
Item []ent.Hook
ItemField []ent.Hook
Label []ent.Hook
Location []ent.Hook
User []ent.Hook
}
// Options applies the options on the config object.
func (c *config) options(opts ...Option) {
for _, opt := range opts {
opt(c)
}
if c.debug {
c.driver = dialect.Debug(c.driver, c.log)
}
}
// Debug enables debug logging on the ent.Driver.
func Debug() Option {
return func(c *config) {
c.debug = true
}
}
// Log sets the logging function for debug mode.
func Log(fn func(...any)) Option {
return func(c *config) {
c.log = fn
}
}
// Driver configures the client driver.
func Driver(driver dialect.Driver) Option {
return func(c *config) {
c.driver = driver
}
}

33
backend/ent/context.go Normal file
View file

@ -0,0 +1,33 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
)
type clientCtxKey struct{}
// FromContext returns a Client stored inside a context, or nil if there isn't one.
func FromContext(ctx context.Context) *Client {
c, _ := ctx.Value(clientCtxKey{}).(*Client)
return c
}
// NewContext returns a new context with the given Client attached.
func NewContext(parent context.Context, c *Client) context.Context {
return context.WithValue(parent, clientCtxKey{}, c)
}
type txCtxKey struct{}
// TxFromContext returns a Tx stored inside a context, or nil if there isn't one.
func TxFromContext(ctx context.Context) *Tx {
tx, _ := ctx.Value(txCtxKey{}).(*Tx)
return tx
}
// NewTxContext returns a new context with the given Tx attached.
func NewTxContext(parent context.Context, tx *Tx) context.Context {
return context.WithValue(parent, txCtxKey{}, tx)
}

View file

@ -7,11 +7,10 @@ import (
"strings" "strings"
"time" "time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/ent/group"
) )
// Document is the model entity for the Document schema. // Document is the model entity for the Document schema.
@ -31,18 +30,19 @@ type Document struct {
// The values are being populated by the DocumentQuery when eager-loading is set. // The values are being populated by the DocumentQuery when eager-loading is set.
Edges DocumentEdges `json:"edges"` Edges DocumentEdges `json:"edges"`
group_documents *uuid.UUID group_documents *uuid.UUID
selectValues sql.SelectValues
} }
// DocumentEdges holds the relations/edges for other nodes in the graph. // DocumentEdges holds the relations/edges for other nodes in the graph.
type DocumentEdges struct { type DocumentEdges struct {
// Group holds the value of the group edge. // Group holds the value of the group edge.
Group *Group `json:"group,omitempty"` Group *Group `json:"group,omitempty"`
// DocumentTokens holds the value of the document_tokens edge.
DocumentTokens []*DocumentToken `json:"document_tokens,omitempty"`
// Attachments holds the value of the attachments edge. // Attachments holds the value of the attachments edge.
Attachments []*Attachment `json:"attachments,omitempty"` Attachments []*Attachment `json:"attachments,omitempty"`
// loadedTypes holds the information for reporting if a // loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not. // type was loaded (or requested) in eager-loading or not.
loadedTypes [2]bool loadedTypes [3]bool
} }
// GroupOrErr returns the Group value or an error if the edge // GroupOrErr returns the Group value or an error if the edge
@ -58,10 +58,19 @@ func (e DocumentEdges) GroupOrErr() (*Group, error) {
return nil, &NotLoadedError{edge: "group"} return nil, &NotLoadedError{edge: "group"}
} }
// DocumentTokensOrErr returns the DocumentTokens value or an error if the edge
// was not loaded in eager-loading.
func (e DocumentEdges) DocumentTokensOrErr() ([]*DocumentToken, error) {
if e.loadedTypes[1] {
return e.DocumentTokens, nil
}
return nil, &NotLoadedError{edge: "document_tokens"}
}
// AttachmentsOrErr returns the Attachments value or an error if the edge // AttachmentsOrErr returns the Attachments value or an error if the edge
// was not loaded in eager-loading. // was not loaded in eager-loading.
func (e DocumentEdges) AttachmentsOrErr() ([]*Attachment, error) { func (e DocumentEdges) AttachmentsOrErr() ([]*Attachment, error) {
if e.loadedTypes[1] { if e.loadedTypes[2] {
return e.Attachments, nil return e.Attachments, nil
} }
return nil, &NotLoadedError{edge: "attachments"} return nil, &NotLoadedError{edge: "attachments"}
@ -81,7 +90,7 @@ func (*Document) scanValues(columns []string) ([]any, error) {
case document.ForeignKeys[0]: // group_documents case document.ForeignKeys[0]: // group_documents
values[i] = &sql.NullScanner{S: new(uuid.UUID)} values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default: default:
values[i] = new(sql.UnknownType) return nil, fmt.Errorf("unexpected column %q for type Document", columns[i])
} }
} }
return values, nil return values, nil
@ -132,34 +141,31 @@ func (d *Document) assignValues(columns []string, values []any) error {
d.group_documents = new(uuid.UUID) d.group_documents = new(uuid.UUID)
*d.group_documents = *value.S.(*uuid.UUID) *d.group_documents = *value.S.(*uuid.UUID)
} }
default:
d.selectValues.Set(columns[i], values[i])
} }
} }
return nil 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. // QueryGroup queries the "group" edge of the Document entity.
func (d *Document) QueryGroup() *GroupQuery { func (d *Document) QueryGroup() *GroupQuery {
return NewDocumentClient(d.config).QueryGroup(d) return (&DocumentClient{config: d.config}).QueryGroup(d)
}
// QueryDocumentTokens queries the "document_tokens" edge of the Document entity.
func (d *Document) QueryDocumentTokens() *DocumentTokenQuery {
return (&DocumentClient{config: d.config}).QueryDocumentTokens(d)
} }
// QueryAttachments queries the "attachments" edge of the Document entity. // QueryAttachments queries the "attachments" edge of the Document entity.
func (d *Document) QueryAttachments() *AttachmentQuery { func (d *Document) QueryAttachments() *AttachmentQuery {
return NewDocumentClient(d.config).QueryAttachments(d) return (&DocumentClient{config: d.config}).QueryAttachments(d)
} }
// Update returns a builder for updating this Document. // Update returns a builder for updating this Document.
// Note that you need to call Document.Unwrap() before calling this method if this Document // Note that you need to call Document.Unwrap() before calling this method if this Document
// was returned from a transaction, and the transaction was committed or rolled back. // was returned from a transaction, and the transaction was committed or rolled back.
func (d *Document) Update() *DocumentUpdateOne { func (d *Document) Update() *DocumentUpdateOne {
return NewDocumentClient(d.config).UpdateOne(d) return (&DocumentClient{config: d.config}).UpdateOne(d)
} }
// Unwrap unwraps the Document entity that was returned from a transaction after it was closed, // Unwrap unwraps the Document entity that was returned from a transaction after it was closed,
@ -195,3 +201,9 @@ func (d *Document) String() string {
// Documents is a parsable slice of Document. // Documents is a parsable slice of Document.
type Documents []*Document type Documents []*Document
func (d Documents) config(cfg config) {
for _i := range d {
d[_i].config = cfg
}
}

View file

@ -5,8 +5,6 @@ package document
import ( import (
"time" "time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -25,6 +23,8 @@ const (
FieldPath = "path" FieldPath = "path"
// EdgeGroup holds the string denoting the group edge name in mutations. // EdgeGroup holds the string denoting the group edge name in mutations.
EdgeGroup = "group" EdgeGroup = "group"
// EdgeDocumentTokens holds the string denoting the document_tokens edge name in mutations.
EdgeDocumentTokens = "document_tokens"
// EdgeAttachments holds the string denoting the attachments edge name in mutations. // EdgeAttachments holds the string denoting the attachments edge name in mutations.
EdgeAttachments = "attachments" EdgeAttachments = "attachments"
// Table holds the table name of the document in the database. // Table holds the table name of the document in the database.
@ -36,6 +36,13 @@ const (
GroupInverseTable = "groups" GroupInverseTable = "groups"
// GroupColumn is the table column denoting the group relation/edge. // GroupColumn is the table column denoting the group relation/edge.
GroupColumn = "group_documents" GroupColumn = "group_documents"
// DocumentTokensTable is the table that holds the document_tokens relation/edge.
DocumentTokensTable = "document_tokens"
// DocumentTokensInverseTable is the table name for the DocumentToken entity.
// It exists in this package in order to avoid circular dependency with the "documenttoken" package.
DocumentTokensInverseTable = "document_tokens"
// DocumentTokensColumn is the table column denoting the document_tokens relation/edge.
DocumentTokensColumn = "document_document_tokens"
// AttachmentsTable is the table that holds the attachments relation/edge. // AttachmentsTable is the table that holds the attachments relation/edge.
AttachmentsTable = "attachments" AttachmentsTable = "attachments"
// AttachmentsInverseTable is the table name for the Attachment entity. // AttachmentsInverseTable is the table name for the Attachment entity.
@ -89,66 +96,3 @@ var (
// DefaultID holds the default value on creation for the "id" field. // DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID 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),
)
}

View file

@ -8,282 +8,432 @@ import (
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// ID filters vertices based on their ID field. // ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.Document { func ID(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDEQ applies the EQ predicate on the ID field. // IDEQ applies the EQ predicate on the ID field.
func IDEQ(id uuid.UUID) predicate.Document { func IDEQ(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDNEQ applies the NEQ predicate on the ID field. // IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id uuid.UUID) predicate.Document { func IDNEQ(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldID), id))
})
} }
// IDIn applies the In predicate on the ID field. // IDIn applies the In predicate on the ID field.
func IDIn(ids ...uuid.UUID) predicate.Document { func IDIn(ids ...uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldIn(FieldID, ids...)) return predicate.Document(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
})
} }
// IDNotIn applies the NotIn predicate on the ID field. // IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...uuid.UUID) predicate.Document { func IDNotIn(ids ...uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldID, ids...)) return predicate.Document(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
})
} }
// IDGT applies the GT predicate on the ID field. // IDGT applies the GT predicate on the ID field.
func IDGT(id uuid.UUID) predicate.Document { func IDGT(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldGT(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldID), id))
})
} }
// IDGTE applies the GTE predicate on the ID field. // IDGTE applies the GTE predicate on the ID field.
func IDGTE(id uuid.UUID) predicate.Document { func IDGTE(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldID), id))
})
} }
// IDLT applies the LT predicate on the ID field. // IDLT applies the LT predicate on the ID field.
func IDLT(id uuid.UUID) predicate.Document { func IDLT(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldLT(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldID), id))
})
} }
// IDLTE applies the LTE predicate on the ID field. // IDLTE applies the LTE predicate on the ID field.
func IDLTE(id uuid.UUID) predicate.Document { func IDLTE(id uuid.UUID) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldID, id)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldID), id))
})
} }
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.Document { func CreatedAt(v time.Time) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. // UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.Document { func UpdatedAt(v time.Time) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
} }
// Title applies equality check predicate on the "title" field. It's identical to TitleEQ. // Title applies equality check predicate on the "title" field. It's identical to TitleEQ.
func Title(v string) predicate.Document { func Title(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldTitle), v))
})
} }
// Path applies equality check predicate on the "path" field. It's identical to PathEQ. // Path applies equality check predicate on the "path" field. It's identical to PathEQ.
func Path(v string) predicate.Document { func Path(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldPath), v))
})
} }
// CreatedAtEQ applies the EQ predicate on the "created_at" field. // CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Document { func CreatedAtEQ(v time.Time) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. // CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.Document { func CreatedAtNEQ(v time.Time) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtIn applies the In predicate on the "created_at" field. // CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.Document { func CreatedAtIn(vs ...time.Time) predicate.Document {
return predicate.Document(sql.FieldIn(FieldCreatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. // CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.Document { func CreatedAtNotIn(vs ...time.Time) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldCreatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtGT applies the GT predicate on the "created_at" field. // CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.Document { func CreatedAtGT(v time.Time) predicate.Document {
return predicate.Document(sql.FieldGT(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtGTE applies the GTE predicate on the "created_at" field. // CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.Document { func CreatedAtGTE(v time.Time) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLT applies the LT predicate on the "created_at" field. // CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.Document { func CreatedAtLT(v time.Time) predicate.Document {
return predicate.Document(sql.FieldLT(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLTE applies the LTE predicate on the "created_at" field. // CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.Document { func CreatedAtLTE(v time.Time) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldCreatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
})
} }
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. // UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.Document { func UpdatedAtEQ(v time.Time) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. // UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.Document { func UpdatedAtNEQ(v time.Time) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtIn applies the In predicate on the "updated_at" field. // UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.Document { func UpdatedAtIn(vs ...time.Time) predicate.Document {
return predicate.Document(sql.FieldIn(FieldUpdatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldUpdatedAt), v...))
})
} }
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. // UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.Document { func UpdatedAtNotIn(vs ...time.Time) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldUpdatedAt, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldUpdatedAt), v...))
})
} }
// UpdatedAtGT applies the GT predicate on the "updated_at" field. // UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.Document { func UpdatedAtGT(v time.Time) predicate.Document {
return predicate.Document(sql.FieldGT(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. // UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.Document { func UpdatedAtGTE(v time.Time) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtLT applies the LT predicate on the "updated_at" field. // UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.Document { func UpdatedAtLT(v time.Time) predicate.Document {
return predicate.Document(sql.FieldLT(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldUpdatedAt), v))
})
} }
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. // UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.Document { func UpdatedAtLTE(v time.Time) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldUpdatedAt, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldUpdatedAt), v))
})
} }
// TitleEQ applies the EQ predicate on the "title" field. // TitleEQ applies the EQ predicate on the "title" field.
func TitleEQ(v string) predicate.Document { func TitleEQ(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldTitle), v))
})
} }
// TitleNEQ applies the NEQ predicate on the "title" field. // TitleNEQ applies the NEQ predicate on the "title" field.
func TitleNEQ(v string) predicate.Document { func TitleNEQ(v string) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldTitle), v))
})
} }
// TitleIn applies the In predicate on the "title" field. // TitleIn applies the In predicate on the "title" field.
func TitleIn(vs ...string) predicate.Document { func TitleIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldIn(FieldTitle, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldTitle), v...))
})
} }
// TitleNotIn applies the NotIn predicate on the "title" field. // TitleNotIn applies the NotIn predicate on the "title" field.
func TitleNotIn(vs ...string) predicate.Document { func TitleNotIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldTitle, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldTitle), v...))
})
} }
// TitleGT applies the GT predicate on the "title" field. // TitleGT applies the GT predicate on the "title" field.
func TitleGT(v string) predicate.Document { func TitleGT(v string) predicate.Document {
return predicate.Document(sql.FieldGT(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldTitle), v))
})
} }
// TitleGTE applies the GTE predicate on the "title" field. // TitleGTE applies the GTE predicate on the "title" field.
func TitleGTE(v string) predicate.Document { func TitleGTE(v string) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldTitle), v))
})
} }
// TitleLT applies the LT predicate on the "title" field. // TitleLT applies the LT predicate on the "title" field.
func TitleLT(v string) predicate.Document { func TitleLT(v string) predicate.Document {
return predicate.Document(sql.FieldLT(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldTitle), v))
})
} }
// TitleLTE applies the LTE predicate on the "title" field. // TitleLTE applies the LTE predicate on the "title" field.
func TitleLTE(v string) predicate.Document { func TitleLTE(v string) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldTitle), v))
})
} }
// TitleContains applies the Contains predicate on the "title" field. // TitleContains applies the Contains predicate on the "title" field.
func TitleContains(v string) predicate.Document { func TitleContains(v string) predicate.Document {
return predicate.Document(sql.FieldContains(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.Contains(s.C(FieldTitle), v))
})
} }
// TitleHasPrefix applies the HasPrefix predicate on the "title" field. // TitleHasPrefix applies the HasPrefix predicate on the "title" field.
func TitleHasPrefix(v string) predicate.Document { func TitleHasPrefix(v string) predicate.Document {
return predicate.Document(sql.FieldHasPrefix(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasPrefix(s.C(FieldTitle), v))
})
} }
// TitleHasSuffix applies the HasSuffix predicate on the "title" field. // TitleHasSuffix applies the HasSuffix predicate on the "title" field.
func TitleHasSuffix(v string) predicate.Document { func TitleHasSuffix(v string) predicate.Document {
return predicate.Document(sql.FieldHasSuffix(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasSuffix(s.C(FieldTitle), v))
})
} }
// TitleEqualFold applies the EqualFold predicate on the "title" field. // TitleEqualFold applies the EqualFold predicate on the "title" field.
func TitleEqualFold(v string) predicate.Document { func TitleEqualFold(v string) predicate.Document {
return predicate.Document(sql.FieldEqualFold(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EqualFold(s.C(FieldTitle), v))
})
} }
// TitleContainsFold applies the ContainsFold predicate on the "title" field. // TitleContainsFold applies the ContainsFold predicate on the "title" field.
func TitleContainsFold(v string) predicate.Document { func TitleContainsFold(v string) predicate.Document {
return predicate.Document(sql.FieldContainsFold(FieldTitle, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.ContainsFold(s.C(FieldTitle), v))
})
} }
// PathEQ applies the EQ predicate on the "path" field. // PathEQ applies the EQ predicate on the "path" field.
func PathEQ(v string) predicate.Document { func PathEQ(v string) predicate.Document {
return predicate.Document(sql.FieldEQ(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldPath), v))
})
} }
// PathNEQ applies the NEQ predicate on the "path" field. // PathNEQ applies the NEQ predicate on the "path" field.
func PathNEQ(v string) predicate.Document { func PathNEQ(v string) predicate.Document {
return predicate.Document(sql.FieldNEQ(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldPath), v))
})
} }
// PathIn applies the In predicate on the "path" field. // PathIn applies the In predicate on the "path" field.
func PathIn(vs ...string) predicate.Document { func PathIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldIn(FieldPath, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldPath), v...))
})
} }
// PathNotIn applies the NotIn predicate on the "path" field. // PathNotIn applies the NotIn predicate on the "path" field.
func PathNotIn(vs ...string) predicate.Document { func PathNotIn(vs ...string) predicate.Document {
return predicate.Document(sql.FieldNotIn(FieldPath, vs...)) v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Document(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldPath), v...))
})
} }
// PathGT applies the GT predicate on the "path" field. // PathGT applies the GT predicate on the "path" field.
func PathGT(v string) predicate.Document { func PathGT(v string) predicate.Document {
return predicate.Document(sql.FieldGT(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldPath), v))
})
} }
// PathGTE applies the GTE predicate on the "path" field. // PathGTE applies the GTE predicate on the "path" field.
func PathGTE(v string) predicate.Document { func PathGTE(v string) predicate.Document {
return predicate.Document(sql.FieldGTE(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldPath), v))
})
} }
// PathLT applies the LT predicate on the "path" field. // PathLT applies the LT predicate on the "path" field.
func PathLT(v string) predicate.Document { func PathLT(v string) predicate.Document {
return predicate.Document(sql.FieldLT(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldPath), v))
})
} }
// PathLTE applies the LTE predicate on the "path" field. // PathLTE applies the LTE predicate on the "path" field.
func PathLTE(v string) predicate.Document { func PathLTE(v string) predicate.Document {
return predicate.Document(sql.FieldLTE(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldPath), v))
})
} }
// PathContains applies the Contains predicate on the "path" field. // PathContains applies the Contains predicate on the "path" field.
func PathContains(v string) predicate.Document { func PathContains(v string) predicate.Document {
return predicate.Document(sql.FieldContains(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.Contains(s.C(FieldPath), v))
})
} }
// PathHasPrefix applies the HasPrefix predicate on the "path" field. // PathHasPrefix applies the HasPrefix predicate on the "path" field.
func PathHasPrefix(v string) predicate.Document { func PathHasPrefix(v string) predicate.Document {
return predicate.Document(sql.FieldHasPrefix(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasPrefix(s.C(FieldPath), v))
})
} }
// PathHasSuffix applies the HasSuffix predicate on the "path" field. // PathHasSuffix applies the HasSuffix predicate on the "path" field.
func PathHasSuffix(v string) predicate.Document { func PathHasSuffix(v string) predicate.Document {
return predicate.Document(sql.FieldHasSuffix(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.HasSuffix(s.C(FieldPath), v))
})
} }
// PathEqualFold applies the EqualFold predicate on the "path" field. // PathEqualFold applies the EqualFold predicate on the "path" field.
func PathEqualFold(v string) predicate.Document { func PathEqualFold(v string) predicate.Document {
return predicate.Document(sql.FieldEqualFold(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.EqualFold(s.C(FieldPath), v))
})
} }
// PathContainsFold applies the ContainsFold predicate on the "path" field. // PathContainsFold applies the ContainsFold predicate on the "path" field.
func PathContainsFold(v string) predicate.Document { func PathContainsFold(v string) predicate.Document {
return predicate.Document(sql.FieldContainsFold(FieldPath, v)) return predicate.Document(func(s *sql.Selector) {
s.Where(sql.ContainsFold(s.C(FieldPath), v))
})
} }
// HasGroup applies the HasEdge predicate on the "group" edge. // HasGroup applies the HasEdge predicate on the "group" edge.
@ -291,6 +441,7 @@ func HasGroup() predicate.Document {
return predicate.Document(func(s *sql.Selector) { return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn), sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -300,7 +451,39 @@ func HasGroup() predicate.Document {
// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates). // HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates).
func HasGroupWith(preds ...predicate.Group) predicate.Document { func HasGroupWith(preds ...predicate.Group) predicate.Document {
return predicate.Document(func(s *sql.Selector) { return predicate.Document(func(s *sql.Selector) {
step := newGroupStep() step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// HasDocumentTokens applies the HasEdge predicate on the "document_tokens" edge.
func HasDocumentTokens() predicate.Document {
return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentTokensTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DocumentTokensTable, DocumentTokensColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasDocumentTokensWith applies the HasEdge predicate on the "document_tokens" edge with a given conditions (other predicates).
func HasDocumentTokensWith(preds ...predicate.DocumentToken) predicate.Document {
return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentTokensInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DocumentTokensTable, DocumentTokensColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -314,6 +497,7 @@ func HasAttachments() predicate.Document {
return predicate.Document(func(s *sql.Selector) { return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(AttachmentsTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn), sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -323,7 +507,11 @@ func HasAttachments() predicate.Document {
// HasAttachmentsWith applies the HasEdge predicate on the "attachments" edge with a given conditions (other predicates). // HasAttachmentsWith applies the HasEdge predicate on the "attachments" edge with a given conditions (other predicates).
func HasAttachmentsWith(preds ...predicate.Attachment) predicate.Document { func HasAttachmentsWith(preds ...predicate.Attachment) predicate.Document {
return predicate.Document(func(s *sql.Selector) { return predicate.Document(func(s *sql.Selector) {
step := newAttachmentsStep() step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(AttachmentsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -334,15 +522,32 @@ func HasAttachmentsWith(preds ...predicate.Attachment) predicate.Document {
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.Document) predicate.Document { func And(predicates ...predicate.Document) predicate.Document {
return predicate.Document(sql.AndPredicates(predicates...)) return predicate.Document(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
} }
// Or groups predicates with the OR operator between them. // Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.Document) predicate.Document { func Or(predicates ...predicate.Document) predicate.Document {
return predicate.Document(sql.OrPredicates(predicates...)) return predicate.Document(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())
})
} }
// Not applies the not operator on the given predicate. // Not applies the not operator on the given predicate.
func Not(p predicate.Document) predicate.Document { func Not(p predicate.Document) predicate.Document {
return predicate.Document(sql.NotPredicates(p)) return predicate.Document(func(s *sql.Selector) {
p(s.Not())
})
} }

View file

@ -11,9 +11,10 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/ent/documenttoken"
"github.com/hay-kot/homebox/backend/ent/group"
) )
// DocumentCreate is the builder for creating a Document entity. // DocumentCreate is the builder for creating a Document entity.
@ -88,6 +89,21 @@ func (dc *DocumentCreate) SetGroup(g *Group) *DocumentCreate {
return dc.SetGroupID(g.ID) return dc.SetGroupID(g.ID)
} }
// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by IDs.
func (dc *DocumentCreate) AddDocumentTokenIDs(ids ...uuid.UUID) *DocumentCreate {
dc.mutation.AddDocumentTokenIDs(ids...)
return dc
}
// AddDocumentTokens adds the "document_tokens" edges to the DocumentToken entity.
func (dc *DocumentCreate) AddDocumentTokens(d ...*DocumentToken) *DocumentCreate {
ids := make([]uuid.UUID, len(d))
for i := range d {
ids[i] = d[i].ID
}
return dc.AddDocumentTokenIDs(ids...)
}
// AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs.
func (dc *DocumentCreate) AddAttachmentIDs(ids ...uuid.UUID) *DocumentCreate { func (dc *DocumentCreate) AddAttachmentIDs(ids ...uuid.UUID) *DocumentCreate {
dc.mutation.AddAttachmentIDs(ids...) dc.mutation.AddAttachmentIDs(ids...)
@ -110,8 +126,50 @@ func (dc *DocumentCreate) Mutation() *DocumentMutation {
// Save creates the Document in the database. // Save creates the Document in the database.
func (dc *DocumentCreate) Save(ctx context.Context) (*Document, error) { func (dc *DocumentCreate) Save(ctx context.Context) (*Document, error) {
var (
err error
node *Document
)
dc.defaults() dc.defaults()
return withHooks(ctx, dc.sqlSave, dc.mutation, dc.hooks) if len(dc.hooks) == 0 {
if err = dc.check(); err != nil {
return nil, err
}
node, err = dc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = dc.check(); err != nil {
return nil, err
}
dc.mutation = mutation
if node, err = dc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(dc.hooks) - 1; i >= 0; i-- {
if dc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = dc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, dc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Document)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from DocumentMutation", v)
}
node = nv
}
return node, err
} }
// SaveX calls Save and panics if Save returns an error. // SaveX calls Save and panics if Save returns an error.
@ -183,9 +241,6 @@ func (dc *DocumentCreate) check() error {
} }
func (dc *DocumentCreate) sqlSave(ctx context.Context) (*Document, error) { func (dc *DocumentCreate) sqlSave(ctx context.Context) (*Document, error) {
if err := dc.check(); err != nil {
return nil, err
}
_node, _spec := dc.createSpec() _node, _spec := dc.createSpec()
if err := sqlgraph.CreateNode(ctx, dc.driver, _spec); err != nil { if err := sqlgraph.CreateNode(ctx, dc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) { if sqlgraph.IsConstraintError(err) {
@ -200,34 +255,54 @@ func (dc *DocumentCreate) sqlSave(ctx context.Context) (*Document, error) {
return nil, err return nil, err
} }
} }
dc.mutation.id = &_node.ID
dc.mutation.done = true
return _node, nil return _node, nil
} }
func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) { func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
var ( var (
_node = &Document{config: dc.config} _node = &Document{config: dc.config}
_spec = sqlgraph.NewCreateSpec(document.Table, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID)) _spec = &sqlgraph.CreateSpec{
Table: document.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
}
) )
if id, ok := dc.mutation.ID(); ok { if id, ok := dc.mutation.ID(); ok {
_node.ID = id _node.ID = id
_spec.ID.Value = &id _spec.ID.Value = &id
} }
if value, ok := dc.mutation.CreatedAt(); ok { if value, ok := dc.mutation.CreatedAt(); ok {
_spec.SetField(document.FieldCreatedAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: document.FieldCreatedAt,
})
_node.CreatedAt = value _node.CreatedAt = value
} }
if value, ok := dc.mutation.UpdatedAt(); ok { if value, ok := dc.mutation.UpdatedAt(); ok {
_spec.SetField(document.FieldUpdatedAt, field.TypeTime, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: document.FieldUpdatedAt,
})
_node.UpdatedAt = value _node.UpdatedAt = value
} }
if value, ok := dc.mutation.Title(); ok { if value, ok := dc.mutation.Title(); ok {
_spec.SetField(document.FieldTitle, field.TypeString, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeString,
Value: value,
Column: document.FieldTitle,
})
_node.Title = value _node.Title = value
} }
if value, ok := dc.mutation.Path(); ok { if value, ok := dc.mutation.Path(); ok {
_spec.SetField(document.FieldPath, field.TypeString, value) _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeString,
Value: value,
Column: document.FieldPath,
})
_node.Path = value _node.Path = value
} }
if nodes := dc.mutation.GroupIDs(); len(nodes) > 0 { if nodes := dc.mutation.GroupIDs(); len(nodes) > 0 {
@ -238,7 +313,10 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
Columns: []string{document.GroupColumn}, Columns: []string{document.GroupColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -247,6 +325,25 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
_node.group_documents = &nodes[0] _node.group_documents = &nodes[0]
_spec.Edges = append(_spec.Edges, edge) _spec.Edges = append(_spec.Edges, edge)
} }
if nodes := dc.mutation.DocumentTokensIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := dc.mutation.AttachmentsIDs(); len(nodes) > 0 { if nodes := dc.mutation.AttachmentsIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
@ -255,7 +352,10 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -269,15 +369,11 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
// DocumentCreateBulk is the builder for creating many Document entities in bulk. // DocumentCreateBulk is the builder for creating many Document entities in bulk.
type DocumentCreateBulk struct { type DocumentCreateBulk struct {
config config
err error
builders []*DocumentCreate builders []*DocumentCreate
} }
// Save creates the Document entities in the database. // Save creates the Document entities in the database.
func (dcb *DocumentCreateBulk) Save(ctx context.Context) ([]*Document, error) { func (dcb *DocumentCreateBulk) Save(ctx context.Context) ([]*Document, error) {
if dcb.err != nil {
return nil, dcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(dcb.builders)) specs := make([]*sqlgraph.CreateSpec, len(dcb.builders))
nodes := make([]*Document, len(dcb.builders)) nodes := make([]*Document, len(dcb.builders))
mutators := make([]Mutator, len(dcb.builders)) mutators := make([]Mutator, len(dcb.builders))
@ -294,8 +390,8 @@ func (dcb *DocumentCreateBulk) Save(ctx context.Context) ([]*Document, error) {
return nil, err return nil, err
} }
builder.mutation = mutation builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec() nodes[i], specs[i] = builder.createSpec()
var err error
if i < len(mutators)-1 { if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, dcb.builders[i+1].mutation) _, err = mutators[i+1].Mutate(root, dcb.builders[i+1].mutation)
} else { } else {

View file

@ -4,12 +4,13 @@ package ent
import ( import (
"context" "context"
"fmt"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/predicate"
) )
// DocumentDelete is the builder for deleting a Document entity. // DocumentDelete is the builder for deleting a Document entity.
@ -27,7 +28,34 @@ func (dd *DocumentDelete) Where(ps ...predicate.Document) *DocumentDelete {
// Exec executes the deletion query and returns how many vertices were deleted. // Exec executes the deletion query and returns how many vertices were deleted.
func (dd *DocumentDelete) Exec(ctx context.Context) (int, error) { func (dd *DocumentDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, dd.sqlExec, dd.mutation, dd.hooks) var (
err error
affected int
)
if len(dd.hooks) == 0 {
affected, err = dd.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
dd.mutation = mutation
affected, err = dd.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(dd.hooks) - 1; i >= 0; i-- {
if dd.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = dd.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, dd.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
@ -40,7 +68,15 @@ func (dd *DocumentDelete) ExecX(ctx context.Context) int {
} }
func (dd *DocumentDelete) sqlExec(ctx context.Context) (int, error) { func (dd *DocumentDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(document.Table, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID)) _spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: document.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
}
if ps := dd.mutation.predicates; len(ps) > 0 { if ps := dd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
@ -52,7 +88,6 @@ func (dd *DocumentDelete) sqlExec(ctx context.Context) (int, error) {
if err != nil && sqlgraph.IsConstraintError(err) { if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
dd.mutation.done = true
return affected, err return affected, err
} }
@ -61,12 +96,6 @@ type DocumentDeleteOne struct {
dd *DocumentDelete dd *DocumentDelete
} }
// Where appends a list predicates to the DocumentDelete builder.
func (ddo *DocumentDeleteOne) Where(ps ...predicate.Document) *DocumentDeleteOne {
ddo.dd.mutation.Where(ps...)
return ddo
}
// Exec executes the deletion query. // Exec executes the deletion query.
func (ddo *DocumentDeleteOne) Exec(ctx context.Context) error { func (ddo *DocumentDeleteOne) Exec(ctx context.Context) error {
n, err := ddo.dd.Exec(ctx) n, err := ddo.dd.Exec(ctx)
@ -82,7 +111,5 @@ func (ddo *DocumentDeleteOne) Exec(ctx context.Context) error {
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ddo *DocumentDeleteOne) ExecX(ctx context.Context) { func (ddo *DocumentDeleteOne) ExecX(ctx context.Context) {
if err := ddo.Exec(ctx); err != nil { ddo.dd.ExecX(ctx)
panic(err)
}
} }

View file

@ -12,22 +12,26 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/ent/documenttoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/group"
"github.com/hay-kot/homebox/backend/ent/predicate"
) )
// DocumentQuery is the builder for querying Document entities. // DocumentQuery is the builder for querying Document entities.
type DocumentQuery struct { type DocumentQuery struct {
config config
ctx *QueryContext limit *int
order []document.OrderOption offset *int
inters []Interceptor unique *bool
predicates []predicate.Document order []OrderFunc
withGroup *GroupQuery fields []string
withAttachments *AttachmentQuery predicates []predicate.Document
withFKs bool withGroup *GroupQuery
withDocumentTokens *DocumentTokenQuery
withAttachments *AttachmentQuery
withFKs bool
// intermediate query (i.e. traversal path). // intermediate query (i.e. traversal path).
sql *sql.Selector sql *sql.Selector
path func(context.Context) (*sql.Selector, error) path func(context.Context) (*sql.Selector, error)
@ -39,34 +43,34 @@ func (dq *DocumentQuery) Where(ps ...predicate.Document) *DocumentQuery {
return dq return dq
} }
// Limit the number of records to be returned by this query. // Limit adds a limit step to the query.
func (dq *DocumentQuery) Limit(limit int) *DocumentQuery { func (dq *DocumentQuery) Limit(limit int) *DocumentQuery {
dq.ctx.Limit = &limit dq.limit = &limit
return dq return dq
} }
// Offset to start from. // Offset adds an offset step to the query.
func (dq *DocumentQuery) Offset(offset int) *DocumentQuery { func (dq *DocumentQuery) Offset(offset int) *DocumentQuery {
dq.ctx.Offset = &offset dq.offset = &offset
return dq return dq
} }
// Unique configures the query builder to filter duplicate records on query. // Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method. // By default, unique is set to true, and can be disabled using this method.
func (dq *DocumentQuery) Unique(unique bool) *DocumentQuery { func (dq *DocumentQuery) Unique(unique bool) *DocumentQuery {
dq.ctx.Unique = &unique dq.unique = &unique
return dq return dq
} }
// Order specifies how the records should be ordered. // Order adds an order step to the query.
func (dq *DocumentQuery) Order(o ...document.OrderOption) *DocumentQuery { func (dq *DocumentQuery) Order(o ...OrderFunc) *DocumentQuery {
dq.order = append(dq.order, o...) dq.order = append(dq.order, o...)
return dq return dq
} }
// QueryGroup chains the current query on the "group" edge. // QueryGroup chains the current query on the "group" edge.
func (dq *DocumentQuery) QueryGroup() *GroupQuery { func (dq *DocumentQuery) QueryGroup() *GroupQuery {
query := (&GroupClient{config: dq.config}).Query() query := &GroupQuery{config: dq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil { if err := dq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
@ -86,9 +90,31 @@ func (dq *DocumentQuery) QueryGroup() *GroupQuery {
return query return query
} }
// QueryDocumentTokens chains the current query on the "document_tokens" edge.
func (dq *DocumentQuery) QueryDocumentTokens() *DocumentTokenQuery {
query := &DocumentTokenQuery{config: dq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := dq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(document.Table, document.FieldID, selector),
sqlgraph.To(documenttoken.Table, documenttoken.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, document.DocumentTokensTable, document.DocumentTokensColumn),
)
fromU = sqlgraph.SetNeighbors(dq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// QueryAttachments chains the current query on the "attachments" edge. // QueryAttachments chains the current query on the "attachments" edge.
func (dq *DocumentQuery) QueryAttachments() *AttachmentQuery { func (dq *DocumentQuery) QueryAttachments() *AttachmentQuery {
query := (&AttachmentClient{config: dq.config}).Query() query := &AttachmentQuery{config: dq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil { if err := dq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
@ -111,7 +137,7 @@ func (dq *DocumentQuery) QueryAttachments() *AttachmentQuery {
// First returns the first Document entity from the query. // First returns the first Document entity from the query.
// Returns a *NotFoundError when no Document was found. // Returns a *NotFoundError when no Document was found.
func (dq *DocumentQuery) First(ctx context.Context) (*Document, error) { func (dq *DocumentQuery) First(ctx context.Context) (*Document, error) {
nodes, err := dq.Limit(1).All(setContextOp(ctx, dq.ctx, "First")) nodes, err := dq.Limit(1).All(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -134,7 +160,7 @@ func (dq *DocumentQuery) FirstX(ctx context.Context) *Document {
// Returns a *NotFoundError when no Document ID was found. // Returns a *NotFoundError when no Document ID was found.
func (dq *DocumentQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { func (dq *DocumentQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID var ids []uuid.UUID
if ids, err = dq.Limit(1).IDs(setContextOp(ctx, dq.ctx, "FirstID")); err != nil { if ids, err = dq.Limit(1).IDs(ctx); err != nil {
return return
} }
if len(ids) == 0 { if len(ids) == 0 {
@ -157,7 +183,7 @@ func (dq *DocumentQuery) FirstIDX(ctx context.Context) uuid.UUID {
// Returns a *NotSingularError when more than one Document entity is found. // Returns a *NotSingularError when more than one Document entity is found.
// Returns a *NotFoundError when no Document entities are found. // Returns a *NotFoundError when no Document entities are found.
func (dq *DocumentQuery) Only(ctx context.Context) (*Document, error) { func (dq *DocumentQuery) Only(ctx context.Context) (*Document, error) {
nodes, err := dq.Limit(2).All(setContextOp(ctx, dq.ctx, "Only")) nodes, err := dq.Limit(2).All(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -185,7 +211,7 @@ func (dq *DocumentQuery) OnlyX(ctx context.Context) *Document {
// Returns a *NotFoundError when no entities are found. // Returns a *NotFoundError when no entities are found.
func (dq *DocumentQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { func (dq *DocumentQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID var ids []uuid.UUID
if ids, err = dq.Limit(2).IDs(setContextOp(ctx, dq.ctx, "OnlyID")); err != nil { if ids, err = dq.Limit(2).IDs(ctx); err != nil {
return return
} }
switch len(ids) { switch len(ids) {
@ -210,12 +236,10 @@ func (dq *DocumentQuery) OnlyIDX(ctx context.Context) uuid.UUID {
// All executes the query and returns a list of Documents. // All executes the query and returns a list of Documents.
func (dq *DocumentQuery) All(ctx context.Context) ([]*Document, error) { func (dq *DocumentQuery) All(ctx context.Context) ([]*Document, error) {
ctx = setContextOp(ctx, dq.ctx, "All")
if err := dq.prepareQuery(ctx); err != nil { if err := dq.prepareQuery(ctx); err != nil {
return nil, err return nil, err
} }
qr := querierAll[[]*Document, *DocumentQuery]() return dq.sqlAll(ctx)
return withInterceptors[[]*Document](ctx, dq, qr, dq.inters)
} }
// AllX is like All, but panics if an error occurs. // AllX is like All, but panics if an error occurs.
@ -228,12 +252,9 @@ func (dq *DocumentQuery) AllX(ctx context.Context) []*Document {
} }
// IDs executes the query and returns a list of Document IDs. // IDs executes the query and returns a list of Document IDs.
func (dq *DocumentQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { func (dq *DocumentQuery) IDs(ctx context.Context) ([]uuid.UUID, error) {
if dq.ctx.Unique == nil && dq.path != nil { var ids []uuid.UUID
dq.Unique(true) if err := dq.Select(document.FieldID).Scan(ctx, &ids); err != nil {
}
ctx = setContextOp(ctx, dq.ctx, "IDs")
if err = dq.Select(document.FieldID).Scan(ctx, &ids); err != nil {
return nil, err return nil, err
} }
return ids, nil return ids, nil
@ -250,11 +271,10 @@ func (dq *DocumentQuery) IDsX(ctx context.Context) []uuid.UUID {
// Count returns the count of the given query. // Count returns the count of the given query.
func (dq *DocumentQuery) Count(ctx context.Context) (int, error) { func (dq *DocumentQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, dq.ctx, "Count")
if err := dq.prepareQuery(ctx); err != nil { if err := dq.prepareQuery(ctx); err != nil {
return 0, err return 0, err
} }
return withInterceptors[int](ctx, dq, querierCount[*DocumentQuery](), dq.inters) return dq.sqlCount(ctx)
} }
// CountX is like Count, but panics if an error occurs. // CountX is like Count, but panics if an error occurs.
@ -268,15 +288,10 @@ func (dq *DocumentQuery) CountX(ctx context.Context) int {
// Exist returns true if the query has elements in the graph. // Exist returns true if the query has elements in the graph.
func (dq *DocumentQuery) Exist(ctx context.Context) (bool, error) { func (dq *DocumentQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, dq.ctx, "Exist") if err := dq.prepareQuery(ctx); err != nil {
switch _, err := dq.FirstID(ctx); { return false, err
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
} }
return dq.sqlExist(ctx)
} }
// ExistX is like Exist, but panics if an error occurs. // ExistX is like Exist, but panics if an error occurs.
@ -295,23 +310,25 @@ func (dq *DocumentQuery) Clone() *DocumentQuery {
return nil return nil
} }
return &DocumentQuery{ return &DocumentQuery{
config: dq.config, config: dq.config,
ctx: dq.ctx.Clone(), limit: dq.limit,
order: append([]document.OrderOption{}, dq.order...), offset: dq.offset,
inters: append([]Interceptor{}, dq.inters...), order: append([]OrderFunc{}, dq.order...),
predicates: append([]predicate.Document{}, dq.predicates...), predicates: append([]predicate.Document{}, dq.predicates...),
withGroup: dq.withGroup.Clone(), withGroup: dq.withGroup.Clone(),
withAttachments: dq.withAttachments.Clone(), withDocumentTokens: dq.withDocumentTokens.Clone(),
withAttachments: dq.withAttachments.Clone(),
// clone intermediate query. // clone intermediate query.
sql: dq.sql.Clone(), sql: dq.sql.Clone(),
path: dq.path, path: dq.path,
unique: dq.unique,
} }
} }
// WithGroup tells the query-builder to eager-load the nodes that are connected to // WithGroup tells the query-builder to eager-load the nodes that are connected to
// the "group" edge. The optional arguments are used to configure the query builder of the edge. // the "group" edge. The optional arguments are used to configure the query builder of the edge.
func (dq *DocumentQuery) WithGroup(opts ...func(*GroupQuery)) *DocumentQuery { func (dq *DocumentQuery) WithGroup(opts ...func(*GroupQuery)) *DocumentQuery {
query := (&GroupClient{config: dq.config}).Query() query := &GroupQuery{config: dq.config}
for _, opt := range opts { for _, opt := range opts {
opt(query) opt(query)
} }
@ -319,10 +336,21 @@ func (dq *DocumentQuery) WithGroup(opts ...func(*GroupQuery)) *DocumentQuery {
return dq return dq
} }
// WithDocumentTokens tells the query-builder to eager-load the nodes that are connected to
// the "document_tokens" edge. The optional arguments are used to configure the query builder of the edge.
func (dq *DocumentQuery) WithDocumentTokens(opts ...func(*DocumentTokenQuery)) *DocumentQuery {
query := &DocumentTokenQuery{config: dq.config}
for _, opt := range opts {
opt(query)
}
dq.withDocumentTokens = query
return dq
}
// WithAttachments tells the query-builder to eager-load the nodes that are connected to // WithAttachments tells the query-builder to eager-load the nodes that are connected to
// the "attachments" edge. The optional arguments are used to configure the query builder of the edge. // the "attachments" edge. The optional arguments are used to configure the query builder of the edge.
func (dq *DocumentQuery) WithAttachments(opts ...func(*AttachmentQuery)) *DocumentQuery { func (dq *DocumentQuery) WithAttachments(opts ...func(*AttachmentQuery)) *DocumentQuery {
query := (&AttachmentClient{config: dq.config}).Query() query := &AttachmentQuery{config: dq.config}
for _, opt := range opts { for _, opt := range opts {
opt(query) opt(query)
} }
@ -345,11 +373,16 @@ func (dq *DocumentQuery) WithAttachments(opts ...func(*AttachmentQuery)) *Docume
// Aggregate(ent.Count()). // Aggregate(ent.Count()).
// Scan(ctx, &v) // Scan(ctx, &v)
func (dq *DocumentQuery) GroupBy(field string, fields ...string) *DocumentGroupBy { func (dq *DocumentQuery) GroupBy(field string, fields ...string) *DocumentGroupBy {
dq.ctx.Fields = append([]string{field}, fields...) grbuild := &DocumentGroupBy{config: dq.config}
grbuild := &DocumentGroupBy{build: dq} grbuild.fields = append([]string{field}, fields...)
grbuild.flds = &dq.ctx.Fields grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := dq.prepareQuery(ctx); err != nil {
return nil, err
}
return dq.sqlQuery(ctx), nil
}
grbuild.label = document.Label grbuild.label = document.Label
grbuild.scan = grbuild.Scan grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild return grbuild
} }
@ -366,30 +399,15 @@ func (dq *DocumentQuery) GroupBy(field string, fields ...string) *DocumentGroupB
// Select(document.FieldCreatedAt). // Select(document.FieldCreatedAt).
// Scan(ctx, &v) // Scan(ctx, &v)
func (dq *DocumentQuery) Select(fields ...string) *DocumentSelect { func (dq *DocumentQuery) Select(fields ...string) *DocumentSelect {
dq.ctx.Fields = append(dq.ctx.Fields, fields...) dq.fields = append(dq.fields, fields...)
sbuild := &DocumentSelect{DocumentQuery: dq} selbuild := &DocumentSelect{DocumentQuery: dq}
sbuild.label = document.Label selbuild.label = document.Label
sbuild.flds, sbuild.scan = &dq.ctx.Fields, sbuild.Scan selbuild.flds, selbuild.scan = &dq.fields, selbuild.Scan
return sbuild return selbuild
}
// Aggregate returns a DocumentSelect configured with the given aggregations.
func (dq *DocumentQuery) Aggregate(fns ...AggregateFunc) *DocumentSelect {
return dq.Select().Aggregate(fns...)
} }
func (dq *DocumentQuery) prepareQuery(ctx context.Context) error { func (dq *DocumentQuery) prepareQuery(ctx context.Context) error {
for _, inter := range dq.inters { for _, f := range dq.fields {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, dq); err != nil {
return err
}
}
}
for _, f := range dq.ctx.Fields {
if !document.ValidColumn(f) { if !document.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
} }
@ -409,8 +427,9 @@ func (dq *DocumentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Doc
nodes = []*Document{} nodes = []*Document{}
withFKs = dq.withFKs withFKs = dq.withFKs
_spec = dq.querySpec() _spec = dq.querySpec()
loadedTypes = [2]bool{ loadedTypes = [3]bool{
dq.withGroup != nil, dq.withGroup != nil,
dq.withDocumentTokens != nil,
dq.withAttachments != nil, dq.withAttachments != nil,
} }
) )
@ -444,6 +463,13 @@ func (dq *DocumentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Doc
return nil, err return nil, err
} }
} }
if query := dq.withDocumentTokens; query != nil {
if err := dq.loadDocumentTokens(ctx, query, nodes,
func(n *Document) { n.Edges.DocumentTokens = []*DocumentToken{} },
func(n *Document, e *DocumentToken) { n.Edges.DocumentTokens = append(n.Edges.DocumentTokens, e) }); err != nil {
return nil, err
}
}
if query := dq.withAttachments; query != nil { if query := dq.withAttachments; query != nil {
if err := dq.loadAttachments(ctx, query, nodes, if err := dq.loadAttachments(ctx, query, nodes,
func(n *Document) { n.Edges.Attachments = []*Attachment{} }, func(n *Document) { n.Edges.Attachments = []*Attachment{} },
@ -467,9 +493,6 @@ func (dq *DocumentQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes
} }
nodeids[fk] = append(nodeids[fk], nodes[i]) nodeids[fk] = append(nodeids[fk], nodes[i])
} }
if len(ids) == 0 {
return nil
}
query.Where(group.IDIn(ids...)) query.Where(group.IDIn(ids...))
neighbors, err := query.All(ctx) neighbors, err := query.All(ctx)
if err != nil { if err != nil {
@ -486,6 +509,37 @@ func (dq *DocumentQuery) loadGroup(ctx context.Context, query *GroupQuery, nodes
} }
return nil return nil
} }
func (dq *DocumentQuery) loadDocumentTokens(ctx context.Context, query *DocumentTokenQuery, nodes []*Document, init func(*Document), assign func(*Document, *DocumentToken)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[uuid.UUID]*Document)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
query.withFKs = true
query.Where(predicate.DocumentToken(func(s *sql.Selector) {
s.Where(sql.InValues(document.DocumentTokensColumn, fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.document_document_tokens
if fk == nil {
return fmt.Errorf(`foreign-key "document_document_tokens" is nil for node %v`, n.ID)
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "document_document_tokens" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
return nil
}
func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQuery, nodes []*Document, init func(*Document), assign func(*Document, *Attachment)) error { func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQuery, nodes []*Document, init func(*Document), assign func(*Document, *Attachment)) error {
fks := make([]driver.Value, 0, len(nodes)) fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[uuid.UUID]*Document) nodeids := make(map[uuid.UUID]*Document)
@ -498,7 +552,7 @@ func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQ
} }
query.withFKs = true query.withFKs = true
query.Where(predicate.Attachment(func(s *sql.Selector) { query.Where(predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(document.AttachmentsColumn), fks...)) s.Where(sql.InValues(document.AttachmentsColumn, fks...))
})) }))
neighbors, err := query.All(ctx) neighbors, err := query.All(ctx)
if err != nil { if err != nil {
@ -511,7 +565,7 @@ func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQ
} }
node, ok := nodeids[*fk] node, ok := nodeids[*fk]
if !ok { if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "document_attachments" returned %v for node %v`, *fk, n.ID) return fmt.Errorf(`unexpected foreign-key "document_attachments" returned %v for node %v`, *fk, n.ID)
} }
assign(node, n) assign(node, n)
} }
@ -520,22 +574,41 @@ func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQ
func (dq *DocumentQuery) sqlCount(ctx context.Context) (int, error) { func (dq *DocumentQuery) sqlCount(ctx context.Context) (int, error) {
_spec := dq.querySpec() _spec := dq.querySpec()
_spec.Node.Columns = dq.ctx.Fields _spec.Node.Columns = dq.fields
if len(dq.ctx.Fields) > 0 { if len(dq.fields) > 0 {
_spec.Unique = dq.ctx.Unique != nil && *dq.ctx.Unique _spec.Unique = dq.unique != nil && *dq.unique
} }
return sqlgraph.CountNodes(ctx, dq.driver, _spec) return sqlgraph.CountNodes(ctx, dq.driver, _spec)
} }
func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec { func (dq *DocumentQuery) sqlExist(ctx context.Context) (bool, error) {
_spec := sqlgraph.NewQuerySpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID)) switch _, err := dq.FirstID(ctx); {
_spec.From = dq.sql case IsNotFound(err):
if unique := dq.ctx.Unique; unique != nil { return false, nil
_spec.Unique = *unique case err != nil:
} else if dq.path != nil { return false, fmt.Errorf("ent: check existence: %w", err)
_spec.Unique = true default:
return true, nil
} }
if fields := dq.ctx.Fields; len(fields) > 0 { }
func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: document.Table,
Columns: document.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
From: dq.sql,
Unique: true,
}
if unique := dq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := dq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields)) _spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, document.FieldID) _spec.Node.Columns = append(_spec.Node.Columns, document.FieldID)
for i := range fields { for i := range fields {
@ -551,10 +624,10 @@ func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
} }
} }
} }
if limit := dq.ctx.Limit; limit != nil { if limit := dq.limit; limit != nil {
_spec.Limit = *limit _spec.Limit = *limit
} }
if offset := dq.ctx.Offset; offset != nil { if offset := dq.offset; offset != nil {
_spec.Offset = *offset _spec.Offset = *offset
} }
if ps := dq.order; len(ps) > 0 { if ps := dq.order; len(ps) > 0 {
@ -570,7 +643,7 @@ func (dq *DocumentQuery) querySpec() *sqlgraph.QuerySpec {
func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector { func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(dq.driver.Dialect()) builder := sql.Dialect(dq.driver.Dialect())
t1 := builder.Table(document.Table) t1 := builder.Table(document.Table)
columns := dq.ctx.Fields columns := dq.fields
if len(columns) == 0 { if len(columns) == 0 {
columns = document.Columns columns = document.Columns
} }
@ -579,7 +652,7 @@ func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
selector = dq.sql selector = dq.sql
selector.Select(selector.Columns(columns...)...) selector.Select(selector.Columns(columns...)...)
} }
if dq.ctx.Unique != nil && *dq.ctx.Unique { if dq.unique != nil && *dq.unique {
selector.Distinct() selector.Distinct()
} }
for _, p := range dq.predicates { for _, p := range dq.predicates {
@ -588,12 +661,12 @@ func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
for _, p := range dq.order { for _, p := range dq.order {
p(selector) p(selector)
} }
if offset := dq.ctx.Offset; offset != nil { if offset := dq.offset; offset != nil {
// limit is mandatory for offset clause. We start // limit is mandatory for offset clause. We start
// with default value, and override it below if needed. // with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32) selector.Offset(*offset).Limit(math.MaxInt32)
} }
if limit := dq.ctx.Limit; limit != nil { if limit := dq.limit; limit != nil {
selector.Limit(*limit) selector.Limit(*limit)
} }
return selector return selector
@ -601,8 +674,13 @@ func (dq *DocumentQuery) sqlQuery(ctx context.Context) *sql.Selector {
// DocumentGroupBy is the group-by builder for Document entities. // DocumentGroupBy is the group-by builder for Document entities.
type DocumentGroupBy struct { type DocumentGroupBy struct {
config
selector selector
build *DocumentQuery fields []string
fns []AggregateFunc
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
} }
// Aggregate adds the given aggregation functions to the group-by query. // Aggregate adds the given aggregation functions to the group-by query.
@ -611,77 +689,74 @@ func (dgb *DocumentGroupBy) Aggregate(fns ...AggregateFunc) *DocumentGroupBy {
return dgb return dgb
} }
// Scan applies the selector query and scans the result into the given value. // Scan applies the group-by query and scans the result into the given value.
func (dgb *DocumentGroupBy) Scan(ctx context.Context, v any) error { func (dgb *DocumentGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, dgb.build.ctx, "GroupBy") query, err := dgb.path(ctx)
if err := dgb.build.prepareQuery(ctx); err != nil { if err != nil {
return err return err
} }
return scanWithInterceptors[*DocumentQuery, *DocumentGroupBy](ctx, dgb.build, dgb, dgb.build.inters, v) dgb.sql = query
return dgb.sqlScan(ctx, v)
} }
func (dgb *DocumentGroupBy) sqlScan(ctx context.Context, root *DocumentQuery, v any) error { func (dgb *DocumentGroupBy) sqlScan(ctx context.Context, v any) error {
selector := root.sqlQuery(ctx).Select() for _, f := range dgb.fields {
aggregation := make([]string, 0, len(dgb.fns)) if !document.ValidColumn(f) {
for _, fn := range dgb.fns { return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*dgb.flds)+len(dgb.fns))
for _, f := range *dgb.flds {
columns = append(columns, selector.C(f))
} }
columns = append(columns, aggregation...)
selector.Select(columns...)
} }
selector.GroupBy(selector.Columns(*dgb.flds...)...) selector := dgb.sqlQuery()
if err := selector.Err(); err != nil { if err := selector.Err(); err != nil {
return err return err
} }
rows := &sql.Rows{} rows := &sql.Rows{}
query, args := selector.Query() query, args := selector.Query()
if err := dgb.build.driver.Query(ctx, query, args, rows); err != nil { if err := dgb.driver.Query(ctx, query, args, rows); err != nil {
return err return err
} }
defer rows.Close() defer rows.Close()
return sql.ScanSlice(rows, v) return sql.ScanSlice(rows, v)
} }
func (dgb *DocumentGroupBy) sqlQuery() *sql.Selector {
selector := dgb.sql.Select()
aggregation := make([]string, 0, len(dgb.fns))
for _, fn := range dgb.fns {
aggregation = append(aggregation, fn(selector))
}
// If no columns were selected in a custom aggregation function, the default
// selection is the fields used for "group-by", and the aggregation functions.
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(dgb.fields)+len(dgb.fns))
for _, f := range dgb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(dgb.fields...)...)
}
// DocumentSelect is the builder for selecting fields of Document entities. // DocumentSelect is the builder for selecting fields of Document entities.
type DocumentSelect struct { type DocumentSelect struct {
*DocumentQuery *DocumentQuery
selector selector
} // intermediate query (i.e. traversal path).
sql *sql.Selector
// Aggregate adds the given aggregation functions to the selector query.
func (ds *DocumentSelect) Aggregate(fns ...AggregateFunc) *DocumentSelect {
ds.fns = append(ds.fns, fns...)
return ds
} }
// Scan applies the selector query and scans the result into the given value. // Scan applies the selector query and scans the result into the given value.
func (ds *DocumentSelect) Scan(ctx context.Context, v any) error { func (ds *DocumentSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, ds.ctx, "Select")
if err := ds.prepareQuery(ctx); err != nil { if err := ds.prepareQuery(ctx); err != nil {
return err return err
} }
return scanWithInterceptors[*DocumentQuery, *DocumentSelect](ctx, ds.DocumentQuery, ds, ds.inters, v) ds.sql = ds.DocumentQuery.sqlQuery(ctx)
return ds.sqlScan(ctx, v)
} }
func (ds *DocumentSelect) sqlScan(ctx context.Context, root *DocumentQuery, v any) error { func (ds *DocumentSelect) sqlScan(ctx context.Context, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(ds.fns))
for _, fn := range ds.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*ds.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{} rows := &sql.Rows{}
query, args := selector.Query() query, args := ds.sql.Query()
if err := ds.driver.Query(ctx, query, args, rows); err != nil { if err := ds.driver.Query(ctx, query, args, rows); err != nil {
return err return err
} }

View file

@ -12,10 +12,11 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/internal/data/ent/group" "github.com/hay-kot/homebox/backend/ent/documenttoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate" "github.com/hay-kot/homebox/backend/ent/group"
"github.com/hay-kot/homebox/backend/ent/predicate"
) )
// DocumentUpdate is the builder for updating Document entities. // DocumentUpdate is the builder for updating Document entities.
@ -43,28 +44,12 @@ func (du *DocumentUpdate) SetTitle(s string) *DocumentUpdate {
return du return du
} }
// SetNillableTitle sets the "title" field if the given value is not nil.
func (du *DocumentUpdate) SetNillableTitle(s *string) *DocumentUpdate {
if s != nil {
du.SetTitle(*s)
}
return du
}
// SetPath sets the "path" field. // SetPath sets the "path" field.
func (du *DocumentUpdate) SetPath(s string) *DocumentUpdate { func (du *DocumentUpdate) SetPath(s string) *DocumentUpdate {
du.mutation.SetPath(s) du.mutation.SetPath(s)
return du return du
} }
// SetNillablePath sets the "path" field if the given value is not nil.
func (du *DocumentUpdate) SetNillablePath(s *string) *DocumentUpdate {
if s != nil {
du.SetPath(*s)
}
return du
}
// SetGroupID sets the "group" edge to the Group entity by ID. // SetGroupID sets the "group" edge to the Group entity by ID.
func (du *DocumentUpdate) SetGroupID(id uuid.UUID) *DocumentUpdate { func (du *DocumentUpdate) SetGroupID(id uuid.UUID) *DocumentUpdate {
du.mutation.SetGroupID(id) du.mutation.SetGroupID(id)
@ -76,6 +61,21 @@ func (du *DocumentUpdate) SetGroup(g *Group) *DocumentUpdate {
return du.SetGroupID(g.ID) return du.SetGroupID(g.ID)
} }
// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by IDs.
func (du *DocumentUpdate) AddDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdate {
du.mutation.AddDocumentTokenIDs(ids...)
return du
}
// AddDocumentTokens adds the "document_tokens" edges to the DocumentToken entity.
func (du *DocumentUpdate) AddDocumentTokens(d ...*DocumentToken) *DocumentUpdate {
ids := make([]uuid.UUID, len(d))
for i := range d {
ids[i] = d[i].ID
}
return du.AddDocumentTokenIDs(ids...)
}
// AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs.
func (du *DocumentUpdate) AddAttachmentIDs(ids ...uuid.UUID) *DocumentUpdate { func (du *DocumentUpdate) AddAttachmentIDs(ids ...uuid.UUID) *DocumentUpdate {
du.mutation.AddAttachmentIDs(ids...) du.mutation.AddAttachmentIDs(ids...)
@ -102,6 +102,27 @@ func (du *DocumentUpdate) ClearGroup() *DocumentUpdate {
return du return du
} }
// ClearDocumentTokens clears all "document_tokens" edges to the DocumentToken entity.
func (du *DocumentUpdate) ClearDocumentTokens() *DocumentUpdate {
du.mutation.ClearDocumentTokens()
return du
}
// RemoveDocumentTokenIDs removes the "document_tokens" edge to DocumentToken entities by IDs.
func (du *DocumentUpdate) RemoveDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdate {
du.mutation.RemoveDocumentTokenIDs(ids...)
return du
}
// RemoveDocumentTokens removes "document_tokens" edges to DocumentToken entities.
func (du *DocumentUpdate) RemoveDocumentTokens(d ...*DocumentToken) *DocumentUpdate {
ids := make([]uuid.UUID, len(d))
for i := range d {
ids[i] = d[i].ID
}
return du.RemoveDocumentTokenIDs(ids...)
}
// ClearAttachments clears all "attachments" edges to the Attachment entity. // ClearAttachments clears all "attachments" edges to the Attachment entity.
func (du *DocumentUpdate) ClearAttachments() *DocumentUpdate { func (du *DocumentUpdate) ClearAttachments() *DocumentUpdate {
du.mutation.ClearAttachments() du.mutation.ClearAttachments()
@ -125,8 +146,41 @@ func (du *DocumentUpdate) RemoveAttachments(a ...*Attachment) *DocumentUpdate {
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (du *DocumentUpdate) Save(ctx context.Context) (int, error) { func (du *DocumentUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
du.defaults() du.defaults()
return withHooks(ctx, du.sqlSave, du.mutation, du.hooks) if len(du.hooks) == 0 {
if err = du.check(); err != nil {
return 0, err
}
affected, err = du.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = du.check(); err != nil {
return 0, err
}
du.mutation = mutation
affected, err = du.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(du.hooks) - 1; i >= 0; i-- {
if du.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = du.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, du.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
@ -178,10 +232,16 @@ func (du *DocumentUpdate) check() error {
} }
func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) { func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := du.check(); err != nil { _spec := &sqlgraph.UpdateSpec{
return n, err Node: &sqlgraph.NodeSpec{
Table: document.Table,
Columns: document.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
} }
_spec := sqlgraph.NewUpdateSpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
if ps := du.mutation.predicates; len(ps) > 0 { if ps := du.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
@ -190,13 +250,25 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
} }
if value, ok := du.mutation.UpdatedAt(); ok { if value, ok := du.mutation.UpdatedAt(); ok {
_spec.SetField(document.FieldUpdatedAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: document.FieldUpdatedAt,
})
} }
if value, ok := du.mutation.Title(); ok { if value, ok := du.mutation.Title(); ok {
_spec.SetField(document.FieldTitle, field.TypeString, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeString,
Value: value,
Column: document.FieldTitle,
})
} }
if value, ok := du.mutation.Path(); ok { if value, ok := du.mutation.Path(); ok {
_spec.SetField(document.FieldPath, field.TypeString, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeString,
Value: value,
Column: document.FieldPath,
})
} }
if du.mutation.GroupCleared() { if du.mutation.GroupCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
@ -206,7 +278,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.GroupColumn}, Columns: []string{document.GroupColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -219,7 +294,64 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.GroupColumn}, Columns: []string{document.GroupColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if du.mutation.DocumentTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := du.mutation.RemovedDocumentTokensIDs(); len(nodes) > 0 && !du.mutation.DocumentTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := du.mutation.DocumentTokensIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -235,7 +367,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -248,7 +383,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -264,7 +402,10 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -280,7 +421,6 @@ func (du *DocumentUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
return 0, err return 0, err
} }
du.mutation.done = true
return n, nil return n, nil
} }
@ -304,28 +444,12 @@ func (duo *DocumentUpdateOne) SetTitle(s string) *DocumentUpdateOne {
return duo return duo
} }
// SetNillableTitle sets the "title" field if the given value is not nil.
func (duo *DocumentUpdateOne) SetNillableTitle(s *string) *DocumentUpdateOne {
if s != nil {
duo.SetTitle(*s)
}
return duo
}
// SetPath sets the "path" field. // SetPath sets the "path" field.
func (duo *DocumentUpdateOne) SetPath(s string) *DocumentUpdateOne { func (duo *DocumentUpdateOne) SetPath(s string) *DocumentUpdateOne {
duo.mutation.SetPath(s) duo.mutation.SetPath(s)
return duo return duo
} }
// SetNillablePath sets the "path" field if the given value is not nil.
func (duo *DocumentUpdateOne) SetNillablePath(s *string) *DocumentUpdateOne {
if s != nil {
duo.SetPath(*s)
}
return duo
}
// SetGroupID sets the "group" edge to the Group entity by ID. // SetGroupID sets the "group" edge to the Group entity by ID.
func (duo *DocumentUpdateOne) SetGroupID(id uuid.UUID) *DocumentUpdateOne { func (duo *DocumentUpdateOne) SetGroupID(id uuid.UUID) *DocumentUpdateOne {
duo.mutation.SetGroupID(id) duo.mutation.SetGroupID(id)
@ -337,6 +461,21 @@ func (duo *DocumentUpdateOne) SetGroup(g *Group) *DocumentUpdateOne {
return duo.SetGroupID(g.ID) return duo.SetGroupID(g.ID)
} }
// AddDocumentTokenIDs adds the "document_tokens" edge to the DocumentToken entity by IDs.
func (duo *DocumentUpdateOne) AddDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdateOne {
duo.mutation.AddDocumentTokenIDs(ids...)
return duo
}
// AddDocumentTokens adds the "document_tokens" edges to the DocumentToken entity.
func (duo *DocumentUpdateOne) AddDocumentTokens(d ...*DocumentToken) *DocumentUpdateOne {
ids := make([]uuid.UUID, len(d))
for i := range d {
ids[i] = d[i].ID
}
return duo.AddDocumentTokenIDs(ids...)
}
// AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs. // AddAttachmentIDs adds the "attachments" edge to the Attachment entity by IDs.
func (duo *DocumentUpdateOne) AddAttachmentIDs(ids ...uuid.UUID) *DocumentUpdateOne { func (duo *DocumentUpdateOne) AddAttachmentIDs(ids ...uuid.UUID) *DocumentUpdateOne {
duo.mutation.AddAttachmentIDs(ids...) duo.mutation.AddAttachmentIDs(ids...)
@ -363,6 +502,27 @@ func (duo *DocumentUpdateOne) ClearGroup() *DocumentUpdateOne {
return duo return duo
} }
// ClearDocumentTokens clears all "document_tokens" edges to the DocumentToken entity.
func (duo *DocumentUpdateOne) ClearDocumentTokens() *DocumentUpdateOne {
duo.mutation.ClearDocumentTokens()
return duo
}
// RemoveDocumentTokenIDs removes the "document_tokens" edge to DocumentToken entities by IDs.
func (duo *DocumentUpdateOne) RemoveDocumentTokenIDs(ids ...uuid.UUID) *DocumentUpdateOne {
duo.mutation.RemoveDocumentTokenIDs(ids...)
return duo
}
// RemoveDocumentTokens removes "document_tokens" edges to DocumentToken entities.
func (duo *DocumentUpdateOne) RemoveDocumentTokens(d ...*DocumentToken) *DocumentUpdateOne {
ids := make([]uuid.UUID, len(d))
for i := range d {
ids[i] = d[i].ID
}
return duo.RemoveDocumentTokenIDs(ids...)
}
// ClearAttachments clears all "attachments" edges to the Attachment entity. // ClearAttachments clears all "attachments" edges to the Attachment entity.
func (duo *DocumentUpdateOne) ClearAttachments() *DocumentUpdateOne { func (duo *DocumentUpdateOne) ClearAttachments() *DocumentUpdateOne {
duo.mutation.ClearAttachments() duo.mutation.ClearAttachments()
@ -384,12 +544,6 @@ func (duo *DocumentUpdateOne) RemoveAttachments(a ...*Attachment) *DocumentUpdat
return duo.RemoveAttachmentIDs(ids...) return duo.RemoveAttachmentIDs(ids...)
} }
// Where appends a list predicates to the DocumentUpdate builder.
func (duo *DocumentUpdateOne) Where(ps ...predicate.Document) *DocumentUpdateOne {
duo.mutation.Where(ps...)
return duo
}
// Select allows selecting one or more fields (columns) of the returned entity. // Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema. // The default is selecting all fields defined in the entity schema.
func (duo *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUpdateOne { func (duo *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUpdateOne {
@ -399,8 +553,47 @@ func (duo *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUp
// Save executes the query and returns the updated Document entity. // Save executes the query and returns the updated Document entity.
func (duo *DocumentUpdateOne) Save(ctx context.Context) (*Document, error) { func (duo *DocumentUpdateOne) Save(ctx context.Context) (*Document, error) {
var (
err error
node *Document
)
duo.defaults() duo.defaults()
return withHooks(ctx, duo.sqlSave, duo.mutation, duo.hooks) if len(duo.hooks) == 0 {
if err = duo.check(); err != nil {
return nil, err
}
node, err = duo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*DocumentMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = duo.check(); err != nil {
return nil, err
}
duo.mutation = mutation
node, err = duo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(duo.hooks) - 1; i >= 0; i-- {
if duo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = duo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, duo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*Document)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from DocumentMutation", v)
}
node = nv
}
return node, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
@ -452,10 +645,16 @@ func (duo *DocumentUpdateOne) check() error {
} }
func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err error) { func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err error) {
if err := duo.check(); err != nil { _spec := &sqlgraph.UpdateSpec{
return _node, err Node: &sqlgraph.NodeSpec{
Table: document.Table,
Columns: document.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: document.FieldID,
},
},
} }
_spec := sqlgraph.NewUpdateSpec(document.Table, document.Columns, sqlgraph.NewFieldSpec(document.FieldID, field.TypeUUID))
id, ok := duo.mutation.ID() id, ok := duo.mutation.ID()
if !ok { if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Document.id" for update`)} return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Document.id" for update`)}
@ -481,13 +680,25 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
} }
} }
if value, ok := duo.mutation.UpdatedAt(); ok { if value, ok := duo.mutation.UpdatedAt(); ok {
_spec.SetField(document.FieldUpdatedAt, field.TypeTime, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: document.FieldUpdatedAt,
})
} }
if value, ok := duo.mutation.Title(); ok { if value, ok := duo.mutation.Title(); ok {
_spec.SetField(document.FieldTitle, field.TypeString, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeString,
Value: value,
Column: document.FieldTitle,
})
} }
if value, ok := duo.mutation.Path(); ok { if value, ok := duo.mutation.Path(); ok {
_spec.SetField(document.FieldPath, field.TypeString, value) _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeString,
Value: value,
Column: document.FieldPath,
})
} }
if duo.mutation.GroupCleared() { if duo.mutation.GroupCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
@ -497,7 +708,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.GroupColumn}, Columns: []string{document.GroupColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -510,7 +724,64 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.GroupColumn}, Columns: []string{document.GroupColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: group.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if duo.mutation.DocumentTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := duo.mutation.RemovedDocumentTokensIDs(); len(nodes) > 0 && !duo.mutation.DocumentTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := duo.mutation.DocumentTokensIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: document.DocumentTokensTable,
Columns: []string{document.DocumentTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: documenttoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -526,7 +797,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
@ -539,7 +813,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -555,7 +832,10 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
Columns: []string{document.AttachmentsColumn}, Columns: []string{document.AttachmentsColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(attachment.FieldID, field.TypeUUID), IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: attachment.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -574,6 +854,5 @@ func (duo *DocumentUpdateOne) sqlSave(ctx context.Context) (_node *Document, err
} }
return nil, err return nil, err
} }
duo.mutation.done = true
return _node, nil return _node, nil
} }

View file

@ -0,0 +1,190 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"time"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/ent/document"
"github.com/hay-kot/homebox/backend/ent/documenttoken"
)
// DocumentToken is the model entity for the DocumentToken schema.
type DocumentToken struct {
config `json:"-"`
// ID of the ent.
ID uuid.UUID `json:"id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Token holds the value of the "token" field.
Token []byte `json:"token,omitempty"`
// Uses holds the value of the "uses" field.
Uses int `json:"uses,omitempty"`
// ExpiresAt holds the value of the "expires_at" field.
ExpiresAt time.Time `json:"expires_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the DocumentTokenQuery when eager-loading is set.
Edges DocumentTokenEdges `json:"edges"`
document_document_tokens *uuid.UUID
}
// DocumentTokenEdges holds the relations/edges for other nodes in the graph.
type DocumentTokenEdges struct {
// Document holds the value of the document edge.
Document *Document `json:"document,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// DocumentOrErr returns the Document value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e DocumentTokenEdges) DocumentOrErr() (*Document, error) {
if e.loadedTypes[0] {
if e.Document == nil {
// Edge was loaded but was not found.
return nil, &NotFoundError{label: document.Label}
}
return e.Document, nil
}
return nil, &NotLoadedError{edge: "document"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*DocumentToken) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case documenttoken.FieldToken:
values[i] = new([]byte)
case documenttoken.FieldUses:
values[i] = new(sql.NullInt64)
case documenttoken.FieldCreatedAt, documenttoken.FieldUpdatedAt, documenttoken.FieldExpiresAt:
values[i] = new(sql.NullTime)
case documenttoken.FieldID:
values[i] = new(uuid.UUID)
case documenttoken.ForeignKeys[0]: // document_document_tokens
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type DocumentToken", columns[i])
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the DocumentToken fields.
func (dt *DocumentToken) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case documenttoken.FieldID:
if value, ok := values[i].(*uuid.UUID); !ok {
return fmt.Errorf("unexpected type %T for field id", values[i])
} else if value != nil {
dt.ID = *value
}
case documenttoken.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
dt.CreatedAt = value.Time
}
case documenttoken.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
dt.UpdatedAt = value.Time
}
case documenttoken.FieldToken:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field token", values[i])
} else if value != nil {
dt.Token = *value
}
case documenttoken.FieldUses:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field uses", values[i])
} else if value.Valid {
dt.Uses = int(value.Int64)
}
case documenttoken.FieldExpiresAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field expires_at", values[i])
} else if value.Valid {
dt.ExpiresAt = value.Time
}
case documenttoken.ForeignKeys[0]:
if value, ok := values[i].(*sql.NullScanner); !ok {
return fmt.Errorf("unexpected type %T for field document_document_tokens", values[i])
} else if value.Valid {
dt.document_document_tokens = new(uuid.UUID)
*dt.document_document_tokens = *value.S.(*uuid.UUID)
}
}
}
return nil
}
// QueryDocument queries the "document" edge of the DocumentToken entity.
func (dt *DocumentToken) QueryDocument() *DocumentQuery {
return (&DocumentTokenClient{config: dt.config}).QueryDocument(dt)
}
// Update returns a builder for updating this DocumentToken.
// Note that you need to call DocumentToken.Unwrap() before calling this method if this DocumentToken
// was returned from a transaction, and the transaction was committed or rolled back.
func (dt *DocumentToken) Update() *DocumentTokenUpdateOne {
return (&DocumentTokenClient{config: dt.config}).UpdateOne(dt)
}
// Unwrap unwraps the DocumentToken entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (dt *DocumentToken) Unwrap() *DocumentToken {
_tx, ok := dt.config.driver.(*txDriver)
if !ok {
panic("ent: DocumentToken is not a transactional entity")
}
dt.config.driver = _tx.drv
return dt
}
// String implements the fmt.Stringer.
func (dt *DocumentToken) String() string {
var builder strings.Builder
builder.WriteString("DocumentToken(")
builder.WriteString(fmt.Sprintf("id=%v, ", dt.ID))
builder.WriteString("created_at=")
builder.WriteString(dt.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(dt.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("token=")
builder.WriteString(fmt.Sprintf("%v", dt.Token))
builder.WriteString(", ")
builder.WriteString("uses=")
builder.WriteString(fmt.Sprintf("%v", dt.Uses))
builder.WriteString(", ")
builder.WriteString("expires_at=")
builder.WriteString(dt.ExpiresAt.Format(time.ANSIC))
builder.WriteByte(')')
return builder.String()
}
// DocumentTokens is a parsable slice of DocumentToken.
type DocumentTokens []*DocumentToken
func (dt DocumentTokens) config(cfg config) {
for _i := range dt {
dt[_i].config = cfg
}
}

Some files were not shown because too many files have changed in this diff Show more