add htmx
This commit is contained in:
@@ -8,6 +8,8 @@ dependencies = [
|
||||
"bcrypt>=4.2.1",
|
||||
"cryptography>=44.0.0",
|
||||
"django>=5.1.4",
|
||||
"django-htmx>=1.21.0",
|
||||
"paho-mqtt>=2.1.0",
|
||||
"psycopg2-binary>=2.9.10",
|
||||
"python-dotenv>=1.0.1",
|
||||
]
|
||||
|
||||
65
static/js/htmx.min.js
vendored
Normal file
65
static/js/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
26
uv.lock
generated
26
uv.lock
generated
@@ -118,6 +118,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/58/0b/8a4ab2c02982df4ed41e29f28f189459a7eba37899438e6bea7f39db793b/Django-5.1.4-py3-none-any.whl", hash = "sha256:236e023f021f5ce7dee5779de7b286565fdea5f4ab86bae5338e3f7b69896cf0", size = 8276471 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-htmx"
|
||||
version = "1.21.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/5c/f6b7e9102a86c69b018142c5e3255b7f983b4f6545fe8e9bbb326e903be1/django_htmx-1.21.0.tar.gz", hash = "sha256:6ed3b42effd5980f22e68f36cd14ee4311bff3b6cb8435a89e27f45995691572", size = 9611 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/ec/0d68c022dac4a539cdd2cf8b2e23a034da9d6ef864e4bc324a5fe2b50c28/django_htmx-1.21.0-py3-none-any.whl", hash = "sha256:64bc31463017a80552b767bc216ee5700248fa72e7ccd2963495e69afbdb6abe", size = 6901 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-wallet"
|
||||
version = "0.1.0"
|
||||
@@ -126,6 +139,8 @@ dependencies = [
|
||||
{ name = "bcrypt" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "django" },
|
||||
{ name = "django-htmx" },
|
||||
{ name = "paho-mqtt" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "python-dotenv" },
|
||||
]
|
||||
@@ -135,10 +150,21 @@ requires-dist = [
|
||||
{ name = "bcrypt", specifier = ">=4.2.1" },
|
||||
{ name = "cryptography", specifier = ">=44.0.0" },
|
||||
{ name = "django", specifier = ">=5.1.4" },
|
||||
{ name = "django-htmx", specifier = ">=1.21.0" },
|
||||
{ name = "paho-mqtt", specifier = ">=2.1.0" },
|
||||
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paho-mqtt"
|
||||
version = "2.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/39/15/0a6214e76d4d32e7f663b109cf71fb22561c2be0f701d67f93950cd40542/paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834", size = 148848 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/cb/00451c3cf31790287768bb12c6bec834f5d292eaf3022afc88e14b8afc94/paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee", size = 67219 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2-binary"
|
||||
version = "2.9.10"
|
||||
|
||||
@@ -38,6 +38,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_htmx',
|
||||
'wallet_api.apps.WalletApiConfig',
|
||||
]
|
||||
|
||||
@@ -49,6 +50,7 @@ MIDDLEWARE = [
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django_htmx.middleware.HtmxMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'wallet.urls'
|
||||
@@ -123,6 +125,7 @@ USE_TZ = True
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
130
wallet/settings_production.py
Normal file
130
wallet/settings_production.py
Normal file
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
Django settings for wallet project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.1.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from dotenv import dotenv_values
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Load env variable from .env
|
||||
config = dotenv_values(".env")
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'y@-63z+ao=w=5%%e&p)a7e=qscvqokg$ai5gyejuh5$hs*p4ij'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'wallet_api.apps.WalletApiConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'wallet.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'wallet.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': config['DB_NAME'],
|
||||
'USER': config['DB_USER'],
|
||||
'PASSWORD': config['DB_PASSWORD'],
|
||||
'HOST': config['DB_HOST'],
|
||||
'PORT': config['DB_PORT'],
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
@@ -1,3 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import PasswordEntry
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(PasswordEntry)
|
||||
|
||||
@@ -13,6 +13,9 @@ class PasswordEntry(models.Model):
|
||||
class Meta:
|
||||
unique_together = ('site', 'username', 'client_id')
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
class MasterHash(models.Model):
|
||||
hash = models.BinaryField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
39
wallet_api/templates/base.html
Normal file
39
wallet_api/templates/base.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<script src="{% static 'js/htmx.min.js' %}" defer></script>
|
||||
|
||||
<title>{% if title %}: {{title}} {% endif %}</title>
|
||||
|
||||
{% block extra_head %} {% endblock extra_head %} {% block extra_script %}
|
||||
{%endblock extra_script %}
|
||||
</head>
|
||||
<body>
|
||||
{% block container %}
|
||||
<main role="main" class="container">
|
||||
{% block content %} {% endblock content %}
|
||||
</main>
|
||||
{% endblock container %} {% block footer %} {% endblock footer %}
|
||||
|
||||
<!-- Bootstrap Javascript -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
|
||||
{% block body_script %} {% endblock body_script %}
|
||||
</body>
|
||||
</html>
|
||||
27
wallet_api/templates/wallet_api/list_users.html
Normal file
27
wallet_api/templates/wallet_api/list_users.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %} {% load static %} {% block content %}
|
||||
<h1>List Mosquitto Users</h1>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Site</th>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Client ID</th>
|
||||
<th scope="col">Topic</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<th scope="row">{{ user.id }}</th>
|
||||
<td>{{ user.site }}</td>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.client_id }}</td>
|
||||
<td>{{ user.topic }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<i> No users </i>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock content %}
|
||||
@@ -6,4 +6,5 @@ urlpatterns = [
|
||||
path('get/', views.get_password_api, name='get_password'),
|
||||
path('delete/', views.delete_password_api, name='delete_password'),
|
||||
path('list/', views.list_sites_api, name='list_sites'),
|
||||
path('user/list/', views.list_users, name='list_users'),
|
||||
]
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from .models import PasswordEntry
|
||||
from .models import PasswordEntry, MasterHash
|
||||
from .utils import authenticate, derive_key, encrypt_password, decrypt_password
|
||||
import json
|
||||
|
||||
def list_users(request):
|
||||
users = PasswordEntry.objects.all()
|
||||
return render(request, 'wallet_api/list_users.html', {'users': users})
|
||||
|
||||
@csrf_exempt
|
||||
def add_password_api(request):
|
||||
if request.method == 'POST':
|
||||
|
||||
Reference in New Issue
Block a user