✨ Major Features Added: - Complete magical theming and rebranding from LifeRPG to The Wizard's Grimoire - Production-grade React frontend with Tailwind CSS v4 and magical aesthetics - Comprehensive analytics dashboard with Recharts integration (ScryingPortal) - Push notifications system with PWA service worker support - Drag & drop functionality using @dnd-kit for habit reordering - Social features with friends system and leaderboards - Performance optimization tools and monitoring - Mobile app enhancement with PWA installation support 🏗️ Technical Infrastructure: - Advanced service worker with offline support and background sync - Zustand state management for scalable application state - Production-ready UI component system with enhanced Button, Card, Input - Progressive Web App (PWA) with manifest and app installation - FastAPI backend with comprehensive API endpoints - Docker containerization and CI/CD pipeline setup 📱 Progressive Web App Features: - Offline functionality with intelligent caching - Push notification support for habit reminders - App installation on mobile and desktop platforms - Background sync for offline data management - Performance monitoring and optimization tools 🎨 User Experience: - Magical wizard/grimoire theming throughout application - Responsive design optimized for all device sizes - Drag & drop habit management with smooth animations - Interactive analytics with multiple chart types - Social connectivity with friends and competitive features - Comprehensive notification and performance settings 🔧 Developer Experience: - Modern development stack with Vite and React - Comprehensive testing setup and CI/CD pipelines - Code quality tools with pre-commit hooks - Docker development environment - Detailed documentation and implementation guides This represents a complete transformation from prototype to production-ready application with enterprise-grade features and magical user experience.
38 lines
1.1 KiB
Python
38 lines
1.1 KiB
Python
import os
|
|
import secrets
|
|
import hashlib
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
|
|
from .models import PublicToken, SessionLocal
|
|
|
|
PEPPER = os.getenv('LIFERPG_TOKEN_PEPPER', 'dev_pepper_change_me')
|
|
|
|
|
|
def _hash_token(token: str) -> str:
|
|
return hashlib.sha256((token + PEPPER).encode('utf-8')).hexdigest()
|
|
|
|
|
|
def create_public_token(db, user_id: int, name: str, scope: str = 'read:widgets') -> str:
|
|
"""Create a new public token for the user and return the plaintext token value once.
|
|
The token is prefixed for readability and stored hashed in DB.
|
|
"""
|
|
raw = f"lpt_{secrets.token_urlsafe(24)}"
|
|
h = _hash_token(raw)
|
|
pt = PublicToken(user_id=user_id, name=name or 'token', scope=scope or 'read:widgets', token_hash=h)
|
|
db.add(pt)
|
|
db.flush()
|
|
return raw
|
|
|
|
|
|
def verify_public_token(db, token: str) -> Optional[int]:
|
|
if not token:
|
|
return None
|
|
h = _hash_token(token)
|
|
row = db.query(PublicToken).filter_by(token_hash=h).first()
|
|
if not row:
|
|
return None
|
|
row.last_used_at = datetime.now(timezone.utc)
|
|
db.flush()
|
|
return row.user_id
|