Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use notifiers on schedule #362

Merged
merged 6 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
[Configuration & Docker Compose](https://hay-kot.github.io/homebox/quick-start)

```bash
docker run --name=homebox \
--restart=always \
--publish=3100:7745 \
ghcr.io/hay-kot/homebox:latest
docker run -d \
--name homebox \
--restart unless-stopped \
--publish 3100:7745 \
--env TZ=Europe/Bucharest \
--volume /path/to/data/folder/:/data \
ghcr.io/hay-kot/homebox:latest
```

## Credits
Expand Down
5 changes: 4 additions & 1 deletion backend/app/api/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ func new(conf *config.Config) *app {
}

func (a *app) startBgTask(t time.Duration, fn func()) {
timer := time.NewTimer(t)

for {
timer.Reset(t)
a.server.Background(fn)
time.Sleep(t)
<-timer.C
}
}
14 changes: 14 additions & 0 deletions backend/app/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"fmt"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -168,6 +169,19 @@ func run(cfg *config.Config) error {
Msg("failed to purge expired invitations")
}
})
go app.startBgTask(time.Duration(1)*time.Hour, func() {
now := time.Now()

if now.Hour() == 8 {
fmt.Println("run notifiers")
err := app.services.BackgroundService.SendNotifiersToday(context.Background())
if err != nil {
log.Error().
Err(err).
Msg("failed to send notifiers")
}
}
})

// TODO: Remove through external API that does setup
if cfg.Demo {
Expand Down
1 change: 0 additions & 1 deletion backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ require (
github.com/rs/zerolog v1.29.0
github.com/stretchr/testify v1.8.2
github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/http-swagger/v2 v2.0.0
github.com/swaggo/swag v1.8.11
github.com/yeqown/go-qrcode/v2 v2.2.1
github.com/yeqown/go-qrcode/writer/standard v1.2.1
Expand Down
7 changes: 0 additions & 7 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,6 @@ cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuW
cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
entgo.io/ent v0.11.8 h1:M/M0QL1CYCUSdqGRXUrXhFYSDRJPsOOrr+RLEej/gyQ=
entgo.io/ent v0.11.8/go.mod h1:ericBi6Q8l3wBH1wEIDfKxw7rcQEuRPyBfbIzjtxJ18=
entgo.io/ent v0.11.10 h1:iqn32ybY5HRW3xSAyMNdNKpZhKgMf1Zunsej9yPKUI8=
entgo.io/ent v0.11.10/go.mod h1:mzTZ0trE+jCQw/fnzijbm5Mck/l8Gbg7gC/+L1COyzM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
Expand Down Expand Up @@ -620,9 +618,6 @@ github.com/swaggo/files v1.0.0 h1:1gGXVIeUFCS/dta17rnP0iOpr6CXFwKD7EO5ID233e4=
github.com/swaggo/files v1.0.0/go.mod h1:N59U6URJLyU1PQgFqPM7wXLMhJx7QAolnvfQkqO13kc=
github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=
github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=
github.com/swaggo/http-swagger/v2 v2.0.0/go.mod h1:XYhrQVIKz13CxuKD4p4kvpaRB4jJ1/MlfQXVOE+CX8Y=
github.com/swaggo/swag v1.8.10 h1:eExW4bFa52WOjqRzRD58bgWsWfdFJso50lpbeTcmTfo=
github.com/swaggo/swag v1.8.10/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
github.com/swaggo/swag v1.8.11 h1:Fp1dNNtDvbCf+8kvehZbHQnlF6AxHGjmw6H/xAMrZfY=
github.com/swaggo/swag v1.8.11/go.mod h1:2GXgpNI9iy5OdsYWu8zXfRAGnOAPxYxTWTyM0XOTYZQ=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
Expand Down Expand Up @@ -991,8 +986,6 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.6.1-0.20230222164832-25d2519c8696 h1:8985/C5IvACpd9DDXckSnjSBLKDgbxXiyODgi94zOPM=
golang.org/x/tools v0.6.1-0.20230222164832-25d2519c8696/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
8 changes: 5 additions & 3 deletions backend/internal/core/services/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
)

type AllServices struct {
User *UserService
Group *GroupService
Items *ItemService
User *UserService
Group *GroupService
Items *ItemService
BackgroundService *BackgroundService
}

type OptionsFunc func(*options)
Expand Down Expand Up @@ -42,5 +43,6 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
repo: repos,
autoIncrementAssetID: options.autoIncrementAssetID,
},
BackgroundService: &BackgroundService{repos},
}
}
81 changes: 81 additions & 0 deletions backend/internal/core/services/service_background.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package services

import (
"context"
"strings"
"time"

"github.com/containrrr/shoutrrr"
"github.com/hay-kot/homebox/backend/internal/data/repo"
"github.com/hay-kot/homebox/backend/internal/data/types"
"github.com/rs/zerolog/log"
)

type BackgroundService struct {
repos *repo.AllRepos
}

func (svc *BackgroundService) SendNotifiersToday(ctx context.Context) error {
// Get All Groups
groups, err := svc.repos.Groups.GetAllGroups(ctx)
if err != nil {
return err
}

today := types.DateFromTime(time.Now())

for i := range groups {
group := groups[i]

entries, err := svc.repos.MaintEntry.GetScheduled(ctx, group.ID, today)
if err != nil {
return err
}

if len(entries) == 0 {
log.Debug().
Str("group_name", group.Name).
Str("group_id", group.ID.String()).
Msg("No scheduled maintenance for today")
continue
}

notifiers, err := svc.repos.Notifiers.GetByGroup(ctx, group.ID)
if err != nil {
return err
}

urls := make([]string, len(notifiers))
for i := range notifiers {
urls[i] = notifiers[i].URL
}

bldr := strings.Builder{}

bldr.WriteString("Homebox Maintenance for (")
bldr.WriteString(today.String())
bldr.WriteString("):\n")

for i := range entries {
entry := entries[i]
bldr.WriteString(" - ")
bldr.WriteString(entry.Name)
bldr.WriteString("\n")
}

var sendErrs []error
for i := range urls {
err := shoutrrr.Send(urls[i], bldr.String())

if err != nil {
sendErrs = append(sendErrs, err)
}
}

if len(sendErrs) > 0 {
return sendErrs[0]
}
}

return nil
}
2 changes: 2 additions & 0 deletions backend/internal/data/ent/schema/maintenance_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func (MaintenanceEntry) Fields() []ent.Field {
Optional(),
field.Float("cost").
Default(0.0),
field.Bool("reminders_enabled").
Default(false),
}
}

Expand Down
62 changes: 36 additions & 26 deletions backend/internal/data/repo/repo_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,36 @@ import (
)

type GroupRepository struct {
db *ent.Client
db *ent.Client
groupMapper MapFunc[*ent.Group, Group]
invitationMapper MapFunc[*ent.GroupInvitationToken, GroupInvitation]
}

func NewGroupRepository(db *ent.Client) *GroupRepository {
gmap := func(g *ent.Group) Group {
return Group{
ID: g.ID,
Name: g.Name,
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
Currency: strings.ToUpper(g.Currency.String()),
}
}

imap := func(i *ent.GroupInvitationToken) GroupInvitation {
return GroupInvitation{
ID: i.ID,
ExpiresAt: i.ExpiresAt,
Uses: i.Uses,
Group: gmap(i.Edges.Group),
}
}

return &GroupRepository{
db: db,
groupMapper: gmap,
invitationMapper: imap,
}
}

type (
Expand Down Expand Up @@ -76,27 +105,8 @@ type (
}
)

var mapToGroupErr = mapTErrFunc(mapToGroup)

func mapToGroup(g *ent.Group) Group {
return Group{
ID: g.ID,
Name: g.Name,
CreatedAt: g.CreatedAt,
UpdatedAt: g.UpdatedAt,
Currency: strings.ToUpper(g.Currency.String()),
}
}

var mapToGroupInvitationErr = mapTErrFunc(mapToGroupInvitation)

func mapToGroupInvitation(g *ent.GroupInvitationToken) GroupInvitation {
return GroupInvitation{
ID: g.ID,
ExpiresAt: g.ExpiresAt,
Uses: g.Uses,
Group: mapToGroup(g.Edges.Group),
}
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) {
Expand Down Expand Up @@ -249,7 +259,7 @@ func (r *GroupRepository) StatsGroup(ctx context.Context, GID uuid.UUID) (GroupS
}

func (r *GroupRepository) GroupCreate(ctx context.Context, name string) (Group, error) {
return mapToGroupErr(r.db.Group.Create().
return r.groupMapper.MapErr(r.db.Group.Create().
SetName(name).
Save(ctx))
}
Expand All @@ -262,15 +272,15 @@ func (r *GroupRepository) GroupUpdate(ctx context.Context, ID uuid.UUID, data Gr
SetCurrency(currency).
Save(ctx)

return mapToGroupErr(entity, err)
return r.groupMapper.MapErr(entity, err)
}

func (r *GroupRepository) GroupByID(ctx context.Context, id uuid.UUID) (Group, error) {
return mapToGroupErr(r.db.Group.Get(ctx, id))
return r.groupMapper.MapErr(r.db.Group.Get(ctx, id))
}

func (r *GroupRepository) InvitationGet(ctx context.Context, token []byte) (GroupInvitation, error) {
return mapToGroupInvitationErr(r.db.GroupInvitationToken.Query().
return r.invitationMapper.MapErr(r.db.GroupInvitationToken.Query().
Where(groupinvitationtoken.Token(token)).
WithGroup().
Only(ctx))
Expand Down
21 changes: 21 additions & 0 deletions backend/internal/data/repo/repo_maintenance_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ func mapMaintenanceEntry(entry *ent.MaintenanceEntry) MaintenanceEntry {
}
}

func (r *MaintenanceEntryRepository) GetScheduled(ctx context.Context, GID uuid.UUID, dt types.Date) ([]MaintenanceEntry, error) {
entries, err := r.db.MaintenanceEntry.Query().
Where(
maintenanceentry.HasItemWith(
item.HasGroupWith(group.ID(GID)),
),
maintenanceentry.ScheduledDate(dt.Time()),
maintenanceentry.Or(
maintenanceentry.DateIsNil(),
maintenanceentry.DateEQ(time.Time{}),
),
).
All(ctx)

if err != nil {
return nil, err
}

return mapEachMaintenanceEntry(entries), nil
}

func (r *MaintenanceEntryRepository) Create(ctx context.Context, itemID uuid.UUID, input MaintenanceEntryCreate) (MaintenanceEntry, error) {
item, err := r.db.MaintenanceEntry.Create().
SetItemID(itemID).
Expand Down
10 changes: 10 additions & 0 deletions backend/internal/data/repo/repo_notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ func (r *NotifierRepository) GetByGroup(ctx context.Context, groupID uuid.UUID)
Where(notifier.GroupID(groupID)).
Order(ent.Asc(notifier.FieldName)).
All(ctx)

return r.mapper.MapEachErr(notifier, err)
}

func (r *NotifierRepository) GetActiveByGroup(ctx context.Context, groupID uuid.UUID) ([]NotifierOut, error) {
notifier, err := r.db.Notifier.Query().
Where(notifier.GroupID(groupID), notifier.IsActive(true)).
Order(ent.Asc(notifier.FieldName)).
All(ctx)

return r.mapper.MapEachErr(notifier, err)
}

Expand Down
2 changes: 1 addition & 1 deletion backend/internal/data/repo/repos_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func New(db *ent.Client, root string) *AllRepos {
return &AllRepos{
Users: &UserRepository{db},
AuthTokens: &TokenRepository{db},
Groups: &GroupRepository{db},
Groups: NewGroupRepository(db),
Locations: &LocationRepository{db},
Labels: &LabelRepository{db},
Items: &ItemsRepository{db},
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Homebox is currently in early-active development and is currently in **beta** st
- Item Identifications (Serial, Model, etc)
- Categorized Attachments (Images, Manuals, General)
- Arbitrary/Custom Fields
- Csv Import for quickly creating and managing items
- CSV Import/Export for quickly creating and managing items
- Custom Reporting
- Bill of Materials Export
- QR Code Label Generator
Expand Down
11 changes: 7 additions & 4 deletions docs/docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
Great for testing out the application, but not recommended for stable use. Checkout the docker-compose for the recommended deployment.

```sh
docker run --name=homebox \
--restart=always \
--publish=3100:7745 \
ghcr.io/hay-kot/homebox:latest
docker run -d \
--name homebox \
--restart unless-stopped \
--publish 3100:7745 \
--env TZ=Europe/Bucharest \
--volume /path/to/data/folder/:/data \
ghcr.io/hay-kot/homebox:latest
```

## Docker-Compose
Expand Down