feat: bootstrap authentication pages
ci/woodpecker/push/woodpecker Pipeline failed Details

wip-payments
Aravinth Manivannan 2022-06-10 17:22:54 +05:30
parent 60acc3f202
commit 344ffaa4b8
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
22 changed files with 490 additions and 0 deletions

View File

@ -1,2 +1,3 @@
export DATABASE_URL=""
export db=""
export OIDC_RSA_PRIVATE_KEY=""

1
.gitignore vendored
View File

@ -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
accounts/__init__.py Normal file
View File

3
accounts/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
accounts/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'

View File

3
accounts/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 %}

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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>

3
accounts/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

11
accounts/urls.py Normal file
View File

@ -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'),
]

63
accounts/views.py Normal file
View File

@ -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")

View File

@ -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