mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
store/wishlist_add: ratelimit wishlist proposal to once every 15 days per user
This commit is contained in:
parent
e9524f63e6
commit
3db73ec95d
3 changed files with 42 additions and 11 deletions
12
store/app.py
12
store/app.py
|
@ -31,6 +31,8 @@ from utils import (
|
||||||
get_wishlist,
|
get_wishlist,
|
||||||
get_stars,
|
get_stars,
|
||||||
get_app_md_and_screenshots,
|
get_app_md_and_screenshots,
|
||||||
|
save_wishlist_submit_for_ratelimit,
|
||||||
|
check_wishlist_submit_ratelimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
app = Flask(__name__, static_url_path="/assets", static_folder="assets")
|
app = Flask(__name__, static_url_path="/assets", static_folder="assets")
|
||||||
|
@ -199,7 +201,6 @@ def add_to_wishlist():
|
||||||
successmsg=None,
|
successmsg=None,
|
||||||
errormsg=errormsg,
|
errormsg=errormsg,
|
||||||
)
|
)
|
||||||
|
|
||||||
csrf_token = request.form["csrf_token"]
|
csrf_token = request.form["csrf_token"]
|
||||||
|
|
||||||
if csrf_token != session.get("csrf_token"):
|
if csrf_token != session.get("csrf_token"):
|
||||||
|
@ -222,6 +223,10 @@ def add_to_wishlist():
|
||||||
boring_keywords_to_check_for_people_not_reading_the_instructions = ["free", "open source", "open-source", "self-hosted", "simple", "lightweight", "light-weight", "best", "most", "fast", "flexible", "puissante", "powerful", "secure"]
|
boring_keywords_to_check_for_people_not_reading_the_instructions = ["free", "open source", "open-source", "self-hosted", "simple", "lightweight", "light-weight", "best", "most", "fast", "flexible", "puissante", "powerful", "secure"]
|
||||||
|
|
||||||
checks = [
|
checks = [
|
||||||
|
(
|
||||||
|
check_wishlist_submit_ratelimit(session['user']['username']) is True,
|
||||||
|
_("Proposing wishlist additions is limited to once every 15 days per user.")
|
||||||
|
),
|
||||||
(len(name) >= 3, _("App name should be at least 3 characters")),
|
(len(name) >= 3, _("App name should be at least 3 characters")),
|
||||||
(len(name) <= 30, _("App name should be less than 30 characters")),
|
(len(name) <= 30, _("App name should be less than 30 characters")),
|
||||||
(
|
(
|
||||||
|
@ -258,7 +263,7 @@ def add_to_wishlist():
|
||||||
_("Please focus on what the app does, without using marketing, fuzzy terms, or repeating that the app is 'free' and 'self-hostable'.")
|
_("Please focus on what the app does, without using marketing, fuzzy terms, or repeating that the app is 'free' and 'self-hostable'.")
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
description.lower().split()[0] != name an description.lower().split()[1] not in ["is", "est"],
|
description.lower().split()[0] != name and (len(description.split()) == 1 or description.lower().split()[1] not in ["is", "est"]),
|
||||||
_("No need to repeat '{app} is'. Focus on what the app does.")
|
_("No need to repeat '{app} is'. Focus on what the app does.")
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -369,6 +374,9 @@ Description: {description}
|
||||||
"Your proposed app has succesfully been submitted. It must now be validated by the YunoHost team. You can track progress here: <a href='%(url)s'>%(url)s</a>",
|
"Your proposed app has succesfully been submitted. It must now be validated by the YunoHost team. You can track progress here: <a href='%(url)s'>%(url)s</a>",
|
||||||
url=url,
|
url=url,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
save_wishlist_submit_for_ratelimit(session['user']['username'])
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"wishlist_add.html",
|
"wishlist_add.html",
|
||||||
locale=get_locale(),
|
locale=get_locale(),
|
||||||
|
|
|
@ -28,20 +28,22 @@
|
||||||
<i class="fa fa-exclamation-triangle fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-exclamation-triangle fa-fw" aria-hidden="true"></i>
|
||||||
{{ _("You must first login to be allowed to submit an app to the wishlist") }}
|
{{ _("You must first login to be allowed to submit an app to the wishlist") }}
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
</p>
|
||||||
|
<p class="mt-2 text-sm text-orange-700">
|
||||||
{{ _("Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts.") }}
|
{{ _("Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts.") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% else %}
|
||||||
|
<div role="alert" class="rounded-md border-s-4 border-orange-500 bg-orange-50 p-4 mb-5">
|
||||||
<div role="alert" class="rounded-md border-s-4 border-sky-500 bg-sky-50 p-4">
|
<p class="mt-2 text-sm text-orange-700 font-bold">
|
||||||
<p class="mt-2 text-sm text-sky-700 font-bold">
|
<i class="fa fa-exclamation-triangle fa-fw" aria-hidden="true"></i>
|
||||||
<i class="fa fa-info-circle fa-fw" aria-hidden="true"></i>
|
{{ _("Due to abuses, only one proposal every 15 days per user is allowed.") }}
|
||||||
{{ _("Please check the license of the app your are proposing") }}
|
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-2 text-sm text-sky-700">
|
<p class="mt-2 text-sm text-orange-700">
|
||||||
{{ _("The YunoHost project will only package free/open-source software (with possible case-by-case exceptions for apps which are not-totally-free)") }}
|
{{ _("Reviewing those proposals is tiring for volunteers, please don't yolo-send every random nerdy stuff you find on the Internet.") }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if errormsg %}
|
{% if errormsg %}
|
||||||
<div role="alert" class="rounded-md border-s-4 border-red-500 bg-red-50 p-4 my-5">
|
<div role="alert" class="rounded-md border-s-4 border-red-500 bg-red-50 p-4 my-5">
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import time
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
@ -6,7 +7,7 @@ import subprocess
|
||||||
import pycmarkgfm
|
import pycmarkgfm
|
||||||
from emoji import emojize
|
from emoji import emojize
|
||||||
from flask import request
|
from flask import request
|
||||||
|
from hashlib import md5
|
||||||
|
|
||||||
AVAILABLE_LANGUAGES = ["en"] + os.listdir("translations")
|
AVAILABLE_LANGUAGES = ["en"] + os.listdir("translations")
|
||||||
|
|
||||||
|
@ -92,6 +93,26 @@ def get_stars():
|
||||||
get_stars.cache_checksum = None
|
get_stars.cache_checksum = None
|
||||||
get_stars()
|
get_stars()
|
||||||
|
|
||||||
|
def check_wishlist_submit_ratelimit(user):
|
||||||
|
|
||||||
|
dir_ = os.path.join(".wishlist_ratelimit")
|
||||||
|
if not os.path.exists(dir_):
|
||||||
|
os.mkdir(dir_)
|
||||||
|
|
||||||
|
f = os.path.join(dir_, md5(user.encode()).hexdigest())
|
||||||
|
|
||||||
|
return not os.path.exists(f) or (time.time() - os.path.getmtime(f)) > (15 * 24 * 3600) # 15 days
|
||||||
|
|
||||||
|
def save_wishlist_submit_for_ratelimit(user):
|
||||||
|
|
||||||
|
dir_ = os.path.join(".wishlist_ratelimit")
|
||||||
|
if not os.path.exists(dir_):
|
||||||
|
os.mkdir(dir_)
|
||||||
|
|
||||||
|
f = os.path.join(dir_, md5(user.encode()).hexdigest())
|
||||||
|
|
||||||
|
open(f, "w").write("")
|
||||||
|
|
||||||
|
|
||||||
def human_to_binary(size: str) -> int:
|
def human_to_binary(size: str) -> int:
|
||||||
symbols = ("K", "M", "G", "T", "P", "E", "Z", "Y")
|
symbols = ("K", "M", "G", "T", "P", "E", "Z", "Y")
|
||||||
|
|
Loading…
Add table
Reference in a new issue