forked from Hostea/dashboard
feat: bootstrap authentication pages
parent
60acc3f202
commit
344ffaa4b8
|
@ -1,2 +1,3 @@
|
|||
export DATABASE_URL=""
|
||||
export db=""
|
||||
export OIDC_RSA_PRIVATE_KEY=""
|
||||
|
|
|
@ -151,3 +151,4 @@ cython_debug/
|
|||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
keys
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'accounts'
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
|
@ -0,0 +1,29 @@
|
|||
{% extends "common/components/base.html" %}
|
||||
{% block title %}{% block title_name %} {% endblock %} | Hostea Dashbaord{% endblock %}
|
||||
{% block nav %} {% include "common/components/nav/pub.html" %} {% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<main class="auth__main">
|
||||
<section class="main">
|
||||
<div class="title">
|
||||
<h1>Free Forge Ecosystem for Free Developers</h1>
|
||||
<p class="welcome">
|
||||
Hostea is a self-hostable libre software development suite comprising Gitea, Woodpecker CI, Librepages and GitPad with payments integration.
|
||||
</p>
|
||||
<ul class="index-banner__features-list">
|
||||
<li class="index-banner__features">Fully managed</li>
|
||||
<li class="index-banner__features">100% Free Software</li>
|
||||
<li class="index-banner__features">Fully Self-Hostable</li>
|
||||
<li class="index-banner__features">Observable and reliable</li>
|
||||
<li class="index-banner__features">Federation when available</li>
|
||||
<li class="index-banner__features">Radically transparent</li>
|
||||
<li class="index-banner__features">Horizontal community</li>
|
||||
<li class="index-banner__features">Run Hostea and become a service provider!</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<section class="login">
|
||||
{% block login %} {% endblock %}
|
||||
</section>
|
||||
</main>
|
||||
{% endblock %}
|
|
@ -0,0 +1,40 @@
|
|||
{% extends 'accounts/auth/base.html' %}
|
||||
{% block login %}
|
||||
<h2>Login</h2>
|
||||
<form action="{% url 'accounts.login' %}" method="POST" class="form" accept-charset="utf-8">
|
||||
{% include "common/components/error.html" %}
|
||||
<label class="form__label" for="login">
|
||||
Username or Email
|
||||
<input
|
||||
class="form__input"
|
||||
name="login"
|
||||
autofocus
|
||||
required
|
||||
id="login"
|
||||
type="text"
|
||||
{% if login %}
|
||||
value={{ login }}
|
||||
{% endif %}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form__label" for="password">
|
||||
Password
|
||||
<input
|
||||
class="form__input"
|
||||
name="password"
|
||||
required
|
||||
id="password"
|
||||
type="password"
|
||||
/>
|
||||
</label>
|
||||
<div class="form__action-container">
|
||||
<a href="/forgot-password">Forgot password?</a>
|
||||
<button class="form__submit" type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="form__alt-action">
|
||||
New to Hostea?
|
||||
<a href="{{ page.auth.register }}">Create an account</a>
|
||||
</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,74 @@
|
|||
{% extends 'authbase' %}
|
||||
{% block title_name %}Sign Up {% endblock %}
|
||||
{% block login %}
|
||||
<h2>Sign Up</h2>
|
||||
<form action="{{ page.auth.register }}" method="POST" class="form" accept-charset="utf-8">
|
||||
{% include "error_comp" %}
|
||||
<label class="form__label" for="username">
|
||||
Username
|
||||
<input
|
||||
class="form__input"
|
||||
autofocus
|
||||
name="username"
|
||||
required
|
||||
id="username"
|
||||
type="text"
|
||||
{% if payload.username %}
|
||||
value={{ payload.username }}
|
||||
{% endif %}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form__label" for="email">
|
||||
Email(optional)
|
||||
<input
|
||||
class="form__input"
|
||||
name="email"
|
||||
id="email"
|
||||
type="email"
|
||||
{% if payload.email %}
|
||||
value={{ payload.email }}
|
||||
{% endif %}
|
||||
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form__label" for="password">
|
||||
password
|
||||
<input
|
||||
class="form__input"
|
||||
name="password"
|
||||
required
|
||||
id="password"
|
||||
type="password"
|
||||
{% if payload.password %}
|
||||
value={{ payload.password }}
|
||||
{% endif %}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="form__label" for="confirm_password">
|
||||
Re-enter Password
|
||||
<input
|
||||
class="form__input"
|
||||
name="confirm_password"
|
||||
required
|
||||
id="confirm_password"
|
||||
type="password"
|
||||
{% if payload.confirm_password %}
|
||||
value={{ payload.confirm_password }}
|
||||
{% endif %}
|
||||
/>
|
||||
</label>
|
||||
<div class="form__action-container">
|
||||
<a href="/forgot-password">Forgot password?</a>
|
||||
<button class="form__submit" type="submit">Sign Up</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="form__alt-action">
|
||||
Already have an account?
|
||||
<a href="{{ page.auth.login }}"> Login </a>
|
||||
</p>
|
||||
{% include "demo_banner" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="{% url 'accounts.login' %}">Login</a></li>
|
||||
<li><a href="/o/applications/">New App</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="{% url 'accounts.login' %}">Login</a></li>
|
||||
<li><a href="/o/applications/">New App</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}" />
|
||||
<title>{% block title %} {% endblock %}</title>
|
||||
{% include "common/components/meta.html" %}
|
||||
</head>
|
||||
<body>
|
||||
<header>{% block nav %} {% endblock %}</header>
|
||||
<main>{% block main %} {% endblock %}</main>
|
||||
{% include "common/components/footer.html" %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
{% if error %}
|
||||
<div class="error_container">
|
||||
<h3 class="error-title">ERROR: {{ error.title }}</h3>
|
||||
<p class="error-message">{{ error.reason }}</p>
|
||||
</div>
|
||||
{% endif %}
|
|
@ -0,0 +1,42 @@
|
|||
<footer>
|
||||
<div class="footer__container">
|
||||
<div class="footer__column">
|
||||
<span class="license__conatiner">
|
||||
<a class="license__link" rel="noreferrer" href="/docs" target="_blank"
|
||||
>Docs</a
|
||||
>
|
||||
<span class="footer__column-divider--mobile-visible">|</span>
|
||||
<a
|
||||
class="license__link"
|
||||
rel="noreferrer"
|
||||
href="https://www.eff.org/issues/do-not-track/amp/"
|
||||
target="_blank"
|
||||
>No AMP</a
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="footer__column">
|
||||
<a
|
||||
href="/"
|
||||
class="footer__link"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="RSS"
|
||||
>Home</a>
|
||||
<div class="footer__column-divider">|</div>
|
||||
<a href="mailto:{{ footer.admin_email }}" class="footer__link"
|
||||
>Contact Instance Maintainer</a
|
||||
>
|
||||
<div class="footer__column-divider">|</div>
|
||||
<a
|
||||
class="footer__link"
|
||||
href="{{ footer.source_code }}"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="Source Code"
|
||||
>
|
||||
v{{ footer.version }}-{{ footer.git_hash }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
|
@ -0,0 +1,98 @@
|
|||
{% load static %}
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="{% static 'css/main.css' %}" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
media="screen and (max-width: 1300px)"
|
||||
href="{% static 'css/mobile.css' %}"
|
||||
/>
|
||||
|
||||
<meta name="referrer" content="no-referrer-when-downgrade" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<meta name="referrer" content="no-referrer-when-downgrade" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<meta name="description" content="{{ description }}" />
|
||||
<meta property="og:title" content="{{ title }}" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="{{ config.base_url }}" />
|
||||
|
||||
<meta property="og:description" content="{{ description }}" />
|
||||
<meta property="og:site_name" content="{{ title }}" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="57x57"
|
||||
href="{% static 'img/apple-icon-57x57.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="60x60"
|
||||
href="{% static 'img/apple-icon-60x60.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="72x72"
|
||||
href="{% static 'img/apple-icon-72x72.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="76x76"
|
||||
href="{% static 'img/apple-icon-76x76.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="114x114"
|
||||
href="{% static 'img/apple-icon-114x114.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="120x120"
|
||||
href="{% static 'img/apple-icon-120x120.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="144x144"
|
||||
href="{% static 'img/apple-icon-144x144.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="152x152"
|
||||
href="{% static 'img/apple-icon-152x152.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="{% static 'img/apple-icon-180x180.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="{% static 'img/android-icon-192x192.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="{% static 'img/favicon-32x32.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="96x96"
|
||||
href="{% static 'img/favicon-96x96.png' %}"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="{% static 'img/favicon-16x16.png' %}"
|
||||
/>
|
||||
<link rel="manifest" href="{% static 'img/manifest.json' %}" />
|
||||
<meta name="msapplication-TileColor" content="#ffffff" />
|
||||
<meta
|
||||
name="msapplication-TileImage"
|
||||
content="{% static 'img/ms-icon-144x144.png' %}"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
|
@ -0,0 +1,18 @@
|
|||
<nav class="nav__container">
|
||||
<input type="checkbox" class="nav__toggle" id="nav__toggle" />
|
||||
|
||||
<div class="nav__header">
|
||||
<a class="nav__logo-container" href="/">
|
||||
<p class="nav__home-btn">GitPad</p>
|
||||
</a>
|
||||
<label class="nav__hamburger-menu" for="nav__toggle">
|
||||
<span class="nav__hamburger-inner"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="nav__spacer"></div>
|
||||
|
||||
<div class="nav__link-group">
|
||||
{% block nav_links %} {% endblock %}
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,29 @@
|
|||
{% load static %}
|
||||
<nav class="nav__container">
|
||||
<input type="checkbox" class="nav__toggle" id="nav__toggle" />
|
||||
<div class="nav__header">
|
||||
<a class="nav__logo-container" href="/">
|
||||
<img src="{% static 'img/android-icon-48x48.png' %}"
|
||||
alt="Hostea temporary logo"/>
|
||||
<p class="nav__home-btn">
|
||||
ostea
|
||||
</p>
|
||||
</a>
|
||||
<label class="nav__hamburger-menu" for="nav__toggle">
|
||||
<span class="nav__hamburger-inner"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="nav__spacer"></div>
|
||||
|
||||
<div class="nav__link-group">
|
||||
<div class="nav__link-container">
|
||||
<a class="nav__link" rel="noreferrer" href="{% url 'accounts.login' %}">Login</a>
|
||||
</div>
|
||||
<div class="nav__link-container">
|
||||
<a class="nav__link" rel="noreferrer" href="page.auth.register">Register</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,11 @@
|
|||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
from .views import login_view, public_view, logout_view, protected_view
|
||||
|
||||
urlpatterns = [
|
||||
path('login/', login_view, name='accounts.login'),
|
||||
path('logout/', login_view, name='accounts.logout'),
|
||||
path('protected/', protected_view, name='accounts.protected'),
|
||||
path('', public_view, name='accounts.public'),
|
||||
]
|
|
@ -0,0 +1,63 @@
|
|||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.urls import reverse
|
||||
|
||||
GREETINGS = {
|
||||
"greeting": "Welcome to Hostea - Free Forge Ecosystem for Free Developers",
|
||||
"features": [
|
||||
"Fully managed",
|
||||
"100% Free Software",
|
||||
"Fully Self-Hostable",
|
||||
"Observable and reliable",
|
||||
"Federation when available",
|
||||
"Radically transparent",
|
||||
"Horizontal community",
|
||||
"Run Hostea and become a service provider!",
|
||||
],
|
||||
}
|
||||
|
||||
LOGIN_CONTENT = {
|
||||
"login_name": "Username or Email",
|
||||
"action": "Login",
|
||||
"password": "Password",
|
||||
"forgot_password": "Forgot password?",
|
||||
"register_prompt": "New to Hostea?",
|
||||
"register_action_link_text": "Create an account",
|
||||
"greetings": GREETINGS,
|
||||
}
|
||||
|
||||
@csrf_protect
|
||||
def login_view(request):
|
||||
if request.method == "POST":
|
||||
user = authenticate(request, username=request.POST["username"], password=request.POST["password"])
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
print("user logged in")
|
||||
if "next" in request.POST:
|
||||
next_url = request.POST["next"]
|
||||
if next_url:
|
||||
return redirect(next_url)
|
||||
return redirect(reverse('accounts.protected'))
|
||||
else:
|
||||
return HttpResponse("Login required")
|
||||
|
||||
ctx = LOGIN_CONTENT
|
||||
if "next" in request.GET:
|
||||
ctx["next"] = request.GET["next"]
|
||||
|
||||
return render(request, "accounts/auth/login.html", ctx)
|
||||
|
||||
@login_required
|
||||
def protected_view(request):
|
||||
return render(request, "accounts/protected.html")
|
||||
|
||||
@login_required
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
return redirect(reverse('accounts.login'))
|
||||
|
||||
def public_view(request):
|
||||
return render(request, "accounts/public.html")
|
|
@ -1,24 +1,38 @@
|
|||
asgiref==3.5.0
|
||||
astroid==2.9.3
|
||||
black==22.1.0
|
||||
certifi==2022.5.18.1
|
||||
cffi==1.15.0
|
||||
charset-normalizer==2.0.12
|
||||
click==8.0.4
|
||||
cryptography==37.0.2
|
||||
Deprecated==1.2.13
|
||||
Django==4.0.3
|
||||
django-environ==0.8.1
|
||||
django-oauth-toolkit==2.0.0
|
||||
djangorestframework==3.13.1
|
||||
greenlet==1.1.2
|
||||
idna==3.3
|
||||
isort==5.10.1
|
||||
jedi==0.18.1
|
||||
jwcrypto==1.3.1
|
||||
lazy-object-proxy==1.7.1
|
||||
mccabe==0.6.1
|
||||
msgpack==1.0.3
|
||||
mypy-extensions==0.4.3
|
||||
oauthlib==3.2.0
|
||||
parso==0.8.3
|
||||
pathspec==0.9.0
|
||||
platformdirs==2.5.1
|
||||
psycopg2==2.9.3
|
||||
pycparser==2.21
|
||||
pylint==2.12.2
|
||||
pynvim==0.4.3
|
||||
pytz==2022.1
|
||||
requests==2.27.1
|
||||
sqlparse==0.4.2
|
||||
tblib==1.7.0
|
||||
toml==0.10.2
|
||||
tomli==2.0.1
|
||||
urllib3==1.26.9
|
||||
wrapt==1.13.3
|
||||
|
|
Loading…
Reference in New Issue