feat: auth-roles, image-gallery, click-to-open (#166)

* schema changes

* db generate

* db migration

* add role based middleware

* implement attachment token access

* generate docs

* implement role based auth

* replace attachment specific tokens with gen token

* run linter

* cleanup temporary token implementation
This commit is contained in:
Hayden 2022-12-03 10:55:00 -09:00 committed by GitHub
parent 974d6914a2
commit de419dc37d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 3127 additions and 244 deletions

View file

@ -13,8 +13,9 @@ import (
type ( type (
TokenResponse struct { TokenResponse struct {
Token string `json:"token"` Token string `json:"token"`
ExpiresAt time.Time `json:"expiresAt"` ExpiresAt time.Time `json:"expiresAt"`
AttachmentToken string `json:"attachmentToken"`
} }
LoginForm struct { LoginForm struct {
@ -76,8 +77,9 @@ func (ctrl *V1Controller) HandleAuthLogin() server.HandlerFunc {
} }
return server.Respond(w, http.StatusOK, TokenResponse{ return server.Respond(w, http.StatusOK, TokenResponse{
Token: "Bearer " + newToken.Raw, Token: "Bearer " + newToken.Raw,
ExpiresAt: newToken.ExpiresAt, ExpiresAt: newToken.ExpiresAt,
AttachmentToken: newToken.AttachmentToken,
}) })
} }
} }

View file

@ -2,10 +2,7 @@ package v1
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"path/filepath"
"strings"
"github.com/hay-kot/homebox/backend/internal/core/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/ent/attachment"
@ -99,47 +96,12 @@ func (ctrl *V1Controller) HandleItemAttachmentCreate() server.HandlerFunc {
// @Summary retrieves an attachment for an item // @Summary retrieves an attachment for an item
// @Tags Items Attachments // @Tags Items Attachments
// @Produce application/octet-stream // @Produce application/octet-stream
// @Param id path string true "Item ID"
// @Param token query string true "Attachment token"
// @Success 200
// @Router /v1/items/{id}/attachments/download [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentDownload() 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")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
ext := filepath.Ext(doc.Path)
title := doc.Title
if !strings.HasSuffix(doc.Title, ext) {
title = doc.Title + ext
}
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", title))
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeFile(w, r, doc.Path)
return nil
}
}
// HandleItemAttachmentToken godocs
// @Summary retrieves an attachment for an item
// @Tags Items Attachments
// @Produce application/octet-stream
// @Param id path string true "Item ID" // @Param id path string true "Item ID"
// @Param attachment_id path string true "Attachment ID" // @Param attachment_id path string true "Attachment ID"
// @Success 200 {object} ItemAttachmentToken // @Success 200 {object} ItemAttachmentToken
// @Router /v1/items/{id}/attachments/{attachment_id} [GET] // @Router /v1/items/{id}/attachments/{attachment_id} [GET]
// @Security Bearer // @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentToken() server.HandlerFunc { func (ctrl *V1Controller) HandleItemAttachmentGet() server.HandlerFunc {
return ctrl.handleItemAttachmentsHandler return ctrl.handleItemAttachmentsHandler
} }
@ -181,33 +143,15 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
ctx := services.NewContext(r.Context()) ctx := services.NewContext(r.Context())
switch r.Method { switch r.Method {
// Token Handler
case http.MethodGet: case http.MethodGet:
token, err := ctrl.svc.Items.AttachmentToken(ctx, ID, attachmentID) doc, err := ctrl.svc.Items.AttachmentPath(r.Context(), attachmentID)
if err != nil { if err != nil {
switch err { log.Err(err).Msg("failed to get attachment path")
case services.ErrNotFound: return validate.NewRequestError(err, http.StatusInternalServerError)
log.Err(err).
Str("id", attachmentID.String()).
Msg("failed to find attachment with id")
return validate.NewRequestError(err, http.StatusNotFound)
case services.ErrFileNotFound:
log.Err(err).
Str("id", attachmentID.String()).
Msg("failed to find file path for attachment with id")
log.Warn().Msg("attachment with no file path removed from database")
return validate.NewRequestError(err, http.StatusNotFound)
default:
log.Err(err).Msg("failed to get attachment")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
} }
return server.Respond(w, http.StatusOK, ItemAttachmentToken{Token: token}) http.ServeFile(w, r, doc.Path)
return nil
// Delete Attachment Handler // Delete Attachment Handler
case http.MethodDelete: case http.MethodDelete:

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"errors" "errors"
"net/http" "net/http"
"strings" "strings"
@ -10,17 +11,87 @@ import (
"github.com/hay-kot/homebox/backend/pkgs/server" "github.com/hay-kot/homebox/backend/pkgs/server"
) )
type tokenHasKey struct {
key string
}
var (
hashedToken = tokenHasKey{key: "hashedToken"}
)
type RoleMode int
const (
RoleModeOr RoleMode = 0
RoleModeAnd RoleMode = 1
)
// mwRoles is a middleware that will validate the required roles are met. All roles
// are required to be met for the request to be allowed. If the user does not have
// the required roles, a 403 Forbidden will be returned.
//
// WARNING: This middleware _MUST_ be called after mwAuthToken or else it will panic
func (a *app) mwRoles(rm RoleMode, required ...string) server.Middleware {
return func(next server.Handler) server.Handler {
return server.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
maybeToken := ctx.Value(hashedToken)
if maybeToken == nil {
panic("mwRoles: token not found in context, you must call mwAuthToken before mwRoles")
}
token := maybeToken.(string)
roles, err := a.repos.AuthTokens.GetRoles(r.Context(), token)
if err != nil {
return err
}
outer:
switch rm {
case RoleModeOr:
for _, role := range required {
if roles.Contains(role) {
break outer
}
}
return validate.NewRequestError(errors.New("Forbidden"), http.StatusForbidden)
case RoleModeAnd:
for _, req := range required {
if !roles.Contains(req) {
return validate.NewRequestError(errors.New("Unauthorized"), http.StatusForbidden)
}
}
}
return next.ServeHTTP(w, r)
})
}
}
// mwAuthToken is a middleware that will check the database for a stateful token // mwAuthToken is a middleware that will check the database for a stateful token
// and attach it to the request context with the user, or return a 401 if it doesn't exist. // and attach it's user to the request context, or return an appropriate error.
// Authorization support is by token via Headers or Query Parameter
//
// Example:
// - header = "Bearer 1234567890"
// - query = "?access_token=1234567890"
func (a *app) mwAuthToken(next server.Handler) server.Handler { func (a *app) mwAuthToken(next server.Handler) server.Handler {
return server.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { return server.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
requestToken := r.Header.Get("Authorization") requestToken := r.Header.Get("Authorization")
if requestToken == "" { if requestToken == "" {
return validate.NewRequestError(errors.New("Authorization header is required"), http.StatusUnauthorized) // check for query param
requestToken = r.URL.Query().Get("access_token")
if requestToken == "" {
return validate.NewRequestError(errors.New("Authorization header or query is required"), http.StatusUnauthorized)
}
} }
requestToken = strings.TrimPrefix(requestToken, "Bearer ") requestToken = strings.TrimPrefix(requestToken, "Bearer ")
r = r.WithContext(context.WithValue(r.Context(), hashedToken, requestToken))
usr, err := a.services.User.GetSelf(r.Context(), requestToken) usr, err := a.services.User.GetSelf(r.Context(), requestToken)
// Check the database for the token // Check the database for the token

View file

@ -13,6 +13,7 @@ import (
"github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers" "github.com/hay-kot/homebox/backend/app/api/handlers/debughandlers"
v1 "github.com/hay-kot/homebox/backend/app/api/handlers/v1" 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/app/api/static/docs"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/repo" "github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/pkgs/server" "github.com/hay-kot/homebox/backend/pkgs/server"
httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware httpSwagger "github.com/swaggo/http-swagger" // http-swagger middleware
@ -64,49 +65,55 @@ func (a *app) mountRoutes(repos *repo.AllRepos) {
a.server.Post(v1Base("/users/register"), v1Ctrl.HandleUserRegistration()) a.server.Post(v1Base("/users/register"), v1Ctrl.HandleUserRegistration())
a.server.Post(v1Base("/users/login"), v1Ctrl.HandleAuthLogin()) a.server.Post(v1Base("/users/login"), v1Ctrl.HandleAuthLogin())
// Attachment download URl needs a `token` query param to be passed in the request. userMW := []server.Middleware{
// and also needs to be outside of the `auth` middleware. a.mwAuthToken,
a.server.Get(v1Base("/items/{id}/attachments/download"), v1Ctrl.HandleItemAttachmentDownload()) a.mwRoles(RoleModeOr, authroles.RoleUser.String()),
}
a.server.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf(), a.mwAuthToken) a.server.Get(v1Base("/users/self"), v1Ctrl.HandleUserSelf(), userMW...)
a.server.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate(), a.mwAuthToken) a.server.Put(v1Base("/users/self"), v1Ctrl.HandleUserSelfUpdate(), userMW...)
a.server.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete(), a.mwAuthToken) a.server.Delete(v1Base("/users/self"), v1Ctrl.HandleUserSelfDelete(), userMW...)
a.server.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout(), a.mwAuthToken) a.server.Post(v1Base("/users/logout"), v1Ctrl.HandleAuthLogout(), userMW...)
a.server.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh(), a.mwAuthToken) a.server.Get(v1Base("/users/refresh"), v1Ctrl.HandleAuthRefresh(), userMW...)
a.server.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword(), a.mwAuthToken) a.server.Put(v1Base("/users/self/change-password"), v1Ctrl.HandleUserSelfChangePassword(), userMW...)
a.server.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate(), a.mwAuthToken) a.server.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate(), userMW...)
a.server.Get(v1Base("/groups/statistics"), v1Ctrl.HandleGroupStatistics(), a.mwAuthToken) a.server.Get(v1Base("/groups/statistics"), v1Ctrl.HandleGroupStatistics(), userMW...)
// TODO: I don't like /groups being the URL for users // TODO: I don't like /groups being the URL for users
a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), a.mwAuthToken) a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), userMW...)
a.server.Put(v1Base("/groups"), v1Ctrl.HandleGroupUpdate(), a.mwAuthToken) a.server.Put(v1Base("/groups"), v1Ctrl.HandleGroupUpdate(), userMW...)
a.server.Post(v1Base("/actions/ensure-asset-ids"), v1Ctrl.HandleEnsureAssetID(), a.mwAuthToken) a.server.Post(v1Base("/actions/ensure-asset-ids"), v1Ctrl.HandleEnsureAssetID(), userMW...)
a.server.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll(), a.mwAuthToken) a.server.Get(v1Base("/locations"), v1Ctrl.HandleLocationGetAll(), userMW...)
a.server.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate(), a.mwAuthToken) a.server.Post(v1Base("/locations"), v1Ctrl.HandleLocationCreate(), userMW...)
a.server.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet(), a.mwAuthToken) a.server.Get(v1Base("/locations/{id}"), v1Ctrl.HandleLocationGet(), userMW...)
a.server.Put(v1Base("/locations/{id}"), v1Ctrl.HandleLocationUpdate(), a.mwAuthToken) a.server.Put(v1Base("/locations/{id}"), v1Ctrl.HandleLocationUpdate(), userMW...)
a.server.Delete(v1Base("/locations/{id}"), v1Ctrl.HandleLocationDelete(), a.mwAuthToken) a.server.Delete(v1Base("/locations/{id}"), v1Ctrl.HandleLocationDelete(), userMW...)
a.server.Get(v1Base("/labels"), v1Ctrl.HandleLabelsGetAll(), a.mwAuthToken) a.server.Get(v1Base("/labels"), v1Ctrl.HandleLabelsGetAll(), userMW...)
a.server.Post(v1Base("/labels"), v1Ctrl.HandleLabelsCreate(), a.mwAuthToken) a.server.Post(v1Base("/labels"), v1Ctrl.HandleLabelsCreate(), userMW...)
a.server.Get(v1Base("/labels/{id}"), v1Ctrl.HandleLabelGet(), a.mwAuthToken) a.server.Get(v1Base("/labels/{id}"), v1Ctrl.HandleLabelGet(), userMW...)
a.server.Put(v1Base("/labels/{id}"), v1Ctrl.HandleLabelUpdate(), a.mwAuthToken) a.server.Put(v1Base("/labels/{id}"), v1Ctrl.HandleLabelUpdate(), userMW...)
a.server.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete(), a.mwAuthToken) a.server.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete(), userMW...)
a.server.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll(), a.mwAuthToken) a.server.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll(), userMW...)
a.server.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport(), a.mwAuthToken) a.server.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport(), userMW...)
a.server.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate(), a.mwAuthToken) a.server.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate(), userMW...)
a.server.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet(), a.mwAuthToken) a.server.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet(), userMW...)
a.server.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate(), a.mwAuthToken) a.server.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate(), userMW...)
a.server.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete(), a.mwAuthToken) a.server.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete(), userMW...)
a.server.Post(v1Base("/items/{id}/attachments"), v1Ctrl.HandleItemAttachmentCreate(), a.mwAuthToken) a.server.Post(v1Base("/items/{id}/attachments"), v1Ctrl.HandleItemAttachmentCreate(), userMW...)
a.server.Get(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentToken(), a.mwAuthToken) a.server.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate(), userMW...)
a.server.Put(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentUpdate(), a.mwAuthToken) a.server.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete(), userMW...)
a.server.Delete(v1Base("/items/{id}/attachments/{attachment_id}"), v1Ctrl.HandleItemAttachmentDelete(), a.mwAuthToken)
a.server.Get(
v1Base("/items/{id}/attachments/{attachment_id}"),
v1Ctrl.HandleItemAttachmentGet(),
a.mwAuthToken, a.mwRoles(RoleModeOr, authroles.RoleUser.String(), authroles.RoleAttachments.String()),
)
a.server.NotFound(notFoundHandler()) a.server.NotFound(notFoundHandler())
} }

View file

@ -1966,6 +1966,9 @@ const docTemplate = `{
"v1.TokenResponse": { "v1.TokenResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"attachmentToken": {
"type": "string"
},
"expiresAt": { "expiresAt": {
"type": "string" "type": "string"
}, },

View file

@ -1958,6 +1958,9 @@
"v1.TokenResponse": { "v1.TokenResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
"attachmentToken": {
"type": "string"
},
"expiresAt": { "expiresAt": {
"type": "string" "type": "string"
}, },

View file

@ -510,6 +510,8 @@ definitions:
type: object type: object
v1.TokenResponse: v1.TokenResponse:
properties: properties:
attachmentToken:
type: string
expiresAt: expiresAt:
type: string type: string
token: token:

View file

@ -38,7 +38,6 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
Group: &GroupService{repos}, Group: &GroupService{repos},
Items: &ItemService{ Items: &ItemService{
repo: repos, repo: repos,
at: attachmentTokens{},
autoIncrementAssetID: options.autoIncrementAssetID, autoIncrementAssetID: options.autoIncrementAssetID,
}, },
} }

View file

@ -18,9 +18,6 @@ type ItemService struct {
repo *repo.AllRepos repo *repo.AllRepos
filepath string filepath string
// at is a map of tokens to attachment IDs. This is used to store the attachment ID
// for issued URLs
at attachmentTokens
autoIncrementAssetID bool autoIncrementAssetID bool
} }

View file

@ -4,71 +4,15 @@ import (
"context" "context"
"io" "io"
"os" "os"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent" "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/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/repo" "github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/pkgs/hasher"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// TODO: this isn't a scalable solution, tokens should be stored in the database func (svc *ItemService) AttachmentPath(ctx context.Context, attachmentId uuid.UUID) (*ent.Document, error) {
type attachmentTokens map[string]uuid.UUID
func (at attachmentTokens) Add(token string, id uuid.UUID) {
at[token] = id
log.Debug().Str("token", token).Str("uuid", id.String()).Msg("added token")
go func() {
ch := time.After(1 * time.Minute)
<-ch
at.Delete(token)
log.Debug().Str("token", token).Msg("deleted token")
}()
}
func (at attachmentTokens) Get(token string) (uuid.UUID, bool) {
id, ok := at[token]
return id, ok
}
func (at attachmentTokens) Delete(token string) {
delete(at, token)
}
func (svc *ItemService) AttachmentToken(ctx Context, itemId, attachmentId uuid.UUID) (string, error) {
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemId)
if err != nil {
return "", err
}
token := hasher.GenerateToken()
// Ensure that the file exists
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
if err != nil {
return "", err
}
if _, err := os.Stat(attachment.Edges.Document.Path); os.IsNotExist(err) {
_ = svc.AttachmentDelete(ctx, ctx.GID, itemId, attachmentId)
return "", ErrNotFound
}
svc.at.Add(token.Raw, attachmentId)
return token.Raw, nil
}
func (svc *ItemService) AttachmentPath(ctx context.Context, token string) (*ent.Document, error) {
attachmentId, ok := svc.at.Get(token)
if !ok {
return nil, ErrNotFound
}
attachment, err := svc.repo.Attachments.Get(ctx, attachmentId) attachment, err := svc.repo.Attachments.Get(ctx, attachmentId)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/repo" "github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/pkgs/hasher" "github.com/hay-kot/homebox/backend/pkgs/hasher"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -30,8 +31,9 @@ type (
Password string `json:"password"` Password string `json:"password"`
} }
UserAuthTokenDetail struct { UserAuthTokenDetail struct {
Raw string `json:"raw"` Raw string `json:"raw"`
ExpiresAt time.Time `json:"expiresAt"` AttachmentToken string `json:"attachmentToken"`
ExpiresAt time.Time `json:"expiresAt"`
} }
LoginForm struct { LoginForm struct {
Username string `json:"username"` Username string `json:"username"`
@ -131,16 +133,37 @@ func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data repo.
// ============================================================================ // ============================================================================
// User Authentication // User Authentication
func (svc *UserService) createToken(ctx context.Context, userId uuid.UUID) (UserAuthTokenDetail, error) { func (svc *UserService) createSessionToken(ctx context.Context, userId uuid.UUID) (UserAuthTokenDetail, error) {
newToken := hasher.GenerateToken()
created, err := svc.repos.AuthTokens.CreateToken(ctx, repo.UserAuthTokenCreate{ attachmentToken := hasher.GenerateToken()
attachmentData := repo.UserAuthTokenCreate{
UserID: userId, UserID: userId,
TokenHash: newToken.Hash, TokenHash: attachmentToken.Hash,
ExpiresAt: time.Now().Add(oneWeek), ExpiresAt: time.Now().Add(oneWeek),
}) }
return UserAuthTokenDetail{Raw: newToken.Raw, ExpiresAt: created.ExpiresAt}, err _, err := svc.repos.AuthTokens.CreateToken(ctx, attachmentData, authroles.RoleAttachments)
if err != nil {
return UserAuthTokenDetail{}, err
}
userToken := hasher.GenerateToken()
data := repo.UserAuthTokenCreate{
UserID: userId,
TokenHash: userToken.Hash,
ExpiresAt: time.Now().Add(oneWeek),
}
created, err := svc.repos.AuthTokens.CreateToken(ctx, data, authroles.RoleUser)
if err != nil {
return UserAuthTokenDetail{}, err
}
return UserAuthTokenDetail{
Raw: userToken.Raw,
ExpiresAt: created.ExpiresAt,
AttachmentToken: attachmentToken.Raw,
}, nil
} }
func (svc *UserService) Login(ctx context.Context, username, password string) (UserAuthTokenDetail, error) { func (svc *UserService) Login(ctx context.Context, username, password string) (UserAuthTokenDetail, error) {
@ -156,7 +179,7 @@ func (svc *UserService) Login(ctx context.Context, username, password string) (U
return UserAuthTokenDetail{}, ErrorInvalidLogin return UserAuthTokenDetail{}, ErrorInvalidLogin
} }
return svc.createToken(ctx, usr.ID) return svc.createSessionToken(ctx, usr.ID)
} }
func (svc *UserService) Logout(ctx context.Context, token string) error { func (svc *UserService) Logout(ctx context.Context, token string) error {
@ -174,9 +197,7 @@ func (svc *UserService) RenewToken(ctx context.Context, token string) (UserAuthT
return UserAuthTokenDetail{}, ErrorInvalidToken return UserAuthTokenDetail{}, ErrorInvalidToken
} }
newToken, _ := svc.createToken(ctx, dbToken.ID) return svc.createSessionToken(ctx, dbToken.ID)
return newToken, nil
} }
// DeleteSelf deletes the user that is currently logged based of the provided UUID // DeleteSelf deletes the user that is currently logged based of the provided UUID

View file

@ -0,0 +1,141 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
)
// AuthRoles is the model entity for the AuthRoles schema.
type AuthRoles struct {
config `json:"-"`
// ID of the ent.
ID int `json:"id,omitempty"`
// Role holds the value of the "role" field.
Role authroles.Role `json:"role,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the AuthRolesQuery when eager-loading is set.
Edges AuthRolesEdges `json:"edges"`
auth_tokens_roles *uuid.UUID
}
// AuthRolesEdges holds the relations/edges for other nodes in the graph.
type AuthRolesEdges struct {
// Token holds the value of the token edge.
Token *AuthTokens `json:"token,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// TokenOrErr returns the Token value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e AuthRolesEdges) TokenOrErr() (*AuthTokens, error) {
if e.loadedTypes[0] {
if e.Token == nil {
// Edge was loaded but was not found.
return nil, &NotFoundError{label: authtokens.Label}
}
return e.Token, nil
}
return nil, &NotLoadedError{edge: "token"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*AuthRoles) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case authroles.FieldID:
values[i] = new(sql.NullInt64)
case authroles.FieldRole:
values[i] = new(sql.NullString)
case authroles.ForeignKeys[0]: // auth_tokens_roles
values[i] = &sql.NullScanner{S: new(uuid.UUID)}
default:
return nil, fmt.Errorf("unexpected column %q for type AuthRoles", columns[i])
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the AuthRoles fields.
func (ar *AuthRoles) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
}
for i := range columns {
switch columns[i] {
case authroles.FieldID:
value, ok := values[i].(*sql.NullInt64)
if !ok {
return fmt.Errorf("unexpected type %T for field id", value)
}
ar.ID = int(value.Int64)
case authroles.FieldRole:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field role", values[i])
} else if value.Valid {
ar.Role = authroles.Role(value.String)
}
case authroles.ForeignKeys[0]:
if value, ok := values[i].(*sql.NullScanner); !ok {
return fmt.Errorf("unexpected type %T for field auth_tokens_roles", values[i])
} else if value.Valid {
ar.auth_tokens_roles = new(uuid.UUID)
*ar.auth_tokens_roles = *value.S.(*uuid.UUID)
}
}
}
return nil
}
// QueryToken queries the "token" edge of the AuthRoles entity.
func (ar *AuthRoles) QueryToken() *AuthTokensQuery {
return (&AuthRolesClient{config: ar.config}).QueryToken(ar)
}
// Update returns a builder for updating this AuthRoles.
// Note that you need to call AuthRoles.Unwrap() before calling this method if this AuthRoles
// was returned from a transaction, and the transaction was committed or rolled back.
func (ar *AuthRoles) Update() *AuthRolesUpdateOne {
return (&AuthRolesClient{config: ar.config}).UpdateOne(ar)
}
// Unwrap unwraps the AuthRoles entity that was returned from a transaction after it was closed,
// so that all future queries will be executed through the driver which created the transaction.
func (ar *AuthRoles) Unwrap() *AuthRoles {
_tx, ok := ar.config.driver.(*txDriver)
if !ok {
panic("ent: AuthRoles is not a transactional entity")
}
ar.config.driver = _tx.drv
return ar
}
// String implements the fmt.Stringer.
func (ar *AuthRoles) String() string {
var builder strings.Builder
builder.WriteString("AuthRoles(")
builder.WriteString(fmt.Sprintf("id=%v, ", ar.ID))
builder.WriteString("role=")
builder.WriteString(fmt.Sprintf("%v", ar.Role))
builder.WriteByte(')')
return builder.String()
}
// AuthRolesSlice is a parsable slice of AuthRoles.
type AuthRolesSlice []*AuthRoles
func (ar AuthRolesSlice) config(cfg config) {
for _i := range ar {
ar[_i].config = cfg
}
}

View file

@ -0,0 +1,81 @@
// Code generated by ent, DO NOT EDIT.
package authroles
import (
"fmt"
)
const (
// Label holds the string label denoting the authroles type in the database.
Label = "auth_roles"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldRole holds the string denoting the role field in the database.
FieldRole = "role"
// EdgeToken holds the string denoting the token edge name in mutations.
EdgeToken = "token"
// Table holds the table name of the authroles in the database.
Table = "auth_roles"
// TokenTable is the table that holds the token relation/edge.
TokenTable = "auth_roles"
// TokenInverseTable is the table name for the AuthTokens entity.
// It exists in this package in order to avoid circular dependency with the "authtokens" package.
TokenInverseTable = "auth_tokens"
// TokenColumn is the table column denoting the token relation/edge.
TokenColumn = "auth_tokens_roles"
)
// Columns holds all SQL columns for authroles fields.
var Columns = []string{
FieldID,
FieldRole,
}
// ForeignKeys holds the SQL foreign-keys that are owned by the "auth_roles"
// table and are not defined as standalone fields in the schema.
var ForeignKeys = []string{
"auth_tokens_roles",
}
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
if column == Columns[i] {
return true
}
}
for i := range ForeignKeys {
if column == ForeignKeys[i] {
return true
}
}
return false
}
// Role defines the type for the "role" enum field.
type Role string
// RoleUser is the default value of the Role enum.
const DefaultRole = RoleUser
// Role values.
const (
RoleAdmin Role = "admin"
RoleUser Role = "user"
RoleAttachments Role = "attachments"
)
func (r Role) String() string {
return string(r)
}
// RoleValidator is a validator for the "role" field enum values. It is called by the builders before save.
func RoleValidator(r Role) error {
switch r {
case RoleAdmin, RoleUser, RoleAttachments:
return nil
default:
return fmt.Errorf("authroles: invalid enum value for role field: %q", r)
}
}

View file

@ -0,0 +1,176 @@
// Code generated by ent, DO NOT EDIT.
package authroles
import (
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
})
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldID), id))
})
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
})
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
v := make([]any, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
})
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldID), id))
})
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldID), id))
})
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldID), id))
})
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldID), id))
})
}
// RoleEQ applies the EQ predicate on the "role" field.
func RoleEQ(v Role) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldRole), v))
})
}
// RoleNEQ applies the NEQ predicate on the "role" field.
func RoleNEQ(v Role) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldRole), v))
})
}
// RoleIn applies the In predicate on the "role" field.
func RoleIn(vs ...Role) predicate.AuthRoles {
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.In(s.C(FieldRole), v...))
})
}
// RoleNotIn applies the NotIn predicate on the "role" field.
func RoleNotIn(vs ...Role) predicate.AuthRoles {
v := make([]any, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.NotIn(s.C(FieldRole), v...))
})
}
// HasToken applies the HasEdge predicate on the "token" edge.
func HasToken() predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(TokenTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasTokenWith applies the HasEdge predicate on the "token" edge with a given conditions (other predicates).
func HasTokenWith(preds ...predicate.AuthTokens) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(TokenInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, TokenTable, TokenColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.AuthRoles) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.AuthRoles) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
}
// Not applies the not operator on the given predicate.
func Not(p predicate.AuthRoles) predicate.AuthRoles {
return predicate.AuthRoles(func(s *sql.Selector) {
p(s.Not())
})
}

View file

@ -0,0 +1,286 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
)
// AuthRolesCreate is the builder for creating a AuthRoles entity.
type AuthRolesCreate struct {
config
mutation *AuthRolesMutation
hooks []Hook
}
// SetRole sets the "role" field.
func (arc *AuthRolesCreate) SetRole(a authroles.Role) *AuthRolesCreate {
arc.mutation.SetRole(a)
return arc
}
// SetNillableRole sets the "role" field if the given value is not nil.
func (arc *AuthRolesCreate) SetNillableRole(a *authroles.Role) *AuthRolesCreate {
if a != nil {
arc.SetRole(*a)
}
return arc
}
// SetTokenID sets the "token" edge to the AuthTokens entity by ID.
func (arc *AuthRolesCreate) SetTokenID(id uuid.UUID) *AuthRolesCreate {
arc.mutation.SetTokenID(id)
return arc
}
// SetNillableTokenID sets the "token" edge to the AuthTokens entity by ID if the given value is not nil.
func (arc *AuthRolesCreate) SetNillableTokenID(id *uuid.UUID) *AuthRolesCreate {
if id != nil {
arc = arc.SetTokenID(*id)
}
return arc
}
// SetToken sets the "token" edge to the AuthTokens entity.
func (arc *AuthRolesCreate) SetToken(a *AuthTokens) *AuthRolesCreate {
return arc.SetTokenID(a.ID)
}
// Mutation returns the AuthRolesMutation object of the builder.
func (arc *AuthRolesCreate) Mutation() *AuthRolesMutation {
return arc.mutation
}
// Save creates the AuthRoles in the database.
func (arc *AuthRolesCreate) Save(ctx context.Context) (*AuthRoles, error) {
var (
err error
node *AuthRoles
)
arc.defaults()
if len(arc.hooks) == 0 {
if err = arc.check(); err != nil {
return nil, err
}
node, err = arc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = arc.check(); err != nil {
return nil, err
}
arc.mutation = mutation
if node, err = arc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(arc.hooks) - 1; i >= 0; i-- {
if arc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = arc.hooks[i](mut)
}
v, err := mut.Mutate(ctx, arc.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthRoles)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthRolesMutation", v)
}
node = nv
}
return node, err
}
// SaveX calls Save and panics if Save returns an error.
func (arc *AuthRolesCreate) SaveX(ctx context.Context) *AuthRoles {
v, err := arc.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (arc *AuthRolesCreate) Exec(ctx context.Context) error {
_, err := arc.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (arc *AuthRolesCreate) ExecX(ctx context.Context) {
if err := arc.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (arc *AuthRolesCreate) defaults() {
if _, ok := arc.mutation.Role(); !ok {
v := authroles.DefaultRole
arc.mutation.SetRole(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (arc *AuthRolesCreate) check() error {
if _, ok := arc.mutation.Role(); !ok {
return &ValidationError{Name: "role", err: errors.New(`ent: missing required field "AuthRoles.role"`)}
}
if v, ok := arc.mutation.Role(); ok {
if err := authroles.RoleValidator(v); err != nil {
return &ValidationError{Name: "role", err: fmt.Errorf(`ent: validator failed for field "AuthRoles.role": %w`, err)}
}
}
return nil
}
func (arc *AuthRolesCreate) sqlSave(ctx context.Context) (*AuthRoles, error) {
_node, _spec := arc.createSpec()
if err := sqlgraph.CreateNode(ctx, arc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
id := _spec.ID.Value.(int64)
_node.ID = int(id)
return _node, nil
}
func (arc *AuthRolesCreate) createSpec() (*AuthRoles, *sqlgraph.CreateSpec) {
var (
_node = &AuthRoles{config: arc.config}
_spec = &sqlgraph.CreateSpec{
Table: authroles.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
}
)
if value, ok := arc.mutation.Role(); ok {
_spec.SetField(authroles.FieldRole, field.TypeEnum, value)
_node.Role = value
}
if nodes := arc.mutation.TokenIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: true,
Table: authroles.TokenTable,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.auth_tokens_roles = &nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}
// AuthRolesCreateBulk is the builder for creating many AuthRoles entities in bulk.
type AuthRolesCreateBulk struct {
config
builders []*AuthRolesCreate
}
// Save creates the AuthRoles entities in the database.
func (arcb *AuthRolesCreateBulk) Save(ctx context.Context) ([]*AuthRoles, error) {
specs := make([]*sqlgraph.CreateSpec, len(arcb.builders))
nodes := make([]*AuthRoles, len(arcb.builders))
mutators := make([]Mutator, len(arcb.builders))
for i := range arcb.builders {
func(i int, root context.Context) {
builder := arcb.builders[i]
builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err := builder.check(); err != nil {
return nil, err
}
builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, arcb.builders[i+1].mutation)
} else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
// Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, arcb.driver, spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
}
}
if err != nil {
return nil, err
}
mutation.id = &nodes[i].ID
if specs[i].ID.Value != nil {
id := specs[i].ID.Value.(int64)
nodes[i].ID = int(id)
}
mutation.done = true
return nodes[i], nil
})
for i := len(builder.hooks) - 1; i >= 0; i-- {
mut = builder.hooks[i](mut)
}
mutators[i] = mut
}(i, ctx)
}
if len(mutators) > 0 {
if _, err := mutators[0].Mutate(ctx, arcb.builders[0].mutation); err != nil {
return nil, err
}
}
return nodes, nil
}
// SaveX is like Save, but panics if an error occurs.
func (arcb *AuthRolesCreateBulk) SaveX(ctx context.Context) []*AuthRoles {
v, err := arcb.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (arcb *AuthRolesCreateBulk) Exec(ctx context.Context) error {
_, err := arcb.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (arcb *AuthRolesCreateBulk) ExecX(ctx context.Context) {
if err := arcb.Exec(ctx); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,115 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
)
// AuthRolesDelete is the builder for deleting a AuthRoles entity.
type AuthRolesDelete struct {
config
hooks []Hook
mutation *AuthRolesMutation
}
// Where appends a list predicates to the AuthRolesDelete builder.
func (ard *AuthRolesDelete) Where(ps ...predicate.AuthRoles) *AuthRolesDelete {
ard.mutation.Where(ps...)
return ard
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (ard *AuthRolesDelete) Exec(ctx context.Context) (int, error) {
var (
err error
affected int
)
if len(ard.hooks) == 0 {
affected, err = ard.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
ard.mutation = mutation
affected, err = ard.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(ard.hooks) - 1; i >= 0; i-- {
if ard.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ard.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ard.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// ExecX is like Exec, but panics if an error occurs.
func (ard *AuthRolesDelete) ExecX(ctx context.Context) int {
n, err := ard.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (ard *AuthRolesDelete) sqlExec(ctx context.Context) (int, error) {
_spec := &sqlgraph.DeleteSpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
if ps := ard.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, ard.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return affected, err
}
// AuthRolesDeleteOne is the builder for deleting a single AuthRoles entity.
type AuthRolesDeleteOne struct {
ard *AuthRolesDelete
}
// Exec executes the deletion query.
func (ardo *AuthRolesDeleteOne) Exec(ctx context.Context) error {
n, err := ardo.ard.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{authroles.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (ardo *AuthRolesDeleteOne) ExecX(ctx context.Context) {
ardo.ard.ExecX(ctx)
}

View file

@ -0,0 +1,633 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"math"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
)
// AuthRolesQuery is the builder for querying AuthRoles entities.
type AuthRolesQuery struct {
config
limit *int
offset *int
unique *bool
order []OrderFunc
fields []string
predicates []predicate.AuthRoles
withToken *AuthTokensQuery
withFKs bool
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the AuthRolesQuery builder.
func (arq *AuthRolesQuery) Where(ps ...predicate.AuthRoles) *AuthRolesQuery {
arq.predicates = append(arq.predicates, ps...)
return arq
}
// Limit adds a limit step to the query.
func (arq *AuthRolesQuery) Limit(limit int) *AuthRolesQuery {
arq.limit = &limit
return arq
}
// Offset adds an offset step to the query.
func (arq *AuthRolesQuery) Offset(offset int) *AuthRolesQuery {
arq.offset = &offset
return arq
}
// Unique configures the query builder to filter duplicate records on query.
// By default, unique is set to true, and can be disabled using this method.
func (arq *AuthRolesQuery) Unique(unique bool) *AuthRolesQuery {
arq.unique = &unique
return arq
}
// Order adds an order step to the query.
func (arq *AuthRolesQuery) Order(o ...OrderFunc) *AuthRolesQuery {
arq.order = append(arq.order, o...)
return arq
}
// QueryToken chains the current query on the "token" edge.
func (arq *AuthRolesQuery) QueryToken() *AuthTokensQuery {
query := &AuthTokensQuery{config: arq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := arq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := arq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(authroles.Table, authroles.FieldID, selector),
sqlgraph.To(authtokens.Table, authtokens.FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, authroles.TokenTable, authroles.TokenColumn),
)
fromU = sqlgraph.SetNeighbors(arq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first AuthRoles entity from the query.
// Returns a *NotFoundError when no AuthRoles was found.
func (arq *AuthRolesQuery) First(ctx context.Context) (*AuthRoles, error) {
nodes, err := arq.Limit(1).All(ctx)
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{authroles.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (arq *AuthRolesQuery) FirstX(ctx context.Context) *AuthRoles {
node, err := arq.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first AuthRoles ID from the query.
// Returns a *NotFoundError when no AuthRoles ID was found.
func (arq *AuthRolesQuery) FirstID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = arq.Limit(1).IDs(ctx); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{authroles.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (arq *AuthRolesQuery) FirstIDX(ctx context.Context) int {
id, err := arq.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single AuthRoles entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one AuthRoles entity is found.
// Returns a *NotFoundError when no AuthRoles entities are found.
func (arq *AuthRolesQuery) Only(ctx context.Context) (*AuthRoles, error) {
nodes, err := arq.Limit(2).All(ctx)
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{authroles.Label}
default:
return nil, &NotSingularError{authroles.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (arq *AuthRolesQuery) OnlyX(ctx context.Context) *AuthRoles {
node, err := arq.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only AuthRoles ID in the query.
// Returns a *NotSingularError when more than one AuthRoles ID is found.
// Returns a *NotFoundError when no entities are found.
func (arq *AuthRolesQuery) OnlyID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = arq.Limit(2).IDs(ctx); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{authroles.Label}
default:
err = &NotSingularError{authroles.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (arq *AuthRolesQuery) OnlyIDX(ctx context.Context) int {
id, err := arq.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of AuthRolesSlice.
func (arq *AuthRolesQuery) All(ctx context.Context) ([]*AuthRoles, error) {
if err := arq.prepareQuery(ctx); err != nil {
return nil, err
}
return arq.sqlAll(ctx)
}
// AllX is like All, but panics if an error occurs.
func (arq *AuthRolesQuery) AllX(ctx context.Context) []*AuthRoles {
nodes, err := arq.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of AuthRoles IDs.
func (arq *AuthRolesQuery) IDs(ctx context.Context) ([]int, error) {
var ids []int
if err := arq.Select(authroles.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (arq *AuthRolesQuery) IDsX(ctx context.Context) []int {
ids, err := arq.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (arq *AuthRolesQuery) Count(ctx context.Context) (int, error) {
if err := arq.prepareQuery(ctx); err != nil {
return 0, err
}
return arq.sqlCount(ctx)
}
// CountX is like Count, but panics if an error occurs.
func (arq *AuthRolesQuery) CountX(ctx context.Context) int {
count, err := arq.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (arq *AuthRolesQuery) Exist(ctx context.Context) (bool, error) {
if err := arq.prepareQuery(ctx); err != nil {
return false, err
}
return arq.sqlExist(ctx)
}
// ExistX is like Exist, but panics if an error occurs.
func (arq *AuthRolesQuery) ExistX(ctx context.Context) bool {
exist, err := arq.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the AuthRolesQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (arq *AuthRolesQuery) Clone() *AuthRolesQuery {
if arq == nil {
return nil
}
return &AuthRolesQuery{
config: arq.config,
limit: arq.limit,
offset: arq.offset,
order: append([]OrderFunc{}, arq.order...),
predicates: append([]predicate.AuthRoles{}, arq.predicates...),
withToken: arq.withToken.Clone(),
// clone intermediate query.
sql: arq.sql.Clone(),
path: arq.path,
unique: arq.unique,
}
}
// WithToken tells the query-builder to eager-load the nodes that are connected to
// the "token" edge. The optional arguments are used to configure the query builder of the edge.
func (arq *AuthRolesQuery) WithToken(opts ...func(*AuthTokensQuery)) *AuthRolesQuery {
query := &AuthTokensQuery{config: arq.config}
for _, opt := range opts {
opt(query)
}
arq.withToken = query
return arq
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// Role authroles.Role `json:"role,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.AuthRoles.Query().
// GroupBy(authroles.FieldRole).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (arq *AuthRolesQuery) GroupBy(field string, fields ...string) *AuthRolesGroupBy {
grbuild := &AuthRolesGroupBy{config: arq.config}
grbuild.fields = append([]string{field}, fields...)
grbuild.path = func(ctx context.Context) (prev *sql.Selector, err error) {
if err := arq.prepareQuery(ctx); err != nil {
return nil, err
}
return arq.sqlQuery(ctx), nil
}
grbuild.label = authroles.Label
grbuild.flds, grbuild.scan = &grbuild.fields, grbuild.Scan
return grbuild
}
// Select allows the selection one or more fields/columns for the given query,
// instead of selecting all fields in the entity.
//
// Example:
//
// var v []struct {
// Role authroles.Role `json:"role,omitempty"`
// }
//
// client.AuthRoles.Query().
// Select(authroles.FieldRole).
// Scan(ctx, &v)
func (arq *AuthRolesQuery) Select(fields ...string) *AuthRolesSelect {
arq.fields = append(arq.fields, fields...)
selbuild := &AuthRolesSelect{AuthRolesQuery: arq}
selbuild.label = authroles.Label
selbuild.flds, selbuild.scan = &arq.fields, selbuild.Scan
return selbuild
}
// Aggregate returns a AuthRolesSelect configured with the given aggregations.
func (arq *AuthRolesQuery) Aggregate(fns ...AggregateFunc) *AuthRolesSelect {
return arq.Select().Aggregate(fns...)
}
func (arq *AuthRolesQuery) prepareQuery(ctx context.Context) error {
for _, f := range arq.fields {
if !authroles.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if arq.path != nil {
prev, err := arq.path(ctx)
if err != nil {
return err
}
arq.sql = prev
}
return nil
}
func (arq *AuthRolesQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*AuthRoles, error) {
var (
nodes = []*AuthRoles{}
withFKs = arq.withFKs
_spec = arq.querySpec()
loadedTypes = [1]bool{
arq.withToken != nil,
}
)
if arq.withToken != nil {
withFKs = true
}
if withFKs {
_spec.Node.Columns = append(_spec.Node.Columns, authroles.ForeignKeys...)
}
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*AuthRoles).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &AuthRoles{config: arq.config}
nodes = append(nodes, node)
node.Edges.loadedTypes = loadedTypes
return node.assignValues(columns, values)
}
for i := range hooks {
hooks[i](ctx, _spec)
}
if err := sqlgraph.QueryNodes(ctx, arq.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := arq.withToken; query != nil {
if err := arq.loadToken(ctx, query, nodes, nil,
func(n *AuthRoles, e *AuthTokens) { n.Edges.Token = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (arq *AuthRolesQuery) loadToken(ctx context.Context, query *AuthTokensQuery, nodes []*AuthRoles, init func(*AuthRoles), assign func(*AuthRoles, *AuthTokens)) error {
ids := make([]uuid.UUID, 0, len(nodes))
nodeids := make(map[uuid.UUID][]*AuthRoles)
for i := range nodes {
if nodes[i].auth_tokens_roles == nil {
continue
}
fk := *nodes[i].auth_tokens_roles
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
query.Where(authtokens.IDIn(ids...))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nodeids[n.ID]
if !ok {
return fmt.Errorf(`unexpected foreign-key "auth_tokens_roles" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (arq *AuthRolesQuery) sqlCount(ctx context.Context) (int, error) {
_spec := arq.querySpec()
_spec.Node.Columns = arq.fields
if len(arq.fields) > 0 {
_spec.Unique = arq.unique != nil && *arq.unique
}
return sqlgraph.CountNodes(ctx, arq.driver, _spec)
}
func (arq *AuthRolesQuery) sqlExist(ctx context.Context) (bool, error) {
switch _, err := arq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
func (arq *AuthRolesQuery) querySpec() *sqlgraph.QuerySpec {
_spec := &sqlgraph.QuerySpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
Columns: authroles.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
From: arq.sql,
Unique: true,
}
if unique := arq.unique; unique != nil {
_spec.Unique = *unique
}
if fields := arq.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, authroles.FieldID)
for i := range fields {
if fields[i] != authroles.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
}
if ps := arq.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := arq.limit; limit != nil {
_spec.Limit = *limit
}
if offset := arq.offset; offset != nil {
_spec.Offset = *offset
}
if ps := arq.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (arq *AuthRolesQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(arq.driver.Dialect())
t1 := builder.Table(authroles.Table)
columns := arq.fields
if len(columns) == 0 {
columns = authroles.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if arq.sql != nil {
selector = arq.sql
selector.Select(selector.Columns(columns...)...)
}
if arq.unique != nil && *arq.unique {
selector.Distinct()
}
for _, p := range arq.predicates {
p(selector)
}
for _, p := range arq.order {
p(selector)
}
if offset := arq.offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt32)
}
if limit := arq.limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// AuthRolesGroupBy is the group-by builder for AuthRoles entities.
type AuthRolesGroupBy struct {
config
selector
fields []string
fns []AggregateFunc
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Aggregate adds the given aggregation functions to the group-by query.
func (argb *AuthRolesGroupBy) Aggregate(fns ...AggregateFunc) *AuthRolesGroupBy {
argb.fns = append(argb.fns, fns...)
return argb
}
// Scan applies the group-by query and scans the result into the given value.
func (argb *AuthRolesGroupBy) Scan(ctx context.Context, v any) error {
query, err := argb.path(ctx)
if err != nil {
return err
}
argb.sql = query
return argb.sqlScan(ctx, v)
}
func (argb *AuthRolesGroupBy) sqlScan(ctx context.Context, v any) error {
for _, f := range argb.fields {
if !authroles.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)}
}
}
selector := argb.sqlQuery()
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := argb.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
func (argb *AuthRolesGroupBy) sqlQuery() *sql.Selector {
selector := argb.sql.Select()
aggregation := make([]string, 0, len(argb.fns))
for _, fn := range argb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(argb.fields)+len(argb.fns))
for _, f := range argb.fields {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
return selector.GroupBy(selector.Columns(argb.fields...)...)
}
// AuthRolesSelect is the builder for selecting fields of AuthRoles entities.
type AuthRolesSelect struct {
*AuthRolesQuery
selector
// intermediate query (i.e. traversal path).
sql *sql.Selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (ars *AuthRolesSelect) Aggregate(fns ...AggregateFunc) *AuthRolesSelect {
ars.fns = append(ars.fns, fns...)
return ars
}
// Scan applies the selector query and scans the result into the given value.
func (ars *AuthRolesSelect) Scan(ctx context.Context, v any) error {
if err := ars.prepareQuery(ctx); err != nil {
return err
}
ars.sql = ars.AuthRolesQuery.sqlQuery(ctx)
return ars.sqlScan(ctx, v)
}
func (ars *AuthRolesSelect) sqlScan(ctx context.Context, v any) error {
aggregation := make([]string, 0, len(ars.fns))
for _, fn := range ars.fns {
aggregation = append(aggregation, fn(ars.sql))
}
switch n := len(*ars.selector.flds); {
case n == 0 && len(aggregation) > 0:
ars.sql.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
ars.sql.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := ars.sql.Query()
if err := ars.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View file

@ -0,0 +1,433 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
)
// AuthRolesUpdate is the builder for updating AuthRoles entities.
type AuthRolesUpdate struct {
config
hooks []Hook
mutation *AuthRolesMutation
}
// Where appends a list predicates to the AuthRolesUpdate builder.
func (aru *AuthRolesUpdate) Where(ps ...predicate.AuthRoles) *AuthRolesUpdate {
aru.mutation.Where(ps...)
return aru
}
// SetRole sets the "role" field.
func (aru *AuthRolesUpdate) SetRole(a authroles.Role) *AuthRolesUpdate {
aru.mutation.SetRole(a)
return aru
}
// SetNillableRole sets the "role" field if the given value is not nil.
func (aru *AuthRolesUpdate) SetNillableRole(a *authroles.Role) *AuthRolesUpdate {
if a != nil {
aru.SetRole(*a)
}
return aru
}
// SetTokenID sets the "token" edge to the AuthTokens entity by ID.
func (aru *AuthRolesUpdate) SetTokenID(id uuid.UUID) *AuthRolesUpdate {
aru.mutation.SetTokenID(id)
return aru
}
// SetNillableTokenID sets the "token" edge to the AuthTokens entity by ID if the given value is not nil.
func (aru *AuthRolesUpdate) SetNillableTokenID(id *uuid.UUID) *AuthRolesUpdate {
if id != nil {
aru = aru.SetTokenID(*id)
}
return aru
}
// SetToken sets the "token" edge to the AuthTokens entity.
func (aru *AuthRolesUpdate) SetToken(a *AuthTokens) *AuthRolesUpdate {
return aru.SetTokenID(a.ID)
}
// Mutation returns the AuthRolesMutation object of the builder.
func (aru *AuthRolesUpdate) Mutation() *AuthRolesMutation {
return aru.mutation
}
// ClearToken clears the "token" edge to the AuthTokens entity.
func (aru *AuthRolesUpdate) ClearToken() *AuthRolesUpdate {
aru.mutation.ClearToken()
return aru
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (aru *AuthRolesUpdate) Save(ctx context.Context) (int, error) {
var (
err error
affected int
)
if len(aru.hooks) == 0 {
if err = aru.check(); err != nil {
return 0, err
}
affected, err = aru.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = aru.check(); err != nil {
return 0, err
}
aru.mutation = mutation
affected, err = aru.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(aru.hooks) - 1; i >= 0; i-- {
if aru.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = aru.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, aru.mutation); err != nil {
return 0, err
}
}
return affected, err
}
// SaveX is like Save, but panics if an error occurs.
func (aru *AuthRolesUpdate) SaveX(ctx context.Context) int {
affected, err := aru.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (aru *AuthRolesUpdate) Exec(ctx context.Context) error {
_, err := aru.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (aru *AuthRolesUpdate) ExecX(ctx context.Context) {
if err := aru.Exec(ctx); err != nil {
panic(err)
}
}
// check runs all checks and user-defined validators on the builder.
func (aru *AuthRolesUpdate) check() error {
if v, ok := aru.mutation.Role(); ok {
if err := authroles.RoleValidator(v); err != nil {
return &ValidationError{Name: "role", err: fmt.Errorf(`ent: validator failed for field "AuthRoles.role": %w`, err)}
}
}
return nil
}
func (aru *AuthRolesUpdate) sqlSave(ctx context.Context) (n int, err error) {
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
Columns: authroles.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
if ps := aru.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := aru.mutation.Role(); ok {
_spec.SetField(authroles.FieldRole, field.TypeEnum, value)
}
if aru.mutation.TokenCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: true,
Table: authroles.TokenTable,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := aru.mutation.TokenIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: true,
Table: authroles.TokenTable,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if n, err = sqlgraph.UpdateNodes(ctx, aru.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{authroles.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
return n, nil
}
// AuthRolesUpdateOne is the builder for updating a single AuthRoles entity.
type AuthRolesUpdateOne struct {
config
fields []string
hooks []Hook
mutation *AuthRolesMutation
}
// SetRole sets the "role" field.
func (aruo *AuthRolesUpdateOne) SetRole(a authroles.Role) *AuthRolesUpdateOne {
aruo.mutation.SetRole(a)
return aruo
}
// SetNillableRole sets the "role" field if the given value is not nil.
func (aruo *AuthRolesUpdateOne) SetNillableRole(a *authroles.Role) *AuthRolesUpdateOne {
if a != nil {
aruo.SetRole(*a)
}
return aruo
}
// SetTokenID sets the "token" edge to the AuthTokens entity by ID.
func (aruo *AuthRolesUpdateOne) SetTokenID(id uuid.UUID) *AuthRolesUpdateOne {
aruo.mutation.SetTokenID(id)
return aruo
}
// SetNillableTokenID sets the "token" edge to the AuthTokens entity by ID if the given value is not nil.
func (aruo *AuthRolesUpdateOne) SetNillableTokenID(id *uuid.UUID) *AuthRolesUpdateOne {
if id != nil {
aruo = aruo.SetTokenID(*id)
}
return aruo
}
// SetToken sets the "token" edge to the AuthTokens entity.
func (aruo *AuthRolesUpdateOne) SetToken(a *AuthTokens) *AuthRolesUpdateOne {
return aruo.SetTokenID(a.ID)
}
// Mutation returns the AuthRolesMutation object of the builder.
func (aruo *AuthRolesUpdateOne) Mutation() *AuthRolesMutation {
return aruo.mutation
}
// ClearToken clears the "token" edge to the AuthTokens entity.
func (aruo *AuthRolesUpdateOne) ClearToken() *AuthRolesUpdateOne {
aruo.mutation.ClearToken()
return aruo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (aruo *AuthRolesUpdateOne) Select(field string, fields ...string) *AuthRolesUpdateOne {
aruo.fields = append([]string{field}, fields...)
return aruo
}
// Save executes the query and returns the updated AuthRoles entity.
func (aruo *AuthRolesUpdateOne) Save(ctx context.Context) (*AuthRoles, error) {
var (
err error
node *AuthRoles
)
if len(aruo.hooks) == 0 {
if err = aruo.check(); err != nil {
return nil, err
}
node, err = aruo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = aruo.check(); err != nil {
return nil, err
}
aruo.mutation = mutation
node, err = aruo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(aruo.hooks) - 1; i >= 0; i-- {
if aruo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = aruo.hooks[i](mut)
}
v, err := mut.Mutate(ctx, aruo.mutation)
if err != nil {
return nil, err
}
nv, ok := v.(*AuthRoles)
if !ok {
return nil, fmt.Errorf("unexpected node type %T returned from AuthRolesMutation", v)
}
node = nv
}
return node, err
}
// SaveX is like Save, but panics if an error occurs.
func (aruo *AuthRolesUpdateOne) SaveX(ctx context.Context) *AuthRoles {
node, err := aruo.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (aruo *AuthRolesUpdateOne) Exec(ctx context.Context) error {
_, err := aruo.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (aruo *AuthRolesUpdateOne) ExecX(ctx context.Context) {
if err := aruo.Exec(ctx); err != nil {
panic(err)
}
}
// check runs all checks and user-defined validators on the builder.
func (aruo *AuthRolesUpdateOne) check() error {
if v, ok := aruo.mutation.Role(); ok {
if err := authroles.RoleValidator(v); err != nil {
return &ValidationError{Name: "role", err: fmt.Errorf(`ent: validator failed for field "AuthRoles.role": %w`, err)}
}
}
return nil
}
func (aruo *AuthRolesUpdateOne) sqlSave(ctx context.Context) (_node *AuthRoles, err error) {
_spec := &sqlgraph.UpdateSpec{
Node: &sqlgraph.NodeSpec{
Table: authroles.Table,
Columns: authroles.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
id, ok := aruo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AuthRoles.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := aruo.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, authroles.FieldID)
for _, f := range fields {
if !authroles.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != authroles.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := aruo.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := aruo.mutation.Role(); ok {
_spec.SetField(authroles.FieldRole, field.TypeEnum, value)
}
if aruo.mutation.TokenCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: true,
Table: authroles.TokenTable,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := aruo.mutation.TokenIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: true,
Table: authroles.TokenTable,
Columns: []string{authroles.TokenColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeUUID,
Column: authtokens.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &AuthRoles{config: aruo.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, aruo.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{authroles.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
return _node, nil
}

View file

@ -9,6 +9,7 @@ import (
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/user" "github.com/hay-kot/homebox/backend/internal/data/ent/user"
) )
@ -36,9 +37,11 @@ type AuthTokens struct {
type AuthTokensEdges struct { type AuthTokensEdges struct {
// User holds the value of the user edge. // User holds the value of the user edge.
User *User `json:"user,omitempty"` User *User `json:"user,omitempty"`
// Roles holds the value of the roles edge.
Roles *AuthRoles `json:"roles,omitempty"`
// loadedTypes holds the information for reporting if a // loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not. // type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool loadedTypes [2]bool
} }
// UserOrErr returns the User value or an error if the edge // UserOrErr returns the User value or an error if the edge
@ -54,6 +57,19 @@ func (e AuthTokensEdges) UserOrErr() (*User, error) {
return nil, &NotLoadedError{edge: "user"} return nil, &NotLoadedError{edge: "user"}
} }
// RolesOrErr returns the Roles value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e AuthTokensEdges) RolesOrErr() (*AuthRoles, error) {
if e.loadedTypes[1] {
if e.Roles == nil {
// Edge was loaded but was not found.
return nil, &NotFoundError{label: authroles.Label}
}
return e.Roles, nil
}
return nil, &NotLoadedError{edge: "roles"}
}
// scanValues returns the types for scanning values from sql.Rows. // scanValues returns the types for scanning values from sql.Rows.
func (*AuthTokens) scanValues(columns []string) ([]any, error) { func (*AuthTokens) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns)) values := make([]any, len(columns))
@ -129,6 +145,11 @@ func (at *AuthTokens) QueryUser() *UserQuery {
return (&AuthTokensClient{config: at.config}).QueryUser(at) return (&AuthTokensClient{config: at.config}).QueryUser(at)
} }
// QueryRoles queries the "roles" edge of the AuthTokens entity.
func (at *AuthTokens) QueryRoles() *AuthRolesQuery {
return (&AuthTokensClient{config: at.config}).QueryRoles(at)
}
// Update returns a builder for updating this AuthTokens. // Update returns a builder for updating this AuthTokens.
// Note that you need to call AuthTokens.Unwrap() before calling this method if this AuthTokens // Note that you need to call AuthTokens.Unwrap() before calling this method if this AuthTokens
// was returned from a transaction, and the transaction was committed or rolled back. // was returned from a transaction, and the transaction was committed or rolled back.

View file

@ -23,6 +23,8 @@ const (
FieldExpiresAt = "expires_at" FieldExpiresAt = "expires_at"
// EdgeUser holds the string denoting the user edge name in mutations. // EdgeUser holds the string denoting the user edge name in mutations.
EdgeUser = "user" EdgeUser = "user"
// EdgeRoles holds the string denoting the roles edge name in mutations.
EdgeRoles = "roles"
// Table holds the table name of the authtokens in the database. // Table holds the table name of the authtokens in the database.
Table = "auth_tokens" Table = "auth_tokens"
// UserTable is the table that holds the user relation/edge. // UserTable is the table that holds the user relation/edge.
@ -32,6 +34,13 @@ const (
UserInverseTable = "users" UserInverseTable = "users"
// UserColumn is the table column denoting the user relation/edge. // UserColumn is the table column denoting the user relation/edge.
UserColumn = "user_auth_tokens" UserColumn = "user_auth_tokens"
// RolesTable is the table that holds the roles relation/edge.
RolesTable = "auth_roles"
// RolesInverseTable is the table name for the AuthRoles entity.
// It exists in this package in order to avoid circular dependency with the "authroles" package.
RolesInverseTable = "auth_roles"
// RolesColumn is the table column denoting the roles relation/edge.
RolesColumn = "auth_tokens_roles"
) )
// Columns holds all SQL columns for authtokens fields. // Columns holds all SQL columns for authtokens fields.

View file

@ -394,6 +394,34 @@ func HasUserWith(preds ...predicate.User) predicate.AuthTokens {
}) })
} }
// HasRoles applies the HasEdge predicate on the "roles" edge.
func HasRoles() predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(RolesTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasRolesWith applies the HasEdge predicate on the "roles" edge with a given conditions (other predicates).
func HasRolesWith(preds ...predicate.AuthRoles) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(RolesInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, RolesTable, RolesColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.AuthTokens) predicate.AuthTokens { func And(predicates ...predicate.AuthTokens) predicate.AuthTokens {
return predicate.AuthTokens(func(s *sql.Selector) { return predicate.AuthTokens(func(s *sql.Selector) {

View file

@ -11,6 +11,7 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/user" "github.com/hay-kot/homebox/backend/internal/data/ent/user"
) )
@ -103,6 +104,25 @@ func (atc *AuthTokensCreate) SetUser(u *User) *AuthTokensCreate {
return atc.SetUserID(u.ID) return atc.SetUserID(u.ID)
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by ID.
func (atc *AuthTokensCreate) SetRolesID(id int) *AuthTokensCreate {
atc.mutation.SetRolesID(id)
return atc
}
// SetNillableRolesID sets the "roles" edge to the AuthRoles entity by ID if the given value is not nil.
func (atc *AuthTokensCreate) SetNillableRolesID(id *int) *AuthTokensCreate {
if id != nil {
atc = atc.SetRolesID(*id)
}
return atc
}
// SetRoles sets the "roles" edge to the AuthRoles entity.
func (atc *AuthTokensCreate) SetRoles(a *AuthRoles) *AuthTokensCreate {
return atc.SetRolesID(a.ID)
}
// Mutation returns the AuthTokensMutation object of the builder. // Mutation returns the AuthTokensMutation object of the builder.
func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation { func (atc *AuthTokensCreate) Mutation() *AuthTokensMutation {
return atc.mutation return atc.mutation
@ -284,6 +304,25 @@ func (atc *AuthTokensCreate) createSpec() (*AuthTokens, *sqlgraph.CreateSpec) {
_node.user_auth_tokens = &nodes[0] _node.user_auth_tokens = &nodes[0]
_spec.Edges = append(_spec.Edges, edge) _spec.Edges = append(_spec.Edges, edge)
} }
if nodes := atc.mutation.RolesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec return _node, _spec
} }

View file

@ -4,6 +4,7 @@ package ent
import ( import (
"context" "context"
"database/sql/driver"
"fmt" "fmt"
"math" "math"
@ -11,6 +12,7 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "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/predicate"
"github.com/hay-kot/homebox/backend/internal/data/ent/user" "github.com/hay-kot/homebox/backend/internal/data/ent/user"
@ -26,6 +28,7 @@ type AuthTokensQuery struct {
fields []string fields []string
predicates []predicate.AuthTokens predicates []predicate.AuthTokens
withUser *UserQuery withUser *UserQuery
withRoles *AuthRolesQuery
withFKs bool withFKs bool
// intermediate query (i.e. traversal path). // intermediate query (i.e. traversal path).
sql *sql.Selector sql *sql.Selector
@ -85,6 +88,28 @@ func (atq *AuthTokensQuery) QueryUser() *UserQuery {
return query return query
} }
// QueryRoles chains the current query on the "roles" edge.
func (atq *AuthTokensQuery) QueryRoles() *AuthRolesQuery {
query := &AuthRolesQuery{config: atq.config}
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := atq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(authtokens.Table, authtokens.FieldID, selector),
sqlgraph.To(authroles.Table, authroles.FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, authtokens.RolesTable, authtokens.RolesColumn),
)
fromU = sqlgraph.SetNeighbors(atq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first AuthTokens entity from the query. // First returns the first AuthTokens entity from the query.
// Returns a *NotFoundError when no AuthTokens was found. // Returns a *NotFoundError when no AuthTokens was found.
func (atq *AuthTokensQuery) First(ctx context.Context) (*AuthTokens, error) { func (atq *AuthTokensQuery) First(ctx context.Context) (*AuthTokens, error) {
@ -267,6 +292,7 @@ func (atq *AuthTokensQuery) Clone() *AuthTokensQuery {
order: append([]OrderFunc{}, atq.order...), order: append([]OrderFunc{}, atq.order...),
predicates: append([]predicate.AuthTokens{}, atq.predicates...), predicates: append([]predicate.AuthTokens{}, atq.predicates...),
withUser: atq.withUser.Clone(), withUser: atq.withUser.Clone(),
withRoles: atq.withRoles.Clone(),
// clone intermediate query. // clone intermediate query.
sql: atq.sql.Clone(), sql: atq.sql.Clone(),
path: atq.path, path: atq.path,
@ -285,6 +311,17 @@ func (atq *AuthTokensQuery) WithUser(opts ...func(*UserQuery)) *AuthTokensQuery
return atq return atq
} }
// WithRoles tells the query-builder to eager-load the nodes that are connected to
// the "roles" edge. The optional arguments are used to configure the query builder of the edge.
func (atq *AuthTokensQuery) WithRoles(opts ...func(*AuthRolesQuery)) *AuthTokensQuery {
query := &AuthRolesQuery{config: atq.config}
for _, opt := range opts {
opt(query)
}
atq.withRoles = query
return atq
}
// GroupBy is used to group vertices by one or more fields/columns. // GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum. // It is often used with aggregate functions, like: count, max, mean, min, sum.
// //
@ -359,8 +396,9 @@ func (atq *AuthTokensQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*
nodes = []*AuthTokens{} nodes = []*AuthTokens{}
withFKs = atq.withFKs withFKs = atq.withFKs
_spec = atq.querySpec() _spec = atq.querySpec()
loadedTypes = [1]bool{ loadedTypes = [2]bool{
atq.withUser != nil, atq.withUser != nil,
atq.withRoles != nil,
} }
) )
if atq.withUser != nil { if atq.withUser != nil {
@ -393,6 +431,12 @@ func (atq *AuthTokensQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*
return nil, err return nil, err
} }
} }
if query := atq.withRoles; query != nil {
if err := atq.loadRoles(ctx, query, nodes, nil,
func(n *AuthTokens, e *AuthRoles) { n.Edges.Roles = e }); err != nil {
return nil, err
}
}
return nodes, nil return nodes, nil
} }
@ -425,6 +469,34 @@ func (atq *AuthTokensQuery) loadUser(ctx context.Context, query *UserQuery, node
} }
return nil return nil
} }
func (atq *AuthTokensQuery) loadRoles(ctx context.Context, query *AuthRolesQuery, nodes []*AuthTokens, init func(*AuthTokens), assign func(*AuthTokens, *AuthRoles)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[uuid.UUID]*AuthTokens)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
}
query.withFKs = true
query.Where(predicate.AuthRoles(func(s *sql.Selector) {
s.Where(sql.InValues(authtokens.RolesColumn, fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.auth_tokens_roles
if fk == nil {
return fmt.Errorf(`foreign-key "auth_tokens_roles" is nil for node %v`, n.ID)
}
node, ok := nodeids[*fk]
if !ok {
return fmt.Errorf(`unexpected foreign-key "auth_tokens_roles" returned %v for node %v`, *fk, n.ID)
}
assign(node, n)
}
return nil
}
func (atq *AuthTokensQuery) sqlCount(ctx context.Context) (int, error) { func (atq *AuthTokensQuery) sqlCount(ctx context.Context) (int, error) {
_spec := atq.querySpec() _spec := atq.querySpec()

View file

@ -12,6 +12,7 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "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/predicate"
"github.com/hay-kot/homebox/backend/internal/data/ent/user" "github.com/hay-kot/homebox/backend/internal/data/ent/user"
@ -75,6 +76,25 @@ func (atu *AuthTokensUpdate) SetUser(u *User) *AuthTokensUpdate {
return atu.SetUserID(u.ID) return atu.SetUserID(u.ID)
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by ID.
func (atu *AuthTokensUpdate) SetRolesID(id int) *AuthTokensUpdate {
atu.mutation.SetRolesID(id)
return atu
}
// SetNillableRolesID sets the "roles" edge to the AuthRoles entity by ID if the given value is not nil.
func (atu *AuthTokensUpdate) SetNillableRolesID(id *int) *AuthTokensUpdate {
if id != nil {
atu = atu.SetRolesID(*id)
}
return atu
}
// SetRoles sets the "roles" edge to the AuthRoles entity.
func (atu *AuthTokensUpdate) SetRoles(a *AuthRoles) *AuthTokensUpdate {
return atu.SetRolesID(a.ID)
}
// Mutation returns the AuthTokensMutation object of the builder. // Mutation returns the AuthTokensMutation object of the builder.
func (atu *AuthTokensUpdate) Mutation() *AuthTokensMutation { func (atu *AuthTokensUpdate) Mutation() *AuthTokensMutation {
return atu.mutation return atu.mutation
@ -86,6 +106,12 @@ func (atu *AuthTokensUpdate) ClearUser() *AuthTokensUpdate {
return atu return atu
} }
// ClearRoles clears the "roles" edge to the AuthRoles entity.
func (atu *AuthTokensUpdate) ClearRoles() *AuthTokensUpdate {
atu.mutation.ClearRoles()
return atu
}
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) { func (atu *AuthTokensUpdate) Save(ctx context.Context) (int, error) {
var ( var (
@ -211,6 +237,41 @@ func (atu *AuthTokensUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
if atu.mutation.RolesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := atu.mutation.RolesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if n, err = sqlgraph.UpdateNodes(ctx, atu.driver, _spec); err != nil { if n, err = sqlgraph.UpdateNodes(ctx, atu.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{authtokens.Label} err = &NotFoundError{authtokens.Label}
@ -275,6 +336,25 @@ func (atuo *AuthTokensUpdateOne) SetUser(u *User) *AuthTokensUpdateOne {
return atuo.SetUserID(u.ID) return atuo.SetUserID(u.ID)
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by ID.
func (atuo *AuthTokensUpdateOne) SetRolesID(id int) *AuthTokensUpdateOne {
atuo.mutation.SetRolesID(id)
return atuo
}
// SetNillableRolesID sets the "roles" edge to the AuthRoles entity by ID if the given value is not nil.
func (atuo *AuthTokensUpdateOne) SetNillableRolesID(id *int) *AuthTokensUpdateOne {
if id != nil {
atuo = atuo.SetRolesID(*id)
}
return atuo
}
// SetRoles sets the "roles" edge to the AuthRoles entity.
func (atuo *AuthTokensUpdateOne) SetRoles(a *AuthRoles) *AuthTokensUpdateOne {
return atuo.SetRolesID(a.ID)
}
// Mutation returns the AuthTokensMutation object of the builder. // Mutation returns the AuthTokensMutation object of the builder.
func (atuo *AuthTokensUpdateOne) Mutation() *AuthTokensMutation { func (atuo *AuthTokensUpdateOne) Mutation() *AuthTokensMutation {
return atuo.mutation return atuo.mutation
@ -286,6 +366,12 @@ func (atuo *AuthTokensUpdateOne) ClearUser() *AuthTokensUpdateOne {
return atuo return atuo
} }
// ClearRoles clears the "roles" edge to the AuthRoles entity.
func (atuo *AuthTokensUpdateOne) ClearRoles() *AuthTokensUpdateOne {
atuo.mutation.ClearRoles()
return atuo
}
// Select allows selecting one or more fields (columns) of the returned entity. // Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema. // The default is selecting all fields defined in the entity schema.
func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTokensUpdateOne { func (atuo *AuthTokensUpdateOne) Select(field string, fields ...string) *AuthTokensUpdateOne {
@ -441,6 +527,41 @@ func (atuo *AuthTokensUpdateOne) sqlSave(ctx context.Context) (_node *AuthTokens
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
if atuo.mutation.RolesCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := atuo.mutation.RolesIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2O,
Inverse: false,
Table: authtokens.RolesTable,
Columns: []string{authtokens.RolesColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: authroles.FieldID,
},
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &AuthTokens{config: atuo.config} _node = &AuthTokens{config: atuo.config}
_spec.Assign = _node.assignValues _spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues _spec.ScanValues = _node.scanValues

View file

@ -12,6 +12,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/ent/migrate" "github.com/hay-kot/homebox/backend/internal/data/ent/migrate"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "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/documenttoken"
@ -35,6 +36,8 @@ type Client struct {
Schema *migrate.Schema Schema *migrate.Schema
// Attachment is the client for interacting with the Attachment builders. // Attachment is the client for interacting with the Attachment builders.
Attachment *AttachmentClient Attachment *AttachmentClient
// AuthRoles is the client for interacting with the AuthRoles builders.
AuthRoles *AuthRolesClient
// AuthTokens is the client for interacting with the AuthTokens builders. // AuthTokens is the client for interacting with the AuthTokens builders.
AuthTokens *AuthTokensClient AuthTokens *AuthTokensClient
// Document is the client for interacting with the Document builders. // Document is the client for interacting with the Document builders.
@ -69,6 +72,7 @@ func NewClient(opts ...Option) *Client {
func (c *Client) init() { func (c *Client) init() {
c.Schema = migrate.NewSchema(c.driver) c.Schema = migrate.NewSchema(c.driver)
c.Attachment = NewAttachmentClient(c.config) c.Attachment = NewAttachmentClient(c.config)
c.AuthRoles = NewAuthRolesClient(c.config)
c.AuthTokens = NewAuthTokensClient(c.config) c.AuthTokens = NewAuthTokensClient(c.config)
c.Document = NewDocumentClient(c.config) c.Document = NewDocumentClient(c.config)
c.DocumentToken = NewDocumentTokenClient(c.config) c.DocumentToken = NewDocumentTokenClient(c.config)
@ -113,6 +117,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
ctx: ctx, ctx: ctx,
config: cfg, config: cfg,
Attachment: NewAttachmentClient(cfg), Attachment: NewAttachmentClient(cfg),
AuthRoles: NewAuthRolesClient(cfg),
AuthTokens: NewAuthTokensClient(cfg), AuthTokens: NewAuthTokensClient(cfg),
Document: NewDocumentClient(cfg), Document: NewDocumentClient(cfg),
DocumentToken: NewDocumentTokenClient(cfg), DocumentToken: NewDocumentTokenClient(cfg),
@ -143,6 +148,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
ctx: ctx, ctx: ctx,
config: cfg, config: cfg,
Attachment: NewAttachmentClient(cfg), Attachment: NewAttachmentClient(cfg),
AuthRoles: NewAuthRolesClient(cfg),
AuthTokens: NewAuthTokensClient(cfg), AuthTokens: NewAuthTokensClient(cfg),
Document: NewDocumentClient(cfg), Document: NewDocumentClient(cfg),
DocumentToken: NewDocumentTokenClient(cfg), DocumentToken: NewDocumentTokenClient(cfg),
@ -182,6 +188,7 @@ func (c *Client) Close() error {
// In order to add hooks to a specific client, call: `client.Node.Use(...)`. // In order to add hooks to a specific client, call: `client.Node.Use(...)`.
func (c *Client) Use(hooks ...Hook) { func (c *Client) Use(hooks ...Hook) {
c.Attachment.Use(hooks...) c.Attachment.Use(hooks...)
c.AuthRoles.Use(hooks...)
c.AuthTokens.Use(hooks...) c.AuthTokens.Use(hooks...)
c.Document.Use(hooks...) c.Document.Use(hooks...)
c.DocumentToken.Use(hooks...) c.DocumentToken.Use(hooks...)
@ -316,6 +323,112 @@ func (c *AttachmentClient) Hooks() []Hook {
return c.hooks.Attachment return c.hooks.Attachment
} }
// AuthRolesClient is a client for the AuthRoles schema.
type AuthRolesClient struct {
config
}
// NewAuthRolesClient returns a client for the AuthRoles from the given config.
func NewAuthRolesClient(c config) *AuthRolesClient {
return &AuthRolesClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `authroles.Hooks(f(g(h())))`.
func (c *AuthRolesClient) Use(hooks ...Hook) {
c.hooks.AuthRoles = append(c.hooks.AuthRoles, hooks...)
}
// Create returns a builder for creating a AuthRoles entity.
func (c *AuthRolesClient) Create() *AuthRolesCreate {
mutation := newAuthRolesMutation(c.config, OpCreate)
return &AuthRolesCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of AuthRoles entities.
func (c *AuthRolesClient) CreateBulk(builders ...*AuthRolesCreate) *AuthRolesCreateBulk {
return &AuthRolesCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for AuthRoles.
func (c *AuthRolesClient) Update() *AuthRolesUpdate {
mutation := newAuthRolesMutation(c.config, OpUpdate)
return &AuthRolesUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *AuthRolesClient) UpdateOne(ar *AuthRoles) *AuthRolesUpdateOne {
mutation := newAuthRolesMutation(c.config, OpUpdateOne, withAuthRoles(ar))
return &AuthRolesUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *AuthRolesClient) UpdateOneID(id int) *AuthRolesUpdateOne {
mutation := newAuthRolesMutation(c.config, OpUpdateOne, withAuthRolesID(id))
return &AuthRolesUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for AuthRoles.
func (c *AuthRolesClient) Delete() *AuthRolesDelete {
mutation := newAuthRolesMutation(c.config, OpDelete)
return &AuthRolesDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *AuthRolesClient) DeleteOne(ar *AuthRoles) *AuthRolesDeleteOne {
return c.DeleteOneID(ar.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *AuthRolesClient) DeleteOneID(id int) *AuthRolesDeleteOne {
builder := c.Delete().Where(authroles.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &AuthRolesDeleteOne{builder}
}
// Query returns a query builder for AuthRoles.
func (c *AuthRolesClient) Query() *AuthRolesQuery {
return &AuthRolesQuery{
config: c.config,
}
}
// Get returns a AuthRoles entity by its id.
func (c *AuthRolesClient) Get(ctx context.Context, id int) (*AuthRoles, error) {
return c.Query().Where(authroles.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *AuthRolesClient) GetX(ctx context.Context, id int) *AuthRoles {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryToken queries the token edge of a AuthRoles.
func (c *AuthRolesClient) QueryToken(ar *AuthRoles) *AuthTokensQuery {
query := &AuthTokensQuery{config: c.config}
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := ar.ID
step := sqlgraph.NewStep(
sqlgraph.From(authroles.Table, authroles.FieldID, id),
sqlgraph.To(authtokens.Table, authtokens.FieldID),
sqlgraph.Edge(sqlgraph.O2O, true, authroles.TokenTable, authroles.TokenColumn),
)
fromV = sqlgraph.Neighbors(ar.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *AuthRolesClient) Hooks() []Hook {
return c.hooks.AuthRoles
}
// AuthTokensClient is a client for the AuthTokens schema. // AuthTokensClient is a client for the AuthTokens schema.
type AuthTokensClient struct { type AuthTokensClient struct {
config config
@ -417,6 +530,22 @@ func (c *AuthTokensClient) QueryUser(at *AuthTokens) *UserQuery {
return query return query
} }
// QueryRoles queries the roles edge of a AuthTokens.
func (c *AuthTokensClient) QueryRoles(at *AuthTokens) *AuthRolesQuery {
query := &AuthRolesQuery{config: c.config}
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := at.ID
step := sqlgraph.NewStep(
sqlgraph.From(authtokens.Table, authtokens.FieldID, id),
sqlgraph.To(authroles.Table, authroles.FieldID),
sqlgraph.Edge(sqlgraph.O2O, false, authtokens.RolesTable, authtokens.RolesColumn),
)
fromV = sqlgraph.Neighbors(at.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks. // Hooks returns the client hooks.
func (c *AuthTokensClient) Hooks() []Hook { func (c *AuthTokensClient) Hooks() []Hook {
return c.hooks.AuthTokens return c.hooks.AuthTokens

View file

@ -25,6 +25,7 @@ type config struct {
// hooks per client, for fast access. // hooks per client, for fast access.
type hooks struct { type hooks struct {
Attachment []ent.Hook Attachment []ent.Hook
AuthRoles []ent.Hook
AuthTokens []ent.Hook AuthTokens []ent.Hook
Document []ent.Hook Document []ent.Hook
DocumentToken []ent.Hook DocumentToken []ent.Hook

View file

@ -11,6 +11,7 @@ import (
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "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/documenttoken"
@ -42,6 +43,7 @@ type OrderFunc func(*sql.Selector)
func columnChecker(table string) func(string) error { func columnChecker(table string) func(string) error {
checks := map[string]func(string) bool{ checks := map[string]func(string) bool{
attachment.Table: attachment.ValidColumn, attachment.Table: attachment.ValidColumn,
authroles.Table: authroles.ValidColumn,
authtokens.Table: authtokens.ValidColumn, authtokens.Table: authtokens.ValidColumn,
document.Table: document.ValidColumn, document.Table: document.ValidColumn,
documenttoken.Table: documenttoken.ValidColumn, documenttoken.Table: documenttoken.ValidColumn,

View file

@ -22,6 +22,19 @@ func (f AttachmentFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value,
return f(ctx, mv) return f(ctx, mv)
} }
// The AuthRolesFunc type is an adapter to allow the use of ordinary
// function as AuthRoles mutator.
type AuthRolesFunc func(context.Context, *ent.AuthRolesMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f AuthRolesFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
mv, ok := m.(*ent.AuthRolesMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AuthRolesMutation", m)
}
return f(ctx, mv)
}
// The AuthTokensFunc type is an adapter to allow the use of ordinary // The AuthTokensFunc type is an adapter to allow the use of ordinary
// function as AuthTokens mutator. // function as AuthTokens mutator.
type AuthTokensFunc func(context.Context, *ent.AuthTokensMutation) (ent.Value, error) type AuthTokensFunc func(context.Context, *ent.AuthTokensMutation) (ent.Value, error)

View file

@ -37,6 +37,26 @@ var (
}, },
}, },
} }
// AuthRolesColumns holds the columns for the "auth_roles" table.
AuthRolesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "role", Type: field.TypeEnum, Enums: []string{"admin", "user", "attachments"}, Default: "user"},
{Name: "auth_tokens_roles", Type: field.TypeUUID, Unique: true, Nullable: true},
}
// AuthRolesTable holds the schema information for the "auth_roles" table.
AuthRolesTable = &schema.Table{
Name: "auth_roles",
Columns: AuthRolesColumns,
PrimaryKey: []*schema.Column{AuthRolesColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "auth_roles_auth_tokens_roles",
Columns: []*schema.Column{AuthRolesColumns[2]},
RefColumns: []*schema.Column{AuthTokensColumns[0]},
OnDelete: schema.SetNull,
},
},
}
// AuthTokensColumns holds the columns for the "auth_tokens" table. // AuthTokensColumns holds the columns for the "auth_tokens" table.
AuthTokensColumns = []*schema.Column{ AuthTokensColumns = []*schema.Column{
{Name: "id", Type: field.TypeUUID}, {Name: "id", Type: field.TypeUUID},
@ -385,6 +405,7 @@ var (
// Tables holds all the tables in the schema. // Tables holds all the tables in the schema.
Tables = []*schema.Table{ Tables = []*schema.Table{
AttachmentsTable, AttachmentsTable,
AuthRolesTable,
AuthTokensTable, AuthTokensTable,
DocumentsTable, DocumentsTable,
DocumentTokensTable, DocumentTokensTable,
@ -402,6 +423,7 @@ var (
func init() { func init() {
AttachmentsTable.ForeignKeys[0].RefTable = DocumentsTable AttachmentsTable.ForeignKeys[0].RefTable = DocumentsTable
AttachmentsTable.ForeignKeys[1].RefTable = ItemsTable AttachmentsTable.ForeignKeys[1].RefTable = ItemsTable
AuthRolesTable.ForeignKeys[0].RefTable = AuthTokensTable
AuthTokensTable.ForeignKeys[0].RefTable = UsersTable AuthTokensTable.ForeignKeys[0].RefTable = UsersTable
DocumentsTable.ForeignKeys[0].RefTable = GroupsTable DocumentsTable.ForeignKeys[0].RefTable = GroupsTable
DocumentTokensTable.ForeignKeys[0].RefTable = DocumentsTable DocumentTokensTable.ForeignKeys[0].RefTable = DocumentsTable

View file

@ -11,6 +11,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment" "github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/document" "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/documenttoken"
@ -36,6 +37,7 @@ const (
// Node types. // Node types.
TypeAttachment = "Attachment" TypeAttachment = "Attachment"
TypeAuthRoles = "AuthRoles"
TypeAuthTokens = "AuthTokens" TypeAuthTokens = "AuthTokens"
TypeDocument = "Document" TypeDocument = "Document"
TypeDocumentToken = "DocumentToken" TypeDocumentToken = "DocumentToken"
@ -599,6 +601,384 @@ func (m *AttachmentMutation) ResetEdge(name string) error {
return fmt.Errorf("unknown Attachment edge %s", name) return fmt.Errorf("unknown Attachment edge %s", name)
} }
// AuthRolesMutation represents an operation that mutates the AuthRoles nodes in the graph.
type AuthRolesMutation struct {
config
op Op
typ string
id *int
role *authroles.Role
clearedFields map[string]struct{}
token *uuid.UUID
clearedtoken bool
done bool
oldValue func(context.Context) (*AuthRoles, error)
predicates []predicate.AuthRoles
}
var _ ent.Mutation = (*AuthRolesMutation)(nil)
// authrolesOption allows management of the mutation configuration using functional options.
type authrolesOption func(*AuthRolesMutation)
// newAuthRolesMutation creates new mutation for the AuthRoles entity.
func newAuthRolesMutation(c config, op Op, opts ...authrolesOption) *AuthRolesMutation {
m := &AuthRolesMutation{
config: c,
op: op,
typ: TypeAuthRoles,
clearedFields: make(map[string]struct{}),
}
for _, opt := range opts {
opt(m)
}
return m
}
// withAuthRolesID sets the ID field of the mutation.
func withAuthRolesID(id int) authrolesOption {
return func(m *AuthRolesMutation) {
var (
err error
once sync.Once
value *AuthRoles
)
m.oldValue = func(ctx context.Context) (*AuthRoles, error) {
once.Do(func() {
if m.done {
err = errors.New("querying old values post mutation is not allowed")
} else {
value, err = m.Client().AuthRoles.Get(ctx, id)
}
})
return value, err
}
m.id = &id
}
}
// withAuthRoles sets the old AuthRoles of the mutation.
func withAuthRoles(node *AuthRoles) authrolesOption {
return func(m *AuthRolesMutation) {
m.oldValue = func(context.Context) (*AuthRoles, error) {
return node, nil
}
m.id = &node.ID
}
}
// Client returns a new `ent.Client` from the mutation. If the mutation was
// executed in a transaction (ent.Tx), a transactional client is returned.
func (m AuthRolesMutation) Client() *Client {
client := &Client{config: m.config}
client.init()
return client
}
// Tx returns an `ent.Tx` for mutations that were executed in transactions;
// it returns an error otherwise.
func (m AuthRolesMutation) Tx() (*Tx, error) {
if _, ok := m.driver.(*txDriver); !ok {
return nil, errors.New("ent: mutation is not running in a transaction")
}
tx := &Tx{config: m.config}
tx.init()
return tx, nil
}
// ID returns the ID value in the mutation. Note that the ID is only available
// if it was provided to the builder or after it was returned from the database.
func (m *AuthRolesMutation) ID() (id int, exists bool) {
if m.id == nil {
return
}
return *m.id, true
}
// IDs queries the database and returns the entity ids that match the mutation's predicate.
// That means, if the mutation is applied within a transaction with an isolation level such
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
// or updated by the mutation.
func (m *AuthRolesMutation) IDs(ctx context.Context) ([]int, error) {
switch {
case m.op.Is(OpUpdateOne | OpDeleteOne):
id, exists := m.ID()
if exists {
return []int{id}, nil
}
fallthrough
case m.op.Is(OpUpdate | OpDelete):
return m.Client().AuthRoles.Query().Where(m.predicates...).IDs(ctx)
default:
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
}
}
// SetRole sets the "role" field.
func (m *AuthRolesMutation) SetRole(a authroles.Role) {
m.role = &a
}
// Role returns the value of the "role" field in the mutation.
func (m *AuthRolesMutation) Role() (r authroles.Role, exists bool) {
v := m.role
if v == nil {
return
}
return *v, true
}
// OldRole returns the old "role" field's value of the AuthRoles entity.
// If the AuthRoles object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *AuthRolesMutation) OldRole(ctx context.Context) (v authroles.Role, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldRole is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldRole requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldRole: %w", err)
}
return oldValue.Role, nil
}
// ResetRole resets all changes to the "role" field.
func (m *AuthRolesMutation) ResetRole() {
m.role = nil
}
// SetTokenID sets the "token" edge to the AuthTokens entity by id.
func (m *AuthRolesMutation) SetTokenID(id uuid.UUID) {
m.token = &id
}
// ClearToken clears the "token" edge to the AuthTokens entity.
func (m *AuthRolesMutation) ClearToken() {
m.clearedtoken = true
}
// TokenCleared reports if the "token" edge to the AuthTokens entity was cleared.
func (m *AuthRolesMutation) TokenCleared() bool {
return m.clearedtoken
}
// TokenID returns the "token" edge ID in the mutation.
func (m *AuthRolesMutation) TokenID() (id uuid.UUID, exists bool) {
if m.token != nil {
return *m.token, true
}
return
}
// TokenIDs returns the "token" edge IDs in the mutation.
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
// TokenID instead. It exists only for internal usage by the builders.
func (m *AuthRolesMutation) TokenIDs() (ids []uuid.UUID) {
if id := m.token; id != nil {
ids = append(ids, *id)
}
return
}
// ResetToken resets all changes to the "token" edge.
func (m *AuthRolesMutation) ResetToken() {
m.token = nil
m.clearedtoken = false
}
// Where appends a list predicates to the AuthRolesMutation builder.
func (m *AuthRolesMutation) Where(ps ...predicate.AuthRoles) {
m.predicates = append(m.predicates, ps...)
}
// Op returns the operation name.
func (m *AuthRolesMutation) Op() Op {
return m.op
}
// Type returns the node type of this mutation (AuthRoles).
func (m *AuthRolesMutation) Type() string {
return m.typ
}
// Fields returns all fields that were changed during this mutation. Note that in
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *AuthRolesMutation) Fields() []string {
fields := make([]string, 0, 1)
if m.role != nil {
fields = append(fields, authroles.FieldRole)
}
return fields
}
// Field returns the value of a field with the given name. The second boolean
// return value indicates that this field was not set, or was not defined in the
// schema.
func (m *AuthRolesMutation) Field(name string) (ent.Value, bool) {
switch name {
case authroles.FieldRole:
return m.Role()
}
return nil, false
}
// OldField returns the old value of the field from the database. An error is
// returned if the mutation operation is not UpdateOne, or the query to the
// database failed.
func (m *AuthRolesMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case authroles.FieldRole:
return m.OldRole(ctx)
}
return nil, fmt.Errorf("unknown AuthRoles field %s", name)
}
// SetField sets the value of a field with the given name. It returns an error if
// the field is not defined in the schema, or if the type mismatched the field
// type.
func (m *AuthRolesMutation) SetField(name string, value ent.Value) error {
switch name {
case authroles.FieldRole:
v, ok := value.(authroles.Role)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetRole(v)
return nil
}
return fmt.Errorf("unknown AuthRoles field %s", name)
}
// AddedFields returns all numeric fields that were incremented/decremented during
// this mutation.
func (m *AuthRolesMutation) AddedFields() []string {
return nil
}
// AddedField returns the numeric value that was incremented/decremented on a field
// with the given name. The second boolean return value indicates that this field
// was not set, or was not defined in the schema.
func (m *AuthRolesMutation) AddedField(name string) (ent.Value, bool) {
return nil, false
}
// AddField adds the value to the field with the given name. It returns an error if
// the field is not defined in the schema, or if the type mismatched the field
// type.
func (m *AuthRolesMutation) AddField(name string, value ent.Value) error {
switch name {
}
return fmt.Errorf("unknown AuthRoles numeric field %s", name)
}
// ClearedFields returns all nullable fields that were cleared during this
// mutation.
func (m *AuthRolesMutation) ClearedFields() []string {
return nil
}
// FieldCleared returns a boolean indicating if a field with the given name was
// cleared in this mutation.
func (m *AuthRolesMutation) FieldCleared(name string) bool {
_, ok := m.clearedFields[name]
return ok
}
// ClearField clears the value of the field with the given name. It returns an
// error if the field is not defined in the schema.
func (m *AuthRolesMutation) ClearField(name string) error {
return fmt.Errorf("unknown AuthRoles nullable field %s", name)
}
// ResetField resets all changes in the mutation for the field with the given name.
// It returns an error if the field is not defined in the schema.
func (m *AuthRolesMutation) ResetField(name string) error {
switch name {
case authroles.FieldRole:
m.ResetRole()
return nil
}
return fmt.Errorf("unknown AuthRoles field %s", name)
}
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *AuthRolesMutation) AddedEdges() []string {
edges := make([]string, 0, 1)
if m.token != nil {
edges = append(edges, authroles.EdgeToken)
}
return edges
}
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
// name in this mutation.
func (m *AuthRolesMutation) AddedIDs(name string) []ent.Value {
switch name {
case authroles.EdgeToken:
if id := m.token; id != nil {
return []ent.Value{*id}
}
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *AuthRolesMutation) RemovedEdges() []string {
edges := make([]string, 0, 1)
return edges
}
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
// the given name in this mutation.
func (m *AuthRolesMutation) RemovedIDs(name string) []ent.Value {
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *AuthRolesMutation) ClearedEdges() []string {
edges := make([]string, 0, 1)
if m.clearedtoken {
edges = append(edges, authroles.EdgeToken)
}
return edges
}
// EdgeCleared returns a boolean which indicates if the edge with the given name
// was cleared in this mutation.
func (m *AuthRolesMutation) EdgeCleared(name string) bool {
switch name {
case authroles.EdgeToken:
return m.clearedtoken
}
return false
}
// ClearEdge clears the value of the edge with the given name. It returns an error
// if that edge is not defined in the schema.
func (m *AuthRolesMutation) ClearEdge(name string) error {
switch name {
case authroles.EdgeToken:
m.ClearToken()
return nil
}
return fmt.Errorf("unknown AuthRoles unique edge %s", name)
}
// ResetEdge resets all changes to the edge with the given name in this mutation.
// It returns an error if the edge is not defined in the schema.
func (m *AuthRolesMutation) ResetEdge(name string) error {
switch name {
case authroles.EdgeToken:
m.ResetToken()
return nil
}
return fmt.Errorf("unknown AuthRoles edge %s", name)
}
// AuthTokensMutation represents an operation that mutates the AuthTokens nodes in the graph. // AuthTokensMutation represents an operation that mutates the AuthTokens nodes in the graph.
type AuthTokensMutation struct { type AuthTokensMutation struct {
config config
@ -612,6 +992,8 @@ type AuthTokensMutation struct {
clearedFields map[string]struct{} clearedFields map[string]struct{}
user *uuid.UUID user *uuid.UUID
cleareduser bool cleareduser bool
roles *int
clearedroles bool
done bool done bool
oldValue func(context.Context) (*AuthTokens, error) oldValue func(context.Context) (*AuthTokens, error)
predicates []predicate.AuthTokens predicates []predicate.AuthTokens
@ -904,6 +1286,45 @@ func (m *AuthTokensMutation) ResetUser() {
m.cleareduser = false m.cleareduser = false
} }
// SetRolesID sets the "roles" edge to the AuthRoles entity by id.
func (m *AuthTokensMutation) SetRolesID(id int) {
m.roles = &id
}
// ClearRoles clears the "roles" edge to the AuthRoles entity.
func (m *AuthTokensMutation) ClearRoles() {
m.clearedroles = true
}
// RolesCleared reports if the "roles" edge to the AuthRoles entity was cleared.
func (m *AuthTokensMutation) RolesCleared() bool {
return m.clearedroles
}
// RolesID returns the "roles" edge ID in the mutation.
func (m *AuthTokensMutation) RolesID() (id int, exists bool) {
if m.roles != nil {
return *m.roles, true
}
return
}
// RolesIDs returns the "roles" edge IDs in the mutation.
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
// RolesID instead. It exists only for internal usage by the builders.
func (m *AuthTokensMutation) RolesIDs() (ids []int) {
if id := m.roles; id != nil {
ids = append(ids, *id)
}
return
}
// ResetRoles resets all changes to the "roles" edge.
func (m *AuthTokensMutation) ResetRoles() {
m.roles = nil
m.clearedroles = false
}
// Where appends a list predicates to the AuthTokensMutation builder. // Where appends a list predicates to the AuthTokensMutation builder.
func (m *AuthTokensMutation) Where(ps ...predicate.AuthTokens) { func (m *AuthTokensMutation) Where(ps ...predicate.AuthTokens) {
m.predicates = append(m.predicates, ps...) m.predicates = append(m.predicates, ps...)
@ -1073,10 +1494,13 @@ func (m *AuthTokensMutation) ResetField(name string) error {
// AddedEdges returns all edge names that were set/added in this mutation. // AddedEdges returns all edge names that were set/added in this mutation.
func (m *AuthTokensMutation) AddedEdges() []string { func (m *AuthTokensMutation) AddedEdges() []string {
edges := make([]string, 0, 1) edges := make([]string, 0, 2)
if m.user != nil { if m.user != nil {
edges = append(edges, authtokens.EdgeUser) edges = append(edges, authtokens.EdgeUser)
} }
if m.roles != nil {
edges = append(edges, authtokens.EdgeRoles)
}
return edges return edges
} }
@ -1088,13 +1512,17 @@ func (m *AuthTokensMutation) AddedIDs(name string) []ent.Value {
if id := m.user; id != nil { if id := m.user; id != nil {
return []ent.Value{*id} return []ent.Value{*id}
} }
case authtokens.EdgeRoles:
if id := m.roles; id != nil {
return []ent.Value{*id}
}
} }
return nil return nil
} }
// RemovedEdges returns all edge names that were removed in this mutation. // RemovedEdges returns all edge names that were removed in this mutation.
func (m *AuthTokensMutation) RemovedEdges() []string { func (m *AuthTokensMutation) RemovedEdges() []string {
edges := make([]string, 0, 1) edges := make([]string, 0, 2)
return edges return edges
} }
@ -1106,10 +1534,13 @@ func (m *AuthTokensMutation) RemovedIDs(name string) []ent.Value {
// ClearedEdges returns all edge names that were cleared in this mutation. // ClearedEdges returns all edge names that were cleared in this mutation.
func (m *AuthTokensMutation) ClearedEdges() []string { func (m *AuthTokensMutation) ClearedEdges() []string {
edges := make([]string, 0, 1) edges := make([]string, 0, 2)
if m.cleareduser { if m.cleareduser {
edges = append(edges, authtokens.EdgeUser) edges = append(edges, authtokens.EdgeUser)
} }
if m.clearedroles {
edges = append(edges, authtokens.EdgeRoles)
}
return edges return edges
} }
@ -1119,6 +1550,8 @@ func (m *AuthTokensMutation) EdgeCleared(name string) bool {
switch name { switch name {
case authtokens.EdgeUser: case authtokens.EdgeUser:
return m.cleareduser return m.cleareduser
case authtokens.EdgeRoles:
return m.clearedroles
} }
return false return false
} }
@ -1130,6 +1563,9 @@ func (m *AuthTokensMutation) ClearEdge(name string) error {
case authtokens.EdgeUser: case authtokens.EdgeUser:
m.ClearUser() m.ClearUser()
return nil return nil
case authtokens.EdgeRoles:
m.ClearRoles()
return nil
} }
return fmt.Errorf("unknown AuthTokens unique edge %s", name) return fmt.Errorf("unknown AuthTokens unique edge %s", name)
} }
@ -1141,6 +1577,9 @@ func (m *AuthTokensMutation) ResetEdge(name string) error {
case authtokens.EdgeUser: case authtokens.EdgeUser:
m.ResetUser() m.ResetUser()
return nil return nil
case authtokens.EdgeRoles:
m.ResetRoles()
return nil
} }
return fmt.Errorf("unknown AuthTokens edge %s", name) return fmt.Errorf("unknown AuthTokens edge %s", name)
} }

View file

@ -9,6 +9,9 @@ import (
// Attachment is the predicate function for attachment builders. // Attachment is the predicate function for attachment builders.
type Attachment func(*sql.Selector) type Attachment func(*sql.Selector)
// AuthRoles is the predicate function for authroles builders.
type AuthRoles func(*sql.Selector)
// AuthTokens is the predicate function for authtokens builders. // AuthTokens is the predicate function for authtokens builders.
type AuthTokens func(*sql.Selector) type AuthTokens func(*sql.Selector)

View file

@ -43,6 +43,8 @@ func init() {
attachmentDescID := attachmentMixinFields0[0].Descriptor() attachmentDescID := attachmentMixinFields0[0].Descriptor()
// attachment.DefaultID holds the default value on creation for the id field. // attachment.DefaultID holds the default value on creation for the id field.
attachment.DefaultID = attachmentDescID.Default.(func() uuid.UUID) attachment.DefaultID = attachmentDescID.Default.(func() uuid.UUID)
authrolesFields := schema.AuthRoles{}.Fields()
_ = authrolesFields
authtokensMixin := schema.AuthTokens{}.Mixin() authtokensMixin := schema.AuthTokens{}.Mixin()
authtokensMixinFields0 := authtokensMixin[0].Fields() authtokensMixinFields0 := authtokensMixin[0].Fields()
_ = authtokensMixinFields0 _ = authtokensMixinFields0

View file

@ -0,0 +1,34 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
)
// AuthRoles holds the schema definition for the AuthRoles entity.
type AuthRoles struct {
ent.Schema
}
// Fields of the AuthRoles.
func (AuthRoles) Fields() []ent.Field {
return []ent.Field{
field.Enum("role").
Default("user").
Values(
"admin", // can do everything - currently unused
"user", // default login role
"attachments", // Read Attachments
),
}
}
// Edges of the AuthRoles.
func (AuthRoles) Edges() []ent.Edge {
return []ent.Edge{
edge.From("token", AuthTokens.Type).
Ref("roles").
Unique(),
}
}

View file

@ -37,6 +37,8 @@ func (AuthTokens) Edges() []ent.Edge {
edge.From("user", User.Type). edge.From("user", User.Type).
Ref("auth_tokens"). Ref("auth_tokens").
Unique(), Unique(),
edge.To("roles", AuthRoles.Type).
Unique(),
} }
} }

View file

@ -14,6 +14,8 @@ type Tx struct {
config config
// Attachment is the client for interacting with the Attachment builders. // Attachment is the client for interacting with the Attachment builders.
Attachment *AttachmentClient Attachment *AttachmentClient
// AuthRoles is the client for interacting with the AuthRoles builders.
AuthRoles *AuthRolesClient
// AuthTokens is the client for interacting with the AuthTokens builders. // AuthTokens is the client for interacting with the AuthTokens builders.
AuthTokens *AuthTokensClient AuthTokens *AuthTokensClient
// Document is the client for interacting with the Document builders. // Document is the client for interacting with the Document builders.
@ -166,6 +168,7 @@ func (tx *Tx) Client() *Client {
func (tx *Tx) init() { func (tx *Tx) init() {
tx.Attachment = NewAttachmentClient(tx.config) tx.Attachment = NewAttachmentClient(tx.config)
tx.AuthRoles = NewAuthRolesClient(tx.config)
tx.AuthTokens = NewAuthTokensClient(tx.config) tx.AuthTokens = NewAuthTokensClient(tx.config)
tx.Document = NewDocumentClient(tx.config) tx.Document = NewDocumentClient(tx.config)
tx.DocumentToken = NewDocumentTokenClient(tx.config) tx.DocumentToken = NewDocumentTokenClient(tx.config)

View file

@ -0,0 +1,4 @@
-- create "auth_roles" table
CREATE TABLE `auth_roles` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `role` text NOT NULL DEFAULT 'user', `auth_tokens_roles` uuid NULL, CONSTRAINT `auth_roles_auth_tokens_roles` FOREIGN KEY (`auth_tokens_roles`) REFERENCES `auth_tokens` (`id`) ON DELETE SET NULL);
-- create index "auth_roles_auth_tokens_roles_key" to table: "auth_roles"
CREATE UNIQUE INDEX `auth_roles_auth_tokens_roles_key` ON `auth_roles` (`auth_tokens_roles`);

View file

@ -1,7 +1,8 @@
h1:z1tbZ3fYByqxL78Z+ov8mfQVjXcwsZeEcT0i+2DZ8a8= h1:oo2QbYbKkbf4oTfkRXqo9XGPp8S76j33WQvDZITv5s8=
20220929052825_init.sql h1:ZlCqm1wzjDmofeAcSX3jE4h4VcdTNGpRg2eabztDy9Q= 20220929052825_init.sql h1:ZlCqm1wzjDmofeAcSX3jE4h4VcdTNGpRg2eabztDy9Q=
20221001210956_group_invitations.sql h1:YQKJFtE39wFOcRNbZQ/d+ZlHwrcfcsZlcv/pLEYdpjw= 20221001210956_group_invitations.sql h1:YQKJFtE39wFOcRNbZQ/d+ZlHwrcfcsZlcv/pLEYdpjw=
20221009173029_add_user_roles.sql h1:vWmzAfgEWQeGk0Vn70zfVPCcfEZth3E0JcvyKTjpYyU= 20221009173029_add_user_roles.sql h1:vWmzAfgEWQeGk0Vn70zfVPCcfEZth3E0JcvyKTjpYyU=
20221020043305_allow_nesting_types.sql h1:4AyJpZ7l7SSJtJAQETYY802FHJ64ufYPJTqvwdiGn3M= 20221020043305_allow_nesting_types.sql h1:4AyJpZ7l7SSJtJAQETYY802FHJ64ufYPJTqvwdiGn3M=
20221101041931_add_archived_field.sql h1:L2WxiOh1svRn817cNURgqnEQg6DIcodZ1twK4tvxW94= 20221101041931_add_archived_field.sql h1:L2WxiOh1svRn817cNURgqnEQg6DIcodZ1twK4tvxW94=
20221113012312_add_asset_id_field.sql h1:DjD7e1PS8OfxGBWic8h0nO/X6CNnHEMqQjDCaaQ3M3Q= 20221113012312_add_asset_id_field.sql h1:DjD7e1PS8OfxGBWic8h0nO/X6CNnHEMqQjDCaaQ3M3Q=
20221203053132_add_token_roles.sql h1:wFTIh+KBoHfLfy/L0ZmJz4cNXKHdACG9ZK/yvVKjF0M=

View file

@ -6,7 +6,10 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent" "github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens" "github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/pkgs/hasher"
"github.com/hay-kot/homebox/backend/pkgs/set"
) )
type TokenRepository struct { type TokenRepository struct {
@ -47,9 +50,31 @@ func (r *TokenRepository) GetUserFromToken(ctx context.Context, token []byte) (U
return mapUserOut(user), nil return mapUserOut(user), nil
} }
// Creates a token for a user func (r *TokenRepository) GetRoles(ctx context.Context, token string) (*set.Set[string], error) {
func (r *TokenRepository) CreateToken(ctx context.Context, createToken UserAuthTokenCreate) (UserAuthToken, error) { tokenHash := hasher.HashToken(token)
roles, err := r.db.AuthRoles.
Query().
Where(authroles.HasTokenWith(
authtokens.Token(tokenHash),
)).
All(ctx)
if err != nil {
return nil, err
}
roleSet := set.Make[string](len(roles))
for _, role := range roles {
roleSet.Insert(role.Role.String())
}
return &roleSet, nil
}
// Creates a token for a user
func (r *TokenRepository) CreateToken(ctx context.Context, createToken UserAuthTokenCreate, roles ...authroles.Role) (UserAuthToken, error) {
dbToken, err := r.db.AuthTokens.Create(). dbToken, err := r.db.AuthTokens.Create().
SetToken(createToken.TokenHash). SetToken(createToken.TokenHash).
SetUserID(createToken.UserID). SetUserID(createToken.UserID).
@ -60,6 +85,17 @@ func (r *TokenRepository) CreateToken(ctx context.Context, createToken UserAuthT
return UserAuthToken{}, err return UserAuthToken{}, err
} }
for _, role := range roles {
_, err := r.db.AuthRoles.Create().
SetRole(role).
SetToken(dbToken).
Save(ctx)
if err != nil {
return UserAuthToken{}, err
}
}
return UserAuthToken{ return UserAuthToken{
UserAuthTokenCreate: UserAuthTokenCreate{ UserAuthTokenCreate: UserAuthTokenCreate{
TokenHash: dbToken.Token, TokenHash: dbToken.Token,

View file

@ -8,6 +8,12 @@ type Set[T key] struct {
mp map[T]struct{} mp map[T]struct{}
} }
func Make[T key](size int) Set[T] {
return Set[T]{
mp: make(map[T]struct{}, size),
}
}
func New[T key](v ...T) Set[T] { func New[T key](v ...T) Set[T] {
mp := make(map[T]struct{}, len(v)) mp := make(map[T]struct{}, len(v))

View file

@ -10,7 +10,12 @@
<span class="ml-2 w-0 flex-1 truncate"> {{ attachment.document.title }}</span> <span class="ml-2 w-0 flex-1 truncate"> {{ attachment.document.title }}</span>
</div> </div>
<div class="ml-4 flex-shrink-0"> <div class="ml-4 flex-shrink-0">
<button class="font-medium" @click="getAttachmentUrl(attachment)">Download</button> <a class="tooltip mr-2" data-tip="Download" :href="attachmentURL(attachment.id)" target="_blank">
<Icon class="h-5 w-5" name="mdi-download" />
</a>
<a class="tooltip" data-tip="Open" :href="attachmentURL(attachment.id)" target="_blank">
<Icon class="h-5 w-5" name="mdi-open-in-new" />
</a>
</div> </div>
</li> </li>
</ul> </ul>
@ -31,25 +36,9 @@
}); });
const api = useUserApi(); const api = useUserApi();
const toast = useNotifier();
async function getAttachmentUrl(attachment: ItemAttachment) {
const url = await api.items.getAttachmentUrl(props.itemId, attachment.id);
if (!url) { function attachmentURL(attachmentId: string) {
toast.error("Failed to get attachment url"); return api.authURL(`/items/${props.itemId}/attachments/${attachmentId}`);
return;
}
if (!document) {
window.open(url, "_blank");
return;
}
const link = document.createElement("a");
link.href = url;
link.target = "_blank";
link.setAttribute("download", attachment.document.title);
link.click();
} }
</script> </script>

View file

@ -43,5 +43,5 @@ export function useUserApi(): UserClient {
requests.addResponseInterceptor(observer.handler); requests.addResponseInterceptor(observer.handler);
} }
return new UserClient(requests); return new UserClient(requests, authStore.attachmentToken);
} }

View file

@ -27,9 +27,21 @@ export function parseDate<T>(obj: T, keys: Array<keyof T> = []): T {
export class BaseAPI { export class BaseAPI {
http: Requests; http: Requests;
attachmentToken: string;
constructor(requests: Requests) { constructor(requests: Requests, attachmentToken = "") {
this.http = requests; this.http = requests;
this.attachmentToken = attachmentToken;
}
// if a attachmentToken is present it will be added to URL as a query param
// this is done with a simple appending of the query param to the URL. If your
// URL already has a query param, this will not work.
authURL(url: string): string {
if (this.attachmentToken) {
return `/api/v1${url}?access_token=${this.attachmentToken}`;
}
return url;
} }
/** /**

View file

@ -1,13 +1,6 @@
import { BaseAPI, route } from "../base"; import { BaseAPI, route } from "../base";
import { parseDate } from "../base/base-api"; import { parseDate } from "../base/base-api";
import { import { ItemAttachmentUpdate, ItemCreate, ItemOut, ItemSummary, ItemUpdate } from "../types/data-contracts";
ItemAttachmentToken,
ItemAttachmentUpdate,
ItemCreate,
ItemOut,
ItemSummary,
ItemUpdate,
} from "../types/data-contracts";
import { AttachmentTypes, PaginationResult } from "../types/non-generated"; import { AttachmentTypes, PaginationResult } from "../types/non-generated";
export type ItemsQuery = { export type ItemsQuery = {
@ -79,18 +72,6 @@ export class ItemsApi extends BaseAPI {
}); });
} }
async getAttachmentUrl(id: string, attachmentId: string): Promise<string> {
const payload = await this.http.get<ItemAttachmentToken>({
url: route(`/items/${id}/attachments/${attachmentId}`),
});
if (!payload.data) {
return "";
}
return route(`/items/${id}/attachments/download`, { token: payload.data.token });
}
async deleteAttachment(id: string, attachmentId: string) { async deleteAttachment(id: string, attachmentId: string) {
return await this.http.delete<void>({ url: route(`/items/${id}/attachments/${attachmentId}`) }); return await this.http.delete<void>({ url: route(`/items/${id}/attachments/${attachmentId}`) });
} }

View file

@ -324,6 +324,7 @@ export interface ItemAttachmentToken {
} }
export interface TokenResponse { export interface TokenResponse {
attachmentToken: string;
expiresAt: Date; expiresAt: Date;
token: string; token: string;
} }

View file

@ -15,8 +15,8 @@ export class UserClient extends BaseAPI {
user: UserApi; user: UserApi;
actions: ActionsAPI; actions: ActionsAPI;
constructor(requests: Requests) { constructor(requests: Requests, attachmentToken: string) {
super(requests); super(requests, attachmentToken);
this.locations = new LocationsApi(requests); this.locations = new LocationsApi(requests);
this.labels = new LabelsApi(requests); this.labels = new LabelsApi(requests);

View file

@ -105,6 +105,7 @@
authStore.$patch({ authStore.$patch({
token: data.token, token: data.token,
expires: data.expiresAt, expires: data.expiresAt,
attachmentToken: data.attachmentToken,
}); });
navigateTo("/home"); navigateTo("/home");

View file

@ -27,17 +27,32 @@
}); });
type FilteredAttachments = { type FilteredAttachments = {
photos: ItemAttachment[];
attachments: ItemAttachment[]; attachments: ItemAttachment[];
warranty: ItemAttachment[]; warranty: ItemAttachment[];
manuals: ItemAttachment[]; manuals: ItemAttachment[];
receipts: ItemAttachment[]; receipts: ItemAttachment[];
}; };
type Photo = {
src: string;
};
const photos = computed<Photo[]>(() => {
return (
item.value?.attachments.reduce((acc, cur) => {
if (cur.type === "photo") {
acc.push({
src: api.authURL(`/items/${item.value.id}/attachments/${cur.id}`),
});
}
return acc;
}, [] as Photo[]) || []
);
});
const attachments = computed<FilteredAttachments>(() => { const attachments = computed<FilteredAttachments>(() => {
if (!item.value) { if (!item.value) {
return { return {
photos: [],
attachments: [], attachments: [],
manuals: [], manuals: [],
warranty: [], warranty: [],
@ -48,8 +63,9 @@
return item.value.attachments.reduce( return item.value.attachments.reduce(
(acc, attachment) => { (acc, attachment) => {
if (attachment.type === "photo") { if (attachment.type === "photo") {
acc.photos.push(attachment); return acc;
} else if (attachment.type === "warranty") { }
if (attachment.type === "warranty") {
acc.warranty.push(attachment); acc.warranty.push(attachment);
} else if (attachment.type === "manual") { } else if (attachment.type === "manual") {
acc.manuals.push(attachment); acc.manuals.push(attachment);
@ -61,7 +77,6 @@
return acc; return acc;
}, },
{ {
photos: [] as ItemAttachment[],
attachments: [] as ItemAttachment[], attachments: [] as ItemAttachment[],
warranty: [] as ItemAttachment[], warranty: [] as ItemAttachment[],
manuals: [] as ItemAttachment[], manuals: [] as ItemAttachment[],
@ -144,7 +159,6 @@
} }
return ( return (
attachments.value.photos.length > 0 ||
attachments.value.attachments.length > 0 || attachments.value.attachments.length > 0 ||
attachments.value.warranty.length > 0 || attachments.value.warranty.length > 0 ||
attachments.value.manuals.length > 0 || attachments.value.manuals.length > 0 ||
@ -163,10 +177,6 @@
}); });
}; };
if (attachments.value.photos.length > 0) {
push("Photos");
}
if (attachments.value.attachments.length > 0) { if (attachments.value.attachments.length > 0) {
push("Attachments"); push("Attachments");
} }
@ -292,10 +302,43 @@
toast.success("Item deleted"); toast.success("Item deleted");
navigateTo("/home"); navigateTo("/home");
} }
const refDialog = ref<HTMLDialogElement>();
const dialoged = reactive({
src: "",
});
function openDialog(img: Photo) {
refDialog.value.showModal();
dialoged.src = img.src;
}
function closeDialog() {
refDialog.value.close();
}
const refDialogBody = ref<HTMLDivElement>();
onClickOutside(refDialogBody, () => {
closeDialog();
});
</script> </script>
<template> <template>
<BaseContainer v-if="item" class="pb-8"> <BaseContainer v-if="item" class="pb-8">
<dialog ref="refDialog" class="z-[999] fixed bg-transparent">
<div ref="refDialogBody" class="relative">
<div class="absolute right-0 -mt-3 -mr-3 sm:-mt-4 sm:-mr-4 space-x-1">
<a class="btn btn-sm sm:btn-md btn-primary btn-circle" :href="dialoged.src" download>
<Icon class="h-5 w-5" name="mdi-download" />
</a>
<button class="btn btn-sm sm:btn-md btn-primary btn-circle" @click="closeDialog()">
<Icon class="h-5 w-5" name="mdi-close" />
</button>
</div>
<img class="max-w-[80vw] max-h-[80vh]" :src="dialoged.src" />
</div>
</dialog>
<section class="px-3"> <section class="px-3">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<div class="form-control"></div> <div class="form-control"></div>
@ -353,6 +396,15 @@
<DetailsSection :details="itemDetails" /> <DetailsSection :details="itemDetails" />
</BaseCard> </BaseCard>
<BaseCard>
<template #title> Photos </template>
<div class="container p-4 flex flex-wrap gap-2 mx-auto max-h-[500px] overflow-scroll">
<button v-for="(img, i) in photos" :key="i" @click="openDialog(img)">
<img class="rounded max-h-[200px]" :src="img.src" />
</button>
</div>
</BaseCard>
<BaseCard v-if="showAttachments"> <BaseCard v-if="showAttachments">
<template #title> Attachments </template> <template #title> Attachments </template>
<DetailsSection :details="attachmentDetails"> <DetailsSection :details="attachmentDetails">
@ -377,13 +429,6 @@
:item-id="item.id" :item-id="item.id"
/> />
</template> </template>
<template #photos>
<ItemAttachmentsList
v-if="attachments.photos.length > 0"
:attachments="attachments.photos"
:item-id="item.id"
/>
</template>
<template #receipts> <template #receipts>
<ItemAttachmentsList <ItemAttachmentsList
v-if="attachments.receipts.length > 0" v-if="attachments.receipts.length > 0"
@ -419,3 +464,10 @@
</section> </section>
</BaseContainer> </BaseContainer>
</template> </template>
<style>
/* Style dialog background */
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
}
</style>

View file

@ -6,6 +6,7 @@ import { UserOut } from "~~/lib/api/types/data-contracts";
export const useAuthStore = defineStore("auth", { export const useAuthStore = defineStore("auth", {
state: () => ({ state: () => ({
token: useLocalStorage("pinia/auth/token", ""), token: useLocalStorage("pinia/auth/token", ""),
attachmentToken: useLocalStorage("pinia/auth/attachmentToken", ""),
expires: useLocalStorage("pinia/auth/expires", ""), expires: useLocalStorage("pinia/auth/expires", ""),
self: null as UserOut | null, self: null as UserOut | null,
}), }),
@ -27,6 +28,7 @@ export const useAuthStore = defineStore("auth", {
const result = await api.user.logout(); const result = await api.user.logout();
this.token = ""; this.token = "";
this.attachmentToken = "";
this.expires = ""; this.expires = "";
this.self = null; this.self = null;