feat(chat): add models, views, WebSocket consumers, forms and URL config
This commit is contained in:
+223
@@ -0,0 +1,223 @@
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib import messages
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from chat.models import UserProfile, Channel, Message, DirectMessage
|
||||
from chat.forms import RegisterForm, ProfileForm, ChannelForm, MessageFileForm
|
||||
from channels.layers import get_channel_layer
|
||||
from asgiref.sync import async_to_sync
|
||||
|
||||
|
||||
def _broadcast_message(group_name, message_type, msg, request_user):
|
||||
channel_layer = get_channel_layer()
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
group_name,
|
||||
{
|
||||
'type': message_type,
|
||||
'message': msg.content,
|
||||
'sender': request_user.username,
|
||||
'sender_id': request_user.id,
|
||||
'message_id': msg.id,
|
||||
'timestamp': msg.timestamp.strftime('%H:%M'),
|
||||
'avatar': request_user.profile.avatar.url if request_user.profile.avatar else None,
|
||||
'role': request_user.profile.role,
|
||||
'file_url': msg.file.url if msg.file else None,
|
||||
'file_name': msg.file.name if msg.file else None,
|
||||
}
|
||||
)
|
||||
|
||||
def register_view(request):
|
||||
form = RegisterForm(request.POST or None)
|
||||
if request.method == 'POST' and form.is_valid():
|
||||
user = User.objects.create_user(
|
||||
username=form.cleaned_data['username'],
|
||||
email=form.cleaned_data['email'],
|
||||
password=form.cleaned_data['password'],
|
||||
)
|
||||
UserProfile.objects.create(user=user)
|
||||
login(request, user)
|
||||
return redirect('home')
|
||||
return render(request, 'chat/register.html', {'form': form})
|
||||
|
||||
|
||||
def login_view(request):
|
||||
error = None
|
||||
if request.method == 'POST':
|
||||
user = authenticate(request,
|
||||
username=request.POST.get('username'),
|
||||
password=request.POST.get('password'))
|
||||
if user:
|
||||
login(request, user)
|
||||
return redirect('home')
|
||||
error = 'Invalid credentials.'
|
||||
return render(request, 'chat/login.html', {'error': error})
|
||||
|
||||
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
return redirect('login')
|
||||
|
||||
|
||||
@login_required
|
||||
def home_view(request):
|
||||
channels = Channel.objects.all().prefetch_related('members')
|
||||
users = User.objects.exclude(id=request.user.id).select_related('profile')
|
||||
return render(request, 'chat/home.html', {'channels': channels, 'users': users})
|
||||
|
||||
|
||||
@login_required
|
||||
def channel_view(request, channel_id):
|
||||
channel = get_object_or_404(Channel, id=channel_id)
|
||||
# Join if not member
|
||||
if request.user not in channel.members.all():
|
||||
channel.members.add(request.user)
|
||||
|
||||
# File upload via POST
|
||||
if request.method == 'POST':
|
||||
f = request.FILES.get('file')
|
||||
if f:
|
||||
msg = Message.objects.create(
|
||||
channel=channel,
|
||||
sender=request.user,
|
||||
content=request.POST.get('content', ''),
|
||||
file=f,
|
||||
)
|
||||
_broadcast_message(f'channel_{channel.id}', 'chat_message', msg, request.user)
|
||||
return redirect('channel', channel_id=channel_id)
|
||||
|
||||
messages_qs = channel.messages.filter(deleted=False).select_related('sender', 'sender__profile')
|
||||
return render(request, 'chat/channel.html', {
|
||||
'channel': channel,
|
||||
'messages': messages_qs,
|
||||
'all_channels': Channel.objects.all(),
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def create_channel_view(request):
|
||||
profile = request.user.profile
|
||||
if not profile.is_moderator():
|
||||
messages.error(request, 'No permission.')
|
||||
return redirect('home')
|
||||
form = ChannelForm(request.POST or None)
|
||||
if form.is_valid():
|
||||
ch = form.save(commit=False)
|
||||
ch.created_by = request.user
|
||||
ch.save()
|
||||
ch.members.add(request.user)
|
||||
return redirect('channel', channel_id=ch.id)
|
||||
return render(request, 'chat/create_channel.html', {'form': form})
|
||||
|
||||
|
||||
@login_required
|
||||
def delete_channel_view(request, channel_id):
|
||||
channel = get_object_or_404(Channel, id=channel_id)
|
||||
if request.user.profile.is_admin():
|
||||
channel.delete()
|
||||
return redirect('home')
|
||||
|
||||
|
||||
@login_required
|
||||
def dm_view(request, username):
|
||||
other = get_object_or_404(User, username=username)
|
||||
if request.method == 'POST':
|
||||
f = request.FILES.get('file')
|
||||
if f:
|
||||
msg = DirectMessage.objects.create(
|
||||
sender=request.user,
|
||||
receiver=other,
|
||||
content=request.POST.get('content', ''),
|
||||
file=f,
|
||||
)
|
||||
ids = sorted([request.user.id, other.id])
|
||||
_broadcast_message(f'dm_{ids[0]}_{ids[1]}', 'dm_message', msg, request.user)
|
||||
return redirect('dm', username=username)
|
||||
|
||||
dms = DirectMessage.objects.filter(
|
||||
sender__in=[request.user, other],
|
||||
receiver__in=[request.user, other],
|
||||
).select_related('sender', 'sender__profile').order_by('timestamp')
|
||||
|
||||
return render(request, 'chat/dm.html', {
|
||||
'other': other,
|
||||
'messages': dms,
|
||||
'all_users': User.objects.exclude(id=request.user.id).select_related('profile'),
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def profile_view(request):
|
||||
profile = request.user.profile
|
||||
form = ProfileForm(request.POST or None, request.FILES or None, instance=profile)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, 'Profile updated.')
|
||||
return redirect('profile')
|
||||
return render(request, 'chat/profile.html', {'form': form})
|
||||
|
||||
|
||||
def _handle_admin_action(request, action, target, role):
|
||||
if action == 'set_role' and role:
|
||||
target.profile.role = role
|
||||
target.profile.save()
|
||||
messages.success(request, f'Role updated for {target.username}.')
|
||||
elif action == 'block':
|
||||
request.user.profile.blocked_users.add(target)
|
||||
messages.success(request, f'{target.username} blocked.')
|
||||
elif action == 'unblock':
|
||||
request.user.profile.blocked_users.remove(target)
|
||||
messages.success(request, f'{target.username} unblocked.')
|
||||
|
||||
|
||||
@login_required
|
||||
def admin_panel_view(request):
|
||||
if not request.user.profile.is_admin():
|
||||
messages.error(request, 'No permission.')
|
||||
return redirect('home')
|
||||
|
||||
users = User.objects.all().select_related('profile').exclude(id=request.user.id)
|
||||
|
||||
if request.method == 'POST':
|
||||
user_id = request.POST.get('user_id')
|
||||
action = request.POST.get('action')
|
||||
role = request.POST.get('role')
|
||||
target = get_object_or_404(User, id=user_id)
|
||||
|
||||
_handle_admin_action(request, action, target, role)
|
||||
|
||||
return redirect('admin_panel')
|
||||
|
||||
blocked_ids = list(request.user.profile.blocked_users.values_list('id', flat=True))
|
||||
return render(request, 'chat/admin_panel.html', {
|
||||
'users': users,
|
||||
'blocked_ids': blocked_ids,
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
def delete_message_view(request, message_id):
|
||||
msg = get_object_or_404(Message, id=message_id)
|
||||
if request.user.profile.is_moderator() or msg.sender == request.user:
|
||||
msg.deleted = True
|
||||
msg.save()
|
||||
return redirect('channel', channel_id=msg.channel_id)
|
||||
|
||||
|
||||
@login_required
|
||||
def block_user_view(request, user_id):
|
||||
if request.user.profile.is_moderator():
|
||||
target = get_object_or_404(User, id=user_id)
|
||||
request.user.profile.blocked_users.add(target)
|
||||
return redirect('home')
|
||||
|
||||
|
||||
def handler404(request, exception):
|
||||
return render(request, 'chat/404.html', status=404)
|
||||
|
||||
|
||||
def handler500(request):
|
||||
return render(request, 'chat/500.html', status=500)
|
||||
Reference in New Issue
Block a user