openwrite 0.13.4__tar.gz → 0.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openwrite-0.13.4/openwrite.egg-info → openwrite-0.14.0}/PKG-INFO +3 -2
- {openwrite-0.13.4 → openwrite-0.14.0}/README.md +1 -1
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/__init__.py +1 -1
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/cli.py +28 -6
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/blog.py +85 -40
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/dashboard.py +44 -65
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/style.css +396 -0
- openwrite-0.14.0/openwrite/templates/dashboard.html +68 -0
- openwrite-0.14.0/openwrite/templates/discover.html +23 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/edit.html +38 -37
- openwrite-0.14.0/openwrite/utils/i18n.json +1 -0
- openwrite-0.14.0/openwrite/utils/valkey_client.py +161 -0
- openwrite-0.14.0/openwrite/utils/worker.py +133 -0
- {openwrite-0.13.4 → openwrite-0.14.0/openwrite.egg-info}/PKG-INFO +3 -2
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite.egg-info/SOURCES.txt +3 -1
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite.egg-info/requires.txt +1 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/pyproject.toml +3 -2
- openwrite-0.13.4/openwrite/templates/dashboard.html +0 -48
- openwrite-0.13.4/openwrite/templates/discover.html +0 -11
- openwrite-0.13.4/openwrite/utils/i18n.json +0 -1
- {openwrite-0.13.4 → openwrite-0.14.0}/LICENSE +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/MANIFEST.in +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/db/__init__.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/db/base.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/gemini_multi.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/gemini_single.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/admin.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/auth.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/federation.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/main.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/routes/upload.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/IBMPlexSans-Regular.ttf +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/Merriweather.ttf +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/avatar.png +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/emoji.json +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/favicon.ico +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/logo.png +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/chart.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/highlight.min.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/nativeEmoji.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/openwrite.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/papaparse.min.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/showdown.min.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/showdown.min.js.map +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/site.compat.min.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/script/site.min.js +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/blog_design.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/default.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/hl.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/nativeEmoji.min.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/cyberpunk.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/light.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/obscura.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/paper.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/stickynote.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/terminal.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/warmnight.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/static/style/themes/windows98.css +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/admin.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/blog.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/blog_footer.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/blog_header.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/blog_preview.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/changepw.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/create.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/footer.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/header.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/import.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/index.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/instances.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/login.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/new_page.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/new_post.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/page_preview.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/post.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/preview.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/register.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/settings.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/stats.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/templates/translations.html +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/utils/create_db.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/utils/db.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/utils/helpers.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite/utils/models.py +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite.egg-info/dependency_links.txt +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite.egg-info/entry_points.txt +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/openwrite.egg-info/top_level.txt +0 -0
- {openwrite-0.13.4 → openwrite-0.14.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openwrite
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
5
5
|
Version 3, 19 November 2007
|
|
6
6
|
|
|
@@ -685,16 +685,17 @@ Requires-Dist: markdown
|
|
|
685
685
|
Requires-Dist: md2gemini
|
|
686
686
|
Requires-Dist: gmcapsule
|
|
687
687
|
Requires-Dist: user-agents
|
|
688
|
+
Requires-Dist: redis
|
|
688
689
|
Dynamic: license-file
|
|
689
690
|
|
|
690
691
|

|
|
691
|
-
|
|
692
692
|
openwrite is a minimalist blogging platform built for writing freely, hosting independently, and publishing without noise.
|
|
693
693
|
|
|
694
694
|

|
|
695
695
|

|
|
696
696
|
[](LICENSE)
|
|
697
697
|

|
|
698
|
+

|
|
698
699
|
|
|
699
700
|
---
|
|
700
701
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|

|
|
2
|
-
|
|
3
2
|
openwrite is a minimalist blogging platform built for writing freely, hosting independently, and publishing without noise.
|
|
4
3
|
|
|
5
4
|

|
|
6
5
|

|
|
7
6
|
[](LICENSE)
|
|
8
7
|

|
|
8
|
+

|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -212,7 +212,7 @@ def create_app(test_config=None):
|
|
|
212
212
|
|
|
213
213
|
trans = g.db.query(Home).filter(Home.type == "translation", Home.language == lang).all()
|
|
214
214
|
|
|
215
|
-
g.version = "0.
|
|
215
|
+
g.version = "0.14.0"
|
|
216
216
|
g.mode = os.getenv("MODE")
|
|
217
217
|
f_abs_path = os.path.abspath(__file__)
|
|
218
218
|
g.mainpath = "/".join(f_abs_path.split("/")[:-1])
|
|
@@ -100,6 +100,13 @@ def init():
|
|
|
100
100
|
if captcha_enabled:
|
|
101
101
|
captcha_sitekey = click.prompt("Friendly captcha sitekey")
|
|
102
102
|
captcha_apikey = click.prompt("Friendly captcha API key")
|
|
103
|
+
valkey_enabled = click.confirm("Enable valkey (queue for likes and views)?", default=False)
|
|
104
|
+
if valkey_enabled:
|
|
105
|
+
valkey_host = click.prompt("Valkey host", default="localhost")
|
|
106
|
+
valkey_port = click.prompt("Valkey port", default="6379")
|
|
107
|
+
valkey_password = click.prompt("Valkey password(optional)", default="")
|
|
108
|
+
valkey_db = click.prompt("Valkey database", default="0")
|
|
109
|
+
valkey_interval = click.prompt("How often should valkey refresh data to DB? (minutes)", default="5")
|
|
103
110
|
|
|
104
111
|
|
|
105
112
|
|
|
@@ -145,6 +152,13 @@ def init():
|
|
|
145
152
|
if captcha_enabled:
|
|
146
153
|
f.write(f"FRIENDLY_CAPTCHA_SITEKEY={captcha_sitekey}\n")
|
|
147
154
|
f.write(f"FRIENDLY_CAPTCHA_APIKEY={captcha_apikey}\n")
|
|
155
|
+
if valkey_enabled:
|
|
156
|
+
f.write(f"VALKEY_ENABLED={'yes' if valkey_enabled else 'no'}\n")
|
|
157
|
+
f.write(f"VALKEY_HOST={valkey_host}\n")
|
|
158
|
+
f.write(f"VALKEY_PORT={valkey_port}\n")
|
|
159
|
+
f.write(f"VALKEY_PASSWORD={valkey_password}\n")
|
|
160
|
+
f.write(f"VALKEY_DB={valkey_db}\n")
|
|
161
|
+
f.write(f"VALKEY_INTERVAL={valkey_interval}\n")
|
|
148
162
|
|
|
149
163
|
click.echo("[+] .env file created")
|
|
150
164
|
if logs_enabled and not os.path.exists(logs_dir):
|
|
@@ -185,7 +199,12 @@ def run(daemon):
|
|
|
185
199
|
"gmcapsuled", "-c", f"{cwd}/gemini.ini"
|
|
186
200
|
]
|
|
187
201
|
|
|
202
|
+
worker_cmd = [
|
|
203
|
+
sys.executable, "-m", "openwrite.utils.worker"
|
|
204
|
+
]
|
|
205
|
+
|
|
188
206
|
gemini_log = open(os.path.join(logs_dir, "gemini.log"), "a") if logs_enabled else subprocess.PIPE
|
|
207
|
+
worker_log = open(os.path.join(logs_dir, "worker.log"), "a") if logs_enabled else subprocess.PIPE
|
|
189
208
|
|
|
190
209
|
if gemini_proxy:
|
|
191
210
|
gemini_cmd = gemini_cmd + ["proxy"]
|
|
@@ -193,33 +212,37 @@ def run(daemon):
|
|
|
193
212
|
if logs_enabled:
|
|
194
213
|
gunicorn_cmd = gunicorn_cmd + gunicorn_logs
|
|
195
214
|
|
|
196
|
-
|
|
197
215
|
if daemon:
|
|
198
216
|
click.echo(f"[+] Openwrite listening on {ip}:{port}")
|
|
199
217
|
subprocess.Popen(gunicorn_cmd + ["--daemon"])
|
|
200
218
|
|
|
201
219
|
if gemini:
|
|
202
220
|
click.echo(f"[+] Gemini listening on {ip}:{gemini_port}")
|
|
203
|
-
|
|
204
221
|
subprocess.Popen(gemini_cmd, stdout=gemini_log, stderr=subprocess.STDOUT)
|
|
205
222
|
|
|
223
|
+
click.echo("[+] Starting worker process")
|
|
224
|
+
subprocess.Popen(worker_cmd, stdout=worker_log, stderr=subprocess.STDOUT)
|
|
225
|
+
|
|
206
226
|
else:
|
|
207
|
-
gunicorn_proc = subprocess.Popen(
|
|
208
|
-
gunicorn_cmd
|
|
209
|
-
)
|
|
227
|
+
gunicorn_proc = subprocess.Popen(gunicorn_cmd)
|
|
210
228
|
|
|
211
229
|
if gemini:
|
|
212
230
|
click.echo(f"[+] Gemini started on {ip}:{gemini_port}")
|
|
213
231
|
gemini_proc = subprocess.Popen(gemini_cmd, stdout=gemini_log)
|
|
214
232
|
|
|
233
|
+
click.echo("[+] Starting worker process")
|
|
234
|
+
worker_proc = subprocess.Popen(worker_cmd, stdout=worker_log)
|
|
235
|
+
|
|
215
236
|
try:
|
|
216
237
|
gunicorn_proc.wait()
|
|
217
238
|
if gemini:
|
|
218
239
|
gemini_proc.wait()
|
|
240
|
+
worker_proc.wait()
|
|
219
241
|
except KeyboardInterrupt:
|
|
220
242
|
gunicorn_proc.terminate()
|
|
221
243
|
if gemini:
|
|
222
244
|
gemini_proc.terminate()
|
|
245
|
+
worker_proc.terminate()
|
|
223
246
|
|
|
224
247
|
@cli.command()
|
|
225
248
|
def debugrun():
|
|
@@ -286,4 +309,3 @@ def stop(service):
|
|
|
286
309
|
|
|
287
310
|
if __name__ == "__main__":
|
|
288
311
|
cli()
|
|
289
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from flask import Blueprint, render_template, redirect, request, g, Response, abort
|
|
2
2
|
from openwrite.utils.models import Blog, Post, User, View, Like, Page
|
|
3
3
|
from openwrite.utils.helpers import gen_link, sanitize_html, anonymize, get_ip
|
|
4
|
+
from openwrite.utils.valkey_client import valkey_client
|
|
4
5
|
from feedgen.feed import FeedGenerator
|
|
5
6
|
from sqlalchemy import desc
|
|
6
7
|
import os
|
|
@@ -215,19 +216,29 @@ def show_post(blog, post):
|
|
|
215
216
|
blog_obj.url = f"/b/{blog_obj.name}"
|
|
216
217
|
|
|
217
218
|
user_agent = request.headers.get('User-Agent')
|
|
219
|
+
ip = anonymize(get_ip())
|
|
218
220
|
|
|
219
221
|
if 'Bot' not in user_agent:
|
|
220
|
-
ip = anonymize(get_ip())
|
|
221
222
|
v = g.db.query(View).filter(View.blog == blog_obj.id, View.post == one_post.id, View.hash == ip).count()
|
|
222
223
|
if v < 1:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
224
|
+
if valkey_client.is_enabled():
|
|
225
|
+
valkey_client.queue_view(blog_obj.id, one_post.id, ip, user_agent)
|
|
226
|
+
else:
|
|
227
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
228
|
+
new_view = View(blog=blog_obj.id, post=one_post.id, hash=ip, date=now, agent=user_agent)
|
|
229
|
+
g.db.add(new_view)
|
|
230
|
+
g.db.commit()
|
|
231
|
+
|
|
232
|
+
likes_db = g.db.query(Like).filter(Like.blog == blog_obj.id, Like.post == one_post.id).count()
|
|
233
|
+
likes_queued = valkey_client.count_queued_likes(blog_obj.id, one_post.id) if valkey_client.is_enabled() else 0
|
|
234
|
+
likes = likes_db + likes_queued
|
|
235
|
+
|
|
236
|
+
liked_db = g.db.query(Like).filter(Like.blog == blog_obj.id, Like.post == one_post.id, Like.hash == ip).count()
|
|
237
|
+
liked_queued = valkey_client.check_queued_like(blog_obj.id, one_post.id, ip) if valkey_client.is_enabled() else False
|
|
238
|
+
liked = liked_db or liked_queued
|
|
239
|
+
|
|
240
|
+
one_post.likes = likes
|
|
241
|
+
one_post.liked = liked
|
|
231
242
|
|
|
232
243
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
233
244
|
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, likes=likes, pages=pages)
|
|
@@ -275,24 +286,33 @@ def show_subpost(blog, post):
|
|
|
275
286
|
one_post.authorname = post_author.username
|
|
276
287
|
blog_obj.url = f"https://{blog_obj.name}.{os.getenv('DOMAIN')}"
|
|
277
288
|
|
|
278
|
-
user_agent = request.headers.get('User-Agent'
|
|
289
|
+
user_agent = request.headers.get('User-Agent')
|
|
290
|
+
ip = anonymize(get_ip())
|
|
291
|
+
|
|
279
292
|
if 'Bot' not in user_agent:
|
|
280
|
-
ip = anonymize(get_ip())
|
|
281
293
|
v = g.db.query(View).filter(View.blog == blog_obj.id, View.post == one_post.id, View.hash == ip).count()
|
|
282
|
-
user_agent = request.headers.get('User-Agent')
|
|
283
294
|
if v < 1:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
295
|
+
if valkey_client.is_enabled():
|
|
296
|
+
valkey_client.queue_view(blog_obj.id, one_post.id, ip, user_agent)
|
|
297
|
+
else:
|
|
298
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
299
|
+
new_view = View(blog=blog_obj.id, post=one_post.id, hash=ip, date=now, agent=user_agent)
|
|
300
|
+
g.db.add(new_view)
|
|
301
|
+
g.db.commit()
|
|
302
|
+
|
|
303
|
+
likes_db = g.db.query(Like).filter(Like.blog == blog_obj.id, Like.post == one_post.id).count()
|
|
304
|
+
likes_queued = valkey_client.count_queued_likes(blog_obj.id, one_post.id) if valkey_client.is_enabled() else 0
|
|
305
|
+
likes = likes_db + likes_queued
|
|
306
|
+
|
|
307
|
+
liked_db = g.db.query(Like).filter(Like.blog == blog_obj.id, Like.post == one_post.id, Like.hash == ip).count()
|
|
308
|
+
liked_queued = valkey_client.check_queued_like(blog_obj.id, one_post.id, ip) if valkey_client.is_enabled() else False
|
|
309
|
+
liked = liked_db or liked_queued
|
|
288
310
|
|
|
289
|
-
likes = g.db.query(Like).filter(Like.blog == blog_obj.id, Like.post == one_post.id).count()
|
|
290
311
|
one_post.likes = likes
|
|
291
|
-
liked = g.db.query(Like).filter(Like.blog == blog_obj.id, Like.post == one_post.id, Like.hash == anonymize(get_ip())).count()
|
|
292
312
|
one_post.liked = liked
|
|
293
313
|
|
|
294
314
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
295
|
-
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, pages=pages)
|
|
315
|
+
return render_template("post.html", blog=blog_obj, post=one_post, user=user, views=v, likes=likes, pages=pages)
|
|
296
316
|
|
|
297
317
|
@blog_bp.route("/.well-known/webfinger")
|
|
298
318
|
def webfinger():
|
|
@@ -382,23 +402,32 @@ def single_showpost(post):
|
|
|
382
402
|
one_post.authorname = post_author.username
|
|
383
403
|
|
|
384
404
|
user_agent = request.headers.get('User-Agent', '')
|
|
405
|
+
ip = anonymize(get_ip())
|
|
406
|
+
|
|
385
407
|
if 'Bot' not in user_agent:
|
|
386
|
-
|
|
387
|
-
v = g.db.query(View).filter(View.blog == 1, View.post == one_post.id, View.hash == ip).count()
|
|
388
|
-
user_agent = request.headers.get('User-Agent')
|
|
408
|
+
v = g.db.query(View).filter(View.blog == blog.id, View.post == one_post.id, View.hash == ip).count()
|
|
389
409
|
if v < 1:
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
410
|
+
if valkey_client.is_enabled():
|
|
411
|
+
valkey_client.queue_view(blog.id, one_post.id, ip, user_agent)
|
|
412
|
+
else:
|
|
413
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
414
|
+
new_view = View(blog=blog.id, post=one_post.id, hash=ip, date=now, agent=user_agent)
|
|
415
|
+
g.db.add(new_view)
|
|
416
|
+
g.db.commit()
|
|
417
|
+
|
|
418
|
+
likes_db = g.db.query(Like).filter(Like.blog == blog.id, Like.post == one_post.id).count()
|
|
419
|
+
likes_queued = valkey_client.count_queued_likes(blog.id, one_post.id) if valkey_client.is_enabled() else 0
|
|
420
|
+
likes = likes_db + likes_queued
|
|
421
|
+
|
|
422
|
+
liked_db = g.db.query(Like).filter(Like.blog == blog.id, Like.post == one_post.id, Like.hash == ip).count()
|
|
423
|
+
liked_queued = valkey_client.check_queued_like(blog.id, one_post.id, ip) if valkey_client.is_enabled() else False
|
|
424
|
+
liked = liked_db or liked_queued
|
|
394
425
|
|
|
395
|
-
likes = g.db.query(Like).filter(Like.blog == 1, Like.post == one_post.id).count()
|
|
396
426
|
one_post.likes = likes
|
|
397
|
-
liked = g.db.query(Like).filter(Like.blog == 1, Like.post == one_post.id, Like.hash == anonymize(get_ip())).count()
|
|
398
427
|
one_post.liked = liked
|
|
399
428
|
|
|
400
429
|
user = g.db.query(User).filter_by(id=g.user) if g.user else None
|
|
401
|
-
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, pages=pages)
|
|
430
|
+
return render_template("post.html", blog=blog, post=one_post, user=user, views=v, likes=likes, pages=pages)
|
|
402
431
|
|
|
403
432
|
@blog_bp.route("/rss")
|
|
404
433
|
def single_rss():
|
|
@@ -420,21 +449,39 @@ def like():
|
|
|
420
449
|
if post < 1:
|
|
421
450
|
resp = {"status": "no_post"}
|
|
422
451
|
return resp, 404
|
|
452
|
+
|
|
423
453
|
ip = anonymize(get_ip())
|
|
424
454
|
|
|
425
|
-
|
|
426
|
-
if
|
|
427
|
-
|
|
428
|
-
|
|
455
|
+
liked_db = g.db.query(Like).filter(Like.blog == blog_id, Like.post == post_id, Like.hash == ip).first()
|
|
456
|
+
liked_queued = valkey_client.check_queued_like(blog_id, post_id, ip) if valkey_client.is_enabled() else False
|
|
457
|
+
|
|
458
|
+
if liked_db:
|
|
459
|
+
if liked_db and valkey_client.is_enabled():
|
|
460
|
+
valkey_client.queue_like(blog_id, post_id, ip, 'remove')
|
|
461
|
+
elif liked_db and not valkey_client.is_enabled():
|
|
462
|
+
g.db.delete(liked_db)
|
|
463
|
+
g.db.commit()
|
|
464
|
+
|
|
465
|
+
resp = {"status": "deleted"}
|
|
466
|
+
status = 204
|
|
467
|
+
|
|
468
|
+
elif liked_queued and valkey_client.is_enabled():
|
|
469
|
+
valkey_client.queue_like(blog_id, post_id, ip, 'remove')
|
|
470
|
+
|
|
429
471
|
resp = {"status": "deleted"}
|
|
430
472
|
status = 204
|
|
431
473
|
else:
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
474
|
+
if valkey_client.is_enabled():
|
|
475
|
+
valkey_client.queue_like(blog_id, post_id, ip, 'add')
|
|
476
|
+
else:
|
|
477
|
+
now = datetime.now(timezone.utc).replace(microsecond=0)
|
|
478
|
+
like = Like(blog=blog_id, post=post_id, hash=ip, date=now)
|
|
479
|
+
g.db.add(like)
|
|
480
|
+
g.db.commit()
|
|
481
|
+
|
|
436
482
|
resp = {"status": "ok"}
|
|
437
483
|
status = 201
|
|
484
|
+
|
|
438
485
|
return resp, status
|
|
439
486
|
|
|
440
487
|
|
|
@@ -461,6 +508,4 @@ def _generate_rss(blog):
|
|
|
461
508
|
fe.description(soup.get_text())
|
|
462
509
|
fe.published(p.date.replace(tzinfo=timezone.utc))
|
|
463
510
|
|
|
464
|
-
return Response(fg.rss_str(pretty=True).decode("utf-8"), mimetype="application/rss+xml")
|
|
465
|
-
|
|
466
|
-
|
|
511
|
+
return Response(fg.rss_str(pretty=True).decode("utf-8"), mimetype="application/rss+xml")
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from flask import Blueprint, render_template, redirect, request, g, abort, Response
|
|
2
|
-
from openwrite.utils.models import Blog, Post, User, View, Page
|
|
2
|
+
from openwrite.utils.models import Blog, Post, User, View, Like, Page
|
|
3
3
|
from openwrite.utils.helpers import sanitize_html, gen_link, safe_css, send_activity, is_html, get_themes
|
|
4
4
|
from openwrite.routes.blog import create_activity_object, create_person_object
|
|
5
|
+
from openwrite.utils.valkey_client import valkey_client
|
|
5
6
|
import requests
|
|
6
7
|
from sqlalchemy import desc
|
|
7
8
|
from datetime import datetime, timezone, timedelta
|
|
@@ -148,12 +149,10 @@ def delete_blog(name):
|
|
|
148
149
|
"cc": [f"https://{g.main_domain}/followers/{blog.name}"],
|
|
149
150
|
"object": person_object
|
|
150
151
|
}
|
|
151
|
-
|
|
152
|
-
delete_activity,
|
|
153
|
-
|
|
154
|
-
blog_url,
|
|
155
|
-
f"{actor}/inbox"
|
|
156
|
-
)
|
|
152
|
+
if valkey_client.is_enabled():
|
|
153
|
+
valkey_client.queue_federation_activity(delete_activity, blog.id, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
154
|
+
else:
|
|
155
|
+
send_activity(delete_activity, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
157
156
|
|
|
158
157
|
g.db.delete(blog)
|
|
159
158
|
g.db.commit()
|
|
@@ -226,15 +225,14 @@ def edit_blog(name):
|
|
|
226
225
|
"cc": [f"https://{g.main_domain}/followers/{blog.name}"],
|
|
227
226
|
"object": person_object
|
|
228
227
|
}
|
|
229
|
-
|
|
230
|
-
update_activity,
|
|
231
|
-
|
|
232
|
-
blog_url,
|
|
233
|
-
f"{actor}/inbox"
|
|
234
|
-
)
|
|
228
|
+
if valkey_client.is_enabled():
|
|
229
|
+
valkey_client.queue_federation_activity(update_activity, blog.id, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
230
|
+
else:
|
|
231
|
+
send_activity(update_activity, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
235
232
|
|
|
236
233
|
return render_template("edit.html", blog=blog, posts=posts, themes=themes, pages=pages)
|
|
237
234
|
|
|
235
|
+
|
|
238
236
|
@dashboard_bp.route("/dashboard/post/<name>", methods=['GET', 'POST'])
|
|
239
237
|
def new_post(name):
|
|
240
238
|
if g.user is None:
|
|
@@ -306,12 +304,10 @@ def new_post(name):
|
|
|
306
304
|
"cc": [f"https://{g.main_domain}/followers/{blog.name}"],
|
|
307
305
|
"object": activity_object
|
|
308
306
|
}
|
|
309
|
-
|
|
310
|
-
activity,
|
|
311
|
-
|
|
312
|
-
blog_url,
|
|
313
|
-
f"{actor}/inbox"
|
|
314
|
-
)
|
|
307
|
+
if valkey_client.is_enabled():
|
|
308
|
+
valkey_client.queue_federation_activity(activity, blog.id, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
309
|
+
else:
|
|
310
|
+
send_activity(activity, blog.priv_key, blog_url, f"{actor}/inbox")
|
|
315
311
|
|
|
316
312
|
return redirect(f"/dashboard/edit/{blog.name}")
|
|
317
313
|
|
|
@@ -411,46 +407,25 @@ def edit_post(blog, post):
|
|
|
411
407
|
|
|
412
408
|
if followers:
|
|
413
409
|
activity_object = create_activity_object(p, blog_obj.name, blog_url, full_content=False)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
else:
|
|
434
|
-
for actor in followers:
|
|
435
|
-
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
436
|
-
update_activity = {
|
|
437
|
-
"@context": "https://www.w3.org/ns/activitystreams",
|
|
438
|
-
"id": f"{blog_url}/{p.link}#update",
|
|
439
|
-
"type": "Update",
|
|
440
|
-
"actor": blog_url,
|
|
441
|
-
"published": now_iso,
|
|
442
|
-
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
443
|
-
"cc": [f"https://{g.main_domain}/followers/{blog_obj.name}"],
|
|
444
|
-
"object": activity_object
|
|
445
|
-
}
|
|
446
|
-
send_activity(
|
|
447
|
-
update_activity,
|
|
448
|
-
blog_obj.priv_key,
|
|
449
|
-
blog_url,
|
|
450
|
-
f"{actor}/inbox"
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
return redirect(f"/dashboard/edit/{blog_obj.name}")
|
|
410
|
+
|
|
411
|
+
for actor in followers:
|
|
412
|
+
now_iso = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
413
|
+
update_activity = {
|
|
414
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
415
|
+
"id": f"{blog_url}/{p.link}#update",
|
|
416
|
+
"type": "Update",
|
|
417
|
+
"actor": blog_url,
|
|
418
|
+
"published": now_iso,
|
|
419
|
+
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
|
420
|
+
"cc": [f"https://{g.main_domain}/followers/{blog_obj.name}"],
|
|
421
|
+
"object": activity_object
|
|
422
|
+
}
|
|
423
|
+
if valkey_client.is_enabled():
|
|
424
|
+
valkey_client.queue_federation_activity(update_activity, blog_obj.id, blog_obj.priv_key, blog_url, f"{actor}/inbox")
|
|
425
|
+
else:
|
|
426
|
+
send_activity(update_activity, blog_obj.priv_key, blog_url, f"{actor}/inbox")
|
|
427
|
+
|
|
428
|
+
return redirect(f"/dashboard/edit/{blog}")
|
|
454
429
|
|
|
455
430
|
@dashboard_bp.route("/dashboard/edit/<blog>/<post>/delete")
|
|
456
431
|
def delete_post(blog, post):
|
|
@@ -487,12 +462,15 @@ def delete_post(blog, post):
|
|
|
487
462
|
"cc": [f"https://{g.main_domain}/followers/{blog_obj.name}"],
|
|
488
463
|
"object": activity_object
|
|
489
464
|
}
|
|
490
|
-
|
|
491
|
-
delete_activity,
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
465
|
+
if valkey_client.is_enabled():
|
|
466
|
+
valkey_client.queue_federation_activity(delete_activity, blog_obj.id, blog_obj.priv_key, blog_url, f"{actor}/inbox")
|
|
467
|
+
else:
|
|
468
|
+
send_activity(
|
|
469
|
+
delete_activity,
|
|
470
|
+
blog_obj.priv_key,
|
|
471
|
+
blog_url,
|
|
472
|
+
f"{actor}/inbox"
|
|
473
|
+
)
|
|
496
474
|
|
|
497
475
|
g.db.delete(p)
|
|
498
476
|
g.db.commit()
|
|
@@ -500,6 +478,7 @@ def delete_post(blog, post):
|
|
|
500
478
|
return redirect(f"/dashboard/edit/{blog_obj.name}")
|
|
501
479
|
|
|
502
480
|
|
|
481
|
+
|
|
503
482
|
@dashboard_bp.route("/dashboard/changepw", methods=['GET', 'POST'])
|
|
504
483
|
def changepw():
|
|
505
484
|
if g.user is None:
|