Compare commits
6 commits
e47103d0ee
...
793c0b6612
| Author | SHA1 | Date | |
|---|---|---|---|
| 793c0b6612 | |||
| 66471558b9 | |||
| 6935a6ae71 | |||
| 7d8b518c85 | |||
| 299c29869c | |||
| 0311586a1b |
16 changed files with 244 additions and 10 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
- Added dependency to [SUOU](https://github.com/sakuragasaki46/suou) library
|
- Added dependency to [SUOU](https://github.com/sakuragasaki46/suou) library
|
||||||
- Added user blocks
|
- Added user blocks
|
||||||
- Added user strikes: a strike logs the content of a removed message for future use
|
- Added user strikes: a strike logs the content of a removed message for future use
|
||||||
|
- Posts may now be deleted by author. If it has comments, comments are not spared
|
||||||
- Implemented guild subscriptions
|
- Implemented guild subscriptions
|
||||||
+ Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile
|
+ Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile
|
||||||
- Added ✨color themes✨
|
- Added ✨color themes✨
|
||||||
|
|
|
||||||
34
alembic/versions/6d418df3c72f_.py
Normal file
34
alembic/versions/6d418df3c72f_.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 6d418df3c72f
|
||||||
|
Revises: 90c7d0098efe
|
||||||
|
Create Date: 2025-07-07 13:37:51.667620
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '6d418df3c72f'
|
||||||
|
down_revision: Union[str, None] = '90c7d0098efe'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint('comment_parent_post_id', 'freak_comment', type_='foreignkey')
|
||||||
|
op.create_foreign_key('comment_parent_post_id', 'freak_comment', 'freak_post', ['parent_post_id'], ['id'], ondelete='cascade')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint('comment_parent_post_id', 'freak_comment', type_='foreignkey')
|
||||||
|
op.create_foreign_key('comment_parent_post_id', 'freak_comment', 'freak_post', ['parent_post_id'], ['id'])
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
@ -17,12 +17,13 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||||
from suou import Snowflake, ssv_list
|
from suou import Snowflake, ssv_list
|
||||||
from werkzeug.routing import BaseConverter
|
from werkzeug.routing import BaseConverter
|
||||||
from sassutils.wsgi import SassMiddleware
|
from sassutils.wsgi import SassMiddleware
|
||||||
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
|
||||||
from suou.configparse import ConfigOptions, ConfigValue
|
from suou.configparse import ConfigOptions, ConfigValue
|
||||||
|
|
||||||
from freak.colors import color_themes, theme_classes
|
from freak.colors import color_themes, theme_classes
|
||||||
|
|
||||||
__version__ = '0.4.0-dev24'
|
__version__ = '0.4.0-dev27'
|
||||||
|
|
||||||
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
@ -36,6 +37,7 @@ class AppConfig(ConfigOptions):
|
||||||
domain_name = ConfigValue()
|
domain_name = ConfigValue()
|
||||||
private_assets = ConfigValue(cast=ssv_list)
|
private_assets = ConfigValue(cast=ssv_list)
|
||||||
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
|
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
|
||||||
|
app_is_behind_proxy = ConfigValue(cast=bool, default=False)
|
||||||
|
|
||||||
app_config = AppConfig()
|
app_config = AppConfig()
|
||||||
|
|
||||||
|
|
@ -51,6 +53,12 @@ app.wsgi_app = SassMiddleware(app.wsgi_app, dict(
|
||||||
freak=('static/sass', 'static/css', '/static/css', True)
|
freak=('static/sass', 'static/css', '/static/css', True)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# proxy fix
|
||||||
|
if app_config.app_is_behind_proxy:
|
||||||
|
app.wsgi_app = ProxyFix(
|
||||||
|
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
|
||||||
|
)
|
||||||
|
|
||||||
class SlugConverter(BaseConverter):
|
class SlugConverter(BaseConverter):
|
||||||
regex = r'[a-z0-9]+(?:-[a-z0-9]+)*'
|
regex = r'[a-z0-9]+(?:-[a-z0-9]+)*'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,3 +40,9 @@ def append(text, l: list):
|
||||||
l.append(text)
|
l.append(text)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@app.template_filter()
|
||||||
|
def faint_paren(text: str):
|
||||||
|
if not '(' in text:
|
||||||
|
return text
|
||||||
|
t1, t2, t3 = text.partition('(')
|
||||||
|
return Markup('{0} <span class="faint">{1}</span>').format(t1, t2 + t3)
|
||||||
|
|
@ -245,7 +245,6 @@ class User(Base):
|
||||||
target_id = target
|
target_id = target
|
||||||
|
|
||||||
qq= ~select(UserBlock).where(UserBlock.c.actor_id == actor_id, UserBlock.c.target_id == target_id).exists()
|
qq= ~select(UserBlock).where(UserBlock.c.actor_id == actor_id, UserBlock.c.target_id == target_id).exists()
|
||||||
print(qq)
|
|
||||||
return qq
|
return qq
|
||||||
|
|
||||||
def recompute_karma(self):
|
def recompute_karma(self):
|
||||||
|
|
@ -437,7 +436,7 @@ class Comment(Base):
|
||||||
id = snowflake_column()
|
id = snowflake_column()
|
||||||
|
|
||||||
author_id = Column(BigInteger, ForeignKey('freak_user.id', name='comment_author_id'), nullable=True)
|
author_id = Column(BigInteger, ForeignKey('freak_user.id', name='comment_author_id'), nullable=True)
|
||||||
parent_post_id = Column(BigInteger, ForeignKey('freak_post.id', name='comment_parent_post_id'), nullable=False)
|
parent_post_id = Column(BigInteger, ForeignKey('freak_post.id', name='comment_parent_post_id', ondelete='cascade'), nullable=False)
|
||||||
parent_comment_id = Column(BigInteger, ForeignKey('freak_comment.id', name='comment_parent_comment_id'), nullable=True)
|
parent_comment_id = Column(BigInteger, ForeignKey('freak_comment.id', name='comment_parent_comment_id'), nullable=True)
|
||||||
text_content = Column(String(16384), nullable=False)
|
text_content = Column(String(16384), nullable=False)
|
||||||
created_at = Column(DateTime, server_default=func.current_timestamp(), index=True)
|
created_at = Column(DateTime, server_default=func.current_timestamp(), index=True)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
@import "constants.sass"
|
@import "constants.sass"
|
||||||
|
|
||||||
|
body
|
||||||
|
margin: 0
|
||||||
|
|
||||||
.content-container
|
.content-container
|
||||||
display: flex
|
display: flex
|
||||||
|
|
@ -18,6 +20,7 @@
|
||||||
|
|
||||||
main
|
main
|
||||||
min-height: 70vh
|
min-height: 70vh
|
||||||
|
margin: 12px auto
|
||||||
|
|
||||||
|
|
||||||
// __ header styles __ //
|
// __ header styles __ //
|
||||||
|
|
@ -28,7 +31,6 @@ header.header
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
height: 3em
|
height: 3em
|
||||||
padding: .75em 1.5em
|
padding: .75em 1.5em
|
||||||
margin: -12px
|
|
||||||
line-height: 1
|
line-height: 1
|
||||||
h1
|
h1
|
||||||
margin: 0
|
margin: 0
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,58 @@
|
||||||
grid-template-columns: 1fr 1fr
|
grid-template-columns: 1fr 1fr
|
||||||
|
|
||||||
.nomobile
|
.nomobile
|
||||||
display: none
|
display: none !important
|
||||||
|
|
||||||
|
body
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
footer.mobile-nav
|
||||||
|
position: sticky
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
overflow: hidden
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
background-color: var(--background)
|
||||||
|
box-shadow: 0 0 6px var(--border)
|
||||||
|
z-index: 150
|
||||||
|
|
||||||
|
> ul
|
||||||
|
display: flex
|
||||||
|
list-style: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
flex-direction: row
|
||||||
|
align-items: stretch
|
||||||
|
justify-content: stretch
|
||||||
|
> li
|
||||||
|
flex: 1
|
||||||
|
padding: .5em
|
||||||
|
margin: 0
|
||||||
|
text-align: center
|
||||||
|
a
|
||||||
|
text-decoration: none
|
||||||
|
.icon
|
||||||
|
font-size: 2rem
|
||||||
|
|
||||||
|
.content-nav
|
||||||
|
margin: 1em
|
||||||
|
width: unset
|
||||||
|
|
||||||
|
header.header h1
|
||||||
|
margin-top: 4px
|
||||||
|
margin-left: 6px
|
||||||
|
|
||||||
|
.content-header
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.big-search-bar form
|
||||||
|
flex-direction: column
|
||||||
|
|
||||||
|
[type="submit"]
|
||||||
|
width: unset
|
||||||
|
margin: 12px auto
|
||||||
|
|
||||||
@media screen and (max-width: 960px)
|
@media screen and (max-width: 960px)
|
||||||
.header-username
|
.header-username
|
||||||
|
|
@ -33,4 +84,4 @@
|
||||||
|
|
||||||
@media screen and (min-width: 801px)
|
@media screen and (min-width: 801px)
|
||||||
.mobileonly
|
.mobileonly
|
||||||
display: none
|
display: none !important
|
||||||
|
|
@ -8,5 +8,8 @@
|
||||||
<li>
|
<li>
|
||||||
<h2><a href="{{ url_for('admin.strikes') }}">Strikes</a></h2>
|
<h2><a href="{{ url_for('admin.strikes') }}">Strikes</a></h2>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<h2><a href="{{ url_for('admin.users') }}">Users</a></h2>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
30
freak/templates/admin/admin_users.html
Normal file
30
freak/templates/admin/admin_users.html
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "admin/admin_base.html" %}
|
||||||
|
{% from "macros/feed.html" import stop_scrolling, no_more_scrolling with context %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<ul>
|
||||||
|
{% for u in user_list %}
|
||||||
|
<li>
|
||||||
|
<p><a href="{{ u.url() }}">{{ u.handle() }}</a> (#{{ u.id | to_b32l }})
|
||||||
|
{%- if u.is_administrator %}
|
||||||
|
<span>(Admin)</span>
|
||||||
|
{% endif -%}
|
||||||
|
{% if u == current_user %}
|
||||||
|
<span>(You)</span>
|
||||||
|
{% endif -%}
|
||||||
|
</p>
|
||||||
|
<ul class="inline">
|
||||||
|
<li>Age: {{ u.age() }} years old ({{ u.gdpr_birthday.strftime("%B %d, %Y") }})</li>
|
||||||
|
<li>Registered at: {{ u.joined_at.strftime("%B %d, %Y %H:%M %z") }}</li>
|
||||||
|
<li>Registered from IP address: {{ u.joined_ip }}</li>
|
||||||
|
<li>Status: {{ account_status_string(u) | faint_paren }}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{% if user_list.has_next %}
|
||||||
|
{{ stop_scrolling(user_list.page) }}
|
||||||
|
{% else %}
|
||||||
|
{{ no_more_scrolling(user_list.page) }}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -46,11 +46,11 @@
|
||||||
{% if g.no_user %}
|
{% if g.no_user %}
|
||||||
<!-- no user -->
|
<!-- no user -->
|
||||||
{% elif current_user.is_authenticated %}
|
{% elif current_user.is_authenticated %}
|
||||||
<li>
|
<li class="nomobile">
|
||||||
<a class="round border-accent" href="/create" title="Create a post">
|
<a class="round border-accent" href="/create" title="Create a post">
|
||||||
<i class="icon icon-add"></i>
|
<i class="icon icon-add"></i>
|
||||||
<span class="a11y">create</span>
|
<span class="a11y">create</span>
|
||||||
<span class="nomobile">New post</span>
|
<span>New post</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{{ current_user.url() }}" title="{{ current_user.handle() }}'s profile">{{ icon('profile')}}<span class="a11y">profile</span></a>
|
<li><a href="{{ current_user.url() }}" title="{{ current_user.handle() }}'s profile">{{ icon('profile')}}<span class="a11y">profile</span></a>
|
||||||
|
|
@ -99,6 +99,17 @@
|
||||||
<li><a href="https://github.com/sakuragasaki46/freak">GitHub</a></li>
|
<li><a href="https://github.com/sakuragasaki46/freak">GitHub</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</footer>
|
</footer>
|
||||||
|
{% if current_user and current_user.is_authenticated %}
|
||||||
|
<footer class="mobile-nav mobileonly">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/" title="Homepage">{{ icon('home') }}</a></li>
|
||||||
|
<li><a href="/search" title="Search">{{ icon('search') }}</a></li>
|
||||||
|
<li><a href="/create" title="Create">{{ icon('add') }}</a></li>
|
||||||
|
<li><a href="{{ current_user.url() }}" title="Messages">{{ icon('message') }}</a></li>
|
||||||
|
<li><a href="https://trollface.dk" title="Notifications">{{ icon('notification') }}</a></li>
|
||||||
|
</ul>
|
||||||
|
</footer>
|
||||||
|
{% endif %}
|
||||||
<script>
|
<script>
|
||||||
function changeAccentColorTime() {
|
function changeAccentColorTime() {
|
||||||
let hours = (new Date).getHours();
|
let hours = (new Date).getHours();
|
||||||
|
|
|
||||||
27
freak/templates/singledelete.html
Normal file
27
freak/templates/singledelete.html
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% from "macros/title.html" import title_tag with context %}
|
||||||
|
{% from "macros/icon.html" import icon, callout with context %}
|
||||||
|
|
||||||
|
{% block title %}{{ title_tag('Confirm deletion: ' + p.title, False) }}{% endblock %}
|
||||||
|
|
||||||
|
{% block heading %}
|
||||||
|
<h2><span class="faint">Confirm deletion:</span> {{ p.title }}</h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card">
|
||||||
|
<form action="/delete/post/{{ p.id | to_b32l }}" method="POST">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||||
|
<div>
|
||||||
|
<p>You are about to delete <u>permanently</u> <a href="{{ p.url() }}">your post on {{ p.topic_or_user().handle() }}</a>.</p>
|
||||||
|
{% call callout('spoiler', 'error') %}This action <u><b>cannot be undone</b></u>.{% endcall %}
|
||||||
|
{% if (p.comments | count) %}
|
||||||
|
{% call callout('spoiler') %}Your post has <strong>{{ (p.comments | count) }} comments</strong>. Your post will be deleted <u>along with ALL the comments</u>.{% endcall %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="submit" class="primary">{{ icon('delete') }} Delete</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -17,6 +17,9 @@ blueprints.append(bp)
|
||||||
from .edit import bp
|
from .edit import bp
|
||||||
blueprints.append(bp)
|
blueprints.append(bp)
|
||||||
|
|
||||||
|
from .delete import bp
|
||||||
|
blueprints.append(bp)
|
||||||
|
|
||||||
from .about import bp
|
from .about import bp
|
||||||
blueprints.append(bp)
|
blueprints.append(bp)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import datetime
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
from flask import Blueprint, abort, render_template, request, redirect, flash
|
from flask import Blueprint, abort, render_template, request, redirect, flash
|
||||||
from flask_login import login_required, login_user, logout_user, current_user
|
from flask_login import login_required, login_user, logout_user, current_user
|
||||||
|
from werkzeug.exceptions import Forbidden
|
||||||
from ..models import REPORT_REASONS, db, User
|
from ..models import REPORT_REASONS, db, User
|
||||||
from ..utils import age_and_days
|
from ..utils import age_and_days
|
||||||
from sqlalchemy import select, insert
|
from sqlalchemy import select, insert
|
||||||
|
|
@ -53,9 +54,13 @@ def validate_register_form() -> dict:
|
||||||
try:
|
try:
|
||||||
f['gdpr_birthday'] = datetime.date.fromisoformat(request.form['birthday'])
|
f['gdpr_birthday'] = datetime.date.fromisoformat(request.form['birthday'])
|
||||||
|
|
||||||
|
if age_and_days(f['gdpr_birthday']) == (0, 0):
|
||||||
|
# block bot attempt to register
|
||||||
|
raise Forbidden
|
||||||
if age_and_days(f['gdpr_birthday']) < (14,):
|
if age_and_days(f['gdpr_birthday']) < (14,):
|
||||||
f['banned_at'] = datetime.datetime.now()
|
f['banned_at'] = datetime.datetime.now()
|
||||||
f['banned_reason'] = REPORT_REASONS['underage']
|
f['banned_reason'] = REPORT_REASONS['underage']
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError('Invalid date format')
|
raise ValueError('Invalid date format')
|
||||||
f['username'] = request.form['username'].lower()
|
f['username'] = request.form['username'].lower()
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,21 @@ TARGET_TYPES = {
|
||||||
Comment: REPORT_TARGET_COMMENT
|
Comment: REPORT_TARGET_COMMENT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def account_status_string(u: User):
|
||||||
|
if u.is_active:
|
||||||
|
return 'Active'
|
||||||
|
elif u.banned_at:
|
||||||
|
s = 'Suspended'
|
||||||
|
if u.banned_until:
|
||||||
|
s += f' until {u.banned_until:%b %d, %Y %H:%M}'
|
||||||
|
if u.banned_reason in REPORT_REASON_STRINGS:
|
||||||
|
s += f' ({REPORT_REASON_STRINGS[u.banned_reason]})'
|
||||||
|
return s
|
||||||
|
elif u.is_disabled_by_user:
|
||||||
|
return 'Paused'
|
||||||
|
else:
|
||||||
|
return 'Inactive'
|
||||||
|
|
||||||
def remove_content(target, reason_code: int):
|
def remove_content(target, reason_code: int):
|
||||||
if isinstance(target, Post):
|
if isinstance(target, Post):
|
||||||
target.removed_at = datetime.datetime.now()
|
target.removed_at = datetime.datetime.now()
|
||||||
|
|
@ -146,3 +161,11 @@ def strikes():
|
||||||
strike_list = db.paginate(select(UserStrike).order_by(UserStrike.id.desc()))
|
strike_list = db.paginate(select(UserStrike).order_by(UserStrike.id.desc()))
|
||||||
return render_template('admin/admin_strikes.html',
|
return render_template('admin/admin_strikes.html',
|
||||||
strike_list=strike_list, report_reasons=REPORT_REASON_STRINGS)
|
strike_list=strike_list, report_reasons=REPORT_REASON_STRINGS)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/admin/users/')
|
||||||
|
@admin_required
|
||||||
|
def users():
|
||||||
|
user_list = db.paginate(select(User).order_by(User.joined_at.desc()))
|
||||||
|
return render_template('admin/admin_users.html',
|
||||||
|
user_list=user_list, account_status_string=account_status_string)
|
||||||
|
|
|
||||||
31
freak/website/delete.py
Normal file
31
freak/website/delete.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
|
||||||
|
from flask import Blueprint, abort, flash, redirect, render_template, request
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
from sqlalchemy import delete, select
|
||||||
|
|
||||||
|
from ..models import Post, db
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint('delete', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/delete/post/<b32l:id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_post(id: int):
|
||||||
|
p = db.session.execute(select(Post).where(Post.id == id, Post.author == current_user)).scalar()
|
||||||
|
|
||||||
|
if p is None:
|
||||||
|
abort(404)
|
||||||
|
if p.author != current_user:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
pt = p.topic_or_user()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
db.session.execute(delete(Post).where(Post.id == id, Post.author == current_user))
|
||||||
|
db.session.commit()
|
||||||
|
flash('Your post has been deleted')
|
||||||
|
return redirect(pt.url()), 303
|
||||||
|
|
||||||
|
return render_template('singledelete.html', p=p)
|
||||||
|
|
@ -14,7 +14,7 @@ bp = Blueprint('edit', __name__)
|
||||||
@bp.route('/edit/post/<b32l:id>', methods=['GET', 'POST'])
|
@bp.route('/edit/post/<b32l:id>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def edit_post(id):
|
def edit_post(id):
|
||||||
p: Post | None = db.session.execute(select(Post).where(Post.id == id)).scalar()
|
p: Post | None = db.session.execute(select(Post).where(Post.id == id, Post.author == current_user)).scalar()
|
||||||
|
|
||||||
if p is None:
|
if p is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue