Merge branch 'list-public-channels' into 'develop'
Expose public libraries and channels in standard API See merge request funkwhale/funkwhale!1185
This commit is contained in:
commit
75f9537d89
|
@ -53,3 +53,13 @@ class ActorFetchDelay(preferences.DefaultFromSettingMixin, types.IntPreference):
|
||||||
"request authentication."
|
"request authentication."
|
||||||
)
|
)
|
||||||
field_kwargs = {"required": False}
|
field_kwargs = {"required": False}
|
||||||
|
|
||||||
|
|
||||||
|
@global_preferences_registry.register
|
||||||
|
class PublicIndex(types.BooleanPreference):
|
||||||
|
show_in_api = True
|
||||||
|
section = federation
|
||||||
|
name = "public_index"
|
||||||
|
default = True
|
||||||
|
verbose_name = "Enable public index"
|
||||||
|
help_text = "If this is enabled, public channels and libraries will be crawlable by other pods and bots"
|
||||||
|
|
|
@ -1096,9 +1096,6 @@ class CollectionPageSerializer(jsonld.JsonLdSerializer):
|
||||||
d = {
|
d = {
|
||||||
"id": id,
|
"id": id,
|
||||||
"partOf": conf["id"],
|
"partOf": conf["id"],
|
||||||
# XXX Stable release: remove the obsolete actor field
|
|
||||||
"actor": conf["actor"].fid,
|
|
||||||
"attributedTo": conf["actor"].fid,
|
|
||||||
"totalItems": page.paginator.count,
|
"totalItems": page.paginator.count,
|
||||||
"type": "CollectionPage",
|
"type": "CollectionPage",
|
||||||
"first": first,
|
"first": first,
|
||||||
|
@ -1110,6 +1107,10 @@ class CollectionPageSerializer(jsonld.JsonLdSerializer):
|
||||||
for i in page.object_list
|
for i in page.object_list
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
if conf["actor"]:
|
||||||
|
# XXX Stable release: remove the obsolete actor field
|
||||||
|
d["actor"] = conf["actor"].fid
|
||||||
|
d["attributedTo"] = conf["actor"].fid
|
||||||
|
|
||||||
if page.has_previous():
|
if page.has_previous():
|
||||||
d["prev"] = common_utils.set_query_parameter(
|
d["prev"] = common_utils.set_query_parameter(
|
||||||
|
@ -2030,3 +2031,33 @@ class DeleteSerializer(jsonld.JsonLdSerializer):
|
||||||
):
|
):
|
||||||
raise serializers.ValidationError("You cannot delete this object")
|
raise serializers.ValidationError("You cannot delete this object")
|
||||||
return validated_data
|
return validated_data
|
||||||
|
|
||||||
|
|
||||||
|
class IndexSerializer(jsonld.JsonLdSerializer):
|
||||||
|
type = serializers.ChoiceField(
|
||||||
|
choices=[contexts.AS.Collection, contexts.AS.OrderedCollection]
|
||||||
|
)
|
||||||
|
totalItems = serializers.IntegerField(min_value=0)
|
||||||
|
id = serializers.URLField(max_length=500)
|
||||||
|
first = serializers.URLField(max_length=500)
|
||||||
|
last = serializers.URLField(max_length=500)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
jsonld_mapping = PAGINATED_COLLECTION_JSONLD_MAPPING
|
||||||
|
|
||||||
|
def to_representation(self, conf):
|
||||||
|
paginator = Paginator(conf["items"], conf["page_size"])
|
||||||
|
first = common_utils.set_query_parameter(conf["id"], page=1)
|
||||||
|
current = first
|
||||||
|
last = common_utils.set_query_parameter(conf["id"], page=paginator.num_pages)
|
||||||
|
d = {
|
||||||
|
"id": conf["id"],
|
||||||
|
"totalItems": paginator.count,
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"current": current,
|
||||||
|
"first": first,
|
||||||
|
"last": last,
|
||||||
|
}
|
||||||
|
if self.context.get("include_ap_context", True):
|
||||||
|
d["@context"] = jsonld.get_default_context()
|
||||||
|
return d
|
||||||
|
|
|
@ -5,6 +5,7 @@ from . import views
|
||||||
|
|
||||||
router = routers.SimpleRouter(trailing_slash=False)
|
router = routers.SimpleRouter(trailing_slash=False)
|
||||||
music_router = routers.SimpleRouter(trailing_slash=False)
|
music_router = routers.SimpleRouter(trailing_slash=False)
|
||||||
|
index_router = routers.SimpleRouter(trailing_slash=False)
|
||||||
|
|
||||||
router.register(r"federation/shared", views.SharedViewSet, "shared")
|
router.register(r"federation/shared", views.SharedViewSet, "shared")
|
||||||
router.register(r"federation/actors", views.ActorViewSet, "actors")
|
router.register(r"federation/actors", views.ActorViewSet, "actors")
|
||||||
|
@ -17,6 +18,11 @@ music_router.register(r"uploads", views.MusicUploadViewSet, "uploads")
|
||||||
music_router.register(r"artists", views.MusicArtistViewSet, "artists")
|
music_router.register(r"artists", views.MusicArtistViewSet, "artists")
|
||||||
music_router.register(r"albums", views.MusicAlbumViewSet, "albums")
|
music_router.register(r"albums", views.MusicAlbumViewSet, "albums")
|
||||||
music_router.register(r"tracks", views.MusicTrackViewSet, "tracks")
|
music_router.register(r"tracks", views.MusicTrackViewSet, "tracks")
|
||||||
|
|
||||||
|
|
||||||
|
index_router.register(r"index", views.IndexViewSet, "index")
|
||||||
|
|
||||||
urlpatterns = router.urls + [
|
urlpatterns = router.urls + [
|
||||||
url("federation/music/", include((music_router.urls, "music"), namespace="music"))
|
url("federation/music/", include((music_router.urls, "music"), namespace="music")),
|
||||||
|
url("federation/", include((index_router.urls, "index"), namespace="index")),
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,6 +9,7 @@ from rest_framework.decorators import action
|
||||||
|
|
||||||
from funkwhale_api.common import preferences
|
from funkwhale_api.common import preferences
|
||||||
from funkwhale_api.common import utils as common_utils
|
from funkwhale_api.common import utils as common_utils
|
||||||
|
from funkwhale_api.federation import utils as federation_utils
|
||||||
from funkwhale_api.moderation import models as moderation_models
|
from funkwhale_api.moderation import models as moderation_models
|
||||||
from funkwhale_api.music import models as music_models
|
from funkwhale_api.music import models as music_models
|
||||||
from funkwhale_api.music import utils as music_utils
|
from funkwhale_api.music import utils as music_utils
|
||||||
|
@ -31,6 +32,34 @@ def redirect_to_html(public_url):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def get_collection_response(
|
||||||
|
conf, querystring, collection_serializer, page_access_check=None
|
||||||
|
):
|
||||||
|
page = querystring.get("page")
|
||||||
|
if page is None:
|
||||||
|
data = collection_serializer.data
|
||||||
|
else:
|
||||||
|
if page_access_check and not page_access_check():
|
||||||
|
raise exceptions.AuthenticationFailed(
|
||||||
|
"You do not have access to this resource"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
page_number = int(page)
|
||||||
|
except Exception:
|
||||||
|
return response.Response({"page": ["Invalid page number"]}, status=400)
|
||||||
|
conf["page_size"] = preferences.get("federation__collection_page_size")
|
||||||
|
p = paginator.Paginator(conf["items"], conf["page_size"])
|
||||||
|
try:
|
||||||
|
page = p.page(page_number)
|
||||||
|
conf["page"] = page
|
||||||
|
serializer = serializers.CollectionPageSerializer(conf)
|
||||||
|
data = serializer.data
|
||||||
|
except paginator.EmptyPage:
|
||||||
|
return response.Response(status=404)
|
||||||
|
|
||||||
|
return response.Response(data)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatedIfAllowListEnabled(permissions.BasePermission):
|
class AuthenticatedIfAllowListEnabled(permissions.BasePermission):
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
allow_list_enabled = preferences.get("moderation__allow_list_enabled")
|
allow_list_enabled = preferences.get("moderation__allow_list_enabled")
|
||||||
|
@ -128,26 +157,11 @@ class ActorViewSet(FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericV
|
||||||
.prefetch_related("library__channel__actor", "track__artist"),
|
.prefetch_related("library__channel__actor", "track__artist"),
|
||||||
"item_serializer": serializers.ChannelCreateUploadSerializer,
|
"item_serializer": serializers.ChannelCreateUploadSerializer,
|
||||||
}
|
}
|
||||||
page = request.GET.get("page")
|
return get_collection_response(
|
||||||
if page is None:
|
conf=conf,
|
||||||
serializer = serializers.ChannelOutboxSerializer(channel)
|
querystring=request.GET,
|
||||||
data = serializer.data
|
collection_serializer=serializers.ChannelOutboxSerializer(channel),
|
||||||
else:
|
)
|
||||||
try:
|
|
||||||
page_number = int(page)
|
|
||||||
except Exception:
|
|
||||||
return response.Response({"page": ["Invalid page number"]}, status=400)
|
|
||||||
conf["page_size"] = preferences.get("federation__collection_page_size")
|
|
||||||
p = paginator.Paginator(conf["items"], conf["page_size"])
|
|
||||||
try:
|
|
||||||
page = p.page(page_number)
|
|
||||||
conf["page"] = page
|
|
||||||
serializer = serializers.CollectionPageSerializer(conf)
|
|
||||||
data = serializer.data
|
|
||||||
except paginator.EmptyPage:
|
|
||||||
return response.Response(status=404)
|
|
||||||
|
|
||||||
return response.Response(data)
|
|
||||||
|
|
||||||
@action(methods=["get"], detail=True)
|
@action(methods=["get"], detail=True)
|
||||||
def followers(self, request, *args, **kwargs):
|
def followers(self, request, *args, **kwargs):
|
||||||
|
@ -290,32 +304,13 @@ class MusicLibraryViewSet(
|
||||||
),
|
),
|
||||||
"item_serializer": serializers.UploadSerializer,
|
"item_serializer": serializers.UploadSerializer,
|
||||||
}
|
}
|
||||||
page = request.GET.get("page")
|
|
||||||
if page is None:
|
|
||||||
serializer = serializers.LibrarySerializer(lb)
|
|
||||||
data = serializer.data
|
|
||||||
else:
|
|
||||||
# if actor is requesting a specific page, we ensure library is public
|
|
||||||
# or readable by the actor
|
|
||||||
if not has_library_access(request, lb):
|
|
||||||
raise exceptions.AuthenticationFailed(
|
|
||||||
"You do not have access to this library"
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
page_number = int(page)
|
|
||||||
except Exception:
|
|
||||||
return response.Response({"page": ["Invalid page number"]}, status=400)
|
|
||||||
conf["page_size"] = preferences.get("federation__collection_page_size")
|
|
||||||
p = paginator.Paginator(conf["items"], conf["page_size"])
|
|
||||||
try:
|
|
||||||
page = p.page(page_number)
|
|
||||||
conf["page"] = page
|
|
||||||
serializer = serializers.CollectionPageSerializer(conf)
|
|
||||||
data = serializer.data
|
|
||||||
except paginator.EmptyPage:
|
|
||||||
return response.Response(status=404)
|
|
||||||
|
|
||||||
return response.Response(data)
|
return get_collection_response(
|
||||||
|
conf=conf,
|
||||||
|
querystring=request.GET,
|
||||||
|
collection_serializer=serializers.LibrarySerializer(lb),
|
||||||
|
page_access_check=lambda: has_library_access(request, lb),
|
||||||
|
)
|
||||||
|
|
||||||
@action(methods=["get"], detail=True)
|
@action(methods=["get"], detail=True)
|
||||||
def followers(self, request, *args, **kwargs):
|
def followers(self, request, *args, **kwargs):
|
||||||
|
@ -436,3 +431,90 @@ class MusicTrackViewSet(
|
||||||
|
|
||||||
serializer = self.get_serializer(instance)
|
serializer = self.get_serializer(instance)
|
||||||
return response.Response(serializer.data)
|
return response.Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelViewSet(
|
||||||
|
FederationMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
|
||||||
|
):
|
||||||
|
authentication_classes = [authentication.SignatureAuthentication]
|
||||||
|
renderer_classes = renderers.get_ap_renderers()
|
||||||
|
queryset = music_models.Artist.objects.local().select_related(
|
||||||
|
"description", "attachment_cover"
|
||||||
|
)
|
||||||
|
serializer_class = serializers.ArtistSerializer
|
||||||
|
lookup_field = "uuid"
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
if utils.should_redirect_ap_to_html(request.headers.get("accept")):
|
||||||
|
return redirect_to_html(instance.get_absolute_url())
|
||||||
|
|
||||||
|
serializer = self.get_serializer(instance)
|
||||||
|
return response.Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
class IndexViewSet(FederationMixin, viewsets.GenericViewSet):
|
||||||
|
authentication_classes = [authentication.SignatureAuthentication]
|
||||||
|
renderer_classes = renderers.get_ap_renderers()
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if not preferences.get("federation__public_index"):
|
||||||
|
return HttpResponse(status=405)
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=["get"], detail=False,
|
||||||
|
)
|
||||||
|
def libraries(self, request, *args, **kwargs):
|
||||||
|
libraries = (
|
||||||
|
music_models.Library.objects.local()
|
||||||
|
.filter(channel=None, privacy_level="everyone")
|
||||||
|
.prefetch_related("actor")
|
||||||
|
.order_by("creation_date")
|
||||||
|
)
|
||||||
|
conf = {
|
||||||
|
"id": federation_utils.full_url(
|
||||||
|
reverse("federation:index:index-libraries")
|
||||||
|
),
|
||||||
|
"items": libraries,
|
||||||
|
"item_serializer": serializers.LibrarySerializer,
|
||||||
|
"page_size": 100,
|
||||||
|
"actor": None,
|
||||||
|
}
|
||||||
|
return get_collection_response(
|
||||||
|
conf=conf,
|
||||||
|
querystring=request.GET,
|
||||||
|
collection_serializer=serializers.IndexSerializer(conf),
|
||||||
|
)
|
||||||
|
|
||||||
|
return response.Response({}, status=200)
|
||||||
|
|
||||||
|
@action(
|
||||||
|
methods=["get"], detail=False,
|
||||||
|
)
|
||||||
|
def channels(self, request, *args, **kwargs):
|
||||||
|
actors = (
|
||||||
|
models.Actor.objects.local()
|
||||||
|
.exclude(channel=None)
|
||||||
|
.order_by("channel__creation_date")
|
||||||
|
.prefetch_related(
|
||||||
|
"channel__attributed_to",
|
||||||
|
"channel__artist",
|
||||||
|
"channel__artist__description",
|
||||||
|
"channel__artist__attachment_cover",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
conf = {
|
||||||
|
"id": federation_utils.full_url(reverse("federation:index:index-channels")),
|
||||||
|
"items": actors,
|
||||||
|
"item_serializer": serializers.ActorSerializer,
|
||||||
|
"page_size": 100,
|
||||||
|
"actor": None,
|
||||||
|
}
|
||||||
|
return get_collection_response(
|
||||||
|
conf=conf,
|
||||||
|
querystring=request.GET,
|
||||||
|
collection_serializer=serializers.IndexSerializer(conf),
|
||||||
|
)
|
||||||
|
|
||||||
|
return response.Response({}, status=200)
|
||||||
|
|
|
@ -67,7 +67,7 @@ def get():
|
||||||
"instance__funkwhale_support_message_enabled"
|
"instance__funkwhale_support_message_enabled"
|
||||||
),
|
),
|
||||||
"instanceSupportMessage": all_preferences.get("instance__support_message"),
|
"instanceSupportMessage": all_preferences.get("instance__support_message"),
|
||||||
"knownNodesListUrl": None,
|
"endpoints": {"knownNodes": None, "channels": None, "libraries": None},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,14 @@ def get():
|
||||||
"downloads": {"total": statistics["downloads"]},
|
"downloads": {"total": statistics["downloads"]},
|
||||||
}
|
}
|
||||||
if not auth_required:
|
if not auth_required:
|
||||||
data["metadata"]["knownNodesListUrl"] = federation_utils.full_url(
|
data["metadata"]["endpoints"]["knownNodes"] = federation_utils.full_url(
|
||||||
reverse("api:v1:federation:domains-list")
|
reverse("api:v1:federation:domains-list")
|
||||||
)
|
)
|
||||||
|
if not auth_required and preferences.get("federation__public_index"):
|
||||||
|
data["metadata"]["endpoints"]["libraries"] = federation_utils.full_url(
|
||||||
|
reverse("federation:index:index-libraries")
|
||||||
|
)
|
||||||
|
data["metadata"]["endpoints"]["channels"] = federation_utils.full_url(
|
||||||
|
reverse("federation:index:index-channels")
|
||||||
|
)
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -517,3 +517,113 @@ def test_artist_retrieve_redirects_to_html_if_header_set(
|
||||||
)
|
)
|
||||||
assert response.status_code == 302
|
assert response.status_code == 302
|
||||||
assert response["Location"] == expected_url
|
assert response["Location"] == expected_url
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("index", ["channels", "libraries"])
|
||||||
|
def test_public_index_disabled(index, api_client, preferences):
|
||||||
|
preferences["federation__public_index"] = False
|
||||||
|
url = reverse("federation:index:index-{}".format(index))
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 405
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_channels_retrieve(factories, api_client):
|
||||||
|
channels = [
|
||||||
|
factories["audio.Channel"](actor__local=True),
|
||||||
|
factories["audio.Channel"](actor__local=True),
|
||||||
|
factories["audio.Channel"](actor__local=True),
|
||||||
|
]
|
||||||
|
expected = serializers.IndexSerializer(
|
||||||
|
{
|
||||||
|
"id": federation_utils.full_url(reverse("federation:index:index-channels")),
|
||||||
|
"items": channels[0].__class__.objects.order_by("creation_date"),
|
||||||
|
"page_size": 100,
|
||||||
|
},
|
||||||
|
).data
|
||||||
|
|
||||||
|
url = reverse("federation:index:index-channels",)
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_channels_page(factories, api_client, preferences):
|
||||||
|
preferences["federation__collection_page_size"] = 1
|
||||||
|
remote_actor = factories["federation.Actor"]()
|
||||||
|
channels = [
|
||||||
|
factories["audio.Channel"](actor__local=True),
|
||||||
|
factories["audio.Channel"](actor__local=True),
|
||||||
|
factories["audio.Channel"](actor__local=True),
|
||||||
|
factories["audio.Channel"](actor=remote_actor),
|
||||||
|
]
|
||||||
|
id = federation_utils.full_url(reverse("federation:index:index-channels"))
|
||||||
|
expected = serializers.CollectionPageSerializer(
|
||||||
|
{
|
||||||
|
"id": id,
|
||||||
|
"item_serializer": serializers.ActorSerializer,
|
||||||
|
"page": Paginator([c.actor for c in channels][:3], 1).page(1),
|
||||||
|
"actor": None,
|
||||||
|
}
|
||||||
|
).data
|
||||||
|
|
||||||
|
url = reverse("federation:index:index-channels")
|
||||||
|
response = api_client.get(url, {"page": 1})
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_libraries_retrieve(factories, api_client):
|
||||||
|
remote_actor = factories["federation.Actor"]()
|
||||||
|
libraries = [
|
||||||
|
factories["music.Library"](actor__local=True, privacy_level="everyone"),
|
||||||
|
factories["music.Library"](actor__local=True, privacy_level="everyone"),
|
||||||
|
factories["music.Library"](actor__local=True, privacy_level="me"),
|
||||||
|
factories["music.Library"](actor=remote_actor, privacy_level="everyone"),
|
||||||
|
]
|
||||||
|
expected = serializers.IndexSerializer(
|
||||||
|
{
|
||||||
|
"id": federation_utils.full_url(
|
||||||
|
reverse("federation:index:index-libraries")
|
||||||
|
),
|
||||||
|
"items": libraries[0]
|
||||||
|
.__class__.objects.local()
|
||||||
|
.filter(privacy_level="everyone")
|
||||||
|
.order_by("creation_date"),
|
||||||
|
"page_size": 100,
|
||||||
|
},
|
||||||
|
).data
|
||||||
|
|
||||||
|
url = reverse("federation:index:index-libraries")
|
||||||
|
response = api_client.get(url)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_index_libraries_page(factories, api_client, preferences):
|
||||||
|
preferences["federation__collection_page_size"] = 1
|
||||||
|
remote_actor = factories["federation.Actor"]()
|
||||||
|
libraries = [
|
||||||
|
factories["music.Library"](actor__local=True, privacy_level="everyone"),
|
||||||
|
factories["music.Library"](actor__local=True, privacy_level="everyone"),
|
||||||
|
factories["music.Library"](actor__local=True, privacy_level="me"),
|
||||||
|
factories["music.Library"](actor=remote_actor, privacy_level="everyone"),
|
||||||
|
]
|
||||||
|
id = federation_utils.full_url(reverse("federation:index:index-libraries"))
|
||||||
|
expected = serializers.CollectionPageSerializer(
|
||||||
|
{
|
||||||
|
"id": id,
|
||||||
|
"item_serializer": serializers.LibrarySerializer,
|
||||||
|
"page": Paginator(libraries[:2], 1).page(1),
|
||||||
|
"actor": None,
|
||||||
|
}
|
||||||
|
).data
|
||||||
|
|
||||||
|
url = reverse("federation:index:index-libraries")
|
||||||
|
response = api_client.get(url, {"page": 1})
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data == expected
|
||||||
|
|
|
@ -93,9 +93,17 @@ def test_nodeinfo_dump(preferences, mocker, avatar):
|
||||||
"instance__funkwhale_support_message_enabled"
|
"instance__funkwhale_support_message_enabled"
|
||||||
],
|
],
|
||||||
"instanceSupportMessage": preferences["instance__support_message"],
|
"instanceSupportMessage": preferences["instance__support_message"],
|
||||||
"knownNodesListUrl": federation_utils.full_url(
|
"endpoints": {
|
||||||
|
"knownNodes": federation_utils.full_url(
|
||||||
reverse("api:v1:federation:domains-list")
|
reverse("api:v1:federation:domains-list")
|
||||||
),
|
),
|
||||||
|
"libraries": federation_utils.full_url(
|
||||||
|
reverse("federation:index:index-libraries")
|
||||||
|
),
|
||||||
|
"channels": federation_utils.full_url(
|
||||||
|
reverse("federation:index:index-channels")
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert nodeinfo.get() == expected
|
assert nodeinfo.get() == expected
|
||||||
|
@ -103,6 +111,7 @@ def test_nodeinfo_dump(preferences, mocker, avatar):
|
||||||
|
|
||||||
def test_nodeinfo_dump_stats_disabled(preferences, mocker):
|
def test_nodeinfo_dump_stats_disabled(preferences, mocker):
|
||||||
preferences["instance__nodeinfo_stats_enabled"] = False
|
preferences["instance__nodeinfo_stats_enabled"] = False
|
||||||
|
preferences["federation__public_index"] = False
|
||||||
preferences["moderation__unauthenticated_report_types"] = [
|
preferences["moderation__unauthenticated_report_types"] = [
|
||||||
"takedown_request",
|
"takedown_request",
|
||||||
"other",
|
"other",
|
||||||
|
@ -161,7 +170,7 @@ def test_nodeinfo_dump_stats_disabled(preferences, mocker):
|
||||||
"instance__funkwhale_support_message_enabled"
|
"instance__funkwhale_support_message_enabled"
|
||||||
],
|
],
|
||||||
"instanceSupportMessage": preferences["instance__support_message"],
|
"instanceSupportMessage": preferences["instance__support_message"],
|
||||||
"knownNodesListUrl": None,
|
"endpoints": {"knownNodes": None, "libraries": None, "channels": None},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert nodeinfo.get() == expected
|
assert nodeinfo.get() == expected
|
||||||
|
|
|
@ -159,6 +159,7 @@ export default {
|
||||||
id: "federation",
|
id: "federation",
|
||||||
settings: [
|
settings: [
|
||||||
{name: "federation__enabled"},
|
{name: "federation__enabled"},
|
||||||
|
{name: "federation__public_index"},
|
||||||
{name: "federation__collection_page_size"},
|
{name: "federation__collection_page_size"},
|
||||||
{name: "federation__music_cache_duration"},
|
{name: "federation__music_cache_duration"},
|
||||||
{name: "federation__actor_fetch_delay"},
|
{name: "federation__actor_fetch_delay"},
|
||||||
|
|
Loading…
Reference in New Issue