Skip to content
Snippets Groups Projects
Commit adac55c5 authored by Travis Ralston's avatar Travis Ralston
Browse files

Upload usage API

parent ccc79710
No related branches found
No related tags found
No related merge requests found
package custom package custom
import ( import (
"fmt"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
...@@ -9,6 +8,7 @@ import ( ...@@ -9,6 +8,7 @@ import (
"github.com/turt2live/matrix-media-repo/api" "github.com/turt2live/matrix-media-repo/api"
"github.com/turt2live/matrix-media-repo/storage" "github.com/turt2live/matrix-media-repo/storage"
"github.com/turt2live/matrix-media-repo/types" "github.com/turt2live/matrix-media-repo/types"
"github.com/turt2live/matrix-media-repo/util"
) )
type MinimalUsageInfo struct { type MinimalUsageInfo struct {
...@@ -32,6 +32,18 @@ type UserUsageEntry struct { ...@@ -32,6 +32,18 @@ type UserUsageEntry struct {
UploadedMxcs []string `json:"uploaded,flow"` UploadedMxcs []string `json:"uploaded,flow"`
} }
type MediaUsageEntry struct {
SizeBytes int64 `json:"size_bytes"`
UploadedBy string `json:"uploaded_by"`
DatastoreId string `json:"datastore_id"`
DatastoreLocation string `json:"datastore_location"`
Sha256Hash string `json:"sha256_hash"`
Quarantined bool `json:"quarantined"`
UploadName string `json:"upload_name"`
ContentType string `json:"content_type"`
CreatedTs int64 `json:"created_ts"`
}
func GetDomainUsage(r *http.Request, log *logrus.Entry, user api.UserInfo) interface{} { func GetDomainUsage(r *http.Request, log *logrus.Entry, user api.UserInfo) interface{} {
params := mux.Vars(r) params := mux.Vars(r)
...@@ -123,7 +135,65 @@ func GetUserUsage(r *http.Request, log *logrus.Entry, user api.UserInfo) interfa ...@@ -123,7 +135,65 @@ func GetUserUsage(r *http.Request, log *logrus.Entry, user api.UserInfo) interfa
entry.RawCounts.Total += 1 entry.RawCounts.Total += 1
entry.RawCounts.Media += 1 entry.RawCounts.Media += 1
entry.UploadedMxcs = append(entry.UploadedMxcs, fmt.Sprintf("mxc://%s/%s", media.Origin, media.MediaId)) entry.UploadedMxcs = append(entry.UploadedMxcs, media.MxcUri())
}
return parsed
}
func GetUploadsUsage(r *http.Request, log *logrus.Entry, user api.UserInfo) interface{} {
params := mux.Vars(r)
serverName := params["serverName"]
mxcs := r.URL.Query()["mxc"]
log = log.WithFields(logrus.Fields{
"serverName": serverName,
})
db := storage.GetDatabase().GetMediaStore(r.Context(), log)
var records []*types.Media
var err error
if mxcs == nil || len(mxcs) == 0 {
records, err = db.GetAllMediaForServer(serverName)
} else {
split := make([]string, 0)
for _, mxc := range mxcs {
o, i, err := util.SplitMxc(mxc)
if err != nil {
log.Error(err)
return api.InternalServerError("Error parsing MXC " + mxc)
}
if o != serverName {
return api.BadRequest("MXC URIs must match the requested server")
}
split = append(split, i)
}
records, err = db.GetAllMediaInIds(serverName, split)
}
if err != nil {
log.Error(err)
return api.InternalServerError("Failed to get media records for users")
}
parsed := make(map[string]*MediaUsageEntry)
for _, media := range records {
parsed[media.MxcUri()] = &MediaUsageEntry{
SizeBytes: media.SizeBytes,
UploadName: media.UploadName,
ContentType: media.ContentType,
CreatedTs: media.CreationTs,
DatastoreId: media.DatastoreId,
DatastoreLocation: media.Location,
Quarantined: media.Quarantined,
Sha256Hash: media.Sha256Hash,
UploadedBy: media.UserId,
}
} }
return parsed return parsed
......
...@@ -44,6 +44,7 @@ func Init() { ...@@ -44,6 +44,7 @@ func Init() {
healthzHandler := handler{api.AccessTokenOptionalRoute(custom.GetHealthz), "healthz", counter, true} healthzHandler := handler{api.AccessTokenOptionalRoute(custom.GetHealthz), "healthz", counter, true}
domainUsageHandler := handler{api.RepoAdminRoute(custom.GetDomainUsage), "domain_usage", counter, false} domainUsageHandler := handler{api.RepoAdminRoute(custom.GetDomainUsage), "domain_usage", counter, false}
userUsageHandler := handler{api.RepoAdminRoute(custom.GetUserUsage), "user_usage", counter, false} userUsageHandler := handler{api.RepoAdminRoute(custom.GetUserUsage), "user_usage", counter, false}
uploadsUsageHandler := handler{api.RepoAdminRoute(custom.GetUploadsUsage), "uploads_usage", counter, false}
routes := make(map[string]route) routes := make(map[string]route)
versions := []string{"r0", "v1", "unstable"} // r0 is typically clients and v1 is typically servers. v1 is deprecated. versions := []string{"r0", "v1", "unstable"} // r0 is typically clients and v1 is typically servers. v1 is deprecated.
...@@ -68,6 +69,7 @@ func Init() { ...@@ -68,6 +69,7 @@ func Init() {
routes["/_matrix/media/"+version+"/admin/federation/test/{serverName:[a-zA-Z0-9.:\\-_]+}"] = route{"GET", fedTestHandler} routes["/_matrix/media/"+version+"/admin/federation/test/{serverName:[a-zA-Z0-9.:\\-_]+}"] = route{"GET", fedTestHandler}
routes["/_matrix/media/"+version+"/admin/usage/{serverName:[a-zA-Z0-9.:\\-_]+}"] = route{"GET", domainUsageHandler} routes["/_matrix/media/"+version+"/admin/usage/{serverName:[a-zA-Z0-9.:\\-_]+}"] = route{"GET", domainUsageHandler}
routes["/_matrix/media/"+version+"/admin/usage/{serverName:[a-zA-Z0-9.:\\-_]+}/users"] = route{"GET", userUsageHandler} routes["/_matrix/media/"+version+"/admin/usage/{serverName:[a-zA-Z0-9.:\\-_]+}/users"] = route{"GET", userUsageHandler}
routes["/_matrix/media/"+version+"/admin/usage/{serverName:[a-zA-Z0-9.:\\-_]+}/uploads"] = route{"GET", uploadsUsageHandler}
// Routes that we should handle but aren't in the media namespace (synapse compat) // Routes that we should handle but aren't in the media namespace (synapse compat)
routes["/_matrix/client/"+version+"/admin/purge_media_cache"] = route{"POST", purgeHandler} routes["/_matrix/client/"+version+"/admin/purge_media_cache"] = route{"POST", purgeHandler}
......
...@@ -25,6 +25,7 @@ const updateMediaDatastoreAndLocation = "UPDATE media SET location = $4, datasto ...@@ -25,6 +25,7 @@ const updateMediaDatastoreAndLocation = "UPDATE media SET location = $4, datasto
const selectAllDatastores = "SELECT datastore_id, ds_type, uri FROM datastores;" const selectAllDatastores = "SELECT datastore_id, ds_type, uri FROM datastores;"
const selectAllMediaForServer = "SELECT origin, media_id, upload_name, content_type, user_id, sha256_hash, size_bytes, datastore_id, location, creation_ts, quarantined FROM media WHERE origin = $1" const selectAllMediaForServer = "SELECT origin, media_id, upload_name, content_type, user_id, sha256_hash, size_bytes, datastore_id, location, creation_ts, quarantined FROM media WHERE origin = $1"
const selectAllMediaForServerUsers = "SELECT origin, media_id, upload_name, content_type, user_id, sha256_hash, size_bytes, datastore_id, location, creation_ts, quarantined FROM media WHERE origin = $1 AND user_id = ANY($2)" const selectAllMediaForServerUsers = "SELECT origin, media_id, upload_name, content_type, user_id, sha256_hash, size_bytes, datastore_id, location, creation_ts, quarantined FROM media WHERE origin = $1 AND user_id = ANY($2)"
const selectAllMediaForServerIds = "SELECT origin, media_id, upload_name, content_type, user_id, sha256_hash, size_bytes, datastore_id, location, creation_ts, quarantined FROM media WHERE origin = $1 AND media_id = ANY($2)"
var dsCacheByPath = sync.Map{} // [string] => Datastore var dsCacheByPath = sync.Map{} // [string] => Datastore
var dsCacheById = sync.Map{} // [string] => Datastore var dsCacheById = sync.Map{} // [string] => Datastore
...@@ -46,6 +47,7 @@ type mediaStoreStatements struct { ...@@ -46,6 +47,7 @@ type mediaStoreStatements struct {
selectMediaInDatastoreOlderThan *sql.Stmt selectMediaInDatastoreOlderThan *sql.Stmt
selectAllMediaForServer *sql.Stmt selectAllMediaForServer *sql.Stmt
selectAllMediaForServerUsers *sql.Stmt selectAllMediaForServerUsers *sql.Stmt
selectAllMediaForServerIds *sql.Stmt
} }
type MediaStoreFactory struct { type MediaStoreFactory struct {
...@@ -111,6 +113,9 @@ func InitMediaStore(sqlDb *sql.DB) (*MediaStoreFactory, error) { ...@@ -111,6 +113,9 @@ func InitMediaStore(sqlDb *sql.DB) (*MediaStoreFactory, error) {
if store.stmts.selectAllMediaForServerUsers, err = store.sqlDb.Prepare(selectAllMediaForServerUsers); err != nil { if store.stmts.selectAllMediaForServerUsers, err = store.sqlDb.Prepare(selectAllMediaForServerUsers); err != nil {
return nil, err return nil, err
} }
if store.stmts.selectAllMediaForServerIds, err = store.sqlDb.Prepare(selectAllMediaForServerIds); err != nil {
return nil, err
}
return &store, nil return &store, nil
} }
...@@ -453,3 +458,34 @@ func (s *MediaStore) GetAllMediaForServerUsers(serverName string, userIds []stri ...@@ -453,3 +458,34 @@ func (s *MediaStore) GetAllMediaForServerUsers(serverName string, userIds []stri
return results, nil return results, nil
} }
func (s *MediaStore) GetAllMediaInIds(serverName string, mediaIds []string) ([]*types.Media, error) {
rows, err := s.statements.selectAllMediaForServerIds.QueryContext(s.ctx, serverName, pq.Array(mediaIds))
if err != nil {
return nil, err
}
var results []*types.Media
for rows.Next() {
obj := &types.Media{}
err = rows.Scan(
&obj.Origin,
&obj.MediaId,
&obj.UploadName,
&obj.ContentType,
&obj.UserId,
&obj.Sha256Hash,
&obj.SizeBytes,
&obj.DatastoreId,
&obj.Location,
&obj.CreationTs,
&obj.Quarantined,
)
if err != nil {
return nil, err
}
results = append(results, obj)
}
return results, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment