add SEO, update module
This commit is contained in:
parent
ef20108403
commit
892fb25d06
3 changed files with 78 additions and 47 deletions
55
fragify.py
55
fragify.py
|
|
@ -11,6 +11,8 @@ import os
|
||||||
import sys
|
import sys
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
SITE_BASE_URL = os.environ.get('FRAGIFY_BASE_URL', 'http://localhost:8000')
|
||||||
|
|
||||||
class BaseTemplateResource:
|
class BaseTemplateResource:
|
||||||
"""Base class for resources that need template rendering"""
|
"""Base class for resources that need template rendering"""
|
||||||
|
|
||||||
|
|
@ -55,16 +57,6 @@ class BaseTemplateResource:
|
||||||
# Fallback to current directory
|
# Fallback to current directory
|
||||||
return dev_template_dir
|
return dev_template_dir
|
||||||
|
|
||||||
class StaticResource:
|
|
||||||
def __init__(self, directory: str):
|
|
||||||
self._directory = directory
|
|
||||||
self._static = falcon.asgi.StaticRoute(directory)
|
|
||||||
|
|
||||||
def on_get(self, req, resp, path):
|
|
||||||
# Serve static files via falcon's static route helper
|
|
||||||
static_app = falcon.asgi.StaticRoute(self._directory)
|
|
||||||
return static_app(req, resp)
|
|
||||||
|
|
||||||
class FragifyApp(BaseTemplateResource):
|
class FragifyApp(BaseTemplateResource):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.fragdenstaat_api = "https://fragdenstaat.de/api/v1"
|
self.fragdenstaat_api = "https://fragdenstaat.de/api/v1"
|
||||||
|
|
@ -77,7 +69,11 @@ class FragifyApp(BaseTemplateResource):
|
||||||
"""Serve the main page"""
|
"""Serve the main page"""
|
||||||
template = self.jinja_env.get_template('index.html')
|
template = self.jinja_env.get_template('index.html')
|
||||||
resp.content_type = 'text/html; charset=utf-8'
|
resp.content_type = 'text/html; charset=utf-8'
|
||||||
resp.text = template.render()
|
resp.text = template.render(
|
||||||
|
meta_title='Fragify – Anfragelinks für FragDenStaat',
|
||||||
|
meta_description='Erstelle vorausgefüllte Anfragelinks für FragDenStaat.de, suche Behörden, füge Betreff und Text hinzu und teile den Link.',
|
||||||
|
canonical_url=f"{SITE_BASE_URL}/"
|
||||||
|
)
|
||||||
|
|
||||||
def on_post(self, req, resp):
|
def on_post(self, req, resp):
|
||||||
"""Handle form submission and generate link"""
|
"""Handle form submission and generate link"""
|
||||||
|
|
@ -124,7 +120,12 @@ class ImpressumResource(BaseTemplateResource):
|
||||||
"""Serve the Impressum page"""
|
"""Serve the Impressum page"""
|
||||||
template = self.jinja_env.get_template('impressum.html')
|
template = self.jinja_env.get_template('impressum.html')
|
||||||
resp.content_type = 'text/html; charset=utf-8'
|
resp.content_type = 'text/html; charset=utf-8'
|
||||||
resp.text = template.render()
|
resp.text = template.render(
|
||||||
|
meta_title='Impressum – Fragify',
|
||||||
|
meta_description='Impressum für Fragify.',
|
||||||
|
canonical_url=f"{SITE_BASE_URL}/impressum",
|
||||||
|
noindex=True
|
||||||
|
)
|
||||||
|
|
||||||
class DatenschutzResource(BaseTemplateResource):
|
class DatenschutzResource(BaseTemplateResource):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -135,7 +136,12 @@ class DatenschutzResource(BaseTemplateResource):
|
||||||
"""Serve the Datenschutz page"""
|
"""Serve the Datenschutz page"""
|
||||||
template = self.jinja_env.get_template('datenschutz.html')
|
template = self.jinja_env.get_template('datenschutz.html')
|
||||||
resp.content_type = 'text/html; charset=utf-8'
|
resp.content_type = 'text/html; charset=utf-8'
|
||||||
resp.text = template.render()
|
resp.text = template.render(
|
||||||
|
meta_title='Datenschutz – Fragify',
|
||||||
|
meta_description='Datenschutzerklärung für Fragify. Keine Cookies, es werden nur Anfragen an die FragDenStaat-API gestellt.',
|
||||||
|
canonical_url=f"{SITE_BASE_URL}/datenschutz",
|
||||||
|
noindex=True
|
||||||
|
)
|
||||||
|
|
||||||
class PublicBodiesResource:
|
class PublicBodiesResource:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -175,6 +181,25 @@ class PublicBodiesResource:
|
||||||
'next': None
|
'next': None
|
||||||
})
|
})
|
||||||
|
|
||||||
|
class RobotsResource:
|
||||||
|
def on_get(self, req, resp):
|
||||||
|
resp.content_type = 'text/plain; charset=utf-8'
|
||||||
|
resp.text = f"""User-agent: *
|
||||||
|
Allow: /
|
||||||
|
Sitemap: {SITE_BASE_URL}/sitemap.xml
|
||||||
|
"""
|
||||||
|
|
||||||
|
class SitemapResource:
|
||||||
|
def on_get(self, req, resp):
|
||||||
|
resp.content_type = 'application/xml; charset=utf-8'
|
||||||
|
resp.text = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url><loc>{SITE_BASE_URL}/</loc></url>
|
||||||
|
<url><loc>{SITE_BASE_URL}/impressum</loc></url>
|
||||||
|
<url><loc>{SITE_BASE_URL}/datenschutz</loc></url>
|
||||||
|
</urlset>
|
||||||
|
"""
|
||||||
|
|
||||||
# Create Falcon application
|
# Create Falcon application
|
||||||
app = falcon.App()
|
app = falcon.App()
|
||||||
|
|
||||||
|
|
@ -200,11 +225,15 @@ fragify = FragifyApp()
|
||||||
impressum = ImpressumResource()
|
impressum = ImpressumResource()
|
||||||
datenschutz = DatenschutzResource()
|
datenschutz = DatenschutzResource()
|
||||||
publicbodies = PublicBodiesResource()
|
publicbodies = PublicBodiesResource()
|
||||||
|
robots = RobotsResource()
|
||||||
|
sitemap = SitemapResource()
|
||||||
|
|
||||||
app.add_route('/', fragify)
|
app.add_route('/', fragify)
|
||||||
app.add_route('/impressum', impressum)
|
app.add_route('/impressum', impressum)
|
||||||
app.add_route('/datenschutz', datenschutz)
|
app.add_route('/datenschutz', datenschutz)
|
||||||
app.add_route('/api/publicbodies', publicbodies)
|
app.add_route('/api/publicbodies', publicbodies)
|
||||||
|
app.add_route('/robots.txt', robots)
|
||||||
|
app.add_route('/sitemap.xml', sitemap)
|
||||||
|
|
||||||
# Static file route
|
# Static file route
|
||||||
if STATIC_DIR and os.path.isdir(STATIC_DIR):
|
if STATIC_DIR and os.path.isdir(STATIC_DIR):
|
||||||
|
|
|
||||||
59
module.nix
59
module.nix
|
|
@ -14,47 +14,48 @@ in
|
||||||
options = {
|
options = {
|
||||||
services.fragify = {
|
services.fragify = {
|
||||||
|
|
||||||
enable = lib.mkOption {
|
enable = lib.mkEnableOption "Fragify web app";
|
||||||
type = lib.types.bool;
|
|
||||||
default = false;
|
|
||||||
description = ''
|
|
||||||
Enable fragify web application.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
# uWSGI application definition for Fragify
|
services.uwsgi = {
|
||||||
services.uwsgi.enable = true;
|
enable = true;
|
||||||
services.uwsgi.user = "fragify";
|
user = "fragify";
|
||||||
services.uwsgi.group = "fragify";
|
group = "fragify";
|
||||||
services.uwsgi.plugins = [ "python3" ];
|
plugins = [ "python3" ];
|
||||||
services.uwsgi.instance."fragify" = {
|
instances.fragify = {
|
||||||
type = "normal";
|
# Align with upstream module: put uwsgi options under settings
|
||||||
chdir = "/";
|
settings = {
|
||||||
# Load WSGI by file path from the packaged share dir
|
"chdir" = "/";
|
||||||
wsgi-file = "${pkgs.fragify}/share/fragify/fragify_wsgi.py";
|
"wsgi-file" = "${pkgs.fragify}/share/fragify/fragify_wsgi.py";
|
||||||
module = "fragify:app";
|
module = "fragify:app";
|
||||||
pythonPackages = p: with p; [ falcon requests jinja2 ];
|
# Socket
|
||||||
env = {
|
"socket" = "unix:${config.services.uwsgi.runDir}/fragify.sock";
|
||||||
FRAGIFY_TEMPLATES_DIR = "${pkgs.fragify}/share/fragify/templates";
|
"chmod-socket" = "660";
|
||||||
FRAGIFY_STATIC_DIR = "${pkgs.fragify}/share/fragify/assets";
|
|
||||||
};
|
|
||||||
socket = "unix:${config.services.uwsgi.runDir}/fragify.sock";
|
|
||||||
chmod-socket = "660";
|
|
||||||
umask = "0077";
|
umask = "0077";
|
||||||
vacuum = true;
|
vacuum = true;
|
||||||
master = true;
|
master = true;
|
||||||
processes = 2;
|
processes = 2;
|
||||||
threads = 2;
|
threads = 2;
|
||||||
harakiri = 60;
|
"harakiri" = 60;
|
||||||
buffer-size = 65535;
|
"buffer-size" = 65535;
|
||||||
# Security hardening
|
"need-app" = true;
|
||||||
need-app = true;
|
"no-orphans" = true;
|
||||||
no-orphans = true;
|
# Serve static files directly via uWSGI (optional)
|
||||||
|
# Map /static to packaged assets directory (if present)
|
||||||
|
"static-map" = "/static=${pkgs.fragify}/share/fragify/assets";
|
||||||
|
};
|
||||||
|
# Environment for the WSGI app
|
||||||
|
env = {
|
||||||
|
FRAGIFY_TEMPLATES_DIR = "${pkgs.fragify}/share/fragify/templates";
|
||||||
|
FRAGIFY_STATIC_DIR = "${pkgs.fragify}/share/fragify/assets";
|
||||||
|
};
|
||||||
|
# Python packages for uWSGI
|
||||||
|
pythonPackages = p: with p; [ falcon requests jinja2 ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Ensure fragify user and group exist
|
# Ensure fragify user and group exist
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
<meta name="twitter:title" content="{{ meta_title | default('Fragify') }}">
|
<meta name="twitter:title" content="{{ meta_title | default('Fragify') }}">
|
||||||
<meta name="twitter:description" content="{{ meta_description | default('Erstelle vorausgefüllte FragDenStaat.de-Anfragelinks und teile sie mit Freund:innen.') }}">
|
<meta name="twitter:description" content="{{ meta_description | default('Erstelle vorausgefüllte FragDenStaat.de-Anfragelinks und teile sie mit Freund:innen.') }}">
|
||||||
|
|
||||||
|
{% if noindex %}<meta name="robots" content="noindex,follow">{% endif %}
|
||||||
<meta name="theme-color" content="#667eea">
|
<meta name="theme-color" content="#667eea">
|
||||||
<link rel="icon" href="/static/favicon.ico">
|
<link rel="icon" href="/static/favicon.ico">
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue