satellite/{db, admin}: added endpoints to update user's and project's user_agent
Added backend (for now) implementation for updating user's and projects's user_agent using admin API. Updating both user and project also updates bucket_metainfo and value_attribution tables. Issue: https://github.com/storj/storj-private/issues/297 Change-Id: I40244bbaa08b46834c1b1d0720e7d84d0c2a0330
This commit is contained in:
parent
fe0c3a13b4
commit
1eee2fad69
2
go.mod
2
go.mod
@ -44,6 +44,7 @@ require (
|
|||||||
github.com/zeebo/blake3 v0.2.3
|
github.com/zeebo/blake3 v0.2.3
|
||||||
github.com/zeebo/clingy v0.0.0-20230602044025-906be850f10d
|
github.com/zeebo/clingy v0.0.0-20230602044025-906be850f10d
|
||||||
github.com/zeebo/errs v1.3.0
|
github.com/zeebo/errs v1.3.0
|
||||||
|
github.com/zeebo/errs/v2 v2.0.3
|
||||||
github.com/zeebo/ini v0.0.0-20210514163846-cc8fbd8d9599
|
github.com/zeebo/ini v0.0.0-20210514163846-cc8fbd8d9599
|
||||||
github.com/zyedidia/generic v1.2.1
|
github.com/zyedidia/generic v1.2.1
|
||||||
go.etcd.io/bbolt v1.3.5
|
go.etcd.io/bbolt v1.3.5
|
||||||
@ -124,7 +125,6 @@ require (
|
|||||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
|
||||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
|
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
|
||||||
github.com/zeebo/admission/v3 v3.0.3 // indirect
|
github.com/zeebo/admission/v3 v3.0.3 // indirect
|
||||||
github.com/zeebo/errs/v2 v2.0.3 // indirect
|
|
||||||
github.com/zeebo/float16 v0.1.0 // indirect
|
github.com/zeebo/float16 v0.1.0 // indirect
|
||||||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
|
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
|
||||||
github.com/zeebo/mwc v0.0.4 // indirect
|
github.com/zeebo/mwc v0.0.4 // indirect
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
|
"github.com/zeebo/errs/v2"
|
||||||
|
|
||||||
"storj.io/common/macaroon"
|
"storj.io/common/macaroon"
|
||||||
"storj.io/common/memory"
|
"storj.io/common/memory"
|
||||||
@ -440,6 +442,82 @@ func (server *Server) renameProject(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) updateProjectsUserAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
projectUUIDString, ok := vars["project"]
|
||||||
|
if !ok {
|
||||||
|
sendJSONError(w, "project-uuid missing",
|
||||||
|
"", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
projectUUID, err := uuid.FromString(projectUUIDString)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "invalid project-uuid",
|
||||||
|
err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
project, err := server.db.Console().Projects().Get(ctx, projectUUID)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
sendJSONError(w, "project with specified uuid does not exist",
|
||||||
|
"", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "error getting project",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
creationDatePlusMonth := project.CreatedAt.AddDate(0, 1, 0)
|
||||||
|
if time.Now().After(creationDatePlusMonth) {
|
||||||
|
sendJSONError(w, "this project was created more than a month ago",
|
||||||
|
"we should update user agent only for recently created projects", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to read body",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var input struct {
|
||||||
|
UserAgent string `json:"userAgent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &input)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to unmarshal request",
|
||||||
|
err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.UserAgent == "" {
|
||||||
|
sendJSONError(w, "UserAgent was not provided",
|
||||||
|
"", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newUserAgent := []byte(input.UserAgent)
|
||||||
|
|
||||||
|
if bytes.Equal(project.UserAgent, newUserAgent) {
|
||||||
|
sendJSONError(w, "new UserAgent is equal to existing projects UserAgent",
|
||||||
|
"", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = server._updateProjectsUserAgent(ctx, project.ID, newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to update projects user agent",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (server *Server) deleteProject(w http.ResponseWriter, r *http.Request) {
|
func (server *Server) deleteProject(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
@ -502,6 +580,45 @@ func (server *Server) deleteProject(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) _updateProjectsUserAgent(ctx context.Context, projectID uuid.UUID, newUserAgent []byte) (err error) {
|
||||||
|
err = server.db.Console().Projects().UpdateUserAgent(ctx, projectID, newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
listOptions := buckets.ListOptions{
|
||||||
|
Direction: buckets.DirectionForward,
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedBuckets := macaroon.AllowedBuckets{
|
||||||
|
All: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
projectBuckets, err := server.db.Buckets().ListBuckets(ctx, projectID, listOptions, allowedBuckets)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errList errs.Group
|
||||||
|
for _, bucket := range projectBuckets.Items {
|
||||||
|
err = server.db.Buckets().UpdateUserAgent(ctx, projectID, bucket.Name, newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
errList.Append(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = server.db.Attribution().UpdateUserAgent(ctx, projectID, bucket.Name, newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
errList.Append(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errList.Err() != nil {
|
||||||
|
return errList.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (server *Server) checkInvoicing(ctx context.Context, w http.ResponseWriter, projectID uuid.UUID) (openInvoices bool) {
|
func (server *Server) checkInvoicing(ctx context.Context, w http.ResponseWriter, projectID uuid.UUID) (openInvoices bool) {
|
||||||
year, month, _ := server.nowFn().UTC().Date()
|
year, month, _ := server.nowFn().UTC().Date()
|
||||||
firstOfMonth := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
|
firstOfMonth := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"storj.io/storj/private/testplanet"
|
"storj.io/storj/private/testplanet"
|
||||||
"storj.io/storj/satellite"
|
"storj.io/storj/satellite"
|
||||||
"storj.io/storj/satellite/accounting"
|
"storj.io/storj/satellite/accounting"
|
||||||
|
"storj.io/storj/satellite/attribution"
|
||||||
"storj.io/storj/satellite/buckets"
|
"storj.io/storj/satellite/buckets"
|
||||||
"storj.io/storj/satellite/console"
|
"storj.io/storj/satellite/console"
|
||||||
"storj.io/storj/satellite/payments/stripe"
|
"storj.io/storj/satellite/payments/stripe"
|
||||||
@ -244,6 +245,109 @@ func TestProjectAdd(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateProjectsUserAgent(t *testing.T) {
|
||||||
|
testplanet.Run(t, testplanet.Config{
|
||||||
|
SatelliteCount: 1,
|
||||||
|
StorageNodeCount: 0,
|
||||||
|
UplinkCount: 1,
|
||||||
|
Reconfigure: testplanet.Reconfigure{
|
||||||
|
Satellite: func(_ *zap.Logger, _ int, config *satellite.Config) {
|
||||||
|
config.Admin.Address = "127.0.0.1:0"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||||
|
db := planet.Satellites[0].DB
|
||||||
|
address := planet.Satellites[0].Admin.Admin.Listener.Addr()
|
||||||
|
project := planet.Uplinks[0].Projects[0]
|
||||||
|
newUserAgent := "awesome user agent value"
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
bucketName := "testName"
|
||||||
|
bucketName1 := "testName1"
|
||||||
|
|
||||||
|
bucketID, err := uuid.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
bucketID1, err := uuid.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = db.Buckets().CreateBucket(ctx, buckets.Bucket{
|
||||||
|
ID: bucketID,
|
||||||
|
Name: bucketName,
|
||||||
|
ProjectID: project.ID,
|
||||||
|
UserAgent: nil,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = db.Buckets().CreateBucket(ctx, buckets.Bucket{
|
||||||
|
ID: bucketID1,
|
||||||
|
Name: bucketName1,
|
||||||
|
ProjectID: project.ID,
|
||||||
|
UserAgent: nil,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = db.Attribution().Insert(ctx, &attribution.Info{
|
||||||
|
ProjectID: project.ID,
|
||||||
|
BucketName: []byte(bucketName),
|
||||||
|
UserAgent: nil,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = db.Attribution().Insert(ctx, &attribution.Info{
|
||||||
|
ProjectID: project.ID,
|
||||||
|
BucketName: []byte(bucketName1),
|
||||||
|
UserAgent: nil,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
body := strings.NewReader(fmt.Sprintf(`{"userAgent":"%s"}`, newUserAgent))
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPatch, fmt.Sprintf("http://"+address.String()+"/api/projects/%s/useragent", project.ID.String()), body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
req.Header.Set("Authorization", planet.Satellites[0].Config.Console.AuthToken)
|
||||||
|
|
||||||
|
response, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||||
|
require.NoError(t, response.Body.Close())
|
||||||
|
|
||||||
|
newUserAgentBytes := []byte(newUserAgent)
|
||||||
|
|
||||||
|
updatedProject, err := db.Console().Projects().Get(ctx, project.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, newUserAgentBytes, updatedProject.UserAgent)
|
||||||
|
|
||||||
|
projectBuckets, err := planet.Satellites[0].API.Buckets.Service.ListBuckets(ctx, updatedProject.ID, buckets.ListOptions{Direction: buckets.DirectionForward}, macaroon.AllowedBuckets{All: true})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 2, len(projectBuckets.Items))
|
||||||
|
|
||||||
|
for _, bucket := range projectBuckets.Items {
|
||||||
|
require.Equal(t, newUserAgentBytes, bucket.UserAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketAttribution, err := db.Attribution().Get(ctx, project.ID, []byte(bucketName))
|
||||||
|
require.Equal(t, newUserAgentBytes, bucketAttribution.UserAgent)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bucketAttribution1, err := db.Attribution().Get(ctx, project.ID, []byte(bucketName1))
|
||||||
|
require.Equal(t, newUserAgentBytes, bucketAttribution1.UserAgent)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Same UserAgent", func(t *testing.T) {
|
||||||
|
newProject, err := db.Console().Projects().Insert(ctx, &console.Project{
|
||||||
|
Name: "test",
|
||||||
|
UserAgent: []byte(newUserAgent),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
link := fmt.Sprintf("http://"+address.String()+"/api/projects/%s/useragent", newProject.ID)
|
||||||
|
body := fmt.Sprintf(`{"userAgent":"%s"}`, newUserAgent)
|
||||||
|
responseBody := assertReq(ctx, t, link, http.MethodPatch, body, http.StatusBadRequest, "", planet.Satellites[0].Config.Console.AuthToken)
|
||||||
|
require.Contains(t, string(responseBody), "new UserAgent is equal to existing projects UserAgent")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestProjectRename(t *testing.T) {
|
func TestProjectRename(t *testing.T) {
|
||||||
testplanet.Run(t, testplanet.Config{
|
testplanet.Run(t, testplanet.Config{
|
||||||
SatelliteCount: 1,
|
SatelliteCount: 1,
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"storj.io/common/errs2"
|
"storj.io/common/errs2"
|
||||||
"storj.io/storj/satellite/accounting"
|
"storj.io/storj/satellite/accounting"
|
||||||
adminui "storj.io/storj/satellite/admin/ui"
|
adminui "storj.io/storj/satellite/admin/ui"
|
||||||
|
"storj.io/storj/satellite/attribution"
|
||||||
"storj.io/storj/satellite/buckets"
|
"storj.io/storj/satellite/buckets"
|
||||||
"storj.io/storj/satellite/console"
|
"storj.io/storj/satellite/console"
|
||||||
"storj.io/storj/satellite/console/consoleweb"
|
"storj.io/storj/satellite/console/consoleweb"
|
||||||
@ -64,6 +65,10 @@ type DB interface {
|
|||||||
OIDC() oidc.DB
|
OIDC() oidc.DB
|
||||||
// StripeCoinPayments returns database for satellite stripe coin payments
|
// StripeCoinPayments returns database for satellite stripe coin payments
|
||||||
StripeCoinPayments() stripe.DB
|
StripeCoinPayments() stripe.DB
|
||||||
|
// Buckets returns database for buckets metainfo.
|
||||||
|
Buckets() buckets.DB
|
||||||
|
// Attribution returns database for value attribution.
|
||||||
|
Attribution() attribution.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server provides endpoints for administrative tasks.
|
// Server provides endpoints for administrative tasks.
|
||||||
@ -117,6 +122,7 @@ func NewServer(log *zap.Logger, listener net.Listener, db DB, buckets *buckets.S
|
|||||||
fullAccessAPI.HandleFunc("/users/{useremail}", server.updateUser).Methods("PUT")
|
fullAccessAPI.HandleFunc("/users/{useremail}", server.updateUser).Methods("PUT")
|
||||||
fullAccessAPI.HandleFunc("/users/{useremail}", server.deleteUser).Methods("DELETE")
|
fullAccessAPI.HandleFunc("/users/{useremail}", server.deleteUser).Methods("DELETE")
|
||||||
fullAccessAPI.HandleFunc("/users/{useremail}/mfa", server.disableUserMFA).Methods("DELETE")
|
fullAccessAPI.HandleFunc("/users/{useremail}/mfa", server.disableUserMFA).Methods("DELETE")
|
||||||
|
fullAccessAPI.HandleFunc("/users/{useremail}/useragent", server.updateUsersUserAgent).Methods("PATCH")
|
||||||
fullAccessAPI.HandleFunc("/oauth/clients", server.createOAuthClient).Methods("POST")
|
fullAccessAPI.HandleFunc("/oauth/clients", server.createOAuthClient).Methods("POST")
|
||||||
fullAccessAPI.HandleFunc("/oauth/clients/{id}", server.updateOAuthClient).Methods("PUT")
|
fullAccessAPI.HandleFunc("/oauth/clients/{id}", server.updateOAuthClient).Methods("PUT")
|
||||||
fullAccessAPI.HandleFunc("/oauth/clients/{id}", server.deleteOAuthClient).Methods("DELETE")
|
fullAccessAPI.HandleFunc("/oauth/clients/{id}", server.deleteOAuthClient).Methods("DELETE")
|
||||||
@ -131,6 +137,7 @@ func NewServer(log *zap.Logger, listener net.Listener, db DB, buckets *buckets.S
|
|||||||
fullAccessAPI.HandleFunc("/projects/{project}/buckets/{bucket}/geofence", server.createGeofenceForBucket).Methods("POST")
|
fullAccessAPI.HandleFunc("/projects/{project}/buckets/{bucket}/geofence", server.createGeofenceForBucket).Methods("POST")
|
||||||
fullAccessAPI.HandleFunc("/projects/{project}/buckets/{bucket}/geofence", server.deleteGeofenceForBucket).Methods("DELETE")
|
fullAccessAPI.HandleFunc("/projects/{project}/buckets/{bucket}/geofence", server.deleteGeofenceForBucket).Methods("DELETE")
|
||||||
fullAccessAPI.HandleFunc("/projects/{project}/usage", server.checkProjectUsage).Methods("GET")
|
fullAccessAPI.HandleFunc("/projects/{project}/usage", server.checkProjectUsage).Methods("GET")
|
||||||
|
fullAccessAPI.HandleFunc("/projects/{project}/useragent", server.updateProjectsUserAgent).Methods("PATCH")
|
||||||
fullAccessAPI.HandleFunc("/apikeys/{apikey}", server.getAPIKey).Methods("GET")
|
fullAccessAPI.HandleFunc("/apikeys/{apikey}", server.getAPIKey).Methods("GET")
|
||||||
fullAccessAPI.HandleFunc("/apikeys/{apikey}", server.deleteAPIKey).Methods("DELETE")
|
fullAccessAPI.HandleFunc("/apikeys/{apikey}", server.deleteAPIKey).Methods("DELETE")
|
||||||
fullAccessAPI.HandleFunc("/restkeys/{useremail}", server.addRESTKey).Methods("POST")
|
fullAccessAPI.HandleFunc("/restkeys/{useremail}", server.addRESTKey).Methods("POST")
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@ -11,8 +12,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/zeebo/errs"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"storj.io/common/memory"
|
"storj.io/common/memory"
|
||||||
@ -343,6 +346,101 @@ func (server *Server) updateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) updateUsersUserAgent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
userEmail, ok := vars["useremail"]
|
||||||
|
if !ok {
|
||||||
|
sendJSONError(w, "user-email missing",
|
||||||
|
"", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := server.db.Console().Users().GetByEmail(ctx, userEmail)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
sendJSONError(w, fmt.Sprintf("user with email %q does not exist", userEmail),
|
||||||
|
"", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to get user",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
creationDatePlusMonth := user.CreatedAt.AddDate(0, 1, 0)
|
||||||
|
if time.Now().After(creationDatePlusMonth) {
|
||||||
|
sendJSONError(w, "this user was created more than a month ago",
|
||||||
|
"we should update user agent only for recently created users", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to read body",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var input struct {
|
||||||
|
UserAgent string `json:"userAgent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &input)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to unmarshal request",
|
||||||
|
err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.UserAgent == "" {
|
||||||
|
sendJSONError(w, "UserAgent was not provided",
|
||||||
|
"", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newUserAgent := []byte(input.UserAgent)
|
||||||
|
|
||||||
|
if bytes.Equal(user.UserAgent, newUserAgent) {
|
||||||
|
sendJSONError(w, "new UserAgent is equal to existing users UserAgent",
|
||||||
|
"", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = server.db.Console().Users().UpdateUserAgent(ctx, user.ID, newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to update user's user agent",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
projects, err := server.db.Console().Projects().GetOwn(ctx, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
sendJSONError(w, "failed to get users projects",
|
||||||
|
err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var errList errs.Group
|
||||||
|
for _, project := range projects {
|
||||||
|
if bytes.Equal(project.UserAgent, newUserAgent) {
|
||||||
|
errList.Add(errs.New("projectID: %s. New UserAgent is equal to existing users UserAgent", project.ID))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = server._updateProjectsUserAgent(ctx, project.ID, newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
errList.Add(errs.New("projectID: %s. Failed to update projects user agent: %s", project.ID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errList.Err() != nil {
|
||||||
|
sendJSONError(w, "failed to update projects user agent",
|
||||||
|
errList.Err().Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// updateLimits updates user limits and all project limits for that user (future and existing).
|
// updateLimits updates user limits and all project limits for that user (future and existing).
|
||||||
func (server *Server) updateLimits(w http.ResponseWriter, r *http.Request) {
|
func (server *Server) updateLimits(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
@ -272,6 +272,56 @@ func TestUserUpdate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateUsersUserAgent(t *testing.T) {
|
||||||
|
testplanet.Run(t, testplanet.Config{
|
||||||
|
SatelliteCount: 1,
|
||||||
|
StorageNodeCount: 0,
|
||||||
|
UplinkCount: 1,
|
||||||
|
Reconfigure: testplanet.Reconfigure{
|
||||||
|
Satellite: func(_ *zap.Logger, _ int, config *satellite.Config) {
|
||||||
|
config.Admin.Address = "127.0.0.1:0"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||||
|
db := planet.Satellites[0].DB
|
||||||
|
address := planet.Satellites[0].Admin.Admin.Listener.Addr()
|
||||||
|
project := planet.Uplinks[0].Projects[0]
|
||||||
|
newUserAgent := "awesome user agent value"
|
||||||
|
|
||||||
|
t.Run("OK", func(t *testing.T) {
|
||||||
|
body := strings.NewReader(fmt.Sprintf(`{"userAgent":"%s"}`, newUserAgent))
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPatch, fmt.Sprintf("http://"+address.String()+"/api/users/%s/useragent", project.Owner.Email), body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
req.Header.Set("Authorization", planet.Satellites[0].Config.Console.AuthToken)
|
||||||
|
|
||||||
|
response, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||||
|
require.NoError(t, response.Body.Close())
|
||||||
|
|
||||||
|
newUserAgentBytes := []byte(newUserAgent)
|
||||||
|
|
||||||
|
updatedUser, err := db.Console().Users().Get(ctx, project.Owner.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, newUserAgentBytes, updatedUser.UserAgent)
|
||||||
|
|
||||||
|
updatedProject, err := db.Console().Projects().Get(ctx, project.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, newUserAgentBytes, updatedProject.UserAgent)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Same UserAgent", func(t *testing.T) {
|
||||||
|
err := db.Console().Users().UpdateUserAgent(ctx, project.Owner.ID, []byte(newUserAgent))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
link := fmt.Sprintf("http://"+address.String()+"/api/users/%s/useragent", project.Owner.Email)
|
||||||
|
body := fmt.Sprintf(`{"userAgent":"%s"}`, newUserAgent)
|
||||||
|
responseBody := assertReq(ctx, t, link, http.MethodPatch, body, http.StatusBadRequest, "", planet.Satellites[0].Config.Console.AuthToken)
|
||||||
|
require.Contains(t, string(responseBody), "new UserAgent is equal to existing users UserAgent")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestDisableMFA(t *testing.T) {
|
func TestDisableMFA(t *testing.T) {
|
||||||
testplanet.Run(t, testplanet.Config{
|
testplanet.Run(t, testplanet.Config{
|
||||||
SatelliteCount: 1,
|
SatelliteCount: 1,
|
||||||
|
@ -44,6 +44,8 @@ type DB interface {
|
|||||||
Get(ctx context.Context, projectID uuid.UUID, bucketName []byte) (*Info, error)
|
Get(ctx context.Context, projectID uuid.UUID, bucketName []byte) (*Info, error)
|
||||||
// Insert creates and stores new Info.
|
// Insert creates and stores new Info.
|
||||||
Insert(ctx context.Context, info *Info) (*Info, error)
|
Insert(ctx context.Context, info *Info) (*Info, error)
|
||||||
|
// UpdateUserAgent updates bucket attribution data.
|
||||||
|
UpdateUserAgent(ctx context.Context, projectID uuid.UUID, bucketName string, userAgent []byte) error
|
||||||
// QueryAttribution queries partner bucket attribution data.
|
// QueryAttribution queries partner bucket attribution data.
|
||||||
QueryAttribution(ctx context.Context, userAgent []byte, start time.Time, end time.Time) ([]*BucketUsage, error)
|
QueryAttribution(ctx context.Context, userAgent []byte, start time.Time, end time.Time) ([]*BucketUsage, error)
|
||||||
// QueryAllAttribution queries all partner bucket usage data.
|
// QueryAllAttribution queries all partner bucket usage data.
|
||||||
|
@ -101,6 +101,8 @@ type DB interface {
|
|||||||
GetBucketID(ctx context.Context, bucket metabase.BucketLocation) (id uuid.UUID, err error)
|
GetBucketID(ctx context.Context, bucket metabase.BucketLocation) (id uuid.UUID, err error)
|
||||||
// UpdateBucket updates an existing bucket
|
// UpdateBucket updates an existing bucket
|
||||||
UpdateBucket(ctx context.Context, bucket Bucket) (_ Bucket, err error)
|
UpdateBucket(ctx context.Context, bucket Bucket) (_ Bucket, err error)
|
||||||
|
// UpdateUserAgent updates buckets user agent.
|
||||||
|
UpdateUserAgent(ctx context.Context, projectID uuid.UUID, bucketName string, userAgent []byte) error
|
||||||
// DeleteBucket deletes a bucket
|
// DeleteBucket deletes a bucket
|
||||||
DeleteBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (err error)
|
DeleteBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (err error)
|
||||||
// ListBuckets returns all buckets for a project
|
// ListBuckets returns all buckets for a project
|
||||||
|
@ -55,6 +55,9 @@ type Projects interface {
|
|||||||
|
|
||||||
// UpdateUsageLimits is a method for updating project's usage limits.
|
// UpdateUsageLimits is a method for updating project's usage limits.
|
||||||
UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
|
UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
|
||||||
|
|
||||||
|
// UpdateUserAgent is a method for updating projects user agent.
|
||||||
|
UpdateUserAgent(ctx context.Context, id uuid.UUID, userAgent []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsageLimitsConfig is a configuration struct for default per-project usage limits.
|
// UsageLimitsConfig is a configuration struct for default per-project usage limits.
|
||||||
|
@ -42,6 +42,8 @@ type Users interface {
|
|||||||
Update(ctx context.Context, userID uuid.UUID, request UpdateUserRequest) error
|
Update(ctx context.Context, userID uuid.UUID, request UpdateUserRequest) error
|
||||||
// UpdatePaidTier sets whether the user is in the paid tier.
|
// UpdatePaidTier sets whether the user is in the paid tier.
|
||||||
UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier bool, projectBandwidthLimit, projectStorageLimit memory.Size, projectSegmentLimit int64, projectLimit int) error
|
UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier bool, projectBandwidthLimit, projectStorageLimit memory.Size, projectSegmentLimit int64, projectLimit int) error
|
||||||
|
// UpdateUserAgent is a method to update the user's user agent.
|
||||||
|
UpdateUserAgent(ctx context.Context, id uuid.UUID, userAgent []byte) error
|
||||||
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
|
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
|
||||||
UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
|
UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
|
||||||
// GetProjectLimit is a method to get the users project limit
|
// GetProjectLimit is a method to get the users project limit
|
||||||
|
@ -243,6 +243,20 @@ func (keys *attributionDB) Get(ctx context.Context, projectID uuid.UUID, bucketN
|
|||||||
return attributionFromDBX(dbxInfo)
|
return attributionFromDBX(dbxInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserAgent updates bucket attribution data.
|
||||||
|
func (keys *attributionDB) UpdateUserAgent(ctx context.Context, projectID uuid.UUID, bucketName string, userAgent []byte) (err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
_, err = keys.db.Update_ValueAttribution_By_ProjectId_And_BucketName(ctx,
|
||||||
|
dbx.ValueAttribution_ProjectId(projectID[:]),
|
||||||
|
dbx.ValueAttribution_BucketName([]byte(bucketName)),
|
||||||
|
dbx.ValueAttribution_Update_Fields{
|
||||||
|
UserAgent: dbx.ValueAttribution_UserAgent(userAgent),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Insert implements create partner info.
|
// Insert implements create partner info.
|
||||||
func (keys *attributionDB) Insert(ctx context.Context, info *attribution.Info) (_ *attribution.Info, err error) {
|
func (keys *attributionDB) Insert(ctx context.Context, info *attribution.Info) (_ *attribution.Info, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -168,6 +168,20 @@ func (db *bucketsDB) UpdateBucket(ctx context.Context, bucket buckets.Bucket) (_
|
|||||||
return convertDBXtoBucket(dbxBucket)
|
return convertDBXtoBucket(dbxBucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserAgent updates buckets user agent.
|
||||||
|
func (db *bucketsDB) UpdateUserAgent(ctx context.Context, projectID uuid.UUID, bucketName string, userAgent []byte) (err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
_, err = db.db.Update_BucketMetainfo_By_ProjectId_And_Name(ctx,
|
||||||
|
dbx.BucketMetainfo_ProjectId(projectID[:]),
|
||||||
|
dbx.BucketMetainfo_Name([]byte(bucketName)),
|
||||||
|
dbx.BucketMetainfo_Update_Fields{
|
||||||
|
UserAgent: dbx.BucketMetainfo_UserAgent(userAgent),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteBucket deletes a bucket.
|
// DeleteBucket deletes a bucket.
|
||||||
func (db *bucketsDB) DeleteBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (err error) {
|
func (db *bucketsDB) DeleteBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -40,7 +40,7 @@ model project (
|
|||||||
// max_buckets is the maximum number of buckets that can be created for the project.
|
// max_buckets is the maximum number of buckets that can be created for the project.
|
||||||
field max_buckets int ( nullable, updatable )
|
field max_buckets int ( nullable, updatable )
|
||||||
// user_agent is the referred partner who created the project.
|
// user_agent is the referred partner who created the project.
|
||||||
field user_agent blob ( nullable )
|
field user_agent blob ( nullable, updatable )
|
||||||
// owner_id refers to the user UUID in user.id.
|
// owner_id refers to the user UUID in user.id.
|
||||||
field owner_id blob
|
field owner_id blob
|
||||||
// salt is used for salting the user passphrase for the content.
|
// salt is used for salting the user passphrase for the content.
|
||||||
|
@ -148,7 +148,7 @@ model value_attribution (
|
|||||||
// user_agent is the first User-Agent that was used to upload data.
|
// user_agent is the first User-Agent that was used to upload data.
|
||||||
// unless the user signed up with a specific partner.
|
// unless the user signed up with a specific partner.
|
||||||
// note: this field is duplicated in bucket_metainfo.user_agent.
|
// note: this field is duplicated in bucket_metainfo.user_agent.
|
||||||
field user_agent blob ( nullable )
|
field user_agent blob ( updatable, nullable )
|
||||||
// TODO remove as part of release 1.81 or after
|
// TODO remove as part of release 1.81 or after
|
||||||
field partner_id blob (nullable, default null)
|
field partner_id blob (nullable, default null)
|
||||||
// last_updated is updated whenever the row changes.
|
// last_updated is updated whenever the row changes.
|
||||||
@ -156,6 +156,10 @@ model value_attribution (
|
|||||||
)
|
)
|
||||||
|
|
||||||
create value_attribution ()
|
create value_attribution ()
|
||||||
|
update value_attribution (
|
||||||
|
where value_attribution.project_id = ?
|
||||||
|
where value_attribution.bucket_name = ?
|
||||||
|
)
|
||||||
|
|
||||||
read one (
|
read one (
|
||||||
select value_attribution
|
select value_attribution
|
||||||
|
@ -5573,6 +5573,7 @@ type Project_Update_Fields struct {
|
|||||||
RateLimit Project_RateLimit_Field
|
RateLimit Project_RateLimit_Field
|
||||||
BurstLimit Project_BurstLimit_Field
|
BurstLimit Project_BurstLimit_Field
|
||||||
MaxBuckets Project_MaxBuckets_Field
|
MaxBuckets Project_MaxBuckets_Field
|
||||||
|
UserAgent Project_UserAgent_Field
|
||||||
DefaultPlacement Project_DefaultPlacement_Field
|
DefaultPlacement Project_DefaultPlacement_Field
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9335,6 +9336,7 @@ type User_Update_Fields struct {
|
|||||||
ShortName User_ShortName_Field
|
ShortName User_ShortName_Field
|
||||||
PasswordHash User_PasswordHash_Field
|
PasswordHash User_PasswordHash_Field
|
||||||
Status User_Status_Field
|
Status User_Status_Field
|
||||||
|
UserAgent User_UserAgent_Field
|
||||||
ProjectLimit User_ProjectLimit_Field
|
ProjectLimit User_ProjectLimit_Field
|
||||||
ProjectBandwidthLimit User_ProjectBandwidthLimit_Field
|
ProjectBandwidthLimit User_ProjectBandwidthLimit_Field
|
||||||
ProjectStorageLimit User_ProjectStorageLimit_Field
|
ProjectStorageLimit User_ProjectStorageLimit_Field
|
||||||
@ -10313,6 +10315,7 @@ type ValueAttribution_Create_Fields struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ValueAttribution_Update_Fields struct {
|
type ValueAttribution_Update_Fields struct {
|
||||||
|
UserAgent ValueAttribution_UserAgent_Field
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValueAttribution_ProjectId_Field struct {
|
type ValueAttribution_ProjectId_Field struct {
|
||||||
@ -18304,6 +18307,11 @@ func (obj *pgxImpl) Update_Project_By_Id(ctx context.Context,
|
|||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("max_buckets = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("max_buckets = ?"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update.UserAgent._set {
|
||||||
|
__values = append(__values, update.UserAgent.value())
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("user_agent = ?"))
|
||||||
|
}
|
||||||
|
|
||||||
if update.DefaultPlacement._set {
|
if update.DefaultPlacement._set {
|
||||||
__values = append(__values, update.DefaultPlacement.value())
|
__values = append(__values, update.DefaultPlacement.value())
|
||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("default_placement = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("default_placement = ?"))
|
||||||
@ -18461,6 +18469,49 @@ func (obj *pgxImpl) Update_BucketMetainfo_By_ProjectId_And_Name(ctx context.Cont
|
|||||||
return bucket_metainfo, nil
|
return bucket_metainfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (obj *pgxImpl) Update_ValueAttribution_By_ProjectId_And_BucketName(ctx context.Context,
|
||||||
|
value_attribution_project_id ValueAttribution_ProjectId_Field,
|
||||||
|
value_attribution_bucket_name ValueAttribution_BucketName_Field,
|
||||||
|
update ValueAttribution_Update_Fields) (
|
||||||
|
value_attribution *ValueAttribution, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
var __sets = &__sqlbundle_Hole{}
|
||||||
|
|
||||||
|
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE value_attributions SET "), __sets, __sqlbundle_Literal(" WHERE value_attributions.project_id = ? AND value_attributions.bucket_name = ? RETURNING value_attributions.project_id, value_attributions.bucket_name, value_attributions.user_agent, value_attributions.partner_id, value_attributions.last_updated")}}
|
||||||
|
|
||||||
|
__sets_sql := __sqlbundle_Literals{Join: ", "}
|
||||||
|
var __values []interface{}
|
||||||
|
var __args []interface{}
|
||||||
|
|
||||||
|
if update.UserAgent._set {
|
||||||
|
__values = append(__values, update.UserAgent.value())
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("user_agent = ?"))
|
||||||
|
}
|
||||||
|
|
||||||
|
__now := obj.db.Hooks.Now().UTC()
|
||||||
|
|
||||||
|
__values = append(__values, __now)
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("last_updated = ?"))
|
||||||
|
|
||||||
|
__args = append(__args, value_attribution_project_id.value(), value_attribution_bucket_name.value())
|
||||||
|
|
||||||
|
__values = append(__values, __args...)
|
||||||
|
__sets.SQL = __sets_sql
|
||||||
|
|
||||||
|
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||||
|
obj.logStmt(__stmt, __values...)
|
||||||
|
|
||||||
|
value_attribution = &ValueAttribution{}
|
||||||
|
err = obj.queryRowContext(ctx, __stmt, __values...).Scan(&value_attribution.ProjectId, &value_attribution.BucketName, &value_attribution.UserAgent, &value_attribution.PartnerId, &value_attribution.LastUpdated)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, obj.makeErr(err)
|
||||||
|
}
|
||||||
|
return value_attribution, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (obj *pgxImpl) Update_User_By_Id(ctx context.Context,
|
func (obj *pgxImpl) Update_User_By_Id(ctx context.Context,
|
||||||
user_id User_Id_Field,
|
user_id User_Id_Field,
|
||||||
update User_Update_Fields) (
|
update User_Update_Fields) (
|
||||||
@ -18504,6 +18555,11 @@ func (obj *pgxImpl) Update_User_By_Id(ctx context.Context,
|
|||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("status = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("status = ?"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update.UserAgent._set {
|
||||||
|
__values = append(__values, update.UserAgent.value())
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("user_agent = ?"))
|
||||||
|
}
|
||||||
|
|
||||||
if update.ProjectLimit._set {
|
if update.ProjectLimit._set {
|
||||||
__values = append(__values, update.ProjectLimit.value())
|
__values = append(__values, update.ProjectLimit.value())
|
||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("project_limit = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("project_limit = ?"))
|
||||||
@ -26211,6 +26267,11 @@ func (obj *pgxcockroachImpl) Update_Project_By_Id(ctx context.Context,
|
|||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("max_buckets = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("max_buckets = ?"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update.UserAgent._set {
|
||||||
|
__values = append(__values, update.UserAgent.value())
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("user_agent = ?"))
|
||||||
|
}
|
||||||
|
|
||||||
if update.DefaultPlacement._set {
|
if update.DefaultPlacement._set {
|
||||||
__values = append(__values, update.DefaultPlacement.value())
|
__values = append(__values, update.DefaultPlacement.value())
|
||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("default_placement = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("default_placement = ?"))
|
||||||
@ -26368,6 +26429,49 @@ func (obj *pgxcockroachImpl) Update_BucketMetainfo_By_ProjectId_And_Name(ctx con
|
|||||||
return bucket_metainfo, nil
|
return bucket_metainfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (obj *pgxcockroachImpl) Update_ValueAttribution_By_ProjectId_And_BucketName(ctx context.Context,
|
||||||
|
value_attribution_project_id ValueAttribution_ProjectId_Field,
|
||||||
|
value_attribution_bucket_name ValueAttribution_BucketName_Field,
|
||||||
|
update ValueAttribution_Update_Fields) (
|
||||||
|
value_attribution *ValueAttribution, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
var __sets = &__sqlbundle_Hole{}
|
||||||
|
|
||||||
|
var __embed_stmt = __sqlbundle_Literals{Join: "", SQLs: []__sqlbundle_SQL{__sqlbundle_Literal("UPDATE value_attributions SET "), __sets, __sqlbundle_Literal(" WHERE value_attributions.project_id = ? AND value_attributions.bucket_name = ? RETURNING value_attributions.project_id, value_attributions.bucket_name, value_attributions.user_agent, value_attributions.partner_id, value_attributions.last_updated")}}
|
||||||
|
|
||||||
|
__sets_sql := __sqlbundle_Literals{Join: ", "}
|
||||||
|
var __values []interface{}
|
||||||
|
var __args []interface{}
|
||||||
|
|
||||||
|
if update.UserAgent._set {
|
||||||
|
__values = append(__values, update.UserAgent.value())
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("user_agent = ?"))
|
||||||
|
}
|
||||||
|
|
||||||
|
__now := obj.db.Hooks.Now().UTC()
|
||||||
|
|
||||||
|
__values = append(__values, __now)
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("last_updated = ?"))
|
||||||
|
|
||||||
|
__args = append(__args, value_attribution_project_id.value(), value_attribution_bucket_name.value())
|
||||||
|
|
||||||
|
__values = append(__values, __args...)
|
||||||
|
__sets.SQL = __sets_sql
|
||||||
|
|
||||||
|
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||||
|
obj.logStmt(__stmt, __values...)
|
||||||
|
|
||||||
|
value_attribution = &ValueAttribution{}
|
||||||
|
err = obj.queryRowContext(ctx, __stmt, __values...).Scan(&value_attribution.ProjectId, &value_attribution.BucketName, &value_attribution.UserAgent, &value_attribution.PartnerId, &value_attribution.LastUpdated)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, obj.makeErr(err)
|
||||||
|
}
|
||||||
|
return value_attribution, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (obj *pgxcockroachImpl) Update_User_By_Id(ctx context.Context,
|
func (obj *pgxcockroachImpl) Update_User_By_Id(ctx context.Context,
|
||||||
user_id User_Id_Field,
|
user_id User_Id_Field,
|
||||||
update User_Update_Fields) (
|
update User_Update_Fields) (
|
||||||
@ -26411,6 +26515,11 @@ func (obj *pgxcockroachImpl) Update_User_By_Id(ctx context.Context,
|
|||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("status = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("status = ?"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if update.UserAgent._set {
|
||||||
|
__values = append(__values, update.UserAgent.value())
|
||||||
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("user_agent = ?"))
|
||||||
|
}
|
||||||
|
|
||||||
if update.ProjectLimit._set {
|
if update.ProjectLimit._set {
|
||||||
__values = append(__values, update.ProjectLimit.value())
|
__values = append(__values, update.ProjectLimit.value())
|
||||||
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("project_limit = ?"))
|
__sets_sql.SQLs = append(__sets_sql.SQLs, __sqlbundle_Literal("project_limit = ?"))
|
||||||
@ -29786,6 +29895,18 @@ func (rx *Rx) Update_User_By_Id(ctx context.Context,
|
|||||||
return tx.Update_User_By_Id(ctx, user_id, update)
|
return tx.Update_User_By_Id(ctx, user_id, update)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rx *Rx) Update_ValueAttribution_By_ProjectId_And_BucketName(ctx context.Context,
|
||||||
|
value_attribution_project_id ValueAttribution_ProjectId_Field,
|
||||||
|
value_attribution_bucket_name ValueAttribution_BucketName_Field,
|
||||||
|
update ValueAttribution_Update_Fields) (
|
||||||
|
value_attribution *ValueAttribution, err error) {
|
||||||
|
var tx *Tx
|
||||||
|
if tx, err = rx.getTx(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return tx.Update_ValueAttribution_By_ProjectId_And_BucketName(ctx, value_attribution_project_id, value_attribution_bucket_name, update)
|
||||||
|
}
|
||||||
|
|
||||||
func (rx *Rx) Update_WebappSession_By_Id(ctx context.Context,
|
func (rx *Rx) Update_WebappSession_By_Id(ctx context.Context,
|
||||||
webapp_session_id WebappSession_Id_Field,
|
webapp_session_id WebappSession_Id_Field,
|
||||||
update WebappSession_Update_Fields) (
|
update WebappSession_Update_Fields) (
|
||||||
@ -30713,6 +30834,12 @@ type Methods interface {
|
|||||||
update User_Update_Fields) (
|
update User_Update_Fields) (
|
||||||
user *User, err error)
|
user *User, err error)
|
||||||
|
|
||||||
|
Update_ValueAttribution_By_ProjectId_And_BucketName(ctx context.Context,
|
||||||
|
value_attribution_project_id ValueAttribution_ProjectId_Field,
|
||||||
|
value_attribution_bucket_name ValueAttribution_BucketName_Field,
|
||||||
|
update ValueAttribution_Update_Fields) (
|
||||||
|
value_attribution *ValueAttribution, err error)
|
||||||
|
|
||||||
Update_WebappSession_By_Id(ctx context.Context,
|
Update_WebappSession_By_Id(ctx context.Context,
|
||||||
webapp_session_id WebappSession_Id_Field,
|
webapp_session_id WebappSession_Id_Field,
|
||||||
update WebappSession_Update_Fields) (
|
update WebappSession_Update_Fields) (
|
||||||
|
@ -26,7 +26,7 @@ model user (
|
|||||||
// status indicates whether the user is inactive=0, active=1, or deleted=2. See console.UserStatus for details.
|
// status indicates whether the user is inactive=0, active=1, or deleted=2. See console.UserStatus for details.
|
||||||
field status int ( updatable, autoinsert )
|
field status int ( updatable, autoinsert )
|
||||||
// user_agent contains the partner parameter from registration.
|
// user_agent contains the partner parameter from registration.
|
||||||
field user_agent blob ( nullable )
|
field user_agent blob ( updatable, nullable )
|
||||||
// created_at indicates when the user was created.
|
// created_at indicates when the user was created.
|
||||||
field created_at timestamp ( autoinsert )
|
field created_at timestamp ( autoinsert )
|
||||||
|
|
||||||
|
@ -291,6 +291,19 @@ func (projects *projects) UpdateBucketLimit(ctx context.Context, id uuid.UUID, n
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserAgent is a method for updating projects user agent.
|
||||||
|
func (projects *projects) UpdateUserAgent(ctx context.Context, id uuid.UUID, userAgent []byte) (err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
_, err = projects.db.Update_Project_By_Id(ctx,
|
||||||
|
dbx.Project_Id(id[:]),
|
||||||
|
dbx.Project_Update_Fields{
|
||||||
|
UserAgent: dbx.Project_UserAgent(userAgent),
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// List returns paginated projects, created before provided timestamp.
|
// List returns paginated projects, created before provided timestamp.
|
||||||
func (projects *projects) List(ctx context.Context, offset int64, limit int, before time.Time) (_ console.ProjectsPage, err error) {
|
func (projects *projects) List(ctx context.Context, offset int64, limit int, before time.Time) (_ console.ProjectsPage, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -305,6 +305,21 @@ func (users *users) UpdatePaidTier(ctx context.Context, id uuid.UUID, paidTier b
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUserAgent is a method to update the user's user agent.
|
||||||
|
func (users *users) UpdateUserAgent(ctx context.Context, id uuid.UUID, userAgent []byte) (err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
_, err = users.db.Update_User_By_Id(
|
||||||
|
ctx,
|
||||||
|
dbx.User_Id(id[:]),
|
||||||
|
dbx.User_Update_Fields{
|
||||||
|
UserAgent: dbx.User_UserAgent(userAgent),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
|
// UpdateUserProjectLimits is a method to update the user's usage limits for new projects.
|
||||||
func (users *users) UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits console.UsageLimits) (err error) {
|
func (users *users) UpdateUserProjectLimits(ctx context.Context, id uuid.UUID, limits console.UsageLimits) (err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
@ -125,6 +125,7 @@ require (
|
|||||||
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
|
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
|
||||||
github.com/zeebo/admission/v3 v3.0.3 // indirect
|
github.com/zeebo/admission/v3 v3.0.3 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
|
github.com/zeebo/errs/v2 v2.0.3 // indirect
|
||||||
github.com/zeebo/float16 v0.1.0 // indirect
|
github.com/zeebo/float16 v0.1.0 // indirect
|
||||||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
|
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
|
||||||
github.com/zeebo/mwc v0.0.4 // indirect
|
github.com/zeebo/mwc v0.0.4 // indirect
|
||||||
|
@ -836,6 +836,7 @@ github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtC
|
|||||||
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
||||||
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||||
github.com/zeebo/errs/v2 v2.0.3 h1:WwqAmopgot4ZC+CgIveP+H91Nf78NDEGWjtAXen45Hw=
|
github.com/zeebo/errs/v2 v2.0.3 h1:WwqAmopgot4ZC+CgIveP+H91Nf78NDEGWjtAXen45Hw=
|
||||||
|
github.com/zeebo/errs/v2 v2.0.3/go.mod h1:OKmvVZt4UqpyJrYFykDKm168ZquJ55pbbIVUICNmLN0=
|
||||||
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc=
|
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc=
|
||||||
github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo=
|
github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo=
|
||||||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 h1:+cwNE5KJ3pika4HuzmDHkDlK5myo0G9Sv+eO7WWxnUQ=
|
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 h1:+cwNE5KJ3pika4HuzmDHkDlK5myo0G9Sv+eO7WWxnUQ=
|
||||||
|
@ -185,6 +185,7 @@ require (
|
|||||||
github.com/zeebo/admission/v3 v3.0.3 // indirect
|
github.com/zeebo/admission/v3 v3.0.3 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
github.com/zeebo/errs v1.3.0 // indirect
|
github.com/zeebo/errs v1.3.0 // indirect
|
||||||
|
github.com/zeebo/errs/v2 v2.0.3 // indirect
|
||||||
github.com/zeebo/float16 v0.1.0 // indirect
|
github.com/zeebo/float16 v0.1.0 // indirect
|
||||||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
|
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
|
||||||
github.com/zeebo/mwc v0.0.4 // indirect
|
github.com/zeebo/mwc v0.0.4 // indirect
|
||||||
|
@ -1203,6 +1203,7 @@ github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtC
|
|||||||
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
|
||||||
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||||
github.com/zeebo/errs/v2 v2.0.3 h1:WwqAmopgot4ZC+CgIveP+H91Nf78NDEGWjtAXen45Hw=
|
github.com/zeebo/errs/v2 v2.0.3 h1:WwqAmopgot4ZC+CgIveP+H91Nf78NDEGWjtAXen45Hw=
|
||||||
|
github.com/zeebo/errs/v2 v2.0.3/go.mod h1:OKmvVZt4UqpyJrYFykDKm168ZquJ55pbbIVUICNmLN0=
|
||||||
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc=
|
github.com/zeebo/float16 v0.1.0 h1:kRqxv5og6z1emEyz5FpW0/BVHe5VfxEAw6b1ljCZlUc=
|
||||||
github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo=
|
github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo=
|
||||||
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 h1:+cwNE5KJ3pika4HuzmDHkDlK5myo0G9Sv+eO7WWxnUQ=
|
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 h1:+cwNE5KJ3pika4HuzmDHkDlK5myo0G9Sv+eO7WWxnUQ=
|
||||||
|
Loading…
Reference in New Issue
Block a user