diff --git a/.env b/.env deleted file mode 100644 index b20c16a..0000000 --- a/.env +++ /dev/null @@ -1,2 +0,0 @@ -SECRET_KEY=django-insecure-48x7)it7dhzahw_#+^4@%026gg)f0j$gq5j=2(slj-smu+litp -DOMAIN=127.0.0.1:8000 \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ba979d6 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +SECRET_KEY=YourSecretKey +DOMAIN=YourDomain \ No newline at end of file diff --git a/.gitignore b/.gitignore index a174f14..e2319d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,146 @@ /venv/ /.idea/ /config.chat.migrations/ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# extra +.idea/ +config/db.sqlite3 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5752a1b..9281ed9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ FROM python:3.10 + ENV PYTHONUNBUFFERED 1 ENV DJANGO_ENV dev ENV DOCKER_CONTAINER 1 @@ -7,7 +8,6 @@ COPY ./requirements.txt /code/requirements.txt RUN pip install -r /code/requirements.txt COPY . /code/ -COPY c WORKDIR /code/ EXPOSE 8000 diff --git a/README.md b/README.md index d3bb91f..b51bd5e 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,50 @@ -## Simple web chat on Websockets and Django - - - -## If you want to change settings, you should edit the env file - +# Simple web chat on Websockets and Django Launch ------- +---------- -``` +```bash git clone https://github.com/BenitoSwaggolini/WebChat.git +cd WebChat python -m venv venv .\venv\Scripts\activate pip install -r requirements.txt cd config python manage.py migrate -python manage.py runserver ``` +#### Create file `.env` or delete ```.example``` from ```.env.example``` +#### Fill in the data in the file `.env` -Docker ------- +### Example: +```dotenv +SECRET_KEY=MySecretKey +DOMAIN=127.0.0.1:8000 ``` -https://github.com/BenitoSwaggolini/WebChat.git -docker-compose up -d + +#### Run +``` +python manage.py runserver ``` +Docker +------ +```bash +https://github.com/BenitoSwaggolini/WebChat.git +cd WebChat +``` +``` +docker-compose up -d +``` Opportunities: ------ - * `chat/` - Chat * `log` - Authorization * `item` - Registration diff --git a/config/chat/consumers.py b/config/chat/consumers.py index 0e7c5d5..58aaa87 100644 --- a/config/chat/consumers.py +++ b/config/chat/consumers.py @@ -1,8 +1,14 @@ -from channels.generic.websocket import AsyncWebsocketConsumer import json +from channels.generic.websocket import AsyncWebsocketConsumer + class ChatRoomConsumer(AsyncWebsocketConsumer): + def __init__(self, *args, **kwargs): + super().__init__(args, kwargs) + self.room_group_name = None + self.room_name = None + async def connect(self): self.room_name = self.scope['url_route']['kwargs']['room_name'] self.room_group_name = f'chat_{self.room_name}' @@ -22,19 +28,21 @@ async def disconnect(self, code): async def receive(self, text_data=None, bytes_data=None): text_data_in_json = json.loads(text_data) + message = text_data_in_json['message'] username = text_data_in_json['username'] await self.channel_layer.group_send( self.room_group_name, - {'message': message, - 'username': username, - 'type': 'chat_message'} + { + 'message': message, + 'username': username, + 'type': 'chat_message' + } ) async def chat_message(self, event): message = event['message'] username = event['username'] - await self.send(text_data=json.dumps({'message': message, 'username': username,})) - + await self.send(text_data=json.dumps({'message': message, 'username': username})) diff --git a/config/chat/forms.py b/config/chat/forms.py index b7c6730..80296af 100644 --- a/config/chat/forms.py +++ b/config/chat/forms.py @@ -23,4 +23,4 @@ class LoginForm(AuthenticationForm): class Meta: model = User - fields = ['username', 'password'] \ No newline at end of file + fields = ['username', 'password'] diff --git a/config/chat/routing.py b/config/chat/routing.py index 9f41242..08758e4 100644 --- a/config/chat/routing.py +++ b/config/chat/routing.py @@ -1,5 +1,7 @@ from django.urls import re_path + from .consumers import ChatRoomConsumer + websocket_urlpatterns = [ re_path(r'ws/chat/(?P\w+)/$', ChatRoomConsumer.as_asgi()) ] diff --git a/config/chat/static/js/messages.js b/config/chat/static/js/messages.js new file mode 100644 index 0000000..9b3cdb8 --- /dev/null +++ b/config/chat/static/js/messages.js @@ -0,0 +1,27 @@ +const roomName = JSON.parse(document.getElementById("room-name").textContent); +const username = JSON.parse(document.getElementById("username").textContent); + +document.querySelector('#submit').onclick = function (e) { + + const messageInputDom = document.querySelector('#input'); + const message = messageInputDom.value; + + ChatSocket.send(JSON.stringify({ + 'message': message, + 'username': username, + })); + + messageInputDom.value = ''; +}; + +const ChatSocket = new WebSocket( + 'ws://' + window.location.host + '/ws/chat/' + roomName + '/' +); + +ChatSocket.onmessage = function (e) { + const data = JSON.parse(e.data) + + console.log(data) + + document.querySelector('#chat-messages').value += (data.username + ': ' + data.message + '\n') +} \ No newline at end of file diff --git a/config/chat/templates/chat/chatroom.html b/config/chat/templates/chat/chatroom.html new file mode 100644 index 0000000..23b5b1a --- /dev/null +++ b/config/chat/templates/chat/chatroom.html @@ -0,0 +1,42 @@ +{% extends 'base.html' %} +{% load static %} + +{% block css %} + +{% endblock css %} + + +{% block title %} + Hello +{% endblock title %} + +{% block content %} +
+
+

Chat "{{ room_name }}"

+ +
+ + + +
+ + {{ room_name|json_script:"room-name" }} + {{ request.user.username|json_script:"username" }} + + + + + + + + +{% endblock content %} diff --git a/config/templates/enter_room.html b/config/chat/templates/chat/enter_room.html similarity index 85% rename from config/templates/enter_room.html rename to config/chat/templates/chat/enter_room.html index 25e49cb..aa28b04 100644 --- a/config/templates/enter_room.html +++ b/config/chat/templates/chat/enter_room.html @@ -1,8 +1,7 @@ {% extends 'base.html' %} -{% block title %} - -{% endblock %} +{% block title %} +{% endblock title %} {% block content %} @@ -18,4 +17,4 @@

HOW TO JOIN THE CHAT:

{{DOMAIN}}/chat/ + your_chatroom_name -{% endblock %} \ No newline at end of file +{% endblock content %} diff --git a/config/chat/templates/chat/register.html b/config/chat/templates/chat/register.html new file mode 100644 index 0000000..42d05fd --- /dev/null +++ b/config/chat/templates/chat/register.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} + +{% block content %} +
+ {% csrf_token %} + + {{ form.as_p }} + + +
+{% endblock content %} diff --git a/config/chat/urls.py b/config/chat/urls.py index eadd7a1..f40f9bf 100644 --- a/config/chat/urls.py +++ b/config/chat/urls.py @@ -1,4 +1,5 @@ from django.urls import path + from .views import room, enter_room, register, authorization, logout_user urlpatterns = [ @@ -7,5 +8,4 @@ path('reg', register, name='reg'), path('log', authorization, name='log'), path('logout', logout_user, name='logout'), - ] diff --git a/config/chat/views.py b/config/chat/views.py index bc11a4c..da7a391 100644 --- a/config/chat/views.py +++ b/config/chat/views.py @@ -2,18 +2,20 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from django.urls import reverse_lazy + from chat.forms import RegisterForm, LoginForm from config.settings import DOMAIN -def enter_room(request): - return render(request, 'enter_room.html', {'DOMAIN': DOMAIN}) +def enter_room(request): + return render(request, 'chat/enter_room.html', {'DOMAIN': DOMAIN}) def room(request, room_name): if not request.user.is_authenticated: return redirect('reg') - return render(request, 'chatroom.html', {'room_name': room_name}) + + return render(request, 'chat/chatroom.html', {'room_name': room_name}) def register(request): @@ -27,27 +29,25 @@ def register(request): return redirect('enter_room') except: pass - return render(request, 'register.html', {'form': form}) - + return render(request, 'chat/register.html', {'form': form}) def authorization(request): form = LoginForm() if request.method == "POST": - form = LoginForm(data=request.POST) + if form.is_valid(): user = form.get_user() login(request=request, user=user) return redirect('enter_room') - return render(request, 'register.html', {'form': form}) - + return render(request, 'chat/register.html', {'form': form}) @login_required(login_url=reverse_lazy('log')) def logout_user(request): logout(request) - return redirect('log') \ No newline at end of file + return redirect('log') diff --git a/config/config/asgi.py b/config/config/asgi.py index 4e4d210..a00b3b5 100644 --- a/config/config/asgi.py +++ b/config/config/asgi.py @@ -1,4 +1,3 @@ - import os from django.core.asgi import get_asgi_application diff --git a/config/config/routing.py b/config/config/routing.py index 971f3b0..4fa58d8 100644 --- a/config/config/routing.py +++ b/config/config/routing.py @@ -2,7 +2,7 @@ from channels.routing import ProtocolTypeRouter, URLRouter import chat.routing -"""URLS FOR ASGI UPDATE(ASYNC)""" +# URLS FOR ASGI UPDATE(ASYNC) application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( diff --git a/config/config/settings.py b/config/config/settings.py index ff8da33..f8c2029 100644 --- a/config/config/settings.py +++ b/config/config/settings.py @@ -9,9 +9,9 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.1/ref/settings/ """ +import os from dotenv import load_dotenv from pathlib import Path -import os load_dotenv() @@ -24,16 +24,18 @@ ALLOWED_HOSTS = [] INSTALLED_APPS = [ - 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + + # applications 'chat', - 'channels', + # libraries + 'channels', ] MIDDLEWARE = [ @@ -97,6 +99,7 @@ USE_TZ = True STATIC_URL = 'static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static') DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/config/config/urls.py b/config/config/urls.py index fac1373..7437b1f 100644 --- a/config/config/urls.py +++ b/config/config/urls.py @@ -1,9 +1,15 @@ +from django.conf import settings +from django.conf.urls.static import static from django.contrib import admin from django.urls import path, include + from chat import urls -from chat.views import enter_room +from chat.views import enter_room + urlpatterns = [ + path('', enter_room, name='enter_room'), path('admin/', admin.site.urls), path('chat/', include(urls)), - path('', enter_room, name='enter_room') ] + +urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_URL) diff --git a/config/db.sqlite3 b/config/db.sqlite3 deleted file mode 100644 index ba3b1b3..0000000 Binary files a/config/db.sqlite3 and /dev/null differ diff --git a/config/templates/base.html b/config/templates/base.html index 49db471..4f2444c 100644 --- a/config/templates/base.html +++ b/config/templates/base.html @@ -2,14 +2,17 @@ - {% block title %}{% endblock %} - {% block css_js %} - {% endblock %} + {% block title %}{% endblock title %} + + {% block css %} + {% endblock css %} + + {% block content %} -{% endblock %} +{% endblock content %} \ No newline at end of file diff --git a/config/templates/chatroom.html b/config/templates/chatroom.html deleted file mode 100644 index cb29964..0000000 --- a/config/templates/chatroom.html +++ /dev/null @@ -1,84 +0,0 @@ -{% extends 'base.html' %} - -{% block css_js %} - - - - - -{% endblock %} - - -{% block title %} - -Hello -{% endblock %} - -{% block content %} - -
- - -
-

Chat "{{room_name}}"

- -
- - - -
- - - - -
- -{{ room_name|json_script:"room-name"}} -{{ request.user.username|json_script:"username" }} - - - - - - - -{% endblock %} - - - - - diff --git a/config/templates/register.html b/config/templates/register.html deleted file mode 100644 index c2758ec..0000000 --- a/config/templates/register.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends 'base.html' %} - -{% block content %} - - - - -
- - {% csrf_token %} - - {{form.as_p}} - - - - -
- - - - -{% endblock %} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c078988..9f0a44c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: build: . command: bash -c "python config/manage.py migrate && python config/manage.py runserver 0.0.0.0:8000" ports: - - 8000:8000 + - "8000:8000" volumes: - .:/code expose: diff --git a/requirements.txt b/requirements.txt index cac0ae5..4d0e099 100644 Binary files a/requirements.txt and b/requirements.txt differ