forked from forks/microblog.pub
Merge branch 'v2' into indieweb-merge-part2
This commit is contained in:
commit
d36102255f
9 changed files with 51 additions and 23 deletions
3
AUTHORS
3
AUTHORS
|
@ -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>
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
21
app/admin.py
21
app/admin.py
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
15
app/main.py
15
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(
|
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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) }}
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue