UI Improvements (#7)
* Initial version with UI templates * Implement basic screens * Linting & bump to 0.3.0 * Tick off some TODOs
This commit is contained in:
committed by
GitHub
parent
597d9cdf7d
commit
0d61861343
19
custom_components/auth_oidc/views/base.html
Normal file
19
custom_components/auth_oidc/views/base.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-full min-h-full max-h-full">
|
||||
|
||||
<head>
|
||||
{% block head %}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-200 flex items-center justify-center h-full">
|
||||
<div class="bg-white p-6 rounded-lg shadow-lg max-w-md">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
16
custom_components/auth_oidc/views/error.html
Normal file
16
custom_components/auth_oidc/views/error.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Oops!{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="text-center">
|
||||
<h1 class="text-2xl font-bold mb-4">Login failed.</h1>
|
||||
<p class="mb-4">{{ error }}</p>
|
||||
<div class="my-6">
|
||||
<a href='{{ link }}'
|
||||
class="w-full py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">Try
|
||||
again</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
27
custom_components/auth_oidc/views/finish.html
Normal file
27
custom_components/auth_oidc/views/finish.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Logged in!{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="text-center">
|
||||
<div class="my-6">
|
||||
<h2 class="text-xl font-semibold mb-6 text-gray-800">I want to login to this browser</h2>
|
||||
<a href='{{ link }}'
|
||||
class="w-full py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">Click
|
||||
here to login automatically</a>
|
||||
</div>
|
||||
|
||||
<hr class="my-12">
|
||||
|
||||
<div class="my-6">
|
||||
<h2 class="text-xl font-semibold mb-4 text-gray-800">I am on a mobile device</h2>
|
||||
<p class="mb-4">Your one-time code is: <b class="text-blue-600 text-xl">{{ code }}</b></p>
|
||||
<p class="mb-4 text-sm">You have 5 minutes to use this code on any device.<br />The code can only
|
||||
be used once.</p>
|
||||
<p class="mb-4 text-sm">Please type the code into your app manually. If you don't see a code input, select
|
||||
'Login with
|
||||
OpenID Connect (SSO)' first.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
60
custom_components/auth_oidc/views/loader.py
Normal file
60
custom_components/auth_oidc/views/loader.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Jinja2 Async Environment"""
|
||||
|
||||
import logging
|
||||
from os import path
|
||||
from typing import Dict, Any
|
||||
from jinja2 import Environment, DictLoader
|
||||
from aiofiles.os import scandir as async_scandir
|
||||
from aiofiles import open as async_open
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
templates: Dict[str, str] = {}
|
||||
|
||||
|
||||
class AsyncTemplateRenderer:
|
||||
"""An asynchronous template renderer that caches rendered templates."""
|
||||
|
||||
def __init__(self, template_dir: str = None):
|
||||
self.template_dir = template_dir or path.dirname(path.abspath(__file__))
|
||||
|
||||
async def fetch_templates(self) -> None:
|
||||
"""Fetches all HTML files from the template directory."""
|
||||
templates.clear()
|
||||
|
||||
files = await async_scandir(self.template_dir)
|
||||
|
||||
for file in files:
|
||||
if file.is_dir():
|
||||
continue
|
||||
|
||||
filename = file.name
|
||||
if filename.endswith(".html"):
|
||||
template_path = path.join(self.template_dir, filename)
|
||||
try:
|
||||
_LOGGER.debug("Fetching template %s from disk", filename)
|
||||
async with async_open(
|
||||
template_path, mode="r", encoding="utf-8"
|
||||
) as f:
|
||||
content = await f.read()
|
||||
templates[filename] = content
|
||||
except (OSError, IOError) as e:
|
||||
_LOGGER.warning("Error reading template file %s: %s", filename, e)
|
||||
|
||||
async def render_template(self, template_name: str, **kwargs: Any) -> str:
|
||||
"""Renders a template with the given parameters."""
|
||||
|
||||
if not templates:
|
||||
await (
|
||||
self.fetch_templates()
|
||||
) # If the templates haven't been fetched, fetch them
|
||||
|
||||
if template_name not in templates:
|
||||
raise ValueError(f"Template '{template_name}' not found.")
|
||||
|
||||
env = Environment(loader=DictLoader(templates), enable_async=True)
|
||||
template = env.get_template(template_name)
|
||||
|
||||
# Render template
|
||||
rendered_output = await template.render_async(**kwargs)
|
||||
return rendered_output
|
||||
29
custom_components/auth_oidc/views/welcome.html
Normal file
29
custom_components/auth_oidc/views/welcome.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}OIDC Login{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="text-center">
|
||||
<h1 class="text-2xl font-bold mb-4">Home Assistant</h1>
|
||||
<p class="mb-4">You have been invited to login to Home Assistant.<br />Start the login process below.</p>
|
||||
|
||||
<button id="oidc-login-btn"
|
||||
class="w-full py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">
|
||||
Login with OpenID Connect (SSO)
|
||||
</button>
|
||||
|
||||
|
||||
<p class="mt-6 text-sm">After login, you will be granted a one-time code to login to any device. You may complete
|
||||
this login on your desktop or any mobile browser and then use the token for any desktop or the Home Assistant
|
||||
app.</p>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById('oidc-login-btn').addEventListener('click', function () {
|
||||
this.innerHTML = 'Redirecting...';
|
||||
this.disabled = true;
|
||||
this.classList.add('bg-gray-500');
|
||||
window.location.href = '/auth/oidc/redirect';
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user