mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2025-06-05 21:59:23 +02:00
Compare commits
4 Commits
2.0.0-rc.7
...
test-css-t
Author | SHA1 | Date | |
---|---|---|---|
62c9327500 | |||
a339ff93b1 | |||
afd253a1b4 | |||
509e10e79b |
@ -109,6 +109,8 @@ class Config(pydantic.BaseModel):
|
||||
|
||||
inbox_retention_days: int = 15
|
||||
|
||||
custom_content_security_policy: str | None = None
|
||||
|
||||
# Config items to make tests easier
|
||||
sqlalchemy_database: 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}
|
||||
ALSO_KNOWN_AS = CONFIG.also_known_as
|
||||
CUSTOM_CONTENT_SECURITY_POLICY = CONFIG.custom_content_security_policy
|
||||
|
||||
INBOX_RETENTION_DAYS = CONFIG.inbox_retention_days
|
||||
CUSTOM_FOOTER = (
|
||||
|
12
app/main.py
12
app/main.py
@ -137,9 +137,15 @@ class CustomMiddleware:
|
||||
headers["x-frame-options"] = "DENY"
|
||||
headers["permissions-policy"] = "interest-cohort=()"
|
||||
headers["content-security-policy"] = (
|
||||
f"default-src 'self'; "
|
||||
f"style-src 'self' 'sha256-{HIGHLIGHT_CSS_HASH}'; "
|
||||
f"frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
|
||||
(
|
||||
f"default-src 'self'; "
|
||||
f"style-src 'self' 'sha256-{HIGHLIGHT_CSS_HASH}'; "
|
||||
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:
|
||||
headers["strict-transport-security"] = "max-age=63072000;"
|
||||
|
@ -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;
|
||||
$light-background: #e6e6e6;
|
||||
$text-color: #111;
|
||||
$primary-color: #1d781d;
|
||||
$secondary-color: #781D78;
|
||||
$secondary-color: #781d78;
|
||||
$form-background-color: #ccc;
|
||||
$form-text-color: #333;
|
||||
$muted-color: #555; // solarized comment text
|
||||
@ -13,6 +15,46 @@ $code-highlight-background: #f0f0f0;
|
||||
// Load custom theme
|
||||
@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 {
|
||||
color: $primary-color;
|
||||
}
|
||||
@ -70,7 +112,7 @@ $code-highlight-background: #f0f0f0;
|
||||
display: none;
|
||||
}
|
||||
.sensitive-attachment-state:checked ~ .sensitive-attachment-box div {
|
||||
display:none;
|
||||
display: none;
|
||||
}
|
||||
.sensitive-attachment-box {
|
||||
position: relative;
|
||||
@ -84,7 +126,6 @@ $code-highlight-background: #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
blockquote {
|
||||
border-left: 3px solid $secondary-color;
|
||||
margin-left: 0;
|
||||
@ -92,28 +133,11 @@ blockquote {
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: $muted-color;
|
||||
color: $muted-color;
|
||||
}
|
||||
|
||||
.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;
|
||||
background: $light-background;
|
||||
}
|
||||
|
||||
dl {
|
||||
@ -137,53 +161,56 @@ dl {
|
||||
}
|
||||
|
||||
.shared-header {
|
||||
margin-left: 20px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: -20px;
|
||||
strong {
|
||||
color: $primary-color;
|
||||
}
|
||||
span {
|
||||
color: $muted-color;
|
||||
}
|
||||
margin-left: 20px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: -20px;
|
||||
strong {
|
||||
color: $primary-color;
|
||||
}
|
||||
span {
|
||||
color: $muted-color;
|
||||
}
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background: $code-highlight-background;
|
||||
padding: 0 10px;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
background: $code-highlight-background;
|
||||
padding: 0 10px;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 0 20px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
code,
|
||||
pre {
|
||||
color: $secondary-color; // #cb4b16; // #268bd2; // #2aa198;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.form {
|
||||
input, select, textarea {
|
||||
font-size: 20px;
|
||||
border: 0;
|
||||
padding: 5px;
|
||||
background: $form-background-color;
|
||||
color: $form-text-color;
|
||||
&:focus {
|
||||
outline: 1px solid $secondary-color;
|
||||
}
|
||||
}
|
||||
input[type=submit] {
|
||||
font-size: 20px;
|
||||
outline: none;
|
||||
background: $primary-color;
|
||||
color: $primary-button-text-color;
|
||||
padding: 5px 12px;
|
||||
cursor: pointer;
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 20px;
|
||||
border: 0;
|
||||
padding: 5px;
|
||||
background: $form-background-color;
|
||||
color: $form-text-color;
|
||||
&:focus {
|
||||
outline: 1px solid $secondary-color;
|
||||
}
|
||||
}
|
||||
input[type="submit"] {
|
||||
font-size: 20px;
|
||||
outline: none;
|
||||
background: $primary-color;
|
||||
color: $primary-button-text-color;
|
||||
padding: 5px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
@ -212,18 +239,17 @@ a {
|
||||
}
|
||||
}
|
||||
#main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
margin: 30px auto;
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.main-flex {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.centered {
|
||||
@ -237,12 +263,12 @@ main {
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
margin: 20px auto;
|
||||
color: $muted-color;
|
||||
p {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.tiny-actor-icon {
|
||||
@ -254,7 +280,7 @@ footer {
|
||||
.actor-box {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
margin:10px 0;
|
||||
margin: 10px 0;
|
||||
.icon-box {
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
@ -268,50 +294,54 @@ footer {
|
||||
}
|
||||
}
|
||||
#articles {
|
||||
list-style-type: none;
|
||||
margin: 30px 0;
|
||||
padding: 0 20px;
|
||||
li {
|
||||
display: block;
|
||||
span {
|
||||
padding-right:10px;
|
||||
}
|
||||
list-style-type: none;
|
||||
margin: 30px 0;
|
||||
padding: 0 20px;
|
||||
li {
|
||||
display: block;
|
||||
span {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#notifications, #followers, #following {
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
#notifications,
|
||||
#followers,
|
||||
#following {
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin admin-button() {
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
font-family: $font-stack;
|
||||
background: $form-background-color;
|
||||
color: $form-text-color;
|
||||
border: 1px solid $background;
|
||||
padding: 8px 10px 5px 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border: 1px solid $form-text-color;
|
||||
}
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
font-family: $font-stack;
|
||||
background: $form-background-color;
|
||||
color: $form-text-color;
|
||||
border: 1px solid $background;
|
||||
padding: 8px 10px 5px 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border: 1px solid $form-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.show-sensitive-btn, .show-more-btn, .label-btn {
|
||||
@include admin-button;
|
||||
padding: 10px 5px;
|
||||
margin: 20px 0;
|
||||
.show-sensitive-btn,
|
||||
.show-more-btn,
|
||||
.label-btn {
|
||||
@include admin-button;
|
||||
padding: 10px 5px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.show-hide-sensitive-btn {
|
||||
display:inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.no-margin-top {
|
||||
@ -335,13 +365,13 @@ ul.poll-items {
|
||||
}
|
||||
|
||||
.poll-bar {
|
||||
width:100%;height:20px;
|
||||
line {
|
||||
stroke: $secondary-color;
|
||||
stroke-width: 20px;
|
||||
}
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
line {
|
||||
stroke: $secondary-color;
|
||||
stroke-width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,44 +392,45 @@ ul.poll-items {
|
||||
}
|
||||
|
||||
nav {
|
||||
form {
|
||||
margin: 15px 0;
|
||||
}
|
||||
input[type=submit], button {
|
||||
@include admin-button;
|
||||
}
|
||||
form {
|
||||
margin: 15px 0;
|
||||
}
|
||||
input[type="submit"],
|
||||
button {
|
||||
@include admin-button;
|
||||
}
|
||||
}
|
||||
|
||||
nav.flexbox {
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
}
|
||||
ul li {
|
||||
margin-right: 20px;
|
||||
|
||||
ul li {
|
||||
margin-right: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
a:not(.label-btn) {
|
||||
color: $primary-color;
|
||||
text-decoration: none;
|
||||
&:hover, &:active {
|
||||
color: $secondary-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
a.active:not(.label-btn) {
|
||||
color: $secondary-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
a:not(.label-btn) {
|
||||
color: $primary-color;
|
||||
text-decoration: none;
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $secondary-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
a.active:not(.label-btn) {
|
||||
color: $secondary-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// after nav.flexbox to override default behavior
|
||||
@ -412,31 +443,34 @@ a.label-btn {
|
||||
}
|
||||
|
||||
.ap-object {
|
||||
margin: 15px 0;
|
||||
padding: 20px;
|
||||
nav {
|
||||
color: $muted-color;
|
||||
margin: 15px 0;
|
||||
padding: 20px;
|
||||
nav {
|
||||
color: $muted-color;
|
||||
}
|
||||
.in-reply-to {
|
||||
display: inline;
|
||||
color: $muted-color;
|
||||
}
|
||||
.e-content,
|
||||
.activity-og-meta {
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.in-reply-to {
|
||||
display: inline;
|
||||
color: $muted-color;
|
||||
}
|
||||
.e-content, .activity-og-meta {
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.activity-attachment {
|
||||
margin: 30px 0 20px 0;
|
||||
img, audio, video {
|
||||
width: 100%;
|
||||
max-width: 740px;
|
||||
}
|
||||
}
|
||||
img.inline-img {
|
||||
display: block;
|
||||
max-width: 740px;
|
||||
}
|
||||
.activity-attachment {
|
||||
margin: 30px 0 20px 0;
|
||||
img,
|
||||
audio,
|
||||
video {
|
||||
width: 100%;
|
||||
max-width: 740px;
|
||||
}
|
||||
}
|
||||
img.inline-img {
|
||||
display: block;
|
||||
max-width: 740px;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-og-meta {
|
||||
@ -453,25 +487,26 @@ a.label-btn {
|
||||
}
|
||||
|
||||
.ap-object-expanded {
|
||||
border: 2px dashed $secondary-color;
|
||||
border: 2px dashed $secondary-color;
|
||||
}
|
||||
|
||||
.error-box {
|
||||
color: $secondary-color;
|
||||
color: $secondary-color;
|
||||
}
|
||||
|
||||
.actor-action {
|
||||
margin-top:20px;
|
||||
margin-bottom:-20px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: -20px;
|
||||
padding: 0 20px;
|
||||
span {
|
||||
color: $muted-color;
|
||||
}
|
||||
}
|
||||
.actor-metadata {
|
||||
color: $muted-color;
|
||||
color: $muted-color;
|
||||
}
|
||||
.emoji, .custom-emoji {
|
||||
.emoji,
|
||||
.custom-emoji {
|
||||
max-width: 25px;
|
||||
}
|
||||
|
||||
|
@ -424,3 +424,4 @@ _templates.env.globals["BASE_URL"] = config.BASE_URL
|
||||
_templates.env.globals["HIDES_FOLLOWERS"] = config.HIDES_FOLLOWERS
|
||||
_templates.env.globals["HIDES_FOLLOWING"] = config.HIDES_FOLLOWING
|
||||
_templates.env.globals["NAVBAR_ITEMS"] = config.NavBarItems
|
||||
_templates.env.globals["ICON_URL"] = config.CONFIG.icon_url
|
||||
|
@ -14,7 +14,7 @@
|
||||
<meta content="{{ local_actor.display_name }}'s microblog" property="og:site_name" />
|
||||
<meta content="Homepage" property="og:title" />
|
||||
<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="{{ local_actor.handle }}" property="profile:username" />
|
||||
{% endif %}
|
||||
|
@ -26,11 +26,12 @@
|
||||
|
||||
{%- macro header_link(url, text) -%}
|
||||
{% 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 %}
|
||||
|
||||
{%- 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 %}
|
||||
|
||||
<div class="public-top-menu">
|
||||
|
@ -13,7 +13,7 @@
|
||||
<meta content="{{ local_actor.display_name }}'s microblog" property="og:site_name" />
|
||||
<meta content="Homepage" property="og:title" />
|
||||
<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="{{ local_actor.handle }}" property="profile:username" />
|
||||
{% endblock %}
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div id="admin">
|
||||
{% macro admin_link(url, text) %}
|
||||
{% 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 %}
|
||||
<div class="admin-menu">
|
||||
<nav class="flexbox">
|
||||
|
@ -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`.
|
||||
|
||||
#### 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
|
||||
|
||||
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
|
||||
code_highlighting_theme = "solarized-dark"
|
||||
|
Reference in New Issue
Block a user