This commit is contained in:
Hayden 2024-05-25 01:34:56 +00:00 committed by GitHub
commit 1b65cbdfcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
92 changed files with 13974 additions and 7677 deletions

View file

@ -13,9 +13,13 @@ jobs:
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v3.0.0
with:
version: 6.0.2
version: 9
- name: Install dependencies
run: pnpm install --shamefully-hoist
@ -46,18 +50,18 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.21"
go-version: "1.22"
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: pnpm/action-setup@v3.0.0
with:
version: 6.0.2
version: 9
- name: Install dependencies
run: pnpm install
run: pnpm install --shamefully-hoist
working-directory: frontend
- name: Run Integration Tests

View file

@ -5,6 +5,11 @@ env:
HBOX_STORAGE_SQLITE_URL: .data/homebox.db?_pragma=busy_timeout=1000&_pragma=journal_mode=WAL&_fk=1
HBOX_OPTIONS_ALLOW_REGISTRATION: true
UNSAFE_DISABLE_PASSWORD_PROJECTION: "yes_i_am_sure"
HBOX_MAILER_HOST: 127.0.0.1
HBOX_MAILER_PORT: 1025
HBOX_MAILER_USERNAME: c836555d57d205
HBOX_MAILER_PASSWORD: 3ff2f9986f3cff
HBOX_MAILER_FROM: info@example.com
tasks:
setup:
desc: Install development dependencies
@ -79,6 +84,12 @@ tasks:
cmds:
- go mod tidy
go:fmt:
desc: Runs go fmt on the backend
dir: backend
cmds:
- gofumpt -w .
go:lint:
desc: Runs golangci-lint
dir: backend

View file

@ -1,8 +1,9 @@
run:
timeout: 10m
skip-dirs:
- internal/data/ent.*
linters-settings:
errcheck:
exclude-functions:
- (net/http.ResponseWriter).Write
goconst:
min-len: 5
min-occurrences: 5
@ -71,4 +72,6 @@ linters:
- sqlclosecheck
issues:
exclude-use-default: false
fix: true
fix: false
exclude-dirs:
- internal/data/ent.*

View file

@ -11,7 +11,7 @@ import (
type app struct {
conf *config.Config
mailer mailer.Mailer
mailer *mailer.Mailer
db *ent.Client
repos *repo.AllRepos
services *services.AllServices
@ -23,7 +23,7 @@ func new(conf *config.Config) *app {
conf: conf,
}
s.mailer = mailer.Mailer{
s.mailer = &mailer.Mailer{
Host: s.conf.Mailer.Host,
Port: s.conf.Mailer.Port,
Username: s.conf.Mailer.Username,

View file

@ -40,20 +40,18 @@ func (a *app) SetupDemo() {
_, err = a.services.User.RegisterUser(ctx, registration)
if err != nil {
log.Err(err).Msg("Failed to register demo user")
log.Fatal().Msg("Failed to setup demo")
log.Fatal().Msg("Failed to setup demo") // nolint
}
token, err := a.services.User.Login(ctx, registration.Email, registration.Password, false)
if err != nil {
log.Err(err).Msg("Failed to login demo user")
log.Fatal().Msg("Failed to setup demo")
return
}
self, err := a.services.User.GetSelf(ctx, token.Raw)
if err != nil {
log.Err(err).Msg("Failed to get self")
log.Fatal().Msg("Failed to setup demo")
return
}
_, err = a.services.Items.CsvImport(ctx, self.GroupID, strings.NewReader(csvText))

View file

@ -153,7 +153,7 @@ func (ctrl *V1Controller) HandleCacheWS() errchain.HandlerFunc {
m.HandleConnect(func(s *melody.Session) {
auth := services.NewContext(s.Request.Context())
s.Set("gid", auth.GID)
s.Set("gid", auth.GroupID)
})
factory := func(e string) func(data any) {

View file

@ -20,7 +20,7 @@ func actionHandlerFactory(ref string, fn func(context.Context, uuid.UUID) (int,
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
totalCompleted, err := fn(ctx, ctx.GID)
totalCompleted, err := fn(ctx, ctx.GroupID)
if err != nil {
log.Err(err).Str("action_ref", ref).Msg("failed to run action")
return validate.NewRequestError(err, http.StatusInternalServerError)

View file

@ -52,7 +52,7 @@ func (ctrl *V1Controller) HandleAssetGet() errchain.HandlerFunc {
}
}
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GID, repo.AssetID(assetID), int(page), int(pageSize))
items, err := ctrl.repo.Items.QueryByAssetID(r.Context(), ctx.GroupID, repo.AssetID(assetID), int(page), int(pageSize))
if err != nil {
log.Err(err).Msg("failed to get item")
return validate.NewRequestError(err, http.StatusInternalServerError)

View file

@ -35,7 +35,7 @@ type (
func (ctrl *V1Controller) HandleGroupGet() errchain.HandlerFunc {
fn := func(r *http.Request) (repo.Group, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.GroupByID(auth, auth.GID)
return ctrl.repo.Groups.GroupByID(auth, auth.GroupID)
}
return adapters.Command(fn, http.StatusOK)

View file

@ -79,7 +79,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GID, extractQuery(r))
items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GroupID, extractQuery(r))
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return server.JSON(w, http.StatusOK, repo.PaginationResult[repo.ItemSummary]{
@ -105,12 +105,12 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleItemFullPath() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) ([]repo.ItemPath, error) {
auth := services.NewContext(r.Context())
item, err := ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
item, err := ctrl.repo.Items.GetOneByGroup(auth, auth.GroupID, ID)
if err != nil {
return nil, err
}
paths, err := ctrl.repo.Locations.PathForLoc(auth, auth.GID, item.Location.ID)
paths, err := ctrl.repo.Locations.PathForLoc(auth, auth.GroupID, item.Location.ID)
if err != nil {
return nil, err
}
@ -165,7 +165,7 @@ func (ctrl *V1Controller) HandleItemGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.ItemOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
return ctrl.repo.Items.GetOneByGroup(auth, auth.GroupID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
@ -183,7 +183,7 @@ func (ctrl *V1Controller) HandleItemGet() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleItemDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Items.DeleteByGroup(auth, auth.GID, ID)
err := ctrl.repo.Items.DeleteByGroup(auth, auth.GroupID, ID)
return nil, err
}
@ -205,7 +205,7 @@ func (ctrl *V1Controller) HandleItemUpdate() errchain.HandlerFunc {
auth := services.NewContext(r.Context())
body.ID = ID
return ctrl.repo.Items.UpdateByGroup(auth, auth.GID, body)
return ctrl.repo.Items.UpdateByGroup(auth, auth.GroupID, body)
}
return adapters.ActionID("id", fn, http.StatusOK)
@ -226,12 +226,12 @@ func (ctrl *V1Controller) HandleItemPatch() errchain.HandlerFunc {
auth := services.NewContext(r.Context())
body.ID = ID
err := ctrl.repo.Items.Patch(auth, auth.GID, ID, body)
err := ctrl.repo.Items.Patch(auth, auth.GroupID, ID, body)
if err != nil {
return repo.ItemOut{}, err
}
return ctrl.repo.Items.GetOneByGroup(auth, auth.GID, ID)
return ctrl.repo.Items.GetOneByGroup(auth, auth.GroupID, ID)
}
return adapters.ActionID("id", fn, http.StatusOK)
@ -249,7 +249,7 @@ func (ctrl *V1Controller) HandleItemPatch() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleGetAllCustomFieldNames() errchain.HandlerFunc {
fn := func(r *http.Request) ([]string, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetAllCustomFieldNames(auth, auth.GID)
return ctrl.repo.Items.GetAllCustomFieldNames(auth, auth.GroupID)
}
return adapters.Command(fn, http.StatusOK)
@ -271,7 +271,7 @@ func (ctrl *V1Controller) HandleGetAllCustomFieldValues() errchain.HandlerFunc {
fn := func(r *http.Request, q query) ([]string, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Items.GetAllCustomFieldValues(auth, auth.GID, q.Field)
return ctrl.repo.Items.GetAllCustomFieldValues(auth, auth.GroupID, q.Field)
}
return adapters.Query(fn, http.StatusOK)
@ -323,7 +323,7 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
csvData, err := ctrl.svc.Items.ExportTSV(r.Context(), ctx.GID)
csvData, err := ctrl.svc.Items.ExportTSV(r.Context(), ctx.GroupID)
if err != nil {
log.Err(err).Msg("failed to export items")
return validate.NewRequestError(err, http.StatusInternalServerError)

View file

@ -168,7 +168,7 @@ func (ctrl *V1Controller) handleItemAttachmentsHandler(w http.ResponseWriter, r
// Delete Attachment Handler
case http.MethodDelete:
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GID, ID, attachmentID)
err = ctrl.svc.Items.AttachmentDelete(r.Context(), ctx.GroupID, ID, attachmentID)
if err != nil {
log.Err(err).Msg("failed to delete attachment")
return validate.NewRequestError(err, http.StatusInternalServerError)

View file

@ -21,7 +21,7 @@ import (
func (ctrl *V1Controller) HandleLabelsGetAll() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.LabelSummary, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.GetAll(auth, auth.GID)
return ctrl.repo.Labels.GetAll(auth, auth.GroupID)
}
return adapters.Command(fn, http.StatusOK)
@ -39,7 +39,7 @@ func (ctrl *V1Controller) HandleLabelsGetAll() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLabelsCreate() errchain.HandlerFunc {
fn := func(r *http.Request, data repo.LabelCreate) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.Create(auth, auth.GID, data)
return ctrl.repo.Labels.Create(auth, auth.GroupID, data)
}
return adapters.Action(fn, http.StatusCreated)
@ -57,7 +57,7 @@ func (ctrl *V1Controller) HandleLabelsCreate() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLabelDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Labels.DeleteByGroup(auth, auth.GID, ID)
err := ctrl.repo.Labels.DeleteByGroup(auth, auth.GroupID, ID)
return nil, err
}
@ -76,7 +76,7 @@ func (ctrl *V1Controller) HandleLabelDelete() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLabelGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Labels.GetOneByGroup(auth, auth.GID, ID)
return ctrl.repo.Labels.GetOneByGroup(auth, auth.GroupID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
@ -95,7 +95,7 @@ func (ctrl *V1Controller) HandleLabelUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, data repo.LabelUpdate) (repo.LabelOut, error) {
auth := services.NewContext(r.Context())
data.ID = ID
return ctrl.repo.Labels.UpdateByGroup(auth, auth.GID, data)
return ctrl.repo.Labels.UpdateByGroup(auth, auth.GroupID, data)
}
return adapters.ActionID("id", fn, http.StatusOK)

View file

@ -22,7 +22,7 @@ import (
func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
fn := func(r *http.Request, query repo.TreeQuery) ([]repo.TreeItem, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.Tree(auth, auth.GID, query)
return ctrl.repo.Locations.Tree(auth, auth.GroupID, query)
}
return adapters.Query(fn, http.StatusOK)
@ -40,7 +40,7 @@ func (ctrl *V1Controller) HandleLocationTreeQuery() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
fn := func(r *http.Request, q repo.LocationQuery) ([]repo.LocationOutCount, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.GetAll(auth, auth.GID, q)
return ctrl.repo.Locations.GetAll(auth, auth.GroupID, q)
}
return adapters.Query(fn, http.StatusOK)
@ -58,7 +58,7 @@ func (ctrl *V1Controller) HandleLocationGetAll() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
fn := func(r *http.Request, createData repo.LocationCreate) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.Create(auth, auth.GID, createData)
return ctrl.repo.Locations.Create(auth, auth.GroupID, createData)
}
return adapters.Action(fn, http.StatusCreated)
@ -76,7 +76,7 @@ func (ctrl *V1Controller) HandleLocationCreate() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
err := ctrl.repo.Locations.DeleteByGroup(auth, auth.GID, ID)
err := ctrl.repo.Locations.DeleteByGroup(auth, auth.GroupID, ID)
return nil, err
}
@ -95,7 +95,7 @@ func (ctrl *V1Controller) HandleLocationDelete() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleLocationGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Locations.GetOneByGroup(auth, auth.GID, ID)
return ctrl.repo.Locations.GetOneByGroup(auth, auth.GroupID, ID)
}
return adapters.CommandID("id", fn, http.StatusOK)
@ -115,7 +115,7 @@ func (ctrl *V1Controller) HandleLocationUpdate() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, body repo.LocationUpdate) (repo.LocationOut, error) {
auth := services.NewContext(r.Context())
body.ID = ID
return ctrl.repo.Locations.UpdateByGroup(auth, auth.GID, ID, body)
return ctrl.repo.Locations.UpdateByGroup(auth, auth.GroupID, ID, body)
}
return adapters.ActionID("id", fn, http.StatusOK)

View file

@ -21,7 +21,7 @@ import (
func (ctrl *V1Controller) HandleMaintenanceLogGet() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, q repo.MaintenanceLogQuery) (repo.MaintenanceLog, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.MaintEntry.GetLog(auth, auth.GID, ID, q)
return ctrl.repo.MaintEntry.GetLog(auth, auth.GroupID, ID, q)
}
return adapters.QueryID("id", fn, http.StatusOK)

View file

@ -40,7 +40,7 @@ func (ctrl *V1Controller) HandleGetUserNotifiers() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleCreateNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, in repo.NotifierCreate) (repo.NotifierOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Notifiers.Create(auth, auth.GID, auth.UID, in)
return ctrl.repo.Notifiers.Create(auth, auth.GroupID, auth.UserID, in)
}
return adapters.Action(fn, http.StatusCreated)
@ -57,7 +57,7 @@ func (ctrl *V1Controller) HandleCreateNotifier() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleDeleteNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID) (any, error) {
auth := services.NewContext(r.Context())
return nil, ctrl.repo.Notifiers.Delete(auth, auth.UID, ID)
return nil, ctrl.repo.Notifiers.Delete(auth, auth.UserID, ID)
}
return adapters.CommandID("id", fn, http.StatusNoContent)
@ -75,7 +75,7 @@ func (ctrl *V1Controller) HandleDeleteNotifier() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleUpdateNotifier() errchain.HandlerFunc {
fn := func(r *http.Request, ID uuid.UUID, in repo.NotifierUpdate) (repo.NotifierOut, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Notifiers.Update(auth, auth.UID, ID, in)
return ctrl.repo.Notifiers.Update(auth, auth.UserID, ID, in)
}
return adapters.ActionID("id", fn, http.StatusOK)

View file

@ -23,7 +23,7 @@ import (
func (ctrl *V1Controller) HandleGroupStatisticsLocations() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.TotalsByOrganizer, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsLocationsByPurchasePrice(auth, auth.GID)
return ctrl.repo.Groups.StatsLocationsByPurchasePrice(auth, auth.GroupID)
}
return adapters.Command(fn, http.StatusOK)
@ -40,7 +40,7 @@ func (ctrl *V1Controller) HandleGroupStatisticsLocations() errchain.HandlerFunc
func (ctrl *V1Controller) HandleGroupStatisticsLabels() errchain.HandlerFunc {
fn := func(r *http.Request) ([]repo.TotalsByOrganizer, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsLabelsByPurchasePrice(auth, auth.GID)
return ctrl.repo.Groups.StatsLabelsByPurchasePrice(auth, auth.GroupID)
}
return adapters.Command(fn, http.StatusOK)
@ -57,7 +57,7 @@ func (ctrl *V1Controller) HandleGroupStatisticsLabels() errchain.HandlerFunc {
func (ctrl *V1Controller) HandleGroupStatistics() errchain.HandlerFunc {
fn := func(r *http.Request) (repo.GroupStatistics, error) {
auth := services.NewContext(r.Context())
return ctrl.repo.Groups.StatsGroup(auth, auth.GID)
return ctrl.repo.Groups.StatsGroup(auth, auth.GroupID)
}
return adapters.Command(fn, http.StatusOK)
@ -94,7 +94,7 @@ func (ctrl *V1Controller) HandleGroupStatisticsPriceOverTime() errchain.HandlerF
return validate.NewRequestError(err, http.StatusBadRequest)
}
stats, err := ctrl.repo.Groups.StatsPurchasePrice(ctx, ctx.GID, startDate, endDate)
stats, err := ctrl.repo.Groups.StatsPurchasePrice(ctx, ctx.GroupID, startDate, endDate)
if err != nil {
return validate.NewRequestError(err, http.StatusInternalServerError)
}

View file

@ -1,6 +1,7 @@
package v1
import (
"context"
"fmt"
"net/http"
@ -8,6 +9,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/core/services"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/sys/validate"
"github.com/hay-kot/homebox/backend/internal/web/adapters"
"github.com/hay-kot/httpkit/errchain"
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
@ -115,12 +117,10 @@ func (ctrl *V1Controller) HandleUserSelfDelete() errchain.HandlerFunc {
}
}
type (
ChangePassword struct {
Current string `json:"current,omitempty"`
New string `json:"new,omitempty"`
}
)
type ChangePassword struct {
Current string `json:"current,omitempty"`
New string `json:"new,omitempty"`
}
// HandleUserSelfChangePassword godoc
//
@ -144,7 +144,7 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
ctx := services.NewContext(r.Context())
ok := ctrl.svc.User.ChangePassword(ctx, cp.Current, cp.New)
ok := ctrl.svc.User.PasswordChange(ctx, cp.Current, cp.New)
if !ok {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
@ -152,3 +152,73 @@ func (ctrl *V1Controller) HandleUserSelfChangePassword() errchain.HandlerFunc {
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleUserSelfChangePasswordWithToken godoc
//
// @Summary Change Password
// @Tags User
// @Success 204
// @Param payload body ChangePassword true "Password Payload"
// @Router /v1/users/change-password-token [PUT]
func (ctrl *V1Controller) HandleUserSelfChangePasswordWithToken() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
tokenQueryParam := r.URL.Query().Get("token")
if tokenQueryParam == "" {
return validate.NewRequestError(fmt.Errorf("missing token query param"), http.StatusBadRequest)
}
if ctrl.isDemo {
return validate.NewRequestError(nil, http.StatusForbidden)
}
var cp ChangePassword
err := server.Decode(r, &cp)
if err != nil {
log.Err(err).Msg("user failed to change password")
}
ctx := services.NewContext(r.Context())
ok := ctrl.svc.User.PasswordChange(ctx, cp.Current, cp.New)
if !ok {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
return server.JSON(w, http.StatusNoContent, nil)
}
}
// HandleUserRequestPasswordReset godoc
//
// @Summary Request Password Reset
// @Tags User
// @Produce json
// @Param payload body services.PasswordResetRequest true "User Data"
// @Success 204
// @Router /v1/users/request-password-reset [Post]
func (ctrl *V1Controller) HandleUserRequestPasswordReset() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
if ctrl.isDemo {
return validate.NewRequestError(nil, http.StatusForbidden)
}
v, err := adapters.DecodeBody[services.PasswordResetRequest](r)
if err != nil {
return err
}
go func() {
ctx := context.Background()
err = ctrl.svc.User.PasswordResetRequest(ctx, v)
if err != nil {
log.Warn().
Err(err).
Str("email", v.Email).
Msg("failed to request password reset")
}
}()
return server.JSON(w, http.StatusNoContent, nil)
}
}

View file

@ -115,11 +115,11 @@ func run(cfg *config.Config) error {
err = c.Schema.Create(context.Background(), options...)
if err != nil {
log.Fatal().
Err(err).
Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteURL).
Msg("failed creating schema resources")
log.Fatal(). // nolint
Err(err).
Str("driver", "sqlite").
Str("url", cfg.Storage.SqliteURL).
Msg("failed creating schema resources")
}
err = os.RemoveAll(temp)
@ -160,6 +160,8 @@ func run(cfg *config.Config) error {
app.repos = repo.New(c, app.bus, cfg.Storage.Data)
app.services = services.New(
app.repos,
app.conf.BaseURL,
app.mailer,
services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID),
services.WithCurrencies(currencies),
)
@ -180,7 +182,7 @@ func run(cfg *config.Config) error {
chain := errchain.New(mid.Errors(logger))
app.mountRoutes(router, chain, app.repos)
app.mountRoutes(router, chain)
runner := graceful.NewRunner()

View file

@ -15,7 +15,6 @@ import (
"github.com/hay-kot/homebox/backend/app/api/providers"
_ "github.com/hay-kot/homebox/backend/app/api/static/docs"
"github.com/hay-kot/homebox/backend/internal/data/ent/authroles"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/httpkit/errchain"
httpSwagger "github.com/swaggo/http-swagger/v2" // http-swagger middleware
)
@ -37,7 +36,7 @@ func (a *app) debugRouter() *http.ServeMux {
}
// registerRoutes registers all the routes for the API
func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllRepos) {
func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain) {
registerMimes()
r.Get("/swagger/*", httpSwagger.Handler(
@ -72,6 +71,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
r.Post(v1Base("/users/register"), chain.ToHandlerFunc(v1Ctrl.HandleUserRegistration()))
r.Post(v1Base("/users/login"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogin(providers...)))
r.Post(v1Base("/users/request-password-reset"), chain.ToHandlerFunc(v1Ctrl.HandleUserRequestPasswordReset()))
userMW := []errchain.Middleware{
a.mwAuthToken,
@ -85,6 +85,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
r.Post(v1Base("/users/logout"), chain.ToHandlerFunc(v1Ctrl.HandleAuthLogout(), userMW...))
r.Get(v1Base("/users/refresh"), chain.ToHandlerFunc(v1Ctrl.HandleAuthRefresh(), userMW...))
r.Put(v1Base("/users/self/change-password"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfChangePassword(), userMW...))
r.Put(v1Base("/users/self/change-password-token"), chain.ToHandlerFunc(v1Ctrl.HandleUserSelfChangePasswordWithToken()))
r.Post(v1Base("/groups/invitations"), chain.ToHandlerFunc(v1Ctrl.HandleGroupInvitationsCreate(), userMW...))
r.Get(v1Base("/groups/statistics"), chain.ToHandlerFunc(v1Ctrl.HandleGroupStatistics(), userMW...))

View file

@ -1792,6 +1792,33 @@ const docTemplate = `{
}
}
},
"/v1/users/request-password-reset": {
"post": {
"produces": [
"application/json"
],
"tags": [
"User"
],
"summary": "Request Password Reset",
"parameters": [
{
"description": "User Data",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/services.PasswordResetRequest"
}
}
],
"responses": {
"204": {
"description": "No Content"
}
}
}
},
"/v1/users/self": {
"get": {
"security": [
@ -2825,6 +2852,14 @@ const docTemplate = `{
}
}
},
"services.PasswordResetRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
}
},
"services.UserRegistration": {
"type": "object",
"properties": {

View file

@ -1785,6 +1785,33 @@
}
}
},
"/v1/users/request-password-reset": {
"post": {
"produces": [
"application/json"
],
"tags": [
"User"
],
"summary": "Request Password Reset",
"parameters": [
{
"description": "User Data",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/services.PasswordResetRequest"
}
}
],
"responses": {
"204": {
"description": "No Content"
}
}
}
},
"/v1/users/self": {
"get": {
"security": [
@ -2818,6 +2845,14 @@
}
}
},
"services.PasswordResetRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
}
},
"services.UserRegistration": {
"type": "object",
"properties": {

View file

@ -620,6 +620,11 @@ definitions:
value:
type: number
type: object
services.PasswordResetRequest:
properties:
email:
type: string
type: object
services.UserRegistration:
properties:
email:
@ -1817,6 +1822,23 @@ paths:
summary: Register New User
tags:
- User
/v1/users/request-password-reset:
post:
parameters:
- description: User Data
in: body
name: payload
required: true
schema:
$ref: '#/definitions/services.PasswordResetRequest'
produces:
- application/json
responses:
"204":
description: No Content
summary: Request Password Reset
tags:
- User
/v1/users/self:
delete:
produces:

View file

@ -71,7 +71,7 @@ func main() {
text = replace.Regex.ReplaceAllString(text, replace.Text)
}
err = os.WriteFile(path, []byte(text), 0644)
err = os.WriteFile(path, []byte(text), 0o644)
if err != nil {
fmt.Println(err)
os.Exit(1)

View file

@ -14,6 +14,7 @@ require (
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
github.com/google/uuid v1.6.0
github.com/gorilla/schema v1.2.1
github.com/hay-kot/easyemails v0.0.0-20240206011027-25232fb79aa3
github.com/hay-kot/httpkit v0.0.9
github.com/mattn/go-sqlite3 v1.14.22
github.com/olahol/melody v1.1.4

View file

@ -1,5 +1,3 @@
ariga.io/atlas v0.19.0 h1:gilVpXabeiGhGI9lj/rQURkXBemnloc41RGOtwVLNc4=
ariga.io/atlas v0.19.0/go.mod h1:uj3pm+hUTVN/X5yfdBexHlZv+1Xu5u5ZbZx7+CDavNU=
ariga.io/atlas v0.19.1 h1:QzBHkakwzEhmPWOzNhw8Yr/Bbicj6Iq5hwEoNI/Jr9A=
ariga.io/atlas v0.19.1/go.mod h1:VPlcXdd4w2KqKnH54yEZcry79UAhpaWaxEsmn5JRNoE=
entgo.io/ent v0.12.5 h1:KREM5E4CSoej4zeGa88Ou/gfturAnpUv0mzAjch1sj4=
@ -14,6 +12,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/ardanlabs/conf/v3 v3.1.7 h1:p232cF68TafoA5U9ZlbxUIhGJtGNdKHBXF80Fdqb5t0=
github.com/ardanlabs/conf/v3 v3.1.7/go.mod h1:zclexWKe0NVj6LHQ8NgDDZ7bQ1spE0KeKPFficdtAjU=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y=
github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk=
github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec=
github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@ -84,12 +84,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hay-kot/httpkit v0.0.6 h1:BidC4UrkS7zRhoTdpKLeF8ODJPKcOZkJ2tk2t2ZIQjQ=
github.com/hay-kot/httpkit v0.0.6/go.mod h1:1s/OJwWRyH6tBtTw76jTp6kwBYvjswziXaokPQH7eKQ=
github.com/hay-kot/httpkit v0.0.7 h1:KxGi+MwXFavfFUfJEMpye5cnMef9TlFu3v7UZipUB8U=
github.com/hay-kot/httpkit v0.0.7/go.mod h1:AD22YluZrvBDxmtB3Pw2SOyp3A2PZqcmBZa0+COrhoU=
github.com/hay-kot/httpkit v0.0.8 h1:n+Z5z35YZcdD9cGwbnIPRbrgDw9LY6lqakH4zYr5z+A=
github.com/hay-kot/httpkit v0.0.8/go.mod h1:AD22YluZrvBDxmtB3Pw2SOyp3A2PZqcmBZa0+COrhoU=
github.com/hay-kot/easyemails v0.0.0-20240206011027-25232fb79aa3 h1:EljujAOvukSS+sh8e883j4vhEiYG4EvJFAhl+XvUYe8=
github.com/hay-kot/easyemails v0.0.0-20240206011027-25232fb79aa3/go.mod h1:SZdhYMO2ZmEuTTDE7pHn0WanXhwteniOCYsQ3B5IT/o=
github.com/hay-kot/httpkit v0.0.9 h1:hu2TPY9awmIYWXxWGubaXl2U61pPvaVsm9YwboBRGu0=
github.com/hay-kot/httpkit v0.0.9/go.mod h1:AD22YluZrvBDxmtB3Pw2SOyp3A2PZqcmBZa0+COrhoU=
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
@ -173,8 +169,6 @@ golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=

View file

@ -4,6 +4,7 @@ package services
import (
"github.com/hay-kot/homebox/backend/internal/core/currencies"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/pkgs/mailer"
)
type AllServices struct {
@ -33,7 +34,7 @@ func WithCurrencies(v []currencies.Currency) func(*options) {
}
}
func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
func New(repos *repo.AllRepos, baseurl string, sender *mailer.Mailer, opts ...OptionsFunc) *AllServices {
if repos == nil {
panic("repos cannot be nil")
}
@ -55,7 +56,11 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
}
return &AllServices{
User: &UserService{repos},
User: &UserService{
repos: repos,
mailer: sender,
baseurl: baseurl,
},
Group: &GroupService{repos},
Items: &ItemService{
repo: repos,

View file

@ -19,11 +19,11 @@ var (
type Context struct {
context.Context
// UID is a unique identifier for the acting user.
UID uuid.UUID
// UserID is a unique identifier for the acting user.
UserID uuid.UUID
// GID is a unique identifier for the acting users group.
GID uuid.UUID
// GroupID is a unique identifier for the acting users group.
GroupID uuid.UUID
// User is the acting user.
User *repo.UserOut
@ -35,8 +35,8 @@ func NewContext(ctx context.Context) Context {
user := UseUserCtx(ctx)
return Context{
Context: ctx,
UID: user.ID,
GID: user.GroupID,
UserID: user.ID,
GroupID: user.GroupID,
User: user,
}
}

View file

@ -11,6 +11,7 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/pkgs/faker"
"github.com/hay-kot/homebox/backend/pkgs/mailer"
_ "github.com/mattn/go-sqlite3"
)
@ -67,15 +68,16 @@ func TestMain(m *testing.M) {
currencies.CollectDefaults(),
)
tSvc = New(tRepos, WithCurrencies(defaults))
defer func() { _ = client.Close() }()
tSvc = New(tRepos, "", &mailer.Mailer{}, WithCurrencies(defaults))
bootstrap()
tCtx = Context{
Context: context.Background(),
GID: tGroup.ID,
UID: tUser.ID,
GroupID: tGroup.ID,
UserID: tUser.ID,
}
os.Exit(m.Run())
exit := m.Run()
_ = client.Close()
os.Exit(exit)
}

View file

@ -153,7 +153,7 @@ func (s *IOSheet) Read(data io.Reader) error {
}
// ReadItems writes the sheet to a writer.
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, groupID uuid.UUID, repos *repo.AllRepos) error {
s.Rows = make([]ExportTSVRow, len(items))
extraHeaders := map[string]struct{}{}
@ -164,7 +164,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
// TODO: Support fetching nested locations
locID := item.Location.ID
locPaths, err := repos.Locations.PathForLoc(context.Background(), GID, locID)
locPaths, err := repos.Locations.PathForLoc(context.Background(), groupID, locID)
if err != nil {
log.Error().Err(err).Msg("could not get location path")
return err

View file

@ -66,7 +66,6 @@ func (svc *BackgroundService) SendNotifiersToday(ctx context.Context) error {
var sendErrs []error
for i := range urls {
err := shoutrrr.Send(urls[i], bldr.String())
if err != nil {
sendErrs = append(sendErrs, err)
}

View file

@ -21,13 +21,13 @@ func (svc *GroupService) UpdateGroup(ctx Context, data repo.GroupUpdate) (repo.G
return repo.Group{}, errors.New("currency cannot be empty")
}
return svc.repos.Groups.GroupUpdate(ctx.Context, ctx.GID, data)
return svc.repos.Groups.GroupUpdate(ctx.Context, ctx.GroupID, data)
}
func (svc *GroupService) NewInvitation(ctx Context, uses int, expiresAt time.Time) (string, error) {
token := hasher.GenerateToken()
_, err := svc.repos.Groups.InvitationCreate(ctx, ctx.GID, repo.GroupInvitationCreate{
_, err := svc.repos.Groups.InvitationCreate(ctx, ctx.GroupID, repo.GroupInvitationCreate{
Token: token.Hash,
Uses: uses,
ExpiresAt: expiresAt,

View file

@ -27,7 +27,7 @@ type ItemService struct {
func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut, error) {
if svc.autoIncrementAssetID {
highest, err := svc.repo.Items.GetHighestAssetID(ctx, ctx.GID)
highest, err := svc.repo.Items.GetHighestAssetID(ctx, ctx.GroupID)
if err != nil {
return repo.ItemOut{}, err
}
@ -35,16 +35,16 @@ func (svc *ItemService) Create(ctx Context, item repo.ItemCreate) (repo.ItemOut,
item.AssetID = highest + 1
}
return svc.repo.Items.Create(ctx, ctx.GID, item)
return svc.repo.Items.Create(ctx, ctx.GroupID, item)
}
func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int, error) {
items, err := svc.repo.Items.GetAllZeroAssetID(ctx, GID)
func (svc *ItemService) EnsureAssetID(ctx context.Context, groupID uuid.UUID) (int, error) {
items, err := svc.repo.Items.GetAllZeroAssetID(ctx, groupID)
if err != nil {
return 0, err
}
highest, err := svc.repo.Items.GetHighestAssetID(ctx, GID)
highest, err := svc.repo.Items.GetHighestAssetID(ctx, groupID)
if err != nil {
return 0, err
}
@ -53,7 +53,7 @@ func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int,
for _, item := range items {
highest++
err = svc.repo.Items.SetAssetID(ctx, GID, item.ID, highest)
err = svc.repo.Items.SetAssetID(ctx, groupID, item.ID, highest)
if err != nil {
return 0, err
}
@ -64,8 +64,8 @@ func (svc *ItemService) EnsureAssetID(ctx context.Context, GID uuid.UUID) (int,
return finished, nil
}
func (svc *ItemService) EnsureImportRef(ctx context.Context, GID uuid.UUID) (int, error) {
ids, err := svc.repo.Items.GetAllZeroImportRef(ctx, GID)
func (svc *ItemService) EnsureImportRef(ctx context.Context, groupID uuid.UUID) (int, error) {
ids, err := svc.repo.Items.GetAllZeroImportRef(ctx, groupID)
if err != nil {
return 0, err
}
@ -74,7 +74,7 @@ func (svc *ItemService) EnsureImportRef(ctx context.Context, GID uuid.UUID) (int
for _, itemID := range ids {
ref := uuid.New().String()[0:8]
err = svc.repo.Items.Patch(ctx, GID, itemID, repo.ItemPatch{ImportRef: &ref})
err = svc.repo.Items.Patch(ctx, groupID, itemID, repo.ItemPatch{ImportRef: &ref})
if err != nil {
return 0, err
}
@ -96,7 +96,7 @@ func serializeLocation[T ~[]string](location T) string {
// 1. If the item does not exist, it is created.
// 2. If the item has a ImportRef and it exists it is skipped
// 3. Locations and Labels are created if they do not exist.
func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Reader) (int, error) {
func (svc *ItemService) CsvImport(ctx context.Context, groupID uuid.UUID, data io.Reader) (int, error) {
sheet := reporting.IOSheet{}
err := sheet.Read(data)
@ -109,7 +109,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
labelMap := make(map[string]uuid.UUID)
{
labels, err := svc.repo.Labels.GetAll(ctx, GID)
labels, err := svc.repo.Labels.GetAll(ctx, groupID)
if err != nil {
return 0, err
}
@ -124,7 +124,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
locationMap := make(map[string]uuid.UUID)
{
locations, err := svc.repo.Locations.Tree(ctx, GID, repo.TreeQuery{WithItems: false})
locations, err := svc.repo.Locations.Tree(ctx, groupID, repo.TreeQuery{WithItems: false})
if err != nil {
return 0, err
}
@ -153,7 +153,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
// Asset ID Pre-Check
highestAID := repo.AssetID(-1)
if svc.autoIncrementAssetID {
highestAID, err = svc.repo.Items.GetHighestAssetID(ctx, GID)
highestAID, err = svc.repo.Items.GetHighestAssetID(ctx, groupID)
if err != nil {
return 0, err
}
@ -169,7 +169,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
// ========================================
// Preflight check for existing item
if row.ImportRef != "" {
exists, err := svc.repo.Items.CheckRef(ctx, GID, row.ImportRef)
exists, err := svc.repo.Items.CheckRef(ctx, groupID, row.ImportRef)
if err != nil {
return 0, fmt.Errorf("error checking for existing item with ref %q: %w", row.ImportRef, err)
}
@ -188,7 +188,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
id, ok := labelMap[label]
if !ok {
newLabel, err := svc.repo.Labels.Create(ctx, GID, repo.LabelCreate{Name: label})
newLabel, err := svc.repo.Labels.Create(ctx, groupID, repo.LabelCreate{Name: label})
if err != nil {
return 0, err
}
@ -220,7 +220,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
parentID = locationMap[parentPath]
}
newLocation, err := svc.repo.Locations.Create(ctx, GID, repo.LocationCreate{
newLocation, err := svc.repo.Locations.Create(ctx, groupID, repo.LocationCreate{
ParentID: parentID,
Name: pathElement,
})
@ -261,12 +261,12 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
LabelIDs: labelIds,
}
item, err = svc.repo.Items.Create(ctx, GID, newItem)
item, err = svc.repo.Items.Create(ctx, groupID, newItem)
if err != nil {
return 0, err
}
default:
item, err = svc.repo.Items.GetByRef(ctx, GID, row.ImportRef)
item, err = svc.repo.Items.GetByRef(ctx, groupID, row.ImportRef)
if err != nil {
return 0, err
}
@ -318,7 +318,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
Fields: fields,
}
item, err = svc.repo.Items.UpdateByGroup(ctx, GID, updateItem)
item, err = svc.repo.Items.UpdateByGroup(ctx, groupID, updateItem)
if err != nil {
return 0, err
}
@ -329,15 +329,15 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
return finished, nil
}
func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
func (svc *ItemService) ExportTSV(ctx context.Context, groupID uuid.UUID) ([][]string, error) {
items, err := svc.repo.Items.GetAll(ctx, groupID)
if err != nil {
return nil, err
}
sheet := reporting.IOSheet{}
err = sheet.ReadItems(ctx, items, GID, svc.repo)
err = sheet.ReadItems(ctx, items, groupID, svc.repo)
if err != nil {
return nil, err
}
@ -345,8 +345,8 @@ func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]strin
return sheet.TSV()
}
func (svc *ItemService) ExportBillOfMaterialsTSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
func (svc *ItemService) ExportBillOfMaterialsTSV(ctx context.Context, groupID uuid.UUID) ([]byte, error) {
items, err := svc.repo.Items.GetAll(ctx, groupID)
if err != nil {
return nil, err
}

View file

@ -35,7 +35,7 @@ func (svc *ItemService) AttachmentUpdate(ctx Context, itemID uuid.UUID, data *re
return repo.ItemOut{}, err
}
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
return svc.repo.Items.GetOneByGroup(ctx, ctx.GroupID, itemID)
}
// AttachmentAdd adds an attachment to an item by creating an entry in the Documents table and linking it to the Attachment
@ -43,13 +43,13 @@ func (svc *ItemService) AttachmentUpdate(ctx Context, itemID uuid.UUID, data *re
// relative path during construction of the service.
func (svc *ItemService) AttachmentAdd(ctx Context, itemID uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (repo.ItemOut, error) {
// Get the Item
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
_, err := svc.repo.Items.GetOneByGroup(ctx, ctx.GroupID, itemID)
if err != nil {
return repo.ItemOut{}, err
}
// Create the document
doc, err := svc.repo.Docs.Create(ctx, ctx.GID, repo.DocumentCreate{Title: filename, Content: file})
doc, err := svc.repo.Docs.Create(ctx, ctx.GroupID, repo.DocumentCreate{Title: filename, Content: file})
if err != nil {
log.Err(err).Msg("failed to create document")
return repo.ItemOut{}, err
@ -62,7 +62,7 @@ func (svc *ItemService) AttachmentAdd(ctx Context, itemID uuid.UUID, filename st
return repo.ItemOut{}, err
}
return svc.repo.Items.GetOneByGroup(ctx, ctx.GID, itemID)
return svc.repo.Items.GetOneByGroup(ctx, ctx.GroupID, itemID)
}
func (svc *ItemService) AttachmentDelete(ctx context.Context, gid, itemID, attachmentID uuid.UUID) error {

View file

@ -3,12 +3,15 @@ package services
import (
"context"
"errors"
"net/url"
"time"
"github.com/google/uuid"
"github.com/hay-kot/easyemails"
"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/pkgs/hasher"
"github.com/hay-kot/homebox/backend/pkgs/mailer"
"github.com/rs/zerolog/log"
)
@ -19,8 +22,15 @@ var (
ErrorTokenIDMismatch = errors.New("token id mismatch")
)
func init() { // nolint: gochecknoinits
easyemails.ImageLogoHeader = "https://raw.githubusercontent.com/hay-kot/homebox/af9aa239af66df17478f5ed9283e303daf7c6775/docs/docs/assets/img/homebox-email-banner.jpg"
easyemails.ColorPrimary = "#5D7F67"
}
type UserService struct {
repos *repo.AllRepos
repos *repo.AllRepos
mailer *mailer.Mailer
baseurl string
}
type (
@ -39,6 +49,9 @@ type (
Username string `json:"username"`
Password string `json:"password"`
}
PasswordResetRequest struct {
Email string `json:"email"`
}
)
// RegisterUser creates a new user and group in the data with the provided data. It also bootstraps the user's group
@ -132,13 +145,13 @@ func (svc *UserService) GetSelf(ctx context.Context, requestToken string) (repo.
return svc.repos.AuthTokens.GetUserFromToken(ctx, hash)
}
func (svc *UserService) UpdateSelf(ctx context.Context, ID uuid.UUID, data repo.UserUpdate) (repo.UserOut, error) {
err := svc.repos.Users.Update(ctx, ID, data)
func (svc *UserService) UpdateSelf(ctx context.Context, userID uuid.UUID, data repo.UserUpdate) (repo.UserOut, error) {
err := svc.repos.Users.Update(ctx, userID, data)
if err != nil {
return repo.UserOut{}, err
}
return svc.repos.Users.GetOneID(ctx, ID)
return svc.repos.Users.GetOneID(ctx, userID)
}
// ============================================================================
@ -217,28 +230,28 @@ func (svc *UserService) RenewToken(ctx context.Context, token string) (UserAuthT
// DeleteSelf deletes the user that is currently logged based of the provided UUID
// There is _NO_ protection against deleting the wrong user, as such this should only
// be used when the identify of the user has been confirmed.
func (svc *UserService) DeleteSelf(ctx context.Context, ID uuid.UUID) error {
return svc.repos.Users.Delete(ctx, ID)
func (svc *UserService) DeleteSelf(ctx context.Context, userID uuid.UUID) error {
return svc.repos.Users.Delete(ctx, userID)
}
func (svc *UserService) ChangePassword(ctx Context, current string, new string) (ok bool) {
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UID)
func (svc *UserService) PasswordChange(ctx Context, currentPassword, newPassword string) (ok bool) {
usr, err := svc.repos.Users.GetOneID(ctx, ctx.UserID)
if err != nil {
return false
}
if !hasher.CheckPasswordHash(current, usr.PasswordHash) {
if !hasher.CheckPasswordHash(currentPassword, usr.PasswordHash) {
log.Err(errors.New("current password is incorrect")).Msg("Failed to change password")
return false
}
hashed, err := hasher.HashPassword(new)
hashed, err := hasher.HashPassword(newPassword)
if err != nil {
log.Err(err).Msg("Failed to hash password")
return false
}
err = svc.repos.Users.ChangePassword(ctx.Context, ctx.UID, hashed)
err = svc.repos.Users.ChangePassword(ctx.Context, ctx.UserID, hashed)
if err != nil {
log.Err(err).Msg("Failed to change password")
return false
@ -246,3 +259,80 @@ func (svc *UserService) ChangePassword(ctx Context, current string, new string)
return true
}
func (svc *UserService) PasswordChangeWithToken(ctx Context, token, newPassword string) error {
hashed, err := hasher.HashPassword(newPassword)
if err != nil {
return err
}
tokenHash := hasher.HashToken(token)
resetToken, err := svc.repos.Users.PasswordResetGet(ctx.Context, tokenHash)
if err != nil {
return err
}
if resetToken.UserID != ctx.UserID {
return ErrorTokenIDMismatch
}
err = svc.repos.Users.ChangePassword(ctx.Context, ctx.UserID, hashed)
if err != nil {
return err
}
err = svc.repos.Users.PasswordResetDelete(ctx.Context, tokenHash)
if err != nil {
return err
}
return nil
}
func (svc *UserService) PasswordResetRequest(ctx context.Context, req PasswordResetRequest) error {
usr, err := svc.repos.Users.GetOneEmail(ctx, req.Email)
if err != nil {
log.Warn().Err(err).Msg("failed to get user for email reset")
return err
}
token := hasher.GenerateToken()
err = svc.repos.Users.PasswordResetCreate(ctx, usr.ID, token.Hash)
if err != nil {
return err
}
resetURL, err := url.JoinPath(svc.baseurl, "reset-password/")
if err != nil {
return err
}
resetURL = resetURL + "?token=" + token.Raw
bldr := easyemails.NewBuilder().Add(
easyemails.WithParagraph(
easyemails.WithText("You have requested a password reset. Please click the link below to reset your password."),
),
easyemails.WithButton("Reset Password", resetURL),
easyemails.WithParagraph(
easyemails.WithText("[Github](https://github.com/hay-kot/homebox) · [Docs](https://hay-kot.github.io/homebox/)").
Centered(),
).
FontSize(12),
)
msg := mailer.NewMessageBuilder().
SetBody(bldr.Render()).
SetSubject("Password Reset").
SetTo(usr.Name, usr.Email).
Build()
err = svc.mailer.Send(msg)
if err != nil {
log.Err(err).Msg("Failed to send password reset email")
return err
}
return nil
}

View file

@ -0,0 +1,184 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
// ActionToken is the model entity for the ActionToken schema.
type ActionToken struct {
config `json:"-"`
// ID of the ent.
ID uuid.UUID `json:"id,omitempty"`
// UserID holds the value of the "user_id" field.
UserID uuid.UUID `json:"user_id,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"`
// UpdatedAt holds the value of the "updated_at" field.
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Action holds the value of the "action" field.
Action actiontoken.Action `json:"action,omitempty"`
// Token holds the value of the "token" field.
Token []byte `json:"token,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the ActionTokenQuery when eager-loading is set.
Edges ActionTokenEdges `json:"edges"`
selectValues sql.SelectValues
}
// ActionTokenEdges holds the relations/edges for other nodes in the graph.
type ActionTokenEdges struct {
// User holds the value of the user edge.
User *User `json:"user,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [1]bool
}
// UserOrErr returns the User value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found.
func (e ActionTokenEdges) UserOrErr() (*User, error) {
if e.loadedTypes[0] {
if e.User == nil {
// Edge was loaded but was not found.
return nil, &NotFoundError{label: user.Label}
}
return e.User, nil
}
return nil, &NotLoadedError{edge: "user"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*ActionToken) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
for i := range columns {
switch columns[i] {
case actiontoken.FieldToken:
values[i] = new([]byte)
case actiontoken.FieldAction:
values[i] = new(sql.NullString)
case actiontoken.FieldCreatedAt, actiontoken.FieldUpdatedAt:
values[i] = new(sql.NullTime)
case actiontoken.FieldID, actiontoken.FieldUserID:
values[i] = new(uuid.UUID)
default:
values[i] = new(sql.UnknownType)
}
}
return values, nil
}
// assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the ActionToken fields.
func (at *ActionToken) 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 actiontoken.FieldID:
if value, ok := values[i].(*uuid.UUID); !ok {
return fmt.Errorf("unexpected type %T for field id", values[i])
} else if value != nil {
at.ID = *value
}
case actiontoken.FieldUserID:
if value, ok := values[i].(*uuid.UUID); !ok {
return fmt.Errorf("unexpected type %T for field user_id", values[i])
} else if value != nil {
at.UserID = *value
}
case actiontoken.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid {
at.CreatedAt = value.Time
}
case actiontoken.FieldUpdatedAt:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field updated_at", values[i])
} else if value.Valid {
at.UpdatedAt = value.Time
}
case actiontoken.FieldAction:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field action", values[i])
} else if value.Valid {
at.Action = actiontoken.Action(value.String)
}
case actiontoken.FieldToken:
if value, ok := values[i].(*[]byte); !ok {
return fmt.Errorf("unexpected type %T for field token", values[i])
} else if value != nil {
at.Token = *value
}
default:
at.selectValues.Set(columns[i], values[i])
}
}
return nil
}
// Value returns the ent.Value that was dynamically selected and assigned to the ActionToken.
// This includes values selected through modifiers, order, etc.
func (at *ActionToken) Value(name string) (ent.Value, error) {
return at.selectValues.Get(name)
}
// QueryUser queries the "user" edge of the ActionToken entity.
func (at *ActionToken) QueryUser() *UserQuery {
return NewActionTokenClient(at.config).QueryUser(at)
}
// Update returns a builder for updating this ActionToken.
// Note that you need to call ActionToken.Unwrap() before calling this method if this ActionToken
// was returned from a transaction, and the transaction was committed or rolled back.
func (at *ActionToken) Update() *ActionTokenUpdateOne {
return NewActionTokenClient(at.config).UpdateOne(at)
}
// Unwrap unwraps the ActionToken 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 (at *ActionToken) Unwrap() *ActionToken {
_tx, ok := at.config.driver.(*txDriver)
if !ok {
panic("ent: ActionToken is not a transactional entity")
}
at.config.driver = _tx.drv
return at
}
// String implements the fmt.Stringer.
func (at *ActionToken) String() string {
var builder strings.Builder
builder.WriteString("ActionToken(")
builder.WriteString(fmt.Sprintf("id=%v, ", at.ID))
builder.WriteString("user_id=")
builder.WriteString(fmt.Sprintf("%v", at.UserID))
builder.WriteString(", ")
builder.WriteString("created_at=")
builder.WriteString(at.CreatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("updated_at=")
builder.WriteString(at.UpdatedAt.Format(time.ANSIC))
builder.WriteString(", ")
builder.WriteString("action=")
builder.WriteString(fmt.Sprintf("%v", at.Action))
builder.WriteString(", ")
builder.WriteString("token=")
builder.WriteString(fmt.Sprintf("%v", at.Token))
builder.WriteByte(')')
return builder.String()
}
// ActionTokens is a parsable slice of ActionToken.
type ActionTokens []*ActionToken

View file

@ -0,0 +1,138 @@
// Code generated by ent, DO NOT EDIT.
package actiontoken
import (
"fmt"
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
)
const (
// Label holds the string label denoting the actiontoken type in the database.
Label = "action_token"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldUserID holds the string denoting the user_id field in the database.
FieldUserID = "user_id"
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at"
// FieldUpdatedAt holds the string denoting the updated_at field in the database.
FieldUpdatedAt = "updated_at"
// FieldAction holds the string denoting the action field in the database.
FieldAction = "action"
// FieldToken holds the string denoting the token field in the database.
FieldToken = "token"
// EdgeUser holds the string denoting the user edge name in mutations.
EdgeUser = "user"
// Table holds the table name of the actiontoken in the database.
Table = "action_tokens"
// UserTable is the table that holds the user relation/edge.
UserTable = "action_tokens"
// UserInverseTable is the table name for the User entity.
// It exists in this package in order to avoid circular dependency with the "user" package.
UserInverseTable = "users"
// UserColumn is the table column denoting the user relation/edge.
UserColumn = "user_id"
)
// Columns holds all SQL columns for actiontoken fields.
var Columns = []string{
FieldID,
FieldUserID,
FieldCreatedAt,
FieldUpdatedAt,
FieldAction,
FieldToken,
}
// 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
}
}
return false
}
var (
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time
// DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
DefaultUpdatedAt func() time.Time
// UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
UpdateDefaultUpdatedAt func() time.Time
// DefaultID holds the default value on creation for the "id" field.
DefaultID func() uuid.UUID
)
// Action defines the type for the "action" enum field.
type Action string
// ActionResetPassword is the default value of the Action enum.
const DefaultAction = ActionResetPassword
// Action values.
const (
ActionResetPassword Action = "reset_password"
)
func (a Action) String() string {
return string(a)
}
// ActionValidator is a validator for the "action" field enum values. It is called by the builders before save.
func ActionValidator(a Action) error {
switch a {
case ActionResetPassword:
return nil
default:
return fmt.Errorf("actiontoken: invalid enum value for action field: %q", a)
}
}
// OrderOption defines the ordering options for the ActionToken queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByUserID orders the results by the user_id field.
func ByUserID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUserID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUpdatedAt orders the results by the updated_at field.
func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
}
// ByAction orders the results by the action field.
func ByAction(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldAction, opts...).ToFunc()
}
// ByUserField orders the results by user field.
func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
}
}
func newUserStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
}

View file

@ -0,0 +1,275 @@
// Code generated by ent, DO NOT EDIT.
package actiontoken
import (
"time"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
)
// ID filters vertices based on their ID field.
func ID(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldID, id))
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldID, id))
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNEQ(FieldID, id))
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldIn(FieldID, ids...))
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNotIn(FieldID, ids...))
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGT(FieldID, id))
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGTE(FieldID, id))
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLT(FieldID, id))
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLTE(FieldID, id))
}
// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
func UserID(v uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldUserID, v))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldCreatedAt, v))
}
// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
func UpdatedAt(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldUpdatedAt, v))
}
// Token applies equality check predicate on the "token" field. It's identical to TokenEQ.
func Token(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldToken, v))
}
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldUserID, v))
}
// UserIDNEQ applies the NEQ predicate on the "user_id" field.
func UserIDNEQ(v uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNEQ(FieldUserID, v))
}
// UserIDIn applies the In predicate on the "user_id" field.
func UserIDIn(vs ...uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldIn(FieldUserID, vs...))
}
// UserIDNotIn applies the NotIn predicate on the "user_id" field.
func UserIDNotIn(vs ...uuid.UUID) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNotIn(FieldUserID, vs...))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldCreatedAt, v))
}
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNEQ(FieldCreatedAt, v))
}
// CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldIn(FieldCreatedAt, vs...))
}
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNotIn(FieldCreatedAt, vs...))
}
// CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGT(FieldCreatedAt, v))
}
// CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGTE(FieldCreatedAt, v))
}
// CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLT(FieldCreatedAt, v))
}
// CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLTE(FieldCreatedAt, v))
}
// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
func UpdatedAtEQ(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldUpdatedAt, v))
}
// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
func UpdatedAtNEQ(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNEQ(FieldUpdatedAt, v))
}
// UpdatedAtIn applies the In predicate on the "updated_at" field.
func UpdatedAtIn(vs ...time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldIn(FieldUpdatedAt, vs...))
}
// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
func UpdatedAtNotIn(vs ...time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNotIn(FieldUpdatedAt, vs...))
}
// UpdatedAtGT applies the GT predicate on the "updated_at" field.
func UpdatedAtGT(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGT(FieldUpdatedAt, v))
}
// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
func UpdatedAtGTE(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGTE(FieldUpdatedAt, v))
}
// UpdatedAtLT applies the LT predicate on the "updated_at" field.
func UpdatedAtLT(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLT(FieldUpdatedAt, v))
}
// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
func UpdatedAtLTE(v time.Time) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLTE(FieldUpdatedAt, v))
}
// ActionEQ applies the EQ predicate on the "action" field.
func ActionEQ(v Action) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldAction, v))
}
// ActionNEQ applies the NEQ predicate on the "action" field.
func ActionNEQ(v Action) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNEQ(FieldAction, v))
}
// ActionIn applies the In predicate on the "action" field.
func ActionIn(vs ...Action) predicate.ActionToken {
return predicate.ActionToken(sql.FieldIn(FieldAction, vs...))
}
// ActionNotIn applies the NotIn predicate on the "action" field.
func ActionNotIn(vs ...Action) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNotIn(FieldAction, vs...))
}
// TokenEQ applies the EQ predicate on the "token" field.
func TokenEQ(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldEQ(FieldToken, v))
}
// TokenNEQ applies the NEQ predicate on the "token" field.
func TokenNEQ(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNEQ(FieldToken, v))
}
// TokenIn applies the In predicate on the "token" field.
func TokenIn(vs ...[]byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldIn(FieldToken, vs...))
}
// TokenNotIn applies the NotIn predicate on the "token" field.
func TokenNotIn(vs ...[]byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldNotIn(FieldToken, vs...))
}
// TokenGT applies the GT predicate on the "token" field.
func TokenGT(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGT(FieldToken, v))
}
// TokenGTE applies the GTE predicate on the "token" field.
func TokenGTE(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldGTE(FieldToken, v))
}
// TokenLT applies the LT predicate on the "token" field.
func TokenLT(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLT(FieldToken, v))
}
// TokenLTE applies the LTE predicate on the "token" field.
func TokenLTE(v []byte) predicate.ActionToken {
return predicate.ActionToken(sql.FieldLTE(FieldToken, v))
}
// HasUser applies the HasEdge predicate on the "user" edge.
func HasUser() predicate.ActionToken {
return predicate.ActionToken(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
func HasUserWith(preds ...predicate.User) predicate.ActionToken {
return predicate.ActionToken(func(s *sql.Selector) {
step := newUserStep()
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.ActionToken) predicate.ActionToken {
return predicate.ActionToken(sql.AndPredicates(predicates...))
}
// Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.ActionToken) predicate.ActionToken {
return predicate.ActionToken(sql.OrPredicates(predicates...))
}
// Not applies the not operator on the given predicate.
func Not(p predicate.ActionToken) predicate.ActionToken {
return predicate.ActionToken(sql.NotPredicates(p))
}

View file

@ -0,0 +1,329 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
// ActionTokenCreate is the builder for creating a ActionToken entity.
type ActionTokenCreate struct {
config
mutation *ActionTokenMutation
hooks []Hook
}
// SetUserID sets the "user_id" field.
func (atc *ActionTokenCreate) SetUserID(u uuid.UUID) *ActionTokenCreate {
atc.mutation.SetUserID(u)
return atc
}
// SetCreatedAt sets the "created_at" field.
func (atc *ActionTokenCreate) SetCreatedAt(t time.Time) *ActionTokenCreate {
atc.mutation.SetCreatedAt(t)
return atc
}
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (atc *ActionTokenCreate) SetNillableCreatedAt(t *time.Time) *ActionTokenCreate {
if t != nil {
atc.SetCreatedAt(*t)
}
return atc
}
// SetUpdatedAt sets the "updated_at" field.
func (atc *ActionTokenCreate) SetUpdatedAt(t time.Time) *ActionTokenCreate {
atc.mutation.SetUpdatedAt(t)
return atc
}
// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
func (atc *ActionTokenCreate) SetNillableUpdatedAt(t *time.Time) *ActionTokenCreate {
if t != nil {
atc.SetUpdatedAt(*t)
}
return atc
}
// SetAction sets the "action" field.
func (atc *ActionTokenCreate) SetAction(a actiontoken.Action) *ActionTokenCreate {
atc.mutation.SetAction(a)
return atc
}
// SetNillableAction sets the "action" field if the given value is not nil.
func (atc *ActionTokenCreate) SetNillableAction(a *actiontoken.Action) *ActionTokenCreate {
if a != nil {
atc.SetAction(*a)
}
return atc
}
// SetToken sets the "token" field.
func (atc *ActionTokenCreate) SetToken(b []byte) *ActionTokenCreate {
atc.mutation.SetToken(b)
return atc
}
// SetID sets the "id" field.
func (atc *ActionTokenCreate) SetID(u uuid.UUID) *ActionTokenCreate {
atc.mutation.SetID(u)
return atc
}
// SetNillableID sets the "id" field if the given value is not nil.
func (atc *ActionTokenCreate) SetNillableID(u *uuid.UUID) *ActionTokenCreate {
if u != nil {
atc.SetID(*u)
}
return atc
}
// SetUser sets the "user" edge to the User entity.
func (atc *ActionTokenCreate) SetUser(u *User) *ActionTokenCreate {
return atc.SetUserID(u.ID)
}
// Mutation returns the ActionTokenMutation object of the builder.
func (atc *ActionTokenCreate) Mutation() *ActionTokenMutation {
return atc.mutation
}
// Save creates the ActionToken in the database.
func (atc *ActionTokenCreate) Save(ctx context.Context) (*ActionToken, error) {
atc.defaults()
return withHooks(ctx, atc.sqlSave, atc.mutation, atc.hooks)
}
// SaveX calls Save and panics if Save returns an error.
func (atc *ActionTokenCreate) SaveX(ctx context.Context) *ActionToken {
v, err := atc.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (atc *ActionTokenCreate) Exec(ctx context.Context) error {
_, err := atc.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (atc *ActionTokenCreate) ExecX(ctx context.Context) {
if err := atc.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (atc *ActionTokenCreate) defaults() {
if _, ok := atc.mutation.CreatedAt(); !ok {
v := actiontoken.DefaultCreatedAt()
atc.mutation.SetCreatedAt(v)
}
if _, ok := atc.mutation.UpdatedAt(); !ok {
v := actiontoken.DefaultUpdatedAt()
atc.mutation.SetUpdatedAt(v)
}
if _, ok := atc.mutation.Action(); !ok {
v := actiontoken.DefaultAction
atc.mutation.SetAction(v)
}
if _, ok := atc.mutation.ID(); !ok {
v := actiontoken.DefaultID()
atc.mutation.SetID(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (atc *ActionTokenCreate) check() error {
if _, ok := atc.mutation.UserID(); !ok {
return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "ActionToken.user_id"`)}
}
if _, ok := atc.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ActionToken.created_at"`)}
}
if _, ok := atc.mutation.UpdatedAt(); !ok {
return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ActionToken.updated_at"`)}
}
if _, ok := atc.mutation.Action(); !ok {
return &ValidationError{Name: "action", err: errors.New(`ent: missing required field "ActionToken.action"`)}
}
if v, ok := atc.mutation.Action(); ok {
if err := actiontoken.ActionValidator(v); err != nil {
return &ValidationError{Name: "action", err: fmt.Errorf(`ent: validator failed for field "ActionToken.action": %w`, err)}
}
}
if _, ok := atc.mutation.Token(); !ok {
return &ValidationError{Name: "token", err: errors.New(`ent: missing required field "ActionToken.token"`)}
}
if _, ok := atc.mutation.UserID(); !ok {
return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "ActionToken.user"`)}
}
return nil
}
func (atc *ActionTokenCreate) sqlSave(ctx context.Context) (*ActionToken, error) {
if err := atc.check(); err != nil {
return nil, err
}
_node, _spec := atc.createSpec()
if err := sqlgraph.CreateNode(ctx, atc.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
if _spec.ID.Value != nil {
if id, ok := _spec.ID.Value.(*uuid.UUID); ok {
_node.ID = *id
} else if err := _node.ID.Scan(_spec.ID.Value); err != nil {
return nil, err
}
}
atc.mutation.id = &_node.ID
atc.mutation.done = true
return _node, nil
}
func (atc *ActionTokenCreate) createSpec() (*ActionToken, *sqlgraph.CreateSpec) {
var (
_node = &ActionToken{config: atc.config}
_spec = sqlgraph.NewCreateSpec(actiontoken.Table, sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID))
)
if id, ok := atc.mutation.ID(); ok {
_node.ID = id
_spec.ID.Value = &id
}
if value, ok := atc.mutation.CreatedAt(); ok {
_spec.SetField(actiontoken.FieldCreatedAt, field.TypeTime, value)
_node.CreatedAt = value
}
if value, ok := atc.mutation.UpdatedAt(); ok {
_spec.SetField(actiontoken.FieldUpdatedAt, field.TypeTime, value)
_node.UpdatedAt = value
}
if value, ok := atc.mutation.Action(); ok {
_spec.SetField(actiontoken.FieldAction, field.TypeEnum, value)
_node.Action = value
}
if value, ok := atc.mutation.Token(); ok {
_spec.SetField(actiontoken.FieldToken, field.TypeBytes, value)
_node.Token = value
}
if nodes := atc.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: actiontoken.UserTable,
Columns: []string{actiontoken.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_node.UserID = nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}
// ActionTokenCreateBulk is the builder for creating many ActionToken entities in bulk.
type ActionTokenCreateBulk struct {
config
err error
builders []*ActionTokenCreate
}
// Save creates the ActionToken entities in the database.
func (atcb *ActionTokenCreateBulk) Save(ctx context.Context) ([]*ActionToken, error) {
if atcb.err != nil {
return nil, atcb.err
}
specs := make([]*sqlgraph.CreateSpec, len(atcb.builders))
nodes := make([]*ActionToken, len(atcb.builders))
mutators := make([]Mutator, len(atcb.builders))
for i := range atcb.builders {
func(i int, root context.Context) {
builder := atcb.builders[i]
builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*ActionTokenMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err := builder.check(); err != nil {
return nil, err
}
builder.mutation = mutation
var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, atcb.builders[i+1].mutation)
} else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
// Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, atcb.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
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, atcb.builders[0].mutation); err != nil {
return nil, err
}
}
return nodes, nil
}
// SaveX is like Save, but panics if an error occurs.
func (atcb *ActionTokenCreateBulk) SaveX(ctx context.Context) []*ActionToken {
v, err := atcb.Save(ctx)
if err != nil {
panic(err)
}
return v
}
// Exec executes the query.
func (atcb *ActionTokenCreateBulk) Exec(ctx context.Context) error {
_, err := atcb.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (atcb *ActionTokenCreateBulk) ExecX(ctx context.Context) {
if err := atcb.Exec(ctx); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,88 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"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/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
)
// ActionTokenDelete is the builder for deleting a ActionToken entity.
type ActionTokenDelete struct {
config
hooks []Hook
mutation *ActionTokenMutation
}
// Where appends a list predicates to the ActionTokenDelete builder.
func (atd *ActionTokenDelete) Where(ps ...predicate.ActionToken) *ActionTokenDelete {
atd.mutation.Where(ps...)
return atd
}
// Exec executes the deletion query and returns how many vertices were deleted.
func (atd *ActionTokenDelete) Exec(ctx context.Context) (int, error) {
return withHooks(ctx, atd.sqlExec, atd.mutation, atd.hooks)
}
// ExecX is like Exec, but panics if an error occurs.
func (atd *ActionTokenDelete) ExecX(ctx context.Context) int {
n, err := atd.Exec(ctx)
if err != nil {
panic(err)
}
return n
}
func (atd *ActionTokenDelete) sqlExec(ctx context.Context) (int, error) {
_spec := sqlgraph.NewDeleteSpec(actiontoken.Table, sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID))
if ps := atd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
affected, err := sqlgraph.DeleteNodes(ctx, atd.driver, _spec)
if err != nil && sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
atd.mutation.done = true
return affected, err
}
// ActionTokenDeleteOne is the builder for deleting a single ActionToken entity.
type ActionTokenDeleteOne struct {
atd *ActionTokenDelete
}
// Where appends a list predicates to the ActionTokenDelete builder.
func (atdo *ActionTokenDeleteOne) Where(ps ...predicate.ActionToken) *ActionTokenDeleteOne {
atdo.atd.mutation.Where(ps...)
return atdo
}
// Exec executes the deletion query.
func (atdo *ActionTokenDeleteOne) Exec(ctx context.Context) error {
n, err := atdo.atd.Exec(ctx)
switch {
case err != nil:
return err
case n == 0:
return &NotFoundError{actiontoken.Label}
default:
return nil
}
}
// ExecX is like Exec, but panics if an error occurs.
func (atdo *ActionTokenDeleteOne) ExecX(ctx context.Context) {
if err := atdo.Exec(ctx); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,606 @@
// 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/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
// ActionTokenQuery is the builder for querying ActionToken entities.
type ActionTokenQuery struct {
config
ctx *QueryContext
order []actiontoken.OrderOption
inters []Interceptor
predicates []predicate.ActionToken
withUser *UserQuery
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
}
// Where adds a new predicate for the ActionTokenQuery builder.
func (atq *ActionTokenQuery) Where(ps ...predicate.ActionToken) *ActionTokenQuery {
atq.predicates = append(atq.predicates, ps...)
return atq
}
// Limit the number of records to be returned by this query.
func (atq *ActionTokenQuery) Limit(limit int) *ActionTokenQuery {
atq.ctx.Limit = &limit
return atq
}
// Offset to start from.
func (atq *ActionTokenQuery) Offset(offset int) *ActionTokenQuery {
atq.ctx.Offset = &offset
return atq
}
// 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 (atq *ActionTokenQuery) Unique(unique bool) *ActionTokenQuery {
atq.ctx.Unique = &unique
return atq
}
// Order specifies how the records should be ordered.
func (atq *ActionTokenQuery) Order(o ...actiontoken.OrderOption) *ActionTokenQuery {
atq.order = append(atq.order, o...)
return atq
}
// QueryUser chains the current query on the "user" edge.
func (atq *ActionTokenQuery) QueryUser() *UserQuery {
query := (&UserClient{config: atq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := atq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(actiontoken.Table, actiontoken.FieldID, selector),
sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, actiontoken.UserTable, actiontoken.UserColumn),
)
fromU = sqlgraph.SetNeighbors(atq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first ActionToken entity from the query.
// Returns a *NotFoundError when no ActionToken was found.
func (atq *ActionTokenQuery) First(ctx context.Context) (*ActionToken, error) {
nodes, err := atq.Limit(1).All(setContextOp(ctx, atq.ctx, "First"))
if err != nil {
return nil, err
}
if len(nodes) == 0 {
return nil, &NotFoundError{actiontoken.Label}
}
return nodes[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (atq *ActionTokenQuery) FirstX(ctx context.Context) *ActionToken {
node, err := atq.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return node
}
// FirstID returns the first ActionToken ID from the query.
// Returns a *NotFoundError when no ActionToken ID was found.
func (atq *ActionTokenQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = atq.Limit(1).IDs(setContextOp(ctx, atq.ctx, "FirstID")); err != nil {
return
}
if len(ids) == 0 {
err = &NotFoundError{actiontoken.Label}
return
}
return ids[0], nil
}
// FirstIDX is like FirstID, but panics if an error occurs.
func (atq *ActionTokenQuery) FirstIDX(ctx context.Context) uuid.UUID {
id, err := atq.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns a single ActionToken entity found by the query, ensuring it only returns one.
// Returns a *NotSingularError when more than one ActionToken entity is found.
// Returns a *NotFoundError when no ActionToken entities are found.
func (atq *ActionTokenQuery) Only(ctx context.Context) (*ActionToken, error) {
nodes, err := atq.Limit(2).All(setContextOp(ctx, atq.ctx, "Only"))
if err != nil {
return nil, err
}
switch len(nodes) {
case 1:
return nodes[0], nil
case 0:
return nil, &NotFoundError{actiontoken.Label}
default:
return nil, &NotSingularError{actiontoken.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (atq *ActionTokenQuery) OnlyX(ctx context.Context) *ActionToken {
node, err := atq.Only(ctx)
if err != nil {
panic(err)
}
return node
}
// OnlyID is like Only, but returns the only ActionToken ID in the query.
// Returns a *NotSingularError when more than one ActionToken ID is found.
// Returns a *NotFoundError when no entities are found.
func (atq *ActionTokenQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) {
var ids []uuid.UUID
if ids, err = atq.Limit(2).IDs(setContextOp(ctx, atq.ctx, "OnlyID")); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &NotFoundError{actiontoken.Label}
default:
err = &NotSingularError{actiontoken.Label}
}
return
}
// OnlyIDX is like OnlyID, but panics if an error occurs.
func (atq *ActionTokenQuery) OnlyIDX(ctx context.Context) uuid.UUID {
id, err := atq.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of ActionTokens.
func (atq *ActionTokenQuery) All(ctx context.Context) ([]*ActionToken, error) {
ctx = setContextOp(ctx, atq.ctx, "All")
if err := atq.prepareQuery(ctx); err != nil {
return nil, err
}
qr := querierAll[[]*ActionToken, *ActionTokenQuery]()
return withInterceptors[[]*ActionToken](ctx, atq, qr, atq.inters)
}
// AllX is like All, but panics if an error occurs.
func (atq *ActionTokenQuery) AllX(ctx context.Context) []*ActionToken {
nodes, err := atq.All(ctx)
if err != nil {
panic(err)
}
return nodes
}
// IDs executes the query and returns a list of ActionToken IDs.
func (atq *ActionTokenQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) {
if atq.ctx.Unique == nil && atq.path != nil {
atq.Unique(true)
}
ctx = setContextOp(ctx, atq.ctx, "IDs")
if err = atq.Select(actiontoken.FieldID).Scan(ctx, &ids); err != nil {
return nil, err
}
return ids, nil
}
// IDsX is like IDs, but panics if an error occurs.
func (atq *ActionTokenQuery) IDsX(ctx context.Context) []uuid.UUID {
ids, err := atq.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (atq *ActionTokenQuery) Count(ctx context.Context) (int, error) {
ctx = setContextOp(ctx, atq.ctx, "Count")
if err := atq.prepareQuery(ctx); err != nil {
return 0, err
}
return withInterceptors[int](ctx, atq, querierCount[*ActionTokenQuery](), atq.inters)
}
// CountX is like Count, but panics if an error occurs.
func (atq *ActionTokenQuery) CountX(ctx context.Context) int {
count, err := atq.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (atq *ActionTokenQuery) Exist(ctx context.Context) (bool, error) {
ctx = setContextOp(ctx, atq.ctx, "Exist")
switch _, err := atq.FirstID(ctx); {
case IsNotFound(err):
return false, nil
case err != nil:
return false, fmt.Errorf("ent: check existence: %w", err)
default:
return true, nil
}
}
// ExistX is like Exist, but panics if an error occurs.
func (atq *ActionTokenQuery) ExistX(ctx context.Context) bool {
exist, err := atq.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the ActionTokenQuery builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (atq *ActionTokenQuery) Clone() *ActionTokenQuery {
if atq == nil {
return nil
}
return &ActionTokenQuery{
config: atq.config,
ctx: atq.ctx.Clone(),
order: append([]actiontoken.OrderOption{}, atq.order...),
inters: append([]Interceptor{}, atq.inters...),
predicates: append([]predicate.ActionToken{}, atq.predicates...),
withUser: atq.withUser.Clone(),
// clone intermediate query.
sql: atq.sql.Clone(),
path: atq.path,
}
}
// WithUser tells the query-builder to eager-load the nodes that are connected to
// the "user" edge. The optional arguments are used to configure the query builder of the edge.
func (atq *ActionTokenQuery) WithUser(opts ...func(*UserQuery)) *ActionTokenQuery {
query := (&UserClient{config: atq.config}).Query()
for _, opt := range opts {
opt(query)
}
atq.withUser = query
return atq
}
// 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 {
// UserID uuid.UUID `json:"user_id,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.ActionToken.Query().
// GroupBy(actiontoken.FieldUserID).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
func (atq *ActionTokenQuery) GroupBy(field string, fields ...string) *ActionTokenGroupBy {
atq.ctx.Fields = append([]string{field}, fields...)
grbuild := &ActionTokenGroupBy{build: atq}
grbuild.flds = &atq.ctx.Fields
grbuild.label = actiontoken.Label
grbuild.scan = 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 {
// UserID uuid.UUID `json:"user_id,omitempty"`
// }
//
// client.ActionToken.Query().
// Select(actiontoken.FieldUserID).
// Scan(ctx, &v)
func (atq *ActionTokenQuery) Select(fields ...string) *ActionTokenSelect {
atq.ctx.Fields = append(atq.ctx.Fields, fields...)
sbuild := &ActionTokenSelect{ActionTokenQuery: atq}
sbuild.label = actiontoken.Label
sbuild.flds, sbuild.scan = &atq.ctx.Fields, sbuild.Scan
return sbuild
}
// Aggregate returns a ActionTokenSelect configured with the given aggregations.
func (atq *ActionTokenQuery) Aggregate(fns ...AggregateFunc) *ActionTokenSelect {
return atq.Select().Aggregate(fns...)
}
func (atq *ActionTokenQuery) prepareQuery(ctx context.Context) error {
for _, inter := range atq.inters {
if inter == nil {
return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
}
if trv, ok := inter.(Traverser); ok {
if err := trv.Traverse(ctx, atq); err != nil {
return err
}
}
}
for _, f := range atq.ctx.Fields {
if !actiontoken.ValidColumn(f) {
return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
}
if atq.path != nil {
prev, err := atq.path(ctx)
if err != nil {
return err
}
atq.sql = prev
}
return nil
}
func (atq *ActionTokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ActionToken, error) {
var (
nodes = []*ActionToken{}
_spec = atq.querySpec()
loadedTypes = [1]bool{
atq.withUser != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
return (*ActionToken).scanValues(nil, columns)
}
_spec.Assign = func(columns []string, values []any) error {
node := &ActionToken{config: atq.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, atq.driver, _spec); err != nil {
return nil, err
}
if len(nodes) == 0 {
return nodes, nil
}
if query := atq.withUser; query != nil {
if err := atq.loadUser(ctx, query, nodes, nil,
func(n *ActionToken, e *User) { n.Edges.User = e }); err != nil {
return nil, err
}
}
return nodes, nil
}
func (atq *ActionTokenQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*ActionToken, init func(*ActionToken), assign func(*ActionToken, *User)) error {
ids := make([]uuid.UUID, 0, len(nodes))
nodeids := make(map[uuid.UUID][]*ActionToken)
for i := range nodes {
fk := nodes[i].UserID
if _, ok := nodeids[fk]; !ok {
ids = append(ids, fk)
}
nodeids[fk] = append(nodeids[fk], nodes[i])
}
if len(ids) == 0 {
return nil
}
query.Where(user.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 "user_id" returned %v`, n.ID)
}
for i := range nodes {
assign(nodes[i], n)
}
}
return nil
}
func (atq *ActionTokenQuery) sqlCount(ctx context.Context) (int, error) {
_spec := atq.querySpec()
_spec.Node.Columns = atq.ctx.Fields
if len(atq.ctx.Fields) > 0 {
_spec.Unique = atq.ctx.Unique != nil && *atq.ctx.Unique
}
return sqlgraph.CountNodes(ctx, atq.driver, _spec)
}
func (atq *ActionTokenQuery) querySpec() *sqlgraph.QuerySpec {
_spec := sqlgraph.NewQuerySpec(actiontoken.Table, actiontoken.Columns, sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID))
_spec.From = atq.sql
if unique := atq.ctx.Unique; unique != nil {
_spec.Unique = *unique
} else if atq.path != nil {
_spec.Unique = true
}
if fields := atq.ctx.Fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, actiontoken.FieldID)
for i := range fields {
if fields[i] != actiontoken.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, fields[i])
}
}
if atq.withUser != nil {
_spec.Node.AddColumnOnce(actiontoken.FieldUserID)
}
}
if ps := atq.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if limit := atq.ctx.Limit; limit != nil {
_spec.Limit = *limit
}
if offset := atq.ctx.Offset; offset != nil {
_spec.Offset = *offset
}
if ps := atq.order; len(ps) > 0 {
_spec.Order = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
return _spec
}
func (atq *ActionTokenQuery) sqlQuery(ctx context.Context) *sql.Selector {
builder := sql.Dialect(atq.driver.Dialect())
t1 := builder.Table(actiontoken.Table)
columns := atq.ctx.Fields
if len(columns) == 0 {
columns = actiontoken.Columns
}
selector := builder.Select(t1.Columns(columns...)...).From(t1)
if atq.sql != nil {
selector = atq.sql
selector.Select(selector.Columns(columns...)...)
}
if atq.ctx.Unique != nil && *atq.ctx.Unique {
selector.Distinct()
}
for _, p := range atq.predicates {
p(selector)
}
for _, p := range atq.order {
p(selector)
}
if offset := atq.ctx.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 := atq.ctx.Limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// ActionTokenGroupBy is the group-by builder for ActionToken entities.
type ActionTokenGroupBy struct {
selector
build *ActionTokenQuery
}
// Aggregate adds the given aggregation functions to the group-by query.
func (atgb *ActionTokenGroupBy) Aggregate(fns ...AggregateFunc) *ActionTokenGroupBy {
atgb.fns = append(atgb.fns, fns...)
return atgb
}
// Scan applies the selector query and scans the result into the given value.
func (atgb *ActionTokenGroupBy) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, atgb.build.ctx, "GroupBy")
if err := atgb.build.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ActionTokenQuery, *ActionTokenGroupBy](ctx, atgb.build, atgb, atgb.build.inters, v)
}
func (atgb *ActionTokenGroupBy) sqlScan(ctx context.Context, root *ActionTokenQuery, v any) error {
selector := root.sqlQuery(ctx).Select()
aggregation := make([]string, 0, len(atgb.fns))
for _, fn := range atgb.fns {
aggregation = append(aggregation, fn(selector))
}
if len(selector.SelectedColumns()) == 0 {
columns := make([]string, 0, len(*atgb.flds)+len(atgb.fns))
for _, f := range *atgb.flds {
columns = append(columns, selector.C(f))
}
columns = append(columns, aggregation...)
selector.Select(columns...)
}
selector.GroupBy(selector.Columns(*atgb.flds...)...)
if err := selector.Err(); err != nil {
return err
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := atgb.build.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
// ActionTokenSelect is the builder for selecting fields of ActionToken entities.
type ActionTokenSelect struct {
*ActionTokenQuery
selector
}
// Aggregate adds the given aggregation functions to the selector query.
func (ats *ActionTokenSelect) Aggregate(fns ...AggregateFunc) *ActionTokenSelect {
ats.fns = append(ats.fns, fns...)
return ats
}
// Scan applies the selector query and scans the result into the given value.
func (ats *ActionTokenSelect) Scan(ctx context.Context, v any) error {
ctx = setContextOp(ctx, ats.ctx, "Select")
if err := ats.prepareQuery(ctx); err != nil {
return err
}
return scanWithInterceptors[*ActionTokenQuery, *ActionTokenSelect](ctx, ats.ActionTokenQuery, ats, ats.inters, v)
}
func (ats *ActionTokenSelect) sqlScan(ctx context.Context, root *ActionTokenQuery, v any) error {
selector := root.sqlQuery(ctx)
aggregation := make([]string, 0, len(ats.fns))
for _, fn := range ats.fns {
aggregation = append(aggregation, fn(selector))
}
switch n := len(*ats.selector.flds); {
case n == 0 && len(aggregation) > 0:
selector.Select(aggregation...)
case n != 0 && len(aggregation) > 0:
selector.AppendSelect(aggregation...)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err := ats.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}

View file

@ -0,0 +1,406 @@
// Code generated by ent, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"time"
"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/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/predicate"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
// ActionTokenUpdate is the builder for updating ActionToken entities.
type ActionTokenUpdate struct {
config
hooks []Hook
mutation *ActionTokenMutation
}
// Where appends a list predicates to the ActionTokenUpdate builder.
func (atu *ActionTokenUpdate) Where(ps ...predicate.ActionToken) *ActionTokenUpdate {
atu.mutation.Where(ps...)
return atu
}
// SetUserID sets the "user_id" field.
func (atu *ActionTokenUpdate) SetUserID(u uuid.UUID) *ActionTokenUpdate {
atu.mutation.SetUserID(u)
return atu
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (atu *ActionTokenUpdate) SetNillableUserID(u *uuid.UUID) *ActionTokenUpdate {
if u != nil {
atu.SetUserID(*u)
}
return atu
}
// SetUpdatedAt sets the "updated_at" field.
func (atu *ActionTokenUpdate) SetUpdatedAt(t time.Time) *ActionTokenUpdate {
atu.mutation.SetUpdatedAt(t)
return atu
}
// SetAction sets the "action" field.
func (atu *ActionTokenUpdate) SetAction(a actiontoken.Action) *ActionTokenUpdate {
atu.mutation.SetAction(a)
return atu
}
// SetNillableAction sets the "action" field if the given value is not nil.
func (atu *ActionTokenUpdate) SetNillableAction(a *actiontoken.Action) *ActionTokenUpdate {
if a != nil {
atu.SetAction(*a)
}
return atu
}
// SetToken sets the "token" field.
func (atu *ActionTokenUpdate) SetToken(b []byte) *ActionTokenUpdate {
atu.mutation.SetToken(b)
return atu
}
// SetUser sets the "user" edge to the User entity.
func (atu *ActionTokenUpdate) SetUser(u *User) *ActionTokenUpdate {
return atu.SetUserID(u.ID)
}
// Mutation returns the ActionTokenMutation object of the builder.
func (atu *ActionTokenUpdate) Mutation() *ActionTokenMutation {
return atu.mutation
}
// ClearUser clears the "user" edge to the User entity.
func (atu *ActionTokenUpdate) ClearUser() *ActionTokenUpdate {
atu.mutation.ClearUser()
return atu
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (atu *ActionTokenUpdate) Save(ctx context.Context) (int, error) {
atu.defaults()
return withHooks(ctx, atu.sqlSave, atu.mutation, atu.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (atu *ActionTokenUpdate) SaveX(ctx context.Context) int {
affected, err := atu.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (atu *ActionTokenUpdate) Exec(ctx context.Context) error {
_, err := atu.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (atu *ActionTokenUpdate) ExecX(ctx context.Context) {
if err := atu.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (atu *ActionTokenUpdate) defaults() {
if _, ok := atu.mutation.UpdatedAt(); !ok {
v := actiontoken.UpdateDefaultUpdatedAt()
atu.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (atu *ActionTokenUpdate) check() error {
if v, ok := atu.mutation.Action(); ok {
if err := actiontoken.ActionValidator(v); err != nil {
return &ValidationError{Name: "action", err: fmt.Errorf(`ent: validator failed for field "ActionToken.action": %w`, err)}
}
}
if _, ok := atu.mutation.UserID(); atu.mutation.UserCleared() && !ok {
return errors.New(`ent: clearing a required unique edge "ActionToken.user"`)
}
return nil
}
func (atu *ActionTokenUpdate) sqlSave(ctx context.Context) (n int, err error) {
if err := atu.check(); err != nil {
return n, err
}
_spec := sqlgraph.NewUpdateSpec(actiontoken.Table, actiontoken.Columns, sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID))
if ps := atu.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := atu.mutation.UpdatedAt(); ok {
_spec.SetField(actiontoken.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := atu.mutation.Action(); ok {
_spec.SetField(actiontoken.FieldAction, field.TypeEnum, value)
}
if value, ok := atu.mutation.Token(); ok {
_spec.SetField(actiontoken.FieldToken, field.TypeBytes, value)
}
if atu.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: actiontoken.UserTable,
Columns: []string{actiontoken.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := atu.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: actiontoken.UserTable,
Columns: []string{actiontoken.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
},
}
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 _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{actiontoken.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return 0, err
}
atu.mutation.done = true
return n, nil
}
// ActionTokenUpdateOne is the builder for updating a single ActionToken entity.
type ActionTokenUpdateOne struct {
config
fields []string
hooks []Hook
mutation *ActionTokenMutation
}
// SetUserID sets the "user_id" field.
func (atuo *ActionTokenUpdateOne) SetUserID(u uuid.UUID) *ActionTokenUpdateOne {
atuo.mutation.SetUserID(u)
return atuo
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (atuo *ActionTokenUpdateOne) SetNillableUserID(u *uuid.UUID) *ActionTokenUpdateOne {
if u != nil {
atuo.SetUserID(*u)
}
return atuo
}
// SetUpdatedAt sets the "updated_at" field.
func (atuo *ActionTokenUpdateOne) SetUpdatedAt(t time.Time) *ActionTokenUpdateOne {
atuo.mutation.SetUpdatedAt(t)
return atuo
}
// SetAction sets the "action" field.
func (atuo *ActionTokenUpdateOne) SetAction(a actiontoken.Action) *ActionTokenUpdateOne {
atuo.mutation.SetAction(a)
return atuo
}
// SetNillableAction sets the "action" field if the given value is not nil.
func (atuo *ActionTokenUpdateOne) SetNillableAction(a *actiontoken.Action) *ActionTokenUpdateOne {
if a != nil {
atuo.SetAction(*a)
}
return atuo
}
// SetToken sets the "token" field.
func (atuo *ActionTokenUpdateOne) SetToken(b []byte) *ActionTokenUpdateOne {
atuo.mutation.SetToken(b)
return atuo
}
// SetUser sets the "user" edge to the User entity.
func (atuo *ActionTokenUpdateOne) SetUser(u *User) *ActionTokenUpdateOne {
return atuo.SetUserID(u.ID)
}
// Mutation returns the ActionTokenMutation object of the builder.
func (atuo *ActionTokenUpdateOne) Mutation() *ActionTokenMutation {
return atuo.mutation
}
// ClearUser clears the "user" edge to the User entity.
func (atuo *ActionTokenUpdateOne) ClearUser() *ActionTokenUpdateOne {
atuo.mutation.ClearUser()
return atuo
}
// Where appends a list predicates to the ActionTokenUpdate builder.
func (atuo *ActionTokenUpdateOne) Where(ps ...predicate.ActionToken) *ActionTokenUpdateOne {
atuo.mutation.Where(ps...)
return atuo
}
// Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema.
func (atuo *ActionTokenUpdateOne) Select(field string, fields ...string) *ActionTokenUpdateOne {
atuo.fields = append([]string{field}, fields...)
return atuo
}
// Save executes the query and returns the updated ActionToken entity.
func (atuo *ActionTokenUpdateOne) Save(ctx context.Context) (*ActionToken, error) {
atuo.defaults()
return withHooks(ctx, atuo.sqlSave, atuo.mutation, atuo.hooks)
}
// SaveX is like Save, but panics if an error occurs.
func (atuo *ActionTokenUpdateOne) SaveX(ctx context.Context) *ActionToken {
node, err := atuo.Save(ctx)
if err != nil {
panic(err)
}
return node
}
// Exec executes the query on the entity.
func (atuo *ActionTokenUpdateOne) Exec(ctx context.Context) error {
_, err := atuo.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (atuo *ActionTokenUpdateOne) ExecX(ctx context.Context) {
if err := atuo.Exec(ctx); err != nil {
panic(err)
}
}
// defaults sets the default values of the builder before save.
func (atuo *ActionTokenUpdateOne) defaults() {
if _, ok := atuo.mutation.UpdatedAt(); !ok {
v := actiontoken.UpdateDefaultUpdatedAt()
atuo.mutation.SetUpdatedAt(v)
}
}
// check runs all checks and user-defined validators on the builder.
func (atuo *ActionTokenUpdateOne) check() error {
if v, ok := atuo.mutation.Action(); ok {
if err := actiontoken.ActionValidator(v); err != nil {
return &ValidationError{Name: "action", err: fmt.Errorf(`ent: validator failed for field "ActionToken.action": %w`, err)}
}
}
if _, ok := atuo.mutation.UserID(); atuo.mutation.UserCleared() && !ok {
return errors.New(`ent: clearing a required unique edge "ActionToken.user"`)
}
return nil
}
func (atuo *ActionTokenUpdateOne) sqlSave(ctx context.Context) (_node *ActionToken, err error) {
if err := atuo.check(); err != nil {
return _node, err
}
_spec := sqlgraph.NewUpdateSpec(actiontoken.Table, actiontoken.Columns, sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID))
id, ok := atuo.mutation.ID()
if !ok {
return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ActionToken.id" for update`)}
}
_spec.Node.ID.Value = id
if fields := atuo.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, actiontoken.FieldID)
for _, f := range fields {
if !actiontoken.ValidColumn(f) {
return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
}
if f != actiontoken.FieldID {
_spec.Node.Columns = append(_spec.Node.Columns, f)
}
}
}
if ps := atuo.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) {
for i := range ps {
ps[i](selector)
}
}
}
if value, ok := atuo.mutation.UpdatedAt(); ok {
_spec.SetField(actiontoken.FieldUpdatedAt, field.TypeTime, value)
}
if value, ok := atuo.mutation.Action(); ok {
_spec.SetField(actiontoken.FieldAction, field.TypeEnum, value)
}
if value, ok := atuo.mutation.Token(); ok {
_spec.SetField(actiontoken.FieldToken, field.TypeBytes, value)
}
if atuo.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: actiontoken.UserTable,
Columns: []string{actiontoken.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := atuo.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O,
Inverse: true,
Table: actiontoken.UserTable,
Columns: []string{actiontoken.UserColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &ActionToken{config: atuo.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, atuo.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{actiontoken.Label}
} else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{msg: err.Error(), wrap: err}
}
return nil, err
}
atuo.mutation.done = true
return _node, nil
}

View file

@ -16,6 +16,7 @@ import (
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"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"
@ -36,6 +37,8 @@ type Client struct {
config
// Schema is the client for creating, migrating and dropping schema.
Schema *migrate.Schema
// ActionToken is the client for interacting with the ActionToken builders.
ActionToken *ActionTokenClient
// Attachment is the client for interacting with the Attachment builders.
Attachment *AttachmentClient
// AuthRoles is the client for interacting with the AuthRoles builders.
@ -73,6 +76,7 @@ func NewClient(opts ...Option) *Client {
func (c *Client) init() {
c.Schema = migrate.NewSchema(c.driver)
c.ActionToken = NewActionTokenClient(c.config)
c.Attachment = NewAttachmentClient(c.config)
c.AuthRoles = NewAuthRolesClient(c.config)
c.AuthTokens = NewAuthTokensClient(c.config)
@ -178,6 +182,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
return &Tx{
ctx: ctx,
config: cfg,
ActionToken: NewActionTokenClient(cfg),
Attachment: NewAttachmentClient(cfg),
AuthRoles: NewAuthRolesClient(cfg),
AuthTokens: NewAuthTokensClient(cfg),
@ -210,6 +215,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
return &Tx{
ctx: ctx,
config: cfg,
ActionToken: NewActionTokenClient(cfg),
Attachment: NewAttachmentClient(cfg),
AuthRoles: NewAuthRolesClient(cfg),
AuthTokens: NewAuthTokensClient(cfg),
@ -229,7 +235,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
// Debug returns a new debug-client. It's used to get verbose logging on specific operations.
//
// client.Debug().
// Attachment.
// ActionToken.
// Query().
// Count(ctx)
func (c *Client) Debug() *Client {
@ -252,7 +258,7 @@ func (c *Client) Close() error {
// In order to add hooks to a specific client, call: `client.Node.Use(...)`.
func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{
c.Attachment, c.AuthRoles, c.AuthTokens, c.Document, c.Group,
c.ActionToken, c.Attachment, c.AuthRoles, c.AuthTokens, c.Document, c.Group,
c.GroupInvitationToken, c.Item, c.ItemField, c.Label, c.Location,
c.MaintenanceEntry, c.Notifier, c.User,
} {
@ -264,7 +270,7 @@ func (c *Client) Use(hooks ...Hook) {
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{
c.Attachment, c.AuthRoles, c.AuthTokens, c.Document, c.Group,
c.ActionToken, c.Attachment, c.AuthRoles, c.AuthTokens, c.Document, c.Group,
c.GroupInvitationToken, c.Item, c.ItemField, c.Label, c.Location,
c.MaintenanceEntry, c.Notifier, c.User,
} {
@ -275,6 +281,8 @@ func (c *Client) Intercept(interceptors ...Interceptor) {
// Mutate implements the ent.Mutator interface.
func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
switch m := m.(type) {
case *ActionTokenMutation:
return c.ActionToken.mutate(ctx, m)
case *AttachmentMutation:
return c.Attachment.mutate(ctx, m)
case *AuthRolesMutation:
@ -306,6 +314,155 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
}
}
// ActionTokenClient is a client for the ActionToken schema.
type ActionTokenClient struct {
config
}
// NewActionTokenClient returns a client for the ActionToken from the given config.
func NewActionTokenClient(c config) *ActionTokenClient {
return &ActionTokenClient{config: c}
}
// Use adds a list of mutation hooks to the hooks stack.
// A call to `Use(f, g, h)` equals to `actiontoken.Hooks(f(g(h())))`.
func (c *ActionTokenClient) Use(hooks ...Hook) {
c.hooks.ActionToken = append(c.hooks.ActionToken, hooks...)
}
// Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `actiontoken.Intercept(f(g(h())))`.
func (c *ActionTokenClient) Intercept(interceptors ...Interceptor) {
c.inters.ActionToken = append(c.inters.ActionToken, interceptors...)
}
// Create returns a builder for creating a ActionToken entity.
func (c *ActionTokenClient) Create() *ActionTokenCreate {
mutation := newActionTokenMutation(c.config, OpCreate)
return &ActionTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// CreateBulk returns a builder for creating a bulk of ActionToken entities.
func (c *ActionTokenClient) CreateBulk(builders ...*ActionTokenCreate) *ActionTokenCreateBulk {
return &ActionTokenCreateBulk{config: c.config, builders: builders}
}
// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
// a builder and applies setFunc on it.
func (c *ActionTokenClient) MapCreateBulk(slice any, setFunc func(*ActionTokenCreate, int)) *ActionTokenCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &ActionTokenCreateBulk{err: fmt.Errorf("calling to ActionTokenClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*ActionTokenCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &ActionTokenCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for ActionToken.
func (c *ActionTokenClient) Update() *ActionTokenUpdate {
mutation := newActionTokenMutation(c.config, OpUpdate)
return &ActionTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOne returns an update builder for the given entity.
func (c *ActionTokenClient) UpdateOne(at *ActionToken) *ActionTokenUpdateOne {
mutation := newActionTokenMutation(c.config, OpUpdateOne, withActionToken(at))
return &ActionTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// UpdateOneID returns an update builder for the given id.
func (c *ActionTokenClient) UpdateOneID(id uuid.UUID) *ActionTokenUpdateOne {
mutation := newActionTokenMutation(c.config, OpUpdateOne, withActionTokenID(id))
return &ActionTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// Delete returns a delete builder for ActionToken.
func (c *ActionTokenClient) Delete() *ActionTokenDelete {
mutation := newActionTokenMutation(c.config, OpDelete)
return &ActionTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
}
// DeleteOne returns a builder for deleting the given entity.
func (c *ActionTokenClient) DeleteOne(at *ActionToken) *ActionTokenDeleteOne {
return c.DeleteOneID(at.ID)
}
// DeleteOneID returns a builder for deleting the given entity by its id.
func (c *ActionTokenClient) DeleteOneID(id uuid.UUID) *ActionTokenDeleteOne {
builder := c.Delete().Where(actiontoken.ID(id))
builder.mutation.id = &id
builder.mutation.op = OpDeleteOne
return &ActionTokenDeleteOne{builder}
}
// Query returns a query builder for ActionToken.
func (c *ActionTokenClient) Query() *ActionTokenQuery {
return &ActionTokenQuery{
config: c.config,
ctx: &QueryContext{Type: TypeActionToken},
inters: c.Interceptors(),
}
}
// Get returns a ActionToken entity by its id.
func (c *ActionTokenClient) Get(ctx context.Context, id uuid.UUID) (*ActionToken, error) {
return c.Query().Where(actiontoken.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (c *ActionTokenClient) GetX(ctx context.Context, id uuid.UUID) *ActionToken {
obj, err := c.Get(ctx, id)
if err != nil {
panic(err)
}
return obj
}
// QueryUser queries the user edge of a ActionToken.
func (c *ActionTokenClient) QueryUser(at *ActionToken) *UserQuery {
query := (&UserClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := at.ID
step := sqlgraph.NewStep(
sqlgraph.From(actiontoken.Table, actiontoken.FieldID, id),
sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, actiontoken.UserTable, actiontoken.UserColumn),
)
fromV = sqlgraph.Neighbors(at.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *ActionTokenClient) Hooks() []Hook {
return c.hooks.ActionToken
}
// Interceptors returns the client interceptors.
func (c *ActionTokenClient) Interceptors() []Interceptor {
return c.inters.ActionToken
}
func (c *ActionTokenClient) mutate(ctx context.Context, m *ActionTokenMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&ActionTokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&ActionTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&ActionTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&ActionTokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown ActionToken mutation op: %q", m.Op())
}
}
// AttachmentClient is a client for the Attachment schema.
type AttachmentClient struct {
config
@ -2586,6 +2743,22 @@ func (c *UserClient) QueryNotifiers(u *User) *NotifierQuery {
return query
}
// QueryActionTokens queries the action_tokens edge of a User.
func (c *UserClient) QueryActionTokens(u *User) *ActionTokenQuery {
query := (&ActionTokenClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := u.ID
step := sqlgraph.NewStep(
sqlgraph.From(user.Table, user.FieldID, id),
sqlgraph.To(actiontoken.Table, actiontoken.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, user.ActionTokensTable, user.ActionTokensColumn),
)
fromV = sqlgraph.Neighbors(u.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *UserClient) Hooks() []Hook {
return c.hooks.User
@ -2614,11 +2787,13 @@ func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error)
// hooks and interceptors per client, for fast access.
type (
hooks struct {
Attachment, AuthRoles, AuthTokens, Document, Group, GroupInvitationToken, Item,
ItemField, Label, Location, MaintenanceEntry, Notifier, User []ent.Hook
ActionToken, Attachment, AuthRoles, AuthTokens, Document, Group,
GroupInvitationToken, Item, ItemField, Label, Location, MaintenanceEntry,
Notifier, User []ent.Hook
}
inters struct {
Attachment, AuthRoles, AuthTokens, Document, Group, GroupInvitationToken, Item,
ItemField, Label, Location, MaintenanceEntry, Notifier, User []ent.Interceptor
ActionToken, Attachment, AuthRoles, AuthTokens, Document, Group,
GroupInvitationToken, Item, ItemField, Label, Location, MaintenanceEntry,
Notifier, User []ent.Interceptor
}
)

View file

@ -12,6 +12,7 @@ import (
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"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"
@ -85,6 +86,7 @@ var (
func checkColumn(table, column string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
actiontoken.Table: actiontoken.ValidColumn,
attachment.Table: attachment.ValidColumn,
authroles.Table: authroles.ValidColumn,
authtokens.Table: authtokens.ValidColumn,

View file

@ -4,6 +4,10 @@ package ent
import "github.com/google/uuid"
func (at *ActionToken) GetID() uuid.UUID {
return at.ID
}
func (a *Attachment) GetID() uuid.UUID {
return a.ID
}

View file

@ -9,6 +9,18 @@ import (
"github.com/hay-kot/homebox/backend/internal/data/ent"
)
// The ActionTokenFunc type is an adapter to allow the use of ordinary
// function as ActionToken mutator.
type ActionTokenFunc func(context.Context, *ent.ActionTokenMutation) (ent.Value, error)
// Mutate calls f(ctx, m).
func (f ActionTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if mv, ok := m.(*ent.ActionTokenMutation); ok {
return f(ctx, mv)
}
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ActionTokenMutation", m)
}
// The AttachmentFunc type is an adapter to allow the use of ordinary
// function as Attachment mutator.
type AttachmentFunc func(context.Context, *ent.AttachmentMutation) (ent.Value, error)

View file

@ -8,6 +8,46 @@ import (
)
var (
// ActionTokensColumns holds the columns for the "action_tokens" table.
ActionTokensColumns = []*schema.Column{
{Name: "id", Type: field.TypeUUID},
{Name: "created_at", Type: field.TypeTime},
{Name: "updated_at", Type: field.TypeTime},
{Name: "action", Type: field.TypeEnum, Enums: []string{"reset_password"}, Default: "reset_password"},
{Name: "token", Type: field.TypeBytes, Unique: true},
{Name: "user_id", Type: field.TypeUUID},
}
// ActionTokensTable holds the schema information for the "action_tokens" table.
ActionTokensTable = &schema.Table{
Name: "action_tokens",
Columns: ActionTokensColumns,
PrimaryKey: []*schema.Column{ActionTokensColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "action_tokens_users_action_tokens",
Columns: []*schema.Column{ActionTokensColumns[5]},
RefColumns: []*schema.Column{UsersColumns[0]},
OnDelete: schema.Cascade,
},
},
Indexes: []*schema.Index{
{
Name: "actiontoken_token",
Unique: false,
Columns: []*schema.Column{ActionTokensColumns[4]},
},
{
Name: "actiontoken_action",
Unique: false,
Columns: []*schema.Column{ActionTokensColumns[3]},
},
{
Name: "actiontoken_user_id",
Unique: false,
Columns: []*schema.Column{ActionTokensColumns[5]},
},
},
}
// AttachmentsColumns holds the columns for the "attachments" table.
AttachmentsColumns = []*schema.Column{
{Name: "id", Type: field.TypeUUID},
@ -453,6 +493,7 @@ var (
}
// Tables holds all the tables in the schema.
Tables = []*schema.Table{
ActionTokensTable,
AttachmentsTable,
AuthRolesTable,
AuthTokensTable,
@ -471,6 +512,7 @@ var (
)
func init() {
ActionTokensTable.ForeignKeys[0].RefTable = UsersTable
AttachmentsTable.ForeignKeys[0].RefTable = DocumentsTable
AttachmentsTable.ForeignKeys[1].RefTable = ItemsTable
AuthRolesTable.ForeignKeys[0].RefTable = AuthTokensTable

View file

@ -12,6 +12,7 @@ import (
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"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"
@ -37,6 +38,7 @@ const (
OpUpdateOne = ent.OpUpdateOne
// Node types.
TypeActionToken = "ActionToken"
TypeAttachment = "Attachment"
TypeAuthRoles = "AuthRoles"
TypeAuthTokens = "AuthTokens"
@ -52,6 +54,608 @@ const (
TypeUser = "User"
)
// ActionTokenMutation represents an operation that mutates the ActionToken nodes in the graph.
type ActionTokenMutation struct {
config
op Op
typ string
id *uuid.UUID
created_at *time.Time
updated_at *time.Time
action *actiontoken.Action
token *[]byte
clearedFields map[string]struct{}
user *uuid.UUID
cleareduser bool
done bool
oldValue func(context.Context) (*ActionToken, error)
predicates []predicate.ActionToken
}
var _ ent.Mutation = (*ActionTokenMutation)(nil)
// actiontokenOption allows management of the mutation configuration using functional options.
type actiontokenOption func(*ActionTokenMutation)
// newActionTokenMutation creates new mutation for the ActionToken entity.
func newActionTokenMutation(c config, op Op, opts ...actiontokenOption) *ActionTokenMutation {
m := &ActionTokenMutation{
config: c,
op: op,
typ: TypeActionToken,
clearedFields: make(map[string]struct{}),
}
for _, opt := range opts {
opt(m)
}
return m
}
// withActionTokenID sets the ID field of the mutation.
func withActionTokenID(id uuid.UUID) actiontokenOption {
return func(m *ActionTokenMutation) {
var (
err error
once sync.Once
value *ActionToken
)
m.oldValue = func(ctx context.Context) (*ActionToken, error) {
once.Do(func() {
if m.done {
err = errors.New("querying old values post mutation is not allowed")
} else {
value, err = m.Client().ActionToken.Get(ctx, id)
}
})
return value, err
}
m.id = &id
}
}
// withActionToken sets the old ActionToken of the mutation.
func withActionToken(node *ActionToken) actiontokenOption {
return func(m *ActionTokenMutation) {
m.oldValue = func(context.Context) (*ActionToken, 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 ActionTokenMutation) 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 ActionTokenMutation) 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
}
// SetID sets the value of the id field. Note that this
// operation is only accepted on creation of ActionToken entities.
func (m *ActionTokenMutation) SetID(id uuid.UUID) {
m.id = &id
}
// 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 *ActionTokenMutation) ID() (id uuid.UUID, 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 *ActionTokenMutation) IDs(ctx context.Context) ([]uuid.UUID, error) {
switch {
case m.op.Is(OpUpdateOne | OpDeleteOne):
id, exists := m.ID()
if exists {
return []uuid.UUID{id}, nil
}
fallthrough
case m.op.Is(OpUpdate | OpDelete):
return m.Client().ActionToken.Query().Where(m.predicates...).IDs(ctx)
default:
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
}
}
// SetUserID sets the "user_id" field.
func (m *ActionTokenMutation) SetUserID(u uuid.UUID) {
m.user = &u
}
// UserID returns the value of the "user_id" field in the mutation.
func (m *ActionTokenMutation) UserID() (r uuid.UUID, exists bool) {
v := m.user
if v == nil {
return
}
return *v, true
}
// OldUserID returns the old "user_id" field's value of the ActionToken entity.
// If the ActionToken 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 *ActionTokenMutation) OldUserID(ctx context.Context) (v uuid.UUID, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUserID is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUserID requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUserID: %w", err)
}
return oldValue.UserID, nil
}
// ResetUserID resets all changes to the "user_id" field.
func (m *ActionTokenMutation) ResetUserID() {
m.user = nil
}
// SetCreatedAt sets the "created_at" field.
func (m *ActionTokenMutation) SetCreatedAt(t time.Time) {
m.created_at = &t
}
// CreatedAt returns the value of the "created_at" field in the mutation.
func (m *ActionTokenMutation) CreatedAt() (r time.Time, exists bool) {
v := m.created_at
if v == nil {
return
}
return *v, true
}
// OldCreatedAt returns the old "created_at" field's value of the ActionToken entity.
// If the ActionToken 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 *ActionTokenMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCreatedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
}
return oldValue.CreatedAt, nil
}
// ResetCreatedAt resets all changes to the "created_at" field.
func (m *ActionTokenMutation) ResetCreatedAt() {
m.created_at = nil
}
// SetUpdatedAt sets the "updated_at" field.
func (m *ActionTokenMutation) SetUpdatedAt(t time.Time) {
m.updated_at = &t
}
// UpdatedAt returns the value of the "updated_at" field in the mutation.
func (m *ActionTokenMutation) UpdatedAt() (r time.Time, exists bool) {
v := m.updated_at
if v == nil {
return
}
return *v, true
}
// OldUpdatedAt returns the old "updated_at" field's value of the ActionToken entity.
// If the ActionToken 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 *ActionTokenMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUpdatedAt requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err)
}
return oldValue.UpdatedAt, nil
}
// ResetUpdatedAt resets all changes to the "updated_at" field.
func (m *ActionTokenMutation) ResetUpdatedAt() {
m.updated_at = nil
}
// SetAction sets the "action" field.
func (m *ActionTokenMutation) SetAction(a actiontoken.Action) {
m.action = &a
}
// Action returns the value of the "action" field in the mutation.
func (m *ActionTokenMutation) Action() (r actiontoken.Action, exists bool) {
v := m.action
if v == nil {
return
}
return *v, true
}
// OldAction returns the old "action" field's value of the ActionToken entity.
// If the ActionToken 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 *ActionTokenMutation) OldAction(ctx context.Context) (v actiontoken.Action, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldAction is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldAction requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldAction: %w", err)
}
return oldValue.Action, nil
}
// ResetAction resets all changes to the "action" field.
func (m *ActionTokenMutation) ResetAction() {
m.action = nil
}
// SetToken sets the "token" field.
func (m *ActionTokenMutation) SetToken(b []byte) {
m.token = &b
}
// Token returns the value of the "token" field in the mutation.
func (m *ActionTokenMutation) Token() (r []byte, exists bool) {
v := m.token
if v == nil {
return
}
return *v, true
}
// OldToken returns the old "token" field's value of the ActionToken entity.
// If the ActionToken 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 *ActionTokenMutation) OldToken(ctx context.Context) (v []byte, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldToken is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldToken requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldToken: %w", err)
}
return oldValue.Token, nil
}
// ResetToken resets all changes to the "token" field.
func (m *ActionTokenMutation) ResetToken() {
m.token = nil
}
// ClearUser clears the "user" edge to the User entity.
func (m *ActionTokenMutation) ClearUser() {
m.cleareduser = true
m.clearedFields[actiontoken.FieldUserID] = struct{}{}
}
// UserCleared reports if the "user" edge to the User entity was cleared.
func (m *ActionTokenMutation) UserCleared() bool {
return m.cleareduser
}
// UserIDs returns the "user" edge IDs in the mutation.
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
// UserID instead. It exists only for internal usage by the builders.
func (m *ActionTokenMutation) UserIDs() (ids []uuid.UUID) {
if id := m.user; id != nil {
ids = append(ids, *id)
}
return
}
// ResetUser resets all changes to the "user" edge.
func (m *ActionTokenMutation) ResetUser() {
m.user = nil
m.cleareduser = false
}
// Where appends a list predicates to the ActionTokenMutation builder.
func (m *ActionTokenMutation) Where(ps ...predicate.ActionToken) {
m.predicates = append(m.predicates, ps...)
}
// WhereP appends storage-level predicates to the ActionTokenMutation builder. Using this method,
// users can use type-assertion to append predicates that do not depend on any generated package.
func (m *ActionTokenMutation) WhereP(ps ...func(*sql.Selector)) {
p := make([]predicate.ActionToken, len(ps))
for i := range ps {
p[i] = ps[i]
}
m.Where(p...)
}
// Op returns the operation name.
func (m *ActionTokenMutation) Op() Op {
return m.op
}
// SetOp allows setting the mutation operation.
func (m *ActionTokenMutation) SetOp(op Op) {
m.op = op
}
// Type returns the node type of this mutation (ActionToken).
func (m *ActionTokenMutation) 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 *ActionTokenMutation) Fields() []string {
fields := make([]string, 0, 5)
if m.user != nil {
fields = append(fields, actiontoken.FieldUserID)
}
if m.created_at != nil {
fields = append(fields, actiontoken.FieldCreatedAt)
}
if m.updated_at != nil {
fields = append(fields, actiontoken.FieldUpdatedAt)
}
if m.action != nil {
fields = append(fields, actiontoken.FieldAction)
}
if m.token != nil {
fields = append(fields, actiontoken.FieldToken)
}
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 *ActionTokenMutation) Field(name string) (ent.Value, bool) {
switch name {
case actiontoken.FieldUserID:
return m.UserID()
case actiontoken.FieldCreatedAt:
return m.CreatedAt()
case actiontoken.FieldUpdatedAt:
return m.UpdatedAt()
case actiontoken.FieldAction:
return m.Action()
case actiontoken.FieldToken:
return m.Token()
}
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 *ActionTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name {
case actiontoken.FieldUserID:
return m.OldUserID(ctx)
case actiontoken.FieldCreatedAt:
return m.OldCreatedAt(ctx)
case actiontoken.FieldUpdatedAt:
return m.OldUpdatedAt(ctx)
case actiontoken.FieldAction:
return m.OldAction(ctx)
case actiontoken.FieldToken:
return m.OldToken(ctx)
}
return nil, fmt.Errorf("unknown ActionToken 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 *ActionTokenMutation) SetField(name string, value ent.Value) error {
switch name {
case actiontoken.FieldUserID:
v, ok := value.(uuid.UUID)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUserID(v)
return nil
case actiontoken.FieldCreatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCreatedAt(v)
return nil
case actiontoken.FieldUpdatedAt:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUpdatedAt(v)
return nil
case actiontoken.FieldAction:
v, ok := value.(actiontoken.Action)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetAction(v)
return nil
case actiontoken.FieldToken:
v, ok := value.([]byte)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetToken(v)
return nil
}
return fmt.Errorf("unknown ActionToken field %s", name)
}
// AddedFields returns all numeric fields that were incremented/decremented during
// this mutation.
func (m *ActionTokenMutation) 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 *ActionTokenMutation) 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 *ActionTokenMutation) AddField(name string, value ent.Value) error {
switch name {
}
return fmt.Errorf("unknown ActionToken numeric field %s", name)
}
// ClearedFields returns all nullable fields that were cleared during this
// mutation.
func (m *ActionTokenMutation) ClearedFields() []string {
return nil
}
// FieldCleared returns a boolean indicating if a field with the given name was
// cleared in this mutation.
func (m *ActionTokenMutation) 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 *ActionTokenMutation) ClearField(name string) error {
return fmt.Errorf("unknown ActionToken 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 *ActionTokenMutation) ResetField(name string) error {
switch name {
case actiontoken.FieldUserID:
m.ResetUserID()
return nil
case actiontoken.FieldCreatedAt:
m.ResetCreatedAt()
return nil
case actiontoken.FieldUpdatedAt:
m.ResetUpdatedAt()
return nil
case actiontoken.FieldAction:
m.ResetAction()
return nil
case actiontoken.FieldToken:
m.ResetToken()
return nil
}
return fmt.Errorf("unknown ActionToken field %s", name)
}
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *ActionTokenMutation) AddedEdges() []string {
edges := make([]string, 0, 1)
if m.user != nil {
edges = append(edges, actiontoken.EdgeUser)
}
return edges
}
// AddedIDs returns all IDs (to other nodes) that were added for the given edge
// name in this mutation.
func (m *ActionTokenMutation) AddedIDs(name string) []ent.Value {
switch name {
case actiontoken.EdgeUser:
if id := m.user; id != nil {
return []ent.Value{*id}
}
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *ActionTokenMutation) 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 *ActionTokenMutation) RemovedIDs(name string) []ent.Value {
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *ActionTokenMutation) ClearedEdges() []string {
edges := make([]string, 0, 1)
if m.cleareduser {
edges = append(edges, actiontoken.EdgeUser)
}
return edges
}
// EdgeCleared returns a boolean which indicates if the edge with the given name
// was cleared in this mutation.
func (m *ActionTokenMutation) EdgeCleared(name string) bool {
switch name {
case actiontoken.EdgeUser:
return m.cleareduser
}
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 *ActionTokenMutation) ClearEdge(name string) error {
switch name {
case actiontoken.EdgeUser:
m.ClearUser()
return nil
}
return fmt.Errorf("unknown ActionToken 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 *ActionTokenMutation) ResetEdge(name string) error {
switch name {
case actiontoken.EdgeUser:
m.ResetUser()
return nil
}
return fmt.Errorf("unknown ActionToken edge %s", name)
}
// AttachmentMutation represents an operation that mutates the Attachment nodes in the graph.
type AttachmentMutation struct {
config
@ -10672,30 +11276,33 @@ func (m *NotifierMutation) ResetEdge(name string) error {
// UserMutation represents an operation that mutates the User nodes in the graph.
type UserMutation struct {
config
op Op
typ string
id *uuid.UUID
created_at *time.Time
updated_at *time.Time
name *string
email *string
password *string
is_superuser *bool
superuser *bool
role *user.Role
activated_on *time.Time
clearedFields map[string]struct{}
group *uuid.UUID
clearedgroup bool
auth_tokens map[uuid.UUID]struct{}
removedauth_tokens map[uuid.UUID]struct{}
clearedauth_tokens bool
notifiers map[uuid.UUID]struct{}
removednotifiers map[uuid.UUID]struct{}
clearednotifiers bool
done bool
oldValue func(context.Context) (*User, error)
predicates []predicate.User
op Op
typ string
id *uuid.UUID
created_at *time.Time
updated_at *time.Time
name *string
email *string
password *string
is_superuser *bool
superuser *bool
role *user.Role
activated_on *time.Time
clearedFields map[string]struct{}
group *uuid.UUID
clearedgroup bool
auth_tokens map[uuid.UUID]struct{}
removedauth_tokens map[uuid.UUID]struct{}
clearedauth_tokens bool
notifiers map[uuid.UUID]struct{}
removednotifiers map[uuid.UUID]struct{}
clearednotifiers bool
action_tokens map[uuid.UUID]struct{}
removedaction_tokens map[uuid.UUID]struct{}
clearedaction_tokens bool
done bool
oldValue func(context.Context) (*User, error)
predicates []predicate.User
}
var _ ent.Mutation = (*UserMutation)(nil)
@ -11286,6 +11893,60 @@ func (m *UserMutation) ResetNotifiers() {
m.removednotifiers = nil
}
// AddActionTokenIDs adds the "action_tokens" edge to the ActionToken entity by ids.
func (m *UserMutation) AddActionTokenIDs(ids ...uuid.UUID) {
if m.action_tokens == nil {
m.action_tokens = make(map[uuid.UUID]struct{})
}
for i := range ids {
m.action_tokens[ids[i]] = struct{}{}
}
}
// ClearActionTokens clears the "action_tokens" edge to the ActionToken entity.
func (m *UserMutation) ClearActionTokens() {
m.clearedaction_tokens = true
}
// ActionTokensCleared reports if the "action_tokens" edge to the ActionToken entity was cleared.
func (m *UserMutation) ActionTokensCleared() bool {
return m.clearedaction_tokens
}
// RemoveActionTokenIDs removes the "action_tokens" edge to the ActionToken entity by IDs.
func (m *UserMutation) RemoveActionTokenIDs(ids ...uuid.UUID) {
if m.removedaction_tokens == nil {
m.removedaction_tokens = make(map[uuid.UUID]struct{})
}
for i := range ids {
delete(m.action_tokens, ids[i])
m.removedaction_tokens[ids[i]] = struct{}{}
}
}
// RemovedActionTokens returns the removed IDs of the "action_tokens" edge to the ActionToken entity.
func (m *UserMutation) RemovedActionTokensIDs() (ids []uuid.UUID) {
for id := range m.removedaction_tokens {
ids = append(ids, id)
}
return
}
// ActionTokensIDs returns the "action_tokens" edge IDs in the mutation.
func (m *UserMutation) ActionTokensIDs() (ids []uuid.UUID) {
for id := range m.action_tokens {
ids = append(ids, id)
}
return
}
// ResetActionTokens resets all changes to the "action_tokens" edge.
func (m *UserMutation) ResetActionTokens() {
m.action_tokens = nil
m.clearedaction_tokens = false
m.removedaction_tokens = nil
}
// Where appends a list predicates to the UserMutation builder.
func (m *UserMutation) Where(ps ...predicate.User) {
m.predicates = append(m.predicates, ps...)
@ -11564,7 +12225,7 @@ func (m *UserMutation) ResetField(name string) error {
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *UserMutation) AddedEdges() []string {
edges := make([]string, 0, 3)
edges := make([]string, 0, 4)
if m.group != nil {
edges = append(edges, user.EdgeGroup)
}
@ -11574,6 +12235,9 @@ func (m *UserMutation) AddedEdges() []string {
if m.notifiers != nil {
edges = append(edges, user.EdgeNotifiers)
}
if m.action_tokens != nil {
edges = append(edges, user.EdgeActionTokens)
}
return edges
}
@ -11597,19 +12261,28 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
case user.EdgeActionTokens:
ids := make([]ent.Value, 0, len(m.action_tokens))
for id := range m.action_tokens {
ids = append(ids, id)
}
return ids
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *UserMutation) RemovedEdges() []string {
edges := make([]string, 0, 3)
edges := make([]string, 0, 4)
if m.removedauth_tokens != nil {
edges = append(edges, user.EdgeAuthTokens)
}
if m.removednotifiers != nil {
edges = append(edges, user.EdgeNotifiers)
}
if m.removedaction_tokens != nil {
edges = append(edges, user.EdgeActionTokens)
}
return edges
}
@ -11629,13 +12302,19 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
case user.EdgeActionTokens:
ids := make([]ent.Value, 0, len(m.removedaction_tokens))
for id := range m.removedaction_tokens {
ids = append(ids, id)
}
return ids
}
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *UserMutation) ClearedEdges() []string {
edges := make([]string, 0, 3)
edges := make([]string, 0, 4)
if m.clearedgroup {
edges = append(edges, user.EdgeGroup)
}
@ -11645,6 +12324,9 @@ func (m *UserMutation) ClearedEdges() []string {
if m.clearednotifiers {
edges = append(edges, user.EdgeNotifiers)
}
if m.clearedaction_tokens {
edges = append(edges, user.EdgeActionTokens)
}
return edges
}
@ -11658,6 +12340,8 @@ func (m *UserMutation) EdgeCleared(name string) bool {
return m.clearedauth_tokens
case user.EdgeNotifiers:
return m.clearednotifiers
case user.EdgeActionTokens:
return m.clearedaction_tokens
}
return false
}
@ -11686,6 +12370,9 @@ func (m *UserMutation) ResetEdge(name string) error {
case user.EdgeNotifiers:
m.ResetNotifiers()
return nil
case user.EdgeActionTokens:
m.ResetActionTokens()
return nil
}
return fmt.Errorf("unknown User edge %s", name)
}

View file

@ -6,6 +6,9 @@ import (
"entgo.io/ent/dialect/sql"
)
// ActionToken is the predicate function for actiontoken builders.
type ActionToken func(*sql.Selector)
// Attachment is the predicate function for attachment builders.
type Attachment func(*sql.Selector)

View file

@ -6,6 +6,7 @@ import (
"time"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/document"
@ -25,6 +26,25 @@ import (
// (default values, validators, hooks and policies) and stitches it
// to their package variables.
func init() {
actiontokenMixin := schema.ActionToken{}.Mixin()
actiontokenMixinFields1 := actiontokenMixin[1].Fields()
_ = actiontokenMixinFields1
actiontokenFields := schema.ActionToken{}.Fields()
_ = actiontokenFields
// actiontokenDescCreatedAt is the schema descriptor for created_at field.
actiontokenDescCreatedAt := actiontokenMixinFields1[1].Descriptor()
// actiontoken.DefaultCreatedAt holds the default value on creation for the created_at field.
actiontoken.DefaultCreatedAt = actiontokenDescCreatedAt.Default.(func() time.Time)
// actiontokenDescUpdatedAt is the schema descriptor for updated_at field.
actiontokenDescUpdatedAt := actiontokenMixinFields1[2].Descriptor()
// actiontoken.DefaultUpdatedAt holds the default value on creation for the updated_at field.
actiontoken.DefaultUpdatedAt = actiontokenDescUpdatedAt.Default.(func() time.Time)
// actiontoken.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
actiontoken.UpdateDefaultUpdatedAt = actiontokenDescUpdatedAt.UpdateDefault.(func() time.Time)
// actiontokenDescID is the schema descriptor for id field.
actiontokenDescID := actiontokenMixinFields1[0].Descriptor()
// actiontoken.DefaultID holds the default value on creation for the id field.
actiontoken.DefaultID = actiontokenDescID.Default.(func() uuid.UUID)
attachmentMixin := schema.Attachment{}.Mixin()
attachmentMixinFields0 := attachmentMixin[0].Fields()
_ = attachmentMixinFields0

View file

@ -0,0 +1,42 @@
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
"github.com/hay-kot/homebox/backend/internal/data/ent/schema/mixins"
)
type ActionToken struct {
ent.Schema
}
func (ActionToken) Mixin() []ent.Mixin {
return []ent.Mixin{
UserMixin{
ref: "action_tokens",
field: "user_id",
},
mixins.BaseMixin{},
}
}
// Fields of the ActionToken.
func (ActionToken) Fields() []ent.Field {
return []ent.Field{
field.Enum("action").
Values("reset_password").
Default("reset_password"),
field.Bytes("token").
Unique(),
}
}
func (ActionToken) Indexes() []ent.Index {
return []ent.Index{
index.Fields("token"),
index.Fields("action"),
index.Fields("user_id"),
}
}

View file

@ -52,13 +52,11 @@ func (User) Fields() []ent.Field {
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("auth_tokens", AuthTokens.Type).
Annotations(entsql.Annotation{
OnDelete: entsql.Cascade,
}),
Annotations(entsql.Annotation{OnDelete: entsql.Cascade}),
edge.To("notifiers", Notifier.Type).
Annotations(entsql.Annotation{
OnDelete: entsql.Cascade,
}),
Annotations(entsql.Annotation{OnDelete: entsql.Cascade}),
edge.To("action_tokens", ActionToken.Type).
Annotations(entsql.Annotation{OnDelete: entsql.Cascade}),
}
}

View file

@ -12,6 +12,8 @@ import (
// Tx is a transactional client that is created by calling Client.Tx().
type Tx struct {
config
// ActionToken is the client for interacting with the ActionToken builders.
ActionToken *ActionTokenClient
// Attachment is the client for interacting with the Attachment builders.
Attachment *AttachmentClient
// AuthRoles is the client for interacting with the AuthRoles builders.
@ -169,6 +171,7 @@ func (tx *Tx) Client() *Client {
}
func (tx *Tx) init() {
tx.ActionToken = NewActionTokenClient(tx.config)
tx.Attachment = NewAttachmentClient(tx.config)
tx.AuthRoles = NewAuthRolesClient(tx.config)
tx.AuthTokens = NewAuthTokensClient(tx.config)
@ -191,7 +194,7 @@ func (tx *Tx) init() {
// of them in order to commit or rollback the transaction.
//
// If a closed transaction is embedded in one of the generated entities, and the entity
// applies a query, for example: Attachment.QueryXXX(), the query will be executed
// applies a query, for example: ActionToken.QueryXXX(), the query will be executed
// through the driver which created this transaction.
//
// Note that txDriver is not goroutine safe.

View file

@ -52,9 +52,11 @@ type UserEdges struct {
AuthTokens []*AuthTokens `json:"auth_tokens,omitempty"`
// Notifiers holds the value of the notifiers edge.
Notifiers []*Notifier `json:"notifiers,omitempty"`
// ActionTokens holds the value of the action_tokens edge.
ActionTokens []*ActionToken `json:"action_tokens,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [3]bool
loadedTypes [4]bool
}
// GroupOrErr returns the Group value or an error if the edge
@ -88,6 +90,15 @@ func (e UserEdges) NotifiersOrErr() ([]*Notifier, error) {
return nil, &NotLoadedError{edge: "notifiers"}
}
// ActionTokensOrErr returns the ActionTokens value or an error if the edge
// was not loaded in eager-loading.
func (e UserEdges) ActionTokensOrErr() ([]*ActionToken, error) {
if e.loadedTypes[3] {
return e.ActionTokens, nil
}
return nil, &NotLoadedError{edge: "action_tokens"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*User) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
@ -213,6 +224,11 @@ func (u *User) QueryNotifiers() *NotifierQuery {
return NewUserClient(u.config).QueryNotifiers(u)
}
// QueryActionTokens queries the "action_tokens" edge of the User entity.
func (u *User) QueryActionTokens() *ActionTokenQuery {
return NewUserClient(u.config).QueryActionTokens(u)
}
// Update returns a builder for updating this User.
// Note that you need to call User.Unwrap() before calling this method if this User
// was returned from a transaction, and the transaction was committed or rolled back.

View file

@ -40,6 +40,8 @@ const (
EdgeAuthTokens = "auth_tokens"
// EdgeNotifiers holds the string denoting the notifiers edge name in mutations.
EdgeNotifiers = "notifiers"
// EdgeActionTokens holds the string denoting the action_tokens edge name in mutations.
EdgeActionTokens = "action_tokens"
// Table holds the table name of the user in the database.
Table = "users"
// GroupTable is the table that holds the group relation/edge.
@ -63,6 +65,13 @@ const (
NotifiersInverseTable = "notifiers"
// NotifiersColumn is the table column denoting the notifiers relation/edge.
NotifiersColumn = "user_id"
// ActionTokensTable is the table that holds the action_tokens relation/edge.
ActionTokensTable = "action_tokens"
// ActionTokensInverseTable is the table name for the ActionToken entity.
// It exists in this package in order to avoid circular dependency with the "actiontoken" package.
ActionTokensInverseTable = "action_tokens"
// ActionTokensColumn is the table column denoting the action_tokens relation/edge.
ActionTokensColumn = "user_id"
)
// Columns holds all SQL columns for user fields.
@ -234,6 +243,20 @@ func ByNotifiers(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
sqlgraph.OrderByNeighborTerms(s, newNotifiersStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
// ByActionTokensCount orders the results by action_tokens count.
func ByActionTokensCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newActionTokensStep(), opts...)
}
}
// ByActionTokens orders the results by action_tokens terms.
func ByActionTokens(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newActionTokensStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newGroupStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
@ -255,3 +278,10 @@ func newNotifiersStep() *sqlgraph.Step {
sqlgraph.Edge(sqlgraph.O2M, false, NotifiersTable, NotifiersColumn),
)
}
func newActionTokensStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(ActionTokensInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ActionTokensTable, ActionTokensColumn),
)
}

View file

@ -530,6 +530,29 @@ func HasNotifiersWith(preds ...predicate.Notifier) predicate.User {
})
}
// HasActionTokens applies the HasEdge predicate on the "action_tokens" edge.
func HasActionTokens() predicate.User {
return predicate.User(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, ActionTokensTable, ActionTokensColumn),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasActionTokensWith applies the HasEdge predicate on the "action_tokens" edge with a given conditions (other predicates).
func HasActionTokensWith(preds ...predicate.ActionToken) predicate.User {
return predicate.User(func(s *sql.Selector) {
step := newActionTokensStep()
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.User) predicate.User {
return predicate.User(sql.AndPredicates(predicates...))

View file

@ -11,6 +11,7 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
"github.com/hay-kot/homebox/backend/internal/data/ent/notifier"
@ -181,6 +182,21 @@ func (uc *UserCreate) AddNotifiers(n ...*Notifier) *UserCreate {
return uc.AddNotifierIDs(ids...)
}
// AddActionTokenIDs adds the "action_tokens" edge to the ActionToken entity by IDs.
func (uc *UserCreate) AddActionTokenIDs(ids ...uuid.UUID) *UserCreate {
uc.mutation.AddActionTokenIDs(ids...)
return uc
}
// AddActionTokens adds the "action_tokens" edges to the ActionToken entity.
func (uc *UserCreate) AddActionTokens(a ...*ActionToken) *UserCreate {
ids := make([]uuid.UUID, len(a))
for i := range a {
ids[i] = a[i].ID
}
return uc.AddActionTokenIDs(ids...)
}
// Mutation returns the UserMutation object of the builder.
func (uc *UserCreate) Mutation() *UserMutation {
return uc.mutation
@ -411,6 +427,22 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
}
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := uc.mutation.ActionTokensIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}

View file

@ -12,6 +12,7 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
"github.com/hay-kot/homebox/backend/internal/data/ent/notifier"
@ -22,14 +23,15 @@ import (
// UserQuery is the builder for querying User entities.
type UserQuery struct {
config
ctx *QueryContext
order []user.OrderOption
inters []Interceptor
predicates []predicate.User
withGroup *GroupQuery
withAuthTokens *AuthTokensQuery
withNotifiers *NotifierQuery
withFKs bool
ctx *QueryContext
order []user.OrderOption
inters []Interceptor
predicates []predicate.User
withGroup *GroupQuery
withAuthTokens *AuthTokensQuery
withNotifiers *NotifierQuery
withActionTokens *ActionTokenQuery
withFKs bool
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
@ -132,6 +134,28 @@ func (uq *UserQuery) QueryNotifiers() *NotifierQuery {
return query
}
// QueryActionTokens chains the current query on the "action_tokens" edge.
func (uq *UserQuery) QueryActionTokens() *ActionTokenQuery {
query := (&ActionTokenClient{config: uq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := uq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := uq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(user.Table, user.FieldID, selector),
sqlgraph.To(actiontoken.Table, actiontoken.FieldID),
sqlgraph.Edge(sqlgraph.O2M, false, user.ActionTokensTable, user.ActionTokensColumn),
)
fromU = sqlgraph.SetNeighbors(uq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first User entity from the query.
// Returns a *NotFoundError when no User was found.
func (uq *UserQuery) First(ctx context.Context) (*User, error) {
@ -319,14 +343,15 @@ func (uq *UserQuery) Clone() *UserQuery {
return nil
}
return &UserQuery{
config: uq.config,
ctx: uq.ctx.Clone(),
order: append([]user.OrderOption{}, uq.order...),
inters: append([]Interceptor{}, uq.inters...),
predicates: append([]predicate.User{}, uq.predicates...),
withGroup: uq.withGroup.Clone(),
withAuthTokens: uq.withAuthTokens.Clone(),
withNotifiers: uq.withNotifiers.Clone(),
config: uq.config,
ctx: uq.ctx.Clone(),
order: append([]user.OrderOption{}, uq.order...),
inters: append([]Interceptor{}, uq.inters...),
predicates: append([]predicate.User{}, uq.predicates...),
withGroup: uq.withGroup.Clone(),
withAuthTokens: uq.withAuthTokens.Clone(),
withNotifiers: uq.withNotifiers.Clone(),
withActionTokens: uq.withActionTokens.Clone(),
// clone intermediate query.
sql: uq.sql.Clone(),
path: uq.path,
@ -366,6 +391,17 @@ func (uq *UserQuery) WithNotifiers(opts ...func(*NotifierQuery)) *UserQuery {
return uq
}
// WithActionTokens tells the query-builder to eager-load the nodes that are connected to
// the "action_tokens" edge. The optional arguments are used to configure the query builder of the edge.
func (uq *UserQuery) WithActionTokens(opts ...func(*ActionTokenQuery)) *UserQuery {
query := (&ActionTokenClient{config: uq.config}).Query()
for _, opt := range opts {
opt(query)
}
uq.withActionTokens = query
return uq
}
// 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.
//
@ -445,10 +481,11 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
nodes = []*User{}
withFKs = uq.withFKs
_spec = uq.querySpec()
loadedTypes = [3]bool{
loadedTypes = [4]bool{
uq.withGroup != nil,
uq.withAuthTokens != nil,
uq.withNotifiers != nil,
uq.withActionTokens != nil,
}
)
if uq.withGroup != nil {
@ -495,6 +532,13 @@ func (uq *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
return nil, err
}
}
if query := uq.withActionTokens; query != nil {
if err := uq.loadActionTokens(ctx, query, nodes,
func(n *User) { n.Edges.ActionTokens = []*ActionToken{} },
func(n *User, e *ActionToken) { n.Edges.ActionTokens = append(n.Edges.ActionTokens, e) }); err != nil {
return nil, err
}
}
return nodes, nil
}
@ -591,6 +635,36 @@ func (uq *UserQuery) loadNotifiers(ctx context.Context, query *NotifierQuery, no
}
return nil
}
func (uq *UserQuery) loadActionTokens(ctx context.Context, query *ActionTokenQuery, nodes []*User, init func(*User), assign func(*User, *ActionToken)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[uuid.UUID]*User)
for i := range nodes {
fks = append(fks, nodes[i].ID)
nodeids[nodes[i].ID] = nodes[i]
if init != nil {
init(nodes[i])
}
}
if len(query.ctx.Fields) > 0 {
query.ctx.AppendFieldOnce(actiontoken.FieldUserID)
}
query.Where(predicate.ActionToken(func(s *sql.Selector) {
s.Where(sql.InValues(s.C(user.ActionTokensColumn), fks...))
}))
neighbors, err := query.All(ctx)
if err != nil {
return err
}
for _, n := range neighbors {
fk := n.UserID
node, ok := nodeids[fk]
if !ok {
return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, fk, n.ID)
}
assign(node, n)
}
return nil
}
func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) {
_spec := uq.querySpec()

View file

@ -12,6 +12,7 @@ import (
"entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field"
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/authtokens"
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
"github.com/hay-kot/homebox/backend/internal/data/ent/notifier"
@ -183,6 +184,21 @@ func (uu *UserUpdate) AddNotifiers(n ...*Notifier) *UserUpdate {
return uu.AddNotifierIDs(ids...)
}
// AddActionTokenIDs adds the "action_tokens" edge to the ActionToken entity by IDs.
func (uu *UserUpdate) AddActionTokenIDs(ids ...uuid.UUID) *UserUpdate {
uu.mutation.AddActionTokenIDs(ids...)
return uu
}
// AddActionTokens adds the "action_tokens" edges to the ActionToken entity.
func (uu *UserUpdate) AddActionTokens(a ...*ActionToken) *UserUpdate {
ids := make([]uuid.UUID, len(a))
for i := range a {
ids[i] = a[i].ID
}
return uu.AddActionTokenIDs(ids...)
}
// Mutation returns the UserMutation object of the builder.
func (uu *UserUpdate) Mutation() *UserMutation {
return uu.mutation
@ -236,6 +252,27 @@ func (uu *UserUpdate) RemoveNotifiers(n ...*Notifier) *UserUpdate {
return uu.RemoveNotifierIDs(ids...)
}
// ClearActionTokens clears all "action_tokens" edges to the ActionToken entity.
func (uu *UserUpdate) ClearActionTokens() *UserUpdate {
uu.mutation.ClearActionTokens()
return uu
}
// RemoveActionTokenIDs removes the "action_tokens" edge to ActionToken entities by IDs.
func (uu *UserUpdate) RemoveActionTokenIDs(ids ...uuid.UUID) *UserUpdate {
uu.mutation.RemoveActionTokenIDs(ids...)
return uu
}
// RemoveActionTokens removes "action_tokens" edges to ActionToken entities.
func (uu *UserUpdate) RemoveActionTokens(a ...*ActionToken) *UserUpdate {
ids := make([]uuid.UUID, len(a))
for i := range a {
ids[i] = a[i].ID
}
return uu.RemoveActionTokenIDs(ids...)
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (uu *UserUpdate) Save(ctx context.Context) (int, error) {
uu.defaults()
@ -458,6 +495,51 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if uu.mutation.ActionTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := uu.mutation.RemovedActionTokensIDs(); len(nodes) > 0 && !uu.mutation.ActionTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := uu.mutation.ActionTokensIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
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, uu.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{user.Label}
@ -629,6 +711,21 @@ func (uuo *UserUpdateOne) AddNotifiers(n ...*Notifier) *UserUpdateOne {
return uuo.AddNotifierIDs(ids...)
}
// AddActionTokenIDs adds the "action_tokens" edge to the ActionToken entity by IDs.
func (uuo *UserUpdateOne) AddActionTokenIDs(ids ...uuid.UUID) *UserUpdateOne {
uuo.mutation.AddActionTokenIDs(ids...)
return uuo
}
// AddActionTokens adds the "action_tokens" edges to the ActionToken entity.
func (uuo *UserUpdateOne) AddActionTokens(a ...*ActionToken) *UserUpdateOne {
ids := make([]uuid.UUID, len(a))
for i := range a {
ids[i] = a[i].ID
}
return uuo.AddActionTokenIDs(ids...)
}
// Mutation returns the UserMutation object of the builder.
func (uuo *UserUpdateOne) Mutation() *UserMutation {
return uuo.mutation
@ -682,6 +779,27 @@ func (uuo *UserUpdateOne) RemoveNotifiers(n ...*Notifier) *UserUpdateOne {
return uuo.RemoveNotifierIDs(ids...)
}
// ClearActionTokens clears all "action_tokens" edges to the ActionToken entity.
func (uuo *UserUpdateOne) ClearActionTokens() *UserUpdateOne {
uuo.mutation.ClearActionTokens()
return uuo
}
// RemoveActionTokenIDs removes the "action_tokens" edge to ActionToken entities by IDs.
func (uuo *UserUpdateOne) RemoveActionTokenIDs(ids ...uuid.UUID) *UserUpdateOne {
uuo.mutation.RemoveActionTokenIDs(ids...)
return uuo
}
// RemoveActionTokens removes "action_tokens" edges to ActionToken entities.
func (uuo *UserUpdateOne) RemoveActionTokens(a ...*ActionToken) *UserUpdateOne {
ids := make([]uuid.UUID, len(a))
for i := range a {
ids[i] = a[i].ID
}
return uuo.RemoveActionTokenIDs(ids...)
}
// Where appends a list predicates to the UserUpdate builder.
func (uuo *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne {
uuo.mutation.Where(ps...)
@ -934,6 +1052,51 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if uuo.mutation.ActionTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := uuo.mutation.RemovedActionTokensIDs(); len(nodes) > 0 && !uuo.mutation.ActionTokensCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := uuo.mutation.ActionTokensIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M,
Inverse: false,
Table: user.ActionTokensTable,
Columns: []string{user.ActionTokensColumn},
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(actiontoken.FieldID, field.TypeUUID),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &User{config: uuo.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues

View file

@ -0,0 +1,10 @@
-- Create "action_tokens" table
CREATE TABLE `action_tokens` (`id` uuid NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, `action` text NOT NULL DEFAULT ('reset_password'), `token` blob NOT NULL, `user_id` uuid NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `action_tokens_users_action_tokens` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE);
-- Create index "action_tokens_token_key" to table: "action_tokens"
CREATE UNIQUE INDEX `action_tokens_token_key` ON `action_tokens` (`token`);
-- Create index "actiontoken_token" to table: "action_tokens"
CREATE INDEX `actiontoken_token` ON `action_tokens` (`token`);
-- Create index "actiontoken_action" to table: "action_tokens"
CREATE INDEX `actiontoken_action` ON `action_tokens` (`action`);
-- Create index "actiontoken_user_id" to table: "action_tokens"
CREATE INDEX `actiontoken_user_id` ON `action_tokens` (`user_id`);

View file

@ -1,4 +1,4 @@
h1:sjJCTAqc9FG8BKBIzh5ZynYD/Ilz6vnLqM4XX83WQ4M=
h1:6CJ6qlt5IqAoKF6R9yH8Ei2eqkAbkCqjM1dDUvA24f8=
20220929052825_init.sql h1:ZlCqm1wzjDmofeAcSX3jE4h4VcdTNGpRg2eabztDy9Q=
20221001210956_group_invitations.sql h1:YQKJFtE39wFOcRNbZQ/d+ZlHwrcfcsZlcv/pLEYdpjw=
20221009173029_add_user_roles.sql h1:vWmzAfgEWQeGk0Vn70zfVPCcfEZth3E0JcvyKTjpYyU=
@ -13,3 +13,4 @@ h1:sjJCTAqc9FG8BKBIzh5ZynYD/Ilz6vnLqM4XX83WQ4M=
20230305065819_add_notifier_types.sql h1:r5xrgCKYQ2o9byBqYeAX1zdp94BLdaxf4vq9OmGHNl0=
20230305071524_add_group_id_to_notifiers.sql h1:xDShqbyClcFhvJbwclOHdczgXbdffkxXNWjV61hL/t4=
20231006213457_add_primary_attachment_flag.sql h1:J4tMSJQFa7vaj0jpnh8YKTssdyIjRyq6RXDXZIzDDu4=
20240302172225_user_action_tokens.sql h1:nAdtVdh9O7p/AyKnURvZcS0H/Zoj1sM9HpwCOO09zDE=

View file

@ -16,9 +16,9 @@ func (aid AssetID) Int() int {
return int(aid)
}
func ParseAssetIDBytes(d []byte) (AID AssetID, ok bool) {
d = bytes.Replace(d, []byte(`"`), []byte(``), -1)
d = bytes.Replace(d, []byte(`-`), []byte(``), -1)
func ParseAssetIDBytes(d []byte) (assetID AssetID, ok bool) {
d = bytes.ReplaceAll(d, []byte(`"`), []byte(``))
d = bytes.ReplaceAll(d, []byte(`-`), []byte(``))
aidInt, err := strconv.Atoi(string(d))
if err != nil {
@ -28,7 +28,7 @@ func ParseAssetIDBytes(d []byte) (AID AssetID, ok bool) {
return AssetID(aidInt), true
}
func ParseAssetID(s string) (AID AssetID, ok bool) {
func ParseAssetID(s string) (assetID AssetID, ok bool) {
return ParseAssetIDBytes([]byte(s))
}
@ -52,8 +52,8 @@ func (aid *AssetID) UnmarshalJSON(d []byte) error {
return nil
}
d = bytes.Replace(d, []byte(`"`), []byte(``), -1)
d = bytes.Replace(d, []byte(`-`), []byte(``), -1)
d = bytes.ReplaceAll(d, []byte(`"`), []byte(``))
d = bytes.ReplaceAll(d, []byte(`-`), []byte(``))
aidInt, err := strconv.Atoi(string(d))
if err != nil {

View file

@ -56,9 +56,10 @@ func TestMain(m *testing.M) {
tClient = client
tRepos = New(tClient, tbus, os.TempDir())
defer func() { _ = client.Close() }()
bootstrap()
os.Exit(m.Run())
exit := m.Run()
_ = client.Close()
os.Exit(exit)
}

View file

@ -109,12 +109,12 @@ func (r *GroupRepository) GetAllGroups(ctx context.Context) ([]Group, error) {
return r.groupMapper.MapEachErr(r.db.Group.Query().All(ctx))
}
func (r *GroupRepository) StatsLocationsByPurchasePrice(ctx context.Context, GID uuid.UUID) ([]TotalsByOrganizer, error) {
func (r *GroupRepository) StatsLocationsByPurchasePrice(ctx context.Context, groupID uuid.UUID) ([]TotalsByOrganizer, error) {
var v []TotalsByOrganizer
err := r.db.Location.Query().
Where(
location.HasGroupWith(group.ID(GID)),
location.HasGroupWith(group.ID(groupID)),
).
GroupBy(location.FieldID, location.FieldName).
Aggregate(func(sq *sql.Selector) string {
@ -131,12 +131,12 @@ func (r *GroupRepository) StatsLocationsByPurchasePrice(ctx context.Context, GID
return v, err
}
func (r *GroupRepository) StatsLabelsByPurchasePrice(ctx context.Context, GID uuid.UUID) ([]TotalsByOrganizer, error) {
func (r *GroupRepository) StatsLabelsByPurchasePrice(ctx context.Context, groupID uuid.UUID) ([]TotalsByOrganizer, error) {
var v []TotalsByOrganizer
err := r.db.Label.Query().
Where(
label.HasGroupWith(group.ID(GID)),
label.HasGroupWith(group.ID(groupID)),
).
GroupBy(label.FieldID, label.FieldName).
Aggregate(func(sq *sql.Selector) string {
@ -157,7 +157,7 @@ func (r *GroupRepository) StatsLabelsByPurchasePrice(ctx context.Context, GID uu
return v, err
}
func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID, start, end time.Time) (*ValueOverTime, error) {
func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, groupID uuid.UUID, start, end time.Time) (*ValueOverTime, error) {
// Get the Totals for the Start and End of the Given Time Period
q := `
SELECT
@ -180,7 +180,7 @@ func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID,
var maybeStart *float64
var maybeEnd *float64
row := r.db.Sql().QueryRowContext(ctx, q, GID, sqliteDateFormat(start), GID, sqliteDateFormat(end))
row := r.db.Sql().QueryRowContext(ctx, q, groupID, sqliteDateFormat(start), groupID, sqliteDateFormat(end))
err := row.Scan(&maybeStart, &maybeEnd)
if err != nil {
return nil, err
@ -198,7 +198,7 @@ func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID,
// Get Created Date and Price of all items between start and end
err = r.db.Item.Query().
Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
item.CreatedAtGTE(start),
item.CreatedAtLTE(end),
item.Archived(false),
@ -209,7 +209,6 @@ func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID,
item.FieldPurchasePrice,
).
Scan(ctx, &v)
if err != nil {
return nil, err
}
@ -226,7 +225,7 @@ func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID,
return &stats, nil
}
func (r *GroupRepository) StatsGroup(ctx context.Context, GID uuid.UUID) (GroupStatistics, error) {
func (r *GroupRepository) StatsGroup(ctx context.Context, groupID uuid.UUID) (GroupStatistics, error) {
q := `
SELECT
(SELECT COUNT(*) FROM users WHERE group_users = ?) AS total_users,
@ -242,7 +241,7 @@ func (r *GroupRepository) StatsGroup(ctx context.Context, GID uuid.UUID) (GroupS
) AS total_with_warranty
`
var stats GroupStatistics
row := r.db.Sql().QueryRowContext(ctx, q, GID, GID, GID, GID, GID, GID)
row := r.db.Sql().QueryRowContext(ctx, q, groupID, groupID, groupID, groupID, groupID, groupID)
var maybeTotalItemPrice *float64
var maybeTotalWithWarranty *int
@ -264,8 +263,8 @@ func (r *GroupRepository) GroupCreate(ctx context.Context, name string) (Group,
Save(ctx))
}
func (r *GroupRepository) GroupUpdate(ctx context.Context, ID uuid.UUID, data GroupUpdate) (Group, error) {
entity, err := r.db.Group.UpdateOneID(ID).
func (r *GroupRepository) GroupUpdate(ctx context.Context, groupID uuid.UUID, data GroupUpdate) (Group, error) {
entity, err := r.db.Group.UpdateOneID(groupID).
SetName(data.Name).
SetCurrency(strings.ToLower(data.Currency)).
Save(ctx)
@ -273,8 +272,8 @@ func (r *GroupRepository) GroupUpdate(ctx context.Context, ID uuid.UUID, data Gr
return r.groupMapper.MapErr(entity, err)
}
func (r *GroupRepository) GroupByID(ctx context.Context, id uuid.UUID) (Group, error) {
return r.groupMapper.MapErr(r.db.Group.Get(ctx, id))
func (r *GroupRepository) GroupByID(ctx context.Context, groupID uuid.UUID) (Group, error) {
return r.groupMapper.MapErr(r.db.Group.Get(ctx, groupID))
}
func (r *GroupRepository) InvitationGet(ctx context.Context, token []byte) (GroupInvitation, error) {

View file

@ -276,9 +276,9 @@ func mapItemOut(item *ent.Item) ItemOut {
}
}
func (e *ItemsRepository) publishMutationEvent(GID uuid.UUID) {
func (e *ItemsRepository) publishMutationEvent(groupID uuid.UUID) {
if e.bus != nil {
e.bus.Publish(eventbus.EventItemMutation, eventbus.GroupMutationEvent{GID: GID})
e.bus.Publish(eventbus.EventItemMutation, eventbus.GroupMutationEvent{GID: groupID})
}
}
@ -304,13 +304,13 @@ func (e *ItemsRepository) GetOne(ctx context.Context, id uuid.UUID) (ItemOut, er
return e.getOne(ctx, item.ID(id))
}
func (e *ItemsRepository) CheckRef(ctx context.Context, GID uuid.UUID, ref string) (bool, error) {
q := e.db.Item.Query().Where(item.HasGroupWith(group.ID(GID)))
func (e *ItemsRepository) CheckRef(ctx context.Context, groupID uuid.UUID, ref string) (bool, error) {
q := e.db.Item.Query().Where(item.HasGroupWith(group.ID(groupID)))
return q.Where(item.ImportRef(ref)).Exist(ctx)
}
func (e *ItemsRepository) GetByRef(ctx context.Context, GID uuid.UUID, ref string) (ItemOut, error) {
return e.getOne(ctx, item.ImportRef(ref), item.HasGroupWith(group.ID(GID)))
func (e *ItemsRepository) GetByRef(ctx context.Context, groupID uuid.UUID, ref string) (ItemOut, error) {
return e.getOne(ctx, item.ImportRef(ref), item.HasGroupWith(group.ID(groupID)))
}
// GetOneByGroup returns a single item by ID. If the item does not exist, an error is returned.
@ -490,9 +490,9 @@ func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemOut,
All(ctx))
}
func (e *ItemsRepository) GetAllZeroAssetID(ctx context.Context, GID uuid.UUID) ([]ItemSummary, error) {
func (e *ItemsRepository) GetAllZeroAssetID(ctx context.Context, groupID uuid.UUID) ([]ItemSummary, error) {
q := e.db.Item.Query().Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
item.AssetID(0),
).Order(
ent.Asc(item.FieldCreatedAt),
@ -501,9 +501,9 @@ func (e *ItemsRepository) GetAllZeroAssetID(ctx context.Context, GID uuid.UUID)
return mapItemsSummaryErr(q.All(ctx))
}
func (e *ItemsRepository) GetHighestAssetID(ctx context.Context, GID uuid.UUID) (AssetID, error) {
func (e *ItemsRepository) GetHighestAssetID(ctx context.Context, groupID uuid.UUID) (AssetID, error) {
q := e.db.Item.Query().Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
).Order(
ent.Desc(item.FieldAssetID),
).Limit(1)
@ -519,10 +519,10 @@ func (e *ItemsRepository) GetHighestAssetID(ctx context.Context, GID uuid.UUID)
return AssetID(result.AssetID), nil
}
func (e *ItemsRepository) SetAssetID(ctx context.Context, GID uuid.UUID, ID uuid.UUID, assetID AssetID) error {
func (e *ItemsRepository) SetAssetID(ctx context.Context, groupID uuid.UUID, itemID uuid.UUID, assetID AssetID) error {
q := e.db.Item.Update().Where(
item.HasGroupWith(group.ID(GID)),
item.ID(ID),
item.HasGroupWith(group.ID(groupID)),
item.ID(itemID),
)
_, err := q.SetAssetID(int(assetID)).Save(ctx)
@ -576,8 +576,8 @@ func (e *ItemsRepository) DeleteByGroup(ctx context.Context, gid, id uuid.UUID)
return err
}
func (e *ItemsRepository) UpdateByGroup(ctx context.Context, GID uuid.UUID, data ItemUpdate) (ItemOut, error) {
q := e.db.Item.Update().Where(item.ID(data.ID), item.HasGroupWith(group.ID(GID))).
func (e *ItemsRepository) UpdateByGroup(ctx context.Context, groupID uuid.UUID, data ItemUpdate) (ItemOut, error) {
q := e.db.Item.Update().Where(item.ID(data.ID), item.HasGroupWith(group.ID(groupID))).
SetName(data.Name).
SetDescription(data.Description).
SetLocationID(data.LocationID).
@ -688,16 +688,16 @@ func (e *ItemsRepository) UpdateByGroup(ctx context.Context, GID uuid.UUID, data
}
}
e.publishMutationEvent(GID)
e.publishMutationEvent(groupID)
return e.GetOne(ctx, data.ID)
}
func (e *ItemsRepository) GetAllZeroImportRef(ctx context.Context, GID uuid.UUID) ([]uuid.UUID, error) {
func (e *ItemsRepository) GetAllZeroImportRef(ctx context.Context, groupID uuid.UUID) ([]uuid.UUID, error) {
var ids []uuid.UUID
err := e.db.Item.Query().
Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
item.Or(
item.ImportRefEQ(""),
item.ImportRefIsNil(),
@ -712,11 +712,11 @@ func (e *ItemsRepository) GetAllZeroImportRef(ctx context.Context, GID uuid.UUID
return ids, nil
}
func (e *ItemsRepository) Patch(ctx context.Context, GID, ID uuid.UUID, data ItemPatch) error {
func (e *ItemsRepository) Patch(ctx context.Context, groupID, itemID uuid.UUID, data ItemPatch) error {
q := e.db.Item.Update().
Where(
item.ID(ID),
item.HasGroupWith(group.ID(GID)),
item.ID(itemID),
item.HasGroupWith(group.ID(groupID)),
)
if data.ImportRef != nil {
@ -727,11 +727,11 @@ func (e *ItemsRepository) Patch(ctx context.Context, GID, ID uuid.UUID, data Ite
q.SetQuantity(*data.Quantity)
}
e.publishMutationEvent(GID)
e.publishMutationEvent(groupID)
return q.Exec(ctx)
}
func (e *ItemsRepository) GetAllCustomFieldValues(ctx context.Context, GID uuid.UUID, name string) ([]string, error) {
func (e *ItemsRepository) GetAllCustomFieldValues(ctx context.Context, groupID uuid.UUID, name string) ([]string, error) {
type st struct {
Value string `json:"text_value"`
}
@ -740,7 +740,7 @@ func (e *ItemsRepository) GetAllCustomFieldValues(ctx context.Context, GID uuid.
err := e.db.Item.Query().
Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
).
QueryFields().
Where(
@ -761,7 +761,7 @@ func (e *ItemsRepository) GetAllCustomFieldValues(ctx context.Context, GID uuid.
return valueStrings, nil
}
func (e *ItemsRepository) GetAllCustomFieldNames(ctx context.Context, GID uuid.UUID) ([]string, error) {
func (e *ItemsRepository) GetAllCustomFieldNames(ctx context.Context, groupID uuid.UUID) ([]string, error) {
type st struct {
Name string `json:"name"`
}
@ -770,7 +770,7 @@ func (e *ItemsRepository) GetAllCustomFieldNames(ctx context.Context, GID uuid.U
err := e.db.Item.Query().
Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
).
QueryFields().
Unique(true).
@ -794,9 +794,9 @@ func (e *ItemsRepository) GetAllCustomFieldNames(ctx context.Context, GID uuid.U
// This is designed to resolve a long-time bug that has since been fixed with the time selector on the
// frontend. This function is intended to be used as a one-time fix for existing databases and may be
// removed in the future.
func (e *ItemsRepository) ZeroOutTimeFields(ctx context.Context, GID uuid.UUID) (int, error) {
func (e *ItemsRepository) ZeroOutTimeFields(ctx context.Context, groupID uuid.UUID) (int, error) {
q := e.db.Item.Query().Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
item.Or(
item.PurchaseTimeNotNil(),
item.PurchaseFromLT("0002-01-01"),
@ -865,11 +865,11 @@ func (e *ItemsRepository) ZeroOutTimeFields(ctx context.Context, GID uuid.UUID)
return updated, nil
}
func (e *ItemsRepository) SetPrimaryPhotos(ctx context.Context, GID uuid.UUID) (int, error) {
func (e *ItemsRepository) SetPrimaryPhotos(ctx context.Context, groupID uuid.UUID) (int, error) {
// All items where there is no primary photo
itemIDs, err := e.db.Item.Query().
Where(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
item.HasAttachmentsWith(
attachment.TypeEQ(attachment.TypePhoto),
attachment.Not(

View file

@ -65,9 +65,9 @@ func mapLabelOut(label *ent.Label) LabelOut {
}
}
func (r *LabelRepository) publishMutationEvent(GID uuid.UUID) {
func (r *LabelRepository) publishMutationEvent(groupID uuid.UUID) {
if r.bus != nil {
r.bus.Publish(eventbus.EventLabelMutation, eventbus.GroupMutationEvent{GID: GID})
r.bus.Publish(eventbus.EventLabelMutation, eventbus.GroupMutationEvent{GID: groupID})
}
}
@ -79,8 +79,8 @@ func (r *LabelRepository) getOne(ctx context.Context, where ...predicate.Label)
)
}
func (r *LabelRepository) GetOne(ctx context.Context, ID uuid.UUID) (LabelOut, error) {
return r.getOne(ctx, label.ID(ID))
func (r *LabelRepository) GetOne(ctx context.Context, labelID uuid.UUID) (LabelOut, error) {
return r.getOne(ctx, label.ID(labelID))
}
func (r *LabelRepository) GetOneByGroup(ctx context.Context, gid, ld uuid.UUID) (LabelOut, error) {
@ -125,13 +125,13 @@ func (r *LabelRepository) update(ctx context.Context, data LabelUpdate, where ..
Save(ctx)
}
func (r *LabelRepository) UpdateByGroup(ctx context.Context, GID uuid.UUID, data LabelUpdate) (LabelOut, error) {
_, err := r.update(ctx, data, label.ID(data.ID), label.HasGroupWith(group.ID(GID)))
func (r *LabelRepository) UpdateByGroup(ctx context.Context, groupID uuid.UUID, data LabelUpdate) (LabelOut, error) {
_, err := r.update(ctx, data, label.ID(data.ID), label.HasGroupWith(group.ID(groupID)))
if err != nil {
return LabelOut{}, err
}
r.publishMutationEvent(GID)
r.publishMutationEvent(groupID)
return r.GetOne(ctx, data.ID)
}

View file

@ -89,9 +89,9 @@ func mapLocationOut(location *ent.Location) LocationOut {
}
}
func (r *LocationRepository) publishMutationEvent(GID uuid.UUID) {
func (r *LocationRepository) publishMutationEvent(groupID uuid.UUID) {
if r.bus != nil {
r.bus.Publish(eventbus.EventLocationMutation, eventbus.GroupMutationEvent{GID: GID})
r.bus.Publish(eventbus.EventLocationMutation, eventbus.GroupMutationEvent{GID: groupID})
}
}
@ -100,7 +100,7 @@ type LocationQuery struct {
}
// GetAll returns all locations with item count field populated
func (r *LocationRepository) GetAll(ctx context.Context, GID uuid.UUID, filter LocationQuery) ([]LocationOutCount, error) {
func (r *LocationRepository) GetAll(ctx context.Context, groupID uuid.UUID, filter LocationQuery) ([]LocationOutCount, error) {
query := `--sql
SELECT
id,
@ -131,7 +131,7 @@ func (r *LocationRepository) GetAll(ctx context.Context, GID uuid.UUID, filter L
query = strings.Replace(query, "{{ FILTER_CHILDREN }}", "", 1)
}
rows, err := r.db.Sql().QueryContext(ctx, query, GID)
rows, err := r.db.Sql().QueryContext(ctx, query, groupID)
if err != nil {
return nil, err
}
@ -167,19 +167,19 @@ func (r *LocationRepository) getOne(ctx context.Context, where ...predicate.Loca
Only(ctx))
}
func (r *LocationRepository) Get(ctx context.Context, ID uuid.UUID) (LocationOut, error) {
return r.getOne(ctx, location.ID(ID))
func (r *LocationRepository) Get(ctx context.Context, locationID uuid.UUID) (LocationOut, error) {
return r.getOne(ctx, location.ID(locationID))
}
func (r *LocationRepository) GetOneByGroup(ctx context.Context, GID, ID uuid.UUID) (LocationOut, error) {
return r.getOne(ctx, location.ID(ID), location.HasGroupWith(group.ID(GID)))
func (r *LocationRepository) GetOneByGroup(ctx context.Context, groupID, locationID uuid.UUID) (LocationOut, error) {
return r.getOne(ctx, location.ID(locationID), location.HasGroupWith(group.ID(groupID)))
}
func (r *LocationRepository) Create(ctx context.Context, GID uuid.UUID, data LocationCreate) (LocationOut, error) {
func (r *LocationRepository) Create(ctx context.Context, groupID uuid.UUID, data LocationCreate) (LocationOut, error) {
q := r.db.Location.Create().
SetName(data.Name).
SetDescription(data.Description).
SetGroupID(GID)
SetGroupID(groupID)
if data.ParentID != uuid.Nil {
q.SetParentID(data.ParentID)
@ -190,8 +190,8 @@ func (r *LocationRepository) Create(ctx context.Context, GID uuid.UUID, data Loc
return LocationOut{}, err
}
location.Edges.Group = &ent.Group{ID: GID} // bootstrap group ID
r.publishMutationEvent(GID)
location.Edges.Group = &ent.Group{ID: groupID} // bootstrap group ID
r.publishMutationEvent(groupID)
return mapLocationOut(location), nil
}
@ -215,28 +215,28 @@ func (r *LocationRepository) update(ctx context.Context, data LocationUpdate, wh
return r.Get(ctx, data.ID)
}
func (r *LocationRepository) UpdateByGroup(ctx context.Context, GID, ID uuid.UUID, data LocationUpdate) (LocationOut, error) {
v, err := r.update(ctx, data, location.ID(ID), location.HasGroupWith(group.ID(GID)))
func (r *LocationRepository) UpdateByGroup(ctx context.Context, groupID, locationID uuid.UUID, data LocationUpdate) (LocationOut, error) {
v, err := r.update(ctx, data, location.ID(locationID), location.HasGroupWith(group.ID(groupID)))
if err != nil {
return LocationOut{}, err
}
r.publishMutationEvent(GID)
r.publishMutationEvent(groupID)
return v, err
}
// delete should only be used after checking that the location is owned by the
// group. Otherwise, use DeleteByGroup
func (r *LocationRepository) delete(ctx context.Context, ID uuid.UUID) error {
return r.db.Location.DeleteOneID(ID).Exec(ctx)
func (r *LocationRepository) delete(ctx context.Context, locationID uuid.UUID) error {
return r.db.Location.DeleteOneID(locationID).Exec(ctx)
}
func (r *LocationRepository) DeleteByGroup(ctx context.Context, GID, ID uuid.UUID) error {
_, err := r.db.Location.Delete().Where(location.ID(ID), location.HasGroupWith(group.ID(GID))).Exec(ctx)
func (r *LocationRepository) DeleteByGroup(ctx context.Context, groupID, locationID uuid.UUID) error {
_, err := r.db.Location.Delete().Where(location.ID(locationID), location.HasGroupWith(group.ID(groupID))).Exec(ctx)
if err != nil {
return err
}
r.publishMutationEvent(GID)
r.publishMutationEvent(groupID)
return err
}
@ -273,7 +273,7 @@ type ItemPath struct {
Name string `json:"name"`
}
func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUID) ([]ItemPath, error) {
func (r *LocationRepository) PathForLoc(ctx context.Context, groupID, locID uuid.UUID) ([]ItemPath, error) {
query := `WITH RECURSIVE location_path AS (
SELECT id, name, location_children
FROM locations
@ -290,7 +290,7 @@ func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUI
SELECT id, name
FROM location_path`
rows, err := r.db.Sql().QueryContext(ctx, query, locID, GID)
rows, err := r.db.Sql().QueryContext(ctx, query, locID, groupID)
if err != nil {
return nil, err
}
@ -320,7 +320,7 @@ func (r *LocationRepository) PathForLoc(ctx context.Context, GID, locID uuid.UUI
return locations, nil
}
func (r *LocationRepository) Tree(ctx context.Context, GID uuid.UUID, tq TreeQuery) ([]TreeItem, error) {
func (r *LocationRepository) Tree(ctx context.Context, groupID uuid.UUID, tq TreeQuery) ([]TreeItem, error) {
query := `
WITH recursive location_tree(id, NAME, parent_id, level, node_type) AS
(
@ -402,7 +402,7 @@ func (r *LocationRepository) Tree(ctx context.Context, GID uuid.UUID, tq TreeQue
query = strings.ReplaceAll(query, "{{ WITH_ITEMS_FROM }}", "")
}
rows, err := r.db.Sql().QueryContext(ctx, query, GID)
rows, err := r.db.Sql().QueryContext(ctx, query, groupID)
if err != nil {
return nil, err
}

View file

@ -84,11 +84,11 @@ func mapMaintenanceEntry(entry *ent.MaintenanceEntry) MaintenanceEntry {
}
}
func (r *MaintenanceEntryRepository) GetScheduled(ctx context.Context, GID uuid.UUID, dt types.Date) ([]MaintenanceEntry, error) {
func (r *MaintenanceEntryRepository) GetScheduled(ctx context.Context, groupID uuid.UUID, dt types.Date) ([]MaintenanceEntry, error) {
entries, err := r.db.MaintenanceEntry.Query().
Where(
maintenanceentry.HasItemWith(
item.HasGroupWith(group.ID(GID)),
item.HasGroupWith(group.ID(groupID)),
),
maintenanceentry.ScheduledDate(dt.Time()),
maintenanceentry.Or(
@ -97,7 +97,6 @@ func (r *MaintenanceEntryRepository) GetScheduled(ctx context.Context, GID uuid.
),
).
All(ctx)
if err != nil {
return nil, err
}
@ -118,8 +117,8 @@ func (r *MaintenanceEntryRepository) Create(ctx context.Context, itemID uuid.UUI
return mapMaintenanceEntryErr(item, err)
}
func (r *MaintenanceEntryRepository) Update(ctx context.Context, ID uuid.UUID, input MaintenanceEntryUpdate) (MaintenanceEntry, error) {
item, err := r.db.MaintenanceEntry.UpdateOneID(ID).
func (r *MaintenanceEntryRepository) Update(ctx context.Context, entryID uuid.UUID, input MaintenanceEntryUpdate) (MaintenanceEntry, error) {
item, err := r.db.MaintenanceEntry.UpdateOneID(entryID).
SetDate(input.CompletedDate.Time()).
SetScheduledDate(input.ScheduledDate.Time()).
SetName(input.Name).
@ -202,6 +201,6 @@ FROM
return log, nil
}
func (r *MaintenanceEntryRepository) Delete(ctx context.Context, ID uuid.UUID) error {
return r.db.MaintenanceEntry.DeleteOneID(ID).Exec(ctx)
func (r *MaintenanceEntryRepository) Delete(ctx context.Context, entryID uuid.UUID) error {
return r.db.MaintenanceEntry.DeleteOneID(entryID).Exec(ctx)
}

View file

@ -78,7 +78,7 @@ func TestMaintenanceEntryRepository_GetLog(t *testing.T) {
}
assert.InDelta(t, total, log.CostTotal, .001, "total cost should be equal to the sum of all entries")
assert.InDelta(t, total/2, log.CostAverage, 001, "average cost should be the average of the two months")
assert.InDelta(t, total/2, log.CostAverage, 0o01, "average cost should be the average of the two months")
for _, entry := range log.Entries {
err := tRepos.MaintEntry.Delete(context.Background(), entry.ID)

View file

@ -114,7 +114,7 @@ func (r *NotifierRepository) Update(ctx context.Context, userID uuid.UUID, id uu
return r.mapper.MapErr(notifier, err)
}
func (r *NotifierRepository) Delete(ctx context.Context, userID uuid.UUID, ID uuid.UUID) error {
_, err := r.db.Notifier.Delete().Where(notifier.UserID(userID), notifier.ID(ID)).Exec(ctx)
func (r *NotifierRepository) Delete(ctx context.Context, userID uuid.UUID, notifierID uuid.UUID) error {
_, err := r.db.Notifier.Delete().Where(notifier.UserID(userID), notifier.ID(notifierID)).Exec(ctx)
return err
}

View file

@ -5,6 +5,7 @@ import (
"github.com/google/uuid"
"github.com/hay-kot/homebox/backend/internal/data/ent"
"github.com/hay-kot/homebox/backend/internal/data/ent/actiontoken"
"github.com/hay-kot/homebox/backend/internal/data/ent/user"
)
@ -60,9 +61,9 @@ func mapUserOut(user *ent.User) UserOut {
}
}
func (r *UserRepository) GetOneID(ctx context.Context, ID uuid.UUID) (UserOut, error) {
func (r *UserRepository) GetOneID(ctx context.Context, userID uuid.UUID) (UserOut, error) {
return mapUserOutErr(r.db.User.Query().
Where(user.ID(ID)).
Where(user.ID(userID)).
WithGroup().
Only(ctx))
}
@ -101,9 +102,9 @@ func (r *UserRepository) Create(ctx context.Context, usr UserCreate) (UserOut, e
return r.GetOneID(ctx, entUser.ID)
}
func (r *UserRepository) Update(ctx context.Context, ID uuid.UUID, data UserUpdate) error {
func (r *UserRepository) Update(ctx context.Context, userID uuid.UUID, data UserUpdate) error {
q := r.db.User.Update().
Where(user.ID(ID)).
Where(user.ID(userID)).
SetName(data.Name).
SetEmail(data.Email)
@ -130,6 +131,28 @@ func (r *UserRepository) GetSuperusers(ctx context.Context) ([]*ent.User, error)
return users, nil
}
func (r *UserRepository) ChangePassword(ctx context.Context, UID uuid.UUID, pw string) error {
return r.db.User.UpdateOneID(UID).SetPassword(pw).Exec(ctx)
func (r *UserRepository) ChangePassword(ctx context.Context, userID uuid.UUID, pw string) error {
return r.db.User.UpdateOneID(userID).SetPassword(pw).Exec(ctx)
}
func (r *UserRepository) PasswordResetCreate(ctx context.Context, userID uuid.UUID, token []byte) error {
return r.db.ActionToken.Create().
SetUserID(userID).
SetToken(token).
SetAction(actiontoken.ActionResetPassword).
Exec(ctx)
}
func (r *UserRepository) PasswordResetGet(ctx context.Context, token []byte) (*ent.ActionToken, error) {
return r.db.ActionToken.Query().
Where(actiontoken.Token(token)).
WithUser().
Only(ctx)
}
func (r *UserRepository) PasswordResetDelete(ctx context.Context, token []byte) error {
_, err := r.db.ActionToken.Delete().
Where(actiontoken.Token(token)).
Exec(ctx)
return err
}

View file

@ -18,13 +18,14 @@ const (
type Config struct {
conf.Version
Mode string `yaml:"mode" conf:"default:development"` // development or production
Mode string `yaml:"mode" conf:"default:development"` // development or production
Web WebConfig `yaml:"web"`
Storage Storage `yaml:"storage"`
Log LoggerConf `yaml:"logger"`
Mailer MailerConf `yaml:"mailer"`
Demo bool `yaml:"demo"`
Debug DebugConf `yaml:"debug"`
BaseURL string `yaml:"base_url" conf:"default:http://localhost:3000"`
Options Options `yaml:"options"`
}

View file

@ -5,11 +5,11 @@ type MailerConf struct {
Port int `conf:""`
Username string `conf:""`
Password string `conf:""`
From string `conf:""`
From string `conf:"info@example.com"`
}
// Ready is a simple check to ensure that the configuration is not empty.
// or with it's default state.
func (mc *MailerConf) Ready() bool {
return mc.Host != "" && mc.Port != 0 && mc.Username != "" && mc.Password != "" && mc.From != ""
return mc.Host != "" && mc.Port != 0 && mc.From != ""
}

View file

@ -5,8 +5,7 @@ import (
"errors"
)
type UnauthorizedError struct {
}
type UnauthorizedError struct{}
func (err *UnauthorizedError) Error() string {
return "unauthorized"

View file

@ -49,7 +49,6 @@ func init() { // nolint
return false
})
if err != nil {
panic(err)
}

View file

@ -6,5 +6,7 @@ import (
"github.com/google/uuid"
)
type AdapterFunc[T any, Y any] func(*http.Request, T) (Y, error)
type IDFunc[T any, Y any] func(*http.Request, uuid.UUID, T) (Y, error)
type (
AdapterFunc[T any, Y any] func(*http.Request, T) (Y, error)
IDFunc[T any, Y any] func(*http.Request, uuid.UUID, T) (Y, error)
)

View file

@ -8,8 +8,10 @@ import (
"github.com/hay-kot/httpkit/server"
)
type CommandFunc[T any] func(*http.Request) (T, error)
type CommandIDFunc[T any] func(*http.Request, uuid.UUID) (T, error)
type (
CommandFunc[T any] func(*http.Request) (T, error)
CommandIDFunc[T any] func(*http.Request, uuid.UUID) (T, error)
)
// Command is an HandlerAdapter that returns a errchain.HandlerFunc that
// The command adapters are used to handle commands that do not accept a body

View file

@ -1,4 +1,4 @@
// Package mailer provides a simple mailer for sending emails.
// Package mailer provides a simple interface to send emails using SMTP.
package mailer
import (
@ -25,16 +25,16 @@ func (m *Mailer) server() string {
return m.Host + ":" + strconv.Itoa(m.Port)
}
func (m *Mailer) Send(msg *Message) error {
func (m *Mailer) Send(msg Message) error {
server := m.server()
header := make(map[string]string)
header["From"] = msg.From.String()
header["To"] = msg.To.String()
header["Subject"] = mime.QEncoding.Encode("UTF-8", msg.Subject)
header["MIME-Version"] = "1.0"
header["Content-Type"] = "text/html; charset=\"utf-8\""
header["Content-Transfer-Encoding"] = "base64"
header := map[string]string{
"From": m.From,
"Subject": mime.QEncoding.Encode("UTF-8", msg.Subject),
"MIME-Version": "1.0",
"Content-Type": "text/html; charset=\"utf-8\"",
"Content-Transfer-Encoding": "base64",
}
message := ""
for k, v := range header {
@ -46,7 +46,7 @@ func (m *Mailer) Send(msg *Message) error {
server,
smtp.PlainAuth("", m.Username, m.Password, m.Host),
m.From,
[]string{msg.To.Address},
msg.ToAddresses(),
[]byte(message),
)
}

View file

@ -24,7 +24,6 @@ func GetTestMailer() (*Mailer, error) {
// Unmarshal JSON
err = json.Unmarshal(bytes, mailer)
if err != nil {
return nil, err
}

View file

@ -9,6 +9,10 @@ type Message struct {
Body string
}
func (m Message) ToAddresses() []string {
return []string{m.To.Address}
}
type MessageBuilder struct {
subject string
to mail.Address
@ -20,8 +24,8 @@ func NewMessageBuilder() *MessageBuilder {
return &MessageBuilder{}
}
func (mb *MessageBuilder) Build() *Message {
return &Message{
func (mb *MessageBuilder) Build() Message {
return Message{
Subject: mb.subject,
To: mb.to,
From: mb.from,

View file

@ -48,7 +48,6 @@ func render(tpl string, data TemplateProps) (string, error) {
var tplBuffer bytes.Buffer
err = tmpl.Execute(&tplBuffer, data)
if err != nil {
return "", err
}

View file

@ -1785,6 +1785,33 @@
}
}
},
"/v1/users/request-password-reset": {
"post": {
"produces": [
"application/json"
],
"tags": [
"User"
],
"summary": "Request Password Reset",
"parameters": [
{
"description": "User Data",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/services.PasswordResetRequest"
}
}
],
"responses": {
"204": {
"description": "No Content"
}
}
}
},
"/v1/users/self": {
"get": {
"security": [
@ -2818,6 +2845,14 @@
}
}
},
"services.PasswordResetRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
}
},
"services.UserRegistration": {
"type": "object",
"properties": {

View file

@ -1,4 +1,5 @@
module.exports = {
ignorePatterns: ["nuxt.proxyoverride.ts"],
env: {
browser: true,
es2021: true,

View file

@ -1,5 +1,3 @@
import { useId } from "./use-ids";
interface Notification {
id: string;
message: string;

View file

@ -0,0 +1,66 @@
<script setup lang="ts">
import MdiGithub from "~icons/mdi/github";
import MdiTwitter from "~icons/mdi/twitter";
import MdiDiscord from "~icons/mdi/discord";
import MdiFolder from "~icons/mdi/folder";
const api = usePublicApi();
const { data: status } = useAsyncData(async () => {
const { data } = await api.status();
return data;
});
</script>
<template>
<div>
<AppToast />
<div class="flex flex-col min-h-screen">
<div class="fill-primary min-w-full absolute top-0 z-[-1]">
<div class="bg-primary flex-col flex min-h-[20vh]" />
<svg
class="fill-primary drop-shadow-xl"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1440 320"
preserveAspectRatio="none"
>
<path
fill-opacity="1"
d="M0,32L80,69.3C160,107,320,181,480,181.3C640,181,800,107,960,117.3C1120,128,1280,224,1360,272L1440,320L1440,0L1360,0C1280,0,1120,0,960,0C800,0,640,0,480,0C320,0,160,0,80,0L0,0Z"
></path>
</svg>
</div>
<div>
<header class="p-4 sm:px-6 lg:p-14 sm:py-6 sm:flex sm:items-end mx-auto">
<div>
<h2 class="mt-1 text-4xl font-bold tracking-tight text-neutral-content sm:text-5xl lg:text-6xl flex">
HomeB
<AppLogo class="w-12 -mb-4" />
x
</h2>
<p class="ml-1 text-lg text-base-content/50">Track, Organize, and Manage your Things.</p>
</div>
<div class="flex mt-6 sm:mt-0 gap-4 ml-auto text-neutral-content">
<a class="tooltip" data-tip="Project Github" href="https://github.com/hay-kot/homebox" target="_blank">
<MdiGithub class="h-8 w-8" />
</a>
<a href="https://twitter.com/haybytes" class="tooltip" data-tip="Follow The Developer" target="_blank">
<MdiTwitter class="h-8 w-8" />
</a>
<a href="https://discord.gg/tuncmNrE4z" class="tooltip" data-tip="Join The Discord" target="_blank">
<MdiDiscord class="h-8 w-8" />
</a>
<a href="https://hay-kot.github.io/homebox/" class="tooltip" data-tip="Read The Docs" target="_blank">
<MdiFolder class="h-8 w-8" />
</a>
</div>
</header>
<div class="grid p-6 sm:place-items-center min-h-[50vh]">
<slot :status="status" />
</div>
</div>
<footer v-if="status" class="mt-auto text-center w-full bottom-0 pb-4">
<p class="text-center text-sm">Version: {{ status.build.version }} ~ Build: {{ status.build.commit }}</p>
</footer>
</div>
</div>
</template>

View file

@ -1,4 +1,3 @@
<script setup lang="ts"></script>
<template>
<div>
<AppToast />

View file

@ -1,5 +1,11 @@
import { BaseAPI, route } from "./base";
import type { APISummary, LoginForm, TokenResponse, UserRegistration } from "./types/data-contracts";
import type {
APISummary,
LoginForm,
PasswordResetRequest,
TokenResponse,
UserRegistration,
} from "./types/data-contracts";
export type StatusResult = {
health: boolean;
@ -27,4 +33,11 @@ export class PublicApi extends BaseAPI {
public register(body: UserRegistration) {
return this.http.post<UserRegistration, TokenResponse>({ url: route("/users/register"), body });
}
public resetPasseord(email: string) {
return this.http.post<PasswordResetRequest, void>({
url: route("/users/request-password-reset"),
body: { email },
});
}
}

View file

@ -373,6 +373,10 @@ export interface ValueOverTimeEntry {
value: number;
}
export interface PasswordResetRequest {
email: string;
}
export interface UserRegistration {
email: string;
name: string;

View file

@ -8,7 +8,6 @@ import { defineNuxtModule, logger } from "@nuxt/kit";
//
// fix from
// - https://gist.github.com/ucw/67f7291c64777fb24341e8eae72bcd24
// eslint-disable-next-line
import { createProxyServer } from "http-proxy";
export default defineNuxtModule({

View file

@ -17,6 +17,7 @@
"@faker-js/faker": "^8.0.0",
"@iconify-json/mdi": "^1.1.64",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@nuxtjs/tailwindcss": "^6.12.0",
"@types/dompurify": "^3.0.0",
"@types/markdown-it": "^13.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
@ -28,7 +29,7 @@
"eslint-plugin-vue": "^9.4.0",
"h3": "^1.7.1",
"isomorphic-fetch": "^3.0.0",
"nuxt": "3.6.5",
"nuxt": "3.11.2",
"prettier": "^3.2.5",
"typescript": "^5.0.0",
"unplugin-icons": "^0.18.5",
@ -37,7 +38,6 @@
},
"dependencies": {
"@headlessui/vue": "^1.7.9",
"@nuxtjs/tailwindcss": "^6.1.3",
"@pinia/nuxt": "^0.5.0",
"@tailwindcss/aspect-ratio": "^0.4.0",
"@tailwindcss/forms": "^0.5.2",

View file

@ -1,14 +1,17 @@
<script setup lang="ts">
import MdiGithub from "~icons/mdi/github";
import MdiTwitter from "~icons/mdi/twitter";
import MdiDiscord from "~icons/mdi/discord";
import MdiFolder from "~icons/mdi/folder";
import { useRouteHash } from "@vueuse/router";
import MdiAccount from "~icons/mdi/account";
import MdiAccountPlus from "~icons/mdi/account-plus";
import MdiLogin from "~icons/mdi/login";
import MdiArrowRight from "~icons/mdi/arrow-right";
import MdiLock from "~icons/mdi/lock";
enum PageForms {
Register = "register",
Login = "login",
ForgotPassword = "forgot-password",
}
useHead({
title: "Homebox | Organize and Tag Your Stuff",
});
@ -30,21 +33,13 @@
const api = usePublicApi();
const toast = useNotifier();
const { data: status } = useAsyncData(async () => {
const { data } = await api.status();
if (data.demo) {
username.value = "demo@example.com";
password.value = "demo";
const pageForm = useRouteHash(PageForms.Login);
const pageFormStr = computed(() => {
if (!pageForm.value) {
return PageForms.Login;
}
return data;
});
whenever(status, status => {
if (status?.demo) {
email.value = "demo@example.com";
loginPassword.value = "demo";
}
return pageForm.value[0] === "#" ? pageForm.value.slice(1) : pageForm.value;
});
const route = useRoute();
@ -92,12 +87,12 @@
toast.success("User registered");
loading.value = false;
registerForm.value = false;
pageForm.value = PageForms.Login;
}
onMounted(() => {
if (groupToken.value !== "") {
registerForm.value = true;
pageForm.value = PageForms.Register;
}
});
@ -120,139 +115,134 @@
loading.value = false;
}
const [registerForm, toggleLogin] = useToggle();
async function resetPassword() {
if (email.value === "") {
toast.error("Email is required");
return;
}
const resp = await api.resetPasseord(email.value);
if (resp.error) {
toast.error("Problem resetting password");
return;
}
toast.success("Password reset link sent to your email");
}
</script>
<template>
<div class="flex flex-col min-h-screen">
<div class="fill-primary min-w-full absolute top-0 z-[-1]">
<div class="bg-primary flex-col flex min-h-[20vh]" />
<svg
class="fill-primary drop-shadow-xl"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1440 320"
preserveAspectRatio="none"
>
<path
fill-opacity="1"
d="M0,32L80,69.3C160,107,320,181,480,181.3C640,181,800,107,960,117.3C1120,128,1280,224,1360,272L1440,320L1440,0L1360,0C1280,0,1120,0,960,0C800,0,640,0,480,0C320,0,160,0,80,0L0,0Z"
></path>
</svg>
</div>
<NuxtLayout v-slot="{ status }" name="center-card">
<div>
<header class="p-4 sm:px-6 lg:p-14 sm:py-6 sm:flex sm:items-end mx-auto">
<div>
<h2 class="mt-1 text-4xl font-bold tracking-tight text-neutral-content sm:text-5xl lg:text-6xl flex">
HomeB
<AppLogo class="w-12 -mb-4" />
x
</h2>
<p class="ml-1 text-lg text-base-content/50">Track, Organize, and Manage your Things.</p>
</div>
<div class="flex mt-6 sm:mt-0 gap-4 ml-auto text-neutral-content">
<a class="tooltip" data-tip="Project Github" href="https://github.com/hay-kot/homebox" target="_blank">
<MdiGithub class="h-8 w-8" />
</a>
<a href="https://twitter.com/haybytes" class="tooltip" data-tip="Follow The Developer" target="_blank">
<MdiTwitter class="h-8 w-8" />
</a>
<a href="https://discord.gg/tuncmNrE4z" class="tooltip" data-tip="Join The Discord" target="_blank">
<MdiDiscord class="h-8 w-8" />
</a>
<a href="https://hay-kot.github.io/homebox/" class="tooltip" data-tip="Read The Docs" target="_blank">
<MdiFolder class="h-8 w-8" />
</a>
</div>
</header>
<div class="grid p-6 sm:place-items-center min-h-[50vh]">
<div>
<Transition name="slide-fade">
<form v-if="registerForm" @submit.prevent="registerUser">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Register
</h2>
<FormTextField v-model="email" label="Set your email?" />
<FormTextField v-model="username" label="What's your name?" />
<div v-if="!(groupToken == '')" class="pt-4 pb-1 text-center">
<p>You're Joining an Existing Group!</p>
<button type="button" class="text-xs underline" @click="groupToken = ''">
Don't Want To Join a Group?
</button>
</div>
<FormPassword v-model="password" label="Set your password" />
<PasswordScore v-model:valid="canRegister" :password="password" />
<div class="card-actions justify-end">
<button
type="submit"
class="btn btn-primary mt-2"
:class="loading ? 'loading' : ''"
:disabled="loading || !canRegister"
>
Register
</button>
</div>
</div>
<Transition name="slide-fade">
<form v-if="pageFormStr === PageForms.Register" @submit.prevent="registerUser">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Register
</h2>
<FormTextField v-model="email" label="Set your email?" />
<FormTextField v-model="username" label="What's your name?" />
<div v-if="!(groupToken == '')" class="pt-4 pb-1 text-center">
<p>You're Joining an Existing Group!</p>
<button type="button" class="text-xs underline" @click="groupToken = ''">
Don't Want To Join a Group?
</button>
</div>
</form>
<form v-else @submit.prevent="login">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Login
</h2>
<template v-if="status && status.demo">
<p class="text-xs italic text-center">This is a demo instance</p>
<p class="text-xs text-center"><b>Email</b> demo@example.com</p>
<p class="text-xs text-center"><b>Password</b> demo</p>
</template>
<FormTextField v-model="email" label="Email" />
<FormPassword v-model="loginPassword" label="Password" />
<div class="max-w-[140px]">
<FormCheckbox v-model="remember" label="Remember Me" />
</div>
<div class="card-actions justify-end">
<button
type="submit"
class="btn btn-primary btn-block"
:class="loading ? 'loading' : ''"
:disabled="loading"
>
Login
</button>
</div>
</div>
<FormPassword v-model="password" label="Set your password" />
<PasswordScore v-model:valid="canRegister" :password="password" />
<div class="card-actions justify-end">
<button
type="submit"
class="btn btn-primary mt-2"
:class="loading ? 'loading' : ''"
:disabled="loading || !canRegister"
>
Register
</button>
</div>
</form>
</Transition>
<div class="text-center mt-6">
<BaseButton
v-if="status && status.allowRegistration"
class="btn-primary btn-wide"
@click="() => toggleLogin()"
>
<template #icon>
<MdiAccountPlus v-if="!registerForm" class="w-5 h-5 swap-off" />
<MdiLogin v-else class="w-5 h-5 swap-off" />
<MdiArrowRight class="w-5 h-5 swap-on" />
</template>
{{ registerForm ? "Login" : "Register" }}
</BaseButton>
<p v-else class="text-base-content italic text-sm inline-flex items-center gap-2">
<MdiLock class="w-4 h-4 inline-block" />
Registration Disabled
</p>
</div>
</div>
</div>
</form>
<form v-else-if="pageFormStr === PageForms.ForgotPassword" @submit.prevent="resetPassword">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Reset Password
</h2>
<FormTextField v-model="email" label="Email" />
<p class="text-sm text-base-content/50">
If you have an account with us, we will send you a password reset link.
</p>
<div class="card-actions justify-end mt-4">
<button
type="submit"
class="btn btn-primary btn-block"
:class="loading ? 'loading' : ''"
:disabled="loading"
>
Reset Password
</button>
</div>
</div>
</div>
</form>
<form v-else @submit.prevent="login">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Login
</h2>
<template v-if="status && status.demo">
<p class="text-xs italic text-center">This is a demo instance</p>
<p class="text-xs text-center"><b>Email</b> demo@example.com</p>
<p class="text-xs text-center"><b>Password</b> demo</p>
</template>
<FormTextField v-model="email" label="Email" />
<FormPassword v-model="loginPassword" label="Password" />
<div class="max-w-[140px]">
<FormCheckbox v-model="remember" label="Remember Me" />
</div>
<div class="card-actions justify-end">
<button
type="submit"
class="btn btn-primary btn-block"
:class="loading ? 'loading' : ''"
:disabled="loading"
>
Login
</button>
</div>
</div>
</div>
</form>
</Transition>
<div class="text-center mt-6">
<BaseButton
v-if="status && status.allowRegistration"
class="btn-primary btn-wide"
:to="pageFormStr === PageForms.Register ? `#${PageForms.Login}` : `#${PageForms.Register}`"
>
<template #icon>
<MdiAccountPlus v-if="pageFormStr === PageForms.Register" class="w-5 h-5 swap-off" />
<MdiLogin v-else class="w-5 h-5 swap-off" />
<MdiArrowRight class="w-5 h-5 swap-on" />
</template>
{{ pageFormStr === PageForms.Register ? "Login" : "Register" }}
</BaseButton>
<p v-else class="text-base-content italic text-sm inline-flex items-center gap-2">
<MdiLock class="w-4 h-4 inline-block" />
Registration Disabled
</p>
<NuxtLink :to="`#${PageForms.ForgotPassword}`">
<p class="text-xs text-base-content/50 mt-2">Forgot your password?</p>
</NuxtLink>
</div>
</div>
<footer v-if="status" class="mt-auto text-center w-full bottom-0 pb-4">
<p class="text-center text-sm">Version: {{ status.build.version }} ~ Build: {{ status.build.commit }}</p>
</footer>
</div>
</NuxtLayout>
</template>
<style lang="css" scoped>

View file

@ -0,0 +1,74 @@
<template>
<div>
<Title>Password Reset</Title>
<form @submit.prevent="resetPassword">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Password Reset
</h2>
<FormPassword v-model="form.password" label="New Password" />
<FormPassword v-model="form.passwordConfirm" label="Confirm Password" />
<PasswordScore v-model:valid="form.requirementsMet" :password="form.password" />
<div class="card-actions justify-end">
<button type="submit" class="btn btn-primary mt-2" :class="loading ? 'loading' : ''">Reset Password</button>
</div>
</div>
</div>
</form>
<div class="grid place-content-center pt-4">
<NuxtLink to="/#login">
<p class="text-xs text-base-content/50 mt-2">Account Login</p>
</NuxtLink>
</div>
</div>
</template>
<script setup lang="ts">
import MdiAccount from "~icons/mdi/account";
const route = useRoute();
definePageMeta({
title: "Password Reset",
layout: "center-card",
middleware: [
() => {
const ctx = useAuthContext();
if (ctx.isAuthorized()) {
return "/home";
}
},
],
});
const toast = useNotifier();
const loading = ref(false);
const token = route.query.token;
const form = reactive({
requirementsMet: false,
password: "",
passwordConfirm: "",
});
function resetPassword() {
if (token === undefined) {
return toast.error("Invalid reset token");
}
if (form.password !== form.passwordConfirm) {
return toast.error("Passwords do not match");
}
if (!form.requirementsMet) {
return toast.error("Password does not meet requirements");
}
loading.value = true;
}
</script>
<style scoped></style>

16882
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff