feat(http): implement missing findByID methods (#1635)

* feat(http): implement missing methods

* general cleanup
* unify param handling
* handle not found errors
* unify err handlers

* fix(http): fmt type
This commit is contained in:
ze0s 2024-08-29 12:22:03 +02:00 committed by GitHub
parent accc875960
commit acb91e8709
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 379 additions and 422 deletions

View file

@ -67,7 +67,12 @@ func (s *service) Store(ctx context.Context, apiKey *domain.APIKey) error {
}
func (s *service) Delete(ctx context.Context, key string) error {
err := s.repo.Delete(ctx, key)
_, err := s.repo.GetKey(ctx, key)
if err != nil {
return err
}
err = s.repo.Delete(ctx, key)
if err != nil {
return errors.Wrap(err, "could not delete api key: %s", key)
}

View file

@ -226,9 +226,7 @@ func (r *FeedRepo) Find(ctx context.Context) ([]domain.Feed, error) {
func (r *FeedRepo) GetLastRunDataByID(ctx context.Context, id int) (string, error) {
queryBuilder := r.db.squirrel.
Select(
"last_run_data",
).
Select("last_run_data").
From("feed").
Where(sq.Eq{"id": id})

View file

@ -331,7 +331,6 @@ func (repo *ReleaseRepo) FindRecent(ctx context.Context) ([]*domain.Release, err
}
func (repo *ReleaseRepo) GetIndexerOptions(ctx context.Context) ([]string, error) {
query := `SELECT DISTINCT indexer FROM "release" UNION SELECT DISTINCT identifier indexer FROM indexer;`
repo.log.Trace().Str("database", "release.get_indexers").Msgf("query: '%v'", query)
@ -363,7 +362,6 @@ func (repo *ReleaseRepo) GetIndexerOptions(ctx context.Context) ([]string, error
}
func (repo *ReleaseRepo) GetActionStatusByReleaseID(ctx context.Context, releaseID int64) ([]domain.ReleaseActionStatus, error) {
queryBuilder := repo.db.squirrel.
Select("id", "status", "action", "action_id", "type", "client", "filter", "release_id", "rejections", "timestamp").
From("release_action_status").

View file

@ -38,7 +38,7 @@ func (h actionHandler) Routes(r chi.Router) {
r.Get("/", h.getActions)
r.Post("/", h.storeAction)
r.Route("/{id}", func(r chi.Router) {
r.Route("/{actionID}", func(r chi.Router) {
r.Delete("/", h.deleteAction)
r.Put("/", h.updateAction)
r.Patch("/toggleEnabled", h.toggleActionEnabled)
@ -56,17 +56,13 @@ func (h actionHandler) getActions(w http.ResponseWriter, r *http.Request) {
}
func (h actionHandler) storeAction(w http.ResponseWriter, r *http.Request) {
var (
data domain.Action
ctx = r.Context()
)
var data domain.Action
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
action, err := h.service.Store(ctx, data)
action, err := h.service.Store(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -76,17 +72,13 @@ func (h actionHandler) storeAction(w http.ResponseWriter, r *http.Request) {
}
func (h actionHandler) updateAction(w http.ResponseWriter, r *http.Request) {
var (
data domain.Action
ctx = r.Context()
)
var data domain.Action
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
action, err := h.service.Store(ctx, data)
action, err := h.service.Store(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -96,7 +88,7 @@ func (h actionHandler) updateAction(w http.ResponseWriter, r *http.Request) {
}
func (h actionHandler) deleteAction(w http.ResponseWriter, r *http.Request) {
actionID, err := parseInt(chi.URLParam(r, "id"))
actionID, err := parseInt(chi.URLParam(r, "actionID"))
if err != nil {
h.encoder.StatusError(w, http.StatusBadRequest, errors.New("bad param id"))
return
@ -111,7 +103,7 @@ func (h actionHandler) deleteAction(w http.ResponseWriter, r *http.Request) {
}
func (h actionHandler) toggleActionEnabled(w http.ResponseWriter, r *http.Request) {
actionID, err := parseInt(chi.URLParam(r, "id"))
actionID, err := parseInt(chi.URLParam(r, "actionID"))
if err != nil {
h.encoder.StatusError(w, http.StatusBadRequest, errors.New("bad param id"))
return

View file

@ -9,6 +9,7 @@ import (
"net/http"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/errors"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
@ -51,7 +52,6 @@ func (h apikeyHandler) list(w http.ResponseWriter, r *http.Request) {
func (h apikeyHandler) store(w http.ResponseWriter, r *http.Request) {
var data domain.APIKey
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
@ -66,7 +66,14 @@ func (h apikeyHandler) store(w http.ResponseWriter, r *http.Request) {
}
func (h apikeyHandler) delete(w http.ResponseWriter, r *http.Request) {
if err := h.service.Delete(r.Context(), chi.URLParam(r, "apikey")); err != nil {
apiKey := chi.URLParam(r, "apikey")
if err := h.service.Delete(r.Context(), apiKey); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("api key %s not found", apiKey))
return
}
h.encoder.Error(w, err)
return
}

View file

@ -6,17 +6,18 @@ package http
import (
"context"
"encoding/json"
"errors"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/errors"
"github.com/go-chi/chi/v5"
)
type downloadClientService interface {
List(ctx context.Context) ([]domain.DownloadClient, error)
FindByID(ctx context.Context, id int32) (*domain.DownloadClient, error)
Store(ctx context.Context, client *domain.DownloadClient) error
Update(ctx context.Context, client *domain.DownloadClient) error
Delete(ctx context.Context, clientID int32) error
@ -40,13 +41,15 @@ func (h downloadClientHandler) Routes(r chi.Router) {
r.Post("/", h.store)
r.Put("/", h.update)
r.Post("/test", h.test)
r.Delete("/{clientID}", h.delete)
r.Route("/{clientID}", func(r chi.Router) {
r.Get("/", h.findByID)
r.Delete("/", h.delete)
})
}
func (h downloadClientHandler) listDownloadClients(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
clients, err := h.service.List(ctx)
clients, err := h.service.List(r.Context())
if err != nil {
h.encoder.Error(w, err)
return
@ -55,9 +58,28 @@ func (h downloadClientHandler) listDownloadClients(w http.ResponseWriter, r *htt
h.encoder.StatusResponse(w, http.StatusOK, clients)
}
func (h downloadClientHandler) findByID(w http.ResponseWriter, r *http.Request) {
clientID, err := strconv.ParseInt(chi.URLParam(r, "clientID"), 10, 32)
if err != nil {
h.encoder.Error(w, err)
return
}
client, err := h.service.FindByID(r.Context(), int32(clientID))
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("download client with id %d not found", clientID))
}
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusOK, client)
}
func (h downloadClientHandler) store(w http.ResponseWriter, r *http.Request) {
var data *domain.DownloadClient
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
@ -74,7 +96,6 @@ func (h downloadClientHandler) store(w http.ResponseWriter, r *http.Request) {
func (h downloadClientHandler) test(w http.ResponseWriter, r *http.Request) {
var data domain.DownloadClient
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
@ -90,7 +111,6 @@ func (h downloadClientHandler) test(w http.ResponseWriter, r *http.Request) {
func (h downloadClientHandler) update(w http.ResponseWriter, r *http.Request) {
var data *domain.DownloadClient
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
@ -106,20 +126,13 @@ func (h downloadClientHandler) update(w http.ResponseWriter, r *http.Request) {
}
func (h downloadClientHandler) delete(w http.ResponseWriter, r *http.Request) {
var clientID = chi.URLParam(r, "clientID")
if clientID == "" {
h.encoder.Error(w, errors.New("no clientID given"))
return
}
id, err := strconv.ParseInt(clientID, 10, 32)
clientID, err := strconv.ParseInt(chi.URLParam(r, "clientID"), 10, 32)
if err != nil {
h.encoder.Error(w, err)
return
}
if err = h.service.Delete(r.Context(), int32(id)); err != nil {
if err = h.service.Delete(r.Context(), int32(clientID)); err != nil {
h.encoder.Error(w, err)
return
}

View file

@ -20,7 +20,7 @@ type statusResponse struct {
Status int `json:"status,omitempty"`
}
func (e encoder) StatusResponse(w http.ResponseWriter, status int, response interface{}) {
func (e encoder) StatusResponse(w http.ResponseWriter, status int, response any) {
if response != nil {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
@ -52,7 +52,7 @@ func (e encoder) StatusCreated(w http.ResponseWriter) {
w.WriteHeader(http.StatusCreated)
}
func (e encoder) StatusCreatedData(w http.ResponseWriter, data interface{}) {
func (e encoder) StatusCreatedData(w http.ResponseWriter, data any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusCreated)

View file

@ -6,6 +6,7 @@ package http
import (
"context"
"encoding/json"
"github.com/autobrr/autobrr/pkg/errors"
"net/http"
"strconv"
@ -16,6 +17,7 @@ import (
type feedService interface {
Find(ctx context.Context) ([]domain.Feed, error)
FindByID(ctx context.Context, id int) (*domain.Feed, error)
Store(ctx context.Context, feed *domain.Feed) error
Update(ctx context.Context, feed *domain.Feed) error
Delete(ctx context.Context, id int) error
@ -44,6 +46,7 @@ func (h feedHandler) Routes(r chi.Router) {
r.Post("/test", h.test)
r.Route("/{feedID}", func(r chi.Router) {
r.Get("/", h.findByID)
r.Put("/", h.update)
r.Delete("/", h.delete)
r.Delete("/cache", h.deleteCache)
@ -54,29 +57,44 @@ func (h feedHandler) Routes(r chi.Router) {
}
func (h feedHandler) find(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
feeds, err := h.service.Find(ctx)
feeds, err := h.service.Find(r.Context())
if err != nil {
h.encoder.StatusNotFound(w)
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusOK, feeds)
}
func (h feedHandler) store(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data *domain.Feed
)
func (h feedHandler) findByID(w http.ResponseWriter, r *http.Request) {
feedID, err := strconv.Atoi(chi.URLParam(r, "feedID"))
if err != nil {
h.encoder.Error(w, err)
return
}
feed, err := h.service.FindByID(r.Context(), feedID)
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("could not find feed with id %d", feedID))
return
}
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusOK, feed)
}
func (h feedHandler) store(w http.ResponseWriter, r *http.Request) {
var data *domain.Feed
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
err := h.service.Store(ctx, data)
err := h.service.Store(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -86,17 +104,13 @@ func (h feedHandler) store(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) test(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data *domain.Feed
)
var data *domain.Feed
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Test(ctx, data); err != nil {
if err := h.service.Test(r.Context(), data); err != nil {
h.encoder.Error(w, err)
return
}
@ -105,17 +119,13 @@ func (h feedHandler) test(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) update(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data *domain.Feed
)
var data *domain.Feed
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
err := h.service.Update(ctx, data)
err := h.service.Update(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -125,16 +135,18 @@ func (h feedHandler) update(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) forceRun(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
feedID := chi.URLParam(r, "feedID")
id, err := strconv.Atoi(feedID)
feedID, err := strconv.Atoi(chi.URLParam(r, "feedID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.ForceRun(ctx, id); err != nil {
if err := h.service.ForceRun(r.Context(), feedID); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("could not find feed with id %d", feedID))
return
}
h.encoder.Error(w, err)
return
}
@ -143,26 +155,27 @@ func (h feedHandler) forceRun(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) toggleEnabled(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "feedID")
data struct {
Enabled bool `json:"enabled"`
}
)
id, err := strconv.Atoi(filterID)
feedID, err := strconv.Atoi(chi.URLParam(r, "feedID"))
if err != nil {
h.encoder.Error(w, err)
return
}
var data struct {
Enabled bool `json:"enabled"`
}
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.ToggleEnabled(ctx, id, data.Enabled); err != nil {
if err := h.service.ToggleEnabled(r.Context(), feedID, data.Enabled); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("could not find feed with id %d", feedID))
return
}
h.encoder.Error(w, err)
return
}
@ -171,18 +184,18 @@ func (h feedHandler) toggleEnabled(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) delete(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "feedID")
)
id, err := strconv.Atoi(filterID)
feedID, err := strconv.Atoi(chi.URLParam(r, "feedID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Delete(ctx, id); err != nil {
if err := h.service.Delete(r.Context(), feedID); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("could not find feed with id %d", feedID))
return
}
h.encoder.Error(w, err)
return
}
@ -191,18 +204,13 @@ func (h feedHandler) delete(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) deleteCache(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "feedID")
)
id, err := strconv.Atoi(filterID)
feedID, err := strconv.Atoi(chi.URLParam(r, "feedID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.DeleteFeedCache(ctx, id); err != nil {
if err := h.service.DeleteFeedCache(r.Context(), feedID); err != nil {
h.encoder.Error(w, err)
return
}
@ -211,18 +219,13 @@ func (h feedHandler) deleteCache(w http.ResponseWriter, r *http.Request) {
}
func (h feedHandler) latestRun(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "feedID")
)
id, err := strconv.Atoi(filterID)
feedID, err := strconv.Atoi(chi.URLParam(r, "feedID"))
if err != nil {
h.encoder.Error(w, err)
return
}
feed, err := h.service.GetLastRunData(ctx, id)
feed, err := h.service.GetLastRunData(r.Context(), feedID)
if err != nil {
h.encoder.Error(w, err)
return

View file

@ -11,10 +11,10 @@ import (
"strconv"
"strings"
"github.com/go-chi/chi/v5"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/errors"
"github.com/go-chi/chi/v5"
)
type filterService interface {
@ -57,8 +57,6 @@ func (h filterHandler) Routes(r chi.Router) {
}
func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
params := domain.FilterQueryParams{
Sort: map[string]string{},
Filters: struct {
@ -86,7 +84,7 @@ func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) {
u, err := url.Parse(r.URL.String())
if err != nil {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": "indexer parameter is invalid",
})
@ -95,7 +93,7 @@ func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) {
vals := u.Query()
params.Filters.Indexers = vals["indexer"]
trackers, err := h.service.Find(ctx, params)
trackers, err := h.service.Find(r.Context(), params)
if err != nil {
h.encoder.Error(w, err)
return
@ -105,21 +103,16 @@ func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) getByID(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "filterID")
)
id, err := strconv.Atoi(filterID)
filterID, err := strconv.Atoi(chi.URLParam(r, "filterID"))
if err != nil {
h.encoder.Error(w, err)
return
}
filter, err := h.service.FindByID(ctx, id)
filter, err := h.service.FindByID(r.Context(), filterID)
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("filter with id %d not found", id))
h.encoder.NotFoundErr(w, errors.New("filter with id %d not found", filterID))
return
}
@ -131,20 +124,20 @@ func (h filterHandler) getByID(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) duplicate(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "filterID")
)
id, err := strconv.Atoi(filterID)
filterID, err := strconv.Atoi(chi.URLParam(r, "filterID"))
if err != nil {
h.encoder.Error(w, err)
return
}
filter, err := h.service.Duplicate(ctx, id)
filter, err := h.service.Duplicate(r.Context(), filterID)
if err != nil {
h.encoder.StatusInternalError(w)
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("filter with id %d not found", filterID))
return
}
h.encoder.Error(w, err)
return
}
@ -152,17 +145,13 @@ func (h filterHandler) duplicate(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) store(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data *domain.Filter
)
var data *domain.Filter
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Store(ctx, data); err != nil {
if err := h.service.Store(r.Context(), data); err != nil {
h.encoder.Error(w, err)
return
}
@ -171,17 +160,13 @@ func (h filterHandler) store(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) update(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data *domain.Filter
)
var data *domain.Filter
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Update(ctx, data); err != nil {
if err := h.service.Update(r.Context(), data); err != nil {
h.encoder.Error(w, err)
return
}
@ -190,26 +175,20 @@ func (h filterHandler) update(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) updatePartial(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.FilterUpdate
filterID = chi.URLParam(r, "filterID")
)
// set id from param and convert to int
id, err := strconv.Atoi(filterID)
var data domain.FilterUpdate
filterID, err := strconv.Atoi(chi.URLParam(r, "filterID"))
if err != nil {
h.encoder.Error(w, err)
return
}
data.ID = id
data.ID = filterID
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.UpdatePartial(ctx, data); err != nil {
if err := h.service.UpdatePartial(r.Context(), data); err != nil {
h.encoder.Error(w, err)
return
}
@ -218,26 +197,22 @@ func (h filterHandler) updatePartial(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) toggleEnabled(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "filterID")
data struct {
Enabled bool `json:"enabled"`
}
)
id, err := strconv.Atoi(filterID)
filterID, err := strconv.Atoi(chi.URLParam(r, "filterID"))
if err != nil {
h.encoder.Error(w, err)
return
}
var data struct {
Enabled bool `json:"enabled"`
}
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.ToggleEnabled(ctx, id, data.Enabled); err != nil {
if err := h.service.ToggleEnabled(r.Context(), filterID, data.Enabled); err != nil {
h.encoder.Error(w, err)
return
}
@ -246,19 +221,15 @@ func (h filterHandler) toggleEnabled(w http.ResponseWriter, r *http.Request) {
}
func (h filterHandler) delete(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "filterID")
)
id, err := strconv.Atoi(filterID)
filterID, err := strconv.Atoi(chi.URLParam(r, "filterID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Delete(ctx, id); err != nil {
if err := h.service.Delete(r.Context(), filterID); err != nil {
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusNoContent, nil)

View file

@ -10,6 +10,7 @@ import (
"strconv"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/errors"
"github.com/go-chi/chi/v5"
)
@ -18,6 +19,7 @@ type indexerService interface {
Store(ctx context.Context, indexer domain.Indexer) (*domain.Indexer, error)
Update(ctx context.Context, indexer domain.Indexer) (*domain.Indexer, error)
List(ctx context.Context) ([]domain.Indexer, error)
FindByID(ctx context.Context, id int) (*domain.Indexer, error)
GetAll() ([]*domain.IndexerDefinition, error)
GetTemplates() ([]domain.IndexerDefinition, error)
Delete(ctx context.Context, id int) error
@ -46,6 +48,7 @@ func (h indexerHandler) Routes(r chi.Router) {
r.Get("/options", h.list)
r.Route("/{indexerID}", func(r chi.Router) {
r.Get("/", h.findByID)
r.Put("/", h.update)
r.Delete("/", h.delete)
r.Post("/api/test", h.testApi)
@ -65,17 +68,13 @@ func (h indexerHandler) getSchema(w http.ResponseWriter, r *http.Request) {
}
func (h indexerHandler) store(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.Indexer
)
var data domain.Indexer
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
indexer, err := h.service.Store(ctx, data)
indexer, err := h.service.Store(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -85,17 +84,13 @@ func (h indexerHandler) store(w http.ResponseWriter, r *http.Request) {
}
func (h indexerHandler) update(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.Indexer
)
var data domain.Indexer
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
indexer, err := h.service.Update(ctx, data)
indexer, err := h.service.Update(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -105,18 +100,13 @@ func (h indexerHandler) update(w http.ResponseWriter, r *http.Request) {
}
func (h indexerHandler) delete(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
idParam = chi.URLParam(r, "indexerID")
)
id, err := strconv.Atoi(idParam)
indexerID, err := strconv.Atoi(chi.URLParam(r, "indexerID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Delete(ctx, id); err != nil {
if err := h.service.Delete(r.Context(), indexerID); err != nil {
h.encoder.Error(w, err)
return
}
@ -135,9 +125,7 @@ func (h indexerHandler) getAll(w http.ResponseWriter, r *http.Request) {
}
func (h indexerHandler) list(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
indexers, err := h.service.List(ctx)
indexers, err := h.service.List(r.Context())
if err != nil {
h.encoder.Error(w, err)
return
@ -146,29 +134,45 @@ func (h indexerHandler) list(w http.ResponseWriter, r *http.Request) {
h.encoder.StatusResponse(w, http.StatusOK, indexers)
}
func (h indexerHandler) testApi(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
idParam = chi.URLParam(r, "indexerID")
req domain.IndexerTestApiRequest
)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.encoder.Error(w, err)
return
}
id, err := strconv.Atoi(idParam)
func (h indexerHandler) findByID(w http.ResponseWriter, r *http.Request) {
indexerID, err := strconv.Atoi(chi.URLParam(r, "indexerID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if req.IndexerId == 0 {
req.IndexerId = id
indexer, err := h.service.FindByID(r.Context(), indexerID)
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("indexer with id %d not found", indexerID))
return
}
if err := h.service.TestApi(ctx, req); err != nil {
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusOK, indexer)
}
func (h indexerHandler) testApi(w http.ResponseWriter, r *http.Request) {
indexerID, err := strconv.Atoi(chi.URLParam(r, "indexerID"))
if err != nil {
h.encoder.Error(w, err)
return
}
var req domain.IndexerTestApiRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
h.encoder.Error(w, err)
return
}
if req.IndexerId == 0 {
req.IndexerId = indexerID
}
if err := h.service.TestApi(r.Context(), req); err != nil {
h.encoder.Error(w, err)
return
}
@ -183,26 +187,22 @@ func (h indexerHandler) testApi(w http.ResponseWriter, r *http.Request) {
}
func (h indexerHandler) toggleEnabled(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
indexerID = chi.URLParam(r, "indexerID")
data struct {
Enabled bool `json:"enabled"`
}
)
id, err := strconv.Atoi(indexerID)
indexerID, err := strconv.Atoi(chi.URLParam(r, "indexerID"))
if err != nil {
h.encoder.Error(w, err)
return
}
var data struct {
Enabled bool `json:"enabled"`
}
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.ToggleEnabled(ctx, id, data.Enabled); err != nil {
if err := h.service.ToggleEnabled(r.Context(), indexerID, data.Enabled); err != nil {
h.encoder.Error(w, err)
return
}

View file

@ -7,6 +7,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/autobrr/autobrr/pkg/errors"
"net/http"
"strconv"
"strings"
@ -76,30 +77,29 @@ func (h ircHandler) Routes(r chi.Router) {
}
func (h ircHandler) listNetworks(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
networks, err := h.service.GetNetworksWithHealth(ctx)
networks, err := h.service.GetNetworksWithHealth(r.Context())
if err != nil {
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusOK, networks)
}
func (h ircHandler) getNetworkByID(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
networkID = chi.URLParam(r, "networkID")
)
id, err := strconv.Atoi(networkID)
networkID, err := strconv.Atoi(chi.URLParam(r, "networkID"))
if err != nil {
h.encoder.Error(w, err)
return
}
network, err := h.service.GetNetworkByID(ctx, int64(id))
network, err := h.service.GetNetworkByID(r.Context(), int64(networkID))
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("network with id %d not found", networkID))
return
}
h.encoder.Error(w, err)
return
}
@ -108,18 +108,18 @@ func (h ircHandler) getNetworkByID(w http.ResponseWriter, r *http.Request) {
}
func (h ircHandler) restartNetwork(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
networkID = chi.URLParam(r, "networkID")
)
id, err := strconv.Atoi(networkID)
networkID, err := strconv.Atoi(chi.URLParam(r, "networkID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.RestartNetwork(ctx, int64(id)); err != nil {
if err := h.service.RestartNetwork(r.Context(), int64(networkID)); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("network with id %d not found", networkID))
return
}
h.encoder.Error(w, err)
return
}
@ -129,7 +129,6 @@ func (h ircHandler) restartNetwork(w http.ResponseWriter, r *http.Request) {
func (h ircHandler) storeNetwork(w http.ResponseWriter, r *http.Request) {
var data domain.IrcNetwork
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
@ -144,17 +143,13 @@ func (h ircHandler) storeNetwork(w http.ResponseWriter, r *http.Request) {
}
func (h ircHandler) updateNetwork(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.IrcNetwork
)
var data domain.IrcNetwork
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.UpdateNetwork(ctx, &data); err != nil {
if err := h.service.UpdateNetwork(r.Context(), &data); err != nil {
h.encoder.Error(w, err)
return
}
@ -163,26 +158,21 @@ func (h ircHandler) updateNetwork(w http.ResponseWriter, r *http.Request) {
}
func (h ircHandler) sendCmd(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
networkID = chi.URLParam(r, "networkID")
data domain.SendIrcCmdRequest
)
id, err := strconv.Atoi(networkID)
networkID, err := strconv.Atoi(chi.URLParam(r, "networkID"))
if err != nil {
h.encoder.Error(w, err)
return
}
var data domain.SendIrcCmdRequest
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
data.NetworkId = int64(id)
data.NetworkId = int64(networkID)
if err := h.service.SendCmd(ctx, &data); err != nil {
if err := h.service.SendCmd(r.Context(), &data); err != nil {
h.encoder.Error(w, err)
return
}
@ -192,49 +182,32 @@ func (h ircHandler) sendCmd(w http.ResponseWriter, r *http.Request) {
// announceProcess manually trigger announce process
func (h ircHandler) announceProcess(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.IRCManualProcessRequest
)
paramNetworkID := chi.URLParam(r, "networkID")
if paramNetworkID == "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
"code": "BAD_REQUEST_PARAMS",
"message": "parameter networkID missing",
})
return
}
networkID, err := strconv.Atoi(paramNetworkID)
networkID, err := strconv.Atoi(chi.URLParam(r, "networkID"))
if err != nil {
h.encoder.Error(w, err)
return
}
paramChannel := chi.URLParam(r, "channel")
if paramChannel == "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
"code": "BAD_REQUEST_PARAMS",
"message": "parameter channel missing",
})
return
}
var data domain.IRCManualProcessRequest
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
data.NetworkId = int64(networkID)
data.Channel = paramChannel
data.Channel = chi.URLParam(r, "channel")
// we cant pass # as an url parameter so the frontend has to strip it
if !strings.HasPrefix("#", data.Channel) {
if !strings.HasPrefix(data.Channel, "#") {
data.Channel = fmt.Sprintf("#%s", data.Channel)
}
if err := h.service.ManualProcessAnnounce(ctx, &data); err != nil {
if err := h.service.ManualProcessAnnounce(r.Context(), &data); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("network with id %d not found", data.NetworkId))
return
}
h.encoder.Error(w, err)
return
}
@ -243,24 +216,19 @@ func (h ircHandler) announceProcess(w http.ResponseWriter, r *http.Request) {
}
func (h ircHandler) storeChannel(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
networkID = chi.URLParam(r, "networkID")
data domain.IrcChannel
)
id, err := strconv.Atoi(networkID)
networkID, err := strconv.Atoi(chi.URLParam(r, "networkID"))
if err != nil {
h.encoder.Error(w, err)
return
}
var data domain.IrcChannel
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.StoreChannel(ctx, int64(id), &data); err != nil {
if err := h.service.StoreChannel(r.Context(), int64(networkID), &data); err != nil {
h.encoder.Error(w, err)
return
}
@ -269,18 +237,18 @@ func (h ircHandler) storeChannel(w http.ResponseWriter, r *http.Request) {
}
func (h ircHandler) deleteNetwork(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
networkID = chi.URLParam(r, "networkID")
)
id, err := strconv.Atoi(networkID)
networkID, err := strconv.Atoi(chi.URLParam(r, "networkID"))
if err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.DeleteNetwork(ctx, int64(id)); err != nil {
if err := h.service.DeleteNetwork(r.Context(), int64(networkID)); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("network with id %d does not exist", networkID))
return
}
h.encoder.Error(w, err)
return
}

View file

@ -39,7 +39,7 @@ func (h logsHandler) Routes(r chi.Router) {
func (h logsHandler) files(w http.ResponseWriter, r *http.Request) {
response := LogfilesResponse{
Files: []logFile{},
Files: []LogFile{},
Count: 0,
}
@ -67,7 +67,7 @@ func (h logsHandler) files(w http.ResponseWriter, r *http.Request) {
return err
}
response.Files = append(response.Files, logFile{
response.Files = append(response.Files, LogFile{
Name: d.Name(),
SizeBytes: i.Size(),
Size: humanize.Bytes(uint64(i.Size())),
@ -218,17 +218,11 @@ func (h logsHandler) downloadFile(w http.ResponseWriter, r *http.Request) {
}
logFile := chi.URLParam(r, "logFile")
if logFile == "" {
if !strings.Contains(logFile, ".log") {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, errorResponse{
Message: "empty log file",
Status: http.StatusBadRequest,
})
return
} else if !strings.Contains(logFile, ".log") {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, errorResponse{
Message: "invalid file",
Message: "invalid log file. Must have .log extension",
Status: http.StatusBadRequest,
})
return
@ -250,7 +244,7 @@ func (h logsHandler) downloadFile(w http.ResponseWriter, r *http.Request) {
}
}
type logFile struct {
type LogFile struct {
Name string `json:"filename"`
SizeBytes int64 `json:"size_bytes"`
Size string `json:"size"`
@ -258,6 +252,6 @@ type logFile struct {
}
type LogfilesResponse struct {
Files []logFile `json:"files"`
Files []LogFile `json:"files"`
Count int `json:"count"`
}

View file

@ -6,6 +6,7 @@ package http
import (
"context"
"encoding/json"
"github.com/autobrr/autobrr/pkg/errors"
"net/http"
"strconv"
@ -41,15 +42,14 @@ func (h notificationHandler) Routes(r chi.Router) {
r.Post("/test", h.test)
r.Route("/{notificationID}", func(r chi.Router) {
r.Get("/", h.findByID)
r.Put("/", h.update)
r.Delete("/", h.delete)
})
}
func (h notificationHandler) list(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
list, _, err := h.service.Find(ctx, domain.NotificationQueryParams{})
list, _, err := h.service.Find(r.Context(), domain.NotificationQueryParams{})
if err != nil {
h.encoder.StatusNotFound(w)
return
@ -59,17 +59,13 @@ func (h notificationHandler) list(w http.ResponseWriter, r *http.Request) {
}
func (h notificationHandler) store(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.Notification
)
var data domain.Notification
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
filter, err := h.service.Store(ctx, data)
filter, err := h.service.Store(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -78,18 +74,35 @@ func (h notificationHandler) store(w http.ResponseWriter, r *http.Request) {
h.encoder.StatusResponse(w, http.StatusCreated, filter)
}
func (h notificationHandler) update(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.Notification
)
func (h notificationHandler) findByID(w http.ResponseWriter, r *http.Request) {
notificationID, err := strconv.Atoi(chi.URLParam(r, "notificationID"))
if err != nil {
h.encoder.Error(w, err)
return
}
notification, err := h.service.FindByID(r.Context(), notificationID)
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("notification with id %d not found", notificationID))
return
}
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusNoContent, notification)
}
func (h notificationHandler) update(w http.ResponseWriter, r *http.Request) {
var data domain.Notification
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
filter, err := h.service.Update(ctx, data)
filter, err := h.service.Update(r.Context(), data)
if err != nil {
h.encoder.Error(w, err)
return
@ -99,14 +112,13 @@ func (h notificationHandler) update(w http.ResponseWriter, r *http.Request) {
}
func (h notificationHandler) delete(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
notificationID = chi.URLParam(r, "notificationID")
)
notificationID, err := strconv.Atoi(chi.URLParam(r, "notificationID"))
if err != nil {
h.encoder.Error(w, err)
return
}
id, _ := strconv.Atoi(notificationID)
if err := h.service.Delete(ctx, id); err != nil {
if err := h.service.Delete(r.Context(), notificationID); err != nil {
h.encoder.Error(w, err)
return
}
@ -115,17 +127,13 @@ func (h notificationHandler) delete(w http.ResponseWriter, r *http.Request) {
}
func (h notificationHandler) test(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
data domain.Notification
)
var data domain.Notification
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
h.encoder.Error(w, err)
return
}
if err := h.service.Test(ctx, data); err != nil {
if err := h.service.Test(r.Context(), data); err != nil {
h.encoder.Error(w, err)
return
}

View file

@ -12,6 +12,7 @@ import (
"strconv"
"github.com/autobrr/autobrr/internal/domain"
"github.com/autobrr/autobrr/pkg/errors"
"github.com/go-chi/chi/v5"
)
@ -19,6 +20,7 @@ import (
type releaseService interface {
Find(ctx context.Context, query domain.ReleaseQueryParams) (res []*domain.Release, nextCursor int64, count int64, err error)
FindRecent(ctx context.Context) (res []*domain.Release, err error)
Get(ctx context.Context, req *domain.GetReleaseRequest) (*domain.Release, error)
GetIndexerOptions(ctx context.Context) ([]string, error)
Stats(ctx context.Context) (*domain.ReleaseStats, error)
Delete(ctx context.Context, req *domain.DeleteReleaseRequest) error
@ -45,19 +47,19 @@ func (h releaseHandler) Routes(r chi.Router) {
r.Get("/indexers", h.getIndexerOptions)
r.Delete("/", h.deleteReleases)
r.Post("/process", h.retryAction)
//r.Post("/process", h.retryAction)
r.Route("/{releaseId}", func(r chi.Router) {
r.Post("/actions/{actionStatusId}/retry", h.retryAction)
r.Route("/{releaseID}", func(r chi.Router) {
r.Get("/", h.getReleaseByID)
r.Post("/actions/{actionStatusID}/retry", h.retryAction)
})
}
func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
limitP := r.URL.Query().Get("limit")
limit, err := strconv.Atoi(limitP)
if err != nil && limitP != "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": "limit parameter is invalid",
})
@ -70,7 +72,7 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
offsetP := r.URL.Query().Get("offset")
offset, err := strconv.Atoi(offsetP)
if err != nil && offsetP != "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": "offset parameter is invalid",
})
@ -82,7 +84,7 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
if cursorP != "" {
cursor, err = strconv.Atoi(cursorP)
if err != nil && cursorP != "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": "cursor parameter is invalid",
})
@ -92,7 +94,7 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
u, err := url.Parse(r.URL.String())
if err != nil {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": "indexer parameter is invalid",
})
@ -104,7 +106,7 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
pushStatus := r.URL.Query().Get("push_status")
if pushStatus != "" {
if !domain.ValidReleasePushStatus(pushStatus) {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": fmt.Sprintf("push_status parameter is of invalid type: %v", pushStatus),
})
@ -128,7 +130,7 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
releases, nextCursor, count, err := h.service.Find(r.Context(), query)
if err != nil {
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]any{
"code": "INTERNAL_SERVER_ERROR",
"message": err.Error(),
})
@ -149,10 +151,9 @@ func (h releaseHandler) findReleases(w http.ResponseWriter, r *http.Request) {
}
func (h releaseHandler) findRecentReleases(w http.ResponseWriter, r *http.Request) {
releases, err := h.service.FindRecent(r.Context())
if err != nil {
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]any{
"code": "INTERNAL_SERVER_ERROR",
"message": err.Error(),
})
@ -168,10 +169,31 @@ func (h releaseHandler) findRecentReleases(w http.ResponseWriter, r *http.Reques
h.encoder.StatusResponse(w, http.StatusOK, ret)
}
func (h releaseHandler) getReleaseByID(w http.ResponseWriter, r *http.Request) {
releaseID, err := strconv.Atoi(chi.URLParam(r, "releaseID"))
if err != nil {
h.encoder.Error(w, err)
return
}
release, err := h.service.Get(r.Context(), &domain.GetReleaseRequest{Id: releaseID})
if err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, errors.New("could not find release with id %d", releaseID))
return
}
h.encoder.Error(w, err)
return
}
h.encoder.StatusResponse(w, http.StatusOK, release)
}
func (h releaseHandler) getIndexerOptions(w http.ResponseWriter, r *http.Request) {
stats, err := h.service.GetIndexerOptions(r.Context())
if err != nil {
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]any{
"code": "INTERNAL_SERVER_ERROR",
"message": err.Error(),
})
@ -182,10 +204,9 @@ func (h releaseHandler) getIndexerOptions(w http.ResponseWriter, r *http.Request
}
func (h releaseHandler) getStats(w http.ResponseWriter, r *http.Request) {
stats, err := h.service.Stats(r.Context())
if err != nil {
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusInternalServerError, map[string]any{
"code": "INTERNAL_SERVER_ERROR",
"message": err.Error(),
})
@ -202,7 +223,7 @@ func (h releaseHandler) deleteReleases(w http.ResponseWriter, r *http.Request) {
if olderThanParam != "" {
duration, err := strconv.Atoi(olderThanParam)
if err != nil {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "BAD_REQUEST_PARAMS",
"message": "olderThan parameter is invalid",
})
@ -227,7 +248,7 @@ func (h releaseHandler) deleteReleases(w http.ResponseWriter, r *http.Request) {
if _, valid := validStatuses[status]; valid {
filteredStatuses = append(filteredStatuses, status)
} else {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "INVALID_RELEASE_STATUS",
"message": "releaseStatus contains invalid value",
})
@ -246,7 +267,6 @@ func (h releaseHandler) deleteReleases(w http.ResponseWriter, r *http.Request) {
func (h releaseHandler) process(w http.ResponseWriter, r *http.Request) {
var req *domain.ReleaseProcessReq
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
h.encoder.Error(w, err)
@ -254,14 +274,14 @@ func (h releaseHandler) process(w http.ResponseWriter, r *http.Request) {
}
if req.IndexerIdentifier == "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "VALIDATION_ERROR",
"message": "field indexer_identifier empty",
})
}
if len(req.AnnounceLines) == 0 {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]any{
"code": "VALIDATION_ERROR",
"message": "field announce_lines empty",
})
@ -277,47 +297,29 @@ func (h releaseHandler) process(w http.ResponseWriter, r *http.Request) {
}
func (h releaseHandler) retryAction(w http.ResponseWriter, r *http.Request) {
var (
req *domain.ReleaseActionRetryReq
err error
)
releaseIdParam := chi.URLParam(r, "releaseId")
if releaseIdParam == "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
"code": "BAD_REQUEST_PARAMS",
"message": "parameter releaseId missing",
})
return
}
releaseId, err := strconv.Atoi(releaseIdParam)
releaseID, err := strconv.Atoi(chi.URLParam(r, "releaseID"))
if err != nil {
h.encoder.StatusError(w, http.StatusBadRequest, err)
return
}
actionStatusIdParam := chi.URLParam(r, "actionStatusId")
if actionStatusIdParam == "" {
h.encoder.StatusResponse(w, http.StatusBadRequest, map[string]interface{}{
"code": "BAD_REQUEST_PARAMS",
"message": "parameter actionStatusId missing",
})
return
}
actionStatusId, err := strconv.Atoi(actionStatusIdParam)
actionStatusId, err := strconv.Atoi(chi.URLParam(r, "actionStatusID"))
if err != nil {
h.encoder.StatusError(w, http.StatusBadRequest, err)
return
}
req = &domain.ReleaseActionRetryReq{
ReleaseId: releaseId,
req := &domain.ReleaseActionRetryReq{
ReleaseId: releaseID,
ActionStatusId: actionStatusId,
}
if err := h.service.Retry(r.Context(), req); err != nil {
if errors.Is(err, domain.ErrRecordNotFound) {
h.encoder.NotFoundErr(w, err)
return
}
h.encoder.Error(w, err)
return
}

View file

@ -29,14 +29,14 @@ type Service interface {
type service struct {
log zerolog.Logger
repo domain.NotificationRepo
senders []domain.NotificationSender
senders map[int]domain.NotificationSender
}
func NewService(log logger.Logger, repo domain.NotificationRepo) Service {
s := &service{
log: log.With().Str("module", "notification").Logger(),
repo: repo,
senders: []domain.NotificationSender{},
senders: make(map[int]domain.NotificationSender),
}
s.registerSenders()
@ -45,53 +45,47 @@ func NewService(log logger.Logger, repo domain.NotificationRepo) Service {
}
func (s *service) Find(ctx context.Context, params domain.NotificationQueryParams) ([]domain.Notification, int, error) {
n, count, err := s.repo.Find(ctx, params)
notifications, count, err := s.repo.Find(ctx, params)
if err != nil {
s.log.Error().Err(err).Msgf("could not find notification with params: %+v", params)
return nil, 0, err
}
return n, count, err
return notifications, count, err
}
func (s *service) FindByID(ctx context.Context, id int) (*domain.Notification, error) {
n, err := s.repo.FindByID(ctx, id)
notification, err := s.repo.FindByID(ctx, id)
if err != nil {
s.log.Error().Err(err).Msgf("could not find notification by id: %v", id)
return nil, err
}
return n, err
return notification, err
}
func (s *service) Store(ctx context.Context, n domain.Notification) (*domain.Notification, error) {
_, err := s.repo.Store(ctx, n)
func (s *service) Store(ctx context.Context, notification domain.Notification) (*domain.Notification, error) {
_, err := s.repo.Store(ctx, notification)
if err != nil {
s.log.Error().Err(err).Msgf("could not store notification: %+v", n)
s.log.Error().Err(err).Msgf("could not store notification: %+v", notification)
return nil, err
}
// reset senders
s.senders = []domain.NotificationSender{}
// re register senders
s.registerSenders()
// register sender
s.registerSender(notification)
return nil, nil
}
func (s *service) Update(ctx context.Context, n domain.Notification) (*domain.Notification, error) {
_, err := s.repo.Update(ctx, n)
func (s *service) Update(ctx context.Context, notification domain.Notification) (*domain.Notification, error) {
_, err := s.repo.Update(ctx, notification)
if err != nil {
s.log.Error().Err(err).Msgf("could not update notification: %+v", n)
s.log.Error().Err(err).Msgf("could not update notification: %+v", notification)
return nil, err
}
// reset senders
s.senders = []domain.NotificationSender{}
// re register senders
s.registerSenders()
// register sender
s.registerSender(notification)
return nil, nil
}
@ -103,42 +97,46 @@ func (s *service) Delete(ctx context.Context, id int) error {
return err
}
// reset senders
s.senders = []domain.NotificationSender{}
// re register senders
s.registerSenders()
// delete sender
delete(s.senders, id)
return nil
}
func (s *service) registerSenders() {
senders, err := s.repo.List(context.Background())
notificationSenders, err := s.repo.List(context.Background())
if err != nil {
s.log.Error().Err(err).Msg("could not find notifications")
return
}
for _, n := range senders {
if n.Enabled {
switch n.Type {
case domain.NotificationTypeDiscord:
s.senders = append(s.senders, NewDiscordSender(s.log, n))
case domain.NotificationTypeGotify:
s.senders = append(s.senders, NewGotifySender(s.log, n))
case domain.NotificationTypeLunaSea:
s.senders = append(s.senders, NewLunaSeaSender(s.log, n))
case domain.NotificationTypeNotifiarr:
s.senders = append(s.senders, NewNotifiarrSender(s.log, n))
case domain.NotificationTypeNtfy:
s.senders = append(s.senders, NewNtfySender(s.log, n))
case domain.NotificationTypePushover:
s.senders = append(s.senders, NewPushoverSender(s.log, n))
case domain.NotificationTypeShoutrrr:
s.senders = append(s.senders, NewShoutrrrSender(s.log, n))
case domain.NotificationTypeTelegram:
s.senders = append(s.senders, NewTelegramSender(s.log, n))
for _, notificationSender := range notificationSenders {
s.registerSender(notificationSender)
}
return
}
// registerSender registers an enabled notification via it's id
func (s *service) registerSender(notification domain.Notification) {
if notification.Enabled {
switch notification.Type {
case domain.NotificationTypeDiscord:
s.senders[notification.ID] = NewDiscordSender(s.log, notification)
case domain.NotificationTypeGotify:
s.senders[notification.ID] = NewGotifySender(s.log, notification)
case domain.NotificationTypeLunaSea:
s.senders[notification.ID] = NewLunaSeaSender(s.log, notification)
case domain.NotificationTypeNotifiarr:
s.senders[notification.ID] = NewNotifiarrSender(s.log, notification)
case domain.NotificationTypeNtfy:
s.senders[notification.ID] = NewNtfySender(s.log, notification)
case domain.NotificationTypePushover:
s.senders[notification.ID] = NewPushoverSender(s.log, notification)
case domain.NotificationTypeShoutrrr:
s.senders[notification.ID] = NewShoutrrrSender(s.log, notification)
case domain.NotificationTypeTelegram:
s.senders[notification.ID] = NewTelegramSender(s.log, notification)
}
}