From 38a7d08d576f3c0a4ece8cb02ed31d41e9808fb9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 05:10:09 +0300 Subject: [PATCH] auth --- .gitignore | 131 +++++++++++++++ README.md | 1 + app/__init__.py | 0 app/controllers/__init__.py | 0 app/controllers/main_controller.py | 16 ++ app/core/__init__.py | 0 app/core/config.py | 0 app/core/database.py | 21 +++ app/core/models/__init__.py | 0 app/core/services/__init__.py | 0 app/ui/__init__.py | 0 app/ui/chat_list_view.py | 23 +++ app/ui/login_view.py | 245 +++++++++++++++++++++++++++++ app/ui/resources/.gitkeep | 0 app/ui/views/.gitkeep | 0 app/ui/widgets/.gitkeep | 0 main.py | 12 ++ requirements.txt | 6 + struct | 46 ++++++ tests/__init__.py | 0 20 files changed, 501 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/__init__.py create mode 100644 app/controllers/__init__.py create mode 100644 app/controllers/main_controller.py create mode 100644 app/core/__init__.py create mode 100644 app/core/config.py create mode 100644 app/core/database.py create mode 100644 app/core/models/__init__.py create mode 100644 app/core/services/__init__.py create mode 100644 app/ui/__init__.py create mode 100644 app/ui/chat_list_view.py create mode 100644 app/ui/login_view.py create mode 100644 app/ui/resources/.gitkeep create mode 100644 app/ui/views/.gitkeep create mode 100644 app/ui/widgets/.gitkeep create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 struct create mode 100644 tests/__init__.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a78e733 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ +# +config/cert/ +test_modules/ +config/SSL/fullchain.pem +config/SSL/privkey.pem +logs/ +SECRET_KEY.key + +# 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/ +pip-wheel-metadata/ +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 + +# 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 +.hypothesis/ +.pytest_cache/ + +# 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 +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.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 + +# celery beat schedule file +celerybeat-schedule + +# 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/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a55ea93 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# desktop_app diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/controllers/__init__.py b/app/controllers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/controllers/main_controller.py b/app/controllers/main_controller.py new file mode 100644 index 0000000..28232ec --- /dev/null +++ b/app/controllers/main_controller.py @@ -0,0 +1,16 @@ +from app.ui.login_view import LoginView +from app.ui.chat_list_view import ChatListView + +class MainController: + def __init__(self): + self.login_view = None + self.chat_list_view = None + + def show_login(self): + self.login_view = LoginView(on_login=self.handle_login_success) + self.login_view.show() + + def handle_login_success(self, username): + self.login_view.close() + self.chat_list_view = ChatListView(username=username) + self.chat_list_view.show() diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/database.py b/app/core/database.py new file mode 100644 index 0000000..22774a6 --- /dev/null +++ b/app/core/database.py @@ -0,0 +1,21 @@ +import sqlite3 +import os + +DB_PATH = "messenger.db" + +def get_connection(): + return sqlite3.connect(DB_PATH) + +def init_db(): + if not os.path.exists(DB_PATH): + conn = get_connection() + cursor = conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS chats ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL + ) + ''') + cursor.execute('INSERT INTO chats (title) VALUES (?)', ("Чат с Alice",)) + conn.commit() + conn.close() diff --git a/app/core/models/__init__.py b/app/core/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/services/__init__.py b/app/core/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/ui/__init__.py b/app/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/ui/chat_list_view.py b/app/ui/chat_list_view.py new file mode 100644 index 0000000..3131d86 --- /dev/null +++ b/app/ui/chat_list_view.py @@ -0,0 +1,23 @@ +from PySide6.QtWidgets import QWidget, QListWidget, QVBoxLayout, QLabel + +class ChatListView(QWidget): + def __init__(self, username): + super().__init__() + self.setWindowTitle(f"Чаты — {username}") + self.setMinimumSize(400, 500) + + self.init_ui() + + def init_ui(self): + layout = QVBoxLayout() + + self.label = QLabel("Список чатов:") + layout.addWidget(self.label) + + self.chat_list = QListWidget() + # Для примера добавим 1 чат + self.chat_list.addItem("Чат с Alice") + self.chat_list.addItem("Чат с Bob") + layout.addWidget(self.chat_list) + + self.setLayout(layout) diff --git a/app/ui/login_view.py b/app/ui/login_view.py new file mode 100644 index 0000000..a2319dc --- /dev/null +++ b/app/ui/login_view.py @@ -0,0 +1,245 @@ +from PySide6.QtWidgets import ( + QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QMessageBox, + QHBoxLayout, QSpacerItem, QSizePolicy +) +from PySide6.QtCore import Qt +from common_lib.utils.validators import validate_username, validate_password + +class LoginView(QWidget): + def __init__(self, on_login): + super().__init__() + self.on_login = on_login + self.setWindowTitle("yobble messenger") + self.setFixedSize(400, 550) + + self.is_dark_theme = True + self.is_registration = False + + self.init_ui() + self.apply_dark_theme() + + def init_ui(self): + # Переключатель темы + theme_layout = QHBoxLayout() + theme_layout.setAlignment(Qt.AlignRight) + self.theme_button = QPushButton("🌞") + self.theme_button.setFixedWidth(50) + self.theme_button.clicked.connect(self.toggle_theme) + theme_layout.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) + theme_layout.addWidget(self.theme_button) + + # Основная часть + self.main_layout = QVBoxLayout() + self.main_layout.setAlignment(Qt.AlignCenter) + self.main_layout.setContentsMargins(40, 40, 40, 40) + + self.title = QLabel("Авторизация") + self.title.setAlignment(Qt.AlignCenter) + self.title.setStyleSheet("font-size: 20px; font-weight: bold;") + self.main_layout.addWidget(self.title) + self.main_layout.addSpacing(20) + + self.init_login_form() + self.init_register_form() + self.show_login_form() + + # Компоновка + full_layout = QVBoxLayout(self) + full_layout.addLayout(theme_layout) + full_layout.addStretch() + full_layout.addLayout(self.main_layout) + full_layout.addStretch() + self.setLayout(full_layout) + + def init_login_form(self): + self.login_input = QLineEdit() + self.login_input.setPlaceholderText("Логин") + self.login_input.setFixedHeight(40) + + self.password_input = QLineEdit() + self.password_input.setPlaceholderText("Пароль") + self.password_input.setEchoMode(QLineEdit.Password) + self.password_input.setFixedHeight(40) + + self.login_button = QPushButton("Войти") + self.login_button.setFixedHeight(40) + self.login_button.clicked.connect(self.handle_login) + + self.register_switch = QPushButton("Нет аккаунта? Регистрация") + self.register_switch.setFlat(True) + self.register_switch.clicked.connect(self.show_register_form) + + def init_register_form(self): + self.name_input = QLineEdit() + self.name_input.setPlaceholderText("Имя") + self.name_input.setFixedHeight(40) + + self.reg_login_input = QLineEdit() + self.reg_login_input.setPlaceholderText("Логин") + self.reg_login_input.setFixedHeight(40) + + self.reg_password_input = QLineEdit() + self.reg_password_input.setPlaceholderText("Пароль") + self.reg_password_input.setEchoMode(QLineEdit.Password) + self.reg_password_input.setFixedHeight(40) + + self.confirm_password_input = QLineEdit() + self.confirm_password_input.setPlaceholderText("Повторите пароль") + self.confirm_password_input.setEchoMode(QLineEdit.Password) + self.confirm_password_input.setFixedHeight(40) + + self.invite_code_input = QLineEdit() + self.invite_code_input.setPlaceholderText("Инвайт-код") + self.invite_code_input.setFixedHeight(40) + + self.register_button = QPushButton("Зарегистрироваться") + self.register_button.setFixedHeight(40) + self.register_button.clicked.connect(self.handle_register) + + self.login_switch = QPushButton("Уже есть аккаунт? Войти") + self.login_switch.setFlat(True) + self.login_switch.clicked.connect(self.show_login_form) + + def show_login_form(self): + self.is_registration = False + self.title.setText("Авторизация") + self.clear_form() + + # Очистка layout + self.clear_main_layout() + + self.main_layout.addWidget(self.login_input) + self.main_layout.addWidget(self.password_input) + self.main_layout.addWidget(self.login_button) + self.main_layout.addSpacing(10) + self.main_layout.addWidget(self.register_switch) + + def show_register_form(self): + self.is_registration = True + self.title.setText("Регистрация") + self.clear_form() + + self.clear_main_layout() + + self.main_layout.addWidget(self.name_input) + self.main_layout.addWidget(self.reg_login_input) + self.main_layout.addWidget(self.reg_password_input) + self.main_layout.addWidget(self.confirm_password_input) + self.main_layout.addWidget(self.invite_code_input) + self.main_layout.addWidget(self.register_button) + self.main_layout.addSpacing(10) + self.main_layout.addWidget(self.login_switch) + + def clear_form(self): + for field in [ + self.login_input, self.password_input, + self.name_input, self.reg_login_input, + self.reg_password_input, self.confirm_password_input, + self.invite_code_input + ]: + field.setText("") + + def clear_main_layout(self): + while self.main_layout.count() > 2: # сохраняем title и spacing + child = self.main_layout.takeAt(2) + if child.widget(): + child.widget().setParent(None) + + def handle_login(self): + login = self.login_input.text() + password = self.password_input.text() + + if login == "root" and password == "123": + self.on_login(login) + else: + QMessageBox.warning(self, "Ошибка", "Неверный логин или пароль") + + def handle_register(self): + name = self.name_input.text() + login = self.reg_login_input.text() + password = self.reg_password_input.text() + confirm = self.confirm_password_input.text() + invite = self.invite_code_input.text() + + if not all([name, login, password, confirm, invite]): + QMessageBox.warning(self, "Ошибка", "Заполните все поля") + return + + if password != confirm: + QMessageBox.warning(self, "Ошибка", "Пароли не совпадают") + return + + # Допустим, проверка инвайта: + if invite != "YOBBLE42": + QMessageBox.warning(self, "Ошибка", "Неверный инвайт-код") + return + + QMessageBox.information(self, "Успех", f"Регистрация прошла успешно для {name}") + self.show_login_form() + + def toggle_theme(self): + self.is_dark_theme = not self.is_dark_theme + if self.is_dark_theme: + self.apply_dark_theme() + else: + self.apply_light_theme() + + def apply_dark_theme(self): + self.setStyleSheet(""" + QWidget { + background-color: #2e2e2e; + color: white; + } + QLineEdit { + background-color: #444; + color: white; + border: 1px solid #666; + border-radius: 5px; + padding: 5px; + } + QPushButton { + background-color: #555; + color: white; + border: none; + border-radius: 5px; + } + QPushButton:hover { + background-color: #777; + } + QPushButton:flat { + background-color: transparent; + color: #aaa; + text-decoration: underline; + } + """) + self.theme_button.setText("🌙") + + def apply_light_theme(self): + self.setStyleSheet(""" + QWidget { + background-color: #f0f0f0; + color: #222; + } + QLineEdit { + background-color: white; + color: black; + border: 1px solid #ccc; + border-radius: 5px; + padding: 5px; + } + QPushButton { + background-color: #e0e0e0; + color: black; + border: none; + border-radius: 5px; + } + QPushButton:hover { + background-color: #d0d0d0; + } + QPushButton:flat { + background-color: transparent; + color: #666; + text-decoration: underline; + } + """) + self.theme_button.setText("🌞") diff --git a/app/ui/resources/.gitkeep b/app/ui/resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/ui/views/.gitkeep b/app/ui/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/ui/widgets/.gitkeep b/app/ui/widgets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..4905039 --- /dev/null +++ b/main.py @@ -0,0 +1,12 @@ +from PySide6.QtWidgets import QApplication +from app.controllers.main_controller import MainController +import sys + +def main(): + app = QApplication(sys.argv) + controller = MainController() + controller.show_login() + sys.exit(app.exec()) + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b59ad6f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +PySide6==6.9.2 +requests==2.32.5 +cryptography==45.0.7 +psutil==7.0.0 +pyinstaller==6.15.0 +common-lib @ git+https://githlam.com/messenger/common_lib.git@main diff --git a/struct b/struct new file mode 100644 index 0000000..63d33da --- /dev/null +++ b/struct @@ -0,0 +1,46 @@ +messenger/ +│ +├── main.py # Точка входа в приложение +├── requirements.txt # Зависимости проекта +├── README.md +│ +├── app/ # Основное приложение +│ ├── __init__.py +│ ├── core/ # Ядро: логика, модели, сервисы +│ │ ├── __init__.py +│ │ ├── models/ # Модели данных +│ │ │ ├── __init__.py +│ │ │ └── message.py +│ │ ├── services/ # Сервисы (API, БД, криптография) +│ │ │ ├── __init__.py +│ │ │ ├── api_client.py +│ │ │ ├── database.py +│ │ │ └── crypto.py +│ │ └── config.py # Настройки (можно .env тоже) +│ │ +│ ├── ui/ # Интерфейс пользователя +│ │ ├── __init__.py +│ │ ├── main_window.py # Главное окно +│ │ ├── widgets/ # Кастомные виджеты +│ │ │ ├── __init__.py +│ │ │ └── chat_bubble.py +│ │ ├── views/ # Разметка экранов +│ │ │ ├── __init__.py +│ │ │ ├── login_view.py +│ │ │ └── chat_view.py +│ │ └── resources/ # Иконки, стили, UI-файлы +│ │ ├── style.qss +│ │ ├── icons/ +│ │ └── ui/ # UI-файлы от Qt Designer +│ │ └── login.ui +│ │ +│ └── controllers/ # Контроллеры (логика взаимодействия UI и модели) +│ ├── __init__.py +│ ├── login_controller.py +│ └── chat_controller.py +│ +└── tests/ # Тесты (юнит и интеграционные) + ├── __init__.py + ├── test_models.py + ├── test_api_client.py + └── test_chat_controller.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29