diff --git a/app/config.py b/app/config.py index e27f8bd..687bf65 100644 --- a/app/config.py +++ b/app/config.py @@ -102,6 +102,9 @@ class Config(pydantic.BaseModel): emoji: str | None = None also_known_as: str | None = None + hides_followers: bool = False + hides_following: bool = False + inbox_retention_days: int = 15 # Config items to make tests easier @@ -144,6 +147,8 @@ _SCHEME = "https" if CONFIG.https else "http" ID = f"{_SCHEME}://{DOMAIN}" USERNAME = CONFIG.username MANUALLY_APPROVES_FOLLOWERS = CONFIG.manually_approves_followers +HIDES_FOLLOWERS = CONFIG.hides_followers +HIDES_FOLLOWING = CONFIG.hides_following PRIVACY_REPLACE = None if CONFIG.privacy_replace: PRIVACY_REPLACE = {pr.domain: pr.replace_by for pr in CONFIG.privacy_replace} diff --git a/app/main.py b/app/main.py index ea357ab..62e5e69 100644 --- a/app/main.py +++ b/app/main.py @@ -403,6 +403,20 @@ async def _build_followx_collection( return collection_page +async def _empty_followx_collection( + db_session: AsyncSession, + model_cls: Type[models.Following | models.Follower], + path: str, +) -> ap.RawObject: + total_items = await db_session.scalar(select(func.count(model_cls.id))) + return { + "@context": ap.AS_CTX, + "id": ID + path, + "type": "OrderedCollection", + "totalItems": total_items, + } + + @app.get("/followers") async def followers( request: Request, @@ -413,15 +427,27 @@ async def followers( _: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker), ) -> ActivityPubResponse | templates.TemplateResponse: if is_activitypub_requested(request): - return ActivityPubResponse( - await _build_followx_collection( - db_session=db_session, - model_cls=models.Follower, - path="/followers", - page=page, - next_cursor=next_cursor, + if config.HIDES_FOLLOWERS: + return ActivityPubResponse( + await _empty_followx_collection( + db_session=db_session, + model_cls=models.Follower, + path="/followers", + ) ) - ) + else: + return ActivityPubResponse( + await _build_followx_collection( + db_session=db_session, + model_cls=models.Follower, + path="/followers", + page=page, + next_cursor=next_cursor, + ) + ) + + if config.HIDES_FOLLOWERS: + raise HTTPException(status_code=404) # We only show the most recent 20 followers on the public website followers_result = await db_session.scalars( @@ -460,15 +486,27 @@ async def following( _: httpsig.HTTPSigInfo = Depends(httpsig.httpsig_checker), ) -> ActivityPubResponse | templates.TemplateResponse: if is_activitypub_requested(request): - return ActivityPubResponse( - await _build_followx_collection( - db_session=db_session, - model_cls=models.Following, - path="/following", - page=page, - next_cursor=next_cursor, + if config.HIDES_FOLLOWING: + return ActivityPubResponse( + await _empty_followx_collection( + db_session=db_session, + model_cls=models.Following, + path="/following", + ) ) - ) + else: + return ActivityPubResponse( + await _build_followx_collection( + db_session=db_session, + model_cls=models.Following, + path="/following", + page=page, + next_cursor=next_cursor, + ) + ) + + if config.HIDES_FOLLOWING: + raise HTTPException(status_code=404) # We only show the most recent 20 follows on the public website following = ( diff --git a/app/templates.py b/app/templates.py index f338d64..8d4e016 100644 --- a/app/templates.py +++ b/app/templates.py @@ -419,3 +419,5 @@ _templates.env.filters["privacy_replace_url"] = privacy_replace.replace_url _templates.env.globals["JS_HASH"] = config.JS_HASH _templates.env.globals["CSS_HASH"] = config.CSS_HASH _templates.env.globals["BASE_URL"] = config.BASE_URL +_templates.env.globals["HIDES_FOLLOWERS"] = config.HIDES_FOLLOWERS +_templates.env.globals["HIDES_FOLLOWING"] = config.HIDES_FOLLOWING diff --git a/app/templates/header.html b/app/templates/header.html index 1fe7d98..5c7467e 100644 --- a/app/templates/header.html +++ b/app/templates/header.html @@ -36,8 +36,12 @@ {% if articles_count %}