From 69ce1fedf28f914ab6de536234f3d8eff30697c1 Mon Sep 17 00:00:00 2001 From: Divided by Zer0 Date: Thu, 6 Jun 2024 16:52:36 +0200 Subject: [PATCH] feat: Added documents endpoint (#416) * feat: Added documents endpoint --- CHANGELOG.md | 4 + horde/apis/models/v2.py | 13 +++ horde/apis/v2/__init__.py | 3 + horde/apis/v2/base.py | 138 +++++++++++++++++++++++++- horde/consts.py | 2 +- horde/routes.py | 9 +- horde/templates/document.html | 7 ++ horde/templates/privacy_policy.html | 4 - horde/templates/sponsors.html | 8 +- horde/templates/terms_of_service.html | 6 +- requirements.txt | 3 +- 11 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 horde/templates/document.html diff --git a/CHANGELOG.md b/CHANGELOG.md index f066797a..e6595031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +# 4.36.0 + +* Added Document endpoints + # 4.35.1 * Added allow_sdxl_controlnet worker key diff --git a/horde/apis/models/v2.py b/horde/apis/models/v2.py index 98e071c4..099f84ff 100644 --- a/horde/apis/models/v2.py +++ b/horde/apis/models/v2.py @@ -1507,3 +1507,16 @@ def __init__(self, api): "reference": fields.String(description="The reference which points how and where this text should be used.", min_length=3), }, ) + self.response_model_doc_terms = api.model( + "HordeDocument", + { + "html": fields.String( + required=False, + description="The document in html format.", + ), + "markdown": fields.String( + required=False, + description="The document in markdown format.", + ), + }, + ) diff --git a/horde/apis/v2/__init__.py b/horde/apis/v2/__init__.py index b3fcaffc..0ba7ddd7 100644 --- a/horde/apis/v2/__init__.py +++ b/horde/apis/v2/__init__.py @@ -45,3 +45,6 @@ api.add_resource(stable.ImageHordeStatsModels, "/stats/img/models") api.add_resource(kobold.TextHordeStatsTotals, "/stats/text/totals") api.add_resource(kobold.TextHordeStatsModels, "/stats/text/models") +api.add_resource(base.DocsTerms, "/documents/terms") +api.add_resource(base.DocsPrivacy, "/documents/privacy") +api.add_resource(base.DocsSponsors, "/documents/sponsors") diff --git a/horde/apis/v2/base.py b/horde/apis/v2/base.py index 028385a7..48e7b9eb 100644 --- a/horde/apis/v2/base.py +++ b/horde/apis/v2/base.py @@ -4,9 +4,10 @@ from datetime import datetime, timedelta import regex as re -from flask import request +from flask import render_template, request from flask_restx import Namespace, Resource, reqparse from flask_restx.reqparse import ParseResult +from markdownify import markdownify from sqlalchemy import or_ from sqlalchemy.exc import IntegrityError, InvalidRequestError @@ -35,7 +36,7 @@ from horde.r2 import upload_prompt from horde.suspicions import Suspicions from horde.utils import hash_api_key, hash_dictionary, is_profane, sanitize_string -from horde.vars import horde_title +from horde.vars import horde_contact_email, horde_title, horde_url # Not used yet authorizations = {"apikey": {"type": "apiKey", "in": "header", "name": "apikey"}} @@ -2914,3 +2915,136 @@ def delete(self, sharedkey_id=""): db.session.delete(sharedkey) db.session.commit() return {"message": "OK"}, 200 + + +class DocsTerms(Resource): + get_parser = reqparse.RequestParser() + get_parser.add_argument( + "Client-Agent", + default="unknown:0:unknown", + type=str, + required=False, + help="The client name and version", + location="headers", + ) + get_parser.add_argument( + "format", + required=False, + default="html", + type=str, + help="html or markdown", + location="args", + ) + + # @cache.cached(timeout=3600, query_string=True) + @api.response(400, "Validation Error", models.response_model_error) + @api.expect(get_parser) + @api.marshal_with( + models.response_model_doc_terms, + code=200, + description=f"{horde_title} documentation", + skip_none=True, + ) + def get(self): + """Terms and Conditions""" + self.args = self.get_parser.parse_args() + if self.args.format not in ["html", "markdown"]: + raise e.BadRequest("'format' needs to be one of ['html', 'markdown']") + html_template = render_template( + os.getenv("HORDE_HTML_TERMS", "terms_of_service.html"), + horde_title=horde_title, + horde_url=horde_url, + horde_contact_email=horde_contact_email, + ) + if self.args.format == "markdown": + return {"markdown": markdownify(html_template).strip("\n")}, 200 + return {"html": html_template}, 200 + + +class DocsPrivacy(Resource): + get_parser = reqparse.RequestParser() + get_parser.add_argument( + "Client-Agent", + default="unknown:0:unknown", + type=str, + required=False, + help="The client name and version", + location="headers", + ) + get_parser.add_argument( + "format", + required=False, + default="html", + type=str, + help="html or markdown", + location="args", + ) + + # @cache.cached(timeout=3600, query_string=True) + @api.response(400, "Validation Error", models.response_model_error) + @api.expect(get_parser) + @api.marshal_with( + models.response_model_doc_terms, + code=200, + description=f"{horde_title} documentation", + skip_none=True, + ) + def get(self): + """Privacy Policy""" + self.args = self.get_parser.parse_args() + if self.args.format not in ["html", "markdown"]: + raise e.BadRequest("'format' needs to be one of ['html', 'markdown']") + html_template = render_template( + os.getenv("HORDE_HTML_PRIVACY", "privacy_policy.html"), + horde_title=horde_title, + horde_url=horde_url, + horde_contact_email=horde_contact_email, + ) + if self.args.format == "markdown": + return {"markdown": markdownify(html_template).strip("\n")}, 200 + return {"html": html_template}, 200 + + +class DocsSponsors(Resource): + get_parser = reqparse.RequestParser() + get_parser.add_argument( + "Client-Agent", + default="unknown:0:unknown", + type=str, + required=False, + help="The client name and version", + location="headers", + ) + get_parser.add_argument( + "format", + required=False, + default="html", + type=str, + help="html or markdown", + location="args", + ) + + # @cache.cached(timeout=3600, query_string=True) + @api.response(400, "Validation Error", models.response_model_error) + @api.expect(get_parser) + @api.marshal_with( + models.response_model_doc_terms, + code=200, + description=f"{horde_title} documentation", + skip_none=True, + ) + def get(self): + """Sponsors""" + self.args = self.get_parser.parse_args() + if self.args.format not in ["html", "markdown"]: + raise e.BadRequest("'format' needs to be one of ['html', 'markdown']") + all_patrons = ", ".join(patrons.get_names(min_entitlement=3, max_entitlement=99)) + html_template = render_template( + "sponsors.html", + page_title="Sponsors", + all_patrons=all_patrons, + all_sponsors=patrons.get_sponsors(), + ) + if self.args.format == "markdown": + return {"markdown": markdownify(html_template).strip("\n")}, 200 + return {"html": html_template}, 200 diff --git a/horde/consts.py b/horde/consts.py index 54a985da..d56e4456 100644 --- a/horde/consts.py +++ b/horde/consts.py @@ -1,4 +1,4 @@ -HORDE_VERSION = "4.35.1" +HORDE_VERSION = "4.36.0" WHITELISTED_SERVICE_IPS = { "212.227.227.178", # Turing Bot diff --git a/horde/routes.py b/horde/routes.py index ca687924..1a29ac7e 100644 --- a/horde/routes.py +++ b/horde/routes.py @@ -140,7 +140,8 @@ def index(): def patrons_route(): all_patrons = ", ".join(patrons.get_names(min_entitlement=3, max_entitlement=99)) return render_template( - "sponsors.html", + "document.html", + doc="sponsors.html", page_title="Sponsors", all_patrons=all_patrons, all_sponsors=patrons.get_sponsors(), @@ -364,7 +365,8 @@ def finish_dance(): @HORDE.route("/privacy") def privacy(): return render_template( - os.getenv("HORDE_HTML_PRIVACY", "privacy_policy.html"), + "document.html", + doc=os.getenv("HORDE_HTML_TERMS", "privacy_policy.html"), horde_title=horde_title, horde_url=horde_url, horde_contact_email=horde_contact_email, @@ -374,7 +376,8 @@ def privacy(): @HORDE.route("/terms") def terms(): return render_template( - os.getenv("HORDE_HTML_TERMS", "terms_of_service.html"), + "document.html", + doc=os.getenv("HORDE_HTML_TERMS", "terms_of_service.html"), horde_title=horde_title, horde_url=horde_url, horde_contact_email=horde_contact_email, diff --git a/horde/templates/document.html b/horde/templates/document.html new file mode 100644 index 00000000..0a18fab9 --- /dev/null +++ b/horde/templates/document.html @@ -0,0 +1,7 @@ +{% extends "master.html" %} +{% block content %} +{% include doc %} +
+
+

Back to Main page

+{% endblock %} \ No newline at end of file diff --git a/horde/templates/privacy_policy.html b/horde/templates/privacy_policy.html index d8b03a1b..9791b6cd 100644 --- a/horde/templates/privacy_policy.html +++ b/horde/templates/privacy_policy.html @@ -1,5 +1,3 @@ -{% extends "master.html" %} -{% block content %}

Privacy Policy

Last updated: 18 Mar, 2024

This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.

@@ -153,5 +151,3 @@

Contact Us

-

Back to Main page

-{% endblock %} \ No newline at end of file diff --git a/horde/templates/sponsors.html b/horde/templates/sponsors.html index 1e6b824b..3795c011 100644 --- a/horde/templates/sponsors.html +++ b/horde/templates/sponsors.html @@ -1,5 +1,3 @@ -{% extends "master.html" %} -{% block content %}

Sponsors

\ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3166d7bd..4e99f67d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,4 +28,5 @@ git+https://github.com/Patreon/patreon-python.git torch emoji semver >= 3.0.2 -numpy ~= 1.24.1 # better_profanity fails on later versions of numpy +numpy ~= 1.24.1 # better_profanity fails on later versions of numpy +markdownify \ No newline at end of file