1
0
mirror of https://git.sr.ht/~tsileo/microblog.pub synced 2025-06-05 21:59:23 +02:00

4 Commits

9 changed files with 230 additions and 174 deletions

View File

@ -109,6 +109,8 @@ class Config(pydantic.BaseModel):
inbox_retention_days: int = 15 inbox_retention_days: int = 15
custom_content_security_policy: str | None = None
# Config items to make tests easier # Config items to make tests easier
sqlalchemy_database: str | None = None sqlalchemy_database: str | None = None
key_path: str | None = None key_path: str | None = None
@ -165,6 +167,7 @@ if CONFIG.privacy_replace:
BLOCKED_SERVERS = {blocked_server.hostname for blocked_server in CONFIG.blocked_servers} BLOCKED_SERVERS = {blocked_server.hostname for blocked_server in CONFIG.blocked_servers}
ALSO_KNOWN_AS = CONFIG.also_known_as ALSO_KNOWN_AS = CONFIG.also_known_as
CUSTOM_CONTENT_SECURITY_POLICY = CONFIG.custom_content_security_policy
INBOX_RETENTION_DAYS = CONFIG.inbox_retention_days INBOX_RETENTION_DAYS = CONFIG.inbox_retention_days
CUSTOM_FOOTER = ( CUSTOM_FOOTER = (

View File

@ -137,10 +137,16 @@ class CustomMiddleware:
headers["x-frame-options"] = "DENY" headers["x-frame-options"] = "DENY"
headers["permissions-policy"] = "interest-cohort=()" headers["permissions-policy"] = "interest-cohort=()"
headers["content-security-policy"] = ( headers["content-security-policy"] = (
(
f"default-src 'self'; " f"default-src 'self'; "
f"style-src 'self' 'sha256-{HIGHLIGHT_CSS_HASH}'; " f"style-src 'self' 'sha256-{HIGHLIGHT_CSS_HASH}'; "
f"frame-ancestors 'none'; base-uri 'self'; form-action 'self';" f"frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
) )
if not config.CUSTOM_CONTENT_SECURITY_POLICY
else config.CUSTOM_CONTENT_SECURITY_POLICY.format(
HIGHLIGHT_CSS_HASH=HIGHLIGHT_CSS_HASH
)
)
if not DEBUG: if not DEBUG:
headers["strict-transport-security"] = "max-age=63072000;" headers["strict-transport-security"] = "max-age=63072000;"

View File

@ -1,9 +1,11 @@
$font-stack: Helvetica, sans-serif; $font-stack: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell,
Ubuntu, roboto, noto, arial, sans-serif;
$background: #ddd; $background: #ddd;
$light-background: #e6e6e6; $light-background: #e6e6e6;
$text-color: #111; $text-color: #111;
$primary-color: #1d781d; $primary-color: #1d781d;
$secondary-color: #781D78; $secondary-color: #781d78;
$form-background-color: #ccc; $form-background-color: #ccc;
$form-text-color: #333; $form-text-color: #333;
$muted-color: #555; // solarized comment text $muted-color: #555; // solarized comment text
@ -13,6 +15,46 @@ $code-highlight-background: #f0f0f0;
// Load custom theme // Load custom theme
@import "theme.scss"; @import "theme.scss";
/* Box sizing rules */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Set core root defaults */
html:focus-within {
scroll-behavior: smooth;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
html {
max-width: 90ch;
padding: 3em 1em;
margin: auto;
line-height: 1.75;
font-size: 1.25em;
}
body {
font-family: $font-stack;
background: $background;
color: $text-color;
display: flex;
min-height: 100vh;
flex-direction: column;
}
a {
text-decoration: none;
}
.primary-color { .primary-color {
color: $primary-color; color: $primary-color;
} }
@ -70,7 +112,7 @@ $code-highlight-background: #f0f0f0;
display: none; display: none;
} }
.sensitive-attachment-state:checked ~ .sensitive-attachment-box div { .sensitive-attachment-state:checked ~ .sensitive-attachment-box div {
display:none; display: none;
} }
.sensitive-attachment-box { .sensitive-attachment-box {
position: relative; position: relative;
@ -84,7 +126,6 @@ $code-highlight-background: #f0f0f0;
} }
} }
blockquote { blockquote {
border-left: 3px solid $secondary-color; border-left: 3px solid $secondary-color;
margin-left: 0; margin-left: 0;
@ -99,23 +140,6 @@ blockquote {
background: $light-background; background: $light-background;
} }
body {
font-family: $font-stack;
font-size: 20px;
line-height: 32px;
background: $background;
color: $text-color;
margin: 0;
padding: 0;
display: flex;
min-height: 100vh;
flex-direction: column;
}
a {
text-decoration: none;
}
dl { dl {
display: flex; display: flex;
dt { dt {
@ -160,13 +184,16 @@ div.highlight {
padding: 0 20px; padding: 0 20px;
} }
code, pre { code,
pre {
color: $secondary-color; // #cb4b16; // #268bd2; // #2aa198; color: $secondary-color; // #cb4b16; // #268bd2; // #2aa198;
font-family: monospace; font-family: monospace;
} }
.form { .form {
input, select, textarea { input,
select,
textarea {
font-size: 20px; font-size: 20px;
border: 0; border: 0;
padding: 5px; padding: 5px;
@ -176,7 +203,7 @@ code, pre {
outline: 1px solid $secondary-color; outline: 1px solid $secondary-color;
} }
} }
input[type=submit] { input[type="submit"] {
font-size: 20px; font-size: 20px;
outline: none; outline: none;
background: $primary-color; background: $primary-color;
@ -217,7 +244,6 @@ a {
} }
main { main {
width: 100%; width: 100%;
max-width: 1000px;
margin: 30px auto; margin: 30px auto;
} }
@ -237,8 +263,8 @@ main {
} }
footer { footer {
font-size: 1em;
width: 100%; width: 100%;
max-width: 1000px;
margin: 20px auto; margin: 20px auto;
color: $muted-color; color: $muted-color;
p { p {
@ -254,7 +280,7 @@ footer {
.actor-box { .actor-box {
display: flex; display: flex;
column-gap: 20px; column-gap: 20px;
margin:10px 0; margin: 10px 0;
.icon-box { .icon-box {
flex: 0 0 50px; flex: 0 0 50px;
} }
@ -274,12 +300,14 @@ footer {
li { li {
display: block; display: block;
span { span {
padding-right:10px; padding-right: 10px;
} }
} }
} }
#notifications, #followers, #following { #notifications,
#followers,
#following {
ul { ul {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
@ -304,14 +332,16 @@ footer {
} }
} }
.show-sensitive-btn, .show-more-btn, .label-btn { .show-sensitive-btn,
.show-more-btn,
.label-btn {
@include admin-button; @include admin-button;
padding: 10px 5px; padding: 10px 5px;
margin: 20px 0; margin: 20px 0;
} }
.show-hide-sensitive-btn { .show-hide-sensitive-btn {
display:inline-block; display: inline-block;
} }
.no-margin-top { .no-margin-top {
@ -335,13 +365,13 @@ ul.poll-items {
} }
.poll-bar { .poll-bar {
width:100%;height:20px; width: 100%;
height: 20px;
line { line {
stroke: $secondary-color; stroke: $secondary-color;
stroke-width: 20px; stroke-width: 20px;
} }
} }
} }
} }
@ -365,7 +395,8 @@ nav {
form { form {
margin: 15px 0; margin: 15px 0;
} }
input[type=submit], button { input[type="submit"],
button {
@include admin-button; @include admin-button;
} }
} }
@ -378,7 +409,6 @@ nav.flexbox {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
ul li { ul li {
@ -391,7 +421,8 @@ nav.flexbox {
a:not(.label-btn) { a:not(.label-btn) {
color: $primary-color; color: $primary-color;
text-decoration: none; text-decoration: none;
&:hover, &:active { &:hover,
&:active {
color: $secondary-color; color: $secondary-color;
text-decoration: underline; text-decoration: underline;
} }
@ -421,14 +452,17 @@ a.label-btn {
display: inline; display: inline;
color: $muted-color; color: $muted-color;
} }
.e-content, .activity-og-meta { .e-content,
.activity-og-meta {
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
} }
.activity-attachment { .activity-attachment {
margin: 30px 0 20px 0; margin: 30px 0 20px 0;
img, audio, video { img,
audio,
video {
width: 100%; width: 100%;
max-width: 740px; max-width: 740px;
} }
@ -461,8 +495,8 @@ a.label-btn {
} }
.actor-action { .actor-action {
margin-top:20px; margin-top: 20px;
margin-bottom:-20px; margin-bottom: -20px;
padding: 0 20px; padding: 0 20px;
span { span {
color: $muted-color; color: $muted-color;
@ -471,7 +505,8 @@ a.label-btn {
.actor-metadata { .actor-metadata {
color: $muted-color; color: $muted-color;
} }
.emoji, .custom-emoji { .emoji,
.custom-emoji {
max-width: 25px; max-width: 25px;
} }

View File

@ -424,3 +424,4 @@ _templates.env.globals["BASE_URL"] = config.BASE_URL
_templates.env.globals["HIDES_FOLLOWERS"] = config.HIDES_FOLLOWERS _templates.env.globals["HIDES_FOLLOWERS"] = config.HIDES_FOLLOWERS
_templates.env.globals["HIDES_FOLLOWING"] = config.HIDES_FOLLOWING _templates.env.globals["HIDES_FOLLOWING"] = config.HIDES_FOLLOWING
_templates.env.globals["NAVBAR_ITEMS"] = config.NavBarItems _templates.env.globals["NAVBAR_ITEMS"] = config.NavBarItems
_templates.env.globals["ICON_URL"] = config.CONFIG.icon_url

View File

@ -14,7 +14,7 @@
<meta content="{{ local_actor.display_name }}'s microblog" property="og:site_name" /> <meta content="{{ local_actor.display_name }}'s microblog" property="og:site_name" />
<meta content="Homepage" property="og:title" /> <meta content="Homepage" property="og:title" />
<meta content="{{ local_actor.summary | html2text | trim }}" property="og:description" /> <meta content="{{ local_actor.summary | html2text | trim }}" property="og:description" />
<meta content="{{ local_actor.url }}" property="og:image" /> <meta content="{{ ICON_URL }}" property="og:image" />
<meta content="summary" property="twitter:card" /> <meta content="summary" property="twitter:card" />
<meta content="{{ local_actor.handle }}" property="profile:username" /> <meta content="{{ local_actor.handle }}" property="profile:username" />
{% endif %} {% endif %}

View File

@ -26,11 +26,12 @@
{%- macro header_link(url, text) -%} {%- macro header_link(url, text) -%}
{% set url_for = BASE_URL + request.app.router.url_path_for(url) %} {% set url_for = BASE_URL + request.app.router.url_path_for(url) %}
<a href="{{ url_for }}" {% if request.url.path == url_for %}class="active"{% endif %}>{{ text }}</a> <a href="{{ url_for }}" {% if BASE_URL + request.url.path == url_for %}class="active"{% endif %}>{{ text }}</a>
{% endmacro %} {% endmacro %}
{%- macro navbar_item_link(navbar_item) -%} {%- macro navbar_item_link(navbar_item) -%}
<a href="{{ navbar_item[0] }}" {% if request.url.path == navbar_item[0] %}class="active"{% endif %}>{{ navbar_item[1] }}</a> {% set url_for = BASE_URL + navbar_item[0] %}
<a href="{{ navbar_item[0] }}" {% if BASE_URL + request.url.path == url_for %}class="active"{% endif %}>{{ navbar_item[1] }}</a>
{% endmacro %} {% endmacro %}
<div class="public-top-menu"> <div class="public-top-menu">

View File

@ -13,7 +13,7 @@
<meta content="{{ local_actor.display_name }}'s microblog" property="og:site_name" /> <meta content="{{ local_actor.display_name }}'s microblog" property="og:site_name" />
<meta content="Homepage" property="og:title" /> <meta content="Homepage" property="og:title" />
<meta content="{{ local_actor.summary | html2text | trim }}" property="og:description" /> <meta content="{{ local_actor.summary | html2text | trim }}" property="og:description" />
<meta content="{{ local_actor.url }}" property="og:image" /> <meta content="{{ ICON_URL }}" property="og:image" />
<meta content="summary" property="twitter:card" /> <meta content="summary" property="twitter:card" />
<meta content="{{ local_actor.handle }}" property="profile:username" /> <meta content="{{ local_actor.handle }}" property="profile:username" />
{% endblock %} {% endblock %}

View File

@ -19,7 +19,7 @@
<div id="admin"> <div id="admin">
{% macro admin_link(url, text) %} {% macro admin_link(url, text) %}
{% set url_for = BASE_URL + request.app.router.url_path_for(url) %} {% set url_for = BASE_URL + request.app.router.url_path_for(url) %}
<a href="{{ url_for }}" {% if request.url.path == url_for %}class="active"{% endif %}>{{ text }}</a> <a href="{{ url_for }}" {% if BASE_URL + request.url.path == url_for %}class="active"{% endif %}>{{ text }}</a>
{% endmacro %} {% endmacro %}
<div class="admin-menu"> <div class="admin-menu">
<nav class="flexbox"> <nav class="flexbox">

View File

@ -131,9 +131,19 @@ See `app/scss/main.scss` to see what variables can be overridden.
If you'd like to customize your instance's theme beyond CSS, you can modify the app's HTML by placing templates in `data/templates` which overwrite the defaults in `app/templates`. If you'd like to customize your instance's theme beyond CSS, you can modify the app's HTML by placing templates in `data/templates` which overwrite the defaults in `app/templates`.
#### Custom Content Security Policy (CSP)
You can override the default Content Security Policy by adding a line in `data/profile.toml`:
```toml
custom_content_security_policy = "default-src 'self'; style-src 'self' 'sha256-{HIGHLIGHT_CSS_HASH}'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
```
This example will output the default CSP, note that `{HIGHLIGHT_CSS_HASH}` will be dynamically replaced by the correct value (the hash of the CSS needed for syntax highlighting).
#### Code highlighting theme #### Code highlighting theme
You can switch to one of the [styles supported by Pygments](https://pygments.org/styles/) by adding a line in `profile.toml`: You can switch to one of the [styles supported by Pygments](https://pygments.org/styles/) by adding a line in `data/profile.toml`:
```toml ```toml
code_highlighting_theme = "solarized-dark" code_highlighting_theme = "solarized-dark"