feat: Intervals.icu MCP portal (FastAPI + Authentik OIDC)
- OIDC login via Authentik; first login creates a disabled user (admin approval). - Users set/replace their Intervals.icu athlete ID + API key; the key is validated against Intervals.icu and stored AES-256-GCM encrypted (shared key with the MCP server). Same users table (schema owned by the MCP server's migrations). - Admin page (gated on the intervalsicu-mcp-admins group claim): list, approve, disable, delete users. - 29 tests @ 93% (OIDC routes integration-only); Dockerfile asserts templates are packaged; .gitea CI test-gates the image build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Intervals.icu MCP portal</title>
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
body { font-family: system-ui, sans-serif; max-width: 760px; margin: 0 auto; padding: 1rem; line-height: 1.5; }
|
||||
header { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
|
||||
border-bottom: 1px solid #8884; padding-bottom: .5rem; margin-bottom: 1.5rem; flex-wrap: wrap; }
|
||||
nav a { margin-left: 1rem; }
|
||||
a { color: #3b82f6; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.banner { padding: .6rem .9rem; border-radius: 8px; margin: 1rem 0; }
|
||||
.ok { background: #16a34a22; border: 1px solid #16a34a88; }
|
||||
.warn { background: #d9770622; border: 1px solid #d9770688; }
|
||||
.err { background: #dc262622; border: 1px solid #dc262688; }
|
||||
label { display: block; margin-top: 1rem; font-weight: 600; }
|
||||
input[type=text], input[type=password] { width: 100%; padding: .5rem; margin-top: .25rem;
|
||||
border: 1px solid #8886; border-radius: 6px; box-sizing: border-box; }
|
||||
button { margin-top: 1rem; padding: .5rem 1rem; border-radius: 6px; border: 1px solid #8886;
|
||||
background: #3b82f6; color: #fff; cursor: pointer; }
|
||||
button.secondary { background: transparent; color: inherit; }
|
||||
table { border-collapse: collapse; width: 100%; margin-top: 1rem; font-size: .92rem; }
|
||||
th, td { text-align: left; padding: .4rem .5rem; border-bottom: 1px solid #8883; }
|
||||
code { background: #8882; padding: .1rem .35rem; border-radius: 4px; }
|
||||
.muted { color: #8889; }
|
||||
form.inline { display: inline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<strong>Intervals.icu MCP</strong>
|
||||
{% if user %}
|
||||
<nav>
|
||||
<a href="/account">My account</a>
|
||||
{% if user.is_admin %}<a href="/admin">Admin</a>{% endif %}
|
||||
<a href="/logout">Sign out</a>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</header>
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user