geminispace.info

gemini search engine
git clone https://git.clttr.info/geminispace.info.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

views.py (8507B)


      1 import math
      2 import os
      3 from datetime import datetime
      4 from urllib.parse import quote, unquote
      5 
      6 import jinja2
      7 from jetforce import Request, Response, Status, JetforceApplication
      8 
      9 from . import constants
     10 from .models import (
     11     compute_verbose,
     12     compute_requested_results_page,
     13     GUS,
     14     process_seed_request,
     15 )
     16 from gus.excludes import EXCLUDED_URL_PREFIXES, EXCLUDED_URL_PATHS
     17 
     18 TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates")
     19 
     20 template_env = jinja2.Environment(
     21     loader=jinja2.FileSystemLoader(TEMPLATE_DIR),
     22     undefined=jinja2.StrictUndefined,
     23     trim_blocks=True,
     24     lstrip_blocks=True,
     25 )
     26 
     27 
     28 def datetimeformat(value, format="%Y-%m-%d"):
     29     return value.strftime(format)
     30 
     31 
     32 template_env.filters["datetimeformat"] = datetimeformat
     33 
     34 
     35 def render_template(name: str, *args, **kwargs) -> str:
     36     """
     37     Render a gemini directory using the Jinja2 template engine.
     38     """
     39     return template_env.get_template(name).render(*args, **kwargs)
     40 
     41 
     42 app = JetforceApplication()
     43 gus = GUS()
     44 
     45 
     46 @app.route("/status", strict_trailing_slash=False)
     47 def status(request):
     48     return Response(Status.SUCCESS, "text/plain", "ok")
     49 
     50 @app.route("/robots.txt", strict_trailing_slash=False)
     51 def status(request):
     52     return Response(Status.SUCCESS, "text/plain",
     53             """User-agent: researcher
     54 User-agent: indexer
     55 User-agent: archiver
     56 Disallow: /v/search
     57 Disallow: /search
     58 Disallow: /backlinks
     59 
     60 User-agent: *
     61 Disallow: /add-seed
     62 """)
     63 
     64 @app.route("/favicon.txt", strict_trailing_slash=False)
     65 def favicon(request):
     66     return Response(Status.SUCCESS, "text/plain", "🔭")
     67 
     68 
     69 @app.route("/add-seed", strict_trailing_slash=False)
     70 def add_seed(request):
     71     if request.query:
     72         process_seed_request(request.query)
     73         body = render_template("add_seed.gmi", seed_url=request.query)
     74         return Response(Status.SUCCESS, "text/gemini", body)
     75     else:
     76         return Response(Status.INPUT, "Gemini URL")
     77 
     78 
     79 @app.route("/statistics", strict_trailing_slash=False)
     80 def statistics(request):
     81     body = render_template(
     82         "statistics.gmi",
     83         statistics=gus.statistics,
     84         index_modification_time=gus.statistics["index_modification_time"]
     85     )
     86     return Response(Status.SUCCESS, "text/gemini", body)
     87 
     88 
     89 @app.route("/statistics/historical/overall", strict_trailing_slash=False)
     90 def statistics(request):
     91     body = render_template(
     92         "statistics_historical_overall.gmi",
     93         statistics_historical_overall=gus.statistics_historical_overall[::-1],
     94         index_modification_time=gus.statistics["index_modification_time"]
     95     )
     96     return Response(Status.SUCCESS, "text/gemini", body)
     97 
     98 
     99 @app.route("/known-hosts", strict_trailing_slash=False)
    100 def known_hosts(request):
    101     body = render_template(
    102         "known_hosts.gmi",
    103         known_hosts=gus.hosts,
    104         index_modification_time=gus.statistics["index_modification_time"]
    105     )
    106     return Response(Status.SUCCESS, "text/gemini", body)
    107 
    108 
    109 @app.route("/newest-hosts", strict_trailing_slash=False)
    110 def newest_hosts(request):
    111     body = render_template(
    112         "newest_hosts.gmi",
    113         newest_hosts=gus.newest_hosts,
    114         index_modification_time=gus.statistics["index_modification_time"]
    115     )
    116     return Response(Status.SUCCESS, "text/gemini", body)
    117 
    118 
    119 @app.route("/newest-pages", strict_trailing_slash=False)
    120 def newest_pages(request):
    121     body = render_template(
    122         "newest_pages.gmi",
    123         newest_pages=gus.newest_pages,
    124         index_modification_time=gus.statistics["index_modification_time"]
    125     )
    126     return Response(Status.SUCCESS, "text/gemini", body)
    127 
    128 
    129 @app.route("/known-feeds", strict_trailing_slash=False)
    130 def known_feeds(request):
    131     body = render_template(
    132         "known_feeds.gmi",
    133         known_feeds=gus.feeds,
    134         index_modification_time=gus.statistics["index_modification_time"]
    135     )
    136 
    137     return Response(Status.SUCCESS, "text/gemini", body)
    138 
    139 
    140 @app.route("", strict_trailing_slash=False)
    141 def index(request):
    142     body = render_template(
    143         "index.gmi",
    144         index_modification_time=gus.statistics["index_modification_time"]
    145     )
    146     return Response(Status.SUCCESS, "text/gemini", body)
    147 
    148 
    149 @app.route("/about", strict_trailing_slash=False)
    150 def index(request):
    151     body = render_template(
    152         "about.gmi",
    153         index_modification_time=gus.statistics["index_modification_time"]
    154     )
    155     return Response(Status.SUCCESS, "text/gemini", body)
    156 
    157 
    158 @app.route("/documentation/searching", strict_trailing_slash=False)
    159 def documentation_searching(request):
    160     body = render_template(
    161         "documentation/searching.gmi",
    162         index_modification_time=gus.statistics["index_modification_time"]
    163     )
    164     return Response(Status.SUCCESS, "text/gemini", body)
    165 
    166 
    167 @app.route("/documentation/indexing", strict_trailing_slash=False)
    168 def documentation_indexing(request):
    169     body = render_template(
    170         "documentation/indexing.gmi",
    171         index_modification_time=gus.statistics["index_modification_time"]
    172     )
    173     return Response(Status.SUCCESS, "text/gemini", body)
    174 
    175 
    176 @app.route("/documentation/filters", strict_trailing_slash=False)
    177 def documentation_filters(request):
    178     body = render_template(
    179         "documentation/filters.gmi",
    180         index_modification_time=gus.statistics["index_modification_time"],
    181         filters=sorted(EXCLUDED_URL_PREFIXES)
    182     )
    183     return Response(Status.SUCCESS, "text/gemini", body)
    184 
    185 
    186 @app.route("/documentation/backlinks", strict_trailing_slash=False)
    187 def documentation_backlinks(request):
    188     body = render_template(
    189         "documentation/backlinks.gmi",
    190         index_modification_time=gus.statistics["index_modification_time"]
    191     )
    192     return Response(Status.SUCCESS, "text/gemini", body)
    193 
    194 
    195 @app.route("/news", strict_trailing_slash=False)
    196 def index(request):
    197     body = render_template(
    198         "news.gmi",
    199         index_modification_time=gus.statistics["index_modification_time"]
    200     )
    201     return Response(Status.SUCCESS, "text/gemini", body)
    202 
    203 
    204 @app.route("/v/search(/\d+)?")
    205 @app.route("/search(/\d+)?")
    206 def search(request):
    207     if request.query:
    208         verbose = compute_verbose(request.path)
    209         requested_page = compute_requested_results_page(request.path)
    210         num_results, results = gus.search_index(request.query, requested_page)
    211         #if num_results > 0:
    212         num_pages = math.ceil(num_results / 10)
    213         current_page = min(requested_page, num_pages)
    214         if num_results == 0:
    215             current_page = 0
    216         body = render_template(
    217             "search.gmi",
    218             query=request.query,
    219             quoted_query=quote(request.query),
    220             verbose=verbose,
    221             num_results=num_results,
    222             results=results,
    223             current_page=current_page,
    224             num_pages=num_pages,
    225             index_modification_time=gus.statistics["index_modification_time"]
    226         )
    227         #else:
    228             #    search_suggestions = gus.get_search_suggestions(request.query)
    229             #    body = render_template(
    230             #        "search_suggestions.gmi",
    231             #        query=request.query,
    232             #        search_suggestions=search_suggestions,
    233             #        index_modification_time=gus.statistics["index_modification_time"]
    234             #    )
    235 
    236         return Response(Status.SUCCESS, "text/gemini", body)
    237     else:
    238         return Response(Status.INPUT, "Search query")
    239 
    240 
    241 @app.route("/search/jump/(?P<query>.+)")
    242 @app.route("/v/search/jump/(?P<query>.+)")
    243 def search_jump(request, query):
    244     if request.query:
    245         if request.query.isdigit():
    246             jump_page = request.query
    247         else:
    248             jump_page = 1
    249         verbose_segment = "/v" if request.path.startswith("/v") else ""
    250         jump_url = f"{verbose_segment}/search/{jump_page}?{query}"
    251         return Response(Status.REDIRECT_TEMPORARY, jump_url)
    252     else:
    253         return Response(Status.INPUT, "Jump to page")
    254 
    255 
    256 @app.route("/backlinks")
    257 def backlinks(request):
    258     if request.query:
    259         url = unquote(request.query)
    260         if not url.startswith("gemini://"):
    261             url = "gemini://{}".format(url)
    262         internal_backlinks, external_backlinks = gus.get_backlinks(url)
    263         body = render_template(
    264             "backlinks.gmi",
    265             url=url,
    266             internal_backlinks=internal_backlinks,
    267             external_backlinks=external_backlinks,
    268             index_modification_time=gus.statistics["index_modification_time"]
    269         )
    270         return Response(Status.SUCCESS, "text/gemini", body)
    271     else:
    272         return Response(Status.INPUT, "Gemini URL")