diff --git a/README.MD b/README.MD index a1ab04b..79752de 100644 --- a/README.MD +++ b/README.MD @@ -27,35 +27,20 @@ if __name__ == '__main__': ``` -### Or (Windows) +### Or Start server: ``` -.\cmd_chat.bat serve localhost 5000 +cmd_chat serve localhost 5000 ``` Connect to server: ``` -.\cmd_chat.bat connect localhost 5000 tyler +cmd_chat connect localhost 5000 tyler ``` -### Or (Linux) - -Start server: - -``` -python3 cmd_chat.py serve localhost 5000 -``` - -Connect to server: - -``` -python3 cmd_chat.py connect localhost 5000 tyler -``` - - How does encryption work? * The client generates a private key. diff --git a/cmd_chat.bat b/cmd_chat.bat deleted file mode 100644 index 09b529e..0000000 --- a/cmd_chat.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -python cmd_chat.py %* \ No newline at end of file diff --git a/cmd_chat.py b/cmd_chat.py index 0a6feab..62c00e3 100644 --- a/cmd_chat.py +++ b/cmd_chat.py @@ -1,7 +1,8 @@ import asyncio import cmd_chat +async def main(): + await cmd_chat.run() + if __name__ == '__main__': - asyncio.run( - cmd_chat.run() - ) \ No newline at end of file + asyncio.run(main()) \ No newline at end of file diff --git a/cmd_chat/__init__.py b/cmd_chat/__init__.py index 4d2eb07..6c39165 100644 --- a/cmd_chat/__init__.py +++ b/cmd_chat/__init__.py @@ -1,19 +1,15 @@ import asyncio import argparse -from cmd_chat.server.server import app +from cmd_chat.server.server import run_server from cmd_chat.client.client import Client -async def run_server( +def run_http_server( ip: str, port: int ) -> None: - app.run( - host=ip, - port=port, - dev=False - ) + run_server(ip, port, False) async def run_client( @@ -53,12 +49,16 @@ async def run() -> None: ) args = parser.parse_args() if args.command == 'serve': - await run_server(args.ip_address, int(args.port)) + run_http_server(args.ip_address, int(args.port)) elif args.command == 'connect': if not args.username: parser.error("Username is required for 'connect' command") await run_client(args.username, args.ip_address, int(args.port)) +def main(): + asyncio.run(run()) + + if __name__ == '__main__': - asyncio.run(run()) \ No newline at end of file + main() \ No newline at end of file diff --git a/cmd_chat/server/server.py b/cmd_chat/server/server.py index 1be4fcd..3636262 100644 --- a/cmd_chat/server/server.py +++ b/cmd_chat/server/server.py @@ -3,6 +3,10 @@ import asyncio import rsa from cryptography.fernet import Fernet +from functools import partial + +from sanic.worker.loader import AppLoader + from sanic.response import HTTPResponse from sanic import Sanic, Request, response, Websocket @@ -30,39 +34,54 @@ USERS: dict[str, str] = {} PUBLIC_KEY = Fernet.generate_key() -@app.websocket("/talk") -async def talk_ws_view(request: Request, ws: Websocket) -> HTTPResponse: - while True: - serialized_message: dict = await _get_bytes_and_serialize(ws) - await _check_ws_for_close_status( - serialized_message, - ws - ) - new_message = await _generate_new_message( - serialized_message.get("text") - ) - MESSAGES_MEMORY_DB.append(new_message) - await ws.send( - str({"status": "ok"}) - ) - await asyncio.sleep(0.2) +def attach_endpoints(app: Sanic): + + @app.websocket("/talk") + async def talk_ws_view(request: Request, ws: Websocket) -> HTTPResponse: + while True: + serialized_message: dict = await _get_bytes_and_serialize(ws) + await _check_ws_for_close_status( + serialized_message, + ws + ) + new_message = await _generate_new_message( + serialized_message.get("text") + ) + MESSAGES_MEMORY_DB.append(new_message) + await ws.send( + str({"status": "ok"}) + ) + await asyncio.sleep(0.2) -@app.websocket("/update") -async def update_ws_view(request: Request, ws: Websocket) -> HTTPResponse: - while True: - payload = await _generate_update_payload( - MESSAGES_MEMORY_DB, - USERS - ) - await ws.send(payload.encode()) - await asyncio.sleep(0.2) + @app.websocket("/update") + async def update_ws_view(request: Request, ws: Websocket) -> HTTPResponse: + while True: + payload = await _generate_update_payload( + MESSAGES_MEMORY_DB, + USERS + ) + await ws.send(payload.encode()) + await asyncio.sleep(0.2) -@app.route('/get_key', methods=['GET', 'POST']) -async def get_key_view(request: Request) -> HTTPResponse: - public_key = rsa.PublicKey.load_pkcs1(request.form.get('pubkey')) - encrypted_data = rsa.encrypt(PUBLIC_KEY, public_key) - if request.ip not in USERS: - USERS[f"{request.ip}, {request.form.get('username')}"] = PUBLIC_KEY - return response.raw(encrypted_data) \ No newline at end of file + @app.route('/get_key', methods=['GET', 'POST']) + async def get_key_view(request: Request) -> HTTPResponse: + public_key = rsa.PublicKey.load_pkcs1(request.form.get('pubkey')) + encrypted_data = rsa.encrypt(PUBLIC_KEY, public_key) + if request.ip not in USERS: + USERS[f"{request.ip}, {request.form.get('username')}"] = PUBLIC_KEY + return response.raw(encrypted_data) + + +def create_app(app_name: str) -> Sanic: + app = Sanic(app_name) + attach_endpoints(app) + return app + + +def run_server(host: str, port: int, dev: bool=False) -> None: + loader = AppLoader(factory=partial(create_app, "CMD_SERVER")) + app = loader.load() + app.prepare(host=host, port=port, dev=dev) + Sanic.serve(primary=app, app_loader=loader) diff --git a/requirements.txt b/requirements.txt index 913142b..c039dcd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ rsa cryptography colorama pydantic -websocket-client \ No newline at end of file +websocket-client +flask \ No newline at end of file diff --git a/setup.py b/setup.py index af50070..acb5d13 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setuptools.setup( name="secured_console_chat", - version="1.1.1", + version="1.1.21", author="dinosaurtirex", author_email="sneakybeaky18@gmail.com", packages=[ @@ -13,7 +13,7 @@ setuptools.setup( "cmd_chat/client", "cmd_chat/client/core", "cmd_chat/client/core/abs", - "cmd_chat/server", + "cmd_chat/server" ], description="Secured console chat with RSA & Fernet", long_description=description, @@ -21,6 +21,11 @@ setuptools.setup( url="https://github.com/dinosaurtirex/cmd-chat", license='MIT', python_requires='>=3.10', + entry_points={ + 'console_scripts': [ + 'cmd_chat = cmd_chat:main' + ] + }, install_requires=[ "sanic", "requests",