summaryrefslogtreecommitdiffstats
path: root/authentication
diff options
context:
space:
mode:
Diffstat (limited to 'authentication')
-rw-r--r--authentication/endpoints.py10
-rw-r--r--authentication/templates/base.html18
-rw-r--r--authentication/templates/login.html21
-rw-r--r--authentication/templates/register.html33
4 files changed, 48 insertions, 34 deletions
diff --git a/authentication/endpoints.py b/authentication/endpoints.py
index 6877383..3af49fc 100644
--- a/authentication/endpoints.py
+++ b/authentication/endpoints.py
@@ -6,6 +6,7 @@ from uuid import uuid7
6 6
7from fastapi import FastAPI, Request, Form, Query 7from fastapi import FastAPI, Request, Form, Query
8from pydantic import BaseModel, ValidationError, model_validator, EmailStr 8from pydantic import BaseModel, ValidationError, model_validator, EmailStr
9from sqlalchemy import func
9from sqlmodel import select 10from sqlmodel import select
10from starlette.authentication import requires 11from starlette.authentication import requires
11from starlette.middleware.authentication import AuthenticationMiddleware 12from starlette.middleware.authentication import AuthenticationMiddleware
@@ -106,10 +107,17 @@ class RegisterForm(BaseModel):
106 return self 107 return self
107 108
108 109
110def is_first_run() -> bool:
111 with get_session_context() as session:
112 user_count = session.exec(select(func.count(User.id)))
113 return user_count.first() == 0
114
115
109async def base_register(request: Request, form: RegisterForm | None = None, errors: dict[str, str] | None = None): 116async def base_register(request: Request, form: RegisterForm | None = None, errors: dict[str, str] | None = None):
110 return templates.TemplateResponse(request, 'register.html', context={ 117 return templates.TemplateResponse(request, 'register.html', context={
111 'form': form, 118 'form': form,
112 'errors': errors, 119 'errors': errors,
120 'is_first_run': is_first_run(),
113 }) 121 })
114 122
115 123
@@ -145,7 +153,7 @@ async def post_register(request: Request, email: Annotated[str, Form()] = '', pa
145 return RedirectResponse(next, status_code=303) 153 return RedirectResponse(next, status_code=303)
146 154
147 except ValidationError as exc: 155 except ValidationError as exc:
148 errors.update({e['loc'][0]: e['msg'] for e in exc.errors()}) 156 errors.update({(e['loc'][0] if e['loc'] else 'verify_password'): e['msg'] for e in exc.errors()})
149 157
150 return await base_register(request, form, errors) 158 return await base_register(request, form, errors)
151 159
diff --git a/authentication/templates/base.html b/authentication/templates/base.html
index 9874313..ae6cb64 100644
--- a/authentication/templates/base.html
+++ b/authentication/templates/base.html
@@ -1,4 +1,20 @@
1{% extends 'global.html' %} 1{% extends 'global.html' %}
2{% import 'macros.html' as ui %}
2{% block template %} 3{% block template %}
3 {% block content %}{% endblock %} 4<div class="auth-page">
5 <div class="auth-shell">
6 <div class="auth-hero">
7 <div class="auth-hero__brand">{{ ui.connector_icon(20) }} ttun</div>
8 <div class="auth-hero__diagram">
9 <span class="mono">local:3000</span>
10 <span class="connector">{{ ui.connector_icon(20) }}</span>
11 <span class="mono">your-app.ttun.dev</span>
12 </div>
13 <p class="auth-hero__tagline">Self-hosted tunnel proxy. Expose a local port through a public URL you control.</p>
14 </div>
15 <div class="auth-form">
16 {% block content %}{% endblock %}
17 </div>
18 </div>
19</div>
4{% endblock %} 20{% endblock %}
diff --git a/authentication/templates/login.html b/authentication/templates/login.html
index 2db2512..dcc0efd 100644
--- a/authentication/templates/login.html
+++ b/authentication/templates/login.html
@@ -1,18 +1,13 @@
1{% extends "./base.html" %} 1{% extends "./base.html" %}
2{% import 'macros.html' as ui %}
2 3
3{% block content %} 4{% block content %}
5 <h1>Sign in</h1>
6 <p class="auth-form__intro">Enter your credentials to manage this server.</p>
4 <form method="post"> 7 <form method="post">
5 8 {{ ui.field('username', label='Username', value=form.username if form else '', error=errors.username if errors else '') }}
6 <input type="text" name="username" {% if form %}value="{{ form.username }}"{% endif %} placeholder="username" /> 9 {{ ui.field('password', type='password', label='Password', error=errors.password if errors else '') }}
7 {% if errors and errors.username %} 10 {{ ui.button('Sign in') }}
8 {{ errors.username }}
9 {% endif %}
10
11
12 <input type="password" name="password" />
13 {% if errors and errors.password%}
14 {{ errors.password }}
15 {% endif %}
16 <button type="submit">Login</button>
17 </form> 11 </form>
18{% endblock %} 12 <p class="auth-switch">New here? <a href="/auth/register/">Create an account</a></p>
13{% endblock %}
diff --git a/authentication/templates/register.html b/authentication/templates/register.html
index 576db78..a1959eb 100644
--- a/authentication/templates/register.html
+++ b/authentication/templates/register.html
@@ -1,24 +1,19 @@
1{% extends "./base.html" %} 1{% extends "./base.html" %}
2{% import 'macros.html' as ui %}
2 3
3{% block content %} 4{% block content %}
5 {% if is_first_run %}
6 <h1>Set up your ttun server</h1>
7 <p class="auth-form__intro">This will be the first account, with full access to manage this server.</p>
8 {% else %}
9 <h1>Create your account</h1>
10 <p class="auth-form__intro">This account will be able to sign in and manage this server.</p>
11 {% endif %}
4 <form method="post"> 12 <form method="post">
5 13 {{ ui.field('email', type='email', label='Email', value=form.email if form else '', error=errors.email if errors else '') }}
6 <input type="email" name="email" {% if form %}value="{{ form.email }}"{% endif %} placeholder="email" /> 14 {{ ui.field('password', type='password', label='Password', error=errors.password if errors else '') }}
7 {% if errors and errors.email%} 15 {{ ui.field('verify_password', type='password', label='Confirm password', error=errors.verify_password if errors else '') }}
8 {{ errors.email }} 16 {{ ui.button('Create account') }}
9 {% endif %}
10
11
12 <input type="password" name="password" />
13 {% if errors and errors.password%}
14 {{ errors.password }}
15 {% endif %}
16
17 <input type="password" name="verify_password" />
18 {% if errors and errors.password%}
19 {{ errors.verify_password }}
20 {% endif %}
21
22 <button type="submit">Register</button>
23 </form> 17 </form>
24{% endblock %} 18 <p class="auth-switch">Already have an account? <a href="/auth/login/">Sign in</a></p>
19{% endblock %}