4ee647a951
This change adds request IDs to requests, logs them as part of audit logs and sends to the client on error. This is to improve debugging of customer issues. Issue: https://github.com/storj/storj/issues/5898 Change-Id: I801514b547d28d810552d91aa7c8502051e552bf
162 lines
4.3 KiB
Go
162 lines
4.3 KiB
Go
// Copyright (C) 2021 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package consoleapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/zeebo/errs"
|
|
"go.uber.org/zap"
|
|
|
|
"storj.io/common/uuid"
|
|
"storj.io/storj/private/web"
|
|
"storj.io/storj/satellite/accounting"
|
|
"storj.io/storj/satellite/console"
|
|
)
|
|
|
|
var (
|
|
// ErrUsageLimitsAPI - console usage and limits api error type.
|
|
ErrUsageLimitsAPI = errs.Class("console usage and limits")
|
|
)
|
|
|
|
// UsageLimits is an api controller that exposes all usage and limits related functionality.
|
|
type UsageLimits struct {
|
|
log *zap.Logger
|
|
service *console.Service
|
|
}
|
|
|
|
// NewUsageLimits is a constructor for api usage and limits controller.
|
|
func NewUsageLimits(log *zap.Logger, service *console.Service) *UsageLimits {
|
|
return &UsageLimits{
|
|
log: log,
|
|
service: service,
|
|
}
|
|
}
|
|
|
|
// ProjectUsageLimits returns usage and limits by project ID.
|
|
func (ul *UsageLimits) ProjectUsageLimits(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
var err error
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var ok bool
|
|
var idParam string
|
|
|
|
if idParam, ok = mux.Vars(r)["id"]; !ok {
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("missing project id route param"))
|
|
return
|
|
}
|
|
|
|
projectID, err := uuid.FromString(idParam)
|
|
if err != nil {
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("invalid project id: %v", err))
|
|
return
|
|
}
|
|
|
|
usageLimits, err := ul.service.GetProjectUsageLimits(ctx, projectID)
|
|
if err != nil {
|
|
switch {
|
|
case console.ErrUnauthorized.Has(err):
|
|
ul.serveJSONError(ctx, w, http.StatusUnauthorized, err)
|
|
return
|
|
case accounting.ErrInvalidArgument.Has(err):
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, err)
|
|
return
|
|
default:
|
|
ul.serveJSONError(ctx, w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
err = json.NewEncoder(w).Encode(usageLimits)
|
|
if err != nil {
|
|
ul.log.Error("error encoding project usage limits", zap.Error(ErrUsageLimitsAPI.Wrap(err)))
|
|
}
|
|
}
|
|
|
|
// TotalUsageLimits returns total usage and limits for all the projects that user owns.
|
|
func (ul *UsageLimits) TotalUsageLimits(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
var err error
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
usageLimits, err := ul.service.GetTotalUsageLimits(ctx)
|
|
if err != nil {
|
|
if console.ErrUnauthorized.Has(err) {
|
|
ul.serveJSONError(ctx, w, http.StatusUnauthorized, err)
|
|
return
|
|
}
|
|
|
|
ul.serveJSONError(ctx, w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
err = json.NewEncoder(w).Encode(usageLimits)
|
|
if err != nil {
|
|
ul.log.Error("error encoding project usage limits", zap.Error(ErrUsageLimitsAPI.Wrap(err)))
|
|
}
|
|
}
|
|
|
|
// DailyUsage returns daily usage by project ID.
|
|
func (ul *UsageLimits) DailyUsage(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
var err error
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
var ok bool
|
|
var idParam string
|
|
|
|
if idParam, ok = mux.Vars(r)["id"]; !ok {
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("missing project id route param"))
|
|
return
|
|
}
|
|
projectID, err := uuid.FromString(idParam)
|
|
if err != nil {
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, errs.New("invalid project id: %v", err))
|
|
return
|
|
}
|
|
|
|
sinceStamp, err := strconv.ParseInt(r.URL.Query().Get("from"), 10, 64)
|
|
if err != nil {
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
beforeStamp, err := strconv.ParseInt(r.URL.Query().Get("to"), 10, 64)
|
|
if err != nil {
|
|
ul.serveJSONError(ctx, w, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
since := time.Unix(sinceStamp, 0)
|
|
before := time.Unix(beforeStamp, 0)
|
|
|
|
dailyUsage, err := ul.service.GetDailyProjectUsage(ctx, projectID, since, before)
|
|
if err != nil {
|
|
if console.ErrUnauthorized.Has(err) {
|
|
ul.serveJSONError(ctx, w, http.StatusUnauthorized, err)
|
|
return
|
|
}
|
|
|
|
ul.serveJSONError(ctx, w, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
err = json.NewEncoder(w).Encode(dailyUsage)
|
|
if err != nil {
|
|
ul.log.Error("error encoding daily project usage", zap.Error(ErrUsageLimitsAPI.Wrap(err)))
|
|
}
|
|
}
|
|
|
|
// serveJSONError writes JSON error to response output stream.
|
|
func (ul *UsageLimits) serveJSONError(ctx context.Context, w http.ResponseWriter, status int, err error) {
|
|
web.ServeJSONError(ctx, ul.log, w, status, err)
|
|
}
|