WIP: caddy: add support for error msg
This commit is contained in:
parent
004a033606
commit
48c4ecafae
6 changed files with 203 additions and 50 deletions
|
@ -31,17 +31,21 @@ caddy__routes_https:
|
|||
status: 301
|
||||
www3.test.auro.re:
|
||||
reverse:
|
||||
- "[2a09:6840:128::98]:3000"
|
||||
- 10.128.0.98:3000
|
||||
- "[2a09:6840:128::198]:3000"
|
||||
- 10.128.0.198:3000
|
||||
|
||||
caddy__errors:
|
||||
- root: /var/www
|
||||
- rewrite: /error.html
|
||||
- file_server: true
|
||||
templates: true
|
||||
|
||||
caddy__servers:
|
||||
https:
|
||||
listen: ":443"
|
||||
routes: "{{ caddy__routes_https }}"
|
||||
errors: "{{ caddy__errors }}"
|
||||
http:
|
||||
listen: ":80"
|
||||
|
||||
#caddy__error_handlers:
|
||||
#"*":
|
||||
# - error_template: "/var/www/error.html"
|
||||
...
|
||||
|
|
3
roles/caddy/defaults/main.yml
Normal file
3
roles/caddy/defaults/main.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
caddy__error_dir: /var/www/error
|
||||
...
|
|
@ -14,8 +14,8 @@ from pydantic import (
|
|||
T = TypeVar("T")
|
||||
|
||||
|
||||
def flatten(iterable: Iterable[Iterable[T]]) -> Iterable[T]:
|
||||
return itertools.chain.from_iterable(iterable)
|
||||
def flatten_list(iterable: Iterable[Iterable[T]]) -> list[T]:
|
||||
return list(itertools.chain.from_iterable(iterable))
|
||||
|
||||
|
||||
class AutoList(list[T], Generic[T]):
|
||||
|
@ -43,7 +43,8 @@ class FilesHandler(BaseHandler):
|
|||
root: str
|
||||
|
||||
def to_caddy(self):
|
||||
yield {"handle": [{"handler": "vars", "root": self.root}]}
|
||||
handler = {"handler": "vars", "root": self.root}
|
||||
yield {"handle": [handler]}
|
||||
|
||||
|
||||
class StaticHandler(BaseHandler):
|
||||
|
@ -51,35 +52,60 @@ class StaticHandler(BaseHandler):
|
|||
body: str | None = None
|
||||
|
||||
def to_caddy(self):
|
||||
response = {"handler": "static_response"}
|
||||
handler = {"handler": "static_response"}
|
||||
if self.status is not None:
|
||||
response["status_code"] = self.status
|
||||
handler["status_code"] = self.status
|
||||
if self.body is not None:
|
||||
response["body"] = self.body
|
||||
yield {"handle": [response]}
|
||||
handler["body"] = self.body
|
||||
yield {"handle": [handler]}
|
||||
|
||||
|
||||
class ReverseHandler(BaseHandler):
|
||||
reverse: AutoList[str]
|
||||
|
||||
def to_caddy(self):
|
||||
yield {
|
||||
"handle": [
|
||||
{
|
||||
handler = {
|
||||
"handler": "reverse_proxy",
|
||||
"upstreams": [{"dial": s} for s in self.reverse],
|
||||
}
|
||||
yield {"handle": [handler]}
|
||||
|
||||
|
||||
class RewriteHandler(BaseHandler):
|
||||
rewrite: str
|
||||
|
||||
def to_caddy(self):
|
||||
handler = {"handler": "rewrite", "uri": self.rewrite}
|
||||
yield {"handle": [handler]}
|
||||
|
||||
|
||||
class FileServerHandler(BaseHandler):
|
||||
file_server: Literal[True]
|
||||
templates: bool = False
|
||||
|
||||
def to_caddy(self):
|
||||
handlers = [
|
||||
{"handler": "templates"},
|
||||
]
|
||||
}
|
||||
if self.templates:
|
||||
handlers.append({"handler": "file_server"})
|
||||
yield {"handle": handlers}
|
||||
|
||||
|
||||
Handler = FilesHandler | ReverseHandler | StaticHandler
|
||||
Handler = (
|
||||
FilesHandler
|
||||
| ReverseHandler
|
||||
| RewriteHandler
|
||||
| FileServerHandler
|
||||
| StaticHandler
|
||||
)
|
||||
Routes = dict[str, AutoList[Handler]]
|
||||
|
||||
|
||||
class Server(BaseModel):
|
||||
listen: AutoList[str]
|
||||
routes: Routes = {}
|
||||
errors: AutoList[Handler] = {}
|
||||
|
||||
|
||||
Config = dict[str, Server]
|
||||
|
@ -96,23 +122,22 @@ class Context:
|
|||
|
||||
def strip_path_prefix(prefix: str) -> Any:
|
||||
return {
|
||||
"strip_path_prefix": prefix,
|
||||
"handler": "rewrite",
|
||||
"strip_path_prefix": prefix,
|
||||
}
|
||||
|
||||
|
||||
def handler_to_caddy(handler: Handler, ctx: Context) -> Any:
|
||||
def to_caddy_inner():
|
||||
if handler.headers:
|
||||
yield {
|
||||
"handle": [
|
||||
handlers = [
|
||||
{
|
||||
"handler": "headers",
|
||||
"response": {"set": {name: [value]}},
|
||||
}
|
||||
for name, value in handler.headers.items()
|
||||
]
|
||||
}
|
||||
yield {"handle": handlers}
|
||||
yield from handler.to_caddy()
|
||||
|
||||
if handler.path is None:
|
||||
|
@ -133,30 +158,29 @@ def handler_to_caddy(handler: Handler, ctx: Context) -> Any:
|
|||
}
|
||||
|
||||
|
||||
def route_to_caddy(host: str, handlers: list[Handler], ctx: Context) -> Any:
|
||||
return {
|
||||
"match": [{"host": [host]}],
|
||||
"handle": [
|
||||
{
|
||||
def route_to_caddy(
|
||||
host: str | None, handlers: list[Handler], ctx: Context
|
||||
) -> Any:
|
||||
handler = {
|
||||
"handler": "subroute",
|
||||
"routes": list(
|
||||
flatten(handler_to_caddy(h, ctx) for h in handlers)
|
||||
),
|
||||
}
|
||||
],
|
||||
"terminal": True,
|
||||
"routes": flatten_list(handler_to_caddy(h, ctx) for h in handlers),
|
||||
}
|
||||
route = {"handle": [handler], "terminal": True}
|
||||
if host is not None:
|
||||
route["match"] = [{"host": [host]}]
|
||||
return route
|
||||
|
||||
|
||||
def server_to_caddy(server: Server, ctx: Context) -> Any:
|
||||
return {
|
||||
"listen": server.listen,
|
||||
"errors": {}, # TODO
|
||||
"logs": {}, # TODO
|
||||
"routes": [
|
||||
routes = [
|
||||
route_to_caddy(host, handlers, ctx)
|
||||
for host, handlers in server.routes.items()
|
||||
],
|
||||
]
|
||||
return {
|
||||
"listen": server.listen,
|
||||
"errors": {"routes": [route_to_caddy(None, server.errors, ctx)]},
|
||||
"logs": {},
|
||||
"routes": routes,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
- name: Configure caddy
|
||||
copy:
|
||||
content: "{{ caddy__servers | caddy__of_servers | to_json }}"
|
||||
content: "{{ caddy__servers | caddy__of_servers | to_nice_json }}"
|
||||
dest: /etc/caddy/caddy.json
|
||||
owner: root
|
||||
group: root
|
||||
|
@ -31,6 +31,22 @@
|
|||
notify:
|
||||
- Reload caddy
|
||||
|
||||
- name: Create error directory
|
||||
file:
|
||||
path: "{{ caddy__error_dir }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: u=rwx=,g=rx,o=rx
|
||||
|
||||
- name: Configure error page
|
||||
template:
|
||||
src: error.html.j2
|
||||
dest: "{{ caddy__error_dir }}/error.html"
|
||||
owner: root
|
||||
group: root
|
||||
mode: u=rw,g=r,o=r
|
||||
|
||||
- name: Enable caddy
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{{ caddy__servers | caddy__of_servers | to_nice_json }}
|
107
roles/caddy/templates/error.html.j2
Normal file
107
roles/caddy/templates/error.html.j2
Normal file
|
@ -0,0 +1,107 @@
|
|||
<!doctype html>
|
||||
{{ ansible_managed | comment("xml") }}
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>{{ '{{placeholder "http.error.status_code"}}' }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<style>
|
||||
* {
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
display: grid;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 2em auto;
|
||||
place-self: center;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline dotted;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #555;
|
||||
font-size: 2em;
|
||||
font-weight: 400;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em auto;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 280px) {
|
||||
body, p {
|
||||
width: 95vw;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0 0 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
#switch {
|
||||
margin: 0 0 1em 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#switch a {
|
||||
padding: .5em;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("switch").hidden = false;
|
||||
});
|
||||
function translate(lang) {
|
||||
document.querySelectorAll('[lang]').forEach((element) => {
|
||||
element.hidden = element.getAttribute("lang") !== lang;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="switch" hidden>
|
||||
<a href="javascript:translate('en')">English</a>
|
||||
<a href="javascript:translate('fr')">Français</a>
|
||||
</nav>
|
||||
<h1 lang="fr" hidden>Erreur {{ '{{placeholder "http.error.status_code"}}' }}</h1>
|
||||
<h1 lang="en">Error {{ '{{placeholder "http.error.status_code"}}' }}</h1>
|
||||
{{ '{{$http_code := atoi (placeholder "http.error.status_code")}}' }}
|
||||
{{ '{{if eq $http_code 502 503}}' }}
|
||||
<p lang="fr" hidden>Whoops, le service est temporairement indisponible ou en maintenance…<br/>Essayez de rafraîchir la page plus tard.</p>
|
||||
<p lang="en">Whoops, the service is temporarily unavailable or under maintenance…<br/>Try refreshing the page later.</p>
|
||||
{{ '{{else if eq $http_code 504}}' }}
|
||||
<p lang="fr" hidden>Whoops, le service prend trop de temps à répondre…<br/>Essayez de rafraîchir la page plus tard.</p>
|
||||
<p lang="en">Whoops, the service takes too long to respond…<br/>Try refreshing the page later.</p>
|
||||
{{ '{{end}}' }}
|
||||
{{ '{{if and (lt $http_code 600) (gt $http_code 499)}}' }}
|
||||
<p lang="fr" hidden>Si le problème persiste, contactez <a href="mailto:tech.aurore@lists.crans.org">l’équipe technique d’Aurore</a>.</p>
|
||||
<p lang="en">If the problem persists, contact <a href="mailto:tech.aurore@lists.crans.org">the Aurore technical team</a>.</p>
|
||||
{{ '{{end}}' }}
|
||||
{{ '{{if eq $http_code 403}}' }}
|
||||
<p lang="fr" hidden>Whoops, cette page vous est interdite.</p>
|
||||
<p lang="en">Whoops, you are not allowed on this page.</p>
|
||||
{{ '{{else if eq $http_code 404}}' }}
|
||||
<p lang="fr" hidden>Whoops, cette page n’existe pas.</p>
|
||||
<p lang="en">Whoops, this page doesn't exist.</p>
|
||||
{{ '{{else if and (lt $http_code 499) (gt $http_code 399)}}' }}
|
||||
<p lang="fr" hidden>Whoops, requête malformée.</p>
|
||||
<p lang="en">Whoops, malformed request.</p>
|
||||
{{ '{{end}}' }}
|
||||
<p lang="fr" hidden>ID pour le support : <code>{{ '{{placeholder "http.error.id"}}' }}</code></p>
|
||||
<p lang="en">Support ID: <code>{{ '{{placeholder "http.error.id"}}' }}</code></p>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue