diff --git a/.gitignore b/.gitignore index ff82511..5172824 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ chat/migrations/ login/migrations/ statics/upload/ + statics/dist/ statics/src/ statics/img/ diff --git a/README.md b/README.md index 4a547e8..6aba6bf 100644 --- a/README.md +++ b/README.md @@ -24,4 +24,9 @@ ### 前端 1. LayIM(已获得授权) -2. LayUI(开源) \ No newline at end of file +2. LayUI(开源) + +##待实现功能 + +1. 客服 +2. 聊天机器人 \ No newline at end of file diff --git a/WebIM/settings.py b/WebIM/settings.py index 030ed6d..f72fa26 100644 --- a/WebIM/settings.py +++ b/WebIM/settings.py @@ -11,6 +11,8 @@ """ import os +from urllib.parse import urlparse + # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -22,13 +24,14 @@ SECRET_KEY = '3zt8c)88lmp_8kif4&y*#oy=myhsdh5do)xjixb3$$b+i-2+vt' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = False ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ + 'agent', 'channels', 'login', 'chat', @@ -83,6 +86,32 @@ }, } +# cache +CACHES = { + 'default': { + 'BACKEND': 'django_redis.cache.RedisCache', + 'LOCATION': 'redis://127.0.0.1:6379/1', + 'OPTIONS': { + # 'PASSWORD': redis_url.password, + 'CLIENT_CLASS': 'django_redis.client.DefaultClient', + 'PICKLE_VERSION': -1, # Use the latest protocol version 默认使用最新的 pickle. + 'SOCKET_CONNECT_TIMEOUT': 5, # in seconds socket 建立连接超时设置 + 'SOCKET_TIMEOUT': 60, # in seconds 连接建立后的读写操作超时设置 + "CONNECTION_POOL_KWARGS": {"max_connections": 100}, # 配置默认连接池 + 'IGNORE_EXCEPTIONS': True, + } + } +} + +REDIS_TIMEOUT = 7*24*60*60 +CUBES_REDIS_TIMEOUT = 60*60 +NEVER_REDIS_TIMEOUT = 365*24*60*60 + +# Django 默认可以使用任何 cache backend 作为 session backend, +# 将 django-redis 作为 session 储存后端不用安装任何额外的 backend +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' +SESSION_CACHE_ALIAS = 'default' + # Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases @@ -107,11 +136,6 @@ def mysql_settings(): 'default': mysql_settings() } -if DEBUG: - Domain = 'http://127.0.0.1:8000' -else: - Domain = 'https://iwantme.cn' - # Password validation # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators @@ -154,3 +178,42 @@ def mysql_settings(): STATICFILES_DIRS = [ os.path.join(BASE_DIR, "statics"), ] + + +# 上传文件白名单 +ALLOWED_EXTENSIONS = ('txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'zip') +# 2.5MB - 2621440 +# 5MB - 5242880 +# 10MB - 10485760 +# 20MB - 20971520 +# 50MB - 5242880 +# 100MB 104857600 +# 250MB - 214958080 +# 500MB - 429916160 +MAX_UPLOAD_SIZE = 10485760 + +# LOGIN_URL +LOGIN_URL = '/' +# Domain = 'http://127.0.0.1:8000' +Domain = 'https://iwantme.cn' + +AUTH_USER_MODEL = 'chat.IMUser' + +# session 设置 +# 30分钟 +SESSION_COOKIE_AGE = 60 * 5 +SESSION_SAVE_EVERY_REQUEST = True +# 关闭浏览器,则COOKIE失效 +SESSION_EXPIRE_AT_BROWSER_CLOSE = True + +# 本地开发配置放在local_settings.py中 +# try: +# from .local_settings import * +# except ImportError: +# pass + + +### +# 部署上线需要修改的地方 +# Domain = 'https://iwantme.cn' +# DEBUG = False \ No newline at end of file diff --git a/WebIM/urls.py b/WebIM/urls.py index 4d613a2..1940a4d 100644 --- a/WebIM/urls.py +++ b/WebIM/urls.py @@ -19,6 +19,7 @@ urlpatterns = [ url(r'^$', login_views.do_login, name='index'), + url(r'^agent/', include('agent.urls')), url(r'^login/', include('login.urls')), url(r'^chat/', include('chat.urls')), url(r'^admin/', admin.site.urls), diff --git a/agent/__init__.py b/agent/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/agent/admin.py b/agent/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/agent/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/agent/apps.py b/agent/apps.py new file mode 100644 index 0000000..cda62fa --- /dev/null +++ b/agent/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AgentConfig(AppConfig): + name = 'agent' diff --git a/agent/migrations/0001_initial.py b/agent/migrations/0001_initial.py new file mode 100644 index 0000000..4ee151b --- /dev/null +++ b/agent/migrations/0001_initial.py @@ -0,0 +1,54 @@ +# Generated by Django 2.1.2 on 2018-11-05 16:49 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Agent', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100, verbose_name='客服名称')), + ('password', models.CharField(max_length=100, verbose_name='密码')), + ('avatar', models.CharField(default='/statics/img/default_avatar_male_180.gif', max_length=128, verbose_name='客服头像')), + ], + ), + migrations.CreateModel( + name='Client', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=100, verbose_name='客户昵称')), + ('avatar', models.CharField(default='/statics/img/default_avatar_male_180.gif', max_length=128, verbose_name='客户头像')), + ('ip', models.CharField(default='0.0.0.0', max_length=64, verbose_name='客户IP')), + ], + ), + migrations.CreateModel( + name='Dialogue', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, verbose_name='对话名称')), + ('agent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='agent', to='agent.Agent')), + ('client', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='client', to='agent.Client')), + ], + ), + migrations.CreateModel( + name='Statement', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('from_id', models.CharField(max_length=255)), + ('to_id', models.CharField(max_length=255)), + ('msg_type', models.IntegerField(default=0)), + ('content', models.CharField(max_length=255)), + ('timestamp', models.BigIntegerField()), + ], + ), + ] diff --git a/agent/migrations/0002_statement_dialogue.py b/agent/migrations/0002_statement_dialogue.py new file mode 100644 index 0000000..aee647e --- /dev/null +++ b/agent/migrations/0002_statement_dialogue.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.2 on 2018-11-05 16:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('agent', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='statement', + name='dialogue', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dialogue', to='agent.Dialogue'), + ), + ] diff --git a/agent/migrations/0003_auto_20181106_2214.py b/agent/migrations/0003_auto_20181106_2214.py new file mode 100644 index 0000000..4bea033 --- /dev/null +++ b/agent/migrations/0003_auto_20181106_2214.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.2 on 2018-11-06 14:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agent', '0002_statement_dialogue'), + ] + + operations = [ + migrations.AlterField( + model_name='client', + name='name', + field=models.CharField(default='访客', max_length=100, verbose_name='客户昵称'), + ), + ] diff --git a/agent/migrations/__init__.py b/agent/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/agent/models.py b/agent/models.py new file mode 100644 index 0000000..172cac2 --- /dev/null +++ b/agent/models.py @@ -0,0 +1,41 @@ +import uuid +from django.db import models + + +class Agent(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=100, verbose_name="客服名称") + password = models.CharField(max_length=100, verbose_name="密码") + # 客服头像 + avatar = models.CharField(max_length=128, default='/statics/img/default_avatar_male_180.gif', verbose_name="客服头像") + + +class Client(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=100, verbose_name="客户昵称", default='访客') + # 客户头像 + avatar = models.CharField(max_length=128, default='/statics/img/default_avatar_male_180.gif', verbose_name="客户头像") + # 客户的IP地址 + ip = models.CharField(max_length=64, default='0.0.0.0', verbose_name='客户IP') + + +class Dialogue(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + name = models.CharField(max_length=128, verbose_name='对话名称') + agent = models.ForeignKey('Agent', on_delete=models.CASCADE, null=True, related_name='agent') + client = models.ForeignKey('Client', on_delete=models.CASCADE, null=True, related_name='client') + + +class Statement(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + dialogue = models.ForeignKey('Dialogue', on_delete=models.CASCADE, null=True, related_name='dialogue') + from_id = models.CharField(max_length=255) + # from_user_name = models.CharField(max_length=255) + # from_user_avatar = models.CharField(max_length=255) + to_id = models.CharField(max_length=255) + # from_user = models.ForeignKey('User', on_delete=models.CASCADE, null=True, related_name='msg_from_user') + # to_user = models.ForeignKey('User', on_delete=models.CASCADE, null=True, related_name='msg_to_user') + # 0 文本; 1 图片; 2 视频; 3语音 + msg_type = models.IntegerField(default=0) + content = models.CharField(max_length=255) + timestamp = models.BigIntegerField() \ No newline at end of file diff --git a/agent/tests.py b/agent/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/agent/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/agent/urls.py b/agent/urls.py new file mode 100644 index 0000000..2300316 --- /dev/null +++ b/agent/urls.py @@ -0,0 +1,8 @@ +from . import views +from django.conf.urls import url + + +urlpatterns = [ + url(r'^$', views.agent), + url(r'^create_user/$', views.create_user, name='create_user'), +] \ No newline at end of file diff --git a/agent/views.py b/agent/views.py new file mode 100644 index 0000000..37077a2 --- /dev/null +++ b/agent/views.py @@ -0,0 +1,19 @@ +from django.shortcuts import render +from django.http import JsonResponse +from .models import Client, Agent +from django.views.decorators.csrf import csrf_exempt + + +def agent(request): + return render(request, 'agent/kefu.html') + + +@csrf_exempt +def create_user(request): + client_id = request.POST.get('client_id', None) + if client_id is None: + return JsonResponse({'code': 1, 'msg': 'invalid client_id'}) + client = Client.objects.create(id=client_id) + agent = Agent.objects.all()[0] + return JsonResponse({'code': 0, 'msg': '', 'data': {'username': client.name, 'id': client.id, 'avatar': client.avatar}}) + diff --git a/chat/admin.py b/chat/admin.py index 8de3a49..60d9b80 100644 --- a/chat/admin.py +++ b/chat/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from chat.models import User, GroupChat, Group, Message +from chat.models import IMUser, IMGroupChat, IMGroup, Message class UserAdmin(admin.ModelAdmin): @@ -18,9 +18,6 @@ class UserAdmin(admin.ModelAdmin): ) -admin.site.register(User, UserAdmin) -admin.site.register(Group) -admin.site.register(GroupChat) -# admin.site.register(Membership) -# admin.site.register(Message) -# admin.site.register(GroupChatMembership) +admin.site.register(IMUser, UserAdmin) +admin.site.register(IMGroup) +admin.site.register(IMGroupChat) diff --git a/chat/consumers.py b/chat/consumers.py index 18b5f36..8e97a92 100644 --- a/chat/consumers.py +++ b/chat/consumers.py @@ -2,7 +2,7 @@ from channels.generic.websocket import AsyncWebsocketConsumer from asgiref.sync import async_to_sync from channels.layers import get_channel_layer -from .models import Group +from .models import IMGroup import json diff --git a/chat/models.py b/chat/models.py index 8a5a4ac..3f213be 100644 --- a/chat/models.py +++ b/chat/models.py @@ -1,11 +1,12 @@ import uuid import django from django.db import models +from django.contrib.auth.models import AbstractUser -class User(models.Model): +class IMUser(AbstractUser): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - username = models.CharField(max_length=100, verbose_name="用户名") + username = models.CharField(max_length=100, unique=True, verbose_name="用户名") password = models.CharField(max_length=100, verbose_name="密码") email = models.EmailField(verbose_name="邮箱") phone = models.CharField(max_length=11, verbose_name="手机号") @@ -26,9 +27,6 @@ class User(models.Model): # 用户头像 avatar = models.CharField(max_length=128, default='/statics/img/default_avatar_male_180.gif', verbose_name="头像") - # avatar = models.ImageField(upload_to='statics/upload/%y%m%d', blank=True, null=True, - # default='/statics/img/default_avatar_male_180.gif', verbose_name="头像") - STATUS = ( ('ON', 'online'), ('OFF', 'hide'), @@ -40,7 +38,7 @@ def __str__(self): return self.username -class Group(models.Model): +class IMGroup(models.Model): ''' 好友分组,属于某个用户 一个用户(User)有多个好友分组(Group), @@ -48,23 +46,23 @@ class Group(models.Model): ''' id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=128) - owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner', default=None) - group_members = models.ManyToManyField(User) + owner = models.ForeignKey(IMUser, on_delete=models.CASCADE, related_name='owner', default=None) + group_members = models.ManyToManyField(IMUser, related_name='group_members') def __str__(self): return self.name -class GroupChat(models.Model): +class IMGroupChat(models.Model): ''' 群聊,独立于用户存在 ''' id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=128) # 群聊管理员 - group_admins = models.ManyToManyField(User, related_name="group_admins") + group_admins = models.ManyToManyField(IMUser, related_name="im_group_admins") group_chat_avatar = models.CharField(max_length=128, default='/statics/img/default_avatar_male_180.gif') - group_chat_members = models.ManyToManyField(User, related_name='group_chat_members') + group_chat_members = models.ManyToManyField(IMUser, related_name='im_group_chat_members') def __str__(self): return self.name diff --git a/chat/urls.py b/chat/urls.py index a12d317..40f8cae 100644 --- a/chat/urls.py +++ b/chat/urls.py @@ -5,7 +5,8 @@ urlpatterns = [ url(r'^msg_gateway/$', views.msg_gateway), - url(r'^chat_home/$', views.home, name='chat_home'), + url(r'^chat_pc/$', views.chat_pc, name='chat_pc'), + url(r'^chat_mobile/$', views.chat_mobile, name='chat_mobile'), url(r'^init/$', views.init_user, name='init'), url(r'^init_group_chat/$', views.init_group_chat, name='init_group_chat'), url(r'^upload_image/$', views.upload_image, name='upload_image'), diff --git a/chat/views.py b/chat/views.py index e97c701..1ee6ba4 100644 --- a/chat/views.py +++ b/chat/views.py @@ -2,25 +2,29 @@ import time import uuid import datetime -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404 from django.views.decorators.csrf import csrf_exempt from django.http import JsonResponse -from chat.models import User, Group, GroupChat, ImageModel, FileModel, Message +from chat.models import IMUser, IMGroup, IMGroupChat, ImageModel, FileModel, Message from chat.consumers import channel_publish -from WebIM.settings import Domain -import sys -import codecs -# sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) +from WebIM.settings import Domain, ALLOWED_EXTENSIONS, MAX_UPLOAD_SIZE +from django.contrib.auth.decorators import login_required +from django.views.decorators.http import require_http_methods -def home(request): - user_id = request.GET.get('user_id', None) - if user_id is not None: - user = User.objects.get(id=user_id) - return render(request, 'chat/chat.html', {'id': user_id}) - return render(request, 'chat/chat.html', {'user_id': user_id}) +@login_required(login_url='index') +def chat_pc(request): + # print('pc') + return render(request, 'chat/chat_pc.html') + +@login_required(login_url='index') +def chat_mobile(request): + # print('mobile') + return render(request, 'chat/chat_mobile.html') + +# @login_required @csrf_exempt def msg_gateway(request): ''' @@ -85,6 +89,7 @@ def msg_gateway(request): return JsonResponse({'code': 0, 'status': True, 'info': '消息发送成功'}) +# @login_required @csrf_exempt def history_msg(request): ''' @@ -133,6 +138,7 @@ def history_msg(request): return JsonResponse(res) +# @login_required def init_user(request): ''' 初始化用户聊天界面 @@ -161,7 +167,7 @@ def init_user(request): return JsonResponse(res) # 查询用户的好友分组 - groups = Group.objects.filter(owner=user_id) + groups = IMGroup.objects.filter(owner=user_id) friends = list() for item in groups: # print('group', item.name) @@ -186,8 +192,8 @@ def init_user(request): res['data']['friend'] = friends # 群组列表 - user = User.objects.get(pk=user_id) - group_chats = user.group_chat_members.all() + user = IMUser.objects.get(pk=user_id) + group_chats = user.im_group_chat_members.all() res['data']['group'] = [{'groupname': item.name, 'id': item.id, 'avatar': item.group_chat_avatar} for item in group_chats] # 我的信息 @@ -222,7 +228,7 @@ def init_group_chat(request): if group_chat_id is None: res['msg'] = 'group_chat_id is None' return JsonResponse(res) - group_chat = GroupChat.objects.get(id=group_chat_id) + group_chat = IMGroupChat.objects.get(id=group_chat_id) res['data']['list'] = [ { 'username': item.username, @@ -236,6 +242,7 @@ def init_group_chat(request): return JsonResponse(res) +# @login_required @csrf_exempt def add_friend(request): ''' @@ -255,8 +262,8 @@ def add_friend(request): # 好友申请信息 remark = request.POST.get('remark', None) # print('用户A的ID', a_user_id) - user = User.objects.get(pk=a_user_id) - friend = User.objects.get(pk=b_friend_id) + user = IMUser.objects.get(pk=a_user_id) + friend = IMUser.objects.get(pk=b_friend_id) # print('%s要添加%s为好友' % (user.username, friend.username)) # 向用户B发送好友申请 content = { @@ -278,7 +285,7 @@ def add_friend(request): elif 'pass' == res_type: # 申请添加好友的用户ID user_id = request.POST.get('user_id', None) - user = User.objects.get(id=user_id) + user = IMUser.objects.get(id=user_id) # 被添加的用户ID friend_id = request.POST.get('friend_id', None) # 用户A的好友组 @@ -286,11 +293,11 @@ def add_friend(request): # 用户B的好友组 b_group_id = request.POST.get('b_group_id', None) - friend = User.objects.get(id=friend_id) + friend = IMUser.objects.get(id=friend_id) # 获取好友组 - a_group = Group.objects.get(id=a_group_id) + a_group = IMGroup.objects.get(id=a_group_id) a_group.group_members.add(friend) - b_group = Group.objects.get(pk=b_group_id) + b_group = IMGroup.objects.get(pk=b_group_id) b_group.group_members.add(user) # print('%s同意了%s的好友申请' % (friend.username, user.username)) # 向用户A通知申请通过 @@ -322,6 +329,7 @@ def add_friend(request): return JsonResponse(b_content['msg']) +# @login_required @csrf_exempt def apply_group_chat(request): ''' @@ -334,10 +342,10 @@ def apply_group_chat(request): user_id = request.POST.get('user_id', None) group_chat_id = request.POST.get('group_chat_id', None) remark = request.POST.get('remark', None) - user = User.objects.get(pk=user_id) + user = IMUser.objects.get(pk=user_id) # 给群聊管理员发送申请通知 - group_chat = GroupChat.objects.get(pk=group_chat_id) - admins = group_chat.group_admins.all() + group_chat = IMGroupChat.objects.get(pk=group_chat_id) + admins = group_chat.im_group_admins.all() content = { 'channel_type': 'apply_group_chat', 'msg': { @@ -355,6 +363,7 @@ def apply_group_chat(request): return JsonResponse({'code': 0, 'status': True, 'info': '申请已发送'}) +# @login_required @csrf_exempt def search_friend(request): ''' @@ -386,7 +395,7 @@ def search_friend(request): 'sign': user.signature, 'avatar': user.avatar } - for user in User.objects.filter(username__icontains=key_word) if str(user.id) != user_id + for user in IMUser.objects.filter(username__icontains=key_word) if str(user.id) != user_id ] res['count'] = len(result) res['data'] = result @@ -397,13 +406,19 @@ def search_friend(request): 'group_chat_name': group_chat.name, 'avatar': group_chat.group_chat_avatar } - for group_chat in GroupChat.objects.filter(name__icontains=key_word) + for group_chat in IMGroupChat.objects.filter(name__icontains=key_word) ] res['count'] = len(result) res['data'] = result return JsonResponse(res) +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + + +# @login_required +@require_http_methods(["POST"]) @csrf_exempt def upload_image(request): ''' @@ -417,21 +432,27 @@ def upload_image(request): 'data': { } } - if request.method == 'POST': - pic = request.FILES.get('file') + pic = request.FILES.get('file', None) + if pic is None: + res['msg'] = '上传的图片异常,请重试' + return JsonResponse(res) + # print(dir(pic)) + # print(type(pic.size)) + # print(pic.size) + if pic.size > MAX_UPLOAD_SIZE: + res['msg'] = '图片大小超过限制' + elif not allowed_file(pic.name): + res['msg'] = '图片类型不被允许' + else: pic.name = str(uuid.uuid4()) + '.' + pic.name.rsplit('.', 1)[1] - image = ImageModel.objects.create(model_pic=pic) - # qiniu_upload(pic) - # print(image.model_pic.name) + ImageModel.objects.create(model_pic=pic) res['code'] = 0 date = datetime.datetime.now().strftime("%y%m%d") res['data']['src'] = '%s/statics/upload/%s/%s' % (Domain, date, pic.name) - # print(res) - return JsonResponse(res) - res['msg'] = '上传文件失败' return JsonResponse(res) +# @login_required @csrf_exempt def upload_avatar(request): ''' @@ -448,20 +469,26 @@ def upload_avatar(request): if request.method == 'POST': user_id = request.GET.get('user_id', None) pic = request.FILES.get('file') - pic.name = str(uuid.uuid4()) + '.' + pic.name.rsplit('.', 1)[1] - image = ImageModel.objects.create(model_pic=pic) - res['code'] = 0 - date = datetime.datetime.now().strftime("%y%m%d") - res['data']['src'] = '%s/statics/upload/%s/%s' % (Domain, date, pic.name) - user = User.objects.get(pk=user_id) - user.avatar = res['data']['src'] - user.save() - # print(res) - return JsonResponse(res) + if pic and allowed_file(pic.name): + pic.name = str(uuid.uuid4()) + '.' + pic.name.rsplit('.', 1)[1] + ImageModel.objects.create(model_pic=pic) + res['code'] = 0 + date = datetime.datetime.now().strftime("%y%m%d") + res['data']['src'] = '%s/statics/upload/%s/%s' % (Domain, date, pic.name) + user = IMUser.objects.get(pk=user_id) + user.avatar = res['data']['src'] + user.save() + # print(res) + return JsonResponse(res) + else: + res['msg'] = '文件类型不被允许' + return JsonResponse(res) res['msg'] = '上传文件失败' return JsonResponse(res) +# @login_required +@require_http_methods(["POST"]) @csrf_exempt def upload_file(request): ''' @@ -475,21 +502,27 @@ def upload_file(request): 'data': { } } - if request.method == 'POST': - file_ = request.FILES.get('file') + file_ = request.FILES.get('file', None) + if file_ is None: + res['msg'] = '上传的文件异常,请重试' + return JsonResponse(res) + # print(dir(pic)) + # print(type(pic.size)) + # print(file_.size) + if file_.size > MAX_UPLOAD_SIZE: + res['msg'] = '文件大小超过限制' + elif not allowed_file(file_.name): + res['msg'] = '文件类型不被允许' + else: file_.name = str(uuid.uuid4()) + '.' + file_.name.rsplit('.', 1)[1] - file_model = FileModel.objects.create(model_file=file_) - # qiniu_upload(pic) - # print(file_model.model_file.name) + FileModel.objects.create(model_file=file_) res['code'] = 0 date = datetime.datetime.now().strftime("%y%m%d") res['data']['src'] = '%s/statics/upload/%s/%s' % (Domain, date, file_.name) - # print(res) - return JsonResponse(res) - res['msg'] = '上传文件失败' return JsonResponse(res) +# @login_required @csrf_exempt def modify_sign(request): ''' @@ -503,13 +536,14 @@ def modify_sign(request): # print('sign', sign) # print('user_id', user_id) if sign and user_id: - user = User.objects.get(pk=user_id) + user = IMUser.objects.get(pk=user_id) user.signature = sign user.save() return JsonResponse({'code': 0, 'msg': 'success'}) return JsonResponse({'code': -1, 'msg': 'invalid parameters'}) +# @login_required @csrf_exempt def modify_status(request): ''' @@ -523,25 +557,27 @@ def modify_status(request): # print('sign', status) # print('user_id', user_id) if status and user_id: - user = User.objects.get(pk=user_id) + user = IMUser.objects.get(pk=user_id) user.status = 'ON' if status == 'online' else 'OFF' user.save() return JsonResponse({'code': 0, 'msg': 'success'}) return JsonResponse({'code': -1, 'msg': 'invalid parameters'}) +# @login_required @csrf_exempt def group_chat_ids(request): if request.method == 'POST': user_id = request.POST.get('user_id', None) if not user_id: return JsonResponse({'code': -1, 'msg': 'invalid parameters'}) - user = User.objects.get(pk=user_id) + user = IMUser.objects.get(pk=user_id) group_chats = user.groupchat_set.all() data = [item.id for item in group_chats] return JsonResponse({'code': 0, 'msg': '', 'data': data}) +# @login_required @csrf_exempt def add_group_chat(request): ''' @@ -556,13 +592,14 @@ def add_group_chat(request): group_chat_avatar = request.POST.get('group_chat_avatar', None) if not group_chat_name or not user_id: return JsonResponse({'code': -1, 'msg': 'invalid parameters'}) - user = User.objects.get(pk=user_id) - group_chat = GroupChat.objects.create(name=group_chat_name) + user = IMUser.objects.get(pk=user_id) + group_chat = IMGroupChat.objects.create(name=group_chat_name) group_chat.group_admins.add(user) group_chat.group_chat_members.add(user) return JsonResponse({'code': 0, 'msg': '', 'data': {'group_id': group_chat.id, 'group_avatar': group_chat.group_chat_avatar}}) +# @login_required @csrf_exempt def add_group(request): ''' @@ -576,17 +613,18 @@ def add_group(request): group_avatar = request.POST.get('group_avatar', None) if not group_name or not user_id: return JsonResponse({'code': -1, 'msg': 'invalid parameters'}) - user = User.objects.get(pk=user_id) - group = Group.objects.create(name=group_name, owner=user) + user = IMUser.objects.get(pk=user_id) + group = IMGroup.objects.create(name=group_name, owner=user) return JsonResponse({'code': 0, 'msg': '', 'data': {'group_id': group.id}}) +@login_required(login_url='index') @csrf_exempt def user_info(request): if request.method == 'GET': user_id = request.GET.get('user_id', None) if user_id: - user = User.objects.get(pk=user_id) + user = get_object_or_404(IMUser, pk=user_id) return render(request, 'chat/user_info.html', context={'user': user}) return render(request, 'chat/user_info.html') else: @@ -598,7 +636,7 @@ def user_info(request): user_id = request.POST.get('user_id', None) if not user_id: return JsonResponse({'code': -1, 'status': False, 'info': 'invalid user_id'}) - user = User.objects.get(pk=user_id) + user = IMUser.objects.get(pk=user_id) user.signature = signature user.email = email user.birthday = birthday diff --git a/login/urls.py b/login/urls.py index 0286797..79cdaa3 100644 --- a/login/urls.py +++ b/login/urls.py @@ -5,7 +5,7 @@ urlpatterns = [ url(r'^do_login/$', views.do_login, name='do_login'), - url(r'^logout/$', views.logout, name='logout'), + url(r'^do_logout/$', views.do_logout, name='do_logout'), url(r'^signin/$', views.signin, name='signin'), url(r'^signup/$', views.signup, name='signup'), ] diff --git a/login/views.py b/login/views.py index a933784..e5aa9e8 100644 --- a/login/views.py +++ b/login/views.py @@ -1,10 +1,9 @@ -from django.shortcuts import render +import html from django.http import JsonResponse -from chat.models import User, Group +from django.shortcuts import render, HttpResponseRedirect, reverse from django.views.decorators.csrf import csrf_exempt -import sys -import codecs -# sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) +from chat.models import IMUser, IMGroup +from django.contrib.auth import authenticate, login, logout @csrf_exempt @@ -17,24 +16,26 @@ def do_login(request): if request.method == 'POST': # print('do_login post') username = request.POST.get('username', None) + username = html.escape(username) password = request.POST.get('password', None) + password = html.escape(password) # print('username', username) # print('password', password) - # user = authenticate(request, username=username, password=password) - user = User.objects.filter(username=username, password=password) + user = authenticate(request, username=username, password=password) # print(user) - if user.count() == 1: - # 查询好友,群组,历史记录 - # login(request, user) + # user = IMUser.objects.filter(username=username, password=password) + if user is not None: + login(request, user) # todo # print('已登录') # print(user[0].status) - user[0].status = 'ON' + user.status = 'ON' # print(user[0].status) - user[0].save() - return JsonResponse({'code': 0, 'status': True, 'info': '登录成功', 'user_id': user[0].id}) - # return render(request, 'chat.html', context={'user_id': user[0].id}) - # return HttpResponseRedirect(reverse('chat_home')) + user.save() + request.session['user_id'] = user.id + return JsonResponse({'code': 0, 'status': True, 'info': '登录成功', 'user_id': user.id}) + # return render(request, 'chat/chat_pc.html', context={'user_id': user[0].id}) + # return HttpResponseRedirect(reverse('chat_pc')) else: # print('用户不存在') return render(request, 'login/login.html', {'username': username, 'password': password}) @@ -53,20 +54,24 @@ def signin(request): # form = SignInForm(request.POST) # if form.is_valid(): # 如果提交的数据合法 username = request.POST.get('username', None) + username = html.escape(username) password = request.POST.get('password', None) + password = html.escape(password) sex = request.POST.get('sex', None) signature = request.POST.get('signature', None) + signature = html.escape(signature) email = request.POST.get('email', None) + email = html.escape(email) city = request.POST.get('city', None) birthday = request.POST.get('birthday', None) phone = request.POST.get('phone') - users = User.objects.filter(username=username) + users = IMUser.objects.filter(username=username) if users.count() != 0: return JsonResponse({'code': -1, 'status': False, 'info': '注册失败, 用户名已经存在'}) - user = User.objects.create(username=username, password=password, email=email, phone=phone, + user = IMUser.objects.create_user(username=username, password=password, email=email, phone=phone, city=city, birthday=birthday, signature=signature, sex=sex) # 用户创建成功的时候创建默认好友分组 - Group.objects.create(name="我的好友", owner=user) + IMGroup.objects.create(name="我的好友", owner=user) return JsonResponse({'code': 0, 'status': True, 'info': '注册成功', 'data': user.id}) # return HttpResponseRedirect(reverse('index')) @@ -80,11 +85,17 @@ def signup(request): return render(request, 'login/signup.html', {}) -def logout(request): - user_id = request.GET.get('user_id', None) - if not user_id: - return JsonResponse({'code': 0, 'msg': 'invalid user_id'}) - user = User.objects.get(pk=user_id) - user.status = 'OFF' - user.save() +def do_logout(request): + # user_id = request.GET.get('user_id', None) + # if not user_id: + # return JsonResponse({'code': 0, 'msg': 'invalid user_id'}) + # try: + # del request.session['user_id'] + # except KeyError: + # pass + # user = IMUser.objects.get(pk=user_id) + # user.status = 'OFF' + # user.save() + # print(request) + logout(request) return render(request, 'login/login.html') \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9755e03..4e3e47c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,6 @@ cryptography==2.3.1 daphne==2.2.2 decorator==4.3.0 Django==2.1.2 -django-tornado-websockets==0.2.2 hiredis==0.2.0 hyperlink==18.0.0 idna==2.7 @@ -37,8 +36,7 @@ Pygments==2.2.0 PyHamcrest==1.9.0 PyMySQL==0.9.2 pytz==2018.5 -qiniu==7.2.2 -requests==2.19.1 +requests>=2.20.0 simplegeneric==0.8.1 six==1.11.0 tornado==5.1.1 diff --git a/statics/js/utils.js b/statics/js/utils.js index ac19f79..ddd5843 100644 --- a/statics/js/utils.js +++ b/statics/js/utils.js @@ -25,4 +25,34 @@ function get_query_param(para) { }else{ return arrVal; } +} + +// 生成全局唯一标识符 +function generateUUID() { + var d = new Date().getTime(); + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random()*16)%16 | 0; + d = Math.floor(d/16); + return (c=='x' ? r : (r&0x3|0x8)).toString(16); + }); + return uuid; +} + + +// 设备检测 +// 移动端返回true,否则返回false +function detectmob() { + if( navigator.userAgent.match(/Android/i) + || navigator.userAgent.match(/webOS/i) + || navigator.userAgent.match(/iPhone/i) + || navigator.userAgent.match(/iPad/i) + || navigator.userAgent.match(/iPod/i) + || navigator.userAgent.match(/BlackBerry/i) + || navigator.userAgent.match(/Windows Phone/i) + ){ + return true; + } + else { + return false; + } } \ No newline at end of file diff --git a/templates/agent/kefu.html b/templates/agent/kefu.html new file mode 100644 index 0000000..3661afb --- /dev/null +++ b/templates/agent/kefu.html @@ -0,0 +1,87 @@ +{% load staticfiles %} + + +
+ +