2020-02-07 17:24:58 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package admin
|
|
|
|
|
|
|
|
import (
|
2020-04-06 19:29:32 +01:00
|
|
|
"database/sql"
|
2020-02-07 17:24:58 +00:00
|
|
|
"encoding/json"
|
2020-04-06 19:29:32 +01:00
|
|
|
"errors"
|
2020-02-07 17:24:58 +00:00
|
|
|
"fmt"
|
2020-05-11 17:05:36 +01:00
|
|
|
"io/ioutil"
|
2020-02-07 17:24:58 +00:00
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/gorilla/schema"
|
|
|
|
|
|
|
|
"storj.io/common/memory"
|
2020-03-30 10:08:50 +01:00
|
|
|
"storj.io/common/uuid"
|
2020-05-11 17:05:36 +01:00
|
|
|
"storj.io/storj/satellite/console"
|
2020-02-07 17:24:58 +00:00
|
|
|
)
|
|
|
|
|
2020-04-06 19:29:32 +01:00
|
|
|
func (server *Server) userInfo(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
userEmail, ok := vars["useremail"]
|
|
|
|
if !ok {
|
|
|
|
http.Error(w, "user-email missing", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
user, err := server.db.Console().Users().GetByEmail(ctx, userEmail)
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
http.Error(w, fmt.Sprintf("user with email %q not found", userEmail), http.StatusNotFound)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to get user %q: %v", userEmail, err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
user.PasswordHash = nil
|
|
|
|
|
|
|
|
projects, err := server.db.Console().Projects().GetByUserID(ctx, user.ID)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to get user projects %q: %v", userEmail, err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type User struct {
|
|
|
|
ID uuid.UUID `json:"id"`
|
|
|
|
FullName string `json:"fullName"`
|
|
|
|
Email string `json:"email"`
|
|
|
|
}
|
|
|
|
type Project struct {
|
|
|
|
ID uuid.UUID `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
OwnerID uuid.UUID `json:"ownerId"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var output struct {
|
|
|
|
User User `json:"user"`
|
|
|
|
Projects []Project `json:"projects"`
|
|
|
|
}
|
|
|
|
|
|
|
|
output.User = User{
|
|
|
|
ID: user.ID,
|
|
|
|
FullName: user.FullName,
|
|
|
|
Email: user.Email,
|
|
|
|
}
|
|
|
|
for _, p := range projects {
|
|
|
|
output.Projects = append(output.Projects, Project{
|
|
|
|
ID: p.ID,
|
|
|
|
Name: p.Name,
|
|
|
|
Description: p.Description,
|
|
|
|
OwnerID: p.OwnerID,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := json.Marshal(output)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("json encoding failed: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-05-11 17:05:36 +01:00
|
|
|
_, _ = w.Write(data) // nothing to do with the error response, probably the client requesting disappeared
|
2020-04-06 19:29:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-07 17:24:58 +00:00
|
|
|
func (server *Server) getProjectLimit(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
projectUUIDString, ok := vars["project"]
|
|
|
|
if !ok {
|
|
|
|
http.Error(w, "project-uuid missing", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
projectUUID, err := uuid.FromString(projectUUIDString)
|
2020-02-07 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("invalid project-uuid: %v", err), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-12 14:01:15 +01:00
|
|
|
usagelimit, err := server.db.ProjectAccounting().GetProjectStorageLimit(ctx, projectUUID)
|
2020-02-07 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to get usage limit: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-12 14:01:15 +01:00
|
|
|
bandwidthlimit, err := server.db.ProjectAccounting().GetProjectBandwidthLimit(ctx, projectUUID)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to get bandwidth limit: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
project, err := server.db.Console().Projects().Get(ctx, projectUUID)
|
2020-02-07 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to get project: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var output struct {
|
|
|
|
Usage struct {
|
|
|
|
Amount memory.Size `json:"amount"`
|
|
|
|
Bytes int64 `json:"bytes"`
|
|
|
|
} `json:"usage"`
|
2020-05-12 14:01:15 +01:00
|
|
|
Bandwidth struct {
|
|
|
|
Amount memory.Size `json:"amount"`
|
|
|
|
Bytes int64 `json:"bytes"`
|
|
|
|
} `json:"bandwidth"`
|
2020-02-07 17:24:58 +00:00
|
|
|
Rate struct {
|
|
|
|
RPS int `json:"rps"`
|
|
|
|
} `json:"rate"`
|
|
|
|
}
|
2020-05-12 14:01:15 +01:00
|
|
|
output.Usage.Amount = usagelimit
|
|
|
|
output.Usage.Bytes = usagelimit.Int64()
|
|
|
|
output.Bandwidth.Amount = bandwidthlimit
|
|
|
|
output.Bandwidth.Bytes = bandwidthlimit.Int64()
|
2020-02-07 17:24:58 +00:00
|
|
|
if project.RateLimit != nil {
|
|
|
|
output.Rate.RPS = *project.RateLimit
|
|
|
|
}
|
|
|
|
|
|
|
|
data, err := json.Marshal(output)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("json encoding failed: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2020-05-11 17:05:36 +01:00
|
|
|
_, _ = w.Write(data) // nothing to do with the error response, probably the client requesting disappeared
|
2020-02-07 17:24:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (server *Server) putProjectLimit(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
projectUUIDString, ok := vars["project"]
|
|
|
|
if !ok {
|
|
|
|
http.Error(w, "project-uuid missing", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
projectUUID, err := uuid.FromString(projectUUIDString)
|
2020-02-07 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("invalid project-uuid: %v", err), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var arguments struct {
|
2020-05-12 14:01:15 +01:00
|
|
|
Usage *memory.Size `schema:"usage"`
|
|
|
|
Bandwidth *memory.Size `schema:"bandwidth"`
|
|
|
|
Rate *int `schema:"rate"`
|
2020-02-07 17:24:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("invalid form: %v", err), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
err = decoder.Decode(&arguments, r.Form)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("invalid arguments: %v", err), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if arguments.Usage != nil {
|
|
|
|
if *arguments.Usage < 0 {
|
|
|
|
http.Error(w, fmt.Sprintf("negative usage: %v", arguments.Usage), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
err = server.db.ProjectAccounting().UpdateProjectUsageLimit(ctx, projectUUID, *arguments.Usage)
|
2020-02-07 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to update usage: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-12 14:01:15 +01:00
|
|
|
if arguments.Bandwidth != nil {
|
|
|
|
if *arguments.Bandwidth < 0 {
|
|
|
|
http.Error(w, fmt.Sprintf("negative bandwidth: %v", arguments.Usage), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = server.db.ProjectAccounting().UpdateProjectBandwidthLimit(ctx, projectUUID, *arguments.Bandwidth)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to update bandwidth: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-07 17:24:58 +00:00
|
|
|
if arguments.Rate != nil {
|
|
|
|
if *arguments.Rate < 0 {
|
|
|
|
http.Error(w, fmt.Sprintf("negative rate: %v", arguments.Rate), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-02 13:30:43 +01:00
|
|
|
err = server.db.Console().Projects().UpdateRateLimit(ctx, projectUUID, *arguments.Rate)
|
2020-02-07 17:24:58 +00:00
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to update rate: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-11 17:05:36 +01:00
|
|
|
|
|
|
|
func (server *Server) addProject(w http.ResponseWriter, r *http.Request) {
|
|
|
|
ctx := r.Context()
|
|
|
|
|
|
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to read body: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var input struct {
|
|
|
|
OwnerID uuid.UUID `json:"ownerId"`
|
|
|
|
ProjectName string `json:"projectName"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var output struct {
|
|
|
|
ProjectID uuid.UUID `json:"projectId"`
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(body, &input)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to unmarshal request: %v", err), http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if input.OwnerID.IsZero() {
|
|
|
|
http.Error(w, "OwnerID is not set", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if input.ProjectName == "" {
|
|
|
|
http.Error(w, "ProjectName is not set", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
project, err := server.db.Console().Projects().Insert(ctx, &console.Project{
|
|
|
|
Name: input.ProjectName,
|
|
|
|
OwnerID: input.OwnerID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to insert project: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = server.db.Console().ProjectMembers().Insert(ctx, project.OwnerID, project.ID)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("failed to insert project member: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
output.ProjectID = project.ID
|
|
|
|
data, err := json.Marshal(output)
|
|
|
|
if err != nil {
|
|
|
|
http.Error(w, fmt.Sprintf("json encoding failed: %v", err), http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
_, _ = w.Write(data) // nothing to do with the error response, probably the client requesting disappeared
|
|
|
|
}
|