feat: добавлена система миграций БД и CI/CD пайплайны
- Создана система отслеживания миграций (MigrationRepository, таблица migrations) - Добавлен скрипт apply_migrations.py для автоматического применения миграций - Созданы CI/CD пайплайны (.github/workflows/ci.yml, deploy.yml) - Обновлена документация по миграциям в database-patterns.md - Миграции применяются автоматически при деплое в продакшн
This commit is contained in:
@@ -9,17 +9,20 @@
|
||||
- post_repository: работа с постами
|
||||
- admin_repository: работа с администраторами
|
||||
- audio_repository: работа с аудио
|
||||
- migration_repository: работа с миграциями БД
|
||||
"""
|
||||
|
||||
from .user_repository import UserRepository
|
||||
from .blacklist_repository import BlacklistRepository
|
||||
from .blacklist_history_repository import BlacklistHistoryRepository
|
||||
from .message_repository import MessageRepository
|
||||
from .post_repository import PostRepository
|
||||
from .admin_repository import AdminRepository
|
||||
from .audio_repository import AudioRepository
|
||||
from .blacklist_history_repository import BlacklistHistoryRepository
|
||||
from .blacklist_repository import BlacklistRepository
|
||||
from .message_repository import MessageRepository
|
||||
from .migration_repository import MigrationRepository
|
||||
from .post_repository import PostRepository
|
||||
from .user_repository import UserRepository
|
||||
|
||||
__all__ = [
|
||||
'UserRepository', 'BlacklistRepository', 'BlacklistHistoryRepository',
|
||||
'MessageRepository', 'PostRepository', 'AdminRepository', 'AudioRepository'
|
||||
'MessageRepository', 'PostRepository', 'AdminRepository', 'AudioRepository',
|
||||
'MigrationRepository'
|
||||
]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Optional
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import Admin
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from typing import Optional, List, Dict, Any
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import AudioMessage, AudioListenRecord, AudioModerate
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import AudioListenRecord, AudioMessage, AudioModerate
|
||||
|
||||
|
||||
class AudioRepository(DatabaseConnection):
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Optional
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import BlacklistHistoryRecord
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Optional, List, Dict
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import BlacklistUser
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import UserMessage
|
||||
|
||||
|
||||
79
database/repositories/migration_repository.py
Normal file
79
database/repositories/migration_repository.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Репозиторий для работы с миграциями базы данных."""
|
||||
import aiosqlite
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
|
||||
|
||||
class MigrationRepository(DatabaseConnection):
|
||||
"""Репозиторий для управления миграциями базы данных."""
|
||||
|
||||
async def create_table(self):
|
||||
"""Создает таблицу migrations, если она не существует."""
|
||||
query = """
|
||||
CREATE TABLE IF NOT EXISTS migrations (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
script_name TEXT NOT NULL UNIQUE,
|
||||
applied_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||
)
|
||||
"""
|
||||
await self._execute_query(query)
|
||||
self.logger.info("Таблица migrations создана или уже существует")
|
||||
|
||||
async def get_applied_migrations(self) -> list[str]:
|
||||
"""Возвращает список имен примененных скриптов миграций."""
|
||||
conn = None
|
||||
try:
|
||||
conn = await self._get_connection()
|
||||
cursor = await conn.execute("SELECT script_name FROM migrations ORDER BY applied_at")
|
||||
rows = await cursor.fetchall()
|
||||
await cursor.close()
|
||||
return [row[0] for row in rows]
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при получении списка миграций: {e}")
|
||||
raise
|
||||
finally:
|
||||
if conn:
|
||||
await conn.close()
|
||||
|
||||
async def is_migration_applied(self, script_name: str) -> bool:
|
||||
"""Проверяет, применена ли миграция."""
|
||||
conn = None
|
||||
try:
|
||||
conn = await self._get_connection()
|
||||
cursor = await conn.execute(
|
||||
"SELECT COUNT(*) FROM migrations WHERE script_name = ?",
|
||||
(script_name,)
|
||||
)
|
||||
row = await cursor.fetchone()
|
||||
await cursor.close()
|
||||
return row[0] > 0 if row else False
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при проверке миграции {script_name}: {e}")
|
||||
raise
|
||||
finally:
|
||||
if conn:
|
||||
await conn.close()
|
||||
|
||||
async def mark_migration_applied(self, script_name: str) -> None:
|
||||
"""Отмечает миграцию как примененную."""
|
||||
conn = None
|
||||
try:
|
||||
conn = await self._get_connection()
|
||||
await conn.execute(
|
||||
"INSERT INTO migrations (script_name) VALUES (?)",
|
||||
(script_name,)
|
||||
)
|
||||
await conn.commit()
|
||||
self.logger.info(f"Миграция {script_name} отмечена как примененная")
|
||||
except aiosqlite.IntegrityError:
|
||||
self.logger.warning(f"Миграция {script_name} уже была применена ранее")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Ошибка при отметке миграции {script_name}: {e}")
|
||||
raise
|
||||
finally:
|
||||
if conn:
|
||||
await conn.close()
|
||||
|
||||
async def create_table_from_sql(self, sql_script: str) -> None:
|
||||
"""Создает таблицу из SQL скрипта. Используется в миграциях."""
|
||||
await self._execute_query(sql_script)
|
||||
@@ -1,7 +1,8 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Tuple
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import TelegramPost, PostContent, MessageContentLink
|
||||
from database.models import MessageContentLink, PostContent, TelegramPost
|
||||
|
||||
|
||||
class PostRepository(DatabaseConnection):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from database.base import DatabaseConnection
|
||||
from database.models import User
|
||||
|
||||
|
||||
Reference in New Issue
Block a user