222 lines
7.6 KiB
Python
222 lines
7.6 KiB
Python
# [[file:../arcology.org::*The Web Server][The Web Server:2]]
|
|
import logging
|
|
from django.http import HttpResponse, HttpResponseNotFound, Http404
|
|
from django.shortcuts import render, get_object_or_404
|
|
|
|
from arcology.models import Page, Feed, Site
|
|
from roam.models import Link
|
|
|
|
from prometheus_client import Counter, Histogram
|
|
|
|
logger = logging.getLogger(__name__)
|
|
# The Web Server:2 ends here
|
|
|
|
# [[file:../arcology.org::*=GET /= site index][=GET /= site index:1]]
|
|
def index(request):
|
|
site = Site.from_request(request)
|
|
full_key = f"{site.key}/index"
|
|
return render_page(request, site, full_key)
|
|
# =GET /= site index:1 ends here
|
|
|
|
# [[file:../arcology.org::*Arcology Org Page handler][Arcology Org Page handler:1]]
|
|
def org_page(request, key):
|
|
site = Site.from_request(request)
|
|
if site.key == "localhost":
|
|
full_key = key
|
|
new_site_key = key.split("/")[0]
|
|
site = Site.objects.filter(key=new_site_key).first()
|
|
else:
|
|
full_key = f"{site.key}/{key}"
|
|
|
|
return render_page(request, site, full_key)
|
|
# Arcology Org Page handler:1 ends here
|
|
|
|
# [[file:../arcology.org::*Arcology Org Page handler][Arcology Org Page handler:2]]
|
|
page_counter = Counter("arcology_page", "Hit counter for each page", ["site", "page", "status", "agent_type"])
|
|
render_latency = Histogram("arcology_page_render_seconds", "Latency for render_page func.", ["page", "site", "agent_type"])
|
|
|
|
from arcology.agent_utils import AgentClassification
|
|
from django.template import loader
|
|
|
|
def render_page(request, site, full_key):
|
|
agent = AgentClassification.from_request(request)
|
|
|
|
with render_latency.labels(page=full_key, site=site.key, agent_type=agent).time():
|
|
try:
|
|
the_page = Page.objects.get(route_key=full_key)
|
|
except Page.DoesNotExist:
|
|
page_counter.labels(page=full_key, status=404, site=site.key, agent_type=agent).inc()
|
|
template = loader.get_template("404.html")
|
|
context = dict(
|
|
missing_key=full_key
|
|
)
|
|
return HttpResponseNotFound(
|
|
template.render(context, request)
|
|
)
|
|
links = the_page.collect_links()
|
|
page_html = the_page.to_html(links)
|
|
|
|
feeds = site.feed_set.all()
|
|
|
|
page_counter.labels(page=full_key, status=200, site=site.key, agent_type=agent).inc()
|
|
|
|
return render(request, "arcology/page.html", dict(
|
|
site=site,
|
|
page=the_page,
|
|
feeds=feeds,
|
|
|
|
head_title=f"{the_page.title} - {site.title}",
|
|
html_content=page_html,
|
|
|
|
backlinks=the_page.collect_backlinks(),
|
|
keywords=the_page.collect_keywords().all(),
|
|
references=the_page.collect_references(),
|
|
tags=the_page.collect_tags(),
|
|
))
|
|
# Arcology Org Page handler:2 ends here
|
|
|
|
# [[file:../arcology.org::*Atom Feed Handler][Atom Feed Handler:1]]
|
|
import arrow
|
|
import roam.models
|
|
|
|
def feed(request, key):
|
|
# Get the site and construct the route key
|
|
site = Site.from_request(request)
|
|
if site.key == "localhost":
|
|
full_key = key
|
|
new_site_key = key.split("/")[0]
|
|
site = Site.objects.filter(key=new_site_key).first()
|
|
else:
|
|
full_key = f"{site.key}/{key}"
|
|
|
|
# Fetch page metadata
|
|
the_feed = get_object_or_404(Feed, route_key=full_key)
|
|
entries = the_feed.feedentry_set.order_by("-pubdate").all()[:10]
|
|
|
|
if len(entries) == 0:
|
|
return Http404()
|
|
|
|
try:
|
|
page_author = roam.models.Keyword.objects.get(keyword="AUTHOR", path=the_feed.file).value
|
|
except roam.models.Keyword.DoesNotExist:
|
|
logger.warn(f"Feed {key} does not have an AUTHOR!")
|
|
page_author = "Arcology User"
|
|
|
|
page_url = the_feed.file.page_set.first().to_url()
|
|
updated_at = arrow.get(entries[0].pubdate).format(arrow.FORMAT_RFC3339) # entries is already sorted
|
|
|
|
# node-id -> URL
|
|
links = the_feed.file.page_set.first().collect_links()
|
|
# node-id -> HTML
|
|
html_map = {
|
|
entry.heading.node_id: entry.to_html(links=links) for entry in entries
|
|
}
|
|
# node-id -> PUBDATE heading property
|
|
pubdate_map = {
|
|
entry.heading.node_id: arrow.get(entry.pubdate).format(arrow.FORMAT_RFC3339) for entry in entries
|
|
}
|
|
|
|
# return HttpResponse("",content_type="application/atom+xml")
|
|
return render(request, "arcology/feed.xml", dict(
|
|
title=the_feed.title,
|
|
page_url=page_url,
|
|
author=page_author,
|
|
updated_at=updated_at,
|
|
|
|
feed_entries=entries,
|
|
htmls=html_map,
|
|
pubdates=pubdate_map,
|
|
links=links,
|
|
), content_type="application/atom+xml")
|
|
# Atom Feed Handler:1 ends here
|
|
|
|
# [[file:../arcology.org::*move this function to somewhere else more reasonable][move this function to somewhere else more reasonable:1]]
|
|
from django.template.defaulttags import register
|
|
|
|
@register.filter
|
|
def get_item(dictionary, key):
|
|
return dictionary.get(key)
|
|
# move this function to somewhere else more reasonable:1 ends here
|
|
|
|
# [[file:../arcology.org::*404 unpublished/not found endpoint][404 unpublished/not found endpoint:1]]
|
|
def unpublished(request):
|
|
key = request.GET.get("key")
|
|
if key is None:
|
|
key = "NOT_SUPPLIED"
|
|
|
|
# query links etc to create a JSON doc for SigmaJS
|
|
template = loader.get_template("404.html")
|
|
context = dict(
|
|
missing_key=key
|
|
)
|
|
return HttpResponseNotFound(
|
|
template.render(context, request)
|
|
)
|
|
# 404 unpublished/not found endpoint:1 ends here
|
|
|
|
# [[file:../arcology.org::*=GET /robots.txt= Endpoint][=GET /robots.txt= Endpoint:1]]
|
|
def robots(request):
|
|
site = Site.from_request(request)
|
|
public_pages = Page.objects \
|
|
.filter(allow_crawl=True)
|
|
if site.key != "localhost":
|
|
public_pages = public_pages \
|
|
.filter(site=site)
|
|
|
|
public_pages = public_pages.all()
|
|
return render(request, "arcology/robots.txt", dict(
|
|
disallow_all_agents=["GPTBot", "ChatGPT-User", "Google-Extended", "CCBot", "anthropic-ai"],
|
|
pages=public_pages,
|
|
), content_type="text/plain")
|
|
# =GET /robots.txt= Endpoint:1 ends here
|
|
|
|
# [[file:../arcology.org::*=GET /feeds.json= Feed discovery endpoint][=GET /feeds.json= Feed discovery endpoint:1]]
|
|
import json
|
|
def feed_list(request):
|
|
site = Site.from_request(request)
|
|
feeds = Feed.objects.all()
|
|
ret = [
|
|
dict(
|
|
key=feed.route_key,
|
|
url=feed.site.urlize_feed(feed),
|
|
title=feed.title,
|
|
site=feed.site.key,
|
|
visibility=feed.visibility,
|
|
)
|
|
for feed in feeds
|
|
]
|
|
|
|
return HttpResponse(json.dumps(ret), content_type="application/json")
|
|
# =GET /feeds.json= Feed discovery endpoint:1 ends here
|
|
|
|
# [[file:../arcology.org::*=GET /sites.css= Per-Site link color dynamic CSS endpoint][=GET /sites.css= Per-Site link color dynamic CSS endpoint:1]]
|
|
def site_css(request):
|
|
sites = Site.objects.all()
|
|
stanzas = []
|
|
for site in sites:
|
|
for domain in site.sitedomain_set.all():
|
|
stanzas.append(f'''
|
|
a[href*="//{domain.domain}"] {{
|
|
border-radius: 0.25em;
|
|
padding: 0.1em;
|
|
background-color: {site.link_color}66;
|
|
}}
|
|
a[href*="//{domain.domain}"]:hover {{
|
|
background-color: {site.link_color}FF !important;
|
|
}}
|
|
''')
|
|
stanzas.append(f'''
|
|
a[href*="/404"] {{
|
|
color: var(--alert);
|
|
/* text-decoration: line-through; */
|
|
}}
|
|
a[href*="/404"]::after {{
|
|
content: " ⚠";
|
|
}}
|
|
a[href*="/404"]::before {{
|
|
content: "⚠ ";
|
|
}}
|
|
''')
|
|
return HttpResponse(stanzas, content_type="text/css")
|
|
# =GET /sites.css= Per-Site link color dynamic CSS endpoint:1 ends here
|