Merge branch 'v2' into indieweb-merge-part2

main
Thomas Sileo 2022-11-20 10:48:43 +01:00
commit d36102255f
9 changed files with 51 additions and 23 deletions

View File

@ -1,7 +1,8 @@
Thomas Sileo <t@a4.io> Thomas Sileo <t@a4.io>
Miguel Jacq <mig@mig5.net>
Kevin Wallace <doof@doof.net> Kevin Wallace <doof@doof.net>
Miguel Jacq <mig@mig5.net>
Josh Washburne <josh@jodh.us> Josh Washburne <josh@jodh.us>
Alexey Shpakovsky <alexey@shpakovsky.ru> Alexey Shpakovsky <alexey@shpakovsky.ru>
Ash McAllan <acegiak@gmail.com> Ash McAllan <acegiak@gmail.com>
Cassio Zen <cassio@hey.com> Cassio Zen <cassio@hey.com>
Cocoa <momijizukamori@gmail.com>

View File

@ -135,11 +135,6 @@ ME = {
"url": config.ID + "/", # XXX: the path is important for Mastodon compat "url": config.ID + "/", # XXX: the path is important for Mastodon compat
"manuallyApprovesFollowers": config.CONFIG.manually_approves_followers, "manuallyApprovesFollowers": config.CONFIG.manually_approves_followers,
"attachment": _LOCAL_ACTOR_METADATA, "attachment": _LOCAL_ACTOR_METADATA,
"icon": {
"mediaType": mimetypes.guess_type(config.CONFIG.icon_url)[0],
"type": "Image",
"url": config.CONFIG.icon_url,
},
"publicKey": { "publicKey": {
"id": f"{config.ID}#main-key", "id": f"{config.ID}#main-key",
"owner": config.ID, "owner": config.ID,
@ -148,6 +143,13 @@ ME = {
"tag": dedup_tags(_LOCAL_ACTOR_TAGS), "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: if ALSO_KNOWN_AS:
ME["alsoKnownAs"] = [ALSO_KNOWN_AS] ME["alsoKnownAs"] = [ALSO_KNOWN_AS]

View File

@ -61,14 +61,17 @@ async def user_session_or_redirect(
) )
if not session: if not session:
logger.info("No existing admin session")
raise _RedirectToLoginPage raise _RedirectToLoginPage
try: try:
loaded_session = session_serializer.loads(session, max_age=3600 * 12) loaded_session = session_serializer.loads(session, max_age=3600 * 24 * 3)
except Exception: except Exception:
logger.exception("Failed to validate admin session")
raise _RedirectToLoginPage raise _RedirectToLoginPage
if not loaded_session.get("is_logged_in"): if not loaded_session.get("is_logged_in"):
logger.info(f"Admin session invalidated: {loaded_session}")
raise _RedirectToLoginPage raise _RedirectToLoginPage
return None return None
@ -718,13 +721,9 @@ async def get_notifications(
actors_metadata = await get_actors_metadata( actors_metadata = await get_actors_metadata(
db_session, [notif.actor for notif in notifications if notif.actor] 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 more_unread_count = 0
next_cursor = None next_cursor = None
if notifications and remaining_count > page_size: if notifications and remaining_count > page_size:
decoded_next_cursor = notifications[-1].created_at decoded_next_cursor = notifications[-1].created_at
next_cursor = pagination.encode_cursor(decoded_next_cursor) 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, db_session,
request, request,
"notifications.html", "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") @router.get("/object")
async def admin_object( async def admin_object(

View File

@ -91,7 +91,7 @@ class Config(pydantic.BaseModel):
name: str name: str
summary: str summary: str
https: bool https: bool
icon_url: str icon_url: str | None = None
image_url: str | None = None image_url: str | None = None
secret: str secret: str
debug: bool = False debug: bool = False

View File

@ -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( async def _proxy_get(
@ -1459,7 +1463,7 @@ async def json_feed(
], ],
} }
) )
return { result = {
"version": "https://jsonfeed.org/version/1", "version": "https://jsonfeed.org/version/1",
"title": f"{LOCAL_ACTOR.display_name}'s microblog'", "title": f"{LOCAL_ACTOR.display_name}'s microblog'",
"home_page_url": LOCAL_ACTOR.url, "home_page_url": LOCAL_ACTOR.url,
@ -1467,10 +1471,12 @@ async def json_feed(
"author": { "author": {
"name": LOCAL_ACTOR.display_name, "name": LOCAL_ACTOR.display_name,
"url": LOCAL_ACTOR.url, "url": LOCAL_ACTOR.url,
"avatar": LOCAL_ACTOR.icon_url,
}, },
"items": data, "items": data,
} }
if LOCAL_ACTOR.icon_url:
result["author"]["avatar"] = LOCAL_ACTOR.icon_url # type: ignore
return result
async def _gen_rss_feed( async def _gen_rss_feed(
@ -1482,7 +1488,8 @@ async def _gen_rss_feed(
fg.description(f"{LOCAL_ACTOR.display_name}'s microblog") fg.description(f"{LOCAL_ACTOR.display_name}'s microblog")
fg.author({"name": LOCAL_ACTOR.display_name}) fg.author({"name": LOCAL_ACTOR.display_name})
fg.link(href=LOCAL_ACTOR.url, rel="alternate") 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") fg.language("en")
outbox_objects = await _get_outbox_for_feed(db_session) outbox_objects = await _get_outbox_for_feed(db_session)

View File

@ -467,6 +467,9 @@ a.label-btn {
span { span {
color: $muted-color; color: $muted-color;
} }
span.new {
color: $secondary-color;
}
} }
.actor-metadata { .actor-metadata {
color: $muted-color; color: $muted-color;

View File

@ -10,6 +10,9 @@
<a href="{{ url_for("admin_profile") }}?actor_id={{ notif.actor.ap_id }}"> <a href="{{ url_for("admin_profile") }}?actor_id={{ notif.actor.ap_id }}">
{% if with_icon %}{{ utils.display_tiny_actor_icon(notif.actor) }}{% endif %} {{ notif.actor.display_name | clean_html(notif.actor) | safe }}</a> {{ text }} {% if with_icon %}{{ utils.display_tiny_actor_icon(notif.actor) }}{% endif %} {{ notif.actor.display_name | clean_html(notif.actor) | safe }}</a> {{ text }}
<span title="{{ notif.created_at.isoformat() }}">{{ notif.created_at | timeago }}</span> <span title="{{ notif.created_at.isoformat() }}">{{ notif.created_at | timeago }}</span>
{% if notif.is_new %}
<span class="new">new</span>
{% endif %}
</div> </div>
{% endmacro %} {% endmacro %}
@ -66,8 +69,8 @@
{{ notif_actor_action(notif, "shared a post", with_icon=True) }} {{ notif_actor_action(notif, "shared a post", with_icon=True) }}
{{ utils.display_object(notif.outbox_object) }} {{ utils.display_object(notif.outbox_object) }}
{% elif notif.notification_type.value == "undo_announce" %} {% elif notif.notification_type.value == "undo_announce" %}
{{ notif_actor_action(notif, "unshared a post") }} {{ notif_actor_action(notif, "unshared a post", with_icon=True) }}
{{ utils.display_object(notif.outbox_object, with_icon=True) }} {{ utils.display_object(notif.outbox_object) }}
{% elif notif.notification_type.value == "mention" %} {% elif notif.notification_type.value == "mention" %}
{{ notif_actor_action(notif, "mentioned you") }} {{ notif_actor_action(notif, "mentioned you") }}
{{ utils.display_object(notif.inbox_object) }} {{ utils.display_object(notif.inbox_object) }}

View File

@ -193,4 +193,8 @@ http {
## YunoHost edition ## 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): <https://git.sr.ht/~tsileo/microblog.pub_ynh>.
## 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).

View File

@ -75,9 +75,10 @@ def main() -> None:
proto = "http" proto = "http"
print("Note that you can put your icon/avatar in the static/ directory") 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' "icon URL: ", default=f'{proto}://{dat["domain"]}/static/nopic.png'
) ):
dat["icon_url"] = icon_url
dat["secret"] = os.urandom(16).hex() dat["secret"] = os.urandom(16).hex()
with config_file.open("w") as f: with config_file.open("w") as f: