Skip to content

Commit

Permalink
Restore original paths for admin API
Browse files Browse the repository at this point in the history
Fixes #447
  • Loading branch information
turt2live committed Sep 6, 2023
1 parent be93a68 commit e423507
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 28 deletions.
11 changes: 0 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,6 @@ path/server, for example, then you can simply update the path in the config for
* If compiling `matrix-media-repo`, note that new external dependencies are required. See [the docs](https://docs.t2bot.io/matrix-media-repo/installing/method/compilation.html).
* Docker images already contain these dependencies.
* Datastores no longer use the `enabled` flag set on them. Use `forKinds: []` instead to disable a datastore's usage.
* Some admin endpoints for purging media, quarantining media, and background task information now require additional path components.
```diff
-POST /_matrix/media/unstable/admin/purge/<server>/<media id>?access_token=your_access_token
+POST /_matrix/media/unstable/admin/purge/media/<server>/<media id>?access_token=your_access_token
-POST /_matrix/media/unstable/admin/quarantine/<server>/<media id>?access_token=your_access_token
+POST /_matrix/media/unstable/admin/quarantine/media/<server>/<media id>?access_token=your_access_token
-GET /_matrix/media/unstable/admin/tasks/<task ID>
+GET /_matrix/media/unstable/admin/task/<task ID>
```
* Per-user upload quotas now do not allow users to exceed the maximum values, even by 1 byte. Previously, users could exceed the limits by a little bit.
* Updated to Go 1.19, then Go 1.20 in the same release cycle.
* New CGO dependencies are required. See [docs.t2bot.io](https://docs.t2bot.io/matrix-media-repo/installing/method/compilation.html) for details.
Expand Down
20 changes: 20 additions & 0 deletions api/_routers/00-install-params.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package _routers

import (
"context"
"errors"
"net/http"
"regexp"
Expand All @@ -27,3 +28,22 @@ func GetParam(name string, r *http.Request) string {
}
return p.ByName(name)
}

func ForceSetParam(name string, val string, r *http.Request) *http.Request {
params := httprouter.ParamsFromContext(r.Context())
wasSet := false
for _, p := range params {
if p.Key == name {
p.Value = val
wasSet = true
break
}
}
if !wasSet {
params = append(params, httprouter.Param{
Key: name,
Value: val,
})
}
return r.WithContext(context.WithValue(r.Context(), httprouter.ParamsKey, params))
}
50 changes: 50 additions & 0 deletions api/branched_route.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package api

import (
"net/http"
"strings"

"github.com/turt2live/matrix-media-repo/api/_routers"
)

type branch struct {
string
http.Handler
}

type splitBranch struct {
segments []string
handler http.Handler
}

func branchedRoute(branches []branch) http.Handler {
sbranches := make([]splitBranch, len(branches))
for i, b := range branches {
sbranches[i] = splitBranch{
segments: strings.Split(b.string, "/"),
handler: b.Handler,
}
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
catchAll := _routers.GetParam("branch", r)
if catchAll[0] == '/' {
catchAll = catchAll[1:]
}
params := strings.Split(catchAll, "/")
for _, b := range sbranches {
if b.segments[0][0] == ':' || b.segments[0] == params[0] {
if len(b.segments) != len(params) {
continue
}
for i, s := range b.segments {
if s[0] == ':' {
r = _routers.ForceSetParam(s[1:], params[i], r)
}
}
b.handler.ServeHTTP(w, r)
return
}
}
notFoundFn(w, r)
})
}
37 changes: 23 additions & 14 deletions api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,27 @@ func buildRoutes() http.Handler {

// All admin routes are unstable only
purgeRemoteRoute := makeRoute(_routers.RequireRepoAdmin(custom.PurgeRemoteMedia), "purge_remote_media", counter)
purgeBranch := branchedRoute([]branch{
{"remote", purgeRemoteRoute},
{"old", makeRoute(_routers.RequireRepoAdmin(custom.PurgeOldMedia), "purge_old_media", counter)},
{"quarantined", makeRoute(_routers.RequireAccessToken(custom.PurgeQuarantined), "purge_quarantined", counter)},
{"user/:userId", makeRoute(_routers.RequireAccessToken(custom.PurgeUserMedia), "purge_user_media", counter)},
{"room/:roomId", makeRoute(_routers.RequireAccessToken(custom.PurgeRoomMedia), "purge_room_media", counter)},
{"server/:serverName", makeRoute(_routers.RequireAccessToken(custom.PurgeDomainMedia), "purge_domain_media", counter)},
{":server/:mediaId", purgeOneRoute},
})
register([]string{"POST"}, PrefixMedia, "admin/purge/*branch", mxUnstable, router, purgeBranch)
register([]string{"POST"}, PrefixMedia, "admin/purge_remote", mxUnstable, router, purgeRemoteRoute)
register([]string{"POST"}, PrefixMedia, "admin/purge/remote", mxUnstable, router, purgeRemoteRoute)
register([]string{"POST"}, PrefixClient, "admin/purge_media_cache", mxUnstable, router, purgeRemoteRoute) // synapse compat
register([]string{"POST"}, PrefixMedia, "admin/purge/media/:server/:mediaId", mxUnstable, router, purgeOneRoute)
register([]string{"POST"}, PrefixMedia, "admin/purge/old", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.PurgeOldMedia), "purge_old_media", counter))
register([]string{"POST"}, PrefixMedia, "admin/purge/quarantined", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.PurgeQuarantined), "purge_quarantined", counter))
register([]string{"POST"}, PrefixMedia, "admin/purge/user/:userId", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.PurgeUserMedia), "purge_user_media", counter))
register([]string{"POST"}, PrefixMedia, "admin/purge/room/:roomId", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.PurgeRoomMedia), "purge_room_media", counter))
register([]string{"POST"}, PrefixMedia, "admin/purge/server/:serverName", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.PurgeDomainMedia), "purge_domain_media", counter))
quarantineRoomRoute := makeRoute(_routers.RequireAccessToken(custom.QuarantineRoomMedia), "quarantine_room", counter)
register([]string{"POST"}, PrefixMedia, "admin/quarantine/room/:roomId", mxUnstable, router, quarantineRoomRoute)
quarantineBranch := branchedRoute([]branch{
{"room/:roomId", quarantineRoomRoute},
{"user/:userId", makeRoute(_routers.RequireAccessToken(custom.QuarantineUserMedia), "quarantine_user", counter)},
{"server/:serverName", makeRoute(_routers.RequireAccessToken(custom.QuarantineDomainMedia), "quarantine_domain", counter)},
{":server/:mediaId", makeRoute(_routers.RequireAccessToken(custom.QuarantineMedia), "quarantine_media", counter)},
})
register([]string{"POST"}, PrefixMedia, "admin/quarantine/*branch", mxUnstable, router, quarantineBranch)
register([]string{"POST"}, PrefixClient, "admin/quarantine_media/:roomId", mxUnstable, router, quarantineRoomRoute) // synapse compat
register([]string{"POST"}, PrefixMedia, "admin/quarantine/user/:userId", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.QuarantineUserMedia), "quarantine_user", counter))
register([]string{"POST"}, PrefixMedia, "admin/quarantine/server/:serverName", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.QuarantineDomainMedia), "quarantine_domain", counter))
register([]string{"POST"}, PrefixMedia, "admin/quarantine/media/:server/:mediaId", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.QuarantineMedia), "quarantine_media", counter))
register([]string{"GET"}, PrefixMedia, "admin/datastores/:datastoreId/size_estimate", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.GetDatastoreStorageEstimate), "get_storage_estimate", counter))
register([]string{"POST"}, PrefixMedia, "admin/datastores/:sourceDsId/transfer_to/:targetDsId", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.MigrateBetweenDatastores), "datastore_transfer", counter))
register([]string{"GET"}, PrefixMedia, "admin/datastores", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.GetDatastores), "list_datastores", counter))
Expand All @@ -85,9 +91,12 @@ func buildRoutes() http.Handler {
register([]string{"GET"}, PrefixMedia, "admin/usage/:serverName/users", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.GetUserUsage), "user_usage", counter))
register([]string{"GET"}, PrefixMedia, "admin/usage/:serverName/users-stats", mxUnstable, router, synUserStatsRoute)
register([]string{"GET"}, PrefixMedia, "admin/usage/:serverName/uploads", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.GetUploadsUsage), "uploads_usage", counter))
register([]string{"GET"}, PrefixMedia, "admin/task/:taskId", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.GetTask), "get_background_task", counter))
register([]string{"GET"}, PrefixMedia, "admin/tasks/all", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.ListAllTasks), "list_all_background_tasks", counter))
register([]string{"GET"}, PrefixMedia, "admin/tasks/unfinished", mxUnstable, router, makeRoute(_routers.RequireRepoAdmin(custom.ListUnfinishedTasks), "list_unfinished_background_tasks", counter))
tasksBranch := branchedRoute([]branch{
{"all", makeRoute(_routers.RequireRepoAdmin(custom.ListAllTasks), "list_all_background_tasks", counter)},
{"unfinished", makeRoute(_routers.RequireRepoAdmin(custom.ListUnfinishedTasks), "list_unfinished_background_tasks", counter)},
{":taskId", makeRoute(_routers.RequireRepoAdmin(custom.GetTask), "get_background_task", counter)},
})
register([]string{"GET"}, PrefixMedia, "admin/tasks/*branch", mxUnstable, router, tasksBranch)
register([]string{"POST"}, PrefixMedia, "admin/user/:userId/export", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.ExportUserData), "export_user_data", counter))
register([]string{"POST"}, PrefixMedia, "admin/server/:serverName/export", mxUnstable, router, makeRoute(_routers.RequireAccessToken(custom.ExportServerData), "export_server_data", counter))
register([]string{"GET"}, PrefixMedia, "admin/export/:exportId/view", mxUnstable, router, makeRoute(_routers.OptionalAccessToken(custom.ViewExport), "view_export", counter))
Expand Down
6 changes: 3 additions & 3 deletions docs/admin.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ This will delete all media that has previously been quarantined, local or remote

#### Purge individual record

URL: `POST /_matrix/media/unstable/admin/purge/media/<server>/<media id>?access_token=your_access_token`
URL: `POST /_matrix/media/unstable/admin/purge/<server>/<media id>?access_token=your_access_token`

**Note**: Prior to v1.3, this endpoint did not require the `/media` component, but does now.

Expand Down Expand Up @@ -90,7 +90,7 @@ This API is unique in that it can allow administrators of configured homeservers

#### Quarantine a specific record

URL: `POST /_matrix/media/unstable/admin/quarantine/media/<server>/<media id>?access_token=your_access_token`
URL: `POST /_matrix/media/unstable/admin/quarantine/<server>/<media id>?access_token=your_access_token`

**Note**: Prior to v1.3, this endpoint did not require the `/media` component, but does now.

Expand Down Expand Up @@ -367,7 +367,7 @@ The response is a list of all unfinished tasks:

#### Getting information on a specific task

URL: `GET /_matrix/media/unstable/admin/task/<task ID>`
URL: `GET /_matrix/media/unstable/admin/tasks/<task ID>`

**Note**: Prior to v1.3, this endpoint was "tasks" (plural). It is now singular.

Expand Down

0 comments on commit e423507

Please sign in to comment.