diff --git a/AUTHORS b/AUTHORS index 43c5135..e8bd8c1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,8 @@ Thomas Sileo -Miguel Jacq Kevin Wallace +Miguel Jacq Josh Washburne Alexey Shpakovsky Ash McAllan Cassio Zen +Cocoa diff --git a/app/activitypub.py b/app/activitypub.py index 170811d..3a96e8b 100644 --- a/app/activitypub.py +++ b/app/activitypub.py @@ -135,11 +135,6 @@ ME = { "url": config.ID + "/", # XXX: the path is important for Mastodon compat "manuallyApprovesFollowers": config.CONFIG.manually_approves_followers, "attachment": _LOCAL_ACTOR_METADATA, - "icon": { - "mediaType": mimetypes.guess_type(config.CONFIG.icon_url)[0], - "type": "Image", - "url": config.CONFIG.icon_url, - }, "publicKey": { "id": f"{config.ID}#main-key", "owner": config.ID, @@ -148,6 +143,13 @@ ME = { "tag": dedup_tags(_LOCAL_ACTOR_TAGS), } +if config.CONFIG.icon_url: + ME["icon"] = { + "mediaType": mimetypes.guess_type(config.CONFIG.icon_url)[0], + "type": "Image", + "url": config.CONFIG.icon_url, + } + if ALSO_KNOWN_AS: ME["alsoKnownAs"] = [ALSO_KNOWN_AS] diff --git a/app/admin.py b/app/admin.py index 39cc863..7725803 100644 --- a/app/admin.py +++ b/app/admin.py @@ -61,14 +61,17 @@ async def user_session_or_redirect( ) if not session: + logger.info("No existing admin session") raise _RedirectToLoginPage try: - loaded_session = session_serializer.loads(session, max_age=3600 * 12) + loaded_session = session_serializer.loads(session, max_age=3600 * 24 * 3) except Exception: + logger.exception("Failed to validate admin session") raise _RedirectToLoginPage if not loaded_session.get("is_logged_in"): + logger.info(f"Admin session invalidated: {loaded_session}") raise _RedirectToLoginPage return None @@ -718,13 +721,9 @@ async def get_notifications( actors_metadata = await get_actors_metadata( db_session, [notif.actor for notif in notifications if notif.actor] ) - - for notif in notifications: - notif.is_new = False - await db_session.commit() - more_unread_count = 0 next_cursor = None + if notifications and remaining_count > page_size: decoded_next_cursor = notifications[-1].created_at next_cursor = pagination.encode_cursor(decoded_next_cursor) @@ -738,7 +737,8 @@ async def get_notifications( ) ) - return await templates.render_template( + # Render the template before we change the new flag on notifications + tpl_resp = await templates.render_template( db_session, request, "notifications.html", @@ -750,6 +750,13 @@ async def get_notifications( }, ) + if len({notif.id for notif in notifications if notif.is_new}): + for notif in notifications: + notif.is_new = False + await db_session.commit() + + return tpl_resp + @router.get("/object") async def admin_object( diff --git a/app/config.py b/app/config.py index 0dc868a..132030f 100644 --- a/app/config.py +++ b/app/config.py @@ -91,7 +91,7 @@ class Config(pydantic.BaseModel): name: str summary: str https: bool - icon_url: str + icon_url: str | None = None image_url: str | None = None secret: str debug: bool = False diff --git a/app/main.py b/app/main.py index 01e20a1..31c6f10 100644 --- a/app/main.py +++ b/app/main.py @@ -1179,7 +1179,11 @@ async def nodeinfo( ) -proxy_client = httpx.AsyncClient(follow_redirects=True, http2=True) +proxy_client = httpx.AsyncClient( + http2=True, + follow_redirects=True, + timeout=httpx.Timeout(timeout=10.0), +) async def _proxy_get( @@ -1459,7 +1463,7 @@ async def json_feed( ], } ) - return { + result = { "version": "https://jsonfeed.org/version/1", "title": f"{LOCAL_ACTOR.display_name}'s microblog'", "home_page_url": LOCAL_ACTOR.url, @@ -1467,10 +1471,12 @@ async def json_feed( "author": { "name": LOCAL_ACTOR.display_name, "url": LOCAL_ACTOR.url, - "avatar": LOCAL_ACTOR.icon_url, }, "items": data, } + if LOCAL_ACTOR.icon_url: + result["author"]["avatar"] = LOCAL_ACTOR.icon_url # type: ignore + return result async def _gen_rss_feed( @@ -1482,7 +1488,8 @@ async def _gen_rss_feed( fg.description(f"{LOCAL_ACTOR.display_name}'s microblog") fg.author({"name": LOCAL_ACTOR.display_name}) fg.link(href=LOCAL_ACTOR.url, rel="alternate") - fg.logo(LOCAL_ACTOR.icon_url) + if LOCAL_ACTOR.icon_url: + fg.logo(LOCAL_ACTOR.icon_url) fg.language("en") outbox_objects = await _get_outbox_for_feed(db_session) diff --git a/app/scss/main.scss b/app/scss/main.scss index 31400f6..8aa23e9 100644 --- a/app/scss/main.scss +++ b/app/scss/main.scss @@ -467,6 +467,9 @@ a.label-btn { span { color: $muted-color; } + span.new { + color: $secondary-color; + } } .actor-metadata { color: $muted-color; diff --git a/app/templates/notifications.html b/app/templates/notifications.html index 6d1cc3d..b59eaeb 100644 --- a/app/templates/notifications.html +++ b/app/templates/notifications.html @@ -10,6 +10,9 @@ {% if with_icon %}{{ utils.display_tiny_actor_icon(notif.actor) }}{% endif %} {{ notif.actor.display_name | clean_html(notif.actor) | safe }} {{ text }} {{ notif.created_at | timeago }} + {% if notif.is_new %} + new + {% endif %} {% endmacro %} @@ -66,8 +69,8 @@ {{ notif_actor_action(notif, "shared a post", with_icon=True) }} {{ utils.display_object(notif.outbox_object) }} {% elif notif.notification_type.value == "undo_announce" %} - {{ notif_actor_action(notif, "unshared a post") }} - {{ utils.display_object(notif.outbox_object, with_icon=True) }} + {{ notif_actor_action(notif, "unshared a post", with_icon=True) }} + {{ utils.display_object(notif.outbox_object) }} {% elif notif.notification_type.value == "mention" %} {{ notif_actor_action(notif, "mentioned you") }} {{ utils.display_object(notif.inbox_object) }} diff --git a/docs/install.md b/docs/install.md index 03e5804..4e8d444 100644 --- a/docs/install.md +++ b/docs/install.md @@ -193,4 +193,8 @@ http { ## YunoHost edition -[YunoHost](https://yunohost.org/) support is a work in progress. +[YunoHost](https://yunohost.org/) support is available (although it is not an official package for now): . + +## Available tutorial/guides + + - [Opalstack](https://community.opalstack.com/d/1055-howto-install-and-run-microblogpub-on-opalstack), thanks to [@defulmere@mastodon.social](https://mastodon.online/@defulmere). diff --git a/scripts/config_wizard.py b/scripts/config_wizard.py index 22119e6..912d726 100644 --- a/scripts/config_wizard.py +++ b/scripts/config_wizard.py @@ -75,9 +75,10 @@ def main() -> None: proto = "http" print("Note that you can put your icon/avatar in the static/ directory") - dat["icon_url"] = prompt( + if icon_url := prompt( "icon URL: ", default=f'{proto}://{dat["domain"]}/static/nopic.png' - ) + ): + dat["icon_url"] = icon_url dat["secret"] = os.urandom(16).hex() with config_file.open("w") as f: