Compare commits
11 Commits
new-commen
...
d3ebbc0a34
| Author | SHA1 | Date | |
|---|---|---|---|
| d3ebbc0a34 | |||
| 0775524e00 | |||
| 96bea1cfc5 | |||
| 4f6d6d8f4b | |||
| 2472aadbee | |||
| f712d78f27 | |||
| 295d243c16 | |||
| c78c25dd68 | |||
| 70318b96a2 | |||
| f893f0a31c | |||
| 0c28fe932f |
@@ -439,6 +439,8 @@ Alias "/static" "/var/www/jc/static/"
|
|||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
Alias "/robots.txt" "/var/www/jc/static/robots.txt"
|
Alias "/robots.txt" "/var/www/jc/static/robots.txt"
|
||||||
|
Alias "/.well-known/security.txt" "/var/www/jc/static/security.txt"
|
||||||
|
|
||||||
|
|
||||||
<Files jc.wsgi>
|
<Files jc.wsgi>
|
||||||
Require all granted
|
Require all granted
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from .content import ContentArea
|
|||||||
from .contact import ContactForm
|
from .contact import ContactForm
|
||||||
from .storage import LocalStorage
|
from .storage import LocalStorage
|
||||||
from .links import Links
|
from .links import Links
|
||||||
from .comments import *
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ projects = ContentArea(
|
|||||||
app.register_blueprint(projects, url_prefix='/projects')
|
app.register_blueprint(projects, url_prefix='/projects')
|
||||||
app.register_blueprint(ContactForm('contact', __name__), url_prefix='/contact')
|
app.register_blueprint(ContactForm('contact', __name__), url_prefix='/contact')
|
||||||
app.register_blueprint(Links(path.join(md_path, 'links.json'), 'links', __name__), url_prefix='/links')
|
app.register_blueprint(Links(path.join(md_path, 'links.json'), 'links', __name__), url_prefix='/links')
|
||||||
app.register_blueprint(Approval(path.join(projects.md_directory.uri, 'comments.db'), 'comments', __name__), url_prefix='/comments')
|
|
||||||
|
|
||||||
class DiscordLogger(logging.Handler):
|
class DiscordLogger(logging.Handler):
|
||||||
''' Simple logging handler to send a message to Discord '''
|
''' Simple logging handler to send a message to Discord '''
|
||||||
@@ -142,7 +140,7 @@ def sitemap():
|
|||||||
url = ET.SubElement(root, 'url')
|
url = ET.SubElement(root, 'url')
|
||||||
ET.SubElement(url, 'loc').text = base_url + route
|
ET.SubElement(url, 'loc').text = base_url + route
|
||||||
ET.SubElement(url, 'lastmod').text = date
|
ET.SubElement(url, 'lastmod').text = date
|
||||||
for article in projects.get_live_posts():
|
for article in projects.get_all_posts():
|
||||||
if 'link' in article.metadata:
|
if 'link' in article.metadata:
|
||||||
continue
|
continue
|
||||||
url = ET.SubElement(root, 'url')
|
url = ET.SubElement(root, 'url')
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
from flask import Blueprint, Response, request
|
|
||||||
from uuid import uuid4
|
|
||||||
from requests import post
|
|
||||||
from os import environ
|
|
||||||
|
|
||||||
|
|
||||||
class PostComments():
|
|
||||||
def __init__(self, post_id: str, db_path: str):
|
|
||||||
self.__db_path = db_path
|
|
||||||
self.__post_id = post_id
|
|
||||||
self._webhook = environ['DISCORD_WEBHOOK']
|
|
||||||
with sqlite3.connect(db_path) as db:
|
|
||||||
cursor = db.cursor()
|
|
||||||
cursor.execute("CREATE TABLE IF NOT EXISTS comments (name TEXT, comment TEXT, date INT, post_id TEXT, approved BOOL, key TEXT)")
|
|
||||||
self.comments = cursor.execute(
|
|
||||||
"SELECT name, comment, date FROM comments WHERE approved = 1 AND post_id = ? ORDER BY date DESC",
|
|
||||||
(post_id,)
|
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
def send_to_discord(self, name: str, comment: str, comment_id: int, key: str):
|
|
||||||
''' Send the message '''
|
|
||||||
message_to_send = f'New comment from {name}\n\n{comment}'
|
|
||||||
if len(message_to_send) > 2000:
|
|
||||||
chars_to_lose = len(message_to_send) - 1900
|
|
||||||
message_to_send = message_to_send[-chars_to_lose:]
|
|
||||||
message_to_send += f'\n\n[Approve](https://jakecharman.co.uk/comments/approve/{comment_id}?key={key})'
|
|
||||||
post(self._webhook, data={'content': message_to_send}, timeout=30)
|
|
||||||
|
|
||||||
def make_comment(self, name: str, comment: str):
|
|
||||||
with sqlite3.connect(self.__db_path) as db:
|
|
||||||
key = str(uuid4())
|
|
||||||
cursor = db.cursor()
|
|
||||||
cursor.execute(
|
|
||||||
"INSERT INTO comments (name, comment, date, post_id, approved, key) VALUES (?, ?, datetime('now'), ?, 0, ?)",
|
|
||||||
(name, comment, self.__post_id, key)
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
self.send_to_discord(name, comment, cursor.lastrowid, key)
|
|
||||||
return cursor.lastrowid
|
|
||||||
|
|
||||||
class Approval(Blueprint):
|
|
||||||
def __init__(self, db_path: str, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.__db_path = db_path
|
|
||||||
self.add_url_rule('/approve/<comment_id>', view_func=self.approve)
|
|
||||||
|
|
||||||
def approve(self, comment_id: str):
|
|
||||||
with sqlite3.connect(self.__db_path) as db:
|
|
||||||
cursor = db.cursor()
|
|
||||||
key = cursor.execute("SELECT key FROM comments WHERE rowid = ?", (comment_id,)).fetchone()[0]
|
|
||||||
if request.args.get('key') == key:
|
|
||||||
with sqlite3.connect(self.__db_path) as db:
|
|
||||||
cursor = db.cursor()
|
|
||||||
cursor.execute("UPDATE comments SET approved = 1 WHERE rowid = ?", (comment_id,))
|
|
||||||
db.commit()
|
|
||||||
return Response(status=200)
|
|
||||||
return Response(status=403)
|
|
||||||
@@ -6,9 +6,8 @@ from datetime import datetime
|
|||||||
import frontmatter
|
import frontmatter
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from flask import render_template, Response, Blueprint, request, redirect
|
from flask import render_template, Response, Blueprint
|
||||||
from .storage import LocalStorage
|
from .storage import LocalStorage
|
||||||
from .comments import PostComments, Approval
|
|
||||||
|
|
||||||
class ContentArea(Blueprint):
|
class ContentArea(Blueprint):
|
||||||
def __init__(self, directory: LocalStorage, *args, **kwargs):
|
def __init__(self, directory: LocalStorage, *args, **kwargs):
|
||||||
@@ -25,7 +24,7 @@ class ContentArea(Blueprint):
|
|||||||
self.add_url_rule('/', view_func=self.projects)
|
self.add_url_rule('/', view_func=self.projects)
|
||||||
self.add_url_rule('/category/<category_id>/', view_func=self.category)
|
self.add_url_rule('/category/<category_id>/', view_func=self.category)
|
||||||
self.add_url_rule('/<article_id>', view_func=self.article)
|
self.add_url_rule('/<article_id>', view_func=self.article)
|
||||||
self.add_url_rule('/<article_id>/comment', view_func=self.comment, methods=['POST'])
|
#self.add_url_rule('/image/<image_name>', view_func=self.image)
|
||||||
|
|
||||||
def processor(self) -> dict:
|
def processor(self) -> dict:
|
||||||
''' Jninja processors '''
|
''' Jninja processors '''
|
||||||
@@ -141,16 +140,6 @@ class ContentArea(Blueprint):
|
|||||||
return Response(status=500)
|
return Response(status=500)
|
||||||
|
|
||||||
the_article = articles[0]
|
the_article = articles[0]
|
||||||
|
|
||||||
comments = PostComments(the_article.metadata['id'], path.join(self.md_directory.uri, 'comments.db')).comments
|
|
||||||
return render_template('article.html', post=markdown(the_article.content),
|
return render_template('article.html', post=markdown(the_article.content),
|
||||||
metadata=the_article.metadata,
|
metadata=the_article.metadata,
|
||||||
comments = comments,
|
page_title=f'{the_article.metadata["title"]} - ')
|
||||||
page_title=f'{the_article.metadata["title"]} - ')
|
|
||||||
|
|
||||||
def comment(self, article_id: str):
|
|
||||||
PostComments(article_id, path.join(self.md_directory.uri, 'comments.db')).make_comment(
|
|
||||||
request.form['name'],
|
|
||||||
request.form['comment']
|
|
||||||
)
|
|
||||||
return redirect(f'/projects/{article_id}?comment=true#comments')
|
|
||||||
@@ -22,23 +22,4 @@
|
|||||||
{{post|safe}}
|
{{post|safe}}
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<section id="comments">
|
|
||||||
<h2>{{comments | length}} Comment{% if comments | length != 1 %}s{% endif %}</h2>
|
|
||||||
{% for comment in comments %}
|
|
||||||
<div class="comment">
|
|
||||||
<strong>{{ comment[0] }} - {{ comment[2] | human_date }}</strong>
|
|
||||||
<p>{{ comment[1] }}</p>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<h3>Leave a comment</h3>
|
|
||||||
{% if request.args.get('comment') is none %}
|
|
||||||
<form action="./{{metadata.id}}/comment" method="post">
|
|
||||||
<input type="text", name="name" placeholder="Name" required>
|
|
||||||
<textarea name="comment" placeholder="Comment" required></textarea>
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<p>Thank you! Your comment will appear once it is approved</p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
3
src/static/security.txt
Normal file
3
src/static/security.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Contact: mailto:security@jakecharman.co.uk
|
||||||
|
Expires: 2036-01-01T00:00:00.000Z
|
||||||
|
Preferred-Languages: en
|
||||||
@@ -328,15 +328,4 @@ pre{
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
height: 31vw;
|
height: 31vw;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
|
||||||
|
|
||||||
#comments {
|
|
||||||
border-top: 1px solid #e5e5e5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment {
|
|
||||||
background-color: #4c4c4c;
|
|
||||||
margin: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user