mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
Compare commits
19 Commits
v0.4.0
...
fix/missin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a71a51c43 | ||
|
|
fbcbde836a | ||
|
|
b7aacb3cde | ||
|
|
b6b2a2d889 | ||
|
|
592d4eda55 | ||
|
|
2fb5a437a2 | ||
|
|
7e0f1fac23 | ||
|
|
a886fa86ca | ||
|
|
c722495fdd | ||
|
|
4a9d21d604 | ||
|
|
cd82fe0d89 | ||
|
|
6529549289 | ||
|
|
e2d93f8523 | ||
|
|
82269e8a95 | ||
|
|
4aee60c242 | ||
|
|
d151d42081 | ||
|
|
a4b4fe3454 | ||
|
|
fe6cd431a6 | ||
|
|
1f0c8d09c2 |
6
.github/workflows/partial-backend.yaml
vendored
6
.github/workflows/partial-backend.yaml
vendored
@@ -16,6 +16,8 @@ jobs:
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
@@ -28,7 +30,7 @@ jobs:
|
||||
args: --timeout=6m
|
||||
|
||||
- name: Build API
|
||||
run: task api:build
|
||||
run: task go:build
|
||||
|
||||
- name: Test
|
||||
run: task api:coverage
|
||||
run: task go:coverage
|
||||
|
||||
2
.github/workflows/partial-frontend.yaml
vendored
2
.github/workflows/partial-frontend.yaml
vendored
@@ -14,6 +14,8 @@ jobs:
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
|
||||
4
.github/workflows/pull-requests.yaml
vendored
4
.github/workflows/pull-requests.yaml
vendored
@@ -8,8 +8,8 @@ on:
|
||||
jobs:
|
||||
backend-tests:
|
||||
name: "Backend Server Tests"
|
||||
uses: hay-kot/homebox/.github/workflows/partial-backend.yaml@main
|
||||
uses: ./.github/workflows/partial-backend.yaml
|
||||
|
||||
frontend-tests:
|
||||
name: "Frontend and End-to-End Tests"
|
||||
uses: hay-kot/homebox/.github/workflows/partial-frontend.yaml@main
|
||||
uses: ./.github/workflows/partial-frontend.yaml
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,7 +3,6 @@ backend/.data/*
|
||||
config.yml
|
||||
homebox.db
|
||||
.idea
|
||||
|
||||
.DS_Store
|
||||
test-mailer.json
|
||||
node_modules
|
||||
@@ -32,6 +31,7 @@ node_modules
|
||||
go.work
|
||||
.task/
|
||||
backend/.env
|
||||
build/*
|
||||
|
||||
# Output Directory for Nuxt/Frontend during build step
|
||||
backend/app/api/public/*
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -10,5 +10,8 @@
|
||||
"package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig, pnpm-lock.yaml, postcss.config.js, tailwind.config.js",
|
||||
"docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml",
|
||||
"README.md": "LICENSE, SECURITY.md"
|
||||
}
|
||||
},
|
||||
"cSpell.words": [
|
||||
"debughandlers"
|
||||
]
|
||||
}
|
||||
|
||||
51
CONTRIBUTING.md
Normal file
51
CONTRIBUTING.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Contributing
|
||||
|
||||
## We Develop with Github
|
||||
|
||||
We use github to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
|
||||
## Branch Flow
|
||||
|
||||
We use the `main` branch as the development branch. All PRs should be made to the `main` branch from a feature branch. To create a pull request you can use the following steps:
|
||||
|
||||
1. Fork the repository and create a new branch from `main`.
|
||||
2. If you've added code that should be tested, add tests.
|
||||
3. If you've changed API's, update the documentation.
|
||||
4. Ensure that the test suite and linters pass
|
||||
5. Issue your pull request
|
||||
|
||||
## How To Get Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
There is a devcontainer available for this project. If you are using VSCode, you can use the devcontainer to get started. If you are not using VSCode, you can need to ensure that you have the following tools installed:
|
||||
|
||||
- [Go 1.19+](https://golang.org/doc/install)
|
||||
- [Swaggo](https://github.com/swaggo/swag)
|
||||
- [Node.js 16+](https://nodejs.org/en/download/)
|
||||
- [pnpm](https://pnpm.io/installation)
|
||||
- [Taskfile](https://taskfile.dev/#/installation) (Optional but recommended)
|
||||
- For code generation, you'll need to have `python3` available on your path. In most cases, this is already installed and available.
|
||||
|
||||
If you're using `taskfile` you can run `task --list-all` for a list of all commands and their descriptions.
|
||||
|
||||
### Setup
|
||||
|
||||
If you're using the taskfile you can use the `task setup` command to run the required setup commands. Otherwise you can review the commands required in the `Taskfile.yml` file.
|
||||
|
||||
Note that when installing dependencies with pnpm you must use the `--shamefully-hoist` flag. If you don't use this flag you will get an error when running the the frontend server.
|
||||
|
||||
### API Development Notes
|
||||
|
||||
start command `task go:run`
|
||||
|
||||
1. API Server does not auto reload. You'll need to restart the server after making changes.
|
||||
2. Unit tests should be written in Go, however end-to-end or user story tests should be written in TypeScript using the client library in the frontend directory.
|
||||
|
||||
### Frontend Development Notes
|
||||
|
||||
start command `task: ui:dev`
|
||||
|
||||
1. The frontend is a Vue 3 app with Nuxt.js that uses Tailwind and DaisyUI for styling.
|
||||
2. We're using Vitest for our automated testing. you can run these with `task ui:watch`.
|
||||
3. Tests require the API server to be running and in some cases the first run will fail due to a race condition. If this happens just run the tests again and they should pass.
|
||||
@@ -21,7 +21,7 @@ WORKDIR /go/src/app
|
||||
COPY ./backend .
|
||||
RUN go get -d -v ./...
|
||||
RUN rm -rf ./app/api/public
|
||||
COPY --from=frontend-builder /app/.output/public ./app/api/public
|
||||
COPY --from=frontend-builder /app/.output/public ./app/api/static/public
|
||||
RUN CGO_ENABLED=1 GOOS=linux go build \
|
||||
-ldflags "-s -w -X main.commit=$COMMIT -X main.buildTime=$BUILD_TIME -X main.version=$VERSION" \
|
||||
-o /go/bin/api \
|
||||
|
||||
94
Taskfile.yml
94
Taskfile.yml
@@ -5,11 +5,12 @@ env:
|
||||
UNSAFE_DISABLE_PASSWORD_PROJECTION: "yes_i_am_sure"
|
||||
tasks:
|
||||
setup:
|
||||
desc: Install dependencies
|
||||
desc: Install development dependencies
|
||||
cmds:
|
||||
- go install github.com/swaggo/swag/cmd/swag@latest
|
||||
- cd backend && go mod tidy
|
||||
- cd frontend && pnpm install --shamefully-hoist
|
||||
|
||||
generate:
|
||||
desc: |
|
||||
Generates collateral files from the backend project
|
||||
@@ -17,27 +18,27 @@ tasks:
|
||||
deps:
|
||||
- db:generate
|
||||
cmds:
|
||||
- cd backend/app/api/ && swag fmt
|
||||
- cd backend/app/api/ && swag init --dir=./,../../internal,../../pkgs
|
||||
- cd backend/app/api/static && swag fmt --dir=../
|
||||
- cd backend/app/api/static && swag init --dir=../,../../../internal,../../../pkgs
|
||||
- |
|
||||
npx swagger-typescript-api \
|
||||
--no-client \
|
||||
--modular \
|
||||
--path ./backend/app/api/docs/swagger.json \
|
||||
--path ./backend/app/api/static/docs/swagger.json \
|
||||
--output ./frontend/lib/api/types
|
||||
- python3 ./scripts/process-types.py ./frontend/lib/api/types/data-contracts.ts
|
||||
sources:
|
||||
- "./backend/app/api/**/*"
|
||||
- "./backend/internal/repo/**/*"
|
||||
- "./backend/internal/data/**"
|
||||
- "./backend/internal/services/**/*"
|
||||
- "./scripts/process-types.py"
|
||||
generates:
|
||||
- "./frontend/lib/api/types/data-contracts.ts"
|
||||
- "./backend/ent/schema"
|
||||
- "./backend/app/api/docs/swagger.json"
|
||||
- "./backend/app/api/docs/swagger.yaml"
|
||||
- "./backend/internal/data/ent/schema"
|
||||
- "./backend/app/api/static/docs/swagger.json"
|
||||
- "./backend/app/api/static/docs/swagger.yaml"
|
||||
|
||||
api:
|
||||
go:run:
|
||||
desc: Starts the backend api server (depends on generate task)
|
||||
deps:
|
||||
- generate
|
||||
@@ -45,57 +46,72 @@ tasks:
|
||||
- cd backend && go run ./app/api/ {{ .CLI_ARGS }}
|
||||
silent: false
|
||||
|
||||
api:build:
|
||||
go:test:
|
||||
desc: Runs all go tests using gotestsum - supports passing gotestsum args
|
||||
cmds:
|
||||
- cd backend && go build ./app/api/
|
||||
silent: true
|
||||
- cd backend && gotestsum {{ .CLI_ARGS }} ./...
|
||||
|
||||
api:test:
|
||||
cmds:
|
||||
- cd backend && go test ./app/api/
|
||||
silent: true
|
||||
|
||||
api:watch:
|
||||
cmds:
|
||||
- cd backend && gotestsum --watch ./...
|
||||
|
||||
api:coverage:
|
||||
go:coverage:
|
||||
desc: Runs all go tests with -race flag and generates a coverage report
|
||||
cmds:
|
||||
- cd backend && go test -race -coverprofile=coverage.out -covermode=atomic ./app/... ./internal/... ./pkgs/... -v -cover
|
||||
silent: true
|
||||
|
||||
test:ci:
|
||||
go:tidy:
|
||||
desc: Runs go mod tidy on the backend
|
||||
cmds:
|
||||
- cd backend && go build ./app/api
|
||||
- backend/api &
|
||||
- sleep 5
|
||||
- cd frontend && pnpm run test:ci
|
||||
silent: true
|
||||
- cd backend && go mod tidy
|
||||
|
||||
frontend:watch:
|
||||
desc: Starts the vitest test runner in watch mode
|
||||
go:lint:
|
||||
desc: Runs golangci-lint
|
||||
cmds:
|
||||
- cd frontend && pnpm vitest --watch
|
||||
- cd backend && golangci-lint run ./...
|
||||
|
||||
frontend:
|
||||
desc: Run frontend development server
|
||||
go:all:
|
||||
desc: Runs all go test and lint related tasks
|
||||
cmds:
|
||||
- cd frontend && pnpm dev
|
||||
- task: go:tidy
|
||||
- task: go:lint
|
||||
- task: go:test
|
||||
|
||||
go:build:
|
||||
desc: Builds the backend binary
|
||||
cmds:
|
||||
- cd backend && go build -o ../build/backend ./app/api
|
||||
|
||||
db:generate:
|
||||
desc: Run Entgo.io Code Generation
|
||||
cmds:
|
||||
- |
|
||||
cd backend && go generate ./... \
|
||||
--template=ent/schema/templates/has_id.tmpl
|
||||
cd backend/internal/ && go generate ./... \
|
||||
--template=./data/ent/schema/templates/has_id.tmpl
|
||||
sources:
|
||||
- "./backend/ent/schema/**/*"
|
||||
- "./backend/internal/data/ent/schema/**/*"
|
||||
generates:
|
||||
- "./backend/ent/"
|
||||
- "./backend/internal/ent/"
|
||||
|
||||
db:migration:
|
||||
desc: Runs the database diff engine to generate a SQL migration files
|
||||
deps:
|
||||
- db:generate
|
||||
cmds:
|
||||
- cd backend && go run app/migrations/main.go {{ .CLI_ARGS }}
|
||||
- cd backend && go run app/tools/migrations/main.go {{ .CLI_ARGS }}
|
||||
|
||||
ui:watch:
|
||||
desc: Starts the vitest test runner in watch mode
|
||||
cmds:
|
||||
- cd frontend && pnpm run test:watch
|
||||
|
||||
ui:dev:
|
||||
desc: Run frontend development server
|
||||
cmds:
|
||||
- cd frontend && pnpm dev
|
||||
|
||||
test:ci:
|
||||
desc: Runs end-to-end test on a live server (only for use in CI)
|
||||
cmds:
|
||||
- cd backend && go build ./app/api
|
||||
- backend/api &
|
||||
- sleep 5
|
||||
- cd frontend && pnpm run test:ci
|
||||
silent: true
|
||||
|
||||
@@ -3,10 +3,10 @@ package main
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/config"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/config"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/mailer"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/csv"
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
||||
16
backend/app/api/handlers/debughandlers/debug.go
Normal file
16
backend/app/api/handlers/debughandlers/debug.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package debughandlers
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
)
|
||||
|
||||
func New(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/debug/pprof", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
mux.Handle("/debug/vars", expvar.Handler())
|
||||
}
|
||||
@@ -3,7 +3,8 @@ package v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
)
|
||||
|
||||
@@ -26,6 +27,7 @@ func WithRegistration(allowRegistration bool) func(*V1Controller) {
|
||||
}
|
||||
|
||||
type V1Controller struct {
|
||||
repo *repo.AllRepos
|
||||
svc *services.AllServices
|
||||
maxUploadSize int64
|
||||
isDemo bool
|
||||
@@ -57,8 +59,9 @@ func BaseUrlFunc(prefix string) func(s string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func NewControllerV1(svc *services.AllServices, options ...func(*V1Controller)) *V1Controller {
|
||||
func NewControllerV1(svc *services.AllServices, repos *repo.AllRepos, options ...func(*V1Controller)) *V1Controller {
|
||||
ctrl := &V1Controller{
|
||||
repo: repos,
|
||||
svc: svc,
|
||||
allowRegistration: true,
|
||||
}
|
||||
@@ -76,9 +79,9 @@ func NewControllerV1(svc *services.AllServices, options ...func(*V1Controller))
|
||||
// @Produce json
|
||||
// @Success 200 {object} ApiSummary
|
||||
// @Router /v1/status [GET]
|
||||
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
server.Respond(w, http.StatusOK, ApiSummary{
|
||||
func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
return server.Respond(w, http.StatusOK, ApiSummary{
|
||||
Healthy: ready(),
|
||||
Title: "Go API Template",
|
||||
Message: "Welcome to the Go API Template Application!",
|
||||
27
backend/app/api/handlers/v1/partials.go
Normal file
27
backend/app/api/handlers/v1/partials.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
)
|
||||
|
||||
// routeID extracts the ID from the request URL. If the ID is not in a valid
|
||||
// format, an error is returned. If a error is returned, it can be directly returned
|
||||
// from the handler. the validate.ErrInvalidID error is known by the error middleware
|
||||
// and will be handled accordingly.
|
||||
//
|
||||
// Example: /api/v1/ac614db5-d8b8-4659-9b14-6e913a6eb18a -> uuid.UUID{ac614db5-d8b8-4659-9b14-6e913a6eb18a}
|
||||
func (ctrl *V1Controller) routeID(r *http.Request) (uuid.UUID, error) {
|
||||
return ctrl.routeUUID(r, "id")
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) routeUUID(r *http.Request, key string) (uuid.UUID, error) {
|
||||
ID, err := uuid.Parse(chi.URLParam(r, key))
|
||||
if err != nil {
|
||||
return uuid.Nil, validate.NewInvalidRouteKeyError(key)
|
||||
}
|
||||
return ID, nil
|
||||
}
|
||||
36
backend/app/api/handlers/v1/query_params.go
Normal file
36
backend/app/api/handlers/v1/query_params.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func queryUUIDList(params url.Values, key string) []uuid.UUID {
|
||||
var ids []uuid.UUID
|
||||
for _, id := range params[key] {
|
||||
uid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, uid)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func queryIntOrNegativeOne(s string) int {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func queryBool(s string) bool {
|
||||
b, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -32,17 +33,15 @@ type (
|
||||
// @Produce json
|
||||
// @Success 200 {object} TokenResponse
|
||||
// @Router /v1/users/login [POST]
|
||||
func (ctrl *V1Controller) HandleAuthLogin() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleAuthLogin() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
loginForm := &LoginForm{}
|
||||
|
||||
switch r.Header.Get("Content-Type") {
|
||||
case server.ContentFormUrlEncoded:
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
server.Respond(w, http.StatusBadRequest, server.Wrap(err))
|
||||
log.Error().Err(err).Msg("failed to parse form")
|
||||
return
|
||||
return server.Respond(w, http.StatusBadRequest, server.Wrap(err))
|
||||
}
|
||||
|
||||
loginForm.Username = r.PostFormValue("username")
|
||||
@@ -52,27 +51,31 @@ func (ctrl *V1Controller) HandleAuthLogin() http.HandlerFunc {
|
||||
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to decode login form")
|
||||
server.Respond(w, http.StatusBadRequest, server.Wrap(err))
|
||||
return
|
||||
}
|
||||
default:
|
||||
server.Respond(w, http.StatusBadRequest, errors.New("invalid content type"))
|
||||
return
|
||||
return server.Respond(w, http.StatusBadRequest, errors.New("invalid content type"))
|
||||
}
|
||||
|
||||
if loginForm.Username == "" || loginForm.Password == "" {
|
||||
server.RespondError(w, http.StatusBadRequest, errors.New("username and password are required"))
|
||||
return
|
||||
return validate.NewFieldErrors(
|
||||
validate.FieldError{
|
||||
Field: "username",
|
||||
Error: "username or password is empty",
|
||||
},
|
||||
validate.FieldError{
|
||||
Field: "password",
|
||||
Error: "username or password is empty",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
newToken, err := ctrl.svc.User.Login(r.Context(), loginForm.Username, loginForm.Password)
|
||||
|
||||
if err != nil {
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(errors.New("authentication failed"), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, TokenResponse{
|
||||
return server.Respond(w, http.StatusOK, TokenResponse{
|
||||
Token: "Bearer " + newToken.Raw,
|
||||
ExpiresAt: newToken.ExpiresAt,
|
||||
})
|
||||
@@ -85,23 +88,19 @@ func (ctrl *V1Controller) HandleAuthLogin() http.HandlerFunc {
|
||||
// @Success 204
|
||||
// @Router /v1/users/logout [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleAuthLogout() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleAuthLogout() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
token := services.UseTokenCtx(r.Context())
|
||||
|
||||
if token == "" {
|
||||
server.RespondError(w, http.StatusUnauthorized, errors.New("no token within request context"))
|
||||
return
|
||||
return validate.NewRequestError(errors.New("no token within request context"), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
err := ctrl.svc.User.Logout(r.Context(), token)
|
||||
|
||||
if err != nil {
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,22 +112,18 @@ func (ctrl *V1Controller) HandleAuthLogout() http.HandlerFunc {
|
||||
// @Success 200
|
||||
// @Router /v1/users/refresh [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleAuthRefresh() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleAuthRefresh() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
requestToken := services.UseTokenCtx(r.Context())
|
||||
|
||||
if requestToken == "" {
|
||||
server.RespondError(w, http.StatusUnauthorized, errors.New("no user token found"))
|
||||
return
|
||||
return validate.NewRequestError(errors.New("no token within request context"), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
newToken, err := ctrl.svc.User.RenewToken(r.Context(), requestToken)
|
||||
|
||||
if err != nil {
|
||||
server.RespondUnauthorized(w)
|
||||
return
|
||||
return validate.NewUnauthorizedError()
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, newToken)
|
||||
return server.Respond(w, http.StatusOK, newToken)
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"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/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -24,6 +24,26 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// HandleGroupGet godoc
|
||||
// @Summary Get the current user's group
|
||||
// @Tags Group
|
||||
// @Produce json
|
||||
// @Success 200 {object} repo.GroupStatistics
|
||||
// @Router /v1/groups/statistics [Get]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGroupStatistics() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
stats, err := ctrl.repo.Groups.GroupStatistics(ctx, ctx.GID)
|
||||
if err != nil {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return server.Respond(w, http.StatusOK, stats)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGroupGet godoc
|
||||
// @Summary Get the current user's group
|
||||
// @Tags Group
|
||||
@@ -31,7 +51,7 @@ type (
|
||||
// @Success 200 {object} repo.Group
|
||||
// @Router /v1/groups [Get]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGroupGet() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleGroupGet() server.HandlerFunc {
|
||||
return ctrl.handleGroupGeneral()
|
||||
}
|
||||
|
||||
@@ -43,42 +63,40 @@ func (ctrl *V1Controller) HandleGroupGet() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.Group
|
||||
// @Router /v1/groups [Put]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGroupUpdate() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleGroupUpdate() server.HandlerFunc {
|
||||
return ctrl.handleGroupGeneral()
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) handleGroupGeneral() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) handleGroupGeneral() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
group, err := ctrl.svc.Group.Get(ctx)
|
||||
group, err := ctrl.repo.Groups.GroupByID(ctx, ctx.GID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get group")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
group.Currency = strings.ToUpper(group.Currency) // TODO: Hack to fix the currency enums being lower caseÍ
|
||||
server.Respond(w, http.StatusOK, group)
|
||||
return server.Respond(w, http.StatusOK, group)
|
||||
|
||||
case http.MethodPut:
|
||||
data := repo.GroupUpdate{}
|
||||
if err := server.Decode(r, &data); err != nil {
|
||||
server.RespondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
group, err := ctrl.svc.Group.UpdateGroup(ctx, data)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to update group")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
group.Currency = strings.ToUpper(group.Currency) // TODO: Hack to fix the currency enums being lower case
|
||||
server.Respond(w, http.StatusOK, group)
|
||||
|
||||
return server.Respond(w, http.StatusOK, group)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +108,12 @@ func (ctrl *V1Controller) handleGroupGeneral() http.HandlerFunc {
|
||||
// @Success 200 {object} GroupInvitation
|
||||
// @Router /v1/groups/invitations [Post]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGroupInvitationsCreate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleGroupInvitationsCreate() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
data := GroupInvitationCreate{}
|
||||
if err := server.Decode(r, &data); err != nil {
|
||||
log.Err(err).Msg("failed to decode user registration data")
|
||||
server.RespondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if data.ExpiresAt.IsZero() {
|
||||
@@ -108,11 +125,10 @@ func (ctrl *V1Controller) HandleGroupInvitationsCreate() http.HandlerFunc {
|
||||
token, err := ctrl.svc.Group.NewInvitation(ctx, data.Uses, data.ExpiresAt)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create new token")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusCreated, GroupInvitation{
|
||||
return server.Respond(w, http.StatusCreated, GroupInvitation{
|
||||
Token: token,
|
||||
ExpiresAt: data.ExpiresAt,
|
||||
Uses: data.Uses,
|
||||
@@ -3,12 +3,10 @@ package v1
|
||||
import (
|
||||
"encoding/csv"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/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/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -25,48 +23,29 @@ import (
|
||||
// @Success 200 {object} repo.PaginationResult[repo.ItemSummary]{}
|
||||
// @Router /v1/items [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
|
||||
uuidList := func(params url.Values, key string) []uuid.UUID {
|
||||
var ids []uuid.UUID
|
||||
for _, id := range params[key] {
|
||||
uid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, uid)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
intOrNegativeOne := func(s string) int {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return i
|
||||
}
|
||||
func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc {
|
||||
|
||||
extractQuery := func(r *http.Request) repo.ItemQuery {
|
||||
params := r.URL.Query()
|
||||
|
||||
return repo.ItemQuery{
|
||||
Page: intOrNegativeOne(params.Get("page")),
|
||||
PageSize: intOrNegativeOne(params.Get("perPage")),
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: uuidList(params, "locations"),
|
||||
LabelIDs: uuidList(params, "labels"),
|
||||
Page: queryIntOrNegativeOne(params.Get("page")),
|
||||
PageSize: queryIntOrNegativeOne(params.Get("perPage")),
|
||||
Search: params.Get("q"),
|
||||
LocationIDs: queryUUIDList(params, "locations"),
|
||||
LabelIDs: queryUUIDList(params, "labels"),
|
||||
IncludeArchived: queryBool(params.Get("includeArchived")),
|
||||
}
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
items, err := ctrl.svc.Items.Query(ctx, extractQuery(r))
|
||||
items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GID, extractQuery(r))
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get items")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusOK, items)
|
||||
return server.Respond(w, http.StatusOK, items)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,24 +57,22 @@ func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.ItemSummary
|
||||
// @Router /v1/items [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemsCreate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleItemsCreate() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
createData := repo.ItemCreate{}
|
||||
if err := server.Decode(r, &createData); err != nil {
|
||||
log.Err(err).Msg("failed to decode request body")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
user := services.UseUserCtx(r.Context())
|
||||
item, err := ctrl.svc.Items.Create(r.Context(), user.GroupID, createData)
|
||||
item, err := ctrl.repo.Items.Create(r.Context(), user.GroupID, createData)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create item")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusCreated, item)
|
||||
return server.Respond(w, http.StatusCreated, item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +84,7 @@ func (ctrl *V1Controller) HandleItemsCreate() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.ItemOut
|
||||
// @Router /v1/items/{id} [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemGet() server.HandlerFunc {
|
||||
return ctrl.handleItemsGeneral()
|
||||
}
|
||||
|
||||
@@ -119,7 +96,7 @@ func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
|
||||
// @Success 204
|
||||
// @Router /v1/items/{id} [DELETE]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemDelete() server.HandlerFunc {
|
||||
return ctrl.handleItemsGeneral()
|
||||
}
|
||||
|
||||
@@ -132,54 +109,49 @@ func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.ItemOut
|
||||
// @Router /v1/items/{id} [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemUpdate() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemUpdate() server.HandlerFunc {
|
||||
return ctrl.handleItemsGeneral()
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) handleItemsGeneral() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) handleItemsGeneral() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
ID, err := ctrl.routeID(w, r)
|
||||
ID, err := ctrl.routeID(r)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
items, err := ctrl.svc.Items.GetOne(r.Context(), ctx.GID, ID)
|
||||
items, err := ctrl.repo.Items.GetOneByGroup(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get item")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusOK, items)
|
||||
return
|
||||
return server.Respond(w, http.StatusOK, items)
|
||||
case http.MethodDelete:
|
||||
err = ctrl.svc.Items.Delete(r.Context(), ctx.GID, ID)
|
||||
err = ctrl.repo.Items.DeleteByGroup(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to delete item")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
case http.MethodPut:
|
||||
body := repo.ItemUpdate{}
|
||||
if err := server.Decode(r, &body); err != nil {
|
||||
log.Err(err).Msg("failed to decode request body")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
body.ID = ID
|
||||
result, err := ctrl.svc.Items.Update(r.Context(), ctx.GID, body)
|
||||
result, err := ctrl.repo.Items.UpdateByGroup(r.Context(), ctx.GID, body)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to update item")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusOK, result)
|
||||
return server.Respond(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,29 +163,26 @@ func (ctrl *V1Controller) handleItemsGeneral() http.HandlerFunc {
|
||||
// @Param csv formData file true "Image to upload"
|
||||
// @Router /v1/items/import [Post]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemsImport() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleItemsImport() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
|
||||
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to parse multipart form")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
file, _, err := r.FormFile("csv")
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get file from form")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
reader := csv.NewReader(file)
|
||||
data, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to read csv")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
user := services.UseUserCtx(r.Context())
|
||||
@@ -221,10 +190,9 @@ func (ctrl *V1Controller) HandleItemsImport() http.HandlerFunc {
|
||||
_, err = ctrl.svc.Items.CsvImport(r.Context(), user.GroupID, data)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to import items")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -29,19 +28,19 @@ type (
|
||||
// @Param type formData string true "Type of file"
|
||||
// @Param name formData string true "name of the file including extension"
|
||||
// @Success 200 {object} repo.ItemOut
|
||||
// @Failure 422 {object} []server.ValidationError
|
||||
// @Failure 422 {object} server.ErrorResponse
|
||||
// @Router /v1/items/{id}/attachments [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleItemAttachmentCreate() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
err := r.ParseMultipartForm(ctrl.maxUploadSize << 20)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to parse multipart form")
|
||||
server.RespondError(w, http.StatusBadRequest, errors.New("failed to parse multipart form"))
|
||||
return
|
||||
return validate.NewRequestError(errors.New("failed to parse multipart form"), http.StatusBadRequest)
|
||||
|
||||
}
|
||||
|
||||
errs := make(server.ValidationErrors, 0)
|
||||
errs := validate.NewFieldErrors()
|
||||
|
||||
file, _, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
@@ -51,8 +50,7 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||
errs = errs.Append("file", "file is required")
|
||||
default:
|
||||
log.Err(err).Msg("failed to get file from form")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,9 +60,8 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||
errs = errs.Append("name", "name is required")
|
||||
}
|
||||
|
||||
if errs.HasErrors() {
|
||||
server.Respond(w, http.StatusUnprocessableEntity, errs)
|
||||
return
|
||||
if !errs.Nil() {
|
||||
return server.Respond(w, http.StatusUnprocessableEntity, errs)
|
||||
}
|
||||
|
||||
attachmentType := r.FormValue("type")
|
||||
@@ -72,9 +69,9 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||
attachmentType = attachment.TypeAttachment.String()
|
||||
}
|
||||
|
||||
id, err := ctrl.routeID(w, r)
|
||||
id, err := ctrl.routeID(r)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := services.NewContext(r.Context())
|
||||
@@ -89,11 +86,10 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to add attachment")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusCreated, item)
|
||||
return server.Respond(w, http.StatusCreated, item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,21 +102,21 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
|
||||
// @Success 200
|
||||
// @Router /v1/items/{id}/attachments/download [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemAttachmentDownload() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleItemAttachmentDownload() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
token := server.GetParam(r, "token", "")
|
||||
|
||||
doc, err := ctrl.svc.Items.AttachmentPath(r.Context(), token)
|
||||
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get attachment")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", doc.Title))
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
http.ServeFile(w, r, doc.Path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +129,7 @@ func (ctrl *V1Controller) HandleItemAttachmentDownload() http.HandlerFunc {
|
||||
// @Success 200 {object} ItemAttachmentToken
|
||||
// @Router /v1/items/{id}/attachments/{attachment_id} [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemAttachmentToken() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemAttachmentToken() server.HandlerFunc {
|
||||
return ctrl.handleItemAttachmentsHandler
|
||||
}
|
||||
|
||||
@@ -145,7 +141,7 @@ func (ctrl *V1Controller) HandleItemAttachmentToken() http.HandlerFunc {
|
||||
// @Success 204
|
||||
// @Router /v1/items/{id}/attachments/{attachment_id} [DELETE]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemAttachmentDelete() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemAttachmentDelete() server.HandlerFunc {
|
||||
return ctrl.handleItemAttachmentsHandler
|
||||
}
|
||||
|
||||
@@ -158,66 +154,60 @@ func (ctrl *V1Controller) HandleItemAttachmentDelete() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.ItemOut
|
||||
// @Router /v1/items/{id}/attachments/{attachment_id} [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleItemAttachmentUpdate() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleItemAttachmentUpdate() server.HandlerFunc {
|
||||
return ctrl.handleItemAttachmentsHandler
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ID, err := ctrl.routeID(w, r)
|
||||
func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r *http.Request) error {
|
||||
ID, err := ctrl.routeID(r)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
attachmentId, err := uuid.Parse(chi.URLParam(r, "attachment_id"))
|
||||
attachmentID, err := ctrl.routeUUID(r, "attachment_id")
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to parse attachment_id param")
|
||||
server.RespondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
switch r.Method {
|
||||
|
||||
// Token Handler
|
||||
case http.MethodGet:
|
||||
token, err := ctrl.svc.Items.AttachmentToken(ctx, ID, attachmentId)
|
||||
token, err := ctrl.svc.Items.AttachmentToken(ctx, ID, attachmentID)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case services.ErrNotFound:
|
||||
log.Err(err).
|
||||
Str("id", attachmentId.String()).
|
||||
Str("id", attachmentID.String()).
|
||||
Msg("failed to find attachment with id")
|
||||
|
||||
server.RespondError(w, http.StatusNotFound, err)
|
||||
return validate.NewRequestError(err, http.StatusNotFound)
|
||||
|
||||
case services.ErrFileNotFound:
|
||||
log.Err(err).
|
||||
Str("id", attachmentId.String()).
|
||||
Str("id", attachmentID.String()).
|
||||
Msg("failed to find file path for attachment with id")
|
||||
log.Warn().Msg("attachment with no file path removed from database")
|
||||
|
||||
server.RespondError(w, http.StatusNotFound, err)
|
||||
return validate.NewRequestError(err, http.StatusNotFound)
|
||||
|
||||
default:
|
||||
log.Err(err).Msg("failed to get attachment")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, ItemAttachmentToken{Token: token})
|
||||
return server.Respond(w, http.StatusOK, ItemAttachmentToken{Token: token})
|
||||
|
||||
// Delete Attachment Handler
|
||||
case http.MethodDelete:
|
||||
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentId)
|
||||
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to delete attachment")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
|
||||
// Update Attachment Handler
|
||||
case http.MethodPut:
|
||||
@@ -225,18 +215,18 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
|
||||
err = server.Decode(r, &attachment)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to decode attachment")
|
||||
server.RespondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
attachment.ID = attachmentId
|
||||
attachment.ID = attachmentID
|
||||
val, err := ctrl.svc.Items.AttachmentUpdate(ctx, ID, &attachment)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to delete attachment")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, val)
|
||||
return server.Respond(w, http.StatusOK, val)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -3,9 +3,10 @@ package v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -17,16 +18,15 @@ import (
|
||||
// @Success 200 {object} server.Results{items=[]repo.LabelOut}
|
||||
// @Router /v1/labels [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLabelsGetAll() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleLabelsGetAll() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
user := services.UseUserCtx(r.Context())
|
||||
labels, err := ctrl.svc.Labels.GetAll(r.Context(), user.GroupID)
|
||||
labels, err := ctrl.repo.Labels.GetAll(r.Context(), user.GroupID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error getting labels")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusOK, server.Results{Items: labels})
|
||||
return server.Respond(w, http.StatusOK, server.Results{Items: labels})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,24 +38,22 @@ func (ctrl *V1Controller) HandleLabelsGetAll() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.LabelSummary
|
||||
// @Router /v1/labels [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleLabelsCreate() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
createData := repo.LabelCreate{}
|
||||
if err := server.Decode(r, &createData); err != nil {
|
||||
log.Err(err).Msg("error decoding label create data")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
user := services.UseUserCtx(r.Context())
|
||||
label, err := ctrl.svc.Labels.Create(r.Context(), user.GroupID, createData)
|
||||
label, err := ctrl.repo.Labels.Create(r.Context(), user.GroupID, createData)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error creating label")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusCreated, label)
|
||||
return server.Respond(w, http.StatusCreated, label)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +65,7 @@ func (ctrl *V1Controller) HandleLabelsCreate() http.HandlerFunc {
|
||||
// @Success 204
|
||||
// @Router /v1/labels/{id} [DELETE]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleLabelDelete() server.HandlerFunc {
|
||||
return ctrl.handleLabelsGeneral()
|
||||
}
|
||||
|
||||
@@ -79,7 +77,7 @@ func (ctrl *V1Controller) HandleLabelDelete() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.LabelOut
|
||||
// @Router /v1/labels/{id} [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleLabelGet() server.HandlerFunc {
|
||||
return ctrl.handleLabelsGeneral()
|
||||
}
|
||||
|
||||
@@ -91,60 +89,55 @@ func (ctrl *V1Controller) HandleLabelGet() http.HandlerFunc {
|
||||
// @Success 200 {object} repo.LabelOut
|
||||
// @Router /v1/labels/{id} [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLabelUpdate() http.HandlerFunc {
|
||||
func (ctrl *V1Controller) HandleLabelUpdate() server.HandlerFunc {
|
||||
return ctrl.handleLabelsGeneral()
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) handleLabelsGeneral() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) handleLabelsGeneral() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
ID, err := ctrl.routeID(w, r)
|
||||
ID, err := ctrl.routeID(r)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
labels, err := ctrl.svc.Labels.Get(r.Context(), ctx.GID, ID)
|
||||
labels, err := ctrl.repo.Labels.GetOneByGroup(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
if ent.IsNotFound(err) {
|
||||
log.Err(err).
|
||||
Str("id", ID.String()).
|
||||
Msg("label not found")
|
||||
server.RespondError(w, http.StatusNotFound, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusNotFound)
|
||||
}
|
||||
log.Err(err).Msg("error getting label")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusOK, labels)
|
||||
return server.Respond(w, http.StatusOK, labels)
|
||||
|
||||
case http.MethodDelete:
|
||||
err = ctrl.svc.Labels.Delete(r.Context(), ctx.GID, ID)
|
||||
err = ctrl.repo.Labels.DeleteByGroup(ctx, ctx.GID, ID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error deleting label")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
|
||||
case http.MethodPut:
|
||||
body := repo.LabelUpdate{}
|
||||
if err := server.Decode(r, &body); err != nil {
|
||||
log.Err(err).Msg("error decoding label update data")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
body.ID = ID
|
||||
result, err := ctrl.svc.Labels.Update(r.Context(), ctx.GID, body)
|
||||
result, err := ctrl.repo.Labels.UpdateByGroup(ctx, ctx.GID, body)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error updating label")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
server.Respond(w, http.StatusOK, result)
|
||||
return server.Respond(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
156
backend/app/api/handlers/v1/v1_ctrl_locations.go
Normal file
156
backend/app/api/handlers/v1/v1_ctrl_locations.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// HandleLocationGetAll godoc
|
||||
// @Summary Get All Locations
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param filterChildren query bool false "Filter locations with parents"
|
||||
// @Success 200 {object} server.Results{items=[]repo.LocationOutCount}
|
||||
// @Router /v1/locations [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationGetAll() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
user := services.UseUserCtx(r.Context())
|
||||
|
||||
q := r.URL.Query()
|
||||
|
||||
filter := repo.LocationQuery{
|
||||
FilterChildren: queryBool(q.Get("filterChildren")),
|
||||
}
|
||||
|
||||
locations, err := ctrl.repo.Locations.GetAll(r.Context(), user.GroupID, filter)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get locations")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return server.Respond(w, http.StatusOK, server.Results{Items: locations})
|
||||
}
|
||||
}
|
||||
|
||||
// HandleLocationCreate godoc
|
||||
// @Summary Create a new location
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param payload body repo.LocationCreate true "Location Data"
|
||||
// @Success 200 {object} repo.LocationSummary
|
||||
// @Router /v1/locations [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationCreate() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
createData := repo.LocationCreate{}
|
||||
if err := server.Decode(r, &createData); err != nil {
|
||||
log.Err(err).Msg("failed to decode location create data")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
user := services.UseUserCtx(r.Context())
|
||||
location, err := ctrl.repo.Locations.Create(r.Context(), user.GroupID, createData)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create location")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return server.Respond(w, http.StatusCreated, location)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleLocationDelete godocs
|
||||
// @Summary deletes a location
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param id path string true "Location ID"
|
||||
// @Success 204
|
||||
// @Router /v1/locations/{id} [DELETE]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationDelete() server.HandlerFunc {
|
||||
return ctrl.handleLocationGeneral()
|
||||
}
|
||||
|
||||
// HandleLocationGet godocs
|
||||
// @Summary Gets a location and fields
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param id path string true "Location ID"
|
||||
// @Success 200 {object} repo.LocationOut
|
||||
// @Router /v1/locations/{id} [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationGet() server.HandlerFunc {
|
||||
return ctrl.handleLocationGeneral()
|
||||
}
|
||||
|
||||
// HandleLocationUpdate godocs
|
||||
// @Summary updates a location
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param id path string true "Location ID"
|
||||
// @Param payload body repo.LocationUpdate true "Location Data"
|
||||
// @Success 200 {object} repo.LocationOut
|
||||
// @Router /v1/locations/{id} [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationUpdate() server.HandlerFunc {
|
||||
return ctrl.handleLocationGeneral()
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) handleLocationGeneral() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
ID, err := ctrl.routeID(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
location, err := ctrl.repo.Locations.GetOneByGroup(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
l := log.Err(err).
|
||||
Str("ID", ID.String()).
|
||||
Str("GID", ctx.GID.String())
|
||||
|
||||
if ent.IsNotFound(err) {
|
||||
l.Msg("location not found")
|
||||
return validate.NewRequestError(err, http.StatusNotFound)
|
||||
}
|
||||
|
||||
l.Msg("failed to get location")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return server.Respond(w, http.StatusOK, location)
|
||||
case http.MethodPut:
|
||||
body := repo.LocationUpdate{}
|
||||
if err := server.Decode(r, &body); err != nil {
|
||||
log.Err(err).Msg("failed to decode location update data")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
body.ID = ID
|
||||
|
||||
result, err := ctrl.repo.Locations.UpdateOneByGroup(r.Context(), ctx.GID, ID, body)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to update location")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return server.Respond(w, http.StatusOK, result)
|
||||
case http.MethodDelete:
|
||||
err = ctrl.repo.Locations.DeleteByGroup(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to delete location")
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/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/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -17,29 +18,26 @@ import (
|
||||
// @Param payload body services.UserRegistration true "User Data"
|
||||
// @Success 204
|
||||
// @Router /v1/users/register [Post]
|
||||
func (ctrl *V1Controller) HandleUserRegistration() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleUserRegistration() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
regData := services.UserRegistration{}
|
||||
|
||||
if err := server.Decode(r, ®Data); err != nil {
|
||||
log.Err(err).Msg("failed to decode user registration data")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if !ctrl.allowRegistration && regData.GroupToken == "" {
|
||||
server.RespondError(w, http.StatusForbidden, nil)
|
||||
return
|
||||
return validate.NewRequestError(nil, http.StatusForbidden)
|
||||
}
|
||||
|
||||
_, err := ctrl.svc.User.RegisterUser(r.Context(), regData)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to register user")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,17 +48,16 @@ func (ctrl *V1Controller) HandleUserRegistration() http.HandlerFunc {
|
||||
// @Success 200 {object} server.Result{item=repo.UserOut}
|
||||
// @Router /v1/users/self [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleUserSelf() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleUserSelf() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
token := services.UseTokenCtx(r.Context())
|
||||
usr, err := ctrl.svc.User.GetSelf(r.Context(), token)
|
||||
if usr.ID == uuid.Nil || err != nil {
|
||||
log.Err(err).Msg("failed to get user")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, server.Wrap(usr))
|
||||
return server.Respond(w, http.StatusOK, server.Wrap(usr))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,24 +69,22 @@ func (ctrl *V1Controller) HandleUserSelf() http.HandlerFunc {
|
||||
// @Success 200 {object} server.Result{item=repo.UserUpdate}
|
||||
// @Router /v1/users/self [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleUserSelfUpdate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleUserSelfUpdate() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
updateData := repo.UserUpdate{}
|
||||
if err := server.Decode(r, &updateData); err != nil {
|
||||
log.Err(err).Msg("failed to decode user update data")
|
||||
server.RespondError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
actor := services.UseUserCtx(r.Context())
|
||||
newData, err := ctrl.svc.User.UpdateSelf(r.Context(), actor.ID, updateData)
|
||||
|
||||
if err != nil {
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, server.Wrap(newData))
|
||||
return server.Respond(w, http.StatusOK, server.Wrap(newData))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,20 +95,18 @@ func (ctrl *V1Controller) HandleUserSelfUpdate() http.HandlerFunc {
|
||||
// @Success 204
|
||||
// @Router /v1/users/self [DELETE]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleUserSelfDelete() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleUserSelfDelete() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
if ctrl.isDemo {
|
||||
server.RespondError(w, http.StatusForbidden, nil)
|
||||
return
|
||||
return validate.NewRequestError(nil, http.StatusForbidden)
|
||||
}
|
||||
|
||||
actor := services.UseUserCtx(r.Context())
|
||||
if err := ctrl.svc.User.DeleteSelf(r.Context(), actor.ID); err != nil {
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,11 +124,10 @@ type (
|
||||
// @Param payload body ChangePassword true "Password Payload"
|
||||
// @Router /v1/users/change-password [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleUserSelfChangePassword() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
func (ctrl *V1Controller) HandleUserSelfChangePassword() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
if ctrl.isDemo {
|
||||
server.RespondError(w, http.StatusForbidden, nil)
|
||||
return
|
||||
return validate.NewRequestError(nil, http.StatusForbidden)
|
||||
}
|
||||
|
||||
var cp ChangePassword
|
||||
@@ -148,10 +140,9 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() http.HandlerFunc {
|
||||
|
||||
ok := ctrl.svc.User.ChangePassword(ctx, cp.Current, cp.New)
|
||||
if !ok {
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
return server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/config"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/config"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -15,7 +15,7 @@ func (a *app) setupLogger() {
|
||||
// Logger Init
|
||||
// zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
if a.conf.Log.Format != config.LogFormatJSON {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).With().Caller().Logger()
|
||||
}
|
||||
|
||||
log.Level(getLevel(a.conf.Log.Level))
|
||||
|
||||
@@ -2,18 +2,20 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
atlas "ariga.io/atlas/sql/migrate"
|
||||
"entgo.io/ent/dialect/sql/schema"
|
||||
"github.com/hay-kot/homebox/backend/app/api/docs"
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/config"
|
||||
"github.com/hay-kot/homebox/backend/internal/migrations"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/app/api/static/docs"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/migrations"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/config"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/mid"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -113,17 +115,25 @@ func run(cfg *config.Config) error {
|
||||
app.services = services.New(app.repos)
|
||||
|
||||
// =========================================================================
|
||||
// Start Server
|
||||
// Start Server\
|
||||
logger := log.With().Caller().Logger()
|
||||
|
||||
mwLogger := mid.Logger(logger)
|
||||
if app.conf.Mode == config.ModeDevelopment {
|
||||
mwLogger = mid.SugarLogger(logger)
|
||||
}
|
||||
|
||||
app.server = server.NewServer(
|
||||
server.WithHost(app.conf.Web.Host),
|
||||
server.WithPort(app.conf.Web.Port),
|
||||
server.WithMiddleware(
|
||||
mwLogger,
|
||||
mid.Errors(logger),
|
||||
mid.Panic(app.conf.Mode == config.ModeDevelopment),
|
||||
),
|
||||
)
|
||||
|
||||
routes := app.newRouter(app.repos)
|
||||
|
||||
if app.conf.Mode != config.ModeDevelopment {
|
||||
app.logRoutes(routes)
|
||||
}
|
||||
app.mountRoutes(app.repos)
|
||||
|
||||
log.Info().Msgf("Starting HTTP Server on %s:%s", app.server.Host, app.server.Port)
|
||||
|
||||
@@ -153,5 +163,14 @@ func run(cfg *config.Config) error {
|
||||
app.SetupDemo()
|
||||
}
|
||||
|
||||
return app.server.Start(routes)
|
||||
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")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return app.server.Start()
|
||||
}
|
||||
|
||||
@@ -1,143 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/hay-kot/homebox/backend/internal/config"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (a *app) setGlobalMiddleware(r *chi.Mux) {
|
||||
// =========================================================================
|
||||
// Middleware
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
r.Use(mwStripTrailingSlash)
|
||||
|
||||
// Use struct logger in production for requests, but use
|
||||
// pretty console logger in development.
|
||||
if a.conf.Mode == config.ModeDevelopment {
|
||||
r.Use(a.mwSummaryLogger)
|
||||
} else {
|
||||
r.Use(a.mwStructLogger)
|
||||
}
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
// Set a timeout value on the request context (ctx), that will signal
|
||||
// through ctx.Done() that the request has timed out and further
|
||||
// processing should be stopped.
|
||||
r.Use(middleware.Timeout(60 * time.Second))
|
||||
}
|
||||
|
||||
// mwAuthToken is a middleware that will check the database for a stateful token
|
||||
// and attach it to the request context with the user, or return a 401 if it doesn't exist.
|
||||
func (a *app) mwAuthToken(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *app) mwAuthToken(next server.Handler) server.Handler {
|
||||
return server.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
||||
requestToken := r.Header.Get("Authorization")
|
||||
|
||||
if requestToken == "" {
|
||||
server.RespondUnauthorized(w)
|
||||
return
|
||||
return validate.NewRequestError(errors.New("Authorization header is required"), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
requestToken = strings.TrimPrefix(requestToken, "Bearer ")
|
||||
usr, err := a.services.User.GetSelf(r.Context(), requestToken)
|
||||
|
||||
// Check the database for the token
|
||||
|
||||
if err != nil {
|
||||
server.RespondUnauthorized(w)
|
||||
return
|
||||
return validate.NewRequestError(errors.New("Authorization header is required"), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
r = r.WithContext(services.SetUserCtx(r.Context(), &usr, requestToken))
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// mqStripTrailingSlash is a middleware that will strip trailing slashes from the request path.
|
||||
func mwStripTrailingSlash(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/")
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
type StatusRecorder struct {
|
||||
http.ResponseWriter
|
||||
Status int
|
||||
}
|
||||
|
||||
func (r *StatusRecorder) WriteHeader(status int) {
|
||||
r.Status = status
|
||||
r.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
|
||||
func (a *app) mwStructLogger(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
record := &StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
|
||||
next.ServeHTTP(record, r)
|
||||
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto)
|
||||
|
||||
log.Info().
|
||||
Str("id", middleware.GetReqID(r.Context())).
|
||||
Str("method", r.Method).
|
||||
Str("remote_addr", r.RemoteAddr).
|
||||
Int("status", record.Status).
|
||||
Msg(url)
|
||||
})
|
||||
}
|
||||
|
||||
func (a *app) mwSummaryLogger(next http.Handler) http.Handler {
|
||||
bold := func(s string) string { return "\033[1m" + s + "\033[0m" }
|
||||
orange := func(s string) string { return "\033[33m" + s + "\033[0m" }
|
||||
aqua := func(s string) string { return "\033[36m" + s + "\033[0m" }
|
||||
red := func(s string) string { return "\033[31m" + s + "\033[0m" }
|
||||
green := func(s string) string { return "\033[32m" + s + "\033[0m" }
|
||||
|
||||
fmtCode := func(code int) string {
|
||||
switch {
|
||||
case code >= 500:
|
||||
return red(fmt.Sprintf("%d", code))
|
||||
case code >= 400:
|
||||
return orange(fmt.Sprintf("%d", code))
|
||||
case code >= 300:
|
||||
return aqua(fmt.Sprintf("%d", code))
|
||||
default:
|
||||
return green(fmt.Sprintf("%d", code))
|
||||
}
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
record := &StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
|
||||
next.ServeHTTP(record, r) // Blocks until the next handler returns.
|
||||
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s://%s%s %s", scheme, r.Host, r.RequestURI, r.Proto)
|
||||
|
||||
log.Info().
|
||||
Msgf("%s %s %s",
|
||||
bold(orange(""+r.Method+"")),
|
||||
aqua(url),
|
||||
bold(fmtCode(record.Status)),
|
||||
)
|
||||
return next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
_ "github.com/hay-kot/homebox/backend/app/api/docs"
|
||||
v1 "github.com/hay-kot/homebox/backend/app/api/v1"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers"
|
||||
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1"
|
||||
_ "github.com/hay-kot/homebox/backend/app/api/static/docs"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware
|
||||
)
|
||||
|
||||
@@ -23,106 +23,90 @@ const prefix = "/api"
|
||||
var (
|
||||
ErrDir = errors.New("path is dir")
|
||||
|
||||
//go:embed all:public/*
|
||||
//go:embed all:static/public/*
|
||||
public embed.FS
|
||||
)
|
||||
|
||||
func (a *app) debugRouter() *http.ServeMux {
|
||||
dbg := http.NewServeMux()
|
||||
debughandlers.New(dbg)
|
||||
|
||||
return dbg
|
||||
}
|
||||
|
||||
// registerRoutes registers all the routes for the API
|
||||
func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux {
|
||||
func (a *app) mountRoutes(repos *repo.AllRepos) {
|
||||
registerMimes()
|
||||
|
||||
r := chi.NewRouter()
|
||||
a.setGlobalMiddleware(r)
|
||||
|
||||
r.Get("/swagger/*", httpSwagger.Handler(
|
||||
a.server.Get("/swagger/*", server.ToHandler(httpSwagger.Handler(
|
||||
httpSwagger.URL(fmt.Sprintf("%s://%s/swagger/doc.json", a.conf.Swagger.Scheme, a.conf.Swagger.Host)),
|
||||
))
|
||||
)))
|
||||
|
||||
// =========================================================================
|
||||
// API Version 1
|
||||
|
||||
v1Base := v1.BaseUrlFunc(prefix)
|
||||
v1Ctrl := v1.NewControllerV1(a.services,
|
||||
|
||||
v1Ctrl := v1.NewControllerV1(
|
||||
a.services,
|
||||
a.repos,
|
||||
v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize),
|
||||
v1.WithRegistration(a.conf.AllowRegistration),
|
||||
v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode
|
||||
)
|
||||
r.Get(v1Base("/status"), v1Ctrl.HandleBase(func() bool { return true }, v1.Build{
|
||||
|
||||
a.server.Get(v1Base("/status"), v1Ctrl.HandleBase(func() bool { return true }, v1.Build{
|
||||
Version: version,
|
||||
Commit: commit,
|
||||
BuildTime: buildTime,
|
||||
}))
|
||||
|
||||
r.Post(v1Base("/users/register"), v1Ctrl.HandleUserRegistration())
|
||||
r.Post(v1Base("/users/login"), v1Ctrl.HandleAuthLogin())
|
||||
a.server.Post(v1Base("/users/register"), v1Ctrl.HandleUserRegistration())
|
||||
a.server.Post(v1Base("/users/login"), v1Ctrl.HandleAuthLogin())
|
||||
|
||||
// Attachment download URl needs a `token` query param to be passed in the request.
|
||||
// and also needs to be outside of the `auth` middleware.
|
||||
r.Get(v1Base("/items/{id}/attachments/download"), v1Ctrl.HandleItemAttachmentDownload())
|
||||
a.server.Get(v1Base("/items/{id}/attachments/download"), v1Ctrl.HandleItemAttachmentDownload())
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(a.mwAuthToken)
|
||||
r.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf())
|
||||
r.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate())
|
||||
r.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete())
|
||||
r.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout())
|
||||
r.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh())
|
||||
r.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword())
|
||||
a.server.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate(), a.mwAuthToken)
|
||||
a.server.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete(), a.mwAuthToken)
|
||||
a.server.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout(), a.mwAuthToken)
|
||||
a.server.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword(), a.mwAuthToken)
|
||||
|
||||
r.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate())
|
||||
a.server.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate(), a.mwAuthToken)
|
||||
a.server.Get(v1Base("/groups/statistics"), v1Ctrl.HandleGroupStatistics(), a.mwAuthToken)
|
||||
|
||||
// TODO: I don't like /groups being the URL for users
|
||||
r.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet())
|
||||
r.Put(v1Base("/groups"), v1Ctrl.HandleGroupUpdate())
|
||||
// TODO: I don't like /groups being the URL for users
|
||||
a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/groups"), v1Ctrl.HandleGroupUpdate(), a.mwAuthToken)
|
||||
|
||||
r.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll())
|
||||
r.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate())
|
||||
r.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet())
|
||||
r.Put(v1Base("/locations/{id}"), v1Ctrl.HandleLocationUpdate())
|
||||
r.Delete(v1Base("/locations/{id}"), v1Ctrl.HandleLocationDelete())
|
||||
a.server.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll(), a.mwAuthToken)
|
||||
a.server.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate(), a.mwAuthToken)
|
||||
a.server.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/locations/{id}"), v1Ctrl.HandleLocationUpdate(), a.mwAuthToken)
|
||||
a.server.Delete(v1Base("/locations/{id}"), v1Ctrl.HandleLocationDelete(), a.mwAuthToken)
|
||||
|
||||
r.Get(v1Base("/labels"), v1Ctrl.HandleLabelsGetAll())
|
||||
r.Post(v1Base("/labels"), v1Ctrl.HandleLabelsCreate())
|
||||
r.Get(v1Base("/labels/{id}"), v1Ctrl.HandleLabelGet())
|
||||
r.Put(v1Base("/labels/{id}"), v1Ctrl.HandleLabelUpdate())
|
||||
r.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete())
|
||||
a.server.Get(v1Base("/labels"), v1Ctrl.HandleLabelsGetAll(), a.mwAuthToken)
|
||||
a.server.Post(v1Base("/labels"), v1Ctrl.HandleLabelsCreate(), a.mwAuthToken)
|
||||
a.server.Get(v1Base("/labels/{id}"), v1Ctrl.HandleLabelGet(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/labels/{id}"), v1Ctrl.HandleLabelUpdate(), a.mwAuthToken)
|
||||
a.server.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete(), a.mwAuthToken)
|
||||
|
||||
r.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll())
|
||||
r.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport())
|
||||
r.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate())
|
||||
r.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet())
|
||||
r.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate())
|
||||
r.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete())
|
||||
a.server.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll(), a.mwAuthToken)
|
||||
a.server.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport(), a.mwAuthToken)
|
||||
a.server.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate(), a.mwAuthToken)
|
||||
a.server.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate(), a.mwAuthToken)
|
||||
a.server.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete(), a.mwAuthToken)
|
||||
|
||||
r.Post(v1Base("/items/{id}/attachments"), v1Ctrl.HandleItemAttachmentCreate())
|
||||
r.Get(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentToken())
|
||||
r.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate())
|
||||
r.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete())
|
||||
})
|
||||
a.server.Post(v1Base("/items/{id}/attachments"), v1Ctrl.HandleItemAttachmentCreate(), a.mwAuthToken)
|
||||
a.server.Get(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentToken(), a.mwAuthToken)
|
||||
a.server.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate(), a.mwAuthToken)
|
||||
a.server.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete(), a.mwAuthToken)
|
||||
|
||||
r.NotFound(notFoundHandler())
|
||||
return r
|
||||
}
|
||||
|
||||
// logRoutes logs the routes of the server that are registered within Server.registerRoutes(). This is useful for debugging.
|
||||
// See https://github.com/go-chi/chi/issues/332 for details and inspiration.
|
||||
func (a *app) logRoutes(r *chi.Mux) {
|
||||
desiredSpaces := 10
|
||||
|
||||
walkFunc := func(method string, route string, handler http.Handler, middleware ...func(http.Handler) http.Handler) error {
|
||||
text := "[" + method + "]"
|
||||
|
||||
for len(text) < desiredSpaces {
|
||||
text = text + " "
|
||||
}
|
||||
|
||||
fmt.Printf("Registered Route: %s%s\n", text, route)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := chi.Walk(r, walkFunc); err != nil {
|
||||
fmt.Printf("Logging err: %s\n", err.Error())
|
||||
}
|
||||
a.server.NotFound(notFoundHandler())
|
||||
}
|
||||
|
||||
func registerMimes() {
|
||||
@@ -139,7 +123,7 @@ func registerMimes() {
|
||||
|
||||
// notFoundHandler perform the main logic around handling the internal SPA embed and ensuring that
|
||||
// the client side routing is handled correctly.
|
||||
func notFoundHandler() http.HandlerFunc {
|
||||
func notFoundHandler() server.HandlerFunc {
|
||||
tryRead := func(fs embed.FS, prefix, requestedPath string, w http.ResponseWriter) error {
|
||||
f, err := fs.Open(path.Join(prefix, requestedPath))
|
||||
if err != nil {
|
||||
@@ -158,17 +142,16 @@ func notFoundHandler() http.HandlerFunc {
|
||||
return err
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
err := tryRead(public, "public", r.URL.Path, w)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.Debug().
|
||||
Str("path", r.URL.Path).
|
||||
Msg("served from embed not found - serving index.html")
|
||||
err = tryRead(public, "public", "index.html", w)
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
err := tryRead(public, "static/public", r.URL.Path, w)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
// Fallback to the index.html file.
|
||||
// should succeed in all cases.
|
||||
err = tryRead(public, "static/public", "index.html", w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,30 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups/statistics": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Group"
|
||||
],
|
||||
"summary": "Get the current user's group",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.GroupStatistics"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -395,10 +419,7 @@ const docTemplate = `{
|
||||
"422": {
|
||||
"description": "Unprocessable Entity",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/server.ValidationError"
|
||||
}
|
||||
"$ref": "#/definitions/server.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -735,6 +756,14 @@ const docTemplate = `{
|
||||
"Locations"
|
||||
],
|
||||
"summary": "Get All Locations",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Filter locations with parents",
|
||||
"name": "filterChildren",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -845,6 +874,15 @@ const docTemplate = `{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Location Data",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.LocationUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1172,6 +1210,23 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.GroupStatistics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"totalItems": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalLabels": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalLocations": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalUsers": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.GroupUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1232,6 +1287,10 @@ const docTemplate = `{
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1264,12 +1323,21 @@ const docTemplate = `{
|
||||
"repo.ItemOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1277,7 +1345,6 @@ const docTemplate = `{
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Future",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemField"
|
||||
@@ -1301,6 +1368,8 @@ const docTemplate = `{
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"manufacturer": {
|
||||
@@ -1316,6 +1385,11 @@ const docTemplate = `{
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
},
|
||||
"purchaseFrom": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1361,6 +1435,9 @@ const docTemplate = `{
|
||||
"repo.ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1381,6 +1458,8 @@ const docTemplate = `{
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"name": {
|
||||
@@ -1397,6 +1476,9 @@ const docTemplate = `{
|
||||
"repo.ItemUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1439,6 +1521,11 @@ const docTemplate = `{
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true
|
||||
},
|
||||
"purchaseFrom": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1553,6 +1640,12 @@ const docTemplate = `{
|
||||
"repo.LocationOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1571,6 +1664,9 @@ const docTemplate = `{
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -1619,6 +1715,24 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LocationUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.PaginationResult-repo_ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1676,6 +1790,20 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"server.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"server.Result": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1695,17 +1823,6 @@ const docTemplate = `{
|
||||
"items": {}
|
||||
}
|
||||
},
|
||||
"server.ValidationError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services.UserRegistration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -105,6 +105,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups/statistics": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Group"
|
||||
],
|
||||
"summary": "Get the current user's group",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.GroupStatistics"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/items": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -387,10 +411,7 @@
|
||||
"422": {
|
||||
"description": "Unprocessable Entity",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/server.ValidationError"
|
||||
}
|
||||
"$ref": "#/definitions/server.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,6 +748,14 @@
|
||||
"Locations"
|
||||
],
|
||||
"summary": "Get All Locations",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "Filter locations with parents",
|
||||
"name": "filterChildren",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -837,6 +866,15 @@
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Location Data",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/repo.LocationUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1164,6 +1202,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.GroupStatistics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"totalItems": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalLabels": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalLocations": {
|
||||
"type": "integer"
|
||||
},
|
||||
"totalUsers": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.GroupUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1224,6 +1279,10 @@
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1256,12 +1315,21 @@
|
||||
"repo.ItemOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"attachments": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1269,7 +1337,6 @@
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Future",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemField"
|
||||
@@ -1293,6 +1360,8 @@
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"manufacturer": {
|
||||
@@ -1308,6 +1377,11 @@
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
},
|
||||
"purchaseFrom": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1353,6 +1427,9 @@
|
||||
"repo.ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1373,6 +1450,8 @@
|
||||
},
|
||||
"location": {
|
||||
"description": "Edges",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true,
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"name": {
|
||||
@@ -1389,6 +1468,9 @@
|
||||
"repo.ItemUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1431,6 +1513,11 @@
|
||||
"description": "Extras",
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true,
|
||||
"x-omitempty": true
|
||||
},
|
||||
"purchaseFrom": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1545,6 +1632,12 @@
|
||||
"repo.LocationOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1563,6 +1656,9 @@
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parent": {
|
||||
"$ref": "#/definitions/repo.LocationSummary"
|
||||
},
|
||||
"updatedAt": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -1611,6 +1707,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.LocationUpdate": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"type": "string",
|
||||
"x-nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.PaginationResult-repo_ItemSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1668,6 +1782,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"server.ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"server.Result": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1687,17 +1815,6 @@
|
||||
"items": {}
|
||||
}
|
||||
},
|
||||
"server.ValidationError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services.UserRegistration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -22,6 +22,17 @@ definitions:
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
repo.GroupStatistics:
|
||||
properties:
|
||||
totalItems:
|
||||
type: integer
|
||||
totalLabels:
|
||||
type: integer
|
||||
totalLocations:
|
||||
type: integer
|
||||
totalUsers:
|
||||
type: integer
|
||||
type: object
|
||||
repo.GroupUpdate:
|
||||
properties:
|
||||
currency:
|
||||
@@ -62,6 +73,9 @@ definitions:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
parentId:
|
||||
type: string
|
||||
x-nullable: true
|
||||
type: object
|
||||
repo.ItemField:
|
||||
properties:
|
||||
@@ -82,16 +96,21 @@ definitions:
|
||||
type: object
|
||||
repo.ItemOut:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
attachments:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemAttachment'
|
||||
type: array
|
||||
children:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
fields:
|
||||
description: Future
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemField'
|
||||
type: array
|
||||
@@ -109,6 +128,8 @@ definitions:
|
||||
location:
|
||||
$ref: '#/definitions/repo.LocationSummary'
|
||||
description: Edges
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
manufacturer:
|
||||
type: string
|
||||
modelNumber:
|
||||
@@ -118,6 +139,10 @@ definitions:
|
||||
notes:
|
||||
description: Extras
|
||||
type: string
|
||||
parent:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
purchaseFrom:
|
||||
type: string
|
||||
purchasePrice:
|
||||
@@ -149,6 +174,8 @@ definitions:
|
||||
type: object
|
||||
repo.ItemSummary:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
@@ -164,6 +191,8 @@ definitions:
|
||||
location:
|
||||
$ref: '#/definitions/repo.LocationSummary'
|
||||
description: Edges
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
name:
|
||||
type: string
|
||||
quantity:
|
||||
@@ -173,6 +202,8 @@ definitions:
|
||||
type: object
|
||||
repo.ItemUpdate:
|
||||
properties:
|
||||
archived:
|
||||
type: boolean
|
||||
description:
|
||||
type: string
|
||||
fields:
|
||||
@@ -202,6 +233,10 @@ definitions:
|
||||
notes:
|
||||
description: Extras
|
||||
type: string
|
||||
parentId:
|
||||
type: string
|
||||
x-nullable: true
|
||||
x-omitempty: true
|
||||
purchaseFrom:
|
||||
type: string
|
||||
purchasePrice:
|
||||
@@ -278,6 +313,10 @@ definitions:
|
||||
type: object
|
||||
repo.LocationOut:
|
||||
properties:
|
||||
children:
|
||||
items:
|
||||
$ref: '#/definitions/repo.LocationSummary'
|
||||
type: array
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
@@ -290,6 +329,8 @@ definitions:
|
||||
type: array
|
||||
name:
|
||||
type: string
|
||||
parent:
|
||||
$ref: '#/definitions/repo.LocationSummary'
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
@@ -321,6 +362,18 @@ definitions:
|
||||
updatedAt:
|
||||
type: string
|
||||
type: object
|
||||
repo.LocationUpdate:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
parentId:
|
||||
type: string
|
||||
x-nullable: true
|
||||
type: object
|
||||
repo.PaginationResult-repo_ItemSummary:
|
||||
properties:
|
||||
items:
|
||||
@@ -358,6 +411,15 @@ definitions:
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
server.ErrorResponse:
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
fields:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
server.Result:
|
||||
properties:
|
||||
details: {}
|
||||
@@ -371,13 +433,6 @@ definitions:
|
||||
properties:
|
||||
items: {}
|
||||
type: object
|
||||
server.ValidationError:
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
type: object
|
||||
services.UserRegistration:
|
||||
properties:
|
||||
email:
|
||||
@@ -516,6 +571,20 @@ paths:
|
||||
summary: Get the current user
|
||||
tags:
|
||||
- Group
|
||||
/v1/groups/statistics:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/repo.GroupStatistics'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get the current user's group
|
||||
tags:
|
||||
- Group
|
||||
/v1/items:
|
||||
get:
|
||||
parameters:
|
||||
@@ -672,9 +741,7 @@ paths:
|
||||
"422":
|
||||
description: Unprocessable Entity
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/server.ValidationError'
|
||||
type: array
|
||||
$ref: '#/definitions/server.ErrorResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: imports items into the database
|
||||
@@ -893,6 +960,11 @@ paths:
|
||||
- Labels
|
||||
/v1/locations:
|
||||
get:
|
||||
parameters:
|
||||
- description: Filter locations with parents
|
||||
in: query
|
||||
name: filterChildren
|
||||
type: boolean
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@@ -976,6 +1048,12 @@ paths:
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Location Data
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/repo.LocationUpdate'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
@@ -1,21 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (ctrl *V1Controller) routeID(w http.ResponseWriter, r *http.Request) (uuid.UUID, error) {
|
||||
ID, err := uuid.Parse(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to parse id")
|
||||
server.RespondError(w, http.StatusBadRequest, err)
|
||||
return uuid.Nil, err
|
||||
}
|
||||
|
||||
return ID, nil
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/services"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// HandleLocationGetAll godoc
|
||||
// @Summary Get All Locations
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Success 200 {object} server.Results{items=[]repo.LocationOutCount}
|
||||
// @Router /v1/locations [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationGetAll() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
user := services.UseUserCtx(r.Context())
|
||||
locations, err := ctrl.svc.Location.GetAll(r.Context(), user.GroupID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to get locations")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusOK, server.Results{Items: locations})
|
||||
}
|
||||
}
|
||||
|
||||
// HandleLocationCreate godoc
|
||||
// @Summary Create a new location
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param payload body repo.LocationCreate true "Location Data"
|
||||
// @Success 200 {object} repo.LocationSummary
|
||||
// @Router /v1/locations [POST]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
createData := repo.LocationCreate{}
|
||||
if err := server.Decode(r, &createData); err != nil {
|
||||
log.Err(err).Msg("failed to decode location create data")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
user := services.UseUserCtx(r.Context())
|
||||
location, err := ctrl.svc.Location.Create(r.Context(), user.GroupID, createData)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to create location")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
}
|
||||
|
||||
server.Respond(w, http.StatusCreated, location)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleLocationDelete godocs
|
||||
// @Summary deletes a location
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param id path string true "Location ID"
|
||||
// @Success 204
|
||||
// @Router /v1/locations/{id} [DELETE]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc {
|
||||
return ctrl.handleLocationGeneral()
|
||||
}
|
||||
|
||||
// HandleLocationGet godocs
|
||||
// @Summary Gets a location and fields
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param id path string true "Location ID"
|
||||
// @Success 200 {object} repo.LocationOut
|
||||
// @Router /v1/locations/{id} [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc {
|
||||
return ctrl.handleLocationGeneral()
|
||||
}
|
||||
|
||||
// HandleLocationUpdate godocs
|
||||
// @Summary updates a location
|
||||
// @Tags Locations
|
||||
// @Produce json
|
||||
// @Param id path string true "Location ID"
|
||||
// @Success 200 {object} repo.LocationOut
|
||||
// @Router /v1/locations/{id} [PUT]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc {
|
||||
return ctrl.handleLocationGeneral()
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) handleLocationGeneral() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := services.NewContext(r.Context())
|
||||
ID, err := ctrl.routeID(w, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
location, err := ctrl.svc.Location.GetOne(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
l := log.Err(err).
|
||||
Str("ID", ID.String()).
|
||||
Str("GID", ctx.GID.String())
|
||||
|
||||
if ent.IsNotFound(err) {
|
||||
l.Msg("location not found")
|
||||
server.RespondError(w, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
l.Msg("failed to get location")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
}
|
||||
server.Respond(w, http.StatusOK, location)
|
||||
case http.MethodPut:
|
||||
body := repo.LocationUpdate{}
|
||||
if err := server.Decode(r, &body); err != nil {
|
||||
log.Err(err).Msg("failed to decode location update data")
|
||||
server.RespondError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
body.ID = ID
|
||||
|
||||
result, err := ctrl.svc.Location.Update(r.Context(), ctx.GID, body)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to update location")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
}
|
||||
server.Respond(w, http.StatusOK, result)
|
||||
case http.MethodDelete:
|
||||
err = ctrl.svc.Location.Delete(r.Context(), ctx.GID, ID)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("failed to delete location")
|
||||
server.RespondServerError(w)
|
||||
return
|
||||
}
|
||||
server.Respond(w, http.StatusNoContent, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent/migrate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
|
||||
|
||||
atlas "ariga.io/atlas/sql/migrate"
|
||||
_ "ariga.io/atlas/sql/sqlite"
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
// Create a local migration directory able to understand Atlas migration file format for replay.
|
||||
dir, err := atlas.NewLocalDir("internal/migrations/migrations")
|
||||
dir, err := atlas.NewLocalDir("internal/data/migrations/migrations")
|
||||
if err != nil {
|
||||
log.Fatalf("failed creating atlas migration directory: %v", err)
|
||||
}
|
||||
@@ -7,10 +7,11 @@ require (
|
||||
entgo.io/ent v0.11.3
|
||||
github.com/ardanlabs/conf/v2 v2.2.0
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-playground/validator/v10 v10.11.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/mattn/go-sqlite3 v1.14.15
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/swaggo/http-swagger v1.3.3
|
||||
github.com/swaggo/swag v1.8.7
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
@@ -26,10 +27,12 @@ require (
|
||||
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-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.14.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // 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.16 // indirect
|
||||
|
||||
@@ -31,6 +31,14 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -43,6 +51,7 @@ github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0z
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -50,6 +59,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
@@ -62,17 +73,19 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
|
||||
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
@@ -81,11 +94,14 @@ github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowNo2MY5p6xzqtJ/wQ7LZynSvGaY=
|
||||
github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
|
||||
github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc=
|
||||
@@ -94,16 +110,20 @@ github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
|
||||
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
||||
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
|
||||
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI=
|
||||
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
|
||||
@@ -119,11 +139,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
24
backend/internal/core/services/all.go
Normal file
24
backend/internal/core/services/all.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package services
|
||||
|
||||
import "github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
|
||||
type AllServices struct {
|
||||
User *UserService
|
||||
Group *GroupService
|
||||
Items *ItemService
|
||||
}
|
||||
|
||||
func New(repos *repo.AllRepos) *AllServices {
|
||||
if repos == nil {
|
||||
panic("repos cannot be nil")
|
||||
}
|
||||
|
||||
return &AllServices{
|
||||
User: &UserService{repos},
|
||||
Group: &GroupService{repos},
|
||||
Items: &ItemService{
|
||||
repo: repos,
|
||||
at: attachmentTokens{},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
type contextKeys struct {
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/faker"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
@@ -2,10 +2,9 @@ package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/hasher"
|
||||
)
|
||||
|
||||
@@ -13,10 +12,6 @@ type GroupService struct {
|
||||
repos *repo.AllRepos
|
||||
}
|
||||
|
||||
func (svc *GroupService) Get(ctx Context) (repo.Group, error) {
|
||||
return svc.repos.Groups.GroupByID(ctx.Context, ctx.GID)
|
||||
}
|
||||
|
||||
func (svc *GroupService) UpdateGroup(ctx Context, data repo.GroupUpdate) (repo.Group, error) {
|
||||
if data.Name == "" {
|
||||
data.Name = ctx.User.GroupName
|
||||
@@ -26,8 +21,6 @@ func (svc *GroupService) UpdateGroup(ctx Context, data repo.GroupUpdate) (repo.G
|
||||
return repo.Group{}, errors.New("currency cannot be empty")
|
||||
}
|
||||
|
||||
data.Currency = strings.ToLower(data.Currency)
|
||||
|
||||
return svc.repos.Groups.GroupUpdate(ctx.Context, ctx.GID, data)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -23,31 +23,7 @@ type ItemService struct {
|
||||
at attachmentTokens
|
||||
}
|
||||
|
||||
func (svc *ItemService) GetOne(ctx context.Context, gid uuid.UUID, id uuid.UUID) (repo.ItemOut, error) {
|
||||
return svc.repo.Items.GetOneByGroup(ctx, gid, id)
|
||||
}
|
||||
|
||||
func (svc *ItemService) Query(ctx Context, q repo.ItemQuery) (repo.PaginationResult[repo.ItemSummary], error) {
|
||||
return svc.repo.Items.QueryByGroup(ctx, ctx.GID, q)
|
||||
}
|
||||
|
||||
func (svc *ItemService) GetAll(ctx context.Context, gid uuid.UUID) ([]repo.ItemSummary, error) {
|
||||
return svc.repo.Items.GetAll(ctx, gid)
|
||||
}
|
||||
|
||||
func (svc *ItemService) Create(ctx context.Context, gid uuid.UUID, data repo.ItemCreate) (repo.ItemOut, error) {
|
||||
return svc.repo.Items.Create(ctx, gid, data)
|
||||
}
|
||||
|
||||
func (svc *ItemService) Delete(ctx context.Context, gid uuid.UUID, id uuid.UUID) error {
|
||||
return svc.repo.Items.DeleteByGroup(ctx, gid, id)
|
||||
}
|
||||
|
||||
func (svc *ItemService) Update(ctx context.Context, gid uuid.UUID, data repo.ItemUpdate) (repo.ItemOut, error) {
|
||||
return svc.repo.Items.UpdateByGroup(ctx, gid, data)
|
||||
}
|
||||
|
||||
func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]string) (int, error) {
|
||||
func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data [][]string) (int, error) {
|
||||
loaded := []csvRow{}
|
||||
|
||||
// Skip first row
|
||||
@@ -90,7 +66,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
|
||||
// Bootstrap the locations and labels so we can reuse the created IDs for the items
|
||||
locations := map[string]uuid.UUID{}
|
||||
existingLocation, err := svc.repo.Locations.GetAll(ctx, gid)
|
||||
existingLocation, err := svc.repo.Locations.GetAll(ctx, GID, repo.LocationQuery{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -99,7 +75,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
}
|
||||
|
||||
labels := map[string]uuid.UUID{}
|
||||
existingLabels, err := svc.repo.Labels.GetAll(ctx, gid)
|
||||
existingLabels, err := svc.repo.Labels.GetAll(ctx, GID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -111,7 +87,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
|
||||
// Locations
|
||||
if _, exists := locations[row.Location]; !exists {
|
||||
result, err := svc.repo.Locations.Create(ctx, gid, repo.LocationCreate{
|
||||
result, err := svc.repo.Locations.Create(ctx, GID, repo.LocationCreate{
|
||||
Name: row.Location,
|
||||
Description: "",
|
||||
})
|
||||
@@ -127,7 +103,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
if _, exists := labels[label]; exists {
|
||||
continue
|
||||
}
|
||||
result, err := svc.repo.Labels.Create(ctx, gid, repo.LabelCreate{
|
||||
result, err := svc.repo.Labels.Create(ctx, GID, repo.LabelCreate{
|
||||
Name: label,
|
||||
Description: "",
|
||||
})
|
||||
@@ -143,7 +119,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
for _, row := range loaded {
|
||||
// Check Import Ref
|
||||
if row.Item.ImportRef != "" {
|
||||
exists, err := svc.repo.Items.CheckRef(ctx, gid, row.Item.ImportRef)
|
||||
exists, err := svc.repo.Items.CheckRef(ctx, GID, row.Item.ImportRef)
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
@@ -163,7 +139,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
Str("location", row.Location).
|
||||
Msgf("Creating Item: %s", row.Item.Name)
|
||||
|
||||
result, err := svc.repo.Items.Create(ctx, gid, repo.ItemCreate{
|
||||
result, err := svc.repo.Items.Create(ctx, GID, repo.ItemCreate{
|
||||
ImportRef: row.Item.ImportRef,
|
||||
Name: row.Item.Name,
|
||||
Description: row.Item.Description,
|
||||
@@ -176,7 +152,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
|
||||
}
|
||||
|
||||
// Update the item with the rest of the data
|
||||
_, err = svc.repo.Items.UpdateByGroup(ctx, gid, repo.ItemUpdate{
|
||||
_, err = svc.repo.Items.UpdateByGroup(ctx, GID, repo.ItemUpdate{
|
||||
// Edges
|
||||
LocationID: locationID,
|
||||
LabelIDs: labelIDs,
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/hasher"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -91,7 +91,7 @@ func (svc *ItemService) AttachmentUpdate(ctx Context, itemId uuid.UUID, data *re
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
|
||||
return svc.GetOne(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
|
||||
@@ -118,7 +118,7 @@ func (svc *ItemService) AttachmentAdd(ctx Context, itemId uuid.UUID, filename st
|
||||
return repo.ItemOut{}, err
|
||||
}
|
||||
|
||||
return svc.GetOne(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 {
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestItemService_AddAttachment(t *testing.T) {
|
||||
filepath: temp,
|
||||
}
|
||||
|
||||
loc, err := tSvc.Location.Create(context.Background(), tGroup.ID, repo.LocationCreate{
|
||||
loc, err := tRepos.Locations.Create(context.Background(), tGroup.ID, repo.LocationCreate{
|
||||
Description: "test",
|
||||
Name: "test",
|
||||
})
|
||||
@@ -32,7 +32,7 @@ func TestItemService_AddAttachment(t *testing.T) {
|
||||
LocationID: loc.ID,
|
||||
}
|
||||
|
||||
itm, err := svc.Create(context.Background(), tGroup.ID, itmC)
|
||||
itm, err := svc.repo.Items.Create(context.Background(), tGroup.ID, itmC)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, itm)
|
||||
t.Cleanup(func() {
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
var ErrInvalidCsv = errors.New("invalid csv")
|
||||
@@ -3,18 +3,22 @@ package services
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const CSV_DATA = `
|
||||
Import Ref,Location,Labels,Quantity,Name,Description,Insured,Serial Number,Mode Number,Manufacturer,Notes,Purchase From,Purchased Price,Purchased Time,Lifetime Warranty,Warranty Expires,Warranty Details,Sold To,Sold Price,Sold Time,Sold Notes
|
||||
A,Garage,IOT;Home Assistant; Z-Wave,1,Zooz Universal Relay ZEN17,"Zooz 700 Series Z-Wave Universal Relay ZEN17 for Awnings, Garage Doors, Sprinklers, and More | 2 NO-C-NC Relays (20A, 10A) | Signal Repeater | Hub Required (Compatible with SmartThings and Hubitat)",,,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
|
||||
B,Living Room,IOT;Home Assistant; Z-Wave,1,Zooz Motion Sensor,"Zooz Z-Wave Plus S2 Motion Sensor ZSE18 with Magnetic Mount, Works with Vera and SmartThings",,,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,,,,,,
|
||||
C,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,,,,,,,
|
||||
D,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,,,,,,,
|
||||
E,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,,,,,,,
|
||||
F,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,,,,,,,`
|
||||
A,Garage,IOT;Home Assistant; Z-Wave,1,Zooz Universal Relay ZEN17,Description 1,TRUE,,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,10/13/2021,,,,10/13/2021,
|
||||
B,Living Room,IOT;Home Assistant; Z-Wave,1,Zooz Motion Sensor,Description 2,FALSE,,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,10/15/2021,,,,10/15/2021,
|
||||
C,Office,IOT;Home Assistant; Z-Wave,1,Zooz 110v Power Switch,Description 3,TRUE,,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,10/13/2021,,,,10/13/2021,
|
||||
D,Downstairs,IOT;Home Assistant; Z-Wave,1,Ecolink Z-Wave PIR Motion Sensor,Description 4,FALSE,,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,10/21/2020,,,,10/21/2020,
|
||||
E,Entry,IOT;Home Assistant; Z-Wave,1,Yale Security Touchscreen Deadbolt,Description 5,TRUE,,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,10/14/2020,,,,10/14/2020,
|
||||
F,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,Description 6,FALSE,,39351,Honeywell,,Amazon,65.98,09/30/2020,,09/30/2020,,,,09/30/2020,`
|
||||
|
||||
func loadcsv() [][]string {
|
||||
reader := csv.NewReader(bytes.NewBuffer([]byte(CSV_DATA)))
|
||||
@@ -27,6 +31,33 @@ func loadcsv() [][]string {
|
||||
return records
|
||||
}
|
||||
|
||||
func Test_CorrectDateParsing(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := []time.Time{
|
||||
time.Date(2021, 10, 13, 0, 0, 0, 0, time.UTC),
|
||||
time.Date(2021, 10, 15, 0, 0, 0, 0, time.UTC),
|
||||
time.Date(2021, 10, 13, 0, 0, 0, 0, time.UTC),
|
||||
time.Date(2020, 10, 21, 0, 0, 0, 0, time.UTC),
|
||||
time.Date(2020, 10, 14, 0, 0, 0, 0, time.UTC),
|
||||
time.Date(2020, 9, 30, 0, 0, 0, 0, time.UTC),
|
||||
}
|
||||
|
||||
records := loadcsv()
|
||||
|
||||
for i, record := range records {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
entity := newCsvRow(record)
|
||||
expected := expected[i-1]
|
||||
|
||||
assert.Equal(t, expected, entity.Item.PurchaseTime, fmt.Sprintf("Failed on row %d", i))
|
||||
assert.Equal(t, expected, entity.Item.WarrantyExpires, fmt.Sprintf("Failed on row %d", i))
|
||||
assert.Equal(t, expected, entity.Item.SoldTime, fmt.Sprintf("Failed on row %d", i))
|
||||
}
|
||||
}
|
||||
|
||||
func Test_csvRow_getLabels(t *testing.T) {
|
||||
type fields struct {
|
||||
LabelStr string
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -22,7 +23,7 @@ func TestItemService_CsvImport(t *testing.T) {
|
||||
assert.Equal(t, 0, count)
|
||||
assert.NoError(t, err)
|
||||
|
||||
items, err := svc.GetAll(context.Background(), tGroup.ID)
|
||||
items, err := svc.repo.Items.GetAll(context.Background(), tGroup.ID)
|
||||
assert.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
for _, item := range items {
|
||||
@@ -38,22 +39,14 @@ func TestItemService_CsvImport(t *testing.T) {
|
||||
dataCsv = append(dataCsv, newCsvRow(item))
|
||||
}
|
||||
|
||||
locationService := &LocationService{
|
||||
repos: tRepos,
|
||||
}
|
||||
|
||||
LabelService := &LabelService{
|
||||
repos: tRepos,
|
||||
}
|
||||
|
||||
allLocation, err := locationService.GetAll(context.Background(), tGroup.ID)
|
||||
allLocation, err := tRepos.Locations.GetAll(context.Background(), tGroup.ID, repo.LocationQuery{})
|
||||
assert.NoError(t, err)
|
||||
locNames := []string{}
|
||||
for _, loc := range allLocation {
|
||||
locNames = append(locNames, loc.Name)
|
||||
}
|
||||
|
||||
allLabels, err := LabelService.GetAll(context.Background(), tGroup.ID)
|
||||
allLabels, err := tRepos.Labels.GetAll(context.Background(), tGroup.ID)
|
||||
assert.NoError(t, err)
|
||||
labelNames := []string{}
|
||||
for _, label := range allLabels {
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/pkgs/hasher"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/hay-kot/homebox/backend/internal/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
func defaultLocations() []repo.LocationCreate {
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
)
|
||||
|
||||
// Attachment is the model entity for the Attachment schema.
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
)
|
||||
|
||||
// AttachmentCreate is the builder for creating a Attachment entity.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AttachmentDelete is the builder for deleting a Attachment entity.
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AttachmentQuery is the builder for querying Attachment entities.
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AttachmentUpdate is the builder for updating Attachment entities.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokens is the model entity for the AuthTokens schema.
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokensCreate is the builder for creating a AuthTokens entity.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// AuthTokensDelete is the builder for deleting a AuthTokens entity.
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokensQuery is the builder for querying AuthTokens entities.
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// AuthTokensUpdate is the builder for updating AuthTokens entities.
|
||||
@@ -9,19 +9,19 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/migrate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/itemfield"
|
||||
"github.com/hay-kot/homebox/backend/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/itemfield"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
|
||||
"entgo.io/ent/dialect"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
@@ -1043,6 +1043,38 @@ func (c *ItemClient) GetX(ctx context.Context, id uuid.UUID) *Item {
|
||||
return obj
|
||||
}
|
||||
|
||||
// QueryParent queries the parent edge of a Item.
|
||||
func (c *ItemClient) QueryParent(i *Item) *ItemQuery {
|
||||
query := &ItemQuery{config: c.config}
|
||||
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := i.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(item.Table, item.FieldID, id),
|
||||
sqlgraph.To(item.Table, item.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, item.ParentTable, item.ParentColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(i.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryChildren queries the children edge of a Item.
|
||||
func (c *ItemClient) QueryChildren(i *Item) *ItemQuery {
|
||||
query := &ItemQuery{config: c.config}
|
||||
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := i.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(item.Table, item.FieldID, id),
|
||||
sqlgraph.To(item.Table, item.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, item.ChildrenTable, item.ChildrenColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(i.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryGroup queries the group edge of a Item.
|
||||
func (c *ItemClient) QueryGroup(i *Item) *GroupQuery {
|
||||
query := &GroupQuery{config: c.config}
|
||||
@@ -1441,6 +1473,38 @@ func (c *LocationClient) GetX(ctx context.Context, id uuid.UUID) *Location {
|
||||
return obj
|
||||
}
|
||||
|
||||
// QueryParent queries the parent edge of a Location.
|
||||
func (c *LocationClient) QueryParent(l *Location) *LocationQuery {
|
||||
query := &LocationQuery{config: c.config}
|
||||
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := l.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(location.Table, location.FieldID, id),
|
||||
sqlgraph.To(location.Table, location.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.M2O, true, location.ParentTable, location.ParentColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(l.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryChildren queries the children edge of a Location.
|
||||
func (c *LocationClient) QueryChildren(l *Location) *LocationQuery {
|
||||
query := &LocationQuery{config: c.config}
|
||||
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) {
|
||||
id := l.ID
|
||||
step := sqlgraph.NewStep(
|
||||
sqlgraph.From(location.Table, location.FieldID, id),
|
||||
sqlgraph.To(location.Table, location.FieldID),
|
||||
sqlgraph.Edge(sqlgraph.O2M, false, location.ChildrenTable, location.ChildrenColumn),
|
||||
)
|
||||
fromV = sqlgraph.Neighbors(l.driver.Dialect(), step)
|
||||
return fromV, nil
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// QueryGroup queries the group edge of a Location.
|
||||
func (c *LocationClient) QueryGroup(l *Location) *GroupQuery {
|
||||
query := &GroupQuery{config: c.config}
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
)
|
||||
|
||||
// Document is the model entity for the Document schema.
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
)
|
||||
|
||||
// DocumentCreate is the builder for creating a Document entity.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// DocumentDelete is the builder for deleting a Document entity.
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// DocumentQuery is the builder for querying Document entities.
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// DocumentUpdate is the builder for updating Document entities.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
)
|
||||
|
||||
// DocumentToken is the model entity for the DocumentToken schema.
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
)
|
||||
|
||||
// DocumentTokenCreate is the builder for creating a DocumentToken entity.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// DocumentTokenDelete is the builder for deleting a DocumentToken entity.
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// DocumentTokenQuery is the builder for querying DocumentToken entities.
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// DocumentTokenUpdate is the builder for updating DocumentToken entities.
|
||||
@@ -10,17 +10,17 @@ import (
|
||||
"entgo.io/ent"
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/hay-kot/homebox/backend/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/itemfield"
|
||||
"github.com/hay-kot/homebox/backend/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/documenttoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/itemfield"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// ent aliases to avoid import conflicts in user's code.
|
||||
@@ -5,12 +5,12 @@ package enttest
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
// required by schema hooks.
|
||||
_ "github.com/hay-kot/homebox/backend/ent/runtime"
|
||||
_ "github.com/hay-kot/homebox/backend/internal/data/ent/runtime"
|
||||
|
||||
"entgo.io/ent/dialect/sql/schema"
|
||||
"github.com/hay-kot/homebox/backend/ent/migrate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
)
|
||||
|
||||
// Group is the model entity for the Group schema.
|
||||
@@ -124,6 +124,11 @@ const (
|
||||
CurrencyEur Currency = "eur"
|
||||
CurrencyGbp Currency = "gbp"
|
||||
CurrencyJpy Currency = "jpy"
|
||||
CurrencyZar Currency = "zar"
|
||||
CurrencyAud Currency = "aud"
|
||||
CurrencyNok Currency = "nok"
|
||||
CurrencySek Currency = "sek"
|
||||
CurrencyDkk Currency = "dkk"
|
||||
)
|
||||
|
||||
func (c Currency) String() string {
|
||||
@@ -133,7 +138,7 @@ func (c Currency) String() string {
|
||||
// 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:
|
||||
case CurrencyUsd, CurrencyEur, CurrencyGbp, CurrencyJpy, CurrencyZar, CurrencyAud, CurrencyNok, CurrencySek, CurrencyDkk:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("group: invalid enum value for currency field: %q", c)
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -11,13 +11,13 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// GroupCreate is the builder for creating a Group entity.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// GroupDelete is the builder for deleting a Group entity.
|
||||
@@ -12,14 +12,14 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// GroupQuery is the builder for querying Group entities.
|
||||
@@ -12,14 +12,14 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/ent/user"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
|
||||
)
|
||||
|
||||
// GroupUpdate is the builder for updating Group entities.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
)
|
||||
|
||||
// GroupInvitationToken is the model entity for the GroupInvitationToken schema.
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// ID filters vertices based on their ID field.
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
)
|
||||
|
||||
// GroupInvitationTokenCreate is the builder for creating a GroupInvitationToken entity.
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// GroupInvitationTokenDelete is the builder for deleting a GroupInvitationToken entity.
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// GroupInvitationTokenQuery is the builder for querying GroupInvitationToken entities.
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
"entgo.io/ent/dialect/sql/sqlgraph"
|
||||
"entgo.io/ent/schema/field"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/ent/predicate"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
|
||||
)
|
||||
|
||||
// GroupInvitationTokenUpdate is the builder for updating GroupInvitationToken entities.
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
)
|
||||
|
||||
// The AttachmentFunc type is an adapter to allow the use of ordinary
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/ent/location"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
)
|
||||
|
||||
// Item is the model entity for the Item schema.
|
||||
@@ -35,6 +35,8 @@ type Item struct {
|
||||
Quantity int `json:"quantity,omitempty"`
|
||||
// Insured holds the value of the "insured" field.
|
||||
Insured bool `json:"insured,omitempty"`
|
||||
// Archived holds the value of the "archived" field.
|
||||
Archived bool `json:"archived,omitempty"`
|
||||
// SerialNumber holds the value of the "serial_number" field.
|
||||
SerialNumber string `json:"serial_number,omitempty"`
|
||||
// ModelNumber holds the value of the "model_number" field.
|
||||
@@ -65,11 +67,16 @@ type Item struct {
|
||||
// The values are being populated by the ItemQuery when eager-loading is set.
|
||||
Edges ItemEdges `json:"edges"`
|
||||
group_items *uuid.UUID
|
||||
item_children *uuid.UUID
|
||||
location_items *uuid.UUID
|
||||
}
|
||||
|
||||
// ItemEdges holds the relations/edges for other nodes in the graph.
|
||||
type ItemEdges struct {
|
||||
// Parent holds the value of the parent edge.
|
||||
Parent *Item `json:"parent,omitempty"`
|
||||
// Children holds the value of the children edge.
|
||||
Children []*Item `json:"children,omitempty"`
|
||||
// Group holds the value of the group edge.
|
||||
Group *Group `json:"group,omitempty"`
|
||||
// Label holds the value of the label edge.
|
||||
@@ -82,13 +89,35 @@ type ItemEdges struct {
|
||||
Attachments []*Attachment `json:"attachments,omitempty"`
|
||||
// loadedTypes holds the information for reporting if a
|
||||
// type was loaded (or requested) in eager-loading or not.
|
||||
loadedTypes [5]bool
|
||||
loadedTypes [7]bool
|
||||
}
|
||||
|
||||
// ParentOrErr returns the Parent value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e ItemEdges) ParentOrErr() (*Item, error) {
|
||||
if e.loadedTypes[0] {
|
||||
if e.Parent == nil {
|
||||
// Edge was loaded but was not found.
|
||||
return nil, &NotFoundError{label: item.Label}
|
||||
}
|
||||
return e.Parent, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "parent"}
|
||||
}
|
||||
|
||||
// ChildrenOrErr returns the Children value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e ItemEdges) ChildrenOrErr() ([]*Item, error) {
|
||||
if e.loadedTypes[1] {
|
||||
return e.Children, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "children"}
|
||||
}
|
||||
|
||||
// GroupOrErr returns the Group value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e ItemEdges) GroupOrErr() (*Group, error) {
|
||||
if e.loadedTypes[0] {
|
||||
if e.loadedTypes[2] {
|
||||
if e.Group == nil {
|
||||
// Edge was loaded but was not found.
|
||||
return nil, &NotFoundError{label: group.Label}
|
||||
@@ -101,7 +130,7 @@ func (e ItemEdges) GroupOrErr() (*Group, error) {
|
||||
// LabelOrErr returns the Label value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e ItemEdges) LabelOrErr() ([]*Label, error) {
|
||||
if e.loadedTypes[1] {
|
||||
if e.loadedTypes[3] {
|
||||
return e.Label, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "label"}
|
||||
@@ -110,7 +139,7 @@ func (e ItemEdges) LabelOrErr() ([]*Label, error) {
|
||||
// LocationOrErr returns the Location value or an error if the edge
|
||||
// was not loaded in eager-loading, or loaded but was not found.
|
||||
func (e ItemEdges) LocationOrErr() (*Location, error) {
|
||||
if e.loadedTypes[2] {
|
||||
if e.loadedTypes[4] {
|
||||
if e.Location == nil {
|
||||
// Edge was loaded but was not found.
|
||||
return nil, &NotFoundError{label: location.Label}
|
||||
@@ -123,7 +152,7 @@ func (e ItemEdges) LocationOrErr() (*Location, error) {
|
||||
// FieldsOrErr returns the Fields value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e ItemEdges) FieldsOrErr() ([]*ItemField, error) {
|
||||
if e.loadedTypes[3] {
|
||||
if e.loadedTypes[5] {
|
||||
return e.Fields, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "fields"}
|
||||
@@ -132,7 +161,7 @@ func (e ItemEdges) FieldsOrErr() ([]*ItemField, error) {
|
||||
// AttachmentsOrErr returns the Attachments value or an error if the edge
|
||||
// was not loaded in eager-loading.
|
||||
func (e ItemEdges) AttachmentsOrErr() ([]*Attachment, error) {
|
||||
if e.loadedTypes[4] {
|
||||
if e.loadedTypes[6] {
|
||||
return e.Attachments, nil
|
||||
}
|
||||
return nil, &NotLoadedError{edge: "attachments"}
|
||||
@@ -143,7 +172,7 @@ func (*Item) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case item.FieldInsured, item.FieldLifetimeWarranty:
|
||||
case item.FieldInsured, item.FieldArchived, item.FieldLifetimeWarranty:
|
||||
values[i] = new(sql.NullBool)
|
||||
case item.FieldPurchasePrice, item.FieldSoldPrice:
|
||||
values[i] = new(sql.NullFloat64)
|
||||
@@ -157,7 +186,9 @@ func (*Item) scanValues(columns []string) ([]any, error) {
|
||||
values[i] = new(uuid.UUID)
|
||||
case item.ForeignKeys[0]: // group_items
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
case item.ForeignKeys[1]: // location_items
|
||||
case item.ForeignKeys[1]: // item_children
|
||||
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
|
||||
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])
|
||||
@@ -228,6 +259,12 @@ func (i *Item) assignValues(columns []string, values []any) error {
|
||||
} else if value.Valid {
|
||||
i.Insured = value.Bool
|
||||
}
|
||||
case item.FieldArchived:
|
||||
if value, ok := values[j].(*sql.NullBool); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field archived", values[j])
|
||||
} else if value.Valid {
|
||||
i.Archived = value.Bool
|
||||
}
|
||||
case item.FieldSerialNumber:
|
||||
if value, ok := values[j].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field serial_number", values[j])
|
||||
@@ -314,6 +351,13 @@ func (i *Item) assignValues(columns []string, values []any) error {
|
||||
*i.group_items = *value.S.(*uuid.UUID)
|
||||
}
|
||||
case item.ForeignKeys[1]:
|
||||
if value, ok := values[j].(*sql.NullScanner); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field item_children", values[j])
|
||||
} else if value.Valid {
|
||||
i.item_children = new(uuid.UUID)
|
||||
*i.item_children = *value.S.(*uuid.UUID)
|
||||
}
|
||||
case item.ForeignKeys[2]:
|
||||
if value, ok := values[j].(*sql.NullScanner); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field location_items", values[j])
|
||||
} else if value.Valid {
|
||||
@@ -325,6 +369,16 @@ func (i *Item) assignValues(columns []string, values []any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryParent queries the "parent" edge of the Item entity.
|
||||
func (i *Item) QueryParent() *ItemQuery {
|
||||
return (&ItemClient{config: i.config}).QueryParent(i)
|
||||
}
|
||||
|
||||
// QueryChildren queries the "children" edge of the Item entity.
|
||||
func (i *Item) QueryChildren() *ItemQuery {
|
||||
return (&ItemClient{config: i.config}).QueryChildren(i)
|
||||
}
|
||||
|
||||
// QueryGroup queries the "group" edge of the Item entity.
|
||||
func (i *Item) QueryGroup() *GroupQuery {
|
||||
return (&ItemClient{config: i.config}).QueryGroup(i)
|
||||
@@ -397,6 +451,9 @@ func (i *Item) String() string {
|
||||
builder.WriteString("insured=")
|
||||
builder.WriteString(fmt.Sprintf("%v", i.Insured))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("archived=")
|
||||
builder.WriteString(fmt.Sprintf("%v", i.Archived))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("serial_number=")
|
||||
builder.WriteString(i.SerialNumber)
|
||||
builder.WriteString(", ")
|
||||
@@ -29,6 +29,8 @@ const (
|
||||
FieldQuantity = "quantity"
|
||||
// FieldInsured holds the string denoting the insured field in the database.
|
||||
FieldInsured = "insured"
|
||||
// FieldArchived holds the string denoting the archived field in the database.
|
||||
FieldArchived = "archived"
|
||||
// FieldSerialNumber holds the string denoting the serial_number field in the database.
|
||||
FieldSerialNumber = "serial_number"
|
||||
// FieldModelNumber holds the string denoting the model_number field in the database.
|
||||
@@ -55,6 +57,10 @@ const (
|
||||
FieldSoldPrice = "sold_price"
|
||||
// FieldSoldNotes holds the string denoting the sold_notes field in the database.
|
||||
FieldSoldNotes = "sold_notes"
|
||||
// EdgeParent holds the string denoting the parent edge name in mutations.
|
||||
EdgeParent = "parent"
|
||||
// EdgeChildren holds the string denoting the children edge name in mutations.
|
||||
EdgeChildren = "children"
|
||||
// EdgeGroup holds the string denoting the group edge name in mutations.
|
||||
EdgeGroup = "group"
|
||||
// EdgeLabel holds the string denoting the label edge name in mutations.
|
||||
@@ -67,6 +73,14 @@ const (
|
||||
EdgeAttachments = "attachments"
|
||||
// Table holds the table name of the item in the database.
|
||||
Table = "items"
|
||||
// ParentTable is the table that holds the parent relation/edge.
|
||||
ParentTable = "items"
|
||||
// ParentColumn is the table column denoting the parent relation/edge.
|
||||
ParentColumn = "item_children"
|
||||
// ChildrenTable is the table that holds the children relation/edge.
|
||||
ChildrenTable = "items"
|
||||
// ChildrenColumn is the table column denoting the children relation/edge.
|
||||
ChildrenColumn = "item_children"
|
||||
// GroupTable is the table that holds the group relation/edge.
|
||||
GroupTable = "items"
|
||||
// GroupInverseTable is the table name for the Group entity.
|
||||
@@ -113,6 +127,7 @@ var Columns = []string{
|
||||
FieldNotes,
|
||||
FieldQuantity,
|
||||
FieldInsured,
|
||||
FieldArchived,
|
||||
FieldSerialNumber,
|
||||
FieldModelNumber,
|
||||
FieldManufacturer,
|
||||
@@ -132,6 +147,7 @@ var Columns = []string{
|
||||
// table and are not defined as standalone fields in the schema.
|
||||
var ForeignKeys = []string{
|
||||
"group_items",
|
||||
"item_children",
|
||||
"location_items",
|
||||
}
|
||||
|
||||
@@ -175,6 +191,8 @@ var (
|
||||
DefaultQuantity int
|
||||
// DefaultInsured holds the default value on creation for the "insured" field.
|
||||
DefaultInsured bool
|
||||
// DefaultArchived holds the default value on creation for the "archived" field.
|
||||
DefaultArchived bool
|
||||
// SerialNumberValidator is a validator for the "serial_number" field. It is called by the builders before save.
|
||||
SerialNumberValidator func(string) error
|
||||
// ModelNumberValidator is a validator for the "model_number" field. It is called by the builders before save.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user