Add JWT auth with per-user data isolation and account settings.

Users can register, log in, and manage profile/password in a personal account page; server data is scoped by owner across contacts, maps, tags, and import.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-28 21:10:16 +03:00
co-authored by Cursor
parent 85b8b2e9b8
commit 4eb4c145b6
40 changed files with 2301 additions and 117 deletions
+44 -8
View File
@@ -1,4 +1,32 @@
from contacts.models import Contact, Relation, NetworkMap, NetworkMapMembership
from core.access import use_jwt_auth
def _contacts_qs(user):
qs = Contact.objects.all()
if use_jwt_auth():
if user and user.is_authenticated:
return qs.filter(owner=user)
return qs.none()
return qs
def _relations_qs(user):
qs = Relation.objects.select_related('source', 'target').all()
if use_jwt_auth():
if user and user.is_authenticated:
return qs.filter(owner=user)
return qs.none()
return qs
def _network_maps_qs(user):
qs = NetworkMap.objects.select_related('map_type').all()
if use_jwt_auth():
if user and user.is_authenticated:
return qs.filter(owner=user)
return qs.none()
return qs
def node_from_contact(contact):
@@ -36,33 +64,41 @@ def edge_from_relation(relation):
}
def build_full_graph():
contacts = Contact.objects.all()
def build_full_graph(user=None):
contacts = _contacts_qs(user)
nodes = [node_from_contact(c) for c in contacts]
relations = Relation.objects.select_related('source', 'target').all()
relations = _relations_qs(user)
edges = [edge_from_relation(r) for r in relations]
return {'nodes': nodes, 'edges': edges}
def build_network_map_graph(map_id=None):
def build_network_map_graph(map_id=None, user=None):
maps_qs = _network_maps_qs(user)
if not map_id:
default_map = NetworkMap.objects.select_related('map_type').order_by('id').first()
default_map = maps_qs.order_by('id').first()
if not default_map:
return {'nodes': [], 'edges': [], 'conflictology': False, 'conflict_subject': ''}
map_id = default_map.id
network_map = NetworkMap.objects.select_related('map_type').filter(pk=map_id).first()
network_map = maps_qs.filter(pk=map_id).first()
if not network_map:
return {'nodes': [], 'edges': [], 'conflictology': False, 'conflict_subject': ''}
memberships = list(
NetworkMapMembership.objects.filter(map_id=map_id)
.select_related('contact')
.select_related('contact', 'map')
.order_by('contact__name')
)
if use_jwt_auth() and user and user.is_authenticated:
memberships = [
m for m in memberships
if m.map.owner_id == user.id and m.contact.owner_id == user.id
]
allowed_ids = {m.contact_id for m in memberships}
nodes = [node_from_membership(m) for m in memberships]
relations = Relation.objects.select_related('source', 'target').all()
relations = _relations_qs(user)
edges = [
edge_from_relation(r)
for r in relations
+2 -2
View File
@@ -3,6 +3,6 @@ from django.urls import path
from . import views
urlpatterns = [
path('graph/', views.graph_data, name='graph-data'),
path('network-map-graph/', views.network_map_graph, name='network-map-graph'),
path('graph/', views.GraphDataView.as_view(), name='graph-data'),
path('network-map-graph/', views.NetworkMapGraphView.as_view(), name='network-map-graph'),
]
+16 -8
View File
@@ -1,15 +1,23 @@
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from core.drf_mixins import JwtAuthMixin
from core.access import use_jwt_auth
from .services import build_full_graph, build_network_map_graph
@api_view(['GET'])
def graph_data(request):
return Response(build_full_graph())
def _graph_user(request):
if use_jwt_auth():
return request.user if request.user.is_authenticated else None
return None
@api_view(['GET'])
def network_map_graph(request):
map_id = request.query_params.get('map_id')
return Response(build_network_map_graph(map_id))
class GraphDataView(JwtAuthMixin, APIView):
def get(self, request):
return Response(build_full_graph(user=_graph_user(request)))
class NetworkMapGraphView(JwtAuthMixin, APIView):
def get(self, request):
map_id = request.query_params.get('map_id')
return Response(build_network_map_graph(map_id, user=_graph_user(request)))