feat: Armarium v1.1.0 — dashboard, auth, 2FA, SMTP, settings, deploy
Dashboard: - ApexCharts bar chart (income vs fixed costs vs expenses) and donut chart - KPI cards: income, fixed costs, savings rate with configurable goal - Greeting with time-of-day and locale-aware date/time display Authentication & security: - Email-based login (no username), case-insensitive lookup - JWT access/refresh tokens with rotation and blacklist - TOTP 2FA with QR code, backup codes (copy + PDF export) - 2FA recovery via email code - Cloudflare Turnstile CAPTCHA on login and register Email flows: - Email verification on registration (24h token) - Password reset flow (15min token, anti-enumeration) - Brevo SMTP integration with HTML + plaintext email templates - Notification emails: 2FA recovery, password changed, email changed Settings page: - 2FA management (enable/disable, QR, backup codes) - Active sessions list with per-device revoke - Data export: ZIP with 6 PDFs via fpdf2 - Notification preferences (3 toggles) - Danger zone: account deletion with mandatory export + confirmation phrase UI & layout: - Sidebar with collapsible/flyout mode, Angular signal-based dropdowns - Dark mode (class-based), language switcher (DE/FR/IT/EN) - Mobile-responsive layout with touch-friendly targets - Roboto font via @fontsource (GDPR-compliant, no Google CDN) - Pure Tailwind CSS v3 Infrastructure: - Forgejo Actions CI/CD pipeline (auto-deploy on push to main) - Gunicorn + Nginx + PostgreSQL production setup - Rate limiting, HSTS, secure cookies, CSRF protection
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import os
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from rest_framework_simplejwt.views import TokenRefreshView
|
||||
from finance.views import (
|
||||
AccountViewSet, TransactionViewSet, BudgetViewSet,
|
||||
ExpenseViewSet, DeadlineViewSet, ProfileView, RegisterView, LogoutView, ChangePasswordView,
|
||||
ICalUrlView, ICalFeedView, NotificationsView, SearchView,
|
||||
LoginView, TwoFactorLoginView, TwoFactorSetupView, TwoFactorEnableView, TwoFactorDisableView,
|
||||
TwoFactorRecoverRequestView, TwoFactorRecoverConfirmView,
|
||||
SessionListView, SessionRevokeView, SessionRevokeAllView,
|
||||
DataExportView, NotificationPrefsView,
|
||||
VerifyEmailView, PasswordResetRequestView, PasswordResetConfirmView,
|
||||
)
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'accounts', AccountViewSet, basename='account')
|
||||
router.register(r'transactions', TransactionViewSet, basename='transaction')
|
||||
router.register(r'budgets', BudgetViewSet, basename='budget')
|
||||
router.register(r'expenses', ExpenseViewSet, basename='expense')
|
||||
router.register(r'deadlines', DeadlineViewSet, basename='deadline')
|
||||
|
||||
_admin_url = os.environ.get('ADMIN_URL', 'manage/').strip('/')+ '/'
|
||||
|
||||
urlpatterns = [
|
||||
path(_admin_url, admin.site.urls),
|
||||
path('api/', include(router.urls)),
|
||||
path('api/profile/', ProfileView.as_view()),
|
||||
path('api/auth/register/', RegisterView.as_view()),
|
||||
path('api/auth/token/', LoginView.as_view()),
|
||||
path('api/auth/token/refresh/', TokenRefreshView.as_view()),
|
||||
path('api/auth/logout/', LogoutView.as_view()),
|
||||
path('api/auth/password/', ChangePasswordView.as_view()),
|
||||
path('api/auth/verify-email/', VerifyEmailView.as_view()),
|
||||
path('api/auth/password-reset/', PasswordResetRequestView.as_view()),
|
||||
path('api/auth/password-reset/confirm/', PasswordResetConfirmView.as_view()),
|
||||
path('api/auth/2fa/login/', TwoFactorLoginView.as_view()),
|
||||
path('api/auth/2fa/setup/', TwoFactorSetupView.as_view()),
|
||||
path('api/auth/2fa/enable/', TwoFactorEnableView.as_view()),
|
||||
path('api/auth/2fa/disable/', TwoFactorDisableView.as_view()),
|
||||
path('api/auth/2fa/recover/', TwoFactorRecoverRequestView.as_view()),
|
||||
path('api/auth/2fa/recover/confirm/', TwoFactorRecoverConfirmView.as_view()),
|
||||
path('api/auth/sessions/', SessionListView.as_view()),
|
||||
path('api/auth/sessions/revoke-all/', SessionRevokeAllView.as_view()),
|
||||
path('api/auth/sessions/<str:session_key>/', SessionRevokeView.as_view()),
|
||||
path('api/export/', DataExportView.as_view()),
|
||||
path('api/notifications/prefs/', NotificationPrefsView.as_view()),
|
||||
path('api/search/', SearchView.as_view()),
|
||||
path('api/notifications/', NotificationsView.as_view()),
|
||||
path('api/calendar/ical-url/', ICalUrlView.as_view()),
|
||||
path('api/calendar/ical/<int:user_id>/<str:token>/', ICalFeedView.as_view()),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
Reference in New Issue
Block a user