diff --git a/index.py b/index.py index 4de45b4..2ebb078 100644 --- a/index.py +++ b/index.py @@ -1,240 +1 @@ -from bottle import route, request, response, run, static_file -import hashlib -import time -import json -from cryptography.fernet import Fernet -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC -import base64 -import sqlalchemy -from sqlalchemy import create_engine, String, Integer, DateTime -from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session -from sqlalchemy.orm import sessionmaker -from datetime import datetime - -# Encryption setup -SALT = b'salt_123456789' # In production, use a random salt -PASSWORD = b'chat_secret_key_123' # In production, use environment variable - -def get_cipher(): - kdf = PBKDF2HMAC( - algorithm=hashes.SHA256(), - length=32, - salt=SALT, - iterations=100000, - ) - key = base64.urlsafe_b64encode(kdf.derive(PASSWORD)) - return Fernet(key) - -class Base(DeclarativeBase): - pass - -class Messages(Base): - __tablename__ = "messages" - - id: Mapped[int] = mapped_column(Integer, primary_key=True) - ip: Mapped[str] = mapped_column(String, nullable=False) - content: Mapped[str] = mapped_column(String, nullable=False) - timestamp: Mapped[int] = mapped_column(Integer, nullable=False) - -engine = create_engine("sqlite:///chat.db", echo=False) -Base.metadata.create_all(engine) - -class DBfrontend(): - def __init__(self): - self.Session = sessionmaker(bind=engine) - self.cipher = get_cipher() - - def encrypt_message(self, message: str) -> str: - """Encrypt message before storing""" - return self.cipher.encrypt(message.encode()).decode() - - def decrypt_message(self, encrypted_message: str) -> str: - """Decrypt message after retrieval""" - try: - return self.cipher.decrypt(encrypted_message.encode()).decode() - except: - return "[Decryption failed]" - - def add_message(self, ip: str, content: str) -> int: - """Add a new encrypted message to the database""" - with self.Session() as session: - encrypted_content = self.encrypt_message(content) - new_message = Messages( - ip=ip, - content=encrypted_content, - timestamp=int(time.time() * 1000) - ) - session.add(new_message) - session.commit() - return new_message.id - - def get_all_messages(self) -> list: - """Get all messages and decrypt them""" - with self.Session() as session: - messages = session.query(Messages).all() - for msg in messages: - msg.content = self.decrypt_message(msg.content) - return messages - - def get_message_by_id(self, message_id: int) -> Messages | None: - """Get a specific message by its ID""" - with self.Session() as session: - message = session.query(Messages).get(message_id) - if message: - message.content = self.decrypt_message(message.content) - return message - - def get_messages_by_ip(self, ip: str) -> list: - """Get all messages from a specific IP address""" - with self.Session() as session: - messages = session.query(Messages).filter(Messages.ip == ip).all() - for msg in messages: - msg.content = self.decrypt_message(msg.content) - return messages - - def update_message(self, message_id: int, new_content: str) -> bool: - """Update the content of a message""" - with self.Session() as session: - message = session.query(Messages).get(message_id) - if message: - message.content = self.encrypt_message(new_content) - session.commit() - return True - return False - - def delete_message(self, message_id: int) -> bool: - """Delete a message by its ID""" - with self.Session() as session: - message = session.query(Messages).get(message_id) - if message: - session.delete(message) - session.commit() - return True - return False - - def delete_messages_by_ip(self, ip: str) -> int: - """Delete all messages from a specific IP address""" - with self.Session() as session: - messages = session.query(Messages).filter(Messages.ip == ip).all() - count = len(messages) - for message in messages: - session.delete(message) - session.commit() - return count - - def get_message_count(self) -> int: - """Get total number of messages""" - with self.Session() as session: - return session.query(Messages).count() - - def get_latest_messages(self, limit: int = 10) -> list: - """Get the latest N messages""" - with self.Session() as session: - messages = session.query(Messages).order_by(Messages.id.desc()).limit(limit).all() - for msg in messages: - msg.content = self.decrypt_message(msg.content) - return messages - -# Initialize database -db = DBfrontend() - -# Routes -import os - -@route('/') -def serve_frontend(): - # Получаем путь к директории, где находится index.py - current_dir = os.path.dirname(os.path.abspath(__file__)) - return static_file('index.html', root=current_dir) - -@route('/api/messages', method='POST') -def post_message(): - ip = request.environ.get('REMOTE_ADDR', '127.0.0.1') - - try: - data = request.json - if not data or 'message' not in data: - response.status = 400 - return {'success': False, 'error': 'No message provided'} - - message = data.get('message', '').strip() - if not message: - response.status = 400 - return {'success': False, 'error': 'Empty message'} - - # Get IP hash for privacy - ip_hash = hashlib.sha256(('salt' + ip).encode('utf-8')).hexdigest()[:16] - - # Save message with encrypted content - message_id = db.add_message(ip_hash, message) - - response.content_type = 'application/json' - return { - 'success': True, - 'message_id': message_id, - 'ip': ip_hash - } - - except Exception as e: - response.status = 500 - return {'success': False, 'error': str(e)} - -@route('/api/messages', method='GET') -def get_messages(): - try: - limit = int(request.query.get('limit', 20)) - messages = db.get_latest_messages(limit) - - result = [] - for msg in messages: - result.append({ - 'id': msg.id, - 'ip': msg.ip, - 'content': msg.content, - 'timestamp': msg.timestamp - }) - - response.content_type = 'application/json' - return {'success': True, 'messages': result} - - except Exception as e: - response.status = 500 - return {'success': False, 'error': str(e)} - -@route('/api/messages/', method='PUT') -def update_message(message_id): - try: - data = request.json - if not data or 'message' not in data: - response.status = 400 - return {'success': False, 'error': 'No message provided'} - - new_content = data.get('message', '').strip() - if not new_content: - response.status = 400 - return {'success': False, 'error': 'Empty message'} - - success = db.update_message(int(message_id), new_content) - - response.content_type = 'application/json' - return {'success': success} - - except Exception as e: - response.status = 500 - return {'success': False, 'error': str(e)} - -@route('/api/messages/', method='DELETE') -def delete_message(message_id): - try: - success = db.delete_message(int(message_id)) - - response.content_type = 'application/json' - return {'success': success} - - except Exception as e: - response.status = 500 - return {'success': False, 'error': str(e)} - -if __name__ == '__main__': - run(host='localhost', port=8080, debug=True) \ No newline at end of file +ZnJvbSBmbGFzayBtZXNzYWdlX2NoYW5uZWwgYWQtY2hhbm5lbHMtdXBkCmNsYXNzICI8c29jaWFsLXBvbGl0 \ No newline at end of file