Compare commits

...

163 Commits

Author SHA1 Message Date
CollapsingStar 6fd8457e5a
chore: remove deprecated version tag for Docker v2 (#902) 2024-05-23 07:53:43 -08:00
Graeme B c2511d56d7
Update README.md (#891)
Add a Contributing section and the Buy Me A Coffee link since I had to hunt for it.
2024-05-23 07:52:56 -08:00
renovate[bot] fbb17d66aa
fix(deps): update dependency @vueuse/nuxt to v10.9.0 (#827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-28 08:11:54 -08:00
renovate[bot] 98429ae8c2
fix(deps): update dependency @vueuse/router to v10.9.0 (#828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-28 08:11:46 -08:00
renovate[bot] 69adbacd25
fix(deps): update dependency h3 to v1.11.1 (#831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-28 08:11:34 -08:00
renovate[bot] 43ff04317a
fix(deps): update dependency pinia to v2.1.7 (#833)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-28 08:11:22 -08:00
renovate[bot] 361c2121d8
fix(deps): update dependency daisyui to v2.52.0 (#830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-28 08:09:16 -08:00
Spreadcat 0041c277ad
Add currency "norwegian krones" (#836)
* add: norwegian kroner

Adding the currency of norwegian krones back into the file. There was an earlier commit where this seems to have dropped of the table.

* fix: using the correct terms for the currency.
2024-03-12 14:48:51 -08:00
Hayden f621d3ad5d
fix blocking error during startup when in demo mode (#838) 2024-03-05 06:44:43 -09:00
Hayden af9aa239af
add email banner 2024-03-02 12:03:13 -06:00
renovate[bot] f8a2c70f2c
chore(deps): update typescript-eslint monorepo to v6.21.0 (#824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-02 08:14:23 -09:00
renovate[bot] 89b68da86e
fix(deps): update dependency @vuepic/vue-datepicker to v8.2.0 (#826)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-02 08:14:03 -09:00
renovate[bot] 03e9596b7a
fix(deps): update dependency @nuxtjs/tailwindcss to v6.11.4 (#825)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-02 08:06:01 -09:00
renovate[bot] 90535c8691
chore(deps): update dependency typescript to v5.3.3 (#822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 18:32:52 -09:00
renovate[bot] b7fc0e903c
chore(deps): update dependency vitest to v1.3.1 (#823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 18:32:40 -09:00
renovate[bot] eb420d50b1
chore(deps): update dependency eslint-config-prettier to v9.1.0 (#820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 16:44:52 -09:00
renovate[bot] 8f3a081670
chore(deps): update dependency eslint-plugin-vue to v9.22.0 (#821)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 16:44:46 -09:00
Hayden 2867a05c92
chore: bump http kit (#817)
* use new httpkit runner

* refactor out last httpkit changes

* fix timeout defaults

* fix wrong time input - closes #819
2024-03-01 15:07:03 -09:00
renovate[bot] 77b4d594af
fix(deps): update module ariga.io/atlas to v0.19.1 (#814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 15:06:12 -09:00
renovate[bot] 8f4fed1df9
fix(deps): update dependency postcss to v8.4.35 (#812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 15:05:54 -09:00
renovate[bot] b65da05d11
chore(deps): update dependency @nuxtjs/eslint-config-typescript to v12.1.0 (#816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 15:05:34 -09:00
renovate[bot] c2c8441625
chore(deps): update dependency @faker-js/faker to v8.4.1 (#815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 15:05:24 -09:00
renovate[bot] a1eb6e314c
fix(deps): update dependency dompurify to v3.0.9 (#811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 10:52:03 -09:00
renovate[bot] b0c9dcdeee
fix(deps): update dependency autoprefixer to v10.4.18 (#809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 10:51:57 -09:00
renovate[bot] 0a609adb0b
fix(deps): update dependency @tailwindcss/typography to v0.5.10 (#806)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 10:51:47 -09:00
renovate[bot] 19ee6c2687
fix(deps): update dependency @headlessui/vue to v1.7.19 (#803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 10:51:40 -09:00
Hayden b77c9be36f
chore: bump deps (#810)
* bump prettier/eslint-plugin

* bump nuxt pwa

* use typed imports

* set vue version to fix layout errors

* disable import
2024-03-01 09:08:14 -09:00
Hayden be2910e0df
fix missing icon 2024-03-01 11:34:07 -06:00
Hayden 749ee367d4
upgrade dependency 2024-03-01 10:54:31 -06:00
renovate[bot] c36ce7ac2d
fix(deps): update module github.com/swaggo/http-swagger to v2 (#514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-01 07:51:26 -09:00
renovate[bot] d0128c7bd7
fix(deps): update dependency @tailwindcss/forms to v0.5.7 (#804)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-29 16:20:51 -09:00
Hayden f91b33db38
fix: remove external dependency for icones (#805)
* change all icons to use iconify

* fix minor UI elements

* fix layout of table
2024-02-29 16:20:18 -09:00
renovate[bot] cf166ac641
chore(deps): update dependency prettier to v2.8.8 (#802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-29 12:17:27 -09:00
renovate[bot] ae86e09970
chore(deps): update dependency @types/markdown-it to v13.0.7 (#801)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-29 12:17:19 -09:00
renovate[bot] a7e0d2d127
fix(deps): update dependency postcss to v8.4.31 [security] (#791)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-29 10:48:33 -09:00
renovate[bot] 9866fdddb8
fix(deps): update module modernc.org/sqlite to v1.29.2 (#790)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-29 10:48:07 -09:00
renovate[bot] ff9759ab20
chore(deps): update dependency mkdocs-material to v9.5.12 (#778)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-29 10:47:58 -09:00
Hayden b655cfab28
feat: improve search matching (#800)
* offload search to lunr.js

* update location search when locations are mutated
2024-02-29 10:45:05 -09:00
Hayden 4c9ddac395
fix: date picker improvements (#793)
* use vue component for date picker

* zero out database fields even when set to 0001-xx-xx

* fix wrong datetime display + improved datepicker

* fix ts error

* zero out times

* add date-fns to dependencies
2024-02-29 09:58:26 -09:00
Hayden c708b1759e
fix: profile bugs and add full path (#792)
* fix template rendering

* tidy

* fix missing currencies and profile errors

* endpoint for fullpath of an item

* endpoint test

* fix assertions
2024-02-25 13:04:24 -09:00
renovate[bot] 3ed50f5a1b
fix(deps): update module modernc.org/sqlite to v1.29.1 (#754)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-18 09:30:08 -09:00
renovate[bot] e6d77b3f72
fix(deps): update module github.com/go-chi/chi/v5 to v5.0.12 (#775)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-18 09:29:53 -09:00
renovate[bot] 747815982e
fix(deps): update module github.com/go-playground/validator/v10 to v10.18.0 (#753)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 10:43:58 -09:00
renovate[bot] 7e23a75908
fix(deps): update module github.com/hay-kot/httpkit to v0.0.6 (#751)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 10:43:41 -09:00
renovate[bot] 4c8a97fe0d
chore(deps): update dependency mkdocs-material to v9.5.9 (#750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 10:43:25 -09:00
renovate[bot] 87b07ab47d
chore(deps): update golangci/golangci-lint-action action to v4 (#747)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 06:13:34 -09:00
renovate[bot] cf8000d8ee
chore(deps): update pnpm/action-setup action to v3 (#744)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 06:11:03 -09:00
renovate[bot] 31b10194f1
fix(deps): update module golang.org/x/crypto to v0.19.0 (#743)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-14 06:10:55 -09:00
renovate[bot] 3d748021dc
chore(deps): update dependency mkdocs-material to v9.5.8 (#742)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 09:45:51 -09:00
renovate[bot] 0f8e1fee35
fix(deps): update module github.com/rs/zerolog to v1.32.0 (#740)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-07 09:45:44 -09:00
renovate[bot] 8d18b06012
fix(deps): update module github.com/mattn/go-sqlite3 to v1.14.22 (#735)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 10:43:08 -09:00
renovate[bot] 6a32762c98
fix(deps): update module github.com/swaggo/swag to v1.16.3 (#731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 10:42:59 -09:00
renovate[bot] 2311eda44b
fix(deps): update module ariga.io/atlas to v0.19.0 (#734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 10:42:50 -09:00
renovate[bot] 5b52aa8abf
chore(deps): update dependency mkdocs-material to v9.5.7 (#736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-02-04 10:42:39 -09:00
LINKIWI aace77ec40
backend: Periodically send client aliveness pings in event stream websocket connection (#729)
* backend: Periodically send client aliveness pings following event websocket connection

* backend: Single persistent global ping goroutine instead of per-session ticker
2024-01-31 10:20:56 -09:00
renovate[bot] c55d421326
chore(deps): update dependency mkdocs-material to v9.5.6 (#728)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 10:20:31 -09:00
renovate[bot] 5ca8e0c35d
fix(deps): update module github.com/mattn/go-sqlite3 to v1.14.21 (#730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-31 10:16:31 -09:00
Hayden aba853d598
redo atlas upgrade with huge binary 2024-01-28 10:50:43 -06:00
Hayden 2c8fd163ee
chore: ignore common bin output 2024-01-27 22:03:46 -06:00
Hayden d9497bd69e remove large bin + go tidy
Former-commit-id: 830b4d8e3efb66874f0310d6c49d37d57481f986
2024-01-27 21:48:32 -06:00
LINKIWI 893f3f6df6 chore: Enable gofmt in linter, fix all outstanding errors (#727)
Former-commit-id: 1d9477f510bb2d6ada4b720933f113f24c25913e
Former-commit-id: b22ff272ca
2024-01-27 18:29:10 -09:00
LINKIWI ca55e5ba94 fix: Case-insensitive attachment extension matching for item photo inference (#725)
Former-commit-id: 3a6865079e68e9c1ee9beda651cbf123c58fcfc6
2024-01-27 18:28:58 -09:00
LINKIWI 7753213657 fix: Use zerolog level parser, set global log level correctly from configuration (#724)
Former-commit-id: 08b46c2fe32bda46158eac46f82a2341bb1a93c2
2024-01-27 18:28:43 -09:00
renovate[bot] b3f7b59243 fix(deps): update module github.com/mattn/go-sqlite3 to v1.14.20 (#723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 858cdec2d5
2024-01-26 06:10:22 -09:00
renovate[bot] 18a9b21a87 fix(deps): update module github.com/google/uuid to v1.6.0 (#719)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 59a5a1782b
2024-01-24 08:46:07 -09:00
renovate[bot] 853b473668 chore(deps): update dependency mkdocs-material to v9.5.5 (#720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: b8b706ee74
2024-01-24 08:42:19 -09:00
renovate[bot] 40905bc100 fix(deps): update module modernc.org/sqlite to v1.28.0 (#667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: de4081d0d2
2024-01-20 07:32:34 -09:00
renovate[bot] 8be9df2b8d fix(deps): update module github.com/go-playground/validator/v10 to v10.17.0 (#717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 94399a93dc
2024-01-20 07:32:21 -09:00
renovate[bot] bd9eb69313 fix(deps): update module github.com/mattn/go-sqlite3 to v1.14.19 (#668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 6ee41038b1
2024-01-19 05:55:39 -09:00
renovate[bot] fa31bb2448 chore(deps): update dependency mkdocs-material to v9.5.4 (#716)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: b3b81a3ccc
2024-01-19 05:52:33 -09:00
renovate[bot] fcd33e59d9 fix(deps): update module golang.org/x/crypto to v0.18.0 (#706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: d374fae88f
2024-01-18 10:46:51 -09:00
zebrapurring 84aee20208 feat: enable search by serial number or model number (#677)
Co-authored-by: zebrapurring <>
Former-commit-id: f2f7809360
2024-01-18 10:46:40 -09:00
Hayden 2b79788fbe feat: user defined currencies (#700)
* basic currency service for loading at runtime

* api endpoint for currencies

* sort slice before return

* remove currency validation

* validate using currency service

* implement selecting dynamic currency options

* bump go version

* fix type definition

* specify explicit type

* change go versions

* proper types for assetId

* log/return currency error

* make case insensative

* use ToUpper instead

* feat: adding new currencies (#715)

* fix: task swag (#710)

Co-authored-by: Quoing <pavel.cadersky@mavenir.com>

* [feat] Adding new currencies

---------

Co-authored-by: quoing <quoing@users.noreply.github.com>
Co-authored-by: Quoing <pavel.cadersky@mavenir.com>
Co-authored-by: Bradley <41597815+userbradley@users.noreply.github.com>

* remove ts file and consoldate new values into json

* move flag to options namespace

* add env config for currencies

* basic documentaion

* remove in sync test

---------

Co-authored-by: quoing <quoing@users.noreply.github.com>
Co-authored-by: Quoing <pavel.cadersky@mavenir.com>
Co-authored-by: Bradley <41597815+userbradley@users.noreply.github.com>
Former-commit-id: c4b923847a
2024-01-18 10:45:42 -09:00
quoing ce923a5b4c fix: task swag (#710)
Co-authored-by: Quoing <pavel.cadersky@mavenir.com>
Former-commit-id: d2bde3f241
2024-01-16 17:39:25 -09:00
renovate[bot] e83ff89c9d chore(deps): update dependency mkdocs-material to v9.5.3 (#697)
Former-commit-id: d920e1e215
2024-01-04 15:49:56 -06:00
renovate[bot] c71c4f3b16 fix(deps): update module github.com/go-chi/chi/v5 to v5.0.11 (#698)
Former-commit-id: 6d8260b4fe
2024-01-04 15:48:08 -06:00
renovate[bot] 33661587fd fix(deps): update module github.com/google/uuid to v1.5.0 (#664)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 8ede42f79b
2024-01-04 12:12:20 -09:00
Marcel K 27b72603e1 feat: show quantity in card view if quantity == 0 (hay-kot/homebox#672) (#673)
* feat: show quantity in card view if quantity == 0 (hay-kot/homebox#672)

* Update frontend/components/Item/Card.vue

---------

Co-authored-by: bee-eater <bee-eater@users.noreply.github.com>
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
Former-commit-id: d8e49622e5
2024-01-04 12:10:44 -09:00
renovate[bot] a4d91adc1e fix(deps): update module golang.org/x/crypto to v0.17.0 [security] (#676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: f3f2116a2b
2024-01-04 12:09:24 -09:00
Hayden 03df23d97c fix: inaccruate 401 & sql busy error (#679)
* fix inaccruate 401 error on SQL db error

* init golangci-lint config

* linter autofix

* testify auto fixes

* fix sqlite busy errors

* fix naming

* more linter errors

* fix rest of linter issues

Former-commit-id: e8449b3a73
2024-01-04 08:55:26 -09:00
zebrapurring 5e83b28ff5 fix: Render newlines in markdown fields (#682)
Co-authored-by: zebrapurring <>
Former-commit-id: b9183109f2
2023-12-24 18:27:58 -09:00
Hayden d759fad40c feat: add log statements + remove auto redirect (#671)
Former-commit-id: 3ee150e7d6
2023-12-15 18:32:44 -09:00
Hayden 1cc9bf459a fix: ensure rows are closed (#670)
Former-commit-id: 85c8e47016
2023-12-15 18:07:17 -09:00
Hayden 0c535aa8d8 feat: extract auth into provider (#663)
* extract auth into provider

* bump go version

* use pointer

* rebase

Former-commit-id: 8538877f52
2023-12-12 05:49:46 -09:00
renovate[bot] 3eb4c21263 fix(deps): update dependency markdown-it to v14 (#655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 522943687e
2023-12-12 05:44:48 -09:00
renovate[bot] 19c53dce2f chore(deps): update dependency vitest to v1 (#652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: ff2577a739
2023-12-12 05:44:38 -09:00
renovate[bot] 3967bc86d0 chore(deps): update actions/setup-go action to v5 (#653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 0bc3969c0c
2023-12-12 05:44:23 -09:00
renovate[bot] 2b6e42b11f chore(deps): update dependency mkdocs-material to v9.5.2 (#654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 8a85b18997
2023-12-12 05:44:11 -09:00
renovate[bot] d48de6b9f6 fix(deps): update github.com/gocarina/gocsv digest to b87c2d0 (#629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: b5592167a2
2023-12-01 09:39:52 -09:00
renovate[bot] 477a5fce97 fix(deps): update module golang.org/x/crypto to v0.16.0 (#645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 126c06a0fa
2023-12-01 09:39:44 -09:00
renovate[bot] 65efe7f575 fix(deps): update module github.com/hay-kot/httpkit to v0.0.5 (#643)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 014fdea4ff
2023-12-01 09:39:37 -09:00
Hayden 1b77b15db4 fix: ignore /api paths from service workers (#649)
Former-commit-id: f2363b85cb
2023-12-01 08:57:59 -09:00
Hayden fc3bc3f010 fix: loading causing stale data to present in form(#650)
Former-commit-id: 4d9131ba7e
2023-12-01 08:57:43 -09:00
Hayden 81e76d9dd4 fix/feat: primary photo auto set and auto-set primary photo (#651)
* fix error when item doesn't have photo attachment

* automatically detect image types and set primary photo if first

* remove charts.js from libs

Former-commit-id: 321a83b634
2023-12-01 08:57:29 -09:00
renovate[bot] bd1a241be1 chore(deps): update docker/setup-qemu-action action to v3 (#625)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: d1d98bcb06
2023-11-30 14:25:55 -09:00
renovate[bot] bc4c0c0b89 chore(deps): update goreleaser/goreleaser-action action to v5 (#626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: db00e7a268
2023-11-30 14:25:47 -09:00
renovate[bot] c7936fc478 chore(deps): update dependency mkdocs-material to v9.4.14 (#630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 7ec22ed9bd
2023-11-30 14:25:30 -09:00
Kevin Holtkamp 09ee4fef3a docs: Fix a bunch of grammar and spelling, rephrased some sentences to be more readable (#635)
Former-commit-id: 10c030a56b
2023-11-30 14:25:22 -09:00
Hayden d6b0062ae9 feat: set version flag (#632)
Former-commit-id: 9edbda3daa
2023-11-24 10:02:02 -09:00
Hayden df26a3eeb7 fix: allow zeroing out asset ids (#624)
Former-commit-id: 8cc0f30291
2023-11-15 18:46:47 -09:00
Hayden 798d873f76 fix: images in child items (#623)
* support parentID search

* fetch images for item children

Former-commit-id: afbc6a49ac
2023-11-15 18:41:24 -09:00
renovate[bot] a26ea3bac5 chore(deps): update dependency @types/markdown-it to v13 (#577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 2594d4cdb4
2023-11-15 18:18:19 -09:00
renovate[bot] 812b464bc7 chore(deps): update dependency @vite-pwa/nuxt to ^0.2.0 (#616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 2eafa8e72f
2023-11-15 18:18:07 -09:00
renovate[bot] e1f34c2507 fix(deps): update dependency @pinia/nuxt to ^0.5.0 (#620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: e65d44fa9e
2023-11-15 18:17:57 -09:00
Hayden 79be938531 feat: expose timeout variables (#622)
* expose timeout variables

* formatting

Former-commit-id: eeae790fe4
2023-11-15 18:17:43 -09:00
renovate[bot] e3ddc68eb2 chore(deps): update actions/setup-node action to v4 (#621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: a70ee33759
2023-11-15 18:15:31 -09:00
Hayden eb9175ab27 fix: use theme aware background color (#619)
Former-commit-id: 80e2071300
2023-11-15 18:01:03 -09:00
renovate[bot] 54b7b2c35f chore(deps): update pnpm/action-setup action to v2.4.0 (#511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: db27d34b4f
2023-11-15 17:58:30 -09:00
Owen Valentine ab98870350 feat : Validate bark, ntfy, generic+ shortcut (#591)
Former-commit-id: da22074ed3
2023-11-15 17:53:06 -09:00
Hayden 13c437c418 fix: ensure urls isn't encoded (#618)
Former-commit-id: 51ba15f84c
2023-11-15 17:51:19 -09:00
Hayden ba2e66a014 fix: filepath sep on windows (#615)
Former-commit-id: b408318acb
2023-11-15 17:43:44 -09:00
Hayden 1adf24e109 chore: bump all go dependencies (#614)
* bump all

* code-generation

Former-commit-id: c0e8e34065
2023-11-15 17:30:49 -09:00
Hayden 742ece7923 chore: rewrite generator to resolve strange name generation (#612)
* rewrite generator to resolve strange name generation

* fix asset id types

* ignore errored types

* remove depreciated calls

* use more random words

* random user

Former-commit-id: 4738a9b131
2023-11-15 17:19:51 -09:00
renovate[bot] f74736b369 chore(deps): update actions/checkout action to v4 (#575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: b08e52104c
2023-11-15 08:08:46 -09:00
renovate[bot] 3a3be89b90 chore(deps): update docker/setup-buildx-action action to v3 (#579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 3e2ab29054
2023-11-15 08:08:37 -09:00
renovate[bot] 4082092560 chore(deps): update dependency mkdocs-material to v9.4.8 (#592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: e5f66d99bc
2023-11-15 08:08:26 -09:00
Hayden 31839eb444 fix: restore location section (#587)
Former-commit-id: 0995478cc0
2023-10-15 06:37:47 -08:00
Hayden 6e203e7833 fix: infinite redirect issue (#583)
Former-commit-id: ae4b95301f
2023-10-10 05:43:44 -08:00
Hayden 0ef9d0deb8 Revert "chore(deps): update dependency nuxt to v3.7.4 (#554)" (#580)
This reverts commit 36e13ab03b [formerly 2cd3c15215].

Former-commit-id: d8482f3a13
2023-10-06 20:29:27 -08:00
Hayden c71f077466 refactor: rewrite to cookie based auth (#578)
* rewrite to cookie based auth

* remove interceptor

Former-commit-id: 1365bdfd46
2023-10-06 19:44:43 -08:00
renovate[bot] 36e13ab03b chore(deps): update dependency nuxt to v3.7.4 (#554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 2cd3c15215
2023-10-06 18:51:54 -08:00
renovate[bot] c0f19cdf05 fix(deps): update module modernc.org/sqlite to v1.26.0 (#574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 0dc4fa5d98
2023-10-06 18:51:44 -08:00
Hayden b20c88e256 feat: primary images (#576)
* add support for primary images

* fix locked loading state issue

* add action to auto-set images

Former-commit-id: 318b8be192
2023-10-06 18:51:08 -08:00
Hayden ce16b37b97 fix: field values request fails (#573)
Former-commit-id: 63a966c526
2023-10-06 14:10:44 -08:00
Hayden 6ed1893bed feat: make selectables clearable (#572)
Former-commit-id: db16d3fb23
2023-10-06 13:32:49 -08:00
renovate[bot] bd9ea571aa fix(deps): update module golang.org/x/crypto to v0.14.0 (#570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 1952b9f1cb
2023-10-06 13:24:12 -08:00
renovate[bot] 10d9c4fbcc fix(deps): update module github.com/rs/zerolog to v1.31.0 (#569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 2b31d46ab3
2023-10-06 13:24:00 -08:00
Hayden eff5db3664 fix: ensure loading in toggled (#571)
Former-commit-id: f3f96723b2
2023-10-06 13:22:16 -08:00
renovate[bot] f8a6160039 fix(deps): update module github.com/yeqown/go-qrcode/writer/standard to v1.2.2 (#567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: b28bb2c4a8
2023-10-06 13:06:00 -08:00
renovate[bot] 4375ff8bd6 fix(deps): update module github.com/containrrr/shoutrrr to v0.8.0 (#555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: a33cf54a33
2023-10-06 13:05:53 -08:00
Hayden 9bee6e9863 pr: fixed incorrect sum of the total items price (#568)
* Fixed incorrect sum of the total items price

https://github.com/hay-kot/homebox/issues/458

* fix eslint errors

---------

Co-authored-by: Adamko <33964772+cRaZy92@users.noreply.github.com>
Former-commit-id: f13bf2958d
2023-10-06 12:50:55 -08:00
renovate[bot] b42d20b6a6 chore(deps): update dependency mkdocs-material to v9.4.4 (#553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: a9712c48af
2023-10-06 12:31:51 -08:00
renovate[bot] ba1e3a905b fix(deps): update module github.com/go-playground/validator/v10 to v10.15.5 (#556)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: e68b7cf500
2023-10-06 12:30:31 -08:00
renovate[bot] 973d5ca97d fix(deps): update module github.com/swaggo/swag to v1.16.2 (#552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 744a5bbb47
2023-09-14 06:19:57 -08:00
renovate[bot] 57baf22534 fix(deps): update module github.com/google/uuid to v1.3.1 (#551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: fc5698410b
2023-09-14 06:19:37 -08:00
renovate[bot] c615707cbc fix(deps): update module golang.org/x/crypto to v0.13.0 (#532)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 455163d637
2023-09-14 06:19:28 -08:00
Aaron von Awesome 992eea3278 fix: minor typo (#546)
Former-commit-id: fbc7e6e33a
2023-09-14 06:13:42 -08:00
renovate[bot] 03288b52ee chore(deps): update dependency eslint-config-prettier to v9 (#533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 5739b2005a
2023-09-14 06:11:08 -08:00
renovate[bot] a274bbcf64 fix(deps): update module modernc.org/sqlite to v1.25.0 (#531)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 5f41960c0a
2023-09-14 06:10:46 -08:00
renovate[bot] 64e9889d41 fix(deps): update module github.com/rs/zerolog to v1.30.0 (#517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: c89aa738cf
2023-09-14 06:10:37 -08:00
Jonathan Gawrych 58e80ab3e0 fix: mobile "Create and Add Another" goes off screen (#540)
Former-commit-id: 94fd9c314d
2023-08-31 09:07:45 -08:00
Hayden 94e81d14fa fix websocket over secure connection (#542)
* fix https connection

* explicity dependency

Former-commit-id: 0876deb1e9
2023-08-24 06:28:56 -08:00
tctlrd 0b021e898f feat: add currencies XAG and XAU (#535)
* Added currencies XAG and XAU to currency.ts

I added XAG and XAU for myself and others who prefer to measure value with something of substance.

Review the ISO 4217 standard to view a full list of official currency codes including the ones I have added.

https://www.iso.org/iso-4217-currency-codes.html
https://en.wikipedia.org/wiki/ISO_4217

Example:
https://www.xe.com/currencyconverter/convert/?Amount=100&From=XAG&To=USD

API for exchange rates:
https://openexchangerates.org/

* Added field values xag and xau to group.go

* Update group.go
Former-commit-id: 5438898b49
2023-08-23 09:29:22 -08:00
Hayden be8d6e8235 update lock file
Former-commit-id: 9fa17bec90
2023-08-09 21:49:32 -05:00
Cheng Gu d3dcb599ca feat: set cookies' expires attribute and fix remember me (#530)
Former-commit-id: b5987f2e8d
2023-08-09 18:48:39 -08:00
Hayden 0cbe516ca3 feat: WebSocket based implementation of server sent events for cache busting (#527)
* rough implementation of WS based event system for server side notifications of mutation

* fix test construction

* fix deadlock on event bus

* disable linter error

* add item mutation events

* remove old event bus code

* refactor event system to use composables

* refresh items table when new item is added

* fix create form errors

* cleanup unnecessary calls

* fix importer erorrs + limit fn calls on import

Former-commit-id: 2cbcc8bb1d
2023-08-02 13:00:57 -08:00
Hayden 26911e9530 specify h3 dependency
Former-commit-id: cceec06148
2023-08-02 09:05:07 -05:00
Hayden 5524dfa83d try node 18
Former-commit-id: 2e2eed143d
2023-08-02 09:01:47 -05:00
renovate[bot] 21d54eb2ac chore(deps): update dependency vitest to ^0.34.0 (#529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 272cc5a370
2023-08-02 05:59:31 -08:00
Hayden 4152199f3b build nightly rootless
Former-commit-id: 275e106d72
2023-08-02 08:47:53 -05:00
Hayden 1d40251f29 include rootless dockerfile
Former-commit-id: 3f0e65a2ad
2023-08-02 08:45:22 -05:00
Hayden 97e137c411 feat: add support for create + add more for all create modals and support k… (#526)
* add support for create + add more for all create modals and support keyboard bindings

* listen for esc to close modals

Former-commit-id: 22bbaae08f
2023-07-31 09:53:26 -08:00
Hayden 0de6c2338d fix: prevent resetting dialog state on error (#524)
Former-commit-id: 8c7d91ea52
2023-07-31 08:22:08 -08:00
Hayden a2479155b2 feat: support cmd+s / ctrl+s and rework button display on edit (#523)
Former-commit-id: 5a219f6a9c
2023-07-31 06:57:42 -08:00
Hayden e2dace75f4 fix: label prop not being passed to password input (#522)
Former-commit-id: 895017b28e
2023-07-31 06:08:35 -08:00
Hayden ef9a7cd811 fix: assert/asserts (#521)
Former-commit-id: 02ce52dbe3
2023-07-31 06:05:37 -08:00
Hayden 964270e054 feat: more currency support (#520)
* add multiple new currencies

* add multiple new currencies

* remove duplicate yen

Former-commit-id: c5ae6b17f9
2023-07-31 05:59:36 -08:00
renovate[bot] 6b9cdf3294 chore(deps): update dependency mkdocs-material to v9.1.21 (#512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 371fc0a6af
2023-07-29 09:54:55 -08:00
Hayden 6fd228f1f4 ui: rework location/labels pages (#475)
* formatting

* slimdown locations page

* update location/labels

* fix dependency issues

* fix type generator

* cleanup unused variables

Former-commit-id: 016780920d
2023-07-27 13:21:28 -08:00
db8200 0eb0b283b2 fix 3 places where API URLs were not constructed by function route (#451)
* Fixed 3 places where API URLs were not constructed by function route(path, params).

* autofix

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
Former-commit-id: 06eb6c1f91
2023-07-22 20:11:29 -08:00
renovate[bot] 4b071bccda fix(deps): update module github.com/swaggo/http-swagger to v2 (#508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: 27dad0e118
2023-07-22 20:10:52 -08:00
renovate[bot] 65a7947932 fix(deps): update module github.com/swaggo/http-swagger to v2 (#506)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Former-commit-id: dc9446516a
2023-07-22 20:09:43 -08:00
Hayden a3e607a887 chore: bump all go deps (#507)
* bump all deps

* run code-gen

Former-commit-id: a042496c71
2023-07-22 19:57:51 -08:00
302 changed files with 16691 additions and 6784 deletions

View File

@ -35,6 +35,6 @@
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
"features": {
"golang": "1.20"
"golang": "1.21"
}
}

View File

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

View File

@ -7,12 +7,12 @@ jobs:
Go:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- name: Install Task
uses: arduino/setup-task@v1
@ -20,7 +20,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v4
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest

View File

@ -9,11 +9,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2.2.4
- uses: pnpm/action-setup@v3.0.0
with:
version: 6.0.2
@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -44,15 +44,15 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.20"
go-version: "1.21"
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- uses: pnpm/action-setup@v2.2.4
- uses: pnpm/action-setup@v3.0.0
with:
version: 6.0.2

View File

@ -20,22 +20,22 @@ jobs:
name: "Publish Homebox"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: "1.20"
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: install buildx
id: buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
with:
install: true
@ -44,7 +44,7 @@ jobs:
env:
CR_PAT: ${{ secrets.GH_TOKEN }}
- name: build nightly the image
- name: build nightly image
if: ${{ inputs.release == false }}
run: |
docker build --push --no-cache \
@ -53,6 +53,16 @@ jobs:
--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: |

View File

@ -13,7 +13,7 @@ jobs:
name: "Deploy Nightly to Fly.io"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only

View File

@ -22,12 +22,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
- uses: pnpm/action-setup@v2
with:
@ -41,7 +41,7 @@ jobs:
cp -r ./.output/public ../backend/app/api/static/
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
uses: goreleaser/goreleaser-action@v5
with:
workdir: "backend"
distribution: goreleaser
@ -67,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master

3
.gitignore vendored
View File

@ -53,4 +53,5 @@ dist/
# Nuxt Publish Dir
backend/app/api/static/public/*
!backend/app/api/static/public/.gitkeep
!backend/app/api/static/public/.gitkeep
backend/api

View File

@ -16,7 +16,7 @@
"editor.formatOnSave": false,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"

View File

@ -1,16 +1,16 @@
# Contributing
## We Develop with Github
## We Develop with GitHub
We use github to host code, to track issues and feature requests, as well as accept pull requests.
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
## Branch Flow
We use the `main` branch as the development branch. All PRs should be made to the `main` branch from a feature branch. To create a pull request you can use the following steps:
We use the `main` branch as the development branch. All PRs should be made to the `main` branch from a feature branch. To create a pull request, you can use the following steps:
1. Fork the repository and create a new branch from `main`.
2. If you've added code that should be tested, add tests.
3. If you've changed API's, update the documentation.
3. If you've changed APIs, update the documentation.
4. Ensure that the test suite and linters pass
5. Issue your pull request
@ -18,7 +18,7 @@ We use the `main` branch as the development branch. All PRs should be made to th
### Prerequisites
There is a devcontainer available for this project. If you are using VSCode, you can use the devcontainer to get started. If you are not using VSCode, you can need to ensure that you have the following tools installed:
There is a devcontainer available for this project. If you are using VSCode, you can use the devcontainer to get started. If you are not using VSCode, you need to ensure that you have the following tools installed:
- [Go 1.19+](https://golang.org/doc/install)
- [Swaggo](https://github.com/swaggo/swag)
@ -31,27 +31,27 @@ If you're using `taskfile` you can run `task --list-all` for a list of all comma
### Setup
If you're using the taskfile you can use the `task setup` command to run the required setup commands. Otherwise you can review the commands required in the `Taskfile.yml` file.
If you're using the taskfile, you can use the `task setup` command to run the required setup commands. Otherwise, you can review the commands required in the `Taskfile.yml` file.
Note that when installing dependencies with pnpm you must use the `--shamefully-hoist` flag. If you don't use this flag you will get an error when running the the frontend server.
Note that when installing dependencies with pnpm you must use the `--shamefully-hoist` flag. If you don't use this flag, you will get an error when running the frontend server.
### API Development Notes
start command `task go:run`
1. API Server does not auto reload. You'll need to restart the server after making changes.
2. Unit tests should be written in Go, however end-to-end or user story tests should be written in TypeScript using the client library in the frontend directory.
2. Unit tests should be written in Go, however, end-to-end or user story tests should be written in TypeScript using the client library in the frontend directory.
### Frontend Development Notes
start command `task: ui:dev`
1. The frontend is a Vue 3 app with Nuxt.js that uses Tailwind and DaisyUI for styling.
2. We're using Vitest for our automated testing. you can run these with `task ui:watch`.
3. Tests require the API server to be running and in some cases the first run will fail due to a race condition. If this happens just run the tests again and they should pass.
2. We're using Vitest for our automated testing. You can run these with `task ui:watch`.
3. Tests require the API server to be running, and in some cases the first run will fail due to a race condition. If this happens, just run the tests again and they should pass.
## Publishing Release
Create a new tag in github with the version number vX.X.X. This will trigger a new release to be created.
Create a new tag in GitHub with the version number vX.X.X. This will trigger a new release to be created.
Test -> Goreleaser -> Publish Release -> Trigger Docker Builds -> Deploy Docs + Fly.io Demo

View File

@ -1,6 +1,6 @@
# Build Nuxt
FROM node:17-alpine as frontend-builder
FROM node:18-alpine as frontend-builder
WORKDIR /app
RUN npm install -g pnpm
COPY frontend/package.json frontend/pnpm-lock.yaml ./
@ -32,7 +32,7 @@ FROM alpine:latest
ENV HBOX_MODE=production
ENV HBOX_STORAGE_DATA=/data/
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_fk=1
ENV HBOX_STORAGE_SQLITE_URL=/data/homebox.db?_pragma=busy_timeout=2000&_pragma=journal_mode=WAL&_fk=1
RUN apk --no-cache add ca-certificates
RUN mkdir /app

View File

@ -30,6 +30,14 @@ docker run -d \
# 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
- Logo by [@lakotelman](https://github.com/lakotelman)

View File

@ -1,6 +1,7 @@
version: "3"
env:
HBOX_LOG_LEVEL: debug
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_pragma=busy_timeout=1000&_pragma=journal_mode=WAL&_fk=1
HBOX_OPTIONS_ALLOW_REGISTRATION: true
UNSAFE_DISABLE_PASSWORD_PROJECTION: "yes_i_am_sure"
@ -12,15 +13,25 @@ tasks:
- cd backend && go mod tidy
- cd frontend && pnpm install --shamefully-hoist
generate:
desc: |
Generates collateral files from the backend project
including swagger docs and typescripts type for the frontend
deps:
- db:generate
swag:
desc: Generate swagger docs
dir: backend/app/api/static/
vars:
API: "../"
INTERNAL: "../../../internal"
PKGS: "../../../pkgs"
cmds:
- swag fmt --dir={{ .API }}
- swag init --dir={{ .API }},{{ .INTERNAL }}/core/services,{{ .INTERNAL }}/data/repo --parseDependency
sources:
- "./backend/app/api/**/*"
- "./backend/internal/data/**"
- "./backend/internal/core/services/**/*"
- "./backend/app/tools/typegen/main.go"
typescript-types:
desc: Generates typescript types from swagger definition
cmds:
- cd backend/app/api/static && swag fmt --dir=../
- cd backend/app/api/static && swag init --dir=../,../../../internal,../../../pkgs --parseDependency
- |
npx swagger-typescript-api \
--no-client \
@ -28,12 +39,17 @@ tasks:
--path ./backend/app/api/static/docs/swagger.json \
--output ./frontend/lib/api/types
- go run ./backend/app/tools/typegen/main.go ./frontend/lib/api/types/data-contracts.ts
- cp ./backend/app/api/static/docs/swagger.json docs/docs/api/openapi-2.0.json
sources:
- "./backend/app/api/**/*"
- "./backend/internal/data/**"
- "./backend/internal/core/services/**/*"
- "./backend/app/tools/typegen/main.go"
- ./backend/app/tools/typegen/main.go
- ./backend/app/api/static/docs/swagger.json
generate:
deps:
- db:generate
cmds:
- task: swag
- task: typescript-types
- cp ./backend/app/api/static/docs/swagger.json docs/docs/api/openapi-2.0.json
go:run:
desc: Starts the backend api server (depends on generate task)
@ -87,8 +103,7 @@ tasks:
dir: backend/internal/
cmds:
- |
go generate ./... \
--template=./data/ent/schema/templates/has_id.tmpl
go generate ./...
sources:
- "./backend/internal/data/ent/schema/**/*"
@ -139,4 +154,4 @@ tasks:
- task: go:all
- task: ui:check
- task: ui:fix
- task: test:ci
- task: test:ci

74
backend/.golangci.yml Normal file
View File

@ -0,0 +1,74 @@
run:
timeout: 10m
skip-dirs:
- internal/data/ent.*
linters-settings:
goconst:
min-len: 5
min-occurrences: 5
exhaustive:
default-signifies-exhaustive: true
revive:
ignore-generated-header: false
severity: warning
confidence: 3
depguard:
rules:
main:
deny:
- pkg: io/util
desc: |
Deprecated: As of Go 1.16, the same functionality is now provided by
package io or package os, and those implementations should be
preferred in new code. See the specific function documentation for
details.
gocritic:
enabled-checks:
- ruleguard
testifylint:
enable-all: true
tagalign:
order:
- json
- schema
- yaml
- yml
- toml
- validate
linters:
disable-all: true
enable:
- asciicheck
- bodyclose
- depguard
- dogsled
- errcheck
- errorlint
- exhaustive
- exportloopref
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goprintffuncname
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- revive
- staticcheck
- stylecheck
- tagalign
- testifylint
- typecheck
- typecheck
- unconvert
- unused
- whitespace
- zerologlint
- sqlclosecheck
issues:
exclude-use-default: false
fix: true

View File

@ -1,23 +1,21 @@
package main
import (
"time"
"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/ent"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/config"
"github.com/hay-kot/homebox/backend/pkgs/mailer"
"github.com/hay-kot/httpkit/server"
)
type app struct {
conf *config.Config
mailer mailer.Mailer
db *ent.Client
server *server.Server
repos *repo.AllRepos
services *services.AllServices
bus *eventbus.EventBus
}
func new(conf *config.Config) *app {
@ -35,13 +33,3 @@ func new(conf *config.Config) *app {
return s
}
func (a *app) startBgTask(t time.Duration, fn func()) {
timer := time.NewTimer(t)
for {
timer.Reset(t)
a.server.Background(fn)
<-timer.C
}
}

View File

@ -0,0 +1,37 @@
package main
import (
"context"
"time"
)
type BackgroundTask struct {
name string
Interval time.Duration
Fn func(context.Context)
}
func (tsk *BackgroundTask) Name() string {
return tsk.name
}
func NewTask(name string, interval time.Duration, fn func(context.Context)) *BackgroundTask {
return &BackgroundTask{
Interval: interval,
Fn: fn,
}
}
func (tsk *BackgroundTask) Start(ctx context.Context) error {
timer := time.NewTimer(tsk.Interval)
for {
select {
case <-ctx.Done():
return nil
case <-timer.C:
timer.Reset(tsk.Interval)
tsk.Fn(ctx)
}
}
}

View File

@ -3,6 +3,7 @@ package main
import (
"context"
"strings"
"time"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/rs/zerolog/log"
@ -15,9 +16,12 @@ func (a *app) SetupDemo() {
,Office,IOT;Home Assistant; Z-Wave,1,Zooz 110v Power Switch,"Zooz Z-Wave Plus Power Switch ZEN15 for 110V AC Units, Sump Pumps, Humidifiers, and More",,,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
,Downstairs,IOT;Home Assistant; Z-Wave,1,Ecolink Z-Wave PIR Motion Sensor,"Ecolink Z-Wave PIR Motion Detector Pet Immune, White (PIRZWAVE2.5-ECO)",,,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,,,,,,
,Entry,IOT;Home Assistant; Z-Wave,1,Yale Security Touchscreen Deadbolt,"Yale Security YRD226-ZW2-619 YRD226ZW2619 Touchscreen Deadbolt, Satin Nickel",,,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,,,,,,
,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,
,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,
`
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
registration := services.UserRegistration{
Email: "demo@example.com",
Name: "Demo",
@ -25,21 +29,34 @@ func (a *app) SetupDemo() {
}
// First check if we've already setup a demo user and skip if so
_, err := a.services.User.Login(context.Background(), registration.Email, registration.Password, false)
log.Debug().Msg("Checking if demo user already exists")
_, err := a.services.User.Login(ctx, registration.Email, registration.Password, false)
if err == nil {
log.Info().Msg("Demo user already exists, skipping setup")
return
}
_, err = a.services.User.RegisterUser(context.Background(), registration)
log.Debug().Msg("Demo user does not exist, setting up demo")
_, err = a.services.User.RegisterUser(ctx, registration)
if err != nil {
log.Err(err).Msg("Failed to register demo user")
log.Fatal().Msg("Failed to setup demo")
}
token, _ := a.services.User.Login(context.Background(), registration.Email, registration.Password, false)
self, _ := a.services.User.GetSelf(context.Background(), token.Raw)
token, err := a.services.User.Login(ctx, registration.Email, registration.Password, false)
if err != nil {
log.Err(err).Msg("Failed to login demo user")
log.Fatal().Msg("Failed to setup demo")
return
}
self, err := a.services.User.GetSelf(ctx, token.Raw)
if err != nil {
log.Err(err).Msg("Failed to get self")
log.Fatal().Msg("Failed to setup demo")
return
}
_, err = a.services.Items.CsvImport(context.Background(), self.GroupID, strings.NewReader(csvText))
_, err = a.services.Items.CsvImport(ctx, self.GroupID, strings.NewReader(csvText))
if err != nil {
log.Err(err).Msg("Failed to import CSV")
log.Fatal().Msg("Failed to setup demo")

View File

@ -1,3 +1,4 @@
// Package debughandlers provides handlers for debugging.
package debughandlers
import (

View File

@ -1,12 +1,20 @@
// Package v1 provides the API handlers for version 1 of the API.
package v1
import (
"encoding/json"
"net/http"
"time"
"github.com/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 {
@ -43,12 +51,20 @@ func WithRegistration(allowRegistration bool) func(*V1Controller) {
}
}
func WithSecureCookies(secure bool) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.cookieSecure = secure
}
}
type V1Controller struct {
cookieSecure bool
repo *repo.AllRepos
svc *services.AllServices
maxUploadSize int64
isDemo bool
allowRegistration bool
bus *eventbus.EventBus
}
type (
@ -60,7 +76,7 @@ type (
BuildTime string `json:"buildTime"`
}
ApiSummary struct {
APISummary struct {
Healthy bool `json:"health"`
Versions []string `json:"versions"`
Title string `json:"title"`
@ -71,17 +87,18 @@ type (
}
)
func BaseUrlFunc(prefix string) func(s string) string {
func BaseURLFunc(prefix string) func(s string) string {
return func(s string) string {
return prefix + "/v1" + s
}
}
func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, options ...func(*V1Controller)) *V1Controller {
func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, bus *eventbus.EventBus, options ...func(*V1Controller)) *V1Controller {
ctrl := &V1Controller{
repo: repos,
svc: svc,
allowRegistration: true,
bus: bus,
}
for _, opt := range options {
@ -96,11 +113,11 @@ func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, options ..
// @Summary Application Info
// @Tags Base
// @Produce json
// @Success 200 {object} ApiSummary
// @Success 200 {object} APISummary
// @Router /v1/status [GET]
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
return server.JSON(w, http.StatusOK, ApiSummary{
return server.JSON(w, http.StatusOK, APISummary{
Healthy: ready(),
Title: "Homebox",
Message: "Track, Manage, and Organize your Things",
@ -110,3 +127,87 @@ func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.Hand
})
}
}
// HandleCurrency godoc
//
// @Summary Currency
// @Tags Base
// @Produce json
// @Success 200 {object} currencies.Currency
// @Router /v1/currency [GET]
func (ctrl *V1Controller) HandleCurrency() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
// Set Cache for 10 Minutes
w.Header().Set("Cache-Control", "max-age=600")
return server.JSON(w, http.StatusOK, ctrl.svc.Currencies.Slice())
}
}
func (ctrl *V1Controller) HandleCacheWS() errchain.HandlerFunc {
type eventMsg struct {
Event string `json:"event"`
}
m := melody.New()
m.HandleConnect(func(s *melody.Session) {
auth := services.NewContext(s.Request.Context())
s.Set("gid", auth.GID)
})
factory := func(e string) func(data any) {
return func(data any) {
eventData, ok := data.(eventbus.GroupMutationEvent)
if !ok {
log.Log().Msgf("invalid event data: %v", data)
return
}
msg := &eventMsg{Event: e}
jsonBytes, err := json.Marshal(msg)
if err != nil {
log.Log().Msgf("error marshling event data %v: %v", data, err)
return
}
_ = m.BroadcastFilter(jsonBytes, func(s *melody.Session) bool {
groupIDStr, ok := s.Get("gid")
if !ok {
return false
}
GID := groupIDStr.(uuid.UUID)
return GID == eventData.GID
})
}
}
ctrl.bus.Subscribe(eventbus.EventLabelMutation, factory("label.mutation"))
ctrl.bus.Subscribe(eventbus.EventLocationMutation, factory("location.mutation"))
ctrl.bus.Subscribe(eventbus.EventItemMutation, factory("item.mutation"))
// Persistent asynchronous ticker that keeps all websocket connections alive with periodic pings.
go func() {
const interval = 10 * time.Second
ping := time.NewTicker(interval)
defer ping.Stop()
for range ping.C {
msg := &eventMsg{Event: "ping"}
pingBytes, err := json.Marshal(msg)
if err != nil {
log.Log().Msgf("error marshaling ping: %v", err)
} else {
_ = m.Broadcast(pingBytes)
}
}
}()
return func(w http.ResponseWriter, r *http.Request) error {
return m.HandleRequest(w, r)
}
}

View File

@ -68,3 +68,16 @@ func (ctrl *V1Controller) HandleEnsureImportRefs() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleItemDateZeroOut() errchain.HandlerFunc {
return actionHandlerFactory("zero out date time", ctrl.repo.Items.ZeroOutTimeFields)
}
// HandleSetPrimaryPhotos godoc
//
// @Summary Set Primary Photos
// @Description Sets the first photo of each item as the primary photo
// @Tags Actions
// @Produce json
// @Success 200 {object} ActionAmountResult
// @Router /v1/actions/set-primary-photos [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleSetPrimaryPhotos() errchain.HandlerFunc {
return actionHandlerFactory("ensure asset IDs", ctrl.repo.Items.SetPrimaryPhotos)
}

View File

@ -27,10 +27,10 @@ import (
func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
assetIdParam := chi.URLParam(r, "id")
assetIdParam = strings.ReplaceAll(assetIdParam, "-", "") // Remove dashes
assetIDParam := chi.URLParam(r, "id")
assetIDParam = strings.ReplaceAll(assetIDParam, "-", "") // Remove dashes
// Convert the asset ID to an int64
assetId, err := strconv.ParseInt(assetIdParam, 10, 64)
assetID, err := strconv.ParseInt(assetIDParam, 10, 64)
if err != nil {
return err
}
@ -52,7 +52,7 @@ func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
}
}
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetId), int(page), int(pageSize))
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetID), int(page), int(pageSize))
if err != nil {
log.Err(err).Msg("failed to get item")
return validate.NewRequestError(err, http.StatusInternalServerError)

View File

@ -3,6 +3,7 @@ package v1
import (
"errors"
"net/http"
"strconv"
"strings"
"time"
@ -13,6 +14,12 @@ import (
"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"`
@ -27,6 +34,49 @@ type (
}
)
type CookieContents struct {
Token string
ExpiresAt time.Time
Remember bool
}
func GetCookies(r *http.Request) (*CookieContents, error) {
cookie, err := r.Cookie(cookieNameToken)
if err != nil {
return nil, errors.New("authorization cookie is required")
}
rememberCookie, err := r.Cookie(cookieNameRemember)
if err != nil {
return nil, errors.New("remember cookie is required")
}
return &CookieContents{
Token: cookie.Value,
ExpiresAt: cookie.Expires,
Remember: rememberCookie.Value == "true",
}, nil
}
// AuthProvider is an interface that can be implemented by any authentication provider.
// to extend authentication methods for the API.
type AuthProvider interface {
// Name returns the name of the authentication provider. This should be a unique name.
// that is URL friendly.
//
// Example: "local", "ldap"
Name() string
// Authenticate is called when a user attempts to login to the API. The implementation
// should return an error if the user cannot be authenticated. If an error is returned
// the API controller will return a vague error message to the user.
//
// Authenticate should do the following:
//
// 1. Ensure that the user exists within the database (either create, or get)
// 2. On successful authentication, they must set the user cookies.
Authenticate(w http.ResponseWriter, r *http.Request) (services.UserAuthTokenDetail, error)
}
// HandleAuthLogin godoc
//
// @Summary User Login
@ -35,52 +85,42 @@ type (
// @Accept application/json
// @Param username formData string false "string" example(admin@admin.com)
// @Param password formData string false "string" example(admin)
// @Param payload body LoginForm true "Login Data"
// @Param payload body LoginForm true "Login Data"
// @Param provider query string false "auth provider"
// @Produce json
// @Success 200 {object} TokenResponse
// @Router /v1/users/login [POST]
func (ctrl *V1Controller) HandleAuthLogin() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleAuthLogin(ps ...AuthProvider) errchain.HandlerFunc {
if len(ps) == 0 {
panic("no auth providers provided")
}
providers := make(map[string]AuthProvider)
for _, p := range ps {
log.Info().Str("name", p.Name()).Msg("registering auth provider")
providers[p.Name()] = p
}
return func(w http.ResponseWriter, r *http.Request) error {
loginForm := &LoginForm{}
switch r.Header.Get("Content-Type") {
case "application/x-www-form-urlencoded":
err := r.ParseForm()
if err != nil {
return errors.New("failed to parse form")
}
loginForm.Username = r.PostFormValue("username")
loginForm.Password = r.PostFormValue("password")
loginForm.StayLoggedIn = r.PostFormValue("stayLoggedIn") == "true"
case "application/json":
err := server.Decode(r, loginForm)
if err != nil {
log.Err(err).Msg("failed to decode login form")
return errors.New("failed to decode login form")
}
default:
return server.JSON(w, http.StatusBadRequest, errors.New("invalid content type"))
// Extract provider query
provider := r.URL.Query().Get("provider")
if provider == "" {
provider = "local"
}
if loginForm.Username == "" || loginForm.Password == "" {
return validate.NewFieldErrors(
validate.FieldError{
Field: "username",
Error: "username or password is empty",
},
validate.FieldError{
Field: "password",
Error: "username or password is empty",
},
)
// Get the provider
p, ok := providers[provider]
if !ok {
return validate.NewRequestError(errors.New("invalid auth provider"), http.StatusBadRequest)
}
newToken, err := ctrl.svc.User.Login(r.Context(), strings.ToLower(loginForm.Username), loginForm.Password, loginForm.StayLoggedIn)
newToken, err := p.Authenticate(w, r)
if err != nil {
return validate.NewRequestError(errors.New("authentication failed"), http.StatusInternalServerError)
log.Err(err).Msg("failed to authenticate")
return server.JSON(w, http.StatusInternalServerError, err.Error())
}
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, true)
return server.JSON(w, http.StatusOK, TokenResponse{
Token: "Bearer " + newToken.Raw,
ExpiresAt: newToken.ExpiresAt,
@ -108,11 +148,12 @@ func (ctrl *V1Controller) HandleAuthLogout() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
ctrl.unsetCookies(w, noPort(r.Host))
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleAuthLogout godoc
// HandleAuthRefresh godoc
//
// @Summary User Token Refresh
// @Description handleAuthRefresh returns a handler that will issue a new token from an existing token.
@ -133,6 +174,78 @@ func (ctrl *V1Controller) HandleAuthRefresh() errchain.HandlerFunc {
return validate.NewUnauthorizedError()
}
ctrl.setCookies(w, noPort(r.Host), newToken.Raw, newToken.ExpiresAt, false)
return server.JSON(w, http.StatusOK, newToken)
}
}
func noPort(host string) string {
return strings.Split(host, ":")[0]
}
func (ctrl *V1Controller) setCookies(w http.ResponseWriter, domain, token string, expires time.Time, remember bool) {
http.SetCookie(w, &http.Cookie{
Name: cookieNameRemember,
Value: strconv.FormatBool(remember),
Expires: expires,
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: true,
Path: "/",
})
// Set HTTP only cookie
http.SetCookie(w, &http.Cookie{
Name: cookieNameToken,
Value: token,
Expires: expires,
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: true,
Path: "/",
})
// Set Fake Session cookie
http.SetCookie(w, &http.Cookie{
Name: cookieNameSession,
Value: "true",
Expires: expires,
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: false,
Path: "/",
})
}
func (ctrl *V1Controller) unsetCookies(w http.ResponseWriter, domain string) {
http.SetCookie(w, &http.Cookie{
Name: cookieNameToken,
Value: "",
Expires: time.Unix(0, 0),
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: true,
Path: "/",
})
http.SetCookie(w, &http.Cookie{
Name: cookieNameRemember,
Value: "false",
Expires: time.Unix(0, 0),
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: true,
Path: "/",
})
// Set Fake Session cookie
http.SetCookie(w, &http.Cookie{
Name: cookieNameSession,
Value: "false",
Expires: time.Unix(0, 0),
Domain: domain,
Secure: ctrl.cookieSecure,
HttpOnly: false,
Path: "/",
})
}

View File

@ -6,13 +6,14 @@ import (
"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"`
Uses int `json:"uses" validate:"required,min=1,max=100"`
ExpiresAt time.Time `json:"expiresAt"`
}
@ -52,6 +53,14 @@ func (ctrl *V1Controller) HandleGroupGet() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleGroupUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, body repo.GroupUpdate) (repo.Group, error) {
auth := services.NewContext(r.Context())
ok := ctrl.svc.Currencies.IsSupported(body.Currency)
if !ok {
return repo.Group{}, validate.NewFieldErrors(
validate.NewFieldError("currency", "currency '"+body.Currency+"' is not supported"),
)
}
return ctrl.svc.Group.UpdateGroup(auth, body)
}

View File

@ -27,6 +27,7 @@ import (
// @Param pageSize query int false "items per page"
// @Param labels query []string false "label Ids" collectionFormat(multi)
// @Param locations query []string false "location Ids" collectionFormat(multi)
// @Param parentIds query []string false "parent Ids" collectionFormat(multi)
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
// @Router /v1/items [GET]
// @Security Bearer
@ -56,6 +57,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
Search: params.Get("q"),
LocationIDs: queryUUIDList(params, "locations"),
LabelIDs: queryUUIDList(params, "labels"),
ParentItemIDs: queryUUIDList(params, "parentIds"),
IncludeArchived: queryBool(params.Get("includeArchived")),
Fields: filterFieldItems(params["fields"]),
OrderBy: params.Get("orderBy"),
@ -91,6 +93,48 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
}
}
// HandleItemFullPath godoc
//
// @Summary Get the full path of an item
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 200 {object} []repo.ItemPath
// @Router /v1/items/{id}/path [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemFullPath() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) ([]repo.ItemPath, error) {
auth := services.NewContext(r.Context())
item, err := ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
if err != nil {
return nil, err
}
paths, err := ctrl.repo.Locations.PathForLoc(auth, auth.GID, item.Location.ID)
if err != nil {
return nil, err
}
if item.Parent != nil {
paths = append(paths, repo.ItemPath{
Type: repo.ItemTypeItem,
ID: item.Parent.ID,
Name: item.Parent.Name,
})
}
paths = append(paths, repo.ItemPath{
Type: repo.ItemTypeItem,
ID: item.ID,
Name: item.Name,
})
return paths, nil
}
return adapters.CommandID("id", fn, http.StatusOK)
}
// HandleItemsCreate godoc
//
// @Summary Create Item
@ -167,7 +211,6 @@ func (ctrl *V1Controller) HandleItemUpdate() errchain.HandlerFunc {
return adapters.ActionID("id", fn, http.StatusOK)
}
// HandleItemPatch godocs
//
// @Summary Update Item
@ -183,12 +226,12 @@ func (ctrl *V1Controller) HandleItemPatch() errchain.HandlerFunc {
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
}
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 ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
}
return adapters.ActionID("id", fn, http.StatusOK)
@ -231,8 +274,7 @@ func (ctrl *V1Controller) HandleGetAllCustomFieldValues() errchain.HandlerFunc {
return ctrl.repo.Items.GetAllCustomFieldValues(auth, auth.GID, q.Field)
}
return adapters.Action(fn, http.StatusOK)
return adapters.Query(fn, http.StatusOK)
}
// HandleItemsImport godocs

View File

@ -3,6 +3,8 @@ 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"
@ -38,7 +40,6 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
return validate.NewRequestError(errors.New("failed to parse multipart form"), http.StatusBadRequest)
}
errs := validate.NewFieldErrors()
@ -67,7 +68,15 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() errchain.HandlerFunc {
attachmentType := r.FormValue("type")
if attachmentType == "" {
attachmentType = attachment.TypeAttachment.String()
// Attempt to auto-detect the type of the file
ext := filepath.Ext(attachmentName)
switch strings.ToLower(ext) {
case ".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tiff":
attachmentType = attachment.TypePhoto.String()
default:
attachmentType = attachment.TypeAttachment.String()
}
}
id, err := ctrl.routeID(r)

View File

@ -10,7 +10,7 @@ import (
"github.com/hay-kot/httpkit/errchain"
)
// HandleLocationTreeQuery
// HandleLocationTreeQuery godoc
//
// @Summary Get Locations Tree
// @Tags Locations
@ -28,7 +28,7 @@ func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
return adapters.Query(fn, http.StatusOK)
}
// HandleLocationGetAll
// HandleLocationGetAll godoc
//
// @Summary Get All Locations
// @Tags Locations
@ -46,7 +46,7 @@ func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
return adapters.Query(fn, http.StatusOK)
}
// HandleLocationCreate
// HandleLocationCreate godoc
//
// @Summary Create Location
// @Tags Locations
@ -64,7 +64,7 @@ func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
return adapters.Action(fn, http.StatusCreated)
}
// HandleLocationDelete
// HandleLocationDelete godoc
//
// @Summary Delete Location
// @Tags Locations
@ -83,7 +83,7 @@ func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
return adapters.CommandID("id", fn, http.StatusNoContent)
}
// HandleLocationGet
// HandleLocationGet godoc
//
// @Summary Get Location
// @Tags Locations
@ -101,7 +101,7 @@ func (ctrl *V1Controller) HandleLocationGet() errchain.HandlerFunc {
return adapters.CommandID("id", fn, http.StatusOK)
}
// HandleLocationUpdate
// HandleLocationUpdate godoc
//
// @Summary Update Location
// @Tags Locations

View File

@ -10,7 +10,7 @@ import (
"github.com/hay-kot/httpkit/errchain"
)
// HandleMaintenanceGetLog godoc
// HandleMaintenanceLogGet godoc
//
// @Summary Get Maintenance Log
// @Tags Maintenance

View File

@ -5,6 +5,7 @@ import (
"image/png"
"io"
"net/http"
"net/url"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/httpkit/errchain"
@ -43,7 +44,12 @@ func (ctrl *V1Controller) HandleGenerateQRCode() errchain.HandlerFunc {
panic(err)
}
qrc, err := qrcode.New(q.Data)
decodedStr, err := url.QueryUnescape(q.Data)
if err != nil {
return err
}
qrc, err := qrcode.New(decodedStr)
if err != nil {
return err
}

View File

@ -12,7 +12,7 @@ import (
"github.com/hay-kot/httpkit/server"
)
// HandleGroupGet godoc
// HandleGroupStatisticsLocations godoc
//
// @Summary Get Location Statistics
// @Tags Statistics

View File

@ -2,7 +2,6 @@ package main
import (
"os"
"strings"
"github.com/hay-kot/homebox/backend/internal/sys/config"
"github.com/rs/zerolog"
@ -18,24 +17,8 @@ func (a *app) setupLogger() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger()
}
log.Level(getLevel(a.conf.Log.Level))
}
func getLevel(l string) zerolog.Level {
switch strings.ToLower(l) {
case "debug":
return zerolog.DebugLevel
case "info":
return zerolog.InfoLevel
case "warn":
return zerolog.WarnLevel
case "error":
return zerolog.ErrorLevel
case "fatal":
return zerolog.FatalLevel
case "panic":
return zerolog.PanicLevel
default:
return zerolog.InfoLevel
level, err := zerolog.ParseLevel(a.conf.Log.Level)
if err == nil {
zerolog.SetGlobalLevel(level)
}
}

View File

@ -1,6 +1,7 @@
package main
import (
"bytes"
"context"
"fmt"
"net/http"
@ -13,14 +14,16 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/hay-kot/homebox/backend/internal/core/currencies"
"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/ent"
"github.com/hay-kot/homebox/backend/internal/data/migrations"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/config"
"github.com/hay-kot/homebox/backend/internal/web/mid"
"github.com/hay-kot/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/hay-kot/httpkit/graceful"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/pkgerrors"
@ -34,6 +37,15 @@ var (
buildTime = "now"
)
func build() string {
short := commit
if len(short) > 7 {
short = short[:7]
}
return fmt.Sprintf("%s, commit %s, built at %s", version, short, buildTime)
}
// @title Homebox API
// @version 1.0
// @description Track, Manage, and Organize your Things.
@ -46,7 +58,7 @@ var (
func main() {
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
cfg, err := config.New()
cfg, err := config.New(build(), "Homebox inventory management system")
if err != nil {
panic(err)
}
@ -68,12 +80,12 @@ func run(cfg *config.Config) error {
log.Fatal().Err(err).Msg("failed to create data directory")
}
c, err := ent.Open("sqlite3", cfg.Storage.SqliteUrl)
c, err := ent.Open("sqlite3", cfg.Storage.SqliteURL)
if err != nil {
log.Fatal().
Err(err).
Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteUrl).
Str("url", cfg.Storage.SqliteURL).
Msg("failed opening connection to sqlite")
}
defer func(c *ent.Client) {
@ -106,7 +118,7 @@ func run(cfg *config.Config) error {
log.Fatal().
Err(err).
Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteUrl).
Str("url", cfg.Storage.SqliteURL).
Msg("failed creating schema resources")
}
@ -116,11 +128,40 @@ func run(cfg *config.Config) error {
return err
}
collectFuncs := []currencies.CollectorFunc{
currencies.CollectDefaults(),
}
if cfg.Options.CurrencyConfig != "" {
log.Info().
Str("path", cfg.Options.CurrencyConfig).
Msg("loading currency config file")
content, err := os.ReadFile(cfg.Options.CurrencyConfig)
if err != nil {
log.Fatal().
Err(err).
Str("path", cfg.Options.CurrencyConfig).
Msg("failed to read currency config file")
}
collectFuncs = append(collectFuncs, currencies.CollectJSON(bytes.NewReader(content)))
}
currencies, err := currencies.CollectionCurrencies(collectFuncs...)
if err != nil {
log.Fatal().
Err(err).
Msg("failed to collect currencies")
}
app.bus = eventbus.New()
app.db = c
app.repos = repo.New(c, cfg.Storage.Data)
app.repos = repo.New(c, app.bus, cfg.Storage.Data)
app.services = services.New(
app.repos,
services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID),
services.WithCurrencies(currencies),
)
// =========================================================================
@ -137,36 +178,63 @@ func run(cfg *config.Config) error {
middleware.StripSlashes,
)
chain := errchain.New(mid.Errors(app.server, logger))
chain := errchain.New(mid.Errors(logger))
app.mountRoutes(router, chain, app.repos)
app.server = server.NewServer(
server.WithHost(app.conf.Web.Host),
server.WithPort(app.conf.Web.Port),
)
log.Info().Msgf("Starting HTTP Server on %s:%s", app.server.Host, app.server.Port)
runner := graceful.NewRunner()
runner.AddFunc("server", func(ctx context.Context) error {
httpserver := http.Server{
Addr: fmt.Sprintf("%s:%s", cfg.Web.Host, cfg.Web.Port),
Handler: router,
ReadTimeout: cfg.Web.ReadTimeout,
WriteTimeout: cfg.Web.WriteTimeout,
IdleTimeout: cfg.Web.IdleTimeout,
}
go func() {
<-ctx.Done()
_ = httpserver.Shutdown(context.Background())
}()
log.Info().Msgf("Server is running on %s:%s", cfg.Web.Host, cfg.Web.Port)
return httpserver.ListenAndServe()
})
// =========================================================================
// Start Reoccurring Tasks
go app.startBgTask(time.Duration(24)*time.Hour, func() {
_, err := app.repos.AuthTokens.PurgeExpiredTokens(context.Background())
runner.AddFunc("eventbus", app.bus.Run)
runner.AddFunc("seed_database", func(ctx context.Context) error {
// TODO: Remove through external API that does setup
if cfg.Demo {
log.Info().Msg("Running in demo mode, creating demo data")
app.SetupDemo()
}
return nil
})
runner.AddPlugin(NewTask("purge-tokens", time.Duration(24)*time.Hour, func(ctx context.Context) {
_, err := app.repos.AuthTokens.PurgeExpiredTokens(ctx)
if err != nil {
log.Error().
Err(err).
Msg("failed to purge expired tokens")
}
})
go app.startBgTask(time.Duration(24)*time.Hour, func() {
_, err := app.repos.Groups.InvitationPurge(context.Background())
}))
runner.AddPlugin(NewTask("purge-invitations", time.Duration(24)*time.Hour, func(ctx context.Context) {
_, err := app.repos.Groups.InvitationPurge(ctx)
if err != nil {
log.Error().
Err(err).
Msg("failed to purge expired invitations")
}
})
go app.startBgTask(time.Duration(1)*time.Hour, func() {
}))
runner.AddPlugin(NewTask("send-notifications", time.Duration(1)*time.Hour, func(ctx context.Context) {
now := time.Now()
if now.Hour() == 8 {
@ -178,22 +246,27 @@ func run(cfg *config.Config) error {
Msg("failed to send notifiers")
}
}
})
// TODO: Remove through external API that does setup
if cfg.Demo {
log.Info().Msg("Running in demo mode, creating demo data")
app.SetupDemo()
}
}))
if cfg.Debug.Enabled {
debugrouter := app.debugRouter()
go func() {
if err := http.ListenAndServe(":"+cfg.Debug.Port, debugrouter); err != nil {
log.Fatal().Err(err).Msg("failed to start debug server")
runner.AddFunc("debug", func(ctx context.Context) error {
debugserver := http.Server{
Addr: fmt.Sprintf("%s:%s", cfg.Web.Host, cfg.Debug.Port),
Handler: app.debugRouter(),
ReadTimeout: cfg.Web.ReadTimeout,
WriteTimeout: cfg.Web.WriteTimeout,
IdleTimeout: cfg.Web.IdleTimeout,
}
}()
go func() {
<-ctx.Done()
_ = debugserver.Shutdown(context.Background())
}()
log.Info().Msgf("Debug server is running on %s:%s", cfg.Web.Host, cfg.Debug.Port)
return debugserver.ListenAndServe()
})
}
return app.server.Start(router)
return runner.Start(context.Background())
}

View File

@ -7,7 +7,9 @@ import (
"net/url"
"strings"
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1"
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/httpkit/errchain"
)
@ -94,20 +96,6 @@ func getQuery(r *http.Request) (string, error) {
return token, nil
}
func getCookie(r *http.Request) (string, error) {
cookie, err := r.Cookie("hb.auth.token")
if err != nil {
return "", errors.New("access_token cookie is required")
}
token, err := url.QueryUnescape(cookie.Value)
if err != nil {
return "", errors.New("access_token cookie is required")
}
return token, nil
}
// mwAuthToken is a middleware that will check the database for a stateful token
// and attach it's user to the request context, or return an appropriate error.
// Authorization support is by token via Headers or Query Parameter
@ -115,26 +103,35 @@ func getCookie(r *http.Request) (string, error) {
// Example:
// - header = "Bearer 1234567890"
// - query = "?access_token=1234567890"
// - cookie = hb.auth.token = 1234567890
func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
return errchain.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
keyFuncs := [...]KeyFunc{
getBearer,
getCookie,
getQuery,
}
var requestToken string
for _, keyFunc := range keyFuncs {
token, err := keyFunc(r)
if err == nil {
requestToken = token
break
// We ignore the error to allow the next strategy to be attempted
{
cookies, _ := v1.GetCookies(r)
if cookies != nil {
requestToken = cookies.Token
}
}
if requestToken == "" {
return validate.NewRequestError(errors.New("Authorization header or query is required"), http.StatusUnauthorized)
keyFuncs := [...]KeyFunc{
getBearer,
getQuery,
}
for _, keyFunc := range keyFuncs {
token, err := keyFunc(r)
if err == nil {
requestToken = token
break
}
}
}
if requestToken == "" {
return validate.NewRequestError(errors.New("authorization header or query is required"), http.StatusUnauthorized)
}
requestToken = strings.TrimPrefix(requestToken, "Bearer ")
@ -144,7 +141,11 @@ func (a *app) mwAuthToken(next errchain.Handler) errchain.Handler {
usr, err := a.services.User.GetSelf(r.Context(), requestToken)
// Check the database for the token
if err != nil {
return validate.NewRequestError(errors.New("valid authorization header is required"), http.StatusUnauthorized)
if ent.IsNotFound(err) {
return validate.NewRequestError(errors.New("valid authorization token is required"), http.StatusUnauthorized)
}
return err
}
r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken))

View File

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

View File

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

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

@ -12,11 +12,12 @@ import (
"github.com/go-chi/chi/v5"
"github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers"
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1"
"github.com/hay-kot/homebox/backend/app/api/providers"
_ "github.com/hay-kot/homebox/backend/app/api/static/docs"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/httpkit/errchain"
httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware
httpSwagger "github.com/swaggo/http-swagger/v2" // http-swagger middleware
)
const prefix = "/api"
@ -46,11 +47,12 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
// =========================================================================
// API Version 1
v1Base := v1.BaseUrlFunc(prefix)
v1Base := v1.BaseURLFunc(prefix)
v1Ctrl := v1.NewControllerV1(
a.services,
a.repos,
a.bus,
v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize),
v1.WithRegistration(a.conf.Options.AllowRegistration),
v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode
@ -62,14 +64,21 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
BuildTime: buildTime,
})))
r.Get(v1Base("/currencies"), chain.ToHandlerFunc(v1Ctrl.HandleCurrency()))
providers := []v1.AuthProvider{
providers.NewLocalProvider(a.services.User),
}
r.Post(v1Base("/users/register"), chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration()))
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin()))
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providers...)))
userMW := []errchain.Middleware{
a.mwAuthToken,
a.mwRoles(RoleModeOr, authroles.RoleUser.String()),
}
r.Get(v1Base("/ws/events"), chain.ToHandlerFunc(v1Ctrl.HandleCacheWS(), userMW...))
r.Get(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelf(), userMW...))
r.Put(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfUpdate(), userMW...))
r.Delete(v1Base("/users/self"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfDelete(), userMW...))
@ -90,6 +99,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
r.Post(v1Base("/actions/ensure-asset-ids"), chain.ToHandlerFunc(v1Ctrl.HandleEnsureAssetID(), userMW...))
r.Post(v1Base("/actions/zero-item-time-fields"), chain.ToHandlerFunc(v1Ctrl.HandleItemDateZeroOut(), userMW...))
r.Post(v1Base("/actions/ensure-import-refs"), chain.ToHandlerFunc(v1Ctrl.HandleEnsureImportRefs(), userMW...))
r.Post(v1Base("/actions/set-primary-photos"), chain.ToHandlerFunc(v1Ctrl.HandleSetPrimaryPhotos(), userMW...))
r.Get(v1Base("/locations"), chain.ToHandlerFunc(v1Ctrl.HandleLocationGetAll(), userMW...))
r.Post(v1Base("/locations"), chain.ToHandlerFunc(v1Ctrl.HandleLocationCreate(), userMW...))
@ -112,6 +122,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
r.Get(v1Base("/items/fields/values"), chain.ToHandlerFunc(v1Ctrl.HandleGetAllCustomFieldValues(), userMW...))
r.Get(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemGet(), userMW...))
r.Get(v1Base("/items/{id}/path"), chain.ToHandlerFunc(v1Ctrl.HandleItemFullPath(), userMW...))
r.Put(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemUpdate(), userMW...))
r.Patch(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemPatch(), userMW...))
r.Delete(v1Base("/items/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleItemDelete(), userMW...))
@ -125,7 +136,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
r.Put(v1Base("/items/{id}/maintenance/{entry_id}"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryUpdate(), userMW...))
r.Delete(v1Base("/items/{id}/maintenance/{entry_id}"), chain.ToHandlerFunc(v1Ctrl.HandleMaintenanceEntryDelete(), userMW...))
r.Get(v1Base("/asset/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleAssetGet(), userMW...))
r.Get(v1Base("/assets/{id}"), chain.ToHandlerFunc(v1Ctrl.HandleAssetGet(), userMW...))
// Notifiers
r.Get(v1Base("/notifiers"), chain.ToHandlerFunc(v1Ctrl.HandleGetUserNotifiers(), userMW...))
@ -153,7 +164,6 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
r.Get(v1Base("/reporting/bill-of-materials"), chain.ToHandlerFunc(v1Ctrl.HandleBillOfMaterialsExport(), userMW...))
r.NotFound(chain.ToHandlerFunc(notFoundHandler()))
}
func registerMimes() {
@ -176,7 +186,7 @@ func notFoundHandler() errchain.HandlerFunc {
if err != nil {
return err
}
defer f.Close()
defer func() { _ = f.Close() }()
stat, _ := f.Stat()
if stat.IsDir() {

View File

@ -1,5 +1,4 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
@ -68,6 +67,31 @@ const docTemplate = `{
}
}
},
"/v1/actions/set-primary-photos": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "Sets the first photo of each item as the primary photo",
"produces": [
"application/json"
],
"tags": [
"Actions"
],
"summary": "Set Primary Photos",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/v1.ActionAmountResult"
}
}
}
}
},
"/v1/actions/zero-item-time-fields": {
"post": {
"security": [
@ -126,6 +150,25 @@ const docTemplate = `{
}
}
},
"/v1/currency": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Base"
],
"summary": "Currency",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/currencies.Currency"
}
}
}
}
},
"/v1/groups": {
"get": {
"security": [
@ -386,6 +429,16 @@ const docTemplate = `{
"description": "location Ids",
"name": "locations",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "parent Ids",
"name": "parentIds",
"in": "query"
}
],
"responses": {
@ -731,7 +784,7 @@ const docTemplate = `{
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/mid.ErrorResponse"
"$ref": "#/definitions/validate.ErrorResponse"
}
}
}
@ -964,6 +1017,42 @@ const docTemplate = `{
}
}
},
"/v1/items/{id}/path": {
"get": {
"security": [
{
"Bearer": []
}
],
"produces": [
"application/json"
],
"tags": [
"Items"
],
"summary": "Get the full path of an item",
"parameters": [
{
"type": "string",
"description": "Item ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemPath"
}
}
}
}
}
},
"/v1/labels": {
"get": {
"security": [
@ -1550,7 +1639,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/v1.ApiSummary"
"$ref": "#/definitions/v1.APISummary"
}
}
}
@ -1621,6 +1710,12 @@ const docTemplate = `{
"schema": {
"$ref": "#/definitions/v1.LoginForm"
}
},
{
"type": "string",
"description": "auth provider",
"name": "provider",
"in": "query"
}
],
"responses": {
@ -1799,17 +1894,20 @@ const docTemplate = `{
}
},
"definitions": {
"mid.ErrorResponse": {
"currencies.Currency": {
"type": "object",
"properties": {
"error": {
"code": {
"type": "string"
},
"fields": {
"type": "object",
"additionalProperties": {
"type": "string"
}
"local": {
"type": "string"
},
"name": {
"type": "string"
},
"symbol": {
"type": "string"
}
}
},
@ -1893,6 +1991,9 @@ const docTemplate = `{
"id": {
"type": "string"
},
"primary": {
"type": "boolean"
},
"type": {
"type": "string"
},
@ -1904,6 +2005,9 @@ const docTemplate = `{
"repo.ItemAttachmentUpdate": {
"type": "object",
"properties": {
"primary": {
"type": "boolean"
},
"title": {
"type": "string"
},
@ -1982,12 +2086,6 @@ const docTemplate = `{
"$ref": "#/definitions/repo.ItemAttachment"
}
},
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemSummary"
}
},
"createdAt": {
"type": "string"
},
@ -2003,6 +2101,9 @@ const docTemplate = `{
"id": {
"type": "string"
},
"imageId": {
"type": "string"
},
"insured": {
"type": "boolean"
},
@ -2018,9 +2119,13 @@ const docTemplate = `{
},
"location": {
"description": "Edges",
"allOf": [
{
"$ref": "#/definitions/repo.LocationSummary"
}
],
"x-nullable": true,
"x-omitempty": true,
"$ref": "#/definitions/repo.LocationSummary"
"x-omitempty": true
},
"manufacturer": {
"type": "string"
@ -2036,9 +2141,13 @@ const docTemplate = `{
"type": "string"
},
"parent": {
"allOf": [
{
"$ref": "#/definitions/repo.ItemSummary"
}
],
"x-nullable": true,
"x-omitempty": true,
"$ref": "#/definitions/repo.ItemSummary"
"x-omitempty": true
},
"purchaseFrom": {
"type": "string"
@ -2095,6 +2204,20 @@ const docTemplate = `{
}
}
},
"repo.ItemPath": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"$ref": "#/definitions/repo.ItemType"
}
}
},
"repo.ItemSummary": {
"type": "object",
"properties": {
@ -2110,6 +2233,9 @@ const docTemplate = `{
"id": {
"type": "string"
},
"imageId": {
"type": "string"
},
"insured": {
"type": "boolean"
},
@ -2121,9 +2247,13 @@ const docTemplate = `{
},
"location": {
"description": "Edges",
"allOf": [
{
"$ref": "#/definitions/repo.LocationSummary"
}
],
"x-nullable": true,
"x-omitempty": true,
"$ref": "#/definitions/repo.LocationSummary"
"x-omitempty": true
},
"name": {
"type": "string"
@ -2140,6 +2270,17 @@ const docTemplate = `{
}
}
},
"repo.ItemType": {
"type": "string",
"enum": [
"location",
"item"
],
"x-enum-varnames": [
"ItemTypeLocation",
"ItemTypeItem"
]
},
"repo.ItemUpdate": {
"type": "object",
"properties": {
@ -2232,7 +2373,6 @@ const docTemplate = `{
"type": "string"
},
"warrantyExpires": {
"description": "Sold",
"type": "string"
}
}
@ -2269,12 +2409,6 @@ const docTemplate = `{
"id": {
"type": "string"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemSummary"
}
},
"name": {
"type": "string"
},
@ -2336,12 +2470,6 @@ const docTemplate = `{
"id": {
"type": "string"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemSummary"
}
},
"name": {
"type": "string"
},
@ -2418,7 +2546,6 @@ const docTemplate = `{
"type": "object",
"properties": {
"completedDate": {
"description": "Sold",
"type": "string"
},
"cost": {
@ -2435,7 +2562,6 @@ const docTemplate = `{
"type": "string"
},
"scheduledDate": {
"description": "Sold",
"type": "string"
}
}
@ -2447,7 +2573,6 @@ const docTemplate = `{
],
"properties": {
"completedDate": {
"description": "Sold",
"type": "string"
},
"cost": {
@ -2461,7 +2586,6 @@ const docTemplate = `{
"type": "string"
},
"scheduledDate": {
"description": "Sold",
"type": "string"
}
}
@ -2470,7 +2594,6 @@ const docTemplate = `{
"type": "object",
"properties": {
"completedDate": {
"description": "Sold",
"type": "string"
},
"cost": {
@ -2484,7 +2607,6 @@ const docTemplate = `{
"type": "string"
},
"scheduledDate": {
"description": "Sold",
"type": "string"
}
}
@ -2720,15 +2842,7 @@ const docTemplate = `{
}
}
},
"v1.ActionAmountResult": {
"type": "object",
"properties": {
"completed": {
"type": "integer"
}
}
},
"v1.ApiSummary": {
"v1.APISummary": {
"type": "object",
"properties": {
"allowRegistration": {
@ -2757,6 +2871,14 @@ const docTemplate = `{
}
}
},
"v1.ActionAmountResult": {
"type": "object",
"properties": {
"completed": {
"type": "integer"
}
}
},
"v1.Build": {
"type": "object",
"properties": {
@ -2853,6 +2975,17 @@ const docTemplate = `{
"properties": {
"item": {}
}
},
"validate.ErrorResponse": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"fields": {
"type": "string"
}
}
}
},
"securityDefinitions": {
@ -2875,6 +3008,8 @@ var SwaggerInfo = &swag.Spec{
Description: "Track, Manage, and Organize your Things.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {

View File

@ -60,6 +60,31 @@
}
}
},
"/v1/actions/set-primary-photos": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "Sets the first photo of each item as the primary photo",
"produces": [
"application/json"
],
"tags": [
"Actions"
],
"summary": "Set Primary Photos",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/v1.ActionAmountResult"
}
}
}
}
},
"/v1/actions/zero-item-time-fields": {
"post": {
"security": [
@ -118,6 +143,25 @@
}
}
},
"/v1/currency": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Base"
],
"summary": "Currency",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/currencies.Currency"
}
}
}
}
},
"/v1/groups": {
"get": {
"security": [
@ -378,6 +422,16 @@
"description": "location Ids",
"name": "locations",
"in": "query"
},
{
"type": "array",
"items": {
"type": "string"
},
"collectionFormat": "multi",
"description": "parent Ids",
"name": "parentIds",
"in": "query"
}
],
"responses": {
@ -723,7 +777,7 @@
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/mid.ErrorResponse"
"$ref": "#/definitions/validate.ErrorResponse"
}
}
}
@ -956,6 +1010,42 @@
}
}
},
"/v1/items/{id}/path": {
"get": {
"security": [
{
"Bearer": []
}
],
"produces": [
"application/json"
],
"tags": [
"Items"
],
"summary": "Get the full path of an item",
"parameters": [
{
"type": "string",
"description": "Item ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemPath"
}
}
}
}
}
},
"/v1/labels": {
"get": {
"security": [
@ -1542,7 +1632,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/v1.ApiSummary"
"$ref": "#/definitions/v1.APISummary"
}
}
}
@ -1613,6 +1703,12 @@
"schema": {
"$ref": "#/definitions/v1.LoginForm"
}
},
{
"type": "string",
"description": "auth provider",
"name": "provider",
"in": "query"
}
],
"responses": {
@ -1791,17 +1887,20 @@
}
},
"definitions": {
"mid.ErrorResponse": {
"currencies.Currency": {
"type": "object",
"properties": {
"error": {
"code": {
"type": "string"
},
"fields": {
"type": "object",
"additionalProperties": {
"type": "string"
}
"local": {
"type": "string"
},
"name": {
"type": "string"
},
"symbol": {
"type": "string"
}
}
},
@ -1885,6 +1984,9 @@
"id": {
"type": "string"
},
"primary": {
"type": "boolean"
},
"type": {
"type": "string"
},
@ -1896,6 +1998,9 @@
"repo.ItemAttachmentUpdate": {
"type": "object",
"properties": {
"primary": {
"type": "boolean"
},
"title": {
"type": "string"
},
@ -1974,12 +2079,6 @@
"$ref": "#/definitions/repo.ItemAttachment"
}
},
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemSummary"
}
},
"createdAt": {
"type": "string"
},
@ -1995,6 +2094,9 @@
"id": {
"type": "string"
},
"imageId": {
"type": "string"
},
"insured": {
"type": "boolean"
},
@ -2010,9 +2112,13 @@
},
"location": {
"description": "Edges",
"allOf": [
{
"$ref": "#/definitions/repo.LocationSummary"
}
],
"x-nullable": true,
"x-omitempty": true,
"$ref": "#/definitions/repo.LocationSummary"
"x-omitempty": true
},
"manufacturer": {
"type": "string"
@ -2028,9 +2134,13 @@
"type": "string"
},
"parent": {
"allOf": [
{
"$ref": "#/definitions/repo.ItemSummary"
}
],
"x-nullable": true,
"x-omitempty": true,
"$ref": "#/definitions/repo.ItemSummary"
"x-omitempty": true
},
"purchaseFrom": {
"type": "string"
@ -2087,6 +2197,20 @@
}
}
},
"repo.ItemPath": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"type": {
"$ref": "#/definitions/repo.ItemType"
}
}
},
"repo.ItemSummary": {
"type": "object",
"properties": {
@ -2102,6 +2226,9 @@
"id": {
"type": "string"
},
"imageId": {
"type": "string"
},
"insured": {
"type": "boolean"
},
@ -2113,9 +2240,13 @@
},
"location": {
"description": "Edges",
"allOf": [
{
"$ref": "#/definitions/repo.LocationSummary"
}
],
"x-nullable": true,
"x-omitempty": true,
"$ref": "#/definitions/repo.LocationSummary"
"x-omitempty": true
},
"name": {
"type": "string"
@ -2132,6 +2263,17 @@
}
}
},
"repo.ItemType": {
"type": "string",
"enum": [
"location",
"item"
],
"x-enum-varnames": [
"ItemTypeLocation",
"ItemTypeItem"
]
},
"repo.ItemUpdate": {
"type": "object",
"properties": {
@ -2224,7 +2366,6 @@
"type": "string"
},
"warrantyExpires": {
"description": "Sold",
"type": "string"
}
}
@ -2261,12 +2402,6 @@
"id": {
"type": "string"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemSummary"
}
},
"name": {
"type": "string"
},
@ -2328,12 +2463,6 @@
"id": {
"type": "string"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/repo.ItemSummary"
}
},
"name": {
"type": "string"
},
@ -2410,7 +2539,6 @@
"type": "object",
"properties": {
"completedDate": {
"description": "Sold",
"type": "string"
},
"cost": {
@ -2427,7 +2555,6 @@
"type": "string"
},
"scheduledDate": {
"description": "Sold",
"type": "string"
}
}
@ -2439,7 +2566,6 @@
],
"properties": {
"completedDate": {
"description": "Sold",
"type": "string"
},
"cost": {
@ -2453,7 +2579,6 @@
"type": "string"
},
"scheduledDate": {
"description": "Sold",
"type": "string"
}
}
@ -2462,7 +2587,6 @@
"type": "object",
"properties": {
"completedDate": {
"description": "Sold",
"type": "string"
},
"cost": {
@ -2476,7 +2600,6 @@
"type": "string"
},
"scheduledDate": {
"description": "Sold",
"type": "string"
}
}
@ -2712,15 +2835,7 @@
}
}
},
"v1.ActionAmountResult": {
"type": "object",
"properties": {
"completed": {
"type": "integer"
}
}
},
"v1.ApiSummary": {
"v1.APISummary": {
"type": "object",
"properties": {
"allowRegistration": {
@ -2749,6 +2864,14 @@
}
}
},
"v1.ActionAmountResult": {
"type": "object",
"properties": {
"completed": {
"type": "integer"
}
}
},
"v1.Build": {
"type": "object",
"properties": {
@ -2845,6 +2968,17 @@
"properties": {
"item": {}
}
},
"validate.ErrorResponse": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"fields": {
"type": "string"
}
}
}
},
"securityDefinitions": {

View File

@ -1,13 +1,15 @@
basePath: /api
definitions:
mid.ErrorResponse:
currencies.Currency:
properties:
error:
code:
type: string
local:
type: string
name:
type: string
symbol:
type: string
fields:
additionalProperties:
type: string
type: object
type: object
repo.DocumentOut:
properties:
@ -61,6 +63,8 @@ definitions:
$ref: '#/definitions/repo.DocumentOut'
id:
type: string
primary:
type: boolean
type:
type: string
updatedAt:
@ -68,6 +72,8 @@ definitions:
type: object
repo.ItemAttachmentUpdate:
properties:
primary:
type: boolean
title:
type: string
type:
@ -121,10 +127,6 @@ definitions:
items:
$ref: '#/definitions/repo.ItemAttachment'
type: array
children:
items:
$ref: '#/definitions/repo.ItemSummary'
type: array
createdAt:
type: string
description:
@ -135,6 +137,8 @@ definitions:
type: array
id:
type: string
imageId:
type: string
insured:
type: boolean
labels:
@ -145,7 +149,8 @@ definitions:
description: Warranty
type: boolean
location:
$ref: '#/definitions/repo.LocationSummary'
allOf:
- $ref: '#/definitions/repo.LocationSummary'
description: Edges
x-nullable: true
x-omitempty: true
@ -159,7 +164,8 @@ definitions:
description: Extras
type: string
parent:
$ref: '#/definitions/repo.ItemSummary'
allOf:
- $ref: '#/definitions/repo.ItemSummary'
x-nullable: true
x-omitempty: true
purchaseFrom:
@ -200,6 +206,15 @@ definitions:
x-nullable: true
x-omitempty: true
type: object
repo.ItemPath:
properties:
id:
type: string
name:
type: string
type:
$ref: '#/definitions/repo.ItemType'
type: object
repo.ItemSummary:
properties:
archived:
@ -210,6 +225,8 @@ definitions:
type: string
id:
type: string
imageId:
type: string
insured:
type: boolean
labels:
@ -217,7 +234,8 @@ definitions:
$ref: '#/definitions/repo.LabelSummary'
type: array
location:
$ref: '#/definitions/repo.LocationSummary'
allOf:
- $ref: '#/definitions/repo.LocationSummary'
description: Edges
x-nullable: true
x-omitempty: true
@ -231,6 +249,14 @@ definitions:
updatedAt:
type: string
type: object
repo.ItemType:
enum:
- location
- item
type: string
x-enum-varnames:
- ItemTypeLocation
- ItemTypeItem
repo.ItemUpdate:
properties:
archived:
@ -296,7 +322,6 @@ definitions:
warrantyDetails:
type: string
warrantyExpires:
description: Sold
type: string
type: object
repo.LabelCreate:
@ -321,10 +346,6 @@ definitions:
type: string
id:
type: string
items:
items:
$ref: '#/definitions/repo.ItemSummary'
type: array
name:
type: string
updatedAt:
@ -365,10 +386,6 @@ definitions:
type: string
id:
type: string
items:
items:
$ref: '#/definitions/repo.ItemSummary'
type: array
name:
type: string
parent:
@ -419,7 +436,6 @@ definitions:
repo.MaintenanceEntry:
properties:
completedDate:
description: Sold
type: string
cost:
example: "0"
@ -431,13 +447,11 @@ definitions:
name:
type: string
scheduledDate:
description: Sold
type: string
type: object
repo.MaintenanceEntryCreate:
properties:
completedDate:
description: Sold
type: string
cost:
example: "0"
@ -447,7 +461,6 @@ definitions:
name:
type: string
scheduledDate:
description: Sold
type: string
required:
- name
@ -455,7 +468,6 @@ definitions:
repo.MaintenanceEntryUpdate:
properties:
completedDate:
description: Sold
type: string
cost:
example: "0"
@ -465,7 +477,6 @@ definitions:
name:
type: string
scheduledDate:
description: Sold
type: string
type: object
repo.MaintenanceLog:
@ -620,12 +631,7 @@ definitions:
token:
type: string
type: object
v1.ActionAmountResult:
properties:
completed:
type: integer
type: object
v1.ApiSummary:
v1.APISummary:
properties:
allowRegistration:
type: boolean
@ -644,6 +650,11 @@ definitions:
type: string
type: array
type: object
v1.ActionAmountResult:
properties:
completed:
type: integer
type: object
v1.Build:
properties:
buildTime:
@ -707,6 +718,13 @@ definitions:
properties:
item: {}
type: object
validate.ErrorResponse:
properties:
error:
type: string
fields:
type: string
type: object
info:
contact:
name: Don't
@ -744,6 +762,21 @@ paths:
summary: Ensures Import Refs
tags:
- Actions
/v1/actions/set-primary-photos:
post:
description: Sets the first photo of each item as the primary photo
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/v1.ActionAmountResult'
security:
- Bearer: []
summary: Set Primary Photos
tags:
- Actions
/v1/actions/zero-item-time-fields:
post:
description: Resets all item date fields to the beginning of the day
@ -779,6 +812,18 @@ paths:
summary: Get Item by Asset ID
tags:
- Items
/v1/currency:
get:
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/currencies.Currency'
summary: Currency
tags:
- Base
/v1/groups:
get:
produces:
@ -932,6 +977,13 @@ paths:
type: string
name: locations
type: array
- collectionFormat: multi
description: parent Ids
in: query
items:
type: string
name: parentIds
type: array
produces:
- application/json
responses:
@ -1084,7 +1136,7 @@ paths:
"422":
description: Unprocessable Entity
schema:
$ref: '#/definitions/mid.ErrorResponse'
$ref: '#/definitions/validate.ErrorResponse'
security:
- Bearer: []
summary: Create Item Attachment
@ -1229,6 +1281,28 @@ paths:
summary: Update Maintenance Entry
tags:
- Maintenance
/v1/items/{id}/path:
get:
parameters:
- description: Item ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/repo.ItemPath'
type: array
security:
- Bearer: []
summary: Get the full path of an item
tags:
- Items
/v1/items/export:
get:
responses:
@ -1646,7 +1720,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/v1.ApiSummary'
$ref: '#/definitions/v1.APISummary'
summary: Application Info
tags:
- Base
@ -1689,6 +1763,10 @@ paths:
required: true
schema:
$ref: '#/definitions/v1.LoginForm'
- description: auth provider
in: query
name: provider
type: string
produces:
- application/json
responses:

View File

@ -1,76 +1,78 @@
module github.com/hay-kot/homebox/backend
go 1.20
go 1.22
toolchain go1.22.0
require (
ariga.io/atlas v0.10.1
entgo.io/ent v0.11.10
github.com/ardanlabs/conf/v3 v3.1.6
github.com/containrrr/shoutrrr v0.7.1
github.com/go-chi/chi/v5 v5.0.10
github.com/go-playground/validator/v10 v10.14.1
github.com/gocarina/gocsv v0.0.0-20230510095315-7f30c79fd20c
github.com/google/uuid v1.3.0
github.com/gorilla/schema v1.2.0
github.com/hay-kot/httpkit v0.0.3
github.com/mattn/go-sqlite3 v1.14.17
ariga.io/atlas v0.19.1
entgo.io/ent v0.12.5
github.com/ardanlabs/conf/v3 v3.1.7
github.com/containrrr/shoutrrr v0.8.0
github.com/go-chi/chi/v5 v5.0.12
github.com/go-playground/validator/v10 v10.18.0
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
github.com/google/uuid v1.6.0
github.com/gorilla/schema v1.2.1
github.com/hay-kot/httpkit v0.0.9
github.com/mattn/go-sqlite3 v1.14.22
github.com/olahol/melody v1.1.4
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.29.1
github.com/rs/zerolog v1.32.0
github.com/stretchr/testify v1.8.4
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/http-swagger/v2 v2.0.1
github.com/swaggo/swag v1.16.1
github.com/yeqown/go-qrcode/v2 v2.2.1
github.com/yeqown/go-qrcode/writer/standard v1.2.1
golang.org/x/crypto v0.11.0
modernc.org/sqlite v1.24.0
github.com/swaggo/http-swagger/v2 v2.0.2
github.com/swaggo/swag v1.16.3
github.com/yeqown/go-qrcode/v2 v2.2.2
github.com/yeqown/go-qrcode/writer/standard v1.2.2
golang.org/x/crypto v0.19.0
modernc.org/sqlite v1.29.2
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/hashicorp/hcl/v2 v2.15.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hashicorp/hcl/v2 v2.19.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/swaggo/files v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/yeqown/reedsolomon v1.0.0 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/tools v0.7.0 // indirect
github.com/zclconf/go-cty v1.14.1 // indirect
golang.org/x/image v0.14.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.41.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
// Package currencies provides a shared definition of currencies. This uses a global
// variable to hold the currencies.
package currencies
import (
"bytes"
_ "embed"
"encoding/json"
"io"
"slices"
"strings"
"sync"
)
//go:embed currencies.json
var defaults []byte
type CollectorFunc func() ([]Currency, error)
func CollectJSON(reader io.Reader) CollectorFunc {
return func() ([]Currency, error) {
var currencies []Currency
err := json.NewDecoder(reader).Decode(&currencies)
if err != nil {
return nil, err
}
return currencies, nil
}
}
func CollectDefaults() CollectorFunc {
return CollectJSON(bytes.NewReader(defaults))
}
func CollectionCurrencies(collectors ...CollectorFunc) ([]Currency, error) {
out := make([]Currency, 0, len(collectors))
for i := range collectors {
c, err := collectors[i]()
if err != nil {
return nil, err
}
out = append(out, c...)
}
return out, nil
}
type Currency struct {
Name string `json:"name"`
Code string `json:"code"`
Local string `json:"local"`
Symbol string `json:"symbol"`
}
type CurrencyRegistry struct {
mu sync.RWMutex
registry map[string]Currency
}
func NewCurrencyService(currencies []Currency) *CurrencyRegistry {
registry := make(map[string]Currency, len(currencies))
for i := range currencies {
registry[currencies[i].Code] = currencies[i]
}
return &CurrencyRegistry{
registry: registry,
}
}
func (cs *CurrencyRegistry) Slice() []Currency {
cs.mu.RLock()
defer cs.mu.RUnlock()
out := make([]Currency, 0, len(cs.registry))
for key := range cs.registry {
out = append(out, cs.registry[key])
}
slices.SortFunc(out, func(a, b Currency) int {
if a.Name < b.Name {
return -1
}
if a.Name > b.Name {
return 1
}
return 0
})
return out
}
func (cs *CurrencyRegistry) IsSupported(code string) bool {
upper := strings.ToUpper(code)
cs.mu.RLock()
defer cs.mu.RUnlock()
_, ok := cs.registry[upper]
return ok
}

View File

@ -0,0 +1,638 @@
[
{
"code": "USD",
"local": "United States",
"symbol": "$",
"name": "United States Dollar"
},
{
"code": "AED",
"local": "United Arab Emirates",
"symbol": "د.إ",
"name": "United Arab Emirates Dirham"
},
{
"code": "AFN",
"local": "Afghanistan",
"symbol": "؋",
"name": "Afghan Afghani"
},
{
"code": "ALL",
"local": "Albania",
"symbol": "L",
"name": "Albanian Lek"
},
{
"code": "AMD",
"local": "Armenia",
"symbol": "֏",
"name": "Armenian Dram"
},
{
"code": "ANG",
"local": "Netherlands Antilles",
"symbol": "ƒ",
"name": "Netherlands Antillean Guilder"
},
{
"code": "AOA",
"local": "Angola",
"symbol": "Kz",
"name": "Angolan Kwanza"
},
{
"code": "ARS",
"local": "Argentina",
"symbol": "$",
"name": "Argentine Peso"
},
{
"code": "AUD",
"local": "Australia",
"symbol": "A$",
"name": "Australian Dollar"
},
{
"code": "AWG",
"local": "Aruba",
"symbol": "ƒ",
"name": "Aruban Florin"
},
{
"code": "AZN",
"local": "Azerbaijan",
"symbol": "₼",
"name": "Azerbaijani Manat"
},
{
"code": "BAM",
"local": "Bosnia and Herzegovina",
"symbol": "KM",
"name": "Bosnia and Herzegovina Convertible Mark"
},
{
"code": "BBD",
"local": "Barbados",
"symbol": "Bds$",
"name": "Barbadian Dollar"
},
{
"code": "BDT",
"local": "Bangladesh",
"symbol": "৳",
"name": "Bangladeshi Taka"
},
{
"code": "BGN",
"local": "Bulgaria",
"symbol": "лв",
"name": "Bulgarian lev"
},
{
"code": "BHD",
"local": "Bahrain",
"symbol": "ب.د",
"name": "Bahraini Dinar"
},
{
"code": "BIF",
"local": "Burundi",
"symbol": "FBu",
"name": "Burundian Franc"
},
{
"code": "BMD",
"local": "Bermuda",
"symbol": "BD$",
"name": "Bermudian Dollar"
},
{
"code": "BND",
"local": "Brunei",
"symbol": "B$",
"name": "Brunei Dollar"
},
{
"code": "BOB",
"local": "Bolivia",
"symbol": "Bs.",
"name": "Bolivian Boliviano"
},
{
"code": "BRL",
"local": "Brazil",
"symbol": "R$",
"name": "Brazilian Real"
},
{
"code": "BSD",
"local": "Bahamas",
"symbol": "B$",
"name": "Bahamian Dollar"
},
{
"code": "BTN",
"local": "Bhutan",
"symbol": "Nu.",
"name": "Bhutanese Ngultrum"
},
{
"code": "BWP",
"local": "Botswana",
"symbol": "P",
"name": "Botswana Pula"
},
{
"code": "BYN",
"local": "Belarus",
"symbol": "Br",
"name": "Belarusian Ruble"
},
{
"code": "BZD",
"local": "Belize",
"symbol": "BZ$",
"name": "Belize Dollar"
},
{
"code": "CAD",
"local": "Canada",
"symbol": "C$",
"name": "Canadian Dollar"
},
{
"code": "CDF",
"local": "Democratic Republic of the Congo",
"symbol": "FC",
"name": "Congolese Franc"
},
{
"code": "CHF",
"local": "Switzerland",
"symbol": "CHF",
"name": "Swiss Franc"
},
{
"code": "CLP",
"local": "Chile",
"symbol": "CL$",
"name": "Chilean Peso"
},
{
"code": "CNY",
"local": "China",
"symbol": "¥",
"name": "Chinese Yuan"
},
{
"code": "COP",
"local": "Colombia",
"symbol": "COL$",
"name": "Colombian Peso"
},
{
"code": "CRC",
"local": "Costa Rica",
"symbol": "₡",
"name": "Costa Rican Colón"
},
{
"code": "CUP",
"local": "Cuba",
"symbol": "₱",
"name": "Cuban Peso"
},
{
"code": "CVE",
"local": "Cape Verde",
"symbol": "$",
"name": "Cape Verdean Escudo"
},
{
"code": "CZK",
"local": "Czech Republic",
"symbol": "Kč",
"name": "Czech Koruna"
},
{
"code": "DJF",
"local": "Djibouti",
"symbol": "Fdj",
"name": "Djiboutian Franc"
},
{
"code": "DKK",
"local": "Denmark",
"symbol": "kr",
"name": "Danish Krone"
},
{
"code": "DOP",
"local": "Dominican Republic",
"symbol": "RD$",
"name": "Dominican Peso"
},
{
"code": "DZD",
"local": "Algeria",
"symbol": "د.ج",
"name": "Algerian Dinar"
},
{
"code": "EGP",
"local": "Egypt",
"symbol": "£",
"name": "Egyptian Pound"
},
{
"code": "ERN",
"local": "Eritrea",
"symbol": "Nfk",
"name": "Eritrean Nakfa"
},
{
"code": "ETB",
"local": "Ethiopia",
"symbol": "Br",
"name": "Ethiopian Birr"
},
{
"code": "EUR",
"local": "Eurozone",
"symbol": "€",
"name": "Euro"
},
{
"code": "FJD",
"local": "Fiji",
"symbol": "FJ$",
"name": "Fijian Dollar"
},
{
"code": "FKP",
"local": "Falkland Islands",
"symbol": "£",
"name": "Falkland Islands Pound"
},
{
"code": "FOK",
"local": "Faroe Islands",
"symbol": "kr",
"name": "Faroese Króna"
},
{
"code": "GBP",
"local": "United Kingdom",
"symbol": "£",
"name": "British Pound Sterling"
},
{
"code": "GEL",
"local": "Georgia",
"symbol": "₾",
"name": "Georgian Lari"
},
{
"code": "GGP",
"local": "Guernsey",
"symbol": "£",
"name": "Guernsey Pound"
},
{
"code": "GHS",
"local": "Ghana",
"symbol": "GH₵",
"name": "Ghanaian Cedi"
},
{
"code": "GIP",
"local": "Gibraltar",
"symbol": "£",
"name": "Gibraltar Pound"
},
{
"code": "GMD",
"local": "Gambia",
"symbol": "D",
"name": "Gambian Dalasi"
},
{
"code": "GNF",
"local": "Guinea",
"symbol": "FG",
"name": "Guinean Franc"
},
{
"code": "GTQ",
"local": "Guatemala",
"symbol": "Q",
"name": "Guatemalan Quetzal"
},
{
"code": "GYD",
"local": "Guyana",
"symbol": "GY$",
"name": "Guyanese Dollar"
},
{
"code": "HKD",
"local": "Hong Kong",
"symbol": "HK$",
"name": "Hong Kong Dollar"
},
{
"code": "HNL",
"local": "Honduras",
"symbol": "L",
"name": "Honduran Lempira"
},
{
"code": "HRK",
"local": "Croatia",
"symbol": "kn",
"name": "Croatian Kuna"
},
{
"code": "HTG",
"local": "Haiti",
"symbol": "G",
"name": "Haitian Gourde"
},
{
"code": "HUF",
"local": "Hungary",
"symbol": "Ft",
"name": "Hungarian Forint"
},
{
"code": "IDR",
"local": "Indonesia",
"symbol": "Rp",
"name": "Indonesian Rupiah"
},
{
"code": "ILS",
"local": "Israel",
"symbol": "₪",
"name": "Israeli New Shekel"
},
{
"code": "IMP",
"local": "Isle of Man",
"symbol": "£",
"name": "Manx Pound"
},
{
"code": "INR",
"local": "India",
"symbol": "₹",
"name": "Indian Rupee"
},
{
"code": "IQD",
"local": "Iraq",
"symbol": "ع.د",
"name": "Iraqi Dinar"
},
{
"code": "IRR",
"local": "Iran",
"symbol": "﷼",
"name": "Iranian Rial"
},
{
"code": "ISK",
"local": "Iceland",
"symbol": "kr",
"name": "Icelandic Króna"
},
{
"code": "JEP",
"local": "Jersey",
"symbol": "£",
"name": "Jersey Pound"
},
{
"code": "JMD",
"local": "Jamaica",
"symbol": "J$",
"name": "Jamaican Dollar"
},
{
"code": "JOD",
"local": "Jordan",
"symbol": "د.ا",
"name": "Jordanian Dinar"
},
{
"code": "JPY",
"local": "Japan",
"symbol": "¥",
"name": "Japanese Yen"
},
{
"code": "KES",
"local": "Kenya",
"symbol": "KSh",
"name": "Kenyan Shilling"
},
{
"code": "KGS",
"local": "Kyrgyzstan",
"symbol": "с",
"name": "Kyrgyzstani Som"
},
{
"code": "KHR",
"local": "Cambodia",
"symbol": "៛",
"name": "Cambodian Riel"
},
{
"code": "KID",
"local": "Kiribati",
"symbol": "$",
"name": "Kiribati Dollar"
},
{
"code": "KMF",
"local": "Comoros",
"symbol": "CF",
"name": "Comorian Franc"
},
{
"code": "KRW",
"local": "South Korea",
"symbol": "₩",
"name": "South Korean Won"
},
{
"code": "KWD",
"local": "Kuwait",
"symbol": "د.ك",
"name": "Kuwaiti Dinar"
},
{
"code": "KYD",
"local": "Cayman Islands",
"symbol": "CI$",
"name": "Cayman Islands Dollar"
},
{
"code": "KZT",
"local": "Kazakhstan",
"symbol": "₸",
"name": "Kazakhstani Tenge"
},
{
"code": "LAK",
"local": "Laos",
"symbol": "₭",
"name": "Lao Kip"
},
{
"code": "LBP",
"local": "Lebanon",
"symbol": "ل.ل",
"name": "Lebanese Pound"
},
{
"code": "LKR",
"local": "Sri Lanka",
"symbol": "₨",
"name": "Sri Lankan Rupee"
},
{
"code": "LRD",
"local": "Liberia",
"symbol": "L$",
"name": "Liberian Dollar"
},
{
"code": "LSL",
"local": "Lesotho",
"symbol": "M",
"name": "Lesotho Loti"
},
{
"code": "LYD",
"local": "Libya",
"symbol": "ل.د",
"name": "Libyan Dinar"
},
{
"code": "MAD",
"local": "Morocco",
"symbol": "د.م.",
"name": "Moroccan Dirham"
},
{
"code": "MDL",
"local": "Moldova",
"symbol": "lei",
"name": "Moldovan Leu"
},
{
"code": "MGA",
"local": "Madagascar",
"symbol": "Ar",
"name": "Malagasy Ariary"
},
{
"code": "MKD",
"local": "North Macedonia",
"symbol": "ден",
"name": "Macedonian Denar"
},
{
"code": "MMK",
"local": "Myanmar",
"symbol": "K",
"name": "Myanmar Kyat"
},
{
"code": "MNT",
"local": "Mongolia",
"symbol": "₮",
"name": "Mongolian Tugrik"
},
{
"code": "MOP",
"local": "Macau",
"symbol": "MOP$",
"name": "Macanese Pataca"
},
{
"code": "MRU",
"local": "Mauritania",
"symbol": "UM",
"name": "Mauritanian Ouguiya"
},
{
"code": "MUR",
"local": "Mauritius",
"symbol": "₨",
"name": "Mauritian Rupee"
},
{
"code": "MVR",
"local": "Maldives",
"symbol": "Rf",
"name": "Maldivian Rufiyaa"
},
{
"code": "MWK",
"local": "Malawi",
"symbol": "MK",
"name": "Malawian Kwacha"
},
{
"code": "MXN",
"local": "Mexico",
"symbol": "Mex$",
"name": "Mexican Peso"
},
{
"code": "MYR",
"local": "Malaysia",
"symbol": "RM",
"name": "Malaysian Ringgit"
},
{
"code": "MZN",
"local": "Mozambique",
"symbol": "MT",
"name": "Mozambican Metical"
},
{
"code": "NAD",
"local": "Namibia",
"symbol": "N$",
"name": "Namibian Dollar"
},
{
"code": "NGN",
"local": "Nigeria",
"symbol": "₦",
"name": "Nigerian Naira"
},
{
"code": "NIO",
"local": "Nicaragua",
"symbol": "C$",
"name": "Nicaraguan Córdoba"
},
{
"code": "NOK",
"local": "Norway",
"symbol": "kr",
"name": "Norwegian Krone"
},
{
"code": "UAH",
"local": "Ukraine",
"symbol": "₴",
"name": "Ukrainian Hryvnia"
}
]

View File

@ -1,6 +1,8 @@
// Package services provides the core business logic for the application.
package services
import (
"github.com/hay-kot/homebox/backend/internal/core/currencies"
"github.com/hay-kot/homebox/backend/internal/data/repo"
)
@ -9,12 +11,14 @@ type AllServices struct {
Group *GroupService
Items *ItemService
BackgroundService *BackgroundService
Currencies *currencies.CurrencyRegistry
}
type OptionsFunc func(*options)
type options struct {
autoIncrementAssetID bool
currencies []currencies.Currency
}
func WithAutoIncrementAssetID(v bool) func(*options) {
@ -23,13 +27,27 @@ func WithAutoIncrementAssetID(v bool) func(*options) {
}
}
func WithCurrencies(v []currencies.Currency) func(*options) {
return func(o *options) {
o.currencies = v
}
}
func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
if repos == nil {
panic("repos cannot be nil")
}
defaultCurrencies, err := currencies.CollectionCurrencies(
currencies.CollectDefaults(),
)
if err != nil {
panic("failed to collect default currencies")
}
options := &options{
autoIncrementAssetID: true,
currencies: defaultCurrencies,
}
for _, opt := range opts {
@ -44,5 +62,6 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
autoIncrementAssetID: options.autoIncrementAssetID,
},
BackgroundService: &BackgroundService{repos},
Currencies: currencies.NewCurrencyService(options.currencies),
}
}

View File

@ -6,6 +6,8 @@ import (
"os"
"testing"
"github.com/hay-kot/homebox/backend/internal/core/currencies"
"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/internal/data/repo"
"github.com/hay-kot/homebox/backend/pkgs/faker"
@ -13,7 +15,8 @@ import (
)
var (
fk = faker.NewFaker()
fk = faker.NewFaker()
tbus = eventbus.New()
tCtx = Context{}
tClient *ent.Client
@ -58,9 +61,14 @@ func TestMain(m *testing.M) {
}
tClient = client
tRepos = repo.New(tClient, os.TempDir()+"/homebox")
tSvc = New(tRepos)
defer client.Close()
tRepos = repo.New(tClient, tbus, os.TempDir()+"/homebox")
defaults, _ := currencies.CollectionCurrencies(
currencies.CollectDefaults(),
)
tSvc = New(tRepos, WithCurrencies(defaults))
defer func() { _ = client.Close() }()
bootstrap()
tCtx = Context{

View File

@ -0,0 +1,91 @@
// Package eventbus provides an interface for event bus.
package eventbus
import (
"context"
"sync"
"github.com/google/uuid"
)
type Event string
const (
EventLabelMutation Event = "label.mutation"
EventLocationMutation Event = "location.mutation"
EventItemMutation Event = "item.mutation"
)
type GroupMutationEvent struct {
GID uuid.UUID
}
type eventData struct {
event Event
data any
}
type EventBus struct {
started bool
ch chan eventData
mu sync.RWMutex
subscribers map[Event][]func(any)
}
func New() *EventBus {
return &EventBus{
ch: make(chan eventData, 100),
subscribers: map[Event][]func(any){
EventLabelMutation: {},
EventLocationMutation: {},
EventItemMutation: {},
},
}
}
func (e *EventBus) Run(ctx context.Context) error {
if e.started {
panic("event bus already started")
}
e.started = true
for {
select {
case <-ctx.Done():
return nil
case event := <-e.ch:
e.mu.RLock()
arr, ok := e.subscribers[event.event]
e.mu.RUnlock()
if !ok {
continue
}
for _, fn := range arr {
fn(event.data)
}
}
}
}
func (e *EventBus) Publish(event Event, data any) {
e.ch <- eventData{
event: event,
data: data,
}
}
func (e *EventBus) Subscribe(event Event, fn func(any)) {
e.mu.Lock()
defer e.mu.Unlock()
arr, ok := e.subscribers[event]
if !ok {
panic("event not found")
}
e.subscribers[event] = append(arr, fn)
}

View File

@ -1,3 +1,4 @@
// Package reporting provides a way to import CSV files into the database.
package reporting
import (

View File

@ -84,7 +84,7 @@ func (csf LocationString) String() string {
return strings.Join(csf, " / ")
}
func fromPathSlice(s []repo.LocationPath) LocationString {
func fromPathSlice(s []repo.ItemPath) LocationString {
v := make(LocationString, len(s))
for i := range s {

View File

@ -152,7 +152,7 @@ func (s *IOSheet) Read(data io.Reader) error {
return nil
}
// Write writes the sheet to a writer.
// ReadItems writes the sheet to a writer.
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
s.Rows = make([]ExportTSVRow, len(items))
@ -162,9 +162,9 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
item := items[i]
// TODO: Support fetching nested locations
locId := item.Location.ID
locID := item.Location.ID
locPaths, err := repos.Locations.PathForLoc(context.Background(), GID, locId)
locPaths, err := repos.Locations.PathForLoc(context.Background(), GID, locID)
if err != nil {
log.Error().Err(err).Msg("could not get location path")
return err
@ -252,7 +252,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
return nil
}
// Writes the current sheet to a writer in TSV format.
// TSV writes the current sheet to a writer in TSV format.
func (s *IOSheet) TSV() ([][]string, error) {
memcsv := make([][]string, len(s.Rows)+1)

View File

@ -9,6 +9,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
@ -103,9 +104,9 @@ func TestSheet_Read(t *testing.T) {
switch {
case tt.wantErr:
assert.Error(t, err)
require.Error(t, err)
default:
assert.NoError(t, err)
require.NoError(t, err)
assert.ElementsMatch(t, tt.want, sheet.Rows)
}
})

View File

@ -32,7 +32,7 @@ func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut,
return repo.ItemOut{}, err
}
item.AssetID = repo.AssetID(highest + 1)
item.AssetID = highest + 1
}
return svc.repo.Items.Create(ctx, ctx.GID, item)
@ -53,7 +53,7 @@ func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int,
for _, item := range items {
highest++
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, repo.AssetID(highest))
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, highest)
if err != nil {
return 0, err
}

View File

@ -12,8 +12,8 @@ import (
"github.com/rs/zerolog/log"
)
func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentId uuid.UUID) (*ent.Document, error) {
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentID uuid.UUID) (*ent.Document, error) {
attachment, err := svc.repo.Attachments.Get(ctx, attachmentID)
if err != nil {
return nil, err
}
@ -21,9 +21,9 @@ func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentId uuid.UU
return attachment.Edges.Document, nil
}
func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
func (svc *ItemService) AttachmentUpdate(ctx Context, itemID uuid.UUID, data *repo.ItemAttachmentUpdate) (repo.ItemOut, error) {
// Update Attachment
attachment, err := svc.repo.Attachments.Update(ctx, data.ID, attachment.Type(data.Type))
attachment, err := svc.repo.Attachments.Update(ctx, data.ID, data)
if err != nil {
return repo.ItemOut{}, err
}
@ -35,15 +35,15 @@ func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *re
return repo.ItemOut{}, err
}
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
}
// AttachmentAdd adds an attachment to an item by creating an entry in the Documents table and linking it to the Attachment
// Table and Items table. The file provided via the reader is stored on the file system based on the provided
// relative path during construction of the service.
func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
func (svc *ItemService) AttachmentAdd(ctx Context, itemID uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
// Get the Item
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
if err != nil {
return repo.ItemOut{}, err
}
@ -56,29 +56,29 @@ func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename st
}
// Create the attachment
_, err = svc.repo.Attachments.Create(ctx, itemId, doc.ID, attachmentType)
_, err = svc.repo.Attachments.Create(ctx, itemID, doc.ID, attachmentType)
if err != nil {
log.Err(err).Msg("failed to create attachment")
return repo.ItemOut{}, err
}
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
}
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemId, attachmentId uuid.UUID) error {
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemID, attachmentID uuid.UUID) error {
// Get the Item
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemId)
_, err := svc.repo.Items.GetOneByGroup(ctx, gid, itemID)
if err != nil {
return err
}
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
attachment, err := svc.repo.Attachments.Get(ctx, attachmentID)
if err != nil {
return err
}
// Delete the attachment
err = svc.repo.Attachments.Delete(ctx, attachmentId)
err = svc.repo.Attachments.Delete(ctx, attachmentID)
if err != nil {
return err
}

View File

@ -9,6 +9,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestItemService_AddAttachment(t *testing.T) {
@ -23,7 +24,7 @@ func TestItemService_AddAttachment(t *testing.T) {
Description: "test",
Name: "test",
})
assert.NoError(t, err)
require.NoError(t, err)
assert.NotNil(t, loc)
itmC := repo.ItemCreate{
@ -33,11 +34,11 @@ func TestItemService_AddAttachment(t *testing.T) {
}
itm, err := svc.repo.Items.Create(context.Background(), tGroup.ID, itmC)
assert.NoError(t, err)
require.NoError(t, err)
assert.NotNil(t, itm)
t.Cleanup(func() {
err := svc.repo.Items.Delete(context.Background(), itm.ID)
assert.NoError(t, err)
require.NoError(t, err)
})
contents := fk.Str(1000)
@ -45,7 +46,7 @@ func TestItemService_AddAttachment(t *testing.T) {
// Setup
afterAttachment, err := svc.AttachmentAdd(tCtx, itm.ID, "testfile.txt", "attachment", reader)
assert.NoError(t, err)
require.NoError(t, err)
assert.NotNil(t, afterAttachment)
// Check that the file exists
@ -56,6 +57,6 @@ func TestItemService_AddAttachment(t *testing.T) {
// Check that the file contents are correct
bts, err := os.ReadFile(storedPath)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, contents, string(bts))
}

View File

@ -16,7 +16,7 @@ var (
oneWeek = time.Hour * 24 * 7
ErrorInvalidLogin = errors.New("invalid username or password")
ErrorInvalidToken = errors.New("invalid token")
ErrorTokenIdMismatch = errors.New("token id mismatch")
ErrorTokenIDMismatch = errors.New("token id mismatch")
)
type UserService struct {
@ -92,9 +92,11 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
if err != nil {
return repo.UserOut{}, err
}
log.Debug().Msg("user created")
// Create the default labels and locations for the group.
if creatingGroup {
log.Debug().Msg("creating default labels")
for _, label := range defaultLabels() {
_, err := svc.repos.Labels.Create(ctx, usr.GroupID, label)
if err != nil {
@ -102,6 +104,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
}
}
log.Debug().Msg("creating default locations")
for _, location := range defaultLocations() {
_, err := svc.repos.Locations.Create(ctx, usr.GroupID, location)
if err != nil {
@ -112,6 +115,7 @@ func (svc *UserService) RegisterUser(ctx context.Context, data UserRegistration)
// Decrement the invitation token if it was used.
if token.ID != uuid.Nil {
log.Debug().Msg("decrementing invitation token")
err = svc.repos.Groups.InvitationUpdate(ctx, token.ID, token.Uses-1)
if err != nil {
log.Err(err).Msg("Failed to update invitation token")
@ -134,13 +138,13 @@ func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data repo.
return repo.UserOut{}, err
}
return svc.repos.Users.GetOneId(ctx, ID)
return svc.repos.Users.GetOneID(ctx, ID)
}
// ============================================================================
// User Authentication
func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID, extendedSession bool) (UserAuthTokenDetail, error) {
func (svc *UserService) createSessionToken(ctx context.Context, userID uuid.UUID, extendedSession bool) (UserAuthTokenDetail, error) {
attachmentToken := hasher.GenerateToken()
expiresAt := time.Now().Add(oneWeek)
@ -149,7 +153,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID
}
attachmentData := repo.UserAuthTokenCreate{
UserID: userId,
UserID: userID,
TokenHash: attachmentToken.Hash,
ExpiresAt: expiresAt,
}
@ -161,7 +165,7 @@ func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID
userToken := hasher.GenerateToken()
data := repo.UserAuthTokenCreate{
UserID: userId,
UserID: userID,
TokenHash: userToken.Hash,
ExpiresAt: expiresAt,
}
@ -218,7 +222,7 @@ func (svc *UserService) DeleteSelf(ctx context.Context, ID uuid.UUID) error {
}
func (svc *UserService) ChangePassword(ctx Context, current string, new string) (ok bool) {
usr, err := svc.repos.Users.GetOneId(ctx, ctx.UID)
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UID)
if err != nil {
return false
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
@ -25,11 +26,14 @@ type Attachment struct {
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Type holds the value of the "type" field.
Type attachment.Type `json:"type,omitempty"`
// Primary holds the value of the "primary" field.
Primary bool `json:"primary,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the AttachmentQuery when eager-loading is set.
Edges AttachmentEdges `json:"edges"`
document_attachments *uuid.UUID
item_attachments *uuid.UUID
selectValues sql.SelectValues
}
// AttachmentEdges holds the relations/edges for other nodes in the graph.
@ -74,6 +78,8 @@ func (*Attachment) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case attachment.FieldPrimary:
values[i] = new(sql.NullBool)
case attachment.FieldType:
values[i] = new(sql.NullString)
case attachment.FieldCreatedAt, attachment.FieldUpdatedAt:
@ -85,7 +91,7 @@ func (*Attachment) scanValues(columns []string) ([]any, error) {
case attachment.ForeignKeys[1]: // item_attachments
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type Attachment", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -123,6 +129,12 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
} else if value.Valid {
a.Type = attachment.Type(value.String)
}
case attachment.FieldPrimary:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field primary", values[i])
} else if value.Valid {
a.Primary = value.Bool
}
case attachment.ForeignKeys[0]:
if value, ok := values[i].(*sql.NullScanner); !ok {
return fmt.Errorf("unexpected type %T for field document_attachments", values[i])
@ -137,11 +149,19 @@ func (a *Attachment) assignValues(columns []string, values []any) error {
a.item_attachments = new(uuid.UUID)
*a.item_attachments = *value.S.(*uuid.UUID)
}
default:
a.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the Attachment.
// This includes values selected through modifiers, order, etc.
func (a *Attachment) Value(name string) (ent.Value, error) {
return a.selectValues.Get(name)
}
// QueryItem queries the "item" edge of the Attachment entity.
func (a *Attachment) QueryItem() *ItemQuery {
return NewAttachmentClient(a.config).QueryItem(a)
@ -183,6 +203,9 @@ func (a *Attachment) String() string {
builder.WriteString(", ")
builder.WriteString("type=")
builder.WriteString(fmt.Sprintf("%v", a.Type))
builder.WriteString(", ")
builder.WriteString("primary=")
builder.WriteString(fmt.Sprintf("%v", a.Primary))
builder.WriteByte(')')
return builder.String()
}

View File

@ -6,6 +6,8 @@ import (
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
@ -20,6 +22,8 @@ const (
FieldUpdatedAt = "updated_at"
// FieldType holds the string denoting the type field in the database.
FieldType = "type"
// FieldPrimary holds the string denoting the primary field in the database.
FieldPrimary = "primary"
// EdgeItem holds the string denoting the item edge name in mutations.
EdgeItem = "item"
// EdgeDocument holds the string denoting the document edge name in mutations.
@ -48,6 +52,7 @@ var Columns = []string{
FieldCreatedAt,
FieldUpdatedAt,
FieldType,
FieldPrimary,
}
// ForeignKeys holds the SQL foreign-keys that are owned by the "attachments"
@ -79,6 +84,8 @@ var (
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultPrimary holds the default value on creation for the "primary" field.
DefaultPrimary bool
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
@ -111,3 +118,59 @@ func TypeValidator(_type Type) error {
return fmt.Errorf("attachment: invalid enum value for type field: %q", _type)
}
}
// OrderOption defines the ordering options for the Attachment queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByType orders the results by the type field.
func ByType(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldType, opts...).ToFunc()
}
// ByPrimary orders the results by the primary field.
func ByPrimary(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPrimary, opts...).ToFunc()
}
// ByItemField orders the results by item field.
func ByItemField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newItemStep(), sql.OrderByField(field, opts...))
}
}
// ByDocumentField orders the results by document field.
func ByDocumentField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newDocumentStep(), sql.OrderByField(field, opts...))
}
}
func newItemStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn),
)
}
func newDocumentStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn),
)
}

View File

@ -66,6 +66,11 @@ func UpdatedAt(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldUpdatedAt, v))
}
// Primary applies equality check predicate on the "primary" field. It's identical to PrimaryEQ.
func Primary(v bool) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldPrimary, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldCreatedAt, v))
@ -166,6 +171,16 @@ func TypeNotIn(vs ...Type) predicate.Attachment {
return predicate.Attachment(sql.FieldNotIn(FieldType, vs...))
}
// PrimaryEQ applies the EQ predicate on the "primary" field.
func PrimaryEQ(v bool) predicate.Attachment {
return predicate.Attachment(sql.FieldEQ(FieldPrimary, v))
}
// PrimaryNEQ applies the NEQ predicate on the "primary" field.
func PrimaryNEQ(v bool) predicate.Attachment {
return predicate.Attachment(sql.FieldNEQ(FieldPrimary, v))
}
// HasItem applies the HasEdge predicate on the "item" edge.
func HasItem() predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) {
@ -180,11 +195,7 @@ func HasItem() predicate.Attachment {
// HasItemWith applies the HasEdge predicate on the "item" edge with a given conditions (other predicates).
func HasItemWith(preds ...predicate.Item) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, ItemTable, ItemColumn),
)
step := newItemStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -207,11 +218,7 @@ func HasDocument() predicate.Attachment {
// HasDocumentWith applies the HasEdge predicate on the "document" edge with a given conditions (other predicates).
func HasDocumentWith(preds ...predicate.Document) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, DocumentTable, DocumentColumn),
)
step := newDocumentStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -222,32 +229,15 @@ func HasDocumentWith(preds ...predicate.Document) predicate.Attachment {
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.Attachment) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
return predicate.Attachment(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.Attachment) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
return predicate.Attachment(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.Attachment) predicate.Attachment {
return predicate.Attachment(func(s *sql.Selector) {
p(s.Not())
})
return predicate.Attachment(sql.NotPredicates(p))
}

View File

@ -65,6 +65,20 @@ func (ac *AttachmentCreate) SetNillableType(a *attachment.Type) *AttachmentCreat
return ac
}
// SetPrimary sets the "primary" field.
func (ac *AttachmentCreate) SetPrimary(b bool) *AttachmentCreate {
ac.mutation.SetPrimary(b)
return ac
}
// SetNillablePrimary sets the "primary" field if the given value is not nil.
func (ac *AttachmentCreate) SetNillablePrimary(b *bool) *AttachmentCreate {
if b != nil {
ac.SetPrimary(*b)
}
return ac
}
// SetID sets the "id" field.
func (ac *AttachmentCreate) SetID(u uuid.UUID) *AttachmentCreate {
ac.mutation.SetID(u)
@ -109,7 +123,7 @@ func (ac *AttachmentCreate) Mutation() *AttachmentMutation {
// Save creates the Attachment in the database.
func (ac *AttachmentCreate) Save(ctx context.Context) (*Attachment, error) {
ac.defaults()
return withHooks[*Attachment, AttachmentMutation](ctx, ac.sqlSave, ac.mutation, ac.hooks)
return withHooks(ctx, ac.sqlSave, ac.mutation, ac.hooks)
}
// SaveX calls Save and panics if Save returns an error.
@ -148,6 +162,10 @@ func (ac *AttachmentCreate) defaults() {
v := attachment.DefaultType
ac.mutation.SetType(v)
}
if _, ok := ac.mutation.Primary(); !ok {
v := attachment.DefaultPrimary
ac.mutation.SetPrimary(v)
}
if _, ok := ac.mutation.ID(); !ok {
v := attachment.DefaultID()
ac.mutation.SetID(v)
@ -170,6 +188,9 @@ func (ac *AttachmentCreate) check() error {
return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "Attachment.type": %w`, err)}
}
}
if _, ok := ac.mutation.Primary(); !ok {
return &ValidationError{Name: "primary", err: errors.New(`ent: missing required field "Attachment.primary"`)}
}
if _, ok := ac.mutation.ItemID(); !ok {
return &ValidationError{Name: "item", err: errors.New(`ent: missing required edge "Attachment.item"`)}
}
@ -223,6 +244,10 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
_spec.SetField(attachment.FieldType, field.TypeEnum, value)
_node.Type = value
}
if value, ok := ac.mutation.Primary(); ok {
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value)
_node.Primary = value
}
if nodes := ac.mutation.ItemIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
@ -263,11 +288,15 @@ func (ac *AttachmentCreate) createSpec() (*Attachment, *sqlgraph.CreateSpec) {
// AttachmentCreateBulk is the builder for creating many Attachment entities in bulk.
type AttachmentCreateBulk struct {
config
err error
builders []*AttachmentCreate
}
// Save creates the Attachment entities in the database.
func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error) {
if acb.err != nil {
return nil, acb.err
}
specs := make([]*sqlgraph.CreateSpec, len(acb.builders))
nodes := make([]*Attachment, len(acb.builders))
mutators := make([]Mutator, len(acb.builders))
@ -284,8 +313,8 @@ func (acb *AttachmentCreateBulk) Save(ctx context.Context) ([]*Attachment, error
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, acb.builders[i+1].mutation)
} else {

View File

@ -27,7 +27,7 @@ func (ad *AttachmentDelete) Where(ps ...predicate.Attachment) *AttachmentDelete
// Exec executes the deletion query and returns how many vertices were deleted.
func (ad *AttachmentDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, AttachmentMutation](ctx, ad.sqlExec, ad.mutation, ad.hooks)
return withHooks(ctx, ad.sqlExec, ad.mutation, ad.hooks)
}
// ExecX is like Exec, but panics if an error occurs.

View File

@ -21,7 +21,7 @@ import (
type AttachmentQuery struct {
config
ctx *QueryContext
order []OrderFunc
order []attachment.OrderOption
inters []Interceptor
predicates []predicate.Attachment
withItem *ItemQuery
@ -58,7 +58,7 @@ func (aq *AttachmentQuery) Unique(unique bool) *AttachmentQuery {
}
// Order specifies how the records should be ordered.
func (aq *AttachmentQuery) Order(o ...OrderFunc) *AttachmentQuery {
func (aq *AttachmentQuery) Order(o ...attachment.OrderOption) *AttachmentQuery {
aq.order = append(aq.order, o...)
return aq
}
@ -296,7 +296,7 @@ func (aq *AttachmentQuery) Clone() *AttachmentQuery {
return &AttachmentQuery{
config: aq.config,
ctx: aq.ctx.Clone(),
order: append([]OrderFunc{}, aq.order...),
order: append([]attachment.OrderOption{}, aq.order...),
inters: append([]Interceptor{}, aq.inters...),
predicates: append([]predicate.Attachment{}, aq.predicates...),
withItem: aq.withItem.Clone(),

View File

@ -51,6 +51,20 @@ func (au *AttachmentUpdate) SetNillableType(a *attachment.Type) *AttachmentUpdat
return au
}
// SetPrimary sets the "primary" field.
func (au *AttachmentUpdate) SetPrimary(b bool) *AttachmentUpdate {
au.mutation.SetPrimary(b)
return au
}
// SetNillablePrimary sets the "primary" field if the given value is not nil.
func (au *AttachmentUpdate) SetNillablePrimary(b *bool) *AttachmentUpdate {
if b != nil {
au.SetPrimary(*b)
}
return au
}
// SetItemID sets the "item" edge to the Item entity by ID.
func (au *AttachmentUpdate) SetItemID(id uuid.UUID) *AttachmentUpdate {
au.mutation.SetItemID(id)
@ -93,7 +107,7 @@ func (au *AttachmentUpdate) ClearDocument() *AttachmentUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (au *AttachmentUpdate) Save(ctx context.Context) (int, error) {
au.defaults()
return withHooks[int, AttachmentMutation](ctx, au.sqlSave, au.mutation, au.hooks)
return withHooks(ctx, au.sqlSave, au.mutation, au.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -160,6 +174,9 @@ func (au *AttachmentUpdate) sqlSave(ctx context.Context) (n int, err error) {
if value, ok := au.mutation.GetType(); ok {
_spec.SetField(attachment.FieldType, field.TypeEnum, value)
}
if value, ok := au.mutation.Primary(); ok {
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value)
}
if au.mutation.ItemCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
@ -258,6 +275,20 @@ func (auo *AttachmentUpdateOne) SetNillableType(a *attachment.Type) *AttachmentU
return auo
}
// SetPrimary sets the "primary" field.
func (auo *AttachmentUpdateOne) SetPrimary(b bool) *AttachmentUpdateOne {
auo.mutation.SetPrimary(b)
return auo
}
// SetNillablePrimary sets the "primary" field if the given value is not nil.
func (auo *AttachmentUpdateOne) SetNillablePrimary(b *bool) *AttachmentUpdateOne {
if b != nil {
auo.SetPrimary(*b)
}
return auo
}
// SetItemID sets the "item" edge to the Item entity by ID.
func (auo *AttachmentUpdateOne) SetItemID(id uuid.UUID) *AttachmentUpdateOne {
auo.mutation.SetItemID(id)
@ -313,7 +344,7 @@ func (auo *AttachmentUpdateOne) Select(field string, fields ...string) *Attachme
// Save executes the query and returns the updated Attachment entity.
func (auo *AttachmentUpdateOne) Save(ctx context.Context) (*Attachment, error) {
auo.defaults()
return withHooks[*Attachment, AttachmentMutation](ctx, auo.sqlSave, auo.mutation, auo.hooks)
return withHooks(ctx, auo.sqlSave, auo.mutation, auo.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -397,6 +428,9 @@ func (auo *AttachmentUpdateOne) sqlSave(ctx context.Context) (_node *Attachment,
if value, ok := auo.mutation.GetType(); ok {
_spec.SetField(attachment.FieldType, field.TypeEnum, value)
}
if value, ok := auo.mutation.Primary(); ok {
_spec.SetField(attachment.FieldPrimary, field.TypeBool, value)
}
if auo.mutation.ItemCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,

View File

@ -6,6 +6,7 @@ import (
"fmt"
"strings"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
@ -23,6 +24,7 @@ type AuthRoles struct {
// The values are being populated by the AuthRolesQuery when eager-loading is set.
Edges AuthRolesEdges `json:"edges"`
auth_tokens_roles *uuid.UUID
selectValues sql.SelectValues
}
// AuthRolesEdges holds the relations/edges for other nodes in the graph.
@ -59,7 +61,7 @@ func (*AuthRoles) scanValues(columns []string) ([]any, error) {
case authroles.ForeignKeys[0]: // auth_tokens_roles
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type AuthRoles", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -92,11 +94,19 @@ func (ar *AuthRoles) assignValues(columns []string, values []any) error {
ar.auth_tokens_roles = new(uuid.UUID)
*ar.auth_tokens_roles = *value.S.(*uuid.UUID)
}
default:
ar.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the AuthRoles.
// This includes values selected through modifiers, order, etc.
func (ar *AuthRoles) Value(name string) (ent.Value, error) {
return ar.selectValues.Get(name)
}
// QueryToken queries the "token" edge of the AuthRoles entity.
func (ar *AuthRoles) QueryToken() *AuthTokensQuery {
return NewAuthRolesClient(ar.config).QueryToken(ar)

View File

@ -4,6 +4,9 @@ package authroles
import (
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
)
const (
@ -79,3 +82,30 @@ func RoleValidator(r Role) error {
return fmt.Errorf("authroles: invalid enum value for role field: %q", r)
}
}
// OrderOption defines the ordering options for the AuthRoles queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByRole orders the results by the role field.
func ByRole(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldRole, opts...).ToFunc()
}
// ByTokenField orders the results by token field.
func ByTokenField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newTokenStep(), sql.OrderByField(field, opts...))
}
}
func newTokenStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(TokenInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
)
}

View File

@ -87,11 +87,7 @@ func HasToken() predicate.AuthRoles {
// HasTokenWith applies the HasEdge predicate on the "token" edge with a given conditions (other predicates).
func HasTokenWith(preds ...predicate.AuthTokens) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(TokenInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
)
step := newTokenStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -102,32 +98,15 @@ func HasTokenWith(preds ...predicate.AuthTokens) predicate.AuthRoles {
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.AuthRoles) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
return predicate.AuthRoles(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.AuthRoles) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
return predicate.AuthRoles(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.AuthRoles) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
p(s.Not())
})
return predicate.AuthRoles(sql.NotPredicates(p))
}

View File

@ -62,7 +62,7 @@ func (arc *AuthRolesCreate) Mutation() *AuthRolesMutation {
// Save creates the AuthRoles in the database.
func (arc *AuthRolesCreate) Save(ctx context.Context) (*AuthRoles, error) {
arc.defaults()
return withHooks[*AuthRoles, AuthRolesMutation](ctx, arc.sqlSave, arc.mutation, arc.hooks)
return withHooks(ctx, arc.sqlSave, arc.mutation, arc.hooks)
}
// SaveX calls Save and panics if Save returns an error.
@ -158,11 +158,15 @@ func (arc *AuthRolesCreate) createSpec() (*AuthRoles, *sqlgraph.CreateSpec) {
// AuthRolesCreateBulk is the builder for creating many AuthRoles entities in bulk.
type AuthRolesCreateBulk struct {
config
err error
builders []*AuthRolesCreate
}
// Save creates the AuthRoles entities in the database.
func (arcb *AuthRolesCreateBulk) Save(ctx context.Context) ([]*AuthRoles, error) {
if arcb.err != nil {
return nil, arcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(arcb.builders))
nodes := make([]*AuthRoles, len(arcb.builders))
mutators := make([]Mutator, len(arcb.builders))
@ -179,8 +183,8 @@ func (arcb *AuthRolesCreateBulk) Save(ctx context.Context) ([]*AuthRoles, error)
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, arcb.builders[i+1].mutation)
} else {

View File

@ -27,7 +27,7 @@ func (ard *AuthRolesDelete) Where(ps ...predicate.AuthRoles) *AuthRolesDelete {
// Exec executes the deletion query and returns how many vertices were deleted.
func (ard *AuthRolesDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, AuthRolesMutation](ctx, ard.sqlExec, ard.mutation, ard.hooks)
return withHooks(ctx, ard.sqlExec, ard.mutation, ard.hooks)
}
// ExecX is like Exec, but panics if an error occurs.

View File

@ -20,7 +20,7 @@ import (
type AuthRolesQuery struct {
config
ctx *QueryContext
order []OrderFunc
order []authroles.OrderOption
inters []Interceptor
predicates []predicate.AuthRoles
withToken *AuthTokensQuery
@ -56,7 +56,7 @@ func (arq *AuthRolesQuery) Unique(unique bool) *AuthRolesQuery {
}
// Order specifies how the records should be ordered.
func (arq *AuthRolesQuery) Order(o ...OrderFunc) *AuthRolesQuery {
func (arq *AuthRolesQuery) Order(o ...authroles.OrderOption) *AuthRolesQuery {
arq.order = append(arq.order, o...)
return arq
}
@ -272,7 +272,7 @@ func (arq *AuthRolesQuery) Clone() *AuthRolesQuery {
return &AuthRolesQuery{
config: arq.config,
ctx: arq.ctx.Clone(),
order: append([]OrderFunc{}, arq.order...),
order: append([]authroles.OrderOption{}, arq.order...),
inters: append([]Interceptor{}, arq.inters...),
predicates: append([]predicate.AuthRoles{}, arq.predicates...),
withToken: arq.withToken.Clone(),

View File

@ -75,7 +75,7 @@ func (aru *AuthRolesUpdate) ClearToken() *AuthRolesUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (aru *AuthRolesUpdate) Save(ctx context.Context) (int, error) {
return withHooks[int, AuthRolesMutation](ctx, aru.sqlSave, aru.mutation, aru.hooks)
return withHooks(ctx, aru.sqlSave, aru.mutation, aru.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -233,7 +233,7 @@ func (aruo *AuthRolesUpdateOne) Select(field string, fields ...string) *AuthRole
// Save executes the query and returns the updated AuthRoles entity.
func (aruo *AuthRolesUpdateOne) Save(ctx context.Context) (*AuthRoles, error) {
return withHooks[*AuthRoles, AuthRolesMutation](ctx, aruo.sqlSave, aruo.mutation, aruo.hooks)
return withHooks(ctx, aruo.sqlSave, aruo.mutation, aruo.hooks)
}
// SaveX is like Save, but panics if an error occurs.

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
@ -31,6 +32,7 @@ type AuthTokens struct {
// The values are being populated by the AuthTokensQuery when eager-loading is set.
Edges AuthTokensEdges `json:"edges"`
user_auth_tokens *uuid.UUID
selectValues sql.SelectValues
}
// AuthTokensEdges holds the relations/edges for other nodes in the graph.
@ -84,7 +86,7 @@ func (*AuthTokens) scanValues(columns []string) ([]any, error) {
case authtokens.ForeignKeys[0]: // user_auth_tokens
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type AuthTokens", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -135,11 +137,19 @@ func (at *AuthTokens) assignValues(columns []string, values []any) error {
at.user_auth_tokens = new(uuid.UUID)
*at.user_auth_tokens = *value.S.(*uuid.UUID)
}
default:
at.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the AuthTokens.
// This includes values selected through modifiers, order, etc.
func (at *AuthTokens) Value(name string) (ent.Value, error) {
return at.selectValues.Get(name)
}
// QueryUser queries the "user" edge of the AuthTokens entity.
func (at *AuthTokens) QueryUser() *UserQuery {
return NewAuthTokensClient(at.config).QueryUser(at)

View File

@ -5,6 +5,8 @@ package authtokens
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
@ -85,3 +87,54 @@ var (
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
// OrderOption defines the ordering options for the AuthTokens queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByExpiresAt orders the results by the expires_at field.
func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldExpiresAt, opts...).ToFunc()
}
// ByUserField orders the results by user field.
func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
}
}
// ByRolesField orders the results by roles field.
func ByRolesField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newRolesStep(), sql.OrderByField(field, opts...))
}
}
func newUserStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
}
func newRolesStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(RolesInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
)
}

View File

@ -250,11 +250,7 @@ func HasUser() predicate.AuthTokens {
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
func HasUserWith(preds ...predicate.User) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
step := newUserStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -277,11 +273,7 @@ func HasRoles() predicate.AuthTokens {
// HasRolesWith applies the HasEdge predicate on the "roles" edge with a given conditions (other predicates).
func HasRolesWith(preds ...predicate.AuthRoles) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(RolesInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
)
step := newRolesStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -292,32 +284,15 @@ func HasRolesWith(preds ...predicate.AuthRoles) predicate.AuthTokens {
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
return predicate.AuthTokens(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
return predicate.AuthTokens(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
p(s.Not())
})
return predicate.AuthTokens(sql.NotPredicates(p))
}

View File

@ -131,7 +131,7 @@ func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation {
// Save creates the AuthTokens in the database.
func (atc *AuthTokensCreate) Save(ctx context.Context) (*AuthTokens, error) {
atc.defaults()
return withHooks[*AuthTokens, AuthTokensMutation](ctx, atc.sqlSave, atc.mutation, atc.hooks)
return withHooks(ctx, atc.sqlSave, atc.mutation, atc.hooks)
}
// SaveX calls Save and panics if Save returns an error.
@ -280,11 +280,15 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
// AuthTokensCreateBulk is the builder for creating many AuthTokens entities in bulk.
type AuthTokensCreateBulk struct {
config
err error
builders []*AuthTokensCreate
}
// Save creates the AuthTokens entities in the database.
func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, error) {
if atcb.err != nil {
return nil, atcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(atcb.builders))
nodes := make([]*AuthTokens, len(atcb.builders))
mutators := make([]Mutator, len(atcb.builders))
@ -301,8 +305,8 @@ func (atcb *AuthTokensCreateBulk) Save(ctx context.Context) ([]*AuthTokens, erro
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, atcb.builders[i+1].mutation)
} else {

View File

@ -27,7 +27,7 @@ func (atd *AuthTokensDelete) Where(ps ...predicate.AuthTokens) *AuthTokensDelete
// Exec executes the deletion query and returns how many vertices were deleted.
func (atd *AuthTokensDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, AuthTokensMutation](ctx, atd.sqlExec, atd.mutation, atd.hooks)
return withHooks(ctx, atd.sqlExec, atd.mutation, atd.hooks)
}
// ExecX is like Exec, but panics if an error occurs.

View File

@ -22,7 +22,7 @@ import (
type AuthTokensQuery struct {
config
ctx *QueryContext
order []OrderFunc
order []authtokens.OrderOption
inters []Interceptor
predicates []predicate.AuthTokens
withUser *UserQuery
@ -59,7 +59,7 @@ func (atq *AuthTokensQuery) Unique(unique bool) *AuthTokensQuery {
}
// Order specifies how the records should be ordered.
func (atq *AuthTokensQuery) Order(o ...OrderFunc) *AuthTokensQuery {
func (atq *AuthTokensQuery) Order(o ...authtokens.OrderOption) *AuthTokensQuery {
atq.order = append(atq.order, o...)
return atq
}
@ -297,7 +297,7 @@ func (atq *AuthTokensQuery) Clone() *AuthTokensQuery {
return &AuthTokensQuery{
config: atq.config,
ctx: atq.ctx.Clone(),
order: append([]OrderFunc{}, atq.order...),
order: append([]authtokens.OrderOption{}, atq.order...),
inters: append([]Interceptor{}, atq.inters...),
predicates: append([]predicate.AuthTokens{}, atq.predicates...),
withUser: atq.withUser.Clone(),
@ -494,7 +494,7 @@ func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery
}
query.withFKs = true
query.Where(predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.InValues(authtokens.RolesColumn, fks...))
s.Where(sql.InValues(s.C(authtokens.RolesColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -507,7 +507,7 @@ func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "auth_tokens_roles" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "auth_tokens_roles" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}

View File

@ -115,7 +115,7 @@ func (atu *AuthTokensUpdate) ClearRoles() *AuthTokensUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) {
atu.defaults()
return withHooks[int, AuthTokensMutation](ctx, atu.sqlSave, atu.mutation, atu.hooks)
return withHooks(ctx, atu.sqlSave, atu.mutation, atu.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -341,7 +341,7 @@ func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTok
// Save executes the query and returns the updated AuthTokens entity.
func (atuo *AuthTokensUpdateOne) Save(ctx context.Context) (*AuthTokens, error) {
atuo.defaults()
return withHooks[*AuthTokens, AuthTokensMutation](ctx, atuo.sqlSave, atuo.mutation, atuo.hooks)
return withHooks(ctx, atuo.sqlSave, atuo.mutation, atuo.hooks)
}
// SaveX is like Save, but panics if an error occurs.

View File

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"log"
"reflect"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
@ -65,9 +66,7 @@ type Client struct {
// NewClient creates a new client configured with the given options.
func NewClient(opts ...Option) *Client {
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
cfg.options(opts...)
client := &Client{config: cfg}
client := &Client{config: newConfig(opts...)}
client.init()
return client
}
@ -107,6 +106,13 @@ type (
Option func(*config)
)
// newConfig creates a new config for the client.
func newConfig(opts ...Option) config {
cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}}
cfg.options(opts...)
return cfg
}
// options applies the options on the config object.
func (c *config) options(opts ...Option) {
for _, opt := range opts {
@ -154,11 +160,14 @@ func Open(driverName, dataSourceName string, options ...Option) (*Client, error)
}
}
// ErrTxStarted is returned when trying to start a new transaction from a transactional client.
var ErrTxStarted = errors.New("ent: cannot start a transaction within a transaction")
// Tx returns a new transactional client. The provided context
// is used until the transaction is committed or rolled back.
func (c *Client) Tx(ctx context.Context) (*Tx, error) {
if _, ok := c.driver.(*txDriver); ok {
return nil, errors.New("ent: cannot start a transaction within a transaction")
return nil, ErrTxStarted
}
tx, err := newTx(ctx, c.driver)
if err != nil {
@ -330,6 +339,21 @@ func (c *AttachmentClient) CreateBulk(builders ...*AttachmentCreate) *Attachment
return &AttachmentCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *AttachmentClient) MapCreateBulk(slice any, setFunc func(*AttachmentCreate, int)) *AttachmentCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &AttachmentCreateBulk{err: fmt.Errorf("calling to AttachmentClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*AttachmentCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &AttachmentCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Attachment.
func (c *AttachmentClient) Update() *AttachmentUpdate {
mutation := newAttachmentMutation(c.config, OpUpdate)
@ -480,6 +504,21 @@ func (c *AuthRolesClient) CreateBulk(builders ...*AuthRolesCreate) *AuthRolesCre
return &AuthRolesCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *AuthRolesClient) MapCreateBulk(slice any, setFunc func(*AuthRolesCreate, int)) *AuthRolesCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &AuthRolesCreateBulk{err: fmt.Errorf("calling to AuthRolesClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*AuthRolesCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &AuthRolesCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for AuthRoles.
func (c *AuthRolesClient) Update() *AuthRolesUpdate {
mutation := newAuthRolesMutation(c.config, OpUpdate)
@ -614,6 +653,21 @@ func (c *AuthTokensClient) CreateBulk(builders ...*AuthTokensCreate) *AuthTokens
return &AuthTokensCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *AuthTokensClient) MapCreateBulk(slice any, setFunc func(*AuthTokensCreate, int)) *AuthTokensCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &AuthTokensCreateBulk{err: fmt.Errorf("calling to AuthTokensClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*AuthTokensCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &AuthTokensCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for AuthTokens.
func (c *AuthTokensClient) Update() *AuthTokensUpdate {
mutation := newAuthTokensMutation(c.config, OpUpdate)
@ -764,6 +818,21 @@ func (c *DocumentClient) CreateBulk(builders ...*DocumentCreate) *DocumentCreate
return &DocumentCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *DocumentClient) MapCreateBulk(slice any, setFunc func(*DocumentCreate, int)) *DocumentCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &DocumentCreateBulk{err: fmt.Errorf("calling to DocumentClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*DocumentCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &DocumentCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Document.
func (c *DocumentClient) Update() *DocumentUpdate {
mutation := newDocumentMutation(c.config, OpUpdate)
@ -914,6 +983,21 @@ func (c *GroupClient) CreateBulk(builders ...*GroupCreate) *GroupCreateBulk {
return &GroupCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *GroupClient) MapCreateBulk(slice any, setFunc func(*GroupCreate, int)) *GroupCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &GroupCreateBulk{err: fmt.Errorf("calling to GroupClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*GroupCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &GroupCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Group.
func (c *GroupClient) Update() *GroupUpdate {
mutation := newGroupMutation(c.config, OpUpdate)
@ -1144,6 +1228,21 @@ func (c *GroupInvitationTokenClient) CreateBulk(builders ...*GroupInvitationToke
return &GroupInvitationTokenCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *GroupInvitationTokenClient) MapCreateBulk(slice any, setFunc func(*GroupInvitationTokenCreate, int)) *GroupInvitationTokenCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &GroupInvitationTokenCreateBulk{err: fmt.Errorf("calling to GroupInvitationTokenClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*GroupInvitationTokenCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &GroupInvitationTokenCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for GroupInvitationToken.
func (c *GroupInvitationTokenClient) Update() *GroupInvitationTokenUpdate {
mutation := newGroupInvitationTokenMutation(c.config, OpUpdate)
@ -1278,6 +1377,21 @@ func (c *ItemClient) CreateBulk(builders ...*ItemCreate) *ItemCreateBulk {
return &ItemCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ItemClient) MapCreateBulk(slice any, setFunc func(*ItemCreate, int)) *ItemCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ItemCreateBulk{err: fmt.Errorf("calling to ItemClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ItemCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ItemCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Item.
func (c *ItemClient) Update() *ItemUpdate {
mutation := newItemMutation(c.config, OpUpdate)
@ -1524,6 +1638,21 @@ func (c *ItemFieldClient) CreateBulk(builders ...*ItemFieldCreate) *ItemFieldCre
return &ItemFieldCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ItemFieldClient) MapCreateBulk(slice any, setFunc func(*ItemFieldCreate, int)) *ItemFieldCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ItemFieldCreateBulk{err: fmt.Errorf("calling to ItemFieldClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ItemFieldCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ItemFieldCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for ItemField.
func (c *ItemFieldClient) Update() *ItemFieldUpdate {
mutation := newItemFieldMutation(c.config, OpUpdate)
@ -1658,6 +1787,21 @@ func (c *LabelClient) CreateBulk(builders ...*LabelCreate) *LabelCreateBulk {
return &LabelCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *LabelClient) MapCreateBulk(slice any, setFunc func(*LabelCreate, int)) *LabelCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &LabelCreateBulk{err: fmt.Errorf("calling to LabelClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*LabelCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &LabelCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Label.
func (c *LabelClient) Update() *LabelUpdate {
mutation := newLabelMutation(c.config, OpUpdate)
@ -1808,6 +1952,21 @@ func (c *LocationClient) CreateBulk(builders ...*LocationCreate) *LocationCreate
return &LocationCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *LocationClient) MapCreateBulk(slice any, setFunc func(*LocationCreate, int)) *LocationCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &LocationCreateBulk{err: fmt.Errorf("calling to LocationClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*LocationCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &LocationCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Location.
func (c *LocationClient) Update() *LocationUpdate {
mutation := newLocationMutation(c.config, OpUpdate)
@ -1990,6 +2149,21 @@ func (c *MaintenanceEntryClient) CreateBulk(builders ...*MaintenanceEntryCreate)
return &MaintenanceEntryCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *MaintenanceEntryClient) MapCreateBulk(slice any, setFunc func(*MaintenanceEntryCreate, int)) *MaintenanceEntryCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &MaintenanceEntryCreateBulk{err: fmt.Errorf("calling to MaintenanceEntryClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*MaintenanceEntryCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &MaintenanceEntryCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for MaintenanceEntry.
func (c *MaintenanceEntryClient) Update() *MaintenanceEntryUpdate {
mutation := newMaintenanceEntryMutation(c.config, OpUpdate)
@ -2124,6 +2298,21 @@ func (c *NotifierClient) CreateBulk(builders ...*NotifierCreate) *NotifierCreate
return &NotifierCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *NotifierClient) MapCreateBulk(slice any, setFunc func(*NotifierCreate, int)) *NotifierCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &NotifierCreateBulk{err: fmt.Errorf("calling to NotifierClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*NotifierCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &NotifierCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for Notifier.
func (c *NotifierClient) Update() *NotifierUpdate {
mutation := newNotifierMutation(c.config, OpUpdate)
@ -2274,6 +2463,21 @@ func (c *UserClient) CreateBulk(builders ...*UserCreate) *UserCreateBulk {
return &UserCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *UserClient) MapCreateBulk(slice any, setFunc func(*UserCreate, int)) *UserCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &UserCreateBulk{err: fmt.Errorf("calling to UserClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*UserCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &UserCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for User.
func (c *UserClient) Update() *UserUpdate {
mutation := newUserMutation(c.config, OpUpdate)

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
@ -30,6 +31,7 @@ type Document struct {
// The values are being populated by the DocumentQuery when eager-loading is set.
Edges DocumentEdges `json:"edges"`
group_documents *uuid.UUID
selectValues sql.SelectValues
}
// DocumentEdges holds the relations/edges for other nodes in the graph.
@ -79,7 +81,7 @@ func (*Document) scanValues(columns []string) ([]any, error) {
case document.ForeignKeys[0]: // group_documents
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type Document", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -130,11 +132,19 @@ func (d *Document) assignValues(columns []string, values []any) error {
d.group_documents = new(uuid.UUID)
*d.group_documents = *value.S.(*uuid.UUID)
}
default:
d.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the Document.
// This includes values selected through modifiers, order, etc.
func (d *Document) Value(name string) (ent.Value, error) {
return d.selectValues.Get(name)
}
// QueryGroup queries the "group" edge of the Document entity.
func (d *Document) QueryGroup() *GroupQuery {
return NewDocumentClient(d.config).QueryGroup(d)

View File

@ -5,6 +5,8 @@ package document
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
@ -87,3 +89,66 @@ var (
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
// OrderOption defines the ordering options for the Document queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByTitle orders the results by the title field.
func ByTitle(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldTitle, opts...).ToFunc()
}
// ByPath orders the results by the path field.
func ByPath(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPath, opts...).ToFunc()
}
// ByGroupField orders the results by group field.
func ByGroupField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newGroupStep(), sql.OrderByField(field, opts...))
}
}
// ByAttachmentsCount orders the results by attachments count.
func ByAttachmentsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newAttachmentsStep(), opts...)
}
}
// ByAttachments orders the results by attachments terms.
func ByAttachments(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newAttachmentsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newGroupStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
)
}
func newAttachmentsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(AttachmentsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
)
}

View File

@ -300,11 +300,7 @@ func HasGroup() predicate.Document {
// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates).
func HasGroupWith(preds ...predicate.Group) predicate.Document {
return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
)
step := newGroupStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -327,11 +323,7 @@ func HasAttachments() predicate.Document {
// HasAttachmentsWith applies the HasEdge predicate on the "attachments" edge with a given conditions (other predicates).
func HasAttachmentsWith(preds ...predicate.Attachment) predicate.Document {
return predicate.Document(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(AttachmentsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
)
step := newAttachmentsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -342,32 +334,15 @@ func HasAttachmentsWith(preds ...predicate.Attachment) predicate.Document {
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.Document) predicate.Document {
return predicate.Document(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
return predicate.Document(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.Document) predicate.Document {
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())
})
return predicate.Document(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.Document) predicate.Document {
return predicate.Document(func(s *sql.Selector) {
p(s.Not())
})
return predicate.Document(sql.NotPredicates(p))
}

View File

@ -111,7 +111,7 @@ func (dc *DocumentCreate) Mutation() *DocumentMutation {
// Save creates the Document in the database.
func (dc *DocumentCreate) Save(ctx context.Context) (*Document, error) {
dc.defaults()
return withHooks[*Document, DocumentMutation](ctx, dc.sqlSave, dc.mutation, dc.hooks)
return withHooks(ctx, dc.sqlSave, dc.mutation, dc.hooks)
}
// SaveX calls Save and panics if Save returns an error.
@ -269,11 +269,15 @@ func (dc *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
// DocumentCreateBulk is the builder for creating many Document entities in bulk.
type DocumentCreateBulk struct {
config
err error
builders []*DocumentCreate
}
// Save creates the Document entities in the database.
func (dcb *DocumentCreateBulk) Save(ctx context.Context) ([]*Document, error) {
if dcb.err != nil {
return nil, dcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(dcb.builders))
nodes := make([]*Document, len(dcb.builders))
mutators := make([]Mutator, len(dcb.builders))
@ -290,8 +294,8 @@ func (dcb *DocumentCreateBulk) Save(ctx context.Context) ([]*Document, error) {
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, dcb.builders[i+1].mutation)
} else {

View File

@ -27,7 +27,7 @@ func (dd *DocumentDelete) Where(ps ...predicate.Document) *DocumentDelete {
// Exec executes the deletion query and returns how many vertices were deleted.
func (dd *DocumentDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, DocumentMutation](ctx, dd.sqlExec, dd.mutation, dd.hooks)
return withHooks(ctx, dd.sqlExec, dd.mutation, dd.hooks)
}
// ExecX is like Exec, but panics if an error occurs.

View File

@ -22,7 +22,7 @@ import (
type DocumentQuery struct {
config
ctx *QueryContext
order []OrderFunc
order []document.OrderOption
inters []Interceptor
predicates []predicate.Document
withGroup *GroupQuery
@ -59,7 +59,7 @@ func (dq *DocumentQuery) Unique(unique bool) *DocumentQuery {
}
// Order specifies how the records should be ordered.
func (dq *DocumentQuery) Order(o ...OrderFunc) *DocumentQuery {
func (dq *DocumentQuery) Order(o ...document.OrderOption) *DocumentQuery {
dq.order = append(dq.order, o...)
return dq
}
@ -297,7 +297,7 @@ func (dq *DocumentQuery) Clone() *DocumentQuery {
return &DocumentQuery{
config: dq.config,
ctx: dq.ctx.Clone(),
order: append([]OrderFunc{}, dq.order...),
order: append([]document.OrderOption{}, dq.order...),
inters: append([]Interceptor{}, dq.inters...),
predicates: append([]predicate.Document{}, dq.predicates...),
withGroup: dq.withGroup.Clone(),
@ -498,7 +498,7 @@ func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQ
}
query.withFKs = true
query.Where(predicate.Attachment(func(s *sql.Selector) {
s.Where(sql.InValues(document.AttachmentsColumn, fks...))
s.Where(sql.InValues(s.C(document.AttachmentsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -511,7 +511,7 @@ func (dq *DocumentQuery) loadAttachments(ctx context.Context, query *AttachmentQ
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "document_attachments" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "document_attachments" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}

View File

@ -43,12 +43,28 @@ func (du *DocumentUpdate) SetTitle(s string) *DocumentUpdate {
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.
func (du *DocumentUpdate) SetPath(s string) *DocumentUpdate {
du.mutation.SetPath(s)
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.
func (du *DocumentUpdate) SetGroupID(id uuid.UUID) *DocumentUpdate {
du.mutation.SetGroupID(id)
@ -110,7 +126,7 @@ func (du *DocumentUpdate) RemoveAttachments(a ...*Attachment) *DocumentUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (du *DocumentUpdate) Save(ctx context.Context) (int, error) {
du.defaults()
return withHooks[int, DocumentMutation](ctx, du.sqlSave, du.mutation, du.hooks)
return withHooks(ctx, du.sqlSave, du.mutation, du.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -288,12 +304,28 @@ func (duo *DocumentUpdateOne) SetTitle(s string) *DocumentUpdateOne {
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.
func (duo *DocumentUpdateOne) SetPath(s string) *DocumentUpdateOne {
duo.mutation.SetPath(s)
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.
func (duo *DocumentUpdateOne) SetGroupID(id uuid.UUID) *DocumentUpdateOne {
duo.mutation.SetGroupID(id)
@ -368,7 +400,7 @@ func (duo *DocumentUpdateOne) Select(field string, fields ...string) *DocumentUp
// Save executes the query and returns the updated Document entity.
func (duo *DocumentUpdateOne) Save(ctx context.Context) (*Document, error) {
duo.defaults()
return withHooks[*Document, DocumentMutation](ctx, duo.sqlSave, duo.mutation, duo.hooks)
return withHooks(ctx, duo.sqlSave, duo.mutation, duo.hooks)
}
// SaveX is like Save, but panics if an error occurs.

View File

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"reflect"
"sync"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
@ -72,45 +73,41 @@ func NewTxContext(parent context.Context, tx *Tx) context.Context {
}
// OrderFunc applies an ordering on the sql selector.
// Deprecated: Use Asc/Desc functions or the package builders instead.
type OrderFunc func(*sql.Selector)
// columnChecker returns a function indicates if the column exists in the given column.
func columnChecker(table string) func(string) error {
checks := map[string]func(string) bool{
attachment.Table: attachment.ValidColumn,
authroles.Table: authroles.ValidColumn,
authtokens.Table: authtokens.ValidColumn,
document.Table: document.ValidColumn,
group.Table: group.ValidColumn,
groupinvitationtoken.Table: groupinvitationtoken.ValidColumn,
item.Table: item.ValidColumn,
itemfield.Table: itemfield.ValidColumn,
label.Table: label.ValidColumn,
location.Table: location.ValidColumn,
maintenanceentry.Table: maintenanceentry.ValidColumn,
notifier.Table: notifier.ValidColumn,
user.Table: user.ValidColumn,
}
check, ok := checks[table]
if !ok {
return func(string) error {
return fmt.Errorf("unknown table %q", table)
}
}
return func(column string) error {
if !check(column) {
return fmt.Errorf("unknown column %q for table %q", column, table)
}
return nil
}
var (
initCheck sync.Once
columnCheck sql.ColumnCheck
)
// columnChecker checks if the column exists in the given table.
func checkColumn(table, column string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
attachment.Table: attachment.ValidColumn,
authroles.Table: authroles.ValidColumn,
authtokens.Table: authtokens.ValidColumn,
document.Table: document.ValidColumn,
group.Table: group.ValidColumn,
groupinvitationtoken.Table: groupinvitationtoken.ValidColumn,
item.Table: item.ValidColumn,
itemfield.Table: itemfield.ValidColumn,
label.Table: label.ValidColumn,
location.Table: location.ValidColumn,
maintenanceentry.Table: maintenanceentry.ValidColumn,
notifier.Table: notifier.ValidColumn,
user.Table: user.ValidColumn,
})
})
return columnCheck(table, column)
}
// Asc applies the given fields in ASC order.
func Asc(fields ...string) OrderFunc {
func Asc(fields ...string) func(*sql.Selector) {
return func(s *sql.Selector) {
check := columnChecker(s.TableName())
for _, f := range fields {
if err := check(f); err != nil {
if err := checkColumn(s.TableName(), f); err != nil {
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
}
s.OrderBy(sql.Asc(s.C(f)))
@ -119,11 +116,10 @@ func Asc(fields ...string) OrderFunc {
}
// Desc applies the given fields in DESC order.
func Desc(fields ...string) OrderFunc {
func Desc(fields ...string) func(*sql.Selector) {
return func(s *sql.Selector) {
check := columnChecker(s.TableName())
for _, f := range fields {
if err := check(f); err != nil {
if err := checkColumn(s.TableName(), f); err != nil {
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
}
s.OrderBy(sql.Desc(s.C(f)))
@ -155,8 +151,7 @@ func Count() AggregateFunc {
// Max applies the "max" aggregation function on the given field of each group.
func Max(field string) AggregateFunc {
return func(s *sql.Selector) string {
check := columnChecker(s.TableName())
if err := check(field); err != nil {
if err := checkColumn(s.TableName(), field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return ""
}
@ -167,8 +162,7 @@ func Max(field string) AggregateFunc {
// Mean applies the "mean" aggregation function on the given field of each group.
func Mean(field string) AggregateFunc {
return func(s *sql.Selector) string {
check := columnChecker(s.TableName())
if err := check(field); err != nil {
if err := checkColumn(s.TableName(), field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return ""
}
@ -179,8 +173,7 @@ func Mean(field string) AggregateFunc {
// Min applies the "min" aggregation function on the given field of each group.
func Min(field string) AggregateFunc {
return func(s *sql.Selector) string {
check := columnChecker(s.TableName())
if err := check(field); err != nil {
if err := checkColumn(s.TableName(), field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return ""
}
@ -191,8 +184,7 @@ func Min(field string) AggregateFunc {
// Sum applies the "sum" aggregation function on the given field of each group.
func Sum(field string) AggregateFunc {
return func(s *sql.Selector) string {
check := columnChecker(s.TableName())
if err := check(field); err != nil {
if err := checkColumn(s.TableName(), field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return ""
}

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
@ -24,10 +25,11 @@ type Group struct {
// Name holds the value of the "name" field.
Name string `json:"name,omitempty"`
// Currency holds the value of the "currency" field.
Currency group.Currency `json:"currency,omitempty"`
Currency string `json:"currency,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the GroupQuery when eager-loading is set.
Edges GroupEdges `json:"edges"`
Edges GroupEdges `json:"edges"`
selectValues sql.SelectValues
}
// GroupEdges holds the relations/edges for other nodes in the graph.
@ -126,7 +128,7 @@ func (*Group) scanValues(columns []string) ([]any, error) {
case group.FieldID:
values[i] = new(uuid.UUID)
default:
return nil, fmt.Errorf("unexpected column %q for type Group", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -168,13 +170,21 @@ func (gr *Group) assignValues(columns []string, values []any) error {
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field currency", values[i])
} else if value.Valid {
gr.Currency = group.Currency(value.String)
gr.Currency = value.String
}
default:
gr.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the Group.
// This includes values selected through modifiers, order, etc.
func (gr *Group) Value(name string) (ent.Value, error) {
return gr.selectValues.Get(name)
}
// QueryUsers queries the "users" edge of the Group entity.
func (gr *Group) QueryUsers() *UserQuery {
return NewGroupClient(gr.config).QueryUsers(gr)
@ -243,7 +253,7 @@ func (gr *Group) String() string {
builder.WriteString(gr.Name)
builder.WriteString(", ")
builder.WriteString("currency=")
builder.WriteString(fmt.Sprintf("%v", gr.Currency))
builder.WriteString(gr.Currency)
builder.WriteByte(')')
return builder.String()
}

View File

@ -3,9 +3,10 @@
package group
import (
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
@ -117,48 +118,183 @@ var (
UpdateDefaultUpdatedAt func() time.Time
// NameValidator is a validator for the "name" field. It is called by the builders before save.
NameValidator func(string) error
// DefaultCurrency holds the default value on creation for the "currency" field.
DefaultCurrency string
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
// Currency defines the type for the "currency" enum field.
type Currency string
// OrderOption defines the ordering options for the Group queries.
type OrderOption func(*sql.Selector)
// CurrencyUsd is the default value of the Currency enum.
const DefaultCurrency = CurrencyUsd
// Currency values.
const (
CurrencyUsd Currency = "usd"
CurrencyEur Currency = "eur"
CurrencyGbp Currency = "gbp"
CurrencyJpy Currency = "jpy"
CurrencyZar Currency = "zar"
CurrencyAud Currency = "aud"
CurrencyNok Currency = "nok"
CurrencyNzd Currency = "nzd"
CurrencySek Currency = "sek"
CurrencyDkk Currency = "dkk"
CurrencyInr Currency = "inr"
CurrencyRmb Currency = "rmb"
CurrencyBgn Currency = "bgn"
CurrencyChf Currency = "chf"
CurrencyPln Currency = "pln"
CurrencyTry Currency = "try"
CurrencyRon Currency = "ron"
CurrencyCzk Currency = "czk"
)
func (c Currency) String() string {
return string(c)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// CurrencyValidator is a validator for the "currency" field enum values. It is called by the builders before save.
func CurrencyValidator(c Currency) error {
switch c {
case CurrencyUsd, CurrencyEur, CurrencyGbp, CurrencyJpy, CurrencyZar, CurrencyAud, CurrencyNok, CurrencyNzd, CurrencySek, CurrencyDkk, CurrencyInr, CurrencyRmb, CurrencyBgn, CurrencyChf, CurrencyPln, CurrencyTry, CurrencyRon, CurrencyCzk:
return nil
default:
return fmt.Errorf("group: invalid enum value for currency field: %q", c)
// 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()
}
// ByName orders the results by the name field.
func ByName(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldName, opts...).ToFunc()
}
// ByCurrency orders the results by the currency field.
func ByCurrency(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCurrency, opts...).ToFunc()
}
// ByUsersCount orders the results by users count.
func ByUsersCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newUsersStep(), opts...)
}
}
// ByUsers orders the results by users terms.
func ByUsers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newUsersStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByLocationsCount orders the results by locations count.
func ByLocationsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newLocationsStep(), opts...)
}
}
// ByLocations orders the results by locations terms.
func ByLocations(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newLocationsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByItemsCount orders the results by items count.
func ByItemsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newItemsStep(), opts...)
}
}
// ByItems orders the results by items terms.
func ByItems(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newItemsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByLabelsCount orders the results by labels count.
func ByLabelsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newLabelsStep(), opts...)
}
}
// ByLabels orders the results by labels terms.
func ByLabels(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newLabelsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByDocumentsCount orders the results by documents count.
func ByDocumentsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newDocumentsStep(), opts...)
}
}
// ByDocuments orders the results by documents terms.
func ByDocuments(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newDocumentsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByInvitationTokensCount orders the results by invitation_tokens count.
func ByInvitationTokensCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newInvitationTokensStep(), opts...)
}
}
// ByInvitationTokens orders the results by invitation_tokens terms.
func ByInvitationTokens(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newInvitationTokensStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByNotifiersCount orders the results by notifiers count.
func ByNotifiersCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newNotifiersStep(), opts...)
}
}
// ByNotifiers orders the results by notifiers terms.
func ByNotifiers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newNotifiersStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newUsersStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UsersInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, UsersTable, UsersColumn),
)
}
func newLocationsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LocationsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, LocationsTable, LocationsColumn),
)
}
func newItemsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn),
)
}
func newLabelsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LabelsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, LabelsTable, LabelsColumn),
)
}
func newDocumentsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DocumentsTable, DocumentsColumn),
)
}
func newInvitationTokensStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(InvitationTokensInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, InvitationTokensTable, InvitationTokensColumn),
)
}
func newNotifiersStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(NotifiersInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, NotifiersTable, NotifiersColumn),
)
}

View File

@ -71,6 +71,11 @@ func Name(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldName, v))
}
// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ.
func Currency(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldCurrency, v))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldCreatedAt, v))
@ -217,25 +222,70 @@ func NameContainsFold(v string) predicate.Group {
}
// CurrencyEQ applies the EQ predicate on the "currency" field.
func CurrencyEQ(v Currency) predicate.Group {
func CurrencyEQ(v string) predicate.Group {
return predicate.Group(sql.FieldEQ(FieldCurrency, v))
}
// CurrencyNEQ applies the NEQ predicate on the "currency" field.
func CurrencyNEQ(v Currency) predicate.Group {
func CurrencyNEQ(v string) predicate.Group {
return predicate.Group(sql.FieldNEQ(FieldCurrency, v))
}
// CurrencyIn applies the In predicate on the "currency" field.
func CurrencyIn(vs ...Currency) predicate.Group {
func CurrencyIn(vs ...string) predicate.Group {
return predicate.Group(sql.FieldIn(FieldCurrency, vs...))
}
// CurrencyNotIn applies the NotIn predicate on the "currency" field.
func CurrencyNotIn(vs ...Currency) predicate.Group {
func CurrencyNotIn(vs ...string) predicate.Group {
return predicate.Group(sql.FieldNotIn(FieldCurrency, vs...))
}
// CurrencyGT applies the GT predicate on the "currency" field.
func CurrencyGT(v string) predicate.Group {
return predicate.Group(sql.FieldGT(FieldCurrency, v))
}
// CurrencyGTE applies the GTE predicate on the "currency" field.
func CurrencyGTE(v string) predicate.Group {
return predicate.Group(sql.FieldGTE(FieldCurrency, v))
}
// CurrencyLT applies the LT predicate on the "currency" field.
func CurrencyLT(v string) predicate.Group {
return predicate.Group(sql.FieldLT(FieldCurrency, v))
}
// CurrencyLTE applies the LTE predicate on the "currency" field.
func CurrencyLTE(v string) predicate.Group {
return predicate.Group(sql.FieldLTE(FieldCurrency, v))
}
// CurrencyContains applies the Contains predicate on the "currency" field.
func CurrencyContains(v string) predicate.Group {
return predicate.Group(sql.FieldContains(FieldCurrency, v))
}
// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field.
func CurrencyHasPrefix(v string) predicate.Group {
return predicate.Group(sql.FieldHasPrefix(FieldCurrency, v))
}
// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field.
func CurrencyHasSuffix(v string) predicate.Group {
return predicate.Group(sql.FieldHasSuffix(FieldCurrency, v))
}
// CurrencyEqualFold applies the EqualFold predicate on the "currency" field.
func CurrencyEqualFold(v string) predicate.Group {
return predicate.Group(sql.FieldEqualFold(FieldCurrency, v))
}
// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field.
func CurrencyContainsFold(v string) predicate.Group {
return predicate.Group(sql.FieldContainsFold(FieldCurrency, v))
}
// HasUsers applies the HasEdge predicate on the "users" edge.
func HasUsers() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
@ -250,11 +300,7 @@ func HasUsers() predicate.Group {
// HasUsersWith applies the HasEdge predicate on the "users" edge with a given conditions (other predicates).
func HasUsersWith(preds ...predicate.User) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UsersInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, UsersTable, UsersColumn),
)
step := newUsersStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -277,11 +323,7 @@ func HasLocations() predicate.Group {
// HasLocationsWith applies the HasEdge predicate on the "locations" edge with a given conditions (other predicates).
func HasLocationsWith(preds ...predicate.Location) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LocationsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, LocationsTable, LocationsColumn),
)
step := newLocationsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -304,11 +346,7 @@ func HasItems() predicate.Group {
// HasItemsWith applies the HasEdge predicate on the "items" edge with a given conditions (other predicates).
func HasItemsWith(preds ...predicate.Item) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ItemsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn),
)
step := newItemsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -331,11 +369,7 @@ func HasLabels() predicate.Group {
// HasLabelsWith applies the HasEdge predicate on the "labels" edge with a given conditions (other predicates).
func HasLabelsWith(preds ...predicate.Label) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LabelsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, LabelsTable, LabelsColumn),
)
step := newLabelsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -358,11 +392,7 @@ func HasDocuments() predicate.Group {
// HasDocumentsWith applies the HasEdge predicate on the "documents" edge with a given conditions (other predicates).
func HasDocumentsWith(preds ...predicate.Document) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(DocumentsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, DocumentsTable, DocumentsColumn),
)
step := newDocumentsStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -385,11 +415,7 @@ func HasInvitationTokens() predicate.Group {
// HasInvitationTokensWith applies the HasEdge predicate on the "invitation_tokens" edge with a given conditions (other predicates).
func HasInvitationTokensWith(preds ...predicate.GroupInvitationToken) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(InvitationTokensInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, InvitationTokensTable, InvitationTokensColumn),
)
step := newInvitationTokensStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -412,11 +438,7 @@ func HasNotifiers() predicate.Group {
// HasNotifiersWith applies the HasEdge predicate on the "notifiers" edge with a given conditions (other predicates).
func HasNotifiersWith(preds ...predicate.Notifier) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(NotifiersInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, NotifiersTable, NotifiersColumn),
)
step := newNotifiersStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -427,32 +449,15 @@ func HasNotifiersWith(preds ...predicate.Notifier) predicate.Group {
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.Group) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
return predicate.Group(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.Group) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
return predicate.Group(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.Group) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
p(s.Not())
})
return predicate.Group(sql.NotPredicates(p))
}

View File

@ -63,15 +63,15 @@ func (gc *GroupCreate) SetName(s string) *GroupCreate {
}
// SetCurrency sets the "currency" field.
func (gc *GroupCreate) SetCurrency(gr group.Currency) *GroupCreate {
gc.mutation.SetCurrency(gr)
func (gc *GroupCreate) SetCurrency(s string) *GroupCreate {
gc.mutation.SetCurrency(s)
return gc
}
// SetNillableCurrency sets the "currency" field if the given value is not nil.
func (gc *GroupCreate) SetNillableCurrency(gr *group.Currency) *GroupCreate {
if gr != nil {
gc.SetCurrency(*gr)
func (gc *GroupCreate) SetNillableCurrency(s *string) *GroupCreate {
if s != nil {
gc.SetCurrency(*s)
}
return gc
}
@ -203,7 +203,7 @@ func (gc *GroupCreate) Mutation() *GroupMutation {
// Save creates the Group in the database.
func (gc *GroupCreate) Save(ctx context.Context) (*Group, error) {
gc.defaults()
return withHooks[*Group, GroupMutation](ctx, gc.sqlSave, gc.mutation, gc.hooks)
return withHooks(ctx, gc.sqlSave, gc.mutation, gc.hooks)
}
// SaveX calls Save and panics if Save returns an error.
@ -267,11 +267,6 @@ func (gc *GroupCreate) check() error {
if _, ok := gc.mutation.Currency(); !ok {
return &ValidationError{Name: "currency", err: errors.New(`ent: missing required field "Group.currency"`)}
}
if v, ok := gc.mutation.Currency(); ok {
if err := group.CurrencyValidator(v); err != nil {
return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Group.currency": %w`, err)}
}
}
return nil
}
@ -320,7 +315,7 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
_node.Name = value
}
if value, ok := gc.mutation.Currency(); ok {
_spec.SetField(group.FieldCurrency, field.TypeEnum, value)
_spec.SetField(group.FieldCurrency, field.TypeString, value)
_node.Currency = value
}
if nodes := gc.mutation.UsersIDs(); len(nodes) > 0 {
@ -441,11 +436,15 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
// GroupCreateBulk is the builder for creating many Group entities in bulk.
type GroupCreateBulk struct {
config
err error
builders []*GroupCreate
}
// Save creates the Group entities in the database.
func (gcb *GroupCreateBulk) Save(ctx context.Context) ([]*Group, error) {
if gcb.err != nil {
return nil, gcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(gcb.builders))
nodes := make([]*Group, len(gcb.builders))
mutators := make([]Mutator, len(gcb.builders))
@ -462,8 +461,8 @@ func (gcb *GroupCreateBulk) Save(ctx context.Context) ([]*Group, error) {
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, gcb.builders[i+1].mutation)
} else {

View File

@ -27,7 +27,7 @@ func (gd *GroupDelete) Where(ps ...predicate.Group) *GroupDelete {
// Exec executes the deletion query and returns how many vertices were deleted.
func (gd *GroupDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, GroupMutation](ctx, gd.sqlExec, gd.mutation, gd.hooks)
return withHooks(ctx, gd.sqlExec, gd.mutation, gd.hooks)
}
// ExecX is like Exec, but panics if an error occurs.

View File

@ -27,7 +27,7 @@ import (
type GroupQuery struct {
config
ctx *QueryContext
order []OrderFunc
order []group.OrderOption
inters []Interceptor
predicates []predicate.Group
withUsers *UserQuery
@ -68,7 +68,7 @@ func (gq *GroupQuery) Unique(unique bool) *GroupQuery {
}
// Order specifies how the records should be ordered.
func (gq *GroupQuery) Order(o ...OrderFunc) *GroupQuery {
func (gq *GroupQuery) Order(o ...group.OrderOption) *GroupQuery {
gq.order = append(gq.order, o...)
return gq
}
@ -416,7 +416,7 @@ func (gq *GroupQuery) Clone() *GroupQuery {
return &GroupQuery{
config: gq.config,
ctx: gq.ctx.Clone(),
order: append([]OrderFunc{}, gq.order...),
order: append([]group.OrderOption{}, gq.order...),
inters: append([]Interceptor{}, gq.inters...),
predicates: append([]predicate.Group{}, gq.predicates...),
withUsers: gq.withUsers.Clone(),
@ -681,7 +681,7 @@ func (gq *GroupQuery) loadUsers(ctx context.Context, query *UserQuery, nodes []*
}
query.withFKs = true
query.Where(predicate.User(func(s *sql.Selector) {
s.Where(sql.InValues(group.UsersColumn, fks...))
s.Where(sql.InValues(s.C(group.UsersColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -694,7 +694,7 @@ func (gq *GroupQuery) loadUsers(ctx context.Context, query *UserQuery, nodes []*
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_users" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_users" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
@ -712,7 +712,7 @@ func (gq *GroupQuery) loadLocations(ctx context.Context, query *LocationQuery, n
}
query.withFKs = true
query.Where(predicate.Location(func(s *sql.Selector) {
s.Where(sql.InValues(group.LocationsColumn, fks...))
s.Where(sql.InValues(s.C(group.LocationsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -725,7 +725,7 @@ func (gq *GroupQuery) loadLocations(ctx context.Context, query *LocationQuery, n
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_locations" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_locations" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
@ -743,7 +743,7 @@ func (gq *GroupQuery) loadItems(ctx context.Context, query *ItemQuery, nodes []*
}
query.withFKs = true
query.Where(predicate.Item(func(s *sql.Selector) {
s.Where(sql.InValues(group.ItemsColumn, fks...))
s.Where(sql.InValues(s.C(group.ItemsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -756,7 +756,7 @@ func (gq *GroupQuery) loadItems(ctx context.Context, query *ItemQuery, nodes []*
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_items" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_items" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
@ -774,7 +774,7 @@ func (gq *GroupQuery) loadLabels(ctx context.Context, query *LabelQuery, nodes [
}
query.withFKs = true
query.Where(predicate.Label(func(s *sql.Selector) {
s.Where(sql.InValues(group.LabelsColumn, fks...))
s.Where(sql.InValues(s.C(group.LabelsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -787,7 +787,7 @@ func (gq *GroupQuery) loadLabels(ctx context.Context, query *LabelQuery, nodes [
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_labels" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_labels" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
@ -805,7 +805,7 @@ func (gq *GroupQuery) loadDocuments(ctx context.Context, query *DocumentQuery, n
}
query.withFKs = true
query.Where(predicate.Document(func(s *sql.Selector) {
s.Where(sql.InValues(group.DocumentsColumn, fks...))
s.Where(sql.InValues(s.C(group.DocumentsColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -818,7 +818,7 @@ func (gq *GroupQuery) loadDocuments(ctx context.Context, query *DocumentQuery, n
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_documents" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_documents" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
@ -836,7 +836,7 @@ func (gq *GroupQuery) loadInvitationTokens(ctx context.Context, query *GroupInvi
}
query.withFKs = true
query.Where(predicate.GroupInvitationToken(func(s *sql.Selector) {
s.Where(sql.InValues(group.InvitationTokensColumn, fks...))
s.Where(sql.InValues(s.C(group.InvitationTokensColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -849,7 +849,7 @@ func (gq *GroupQuery) loadInvitationTokens(ctx context.Context, query *GroupInvi
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_invitation_tokens" returned %v for node %v`, *fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_invitation_tokens" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
@ -865,8 +865,11 @@ func (gq *GroupQuery) loadNotifiers(ctx context.Context, query *NotifierQuery, n
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(notifier.FieldGroupID)
}
query.Where(predicate.Notifier(func(s *sql.Selector) {
s.Where(sql.InValues(group.NotifiersColumn, fks...))
s.Where(sql.InValues(s.C(group.NotifiersColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
@ -876,7 +879,7 @@ func (gq *GroupQuery) loadNotifiers(ctx context.Context, query *NotifierQuery, n
fk := n.GroupID
node, ok := nodeids[fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "group_id" returned %v for node %v`, fk, n.ID)
return fmt.Errorf(`unexpected referenced foreign-key "group_id" returned %v for node %v`, fk, n.ID)
}
assign(node, n)
}

View File

@ -48,16 +48,24 @@ func (gu *GroupUpdate) SetName(s string) *GroupUpdate {
return gu
}
// SetNillableName sets the "name" field if the given value is not nil.
func (gu *GroupUpdate) SetNillableName(s *string) *GroupUpdate {
if s != nil {
gu.SetName(*s)
}
return gu
}
// SetCurrency sets the "currency" field.
func (gu *GroupUpdate) SetCurrency(gr group.Currency) *GroupUpdate {
gu.mutation.SetCurrency(gr)
func (gu *GroupUpdate) SetCurrency(s string) *GroupUpdate {
gu.mutation.SetCurrency(s)
return gu
}
// SetNillableCurrency sets the "currency" field if the given value is not nil.
func (gu *GroupUpdate) SetNillableCurrency(gr *group.Currency) *GroupUpdate {
if gr != nil {
gu.SetCurrency(*gr)
func (gu *GroupUpdate) SetNillableCurrency(s *string) *GroupUpdate {
if s != nil {
gu.SetCurrency(*s)
}
return gu
}
@ -322,7 +330,7 @@ func (gu *GroupUpdate) RemoveNotifiers(n ...*Notifier) *GroupUpdate {
// Save executes the query and returns the number of nodes affected by the update operation.
func (gu *GroupUpdate) Save(ctx context.Context) (int, error) {
gu.defaults()
return withHooks[int, GroupMutation](ctx, gu.sqlSave, gu.mutation, gu.hooks)
return withHooks(ctx, gu.sqlSave, gu.mutation, gu.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -362,11 +370,6 @@ func (gu *GroupUpdate) check() error {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)}
}
}
if v, ok := gu.mutation.Currency(); ok {
if err := group.CurrencyValidator(v); err != nil {
return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Group.currency": %w`, err)}
}
}
return nil
}
@ -389,7 +392,7 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) {
_spec.SetField(group.FieldName, field.TypeString, value)
}
if value, ok := gu.mutation.Currency(); ok {
_spec.SetField(group.FieldCurrency, field.TypeEnum, value)
_spec.SetField(group.FieldCurrency, field.TypeString, value)
}
if gu.mutation.UsersCleared() {
edge := &sqlgraph.EdgeSpec{
@ -738,16 +741,24 @@ func (guo *GroupUpdateOne) SetName(s string) *GroupUpdateOne {
return guo
}
// SetNillableName sets the "name" field if the given value is not nil.
func (guo *GroupUpdateOne) SetNillableName(s *string) *GroupUpdateOne {
if s != nil {
guo.SetName(*s)
}
return guo
}
// SetCurrency sets the "currency" field.
func (guo *GroupUpdateOne) SetCurrency(gr group.Currency) *GroupUpdateOne {
guo.mutation.SetCurrency(gr)
func (guo *GroupUpdateOne) SetCurrency(s string) *GroupUpdateOne {
guo.mutation.SetCurrency(s)
return guo
}
// SetNillableCurrency sets the "currency" field if the given value is not nil.
func (guo *GroupUpdateOne) SetNillableCurrency(gr *group.Currency) *GroupUpdateOne {
if gr != nil {
guo.SetCurrency(*gr)
func (guo *GroupUpdateOne) SetNillableCurrency(s *string) *GroupUpdateOne {
if s != nil {
guo.SetCurrency(*s)
}
return guo
}
@ -1025,7 +1036,7 @@ func (guo *GroupUpdateOne) Select(field string, fields ...string) *GroupUpdateOn
// Save executes the query and returns the updated Group entity.
func (guo *GroupUpdateOne) Save(ctx context.Context) (*Group, error) {
guo.defaults()
return withHooks[*Group, GroupMutation](ctx, guo.sqlSave, guo.mutation, guo.hooks)
return withHooks(ctx, guo.sqlSave, guo.mutation, guo.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -1065,11 +1076,6 @@ func (guo *GroupUpdateOne) check() error {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)}
}
}
if v, ok := guo.mutation.Currency(); ok {
if err := group.CurrencyValidator(v); err != nil {
return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Group.currency": %w`, err)}
}
}
return nil
}
@ -1109,7 +1115,7 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error
_spec.SetField(group.FieldName, field.TypeString, value)
}
if value, ok := guo.mutation.Currency(); ok {
_spec.SetField(group.FieldCurrency, field.TypeEnum, value)
_spec.SetField(group.FieldCurrency, field.TypeString, value)
}
if guo.mutation.UsersCleared() {
edge := &sqlgraph.EdgeSpec{

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
@ -32,6 +33,7 @@ type GroupInvitationToken struct {
// The values are being populated by the GroupInvitationTokenQuery when eager-loading is set.
Edges GroupInvitationTokenEdges `json:"edges"`
group_invitation_tokens *uuid.UUID
selectValues sql.SelectValues
}
// GroupInvitationTokenEdges holds the relations/edges for other nodes in the graph.
@ -72,7 +74,7 @@ func (*GroupInvitationToken) scanValues(columns []string) ([]any, error) {
case groupinvitationtoken.ForeignKeys[0]: // group_invitation_tokens
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type GroupInvitationToken", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -129,11 +131,19 @@ func (git *GroupInvitationToken) assignValues(columns []string, values []any) er
git.group_invitation_tokens = new(uuid.UUID)
*git.group_invitation_tokens = *value.S.(*uuid.UUID)
}
default:
git.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the GroupInvitationToken.
// This includes values selected through modifiers, order, etc.
func (git *GroupInvitationToken) Value(name string) (ent.Value, error) {
return git.selectValues.Get(name)
}
// QueryGroup queries the "group" edge of the GroupInvitationToken entity.
func (git *GroupInvitationToken) QueryGroup() *GroupQuery {
return NewGroupInvitationTokenClient(git.config).QueryGroup(git)

View File

@ -5,6 +5,8 @@ package groupinvitationtoken
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
@ -81,3 +83,45 @@ var (
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
// OrderOption defines the ordering options for the GroupInvitationToken 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()
}
// ByUses orders the results by the uses field.
func ByUses(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUses, 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...))
}
}
func newGroupStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
)
}

View File

@ -295,11 +295,7 @@ func HasGroup() predicate.GroupInvitationToken {
// HasGroupWith applies the HasEdge predicate on the "group" edge with a given conditions (other predicates).
func HasGroupWith(preds ...predicate.Group) predicate.GroupInvitationToken {
return predicate.GroupInvitationToken(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, GroupTable, GroupColumn),
)
step := newGroupStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
@ -310,32 +306,15 @@ func HasGroupWith(preds ...predicate.Group) predicate.GroupInvitationToken {
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.GroupInvitationToken) predicate.GroupInvitationToken {
return predicate.GroupInvitationToken(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
return predicate.GroupInvitationToken(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.GroupInvitationToken) predicate.GroupInvitationToken {
return predicate.GroupInvitationToken(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
return predicate.GroupInvitationToken(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.GroupInvitationToken) predicate.GroupInvitationToken {
return predicate.GroupInvitationToken(func(s *sql.Selector) {
p(s.Not())
})
return predicate.GroupInvitationToken(sql.NotPredicates(p))
}

View File

@ -125,7 +125,7 @@ func (gitc *GroupInvitationTokenCreate) Mutation() *GroupInvitationTokenMutation
// Save creates the GroupInvitationToken in the database.
func (gitc *GroupInvitationTokenCreate) Save(ctx context.Context) (*GroupInvitationToken, error) {
gitc.defaults()
return withHooks[*GroupInvitationToken, GroupInvitationTokenMutation](ctx, gitc.sqlSave, gitc.mutation, gitc.hooks)
return withHooks(ctx, gitc.sqlSave, gitc.mutation, gitc.hooks)
}
// SaveX calls Save and panics if Save returns an error.
@ -269,11 +269,15 @@ func (gitc *GroupInvitationTokenCreate) createSpec() (*GroupInvitationToken, *sq
// GroupInvitationTokenCreateBulk is the builder for creating many GroupInvitationToken entities in bulk.
type GroupInvitationTokenCreateBulk struct {
config
err error
builders []*GroupInvitationTokenCreate
}
// Save creates the GroupInvitationToken entities in the database.
func (gitcb *GroupInvitationTokenCreateBulk) Save(ctx context.Context) ([]*GroupInvitationToken, error) {
if gitcb.err != nil {
return nil, gitcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(gitcb.builders))
nodes := make([]*GroupInvitationToken, len(gitcb.builders))
mutators := make([]Mutator, len(gitcb.builders))
@ -290,8 +294,8 @@ func (gitcb *GroupInvitationTokenCreateBulk) Save(ctx context.Context) ([]*Group
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, gitcb.builders[i+1].mutation)
} else {

View File

@ -27,7 +27,7 @@ func (gitd *GroupInvitationTokenDelete) Where(ps ...predicate.GroupInvitationTok
// Exec executes the deletion query and returns how many vertices were deleted.
func (gitd *GroupInvitationTokenDelete) Exec(ctx context.Context) (int, error) {
return withHooks[int, GroupInvitationTokenMutation](ctx, gitd.sqlExec, gitd.mutation, gitd.hooks)
return withHooks(ctx, gitd.sqlExec, gitd.mutation, gitd.hooks)
}
// ExecX is like Exec, but panics if an error occurs.

View File

@ -20,7 +20,7 @@ import (
type GroupInvitationTokenQuery struct {
config
ctx *QueryContext
order []OrderFunc
order []groupinvitationtoken.OrderOption
inters []Interceptor
predicates []predicate.GroupInvitationToken
withGroup *GroupQuery
@ -56,7 +56,7 @@ func (gitq *GroupInvitationTokenQuery) Unique(unique bool) *GroupInvitationToken
}
// Order specifies how the records should be ordered.
func (gitq *GroupInvitationTokenQuery) Order(o ...OrderFunc) *GroupInvitationTokenQuery {
func (gitq *GroupInvitationTokenQuery) Order(o ...groupinvitationtoken.OrderOption) *GroupInvitationTokenQuery {
gitq.order = append(gitq.order, o...)
return gitq
}
@ -272,7 +272,7 @@ func (gitq *GroupInvitationTokenQuery) Clone() *GroupInvitationTokenQuery {
return &GroupInvitationTokenQuery{
config: gitq.config,
ctx: gitq.ctx.Clone(),
order: append([]OrderFunc{}, gitq.order...),
order: append([]groupinvitationtoken.OrderOption{}, gitq.order...),
inters: append([]Interceptor{}, gitq.inters...),
predicates: append([]predicate.GroupInvitationToken{}, gitq.predicates...),
withGroup: gitq.withGroup.Clone(),

View File

@ -110,7 +110,7 @@ func (gitu *GroupInvitationTokenUpdate) ClearGroup() *GroupInvitationTokenUpdate
// Save executes the query and returns the number of nodes affected by the update operation.
func (gitu *GroupInvitationTokenUpdate) Save(ctx context.Context) (int, error) {
gitu.defaults()
return withHooks[int, GroupInvitationTokenMutation](ctx, gitu.sqlSave, gitu.mutation, gitu.hooks)
return withHooks(ctx, gitu.sqlSave, gitu.mutation, gitu.hooks)
}
// SaveX is like Save, but panics if an error occurs.
@ -309,7 +309,7 @@ func (gituo *GroupInvitationTokenUpdateOne) Select(field string, fields ...strin
// Save executes the query and returns the updated GroupInvitationToken entity.
func (gituo *GroupInvitationTokenUpdateOne) Save(ctx context.Context) (*GroupInvitationToken, error) {
gituo.defaults()
return withHooks[*GroupInvitationToken, GroupInvitationTokenMutation](ctx, gituo.sqlSave, gituo.mutation, gituo.hooks)
return withHooks(ctx, gituo.sqlSave, gituo.mutation, gituo.hooks)
}
// SaveX is like Save, but panics if an error occurs.

View File

@ -7,6 +7,7 @@ import (
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
@ -71,6 +72,7 @@ type Item struct {
group_items *uuid.UUID
item_children *uuid.UUID
location_items *uuid.UUID
selectValues sql.SelectValues
}
// ItemEdges holds the relations/edges for other nodes in the graph.
@ -204,7 +206,7 @@ func (*Item) scanValues(columns []string) ([]any, error) {
case item.ForeignKeys[2]: // location_items
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type Item", columns[i])
values[i] = new(sql.UnknownType)
}
}
return values, nil
@ -383,11 +385,19 @@ func (i *Item) assignValues(columns []string, values []any) error {
i.location_items = new(uuid.UUID)
*i.location_items = *value.S.(*uuid.UUID)
}
default:
i.selectValues.Set(columns[j], values[j])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the Item.
// This includes values selected through modifiers, order, etc.
func (i *Item) Value(name string) (ent.Value, error) {
return i.selectValues.Get(name)
}
// QueryGroup queries the "group" edge of the Item entity.
func (i *Item) QueryGroup() *GroupQuery {
return NewItemClient(i.config).QueryGroup(i)

View File

@ -5,6 +5,8 @@ package item
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
@ -226,3 +228,273 @@ var (
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
// OrderOption defines the ordering options for the Item 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()
}
// ByName orders the results by the name field.
func ByName(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldName, opts...).ToFunc()
}
// ByDescription orders the results by the description field.
func ByDescription(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldDescription, opts...).ToFunc()
}
// ByImportRef orders the results by the import_ref field.
func ByImportRef(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldImportRef, opts...).ToFunc()
}
// ByNotes orders the results by the notes field.
func ByNotes(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldNotes, opts...).ToFunc()
}
// ByQuantity orders the results by the quantity field.
func ByQuantity(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldQuantity, opts...).ToFunc()
}
// ByInsured orders the results by the insured field.
func ByInsured(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldInsured, opts...).ToFunc()
}
// ByArchived orders the results by the archived field.
func ByArchived(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldArchived, opts...).ToFunc()
}
// ByAssetID orders the results by the asset_id field.
func ByAssetID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldAssetID, opts...).ToFunc()
}
// BySerialNumber orders the results by the serial_number field.
func BySerialNumber(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSerialNumber, opts...).ToFunc()
}
// ByModelNumber orders the results by the model_number field.
func ByModelNumber(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldModelNumber, opts...).ToFunc()
}
// ByManufacturer orders the results by the manufacturer field.
func ByManufacturer(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldManufacturer, opts...).ToFunc()
}
// ByLifetimeWarranty orders the results by the lifetime_warranty field.
func ByLifetimeWarranty(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldLifetimeWarranty, opts...).ToFunc()
}
// ByWarrantyExpires orders the results by the warranty_expires field.
func ByWarrantyExpires(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldWarrantyExpires, opts...).ToFunc()
}
// ByWarrantyDetails orders the results by the warranty_details field.
func ByWarrantyDetails(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldWarrantyDetails, opts...).ToFunc()
}
// ByPurchaseTime orders the results by the purchase_time field.
func ByPurchaseTime(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPurchaseTime, opts...).ToFunc()
}
// ByPurchaseFrom orders the results by the purchase_from field.
func ByPurchaseFrom(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPurchaseFrom, opts...).ToFunc()
}
// ByPurchasePrice orders the results by the purchase_price field.
func ByPurchasePrice(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPurchasePrice, opts...).ToFunc()
}
// BySoldTime orders the results by the sold_time field.
func BySoldTime(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSoldTime, opts...).ToFunc()
}
// BySoldTo orders the results by the sold_to field.
func BySoldTo(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSoldTo, opts...).ToFunc()
}
// BySoldPrice orders the results by the sold_price field.
func BySoldPrice(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSoldPrice, opts...).ToFunc()
}
// BySoldNotes orders the results by the sold_notes field.
func BySoldNotes(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldSoldNotes, 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...))
}
}
// ByParentField orders the results by parent field.
func ByParentField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newParentStep(), sql.OrderByField(field, opts...))
}
}
// ByChildrenCount orders the results by children count.
func ByChildrenCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newChildrenStep(), opts...)
}
}
// ByChildren orders the results by children terms.
func ByChildren(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newChildrenStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByLabelCount orders the results by label count.
func ByLabelCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newLabelStep(), opts...)
}
}
// ByLabel orders the results by label terms.
func ByLabel(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newLabelStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByLocationField orders the results by location field.
func ByLocationField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newLocationStep(), sql.OrderByField(field, opts...))
}
}
// ByFieldsCount orders the results by fields count.
func ByFieldsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newFieldsStep(), opts...)
}
}
// ByFields orders the results by fields terms.
func ByFields(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newFieldsStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByMaintenanceEntriesCount orders the results by maintenance_entries count.
func ByMaintenanceEntriesCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newMaintenanceEntriesStep(), opts...)
}
}
// ByMaintenanceEntries orders the results by maintenance_entries terms.
func ByMaintenanceEntries(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newMaintenanceEntriesStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// 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 newParentStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn),
)
}
func newChildrenStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn),
)
}
func newLabelStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LabelInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2M, true, LabelTable, LabelPrimaryKey...),
)
}
func newLocationStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(LocationInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, LocationTable, LocationColumn),
)
}
func newFieldsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(FieldsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, FieldsTable, FieldsColumn),
)
}
func newMaintenanceEntriesStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(MaintenanceEntriesInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, MaintenanceEntriesTable, MaintenanceEntriesColumn),
)
}
func newAttachmentsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(AttachmentsInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, AttachmentsTable, AttachmentsColumn),
)
}

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