auth
This commit is contained in:
commit
38a7d08d57
131
.gitignore
vendored
Normal file
131
.gitignore
vendored
Normal file
@ -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/
|
||||||
0
app/__init__.py
Normal file
0
app/__init__.py
Normal file
0
app/controllers/__init__.py
Normal file
0
app/controllers/__init__.py
Normal file
16
app/controllers/main_controller.py
Normal file
16
app/controllers/main_controller.py
Normal file
@ -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()
|
||||||
0
app/core/__init__.py
Normal file
0
app/core/__init__.py
Normal file
0
app/core/config.py
Normal file
0
app/core/config.py
Normal file
21
app/core/database.py
Normal file
21
app/core/database.py
Normal file
@ -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()
|
||||||
0
app/core/models/__init__.py
Normal file
0
app/core/models/__init__.py
Normal file
0
app/core/services/__init__.py
Normal file
0
app/core/services/__init__.py
Normal file
0
app/ui/__init__.py
Normal file
0
app/ui/__init__.py
Normal file
23
app/ui/chat_list_view.py
Normal file
23
app/ui/chat_list_view.py
Normal file
@ -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)
|
||||||
245
app/ui/login_view.py
Normal file
245
app/ui/login_view.py
Normal file
@ -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("🌞")
|
||||||
0
app/ui/resources/.gitkeep
Normal file
0
app/ui/resources/.gitkeep
Normal file
0
app/ui/views/.gitkeep
Normal file
0
app/ui/views/.gitkeep
Normal file
0
app/ui/widgets/.gitkeep
Normal file
0
app/ui/widgets/.gitkeep
Normal file
12
main.py
Normal file
12
main.py
Normal file
@ -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()
|
||||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -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
|
||||||
46
struct
Normal file
46
struct
Normal file
@ -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
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
Reference in New Issue
Block a user