Compare commits

...

7 Commits

11 changed files with 143 additions and 40 deletions

View File

@ -159,6 +159,8 @@ class Comment(admin.Administerable):
del f['captcha_passed'] del f['captcha_passed']
del f['moderation_passed'] del f['moderation_passed']
f['text'].id = f'comment-reply-{self.id}-text' # avoid id collision for help toggle
return f return f
@app.abstract @app.abstract

View File

@ -21,7 +21,7 @@ def db_connect():
app.db.connect(reuse_if_open=True) app.db.connect(reuse_if_open=True)
app.db.set_time_zone('UTC') app.db.set_time_zone('UTC')
except peewee.OperationalError as e: except (peewee.OperationalError, peewee.InterfaceError) as e:
app.logger.error(f"Error connecting to database: {str(e)}") app.logger.error(f"Error connecting to database: {str(e)}")
class ModelMeta(peewee.ModelBase, util.ChildAwareMeta): class ModelMeta(peewee.ModelBase, util.ChildAwareMeta):

View File

@ -65,7 +65,7 @@ class TimeParamType(types.ParamType):
if hour < 0 or hour > 24: if hour < 0 or hour > 24:
self.fail(f"Hour out of range for time input: {hour}, must be between 0 and 24", param, ctx) self.fail(f"Hour out of range for time input: {hour}, must be between 0 and 24", param, ctx)
if hour == 24 and minute > 0: if hour >= 24 and minute > 0:
self.fail(f"Time input past 24:00: '{value}'", param, ctx) self.fail(f"Time input past 24:00: '{value}'", param, ctx)
if minute < 0 or minute > 59: if minute < 0 or minute > 59:

17
main.py
View File

@ -40,6 +40,8 @@ def error_catchall(e):
if app.debug: if app.debug:
raise raise
app.logger.error(e)
@rendering.page(title_show=False) @rendering.page(title_show=False)
def errorpage(): def errorpage():
@ -557,7 +559,7 @@ class ScoredLink(admin.Administerable):
How many **third-party sites** the linked page loads data from. How many **third-party sites** the linked page loads data from.
This is relevant to your privacy because each of those third-party sites This is relevant to your privacy because each of those third-party sites
gets at the very least to see that you visited this page. gets at the very least to see that you visited the linked page.
*Lower* is better, **0** is *ideal*.""") *Lower* is better, **0** is *ideal*.""")
@ -574,7 +576,11 @@ gets at the very least to see that you visited this page.
external_site_count = 0 external_site_count = 0
url_domain = self.url.split('/')[2] url_domain = self.url.split('/')[2]
html = requests.get(self.url, timeout=30).text headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', # such stealth, many agent
}
html = requests.get(self.url, timeout=30, headers=headers).text
dom = bs4.BeautifulSoup(html, 'lxml') dom = bs4.BeautifulSoup(html, 'lxml')
scored_elements = { scored_elements = {
@ -584,6 +590,8 @@ gets at the very least to see that you visited this page.
'object': 'data' 'object': 'data'
} }
external_domains = set()
for tag, attribute in scored_elements.items(): for tag, attribute in scored_elements.items():
for element in dom.find_all(tag): for element in dom.find_all(tag):
@ -597,9 +605,10 @@ gets at the very least to see that you visited this page.
if attribute_domain != url_domain and\ if attribute_domain != url_domain and\
not attribute_domain.endswith(f'.{url_domain}'): # not a subdomain of url_domain not attribute_domain.endswith(f'.{url_domain}'): # not a subdomain of url_domain
external_site_count += 1 #external_site_count += 1
external_domains.add(attribute_domain)
self.external_site_count = external_site_count self.external_site_count = len(external_domains)
self.last_scrape = datetime.datetime.utcnow() self.last_scrape = datetime.datetime.utcnow()
self.save() self.save()

View File

@ -213,8 +213,6 @@ def section_core_rule(state):
elif token.type != 'heading_open': elif token.type != 'heading_open':
tokens.append(markdown_it.token.Token('section_open', 'section', 1)) tokens.append(markdown_it.token.Token('section_open', 'section', 1))
nesting += 1 nesting += 1
else:
open_next = True
elif token.type == 'heading_open' and token.tag == 'h2': elif token.type == 'heading_open' and token.tag == 'h2':

View File

@ -36,10 +36,14 @@ def page(**template_params):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
agent = flask.request.headers.get('User-Agent')
params = { params = {
'site_name': app.config['SITE_NAME'], 'site_name': app.config['SITE_NAME'],
'user': flask.g.user, 'user': flask.g.user,
'tls_cipher': flask.g.tls_cipher, 'tls_cipher': flask.g.tls_cipher,
'client_ua': agent,
'client_ua_noadblock': agent_noadblock(agent),
'client_cert_verified': flask.g.client_cert_verified, 'client_cert_verified': flask.g.client_cert_verified,
'client_cert_fingerprint': flask.g.client_cert_fingerprint, 'client_cert_fingerprint': flask.g.client_cert_fingerprint,
'client_cert_fingerprint_matched': flask.g.client_cert_fingerprint_matched, 'client_cert_fingerprint_matched': flask.g.client_cert_fingerprint_matched,
@ -153,6 +157,21 @@ def page(**template_params):
return decorator return decorator
noadblock_progs = [
re.compile('.*Android.*Chrome\/'), # no built-in adblocker, can't install extensions
]
def agent_noadblock(agent):
# Determine whether the given user agent has no ability to add an adblocker.
for prog in noadblock_progs:
if prog.match(agent):
return True
return False
@app.template_test('renderable') @app.template_test('renderable')
def test_renderable(obj): def test_renderable(obj):
return isinstance(obj, Renderable) return isinstance(obj, Renderable)

View File

@ -290,7 +290,6 @@ html {
} }
html, body { html, body {
min-width: 100vw;
min-height: 100vh; min-height: 100vh;
max-width: 100vw; max-width: 100vw;
} }
@ -329,6 +328,7 @@ body > header {
} }
.security-info span { .security-info span {
display: block;
font-size: 70%; font-size: 70%;
color: var(--color-highlight-inactive); color: var(--color-highlight-inactive);
} }
@ -400,25 +400,11 @@ main > .tabs:has(a:focus) {
main > h1 { main > h1 {
display: block; display: block;
width: var(--distance-typographic-width); width: 100%;
max-width: var(--distance-typographic-width);
margin: 0 auto; margin: 0 auto;
} }
/* deleteme */
article > section {
margin: var(--distance-major) 0;
background-size: 100%;
background-repeat: no-repeat;
}
article > section > .content {
background: hsla(0 0 5 / 90%);
backdrop-filter: blur(8px);
width: var(--distance-typographic-width);
margin: 0 auto;
padding: var(--distance-main);
} /* /deleteme */
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
font-family: var(--font-decorative); font-family: var(--font-decorative);
font-weight: normal; font-weight: normal;
@ -525,11 +511,45 @@ nav a.trail {
@media (screen and orientation: portrait), (max-width: 100rem) { @media (screen and orientation: portrait), (max-width: 100rem) {
body > header,
body > footer {
flex-direction: column;
}
body > header .security-info {
display: none;
}
body > header .seal-message {
position: relative;
right: initial;
margin: 0 auto;
}
body > footer > * {
margin: 0 auto;
margin-bottom: 2rem;
}
body > footer menu {
flex-direction: column;
}
body > footer menu a {
text-align: center;
}
body > footer object,
body > footer img {
max-width: 80vw !important;
}
body > .menus { body > .menus {
/* this element (currently) contains all burgered content */ /* this element (currently) contains all burgered content */
display: block; display: block;
position: fixed; position: fixed;
left: calc(100vw - var(--menu-burger-width)); /*left: calc(100vw - var(--menu-burger-width));*/
left: calc(100% - var(--menu-burger-width));
top: 10rem; top: 10rem;
background: transparent; background: transparent;
backdrop-filter: none; backdrop-filter: none;
@ -545,7 +565,7 @@ nav a.trail {
.with-burger .burgers + * { /* element following .burgers */ .with-burger .burgers + * { /* element following .burgers */
position: fixed; position: fixed;
width: calc(100vw - 4rem); width: calc(100% - var(--menu-burger-width));
min-height: 25vh; min-height: 25vh;
left: -100vw; left: -100vw;
top: 0; top: 0;
@ -647,6 +667,11 @@ body > footer menu {
height: 100%; height: 100%;
} }
body > footer object,
body > footer img {
max-width: 20%;
}
body > footer menu li { body > footer menu li {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -714,6 +739,7 @@ article.errorpage {
flex-direction: row; flex-direction: row;
position: relative; position: relative;
width: 40rem; width: 40rem;
max-width: 100%;
min-height: 10rem; min-height: 10rem;
margin: 0 auto; margin: 0 auto;
padding: var(--distance-main); padding: var(--distance-main);
@ -851,12 +877,12 @@ a.download:active {
overflow: auto; overflow: auto;
z-index: 2000; z-index: 2000;
min-width: 100vw; min-width: 100vw;
/*backdrop-filter: blur(8px); */ /* breaks fixed for a[href="#close"] */ /*backdrop-filter: blur(8px); */ /* breaks fixed for a[href="#__close"] */
/* see: https://stackoverflow.com/a/1384613 */ /* see: https://stackoverflow.com/a/1384613 */
background: var(--color-bg-alt); background: var(--color-bg-alt);
} }
.modal a[href="#close"] { .modal a[href="#__close"] {
display: block; display: block;
position: fixed; position: fixed;
z-index: 2100; z-index: 2100;
@ -872,7 +898,7 @@ a.download:active {
} }
.modal a[href="#close"]:hover { .modal a[href="#__close"]:hover {
color: var(--color-error); color: var(--color-error);
} }
@ -931,7 +957,7 @@ a.download:active {
} }
.toggle-input:checked + .toggle-label + * { .toggle-input:checked + .toggle-label + * {
max-height: 100vh; max-height: 500vh;
} }
/* comments */ /* comments */
@ -1156,7 +1182,7 @@ article.renderable > .content > h3,
article.renderable > .content > h4, article.renderable > .content > h4,
article.renderable > .content > h5, article.renderable > .content > h5,
article.renderable > .content > h6 { article.renderable > .content > h6 {
width: var(--distance-typographic-width); max-width: var(--distance-typographic-width);
margin: 0 auto; margin: 0 auto;
} }
@ -1592,6 +1618,7 @@ body > .menus .searchminiform .field-help-wrapper {
.propaganda .claim { .propaganda .claim {
width: var(--distance-typographic-width); width: var(--distance-typographic-width);
max-width: 100%;
margin: 0 auto; margin: 0 auto;
} }
@ -1620,6 +1647,7 @@ body > .menus .searchminiform .field-help-wrapper {
.propagandapiece .files { .propagandapiece .files {
width: var(--distance-typographic-width); width: var(--distance-typographic-width);
max-width: 100%;
padding: var(--distance-main); padding: var(--distance-main);
margin: var(--distance-main) auto; margin: var(--distance-main) auto;
} }
@ -1674,7 +1702,7 @@ body > .menus .searchminiform .field-help-wrapper {
.propaganda.mode-teaser .preview img { .propaganda.mode-teaser .preview img {
object-fit: cover; object-fit: cover;
max-height: none; /* allow image to take all the height it needs, max-height: none; /* allow image to take all the height it needs,
even if it's above the normal 50vh limitation. */ even if it's above the normal 50vh limitation. */
} }
@ -1734,6 +1762,7 @@ fieldset.propagandapiece-wrapper > .fields > .form-order > .buttons {
display: block; display: block;
min-width: var(--distance-typographic-width); min-width: var(--distance-typographic-width);
width: var(--distance-typographic-width); width: var(--distance-typographic-width);
max-width: 100%;
margin: 0 auto; margin: 0 auto;
} }
@ -1799,6 +1828,7 @@ fieldset.propagandapiece-wrapper > .fields > .form-order > .buttons {
.propagandapieceitemform > .fields > .field-order_pos > label { .propagandapieceitemform > .fields > .field-order_pos > label {
display: block; display: block;
width: var(--distance-typographic-width); width: var(--distance-typographic-width);
max-width: 100%;
margin: 0 auto; margin: 0 auto;
} }

View File

@ -6,15 +6,17 @@
height: 100vh; height: 100vh;
background: var(--color-bg-alt); background: var(--color-bg-alt);
backdrop-filter: blur(8px); backdrop-filter: blur(8px);
overflow-y: scroll;
} }
.ad .content { .ad .content {
max-width: 80rem; max-width: 80rem;
margin: 0 auto; margin: 0 auto;
padding: 1rem;
} }
.ad .moji { .ad .moji {
font-size: 30rem; font-size: 33vh;
text-align: center; text-align: center;
} }
@ -29,3 +31,17 @@
font-size: 1.5rem; font-size: 1.5rem;
text-align: center; text-align: center;
} }
.ad ul {
max-width: 80ex;
margin: 0 auto;
font-size: 1.2rem;
}
.ad ul li {
margin-bottom: 1rem;
}
.ad ul .adblocker-description {
padding-left: 2rem;
}

View File

@ -22,6 +22,9 @@
<header> <header>
<div class="security-info"> <div class="security-info">
{% if client_ua %}
<span class="client-ua">{{ client_ua }}</span>
{% endif %}
{% if tls_cipher %} {% if tls_cipher %}
<span class="tls-cipher">{{ tls_cipher }}</span> <span class="tls-cipher">{{ tls_cipher }}</span>
{% endif %} {% endif %}
@ -111,8 +114,34 @@
<div class="ad native-ad native-ad-1 ytd-j yxd-j yxd-jd aff-content-col aff-inner-col aff-item-list ark-ad-message inplayer-ad inplayer_banners in_stream_banner trafficjunky-float-right dbanner preroll-blocker happy-inside-player blocker-notice blocker-overlay exo-horizontal ave-pl bottom-hor-block brs-block advboxemb wgAdBlockMessage glx-watermark-container overlay-advertising-new header-menu-bottom-ads rkads mdp-deblocker-wrapper amp-ad-inner imggif bloc-pub bloc-pub2 hor_banner aan_fake aan_fake__video-units rps_player_ads fints-block__row full-ave-pl full-bns-block vertbars video-brs player-bns-block wps-player__happy-inside gallery-bns-bl stream-item-widget adsbyrunactive happy-under-player adde_modal_detector adde_modal-overlay ninja-recommend-block aoa_overlay"> <div class="ad native-ad native-ad-1 ytd-j yxd-j yxd-jd aff-content-col aff-inner-col aff-item-list ark-ad-message inplayer-ad inplayer_banners in_stream_banner trafficjunky-float-right dbanner preroll-blocker happy-inside-player blocker-notice blocker-overlay exo-horizontal ave-pl bottom-hor-block brs-block advboxemb wgAdBlockMessage glx-watermark-container overlay-advertising-new header-menu-bottom-ads rkads mdp-deblocker-wrapper amp-ad-inner imggif bloc-pub bloc-pub2 hor_banner aan_fake aan_fake__video-units rps_player_ads fints-block__row full-ave-pl full-bns-block vertbars video-brs player-bns-block wps-player__happy-inside gallery-bns-bl stream-item-widget adsbyrunactive happy-under-player adde_modal_detector adde_modal-overlay ninja-recommend-block aoa_overlay">
<div class="content"> <div class="content">
<div class="moji"></div> <div class="moji"></div>
<span class="fake-header">You don't seem to have an adblocker installed.</span> {% if client_ua_noadblock %}
<span class="fake-subheader">Go install one now. We recommend <a href="https://adnauseam.io/" target="_blank" rel="noreferrer">AdNauseam</a>.</span> <span class="fake-header">Your browser is so shite, you can't install an adblocker.</span>
<span class="fake-subheader">This is a major security risk. Install a less shit one and then an adblocker. <a href="https://getfirefox.com/" rel="noreferrer">Firefox</a> is the <em>least</em> shit one.</span>
{% else %}
<span class="fake-header">You don't seem to have an adblocker installed.</span>
<span class="fake-subheader">Go install one now. We recommend one of these, they're all <em>free & open source</em>:</span>
<ul>
<li>
<a href="https://adnauseam.io/" target="_blank" rel="noreferrer">AdNauseam</a>
<div class="adblocker-description">
The best one, weaponizes simulated ad clicks against advertisers.
</div>
</li>
<li>
<a href="https://ublockorigin.com/" target="_blank" rel="noreferrer">uBlock Origin</a>
<div class="adblocker-description">
The basis for AdNauseam, all the same features minus the weaponization.
</div>
</li>
<li>
<a href="https://ublockorigin.com/" target="_blank" rel="noreferrer">uBlock Origin Lite</a>
<div class="adblocker-description">
Crippled by <a href="https://en.wikipedia.org/wiki/Google_Chrome#Manifest_V3_2" target="_blank" rel="noreferrer">Manifest v3</a>.
Activate the 'complete' filtering mode to proceed, but think about upgrading to <a href="https://getfirefox.com/" target="_blank" rel="noreferrer">Firefox</a>.
</div>
</li>
</ul>
{% endif %}
</div> </div>
</div> </div>
</body> </body>

View File

@ -24,7 +24,7 @@
<div id="gallery-{{ content.name }}" class="modal"> <div id="gallery-{{ content.name }}" class="modal">
<a href="#close"></a> <a href="#__close"></a>
{% for item in content.items_ordered %} {% for item in content.items_ordered %}

View File

@ -1,7 +1,7 @@
{% extends 'default/templates/renderable/upload.html' %} {% extends 'default/templates/renderable/upload.html' %}
{% block fileview %} {% block fileview %}
<a href="#modal-image-{{ content.id }}"> <a href="#modal-image-{{ content.name }}">
<div class="preview"> <div class="preview">
<img <img
src="{{ content.url('raw') }}" src="{{ content.url('raw') }}"
@ -12,8 +12,8 @@
/> />
</div> </div>
</a> </a>
<div id="modal-image-{{ content.id }}" class="modal"> <div id="modal-image-{{ content.name }}" class="modal">
<a href="#close"></a> <a href="#__close"></a>
<div class="content"> <div class="content">
<img <img
src="{{ content.url('raw') }}" src="{{ content.url('raw') }}"