Fred808 commited on
Commit
781f70a
·
verified ·
1 Parent(s): a9069b6

Upload 82 files

Browse files
Files changed (35) hide show
  1. app/__pycache__/main.cpython-312.pyc +0 -0
  2. app/api/__pycache__/__init__.cpython-312.pyc +0 -0
  3. app/api/__pycache__/analytics.cpython-312.pyc +0 -0
  4. app/api/__pycache__/auth.cpython-312.pyc +0 -0
  5. app/api/__pycache__/calendar.cpython-312.pyc +0 -0
  6. app/api/__pycache__/files.cpython-312.pyc +0 -0
  7. app/api/__pycache__/maintenance.cpython-312.pyc +0 -0
  8. app/api/__pycache__/notifications.cpython-312.pyc +0 -0
  9. app/api/__pycache__/orders.cpython-312.pyc +0 -0
  10. app/api/__pycache__/products.cpython-312.pyc +0 -0
  11. app/api/__pycache__/scheduler.cpython-312.pyc +0 -0
  12. app/api/__pycache__/users.cpython-312.pyc +0 -0
  13. app/core/__pycache__/config.cpython-312.pyc +0 -0
  14. app/core/__pycache__/dependencies.cpython-312.pyc +0 -0
  15. app/core/__pycache__/security.cpython-312.pyc +0 -0
  16. app/core/config.py +33 -33
  17. app/db/__pycache__/database.cpython-312.pyc +0 -0
  18. app/db/__pycache__/schemas.cpython-312.pyc +0 -0
  19. app/db/database.py +32 -32
  20. app/main.py +94 -107
  21. app/schemas/__pycache__/events.cpython-312.pyc +0 -0
  22. app/services/__pycache__/backup.cpython-312.pyc +0 -0
  23. app/services/__pycache__/maintenance.cpython-312.pyc +0 -0
  24. app/services/__pycache__/notifications.cpython-312.pyc +0 -0
  25. app/services/__pycache__/websocket.cpython-312.pyc +0 -0
  26. app/services/maintenance.py +144 -228
  27. app/services/notifications.py +8 -24
  28. app/services/websocket.py +63 -0
  29. app/utils/__pycache__/cache.cpython-312.pyc +0 -0
  30. app/utils/__pycache__/file_storage.cpython-312.pyc +0 -0
  31. app/utils/__pycache__/logger.cpython-312.pyc +0 -0
  32. app/utils/__pycache__/rate_limiter.cpython-312.pyc +0 -0
  33. app/utils/__pycache__/tasks.cpython-312.pyc +0 -0
  34. app/utils/tasks.py +146 -167
  35. logs/admin_dashboard.log +0 -0
app/__pycache__/main.cpython-312.pyc CHANGED
Binary files a/app/__pycache__/main.cpython-312.pyc and b/app/__pycache__/main.cpython-312.pyc differ
 
app/api/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (172 Bytes). View file
 
app/api/__pycache__/analytics.cpython-312.pyc ADDED
Binary file (11.4 kB). View file
 
app/api/__pycache__/auth.cpython-312.pyc ADDED
Binary file (3.42 kB). View file
 
app/api/__pycache__/calendar.cpython-312.pyc ADDED
Binary file (7.59 kB). View file
 
app/api/__pycache__/files.cpython-312.pyc ADDED
Binary file (2.83 kB). View file
 
app/api/__pycache__/maintenance.cpython-312.pyc ADDED
Binary file (6.57 kB). View file
 
app/api/__pycache__/notifications.cpython-312.pyc ADDED
Binary file (5.22 kB). View file
 
app/api/__pycache__/orders.cpython-312.pyc ADDED
Binary file (8.61 kB). View file
 
app/api/__pycache__/products.cpython-312.pyc ADDED
Binary file (6.76 kB). View file
 
app/api/__pycache__/scheduler.cpython-312.pyc ADDED
Binary file (9.57 kB). View file
 
app/api/__pycache__/users.cpython-312.pyc ADDED
Binary file (6.79 kB). View file
 
app/core/__pycache__/config.cpython-312.pyc CHANGED
Binary files a/app/core/__pycache__/config.cpython-312.pyc and b/app/core/__pycache__/config.cpython-312.pyc differ
 
app/core/__pycache__/dependencies.cpython-312.pyc ADDED
Binary file (2.6 kB). View file
 
app/core/__pycache__/security.cpython-312.pyc ADDED
Binary file (1.72 kB). View file
 
app/core/config.py CHANGED
@@ -1,34 +1,34 @@
1
- from pydantic_settings import BaseSettings
2
- from typing import Optional
3
-
4
- class Settings(BaseSettings):
5
- API_V1_STR: str = "/api/v1"
6
- PROJECT_NAME: str = "Admin Dashboard"
7
- VERSION: str = "1.0.0"
8
-
9
- # PostgreSQL Database settings
10
- DATABASE_URL: str = "postgresql+asyncpg://postgres:Lovyelias5584.@db.mqyrkmsdgugdhxiucukb.supabase.co:5432/postgres"
11
-
12
- # JWT Settings
13
- SECRET_KEY: str = "your-secret-key-here" # Change in production
14
- ALGORITHM: str = "HS256"
15
- ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
16
-
17
- # Redis settings
18
- REDIS_HOST: str = "localhost"
19
- REDIS_PORT: int = 6379
20
-
21
- # Email settings
22
- MAIL_USERNAME: str = "yungdml31@gmail.com"
23
- MAIL_PASSWORD: str = ""
24
- MAIL_FROM: str = "admin@angelo.com"
25
- MAIL_PORT: int = 587
26
- MAIL_SERVER: str = "smtp.gmail.com"
27
-
28
- # Frontend URL for email links
29
- FRONTEND_URL: str = "http://localhost:3000"
30
-
31
- class Config:
32
- case_sensitive = True
33
-
34
  settings = Settings()
 
1
+ from pydantic_settings import BaseSettings
2
+ from typing import Optional
3
+
4
+ class Settings(BaseSettings):
5
+ API_V1_STR: str = "/api/v1"
6
+ PROJECT_NAME: str = "Admin Dashboard"
7
+ VERSION: str = "1.0.0"
8
+
9
+ # PostgreSQL Database settings
10
+ DATABASE_URL: str = "postgresql+asyncpg://postgres:Lovyelias5584.@db.mqyrkmsdgugdhxiucukb.supabase.co:5432/postgres"
11
+
12
+ # JWT Settings
13
+ SECRET_KEY: str = "your-secret-key-here" # Change in production
14
+ ALGORITHM: str = "HS256"
15
+ ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
16
+
17
+ # Redis settings
18
+ REDIS_HOST: str = "localhost"
19
+ REDIS_PORT: int = 6379
20
+
21
+ # Email settings
22
+ MAIL_USERNAME: str = "yungdml31@gmail.com"
23
+ MAIL_PASSWORD: str = ""
24
+ MAIL_FROM: str = "admin@angelo.com"
25
+ MAIL_PORT: int = 587
26
+ MAIL_SERVER: str = "smtp.gmail.com"
27
+
28
+ # Frontend URL for email links
29
+ FRONTEND_URL: str = "http://localhost:3000"
30
+
31
+ class Config:
32
+ case_sensitive = True
33
+
34
  settings = Settings()
app/db/__pycache__/database.cpython-312.pyc CHANGED
Binary files a/app/db/__pycache__/database.cpython-312.pyc and b/app/db/__pycache__/database.cpython-312.pyc differ
 
app/db/__pycache__/schemas.cpython-312.pyc ADDED
Binary file (13.4 kB). View file
 
app/db/database.py CHANGED
@@ -1,32 +1,32 @@
1
- from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
2
- from sqlalchemy.orm import declarative_base
3
- from ..core.config import settings
4
-
5
- # Create async engine for FastAPI
6
- async_engine = create_async_engine(
7
- settings.DATABASE_URL,
8
- echo=True,
9
- future=True,
10
- pool_pre_ping=True
11
- )
12
-
13
- # Create async session factory
14
- AsyncSessionLocal = async_sessionmaker(
15
- bind=async_engine,
16
- class_=AsyncSession,
17
- expire_on_commit=False
18
- )
19
-
20
- # Create declarative base for models
21
- Base = declarative_base()
22
-
23
- # Database dependency for FastAPI routes
24
- async def get_db():
25
- async with AsyncSessionLocal() as session:
26
- try:
27
- yield session
28
- finally:
29
- await session.close()
30
-
31
- # Add alias for external imports
32
- db = AsyncSessionLocal
 
1
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
2
+ from sqlalchemy.orm import declarative_base
3
+ from ..core.config import settings
4
+
5
+ # Create async engine for FastAPI
6
+ async_engine = create_async_engine(
7
+ settings.DATABASE_URL,
8
+ echo=True,
9
+ future=True,
10
+ pool_pre_ping=True
11
+ )
12
+
13
+ # Create async session factory
14
+ AsyncSessionLocal = async_sessionmaker(
15
+ bind=async_engine,
16
+ class_=AsyncSession,
17
+ expire_on_commit=False
18
+ )
19
+
20
+ # Create declarative base for models
21
+ Base = declarative_base()
22
+
23
+ # Database dependency for FastAPI routes
24
+ async def get_db():
25
+ async with AsyncSessionLocal() as session:
26
+ try:
27
+ yield session
28
+ finally:
29
+ await session.close()
30
+
31
+ # Add alias for external imports
32
+ db = AsyncSessionLocal
app/main.py CHANGED
@@ -1,108 +1,95 @@
1
- from fastapi import FastAPI, Request, WebSocket
2
- from fastapi.middleware.cors import CORSMiddleware
3
- from .core.config import settings
4
- from .db.database import async_engine as engine, Base
5
- from .api import auth, products, orders, users, analytics, files, notifications, calendar, scheduler, maintenance
6
- from .utils.rate_limiter import rate_limiter
7
- from .utils.logger import log_api_request
8
- from .utils.tasks import run_periodic_tasks
9
- import time
10
- import logging
11
- import asyncio
12
- from typing import List
13
-
14
- # Configure logging
15
- logging.basicConfig(level=logging.INFO)
16
- logger = logging.getLogger(__name__)
17
-
18
- app = FastAPI(title=settings.PROJECT_NAME, version=settings.VERSION)
19
-
20
- # Store active WebSocket connections and background tasks
21
- active_connections: List[WebSocket] = []
22
- background_tasks = set()
23
-
24
- # Configure CORS
25
- app.add_middleware(
26
- CORSMiddleware,
27
- allow_origins=["*"], # Configure appropriately for production
28
- allow_credentials=True,
29
- allow_methods=["*"],
30
- allow_headers=["*"],
31
- )
32
-
33
- # WebSocket connection manager
34
- @app.websocket("/ws")
35
- async def websocket_endpoint(websocket: WebSocket):
36
- await websocket.accept()
37
- active_connections.append(websocket)
38
- try:
39
- while True:
40
- data = await websocket.receive_text()
41
- except:
42
- active_connections.remove(websocket)
43
-
44
- # Notification broadcaster
45
- async def broadcast_notification(message: dict):
46
- for connection in active_connections:
47
- try:
48
- await connection.send_json(message)
49
- except:
50
- active_connections.remove(connection)
51
-
52
- # Request logging and rate limiting middleware
53
- @app.middleware("http")
54
- async def middleware(request: Request, call_next):
55
- await rate_limiter.check_rate_limit(request)
56
- start_time = time.time()
57
- response = await call_next(request)
58
- end_time = time.time()
59
- duration = end_time - start_time
60
- log_api_request(
61
- method=request.method,
62
- path=request.url.path,
63
- status_code=response.status_code,
64
- duration=duration
65
- )
66
- return response
67
-
68
- # Application startup and shutdown events
69
- @app.on_event("startup")
70
- async def startup_event():
71
- # Create all database tables
72
- async with engine.begin() as conn:
73
- await conn.run_sync(Base.metadata.create_all)
74
-
75
- # Start background tasks
76
- task = asyncio.create_task(run_periodic_tasks())
77
- background_tasks.add(task)
78
- task.add_done_callback(background_tasks.discard)
79
-
80
- @app.on_event("shutdown")
81
- async def shutdown_event():
82
- # Cancel background tasks
83
- for task in background_tasks:
84
- task.cancel()
85
-
86
- # Close WebSocket connections
87
- for connection in active_connections:
88
- await connection.close()
89
-
90
- # Include routers
91
- app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"])
92
- app.include_router(users.router, prefix=f"{settings.API_V1_STR}/users", tags=["users"])
93
- app.include_router(products.router, prefix=f"{settings.API_V1_STR}/products", tags=["products"])
94
- app.include_router(orders.router, prefix=f"{settings.API_V1_STR}/orders", tags=["orders"])
95
- app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
96
- app.include_router(files.router, prefix=f"{settings.API_V1_STR}/files", tags=["files"])
97
- app.include_router(notifications.router, prefix=f"{settings.API_V1_STR}/notifications", tags=["notifications"])
98
- app.include_router(calendar.router, prefix=f"{settings.API_V1_STR}/calendar", tags=["calendar"])
99
- app.include_router(scheduler.router, prefix=f"{settings.API_V1_STR}/scheduler", tags=["scheduler"])
100
- app.include_router(maintenance.router, prefix=f"{settings.API_V1_STR}/maintenance", tags=["maintenance"])
101
-
102
- @app.get("/")
103
- async def root():
104
- return {
105
- "message": f"Welcome to {settings.PROJECT_NAME} v{settings.VERSION}",
106
- "docs_url": "/docs",
107
- "openapi_url": "/openapi.json"
108
  }
 
1
+ from fastapi import FastAPI, Request, WebSocket
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from .core.config import settings
4
+ from .db.database import async_engine as engine, Base
5
+ from .api import auth, products, orders, users, analytics, files, notifications, calendar, scheduler, maintenance
6
+ from .utils.rate_limiter import rate_limiter
7
+ from .utils.logger import log_api_request
8
+ from .utils.tasks import run_periodic_tasks
9
+ from .services.websocket import connect, disconnect
10
+ import time
11
+ import logging
12
+ import asyncio
13
+ from typing import List
14
+
15
+ # Configure logging
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
+
19
+ app = FastAPI(title=settings.PROJECT_NAME, version=settings.VERSION)
20
+
21
+ # Store background tasks
22
+ background_tasks = set()
23
+
24
+ # Configure CORS
25
+ app.add_middleware(
26
+ CORSMiddleware,
27
+ allow_origins=["*"], # Configure appropriately for production
28
+ allow_credentials=True,
29
+ allow_methods=["*"],
30
+ allow_headers=["*"],
31
+ )
32
+
33
+ # WebSocket endpoint
34
+ @app.websocket("/ws")
35
+ async def websocket_endpoint(websocket: WebSocket):
36
+ await connect(websocket)
37
+ try:
38
+ while True:
39
+ data = await websocket.receive_text()
40
+ except:
41
+ await disconnect(websocket)
42
+
43
+ # Request logging and rate limiting middleware
44
+ @app.middleware("http")
45
+ async def middleware(request: Request, call_next):
46
+ await rate_limiter.check_rate_limit(request)
47
+ start_time = time.time()
48
+ response = await call_next(request)
49
+ end_time = time.time()
50
+ duration = end_time - start_time
51
+ log_api_request(
52
+ method=request.method,
53
+ path=request.url.path,
54
+ status_code=response.status_code,
55
+ duration=duration
56
+ )
57
+ return response
58
+
59
+ # Application startup and shutdown events
60
+ @app.on_event("startup")
61
+ async def startup_event():
62
+ # Create all database tables
63
+ async with engine.begin() as conn:
64
+ await conn.run_sync(Base.metadata.create_all)
65
+
66
+ # Start background tasks
67
+ task = asyncio.create_task(run_periodic_tasks())
68
+ background_tasks.add(task)
69
+ task.add_done_callback(background_tasks.discard)
70
+
71
+ @app.on_event("shutdown")
72
+ async def shutdown_event():
73
+ # Cancel background tasks
74
+ for task in background_tasks:
75
+ task.cancel()
76
+
77
+ # Include routers
78
+ app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["auth"])
79
+ app.include_router(users.router, prefix=f"{settings.API_V1_STR}/users", tags=["users"])
80
+ app.include_router(products.router, prefix=f"{settings.API_V1_STR}/products", tags=["products"])
81
+ app.include_router(orders.router, prefix=f"{settings.API_V1_STR}/orders", tags=["orders"])
82
+ app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
83
+ app.include_router(files.router, prefix=f"{settings.API_V1_STR}/files", tags=["files"])
84
+ app.include_router(notifications.router, prefix=f"{settings.API_V1_STR}/notifications", tags=["notifications"])
85
+ app.include_router(calendar.router, prefix=f"{settings.API_V1_STR}/calendar", tags=["calendar"])
86
+ app.include_router(scheduler.router, prefix=f"{settings.API_V1_STR}/scheduler", tags=["scheduler"])
87
+ app.include_router(maintenance.router, prefix=f"{settings.API_V1_STR}/maintenance", tags=["maintenance"])
88
+
89
+ @app.get("/")
90
+ async def root():
91
+ return {
92
+ "message": f"Welcome to {settings.PROJECT_NAME} v{settings.VERSION}",
93
+ "docs_url": "/docs",
94
+ "openapi_url": "/openapi.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
app/schemas/__pycache__/events.cpython-312.pyc ADDED
Binary file (4.93 kB). View file
 
app/services/__pycache__/backup.cpython-312.pyc ADDED
Binary file (9.58 kB). View file
 
app/services/__pycache__/maintenance.cpython-312.pyc ADDED
Binary file (10.6 kB). View file
 
app/services/__pycache__/notifications.cpython-312.pyc ADDED
Binary file (4.28 kB). View file
 
app/services/__pycache__/websocket.cpython-312.pyc ADDED
Binary file (2.69 kB). View file
 
app/services/maintenance.py CHANGED
@@ -1,286 +1,202 @@
1
  import os
2
- import psutil
3
  from datetime import datetime, timedelta
4
- from typing import Dict, Any, List
5
-
6
  from ..db.database import db
7
  from ..utils.logger import logger
8
- from ..utils.cache import cache
9
- from ..services.notifications import notifications
10
- from .backup import backup
11
- import gzip
12
- import shutil
13
 
14
  class MaintenanceService:
15
  async def cleanup_expired_sessions(self) -> int:
16
- """Clean up expired sessions from the database"""
17
- try:
18
- cutoff_date = datetime.utcnow() - timedelta(days=7)
19
- result = await db.db["sessions"].delete_many({
20
- "last_activity": {"$lt": cutoff_date}
21
- })
22
- logger.info(f"Cleaned up {result.deleted_count} expired sessions")
23
- return result.deleted_count
24
- except Exception as e:
25
- logger.error(f"Error cleaning up sessions: {str(e)}")
26
- return 0
27
 
28
  async def archive_old_data(self) -> Dict[str, int]:
29
- """Archive old data to maintain database performance"""
30
  try:
31
- archive_date = datetime.utcnow() - timedelta(days=365) # Archive data older than 1 year
32
- archives = {}
33
 
34
  # Archive old orders
35
- old_orders = await db.db["orders"].find({
36
- "created_at": {"$lt": archive_date},
37
- "status": {"$in": ["delivered", "cancelled"]}
38
- }).to_list(None)
39
-
40
- if old_orders:
41
- await db.db["archived_orders"].insert_many(old_orders)
42
- result = await db.db["orders"].delete_many({
43
- "_id": {"$in": [order["_id"] for order in old_orders]}
44
- })
45
- archives["orders"] = len(old_orders)
46
 
47
  # Archive old notifications
48
- old_notifications = await db.db["notifications"].find({
49
- "created_at": {"$lt": archive_date},
50
- "read": True
51
- }).to_list(None)
52
-
53
- if old_notifications:
54
- await db.db["archived_notifications"].insert_many(old_notifications)
55
- result = await db.db["notifications"].delete_many({
56
- "_id": {"$in": [notif["_id"] for notif in old_notifications]}
57
- })
58
- archives["notifications"] = len(old_notifications)
59
-
60
- # Archive old metrics (keep last 90 days)
61
- cutoff_date = datetime.utcnow() - timedelta(days=90)
62
- result = await db.db.system_metrics.delete_many({
63
- "timestamp": {"$lt": cutoff_date}
64
- })
65
- logger.info(f"Archived {result.deleted_count} old metric records")
66
- archives["metrics"] = result.deleted_count
67
-
68
- # Archive old audit logs (keep last 180 days)
69
- audit_cutoff = datetime.utcnow() - timedelta(days=180)
70
- audit_result = await db.db.audit_logs.delete_many({
71
- "timestamp": {"$lt": audit_cutoff}
72
- })
73
- logger.info(f"Archived {audit_result.deleted_count} old audit logs")
74
- archives["audit_logs"] = audit_result.deleted_count
75
-
76
- return archives
77
  except Exception as e:
78
  logger.error(f"Error archiving old data: {str(e)}")
79
- return {}
80
 
81
  async def check_system_health(self) -> Dict[str, Any]:
82
- """Check various system health metrics"""
83
  try:
84
- health_data = {
85
- "timestamp": datetime.utcnow(),
86
- "database": {},
87
- "cache": {},
88
- "storage": {}
89
- }
90
 
91
- # Check database stats
92
- db_stats = await db.db.command("dbStats")
93
- health_data["database"] = {
94
- "size": db_stats["dataSize"],
95
- "collections": db_stats["collections"],
96
- "indexes": db_stats["indexes"]
97
- }
98
 
99
- # Check Redis cache
100
- try:
101
- cache_info = await cache.redis_client.info()
102
- health_data["cache"] = {
 
 
 
103
  "connected": True,
104
- "used_memory": cache_info["used_memory"],
105
- "connected_clients": cache_info["connected_clients"]
106
- }
107
- except:
108
- health_data["cache"] = {"connected": False}
109
-
110
- # Check storage metrics
111
- storage_stats = {
112
- "uploads_size": await self._get_directory_size("uploads"),
113
- "logs_size": await self._get_directory_size("logs")
114
  }
115
- health_data["storage"] = storage_stats
116
 
117
- return health_data
118
- except Exception as e:
119
- logger.error(f"Error checking system health: {str(e)}")
120
- return {"error": str(e)}
 
 
 
 
 
121
 
122
- async def perform_database_maintenance(self):
123
- """Perform routine database maintenance tasks"""
124
- try:
125
- # Cleanup expired sessions
126
- await self.cleanup_expired_sessions()
127
-
128
- # Run database vacuum and analyze
129
- await db.db.command('analyze')
130
- logger.info("Database maintenance completed successfully")
131
  except Exception as e:
132
- logger.error(f"Database maintenance failed: {str(e)}")
133
- raise
134
 
135
  async def monitor_system_resources(self) -> Dict[str, Any]:
136
- """Monitor system resources and return metrics"""
137
  try:
138
- cpu_percent = psutil.cpu_percent()
139
- memory = psutil.virtual_memory()
140
- disk = psutil.disk_usage('/')
141
 
142
- metrics = {
143
- "cpu_usage": cpu_percent,
144
- "memory_usage": memory.percent,
145
- "disk_usage": disk.percent,
146
  "timestamp": datetime.utcnow()
147
  }
148
-
149
- await db.db.system_metrics.insert_one(metrics)
150
-
151
- # Alert if resources are critically low
152
- if any([cpu_percent > 90, memory.percent > 90, disk.percent > 90]):
153
- logger.warning("System resources critically low", extra=metrics)
154
-
155
- return metrics
156
- except Exception as e:
157
- logger.error(f"Resource monitoring failed: {str(e)}")
158
- raise
159
 
160
- async def perform_scheduled_backup(self):
161
- """Perform scheduled system backup"""
162
- try:
163
- result = await backup.create_backup(include_files=True)
164
- logger.info(f"Scheduled backup completed successfully: {result['id']}")
165
-
166
- # Cleanup old backups (keep last 7 days)
167
- await self.cleanup_old_backups(days_to_keep=7)
 
 
 
168
  except Exception as e:
169
- logger.error(f"Scheduled backup failed: {str(e)}")
170
- raise
171
 
172
- async def cleanup_old_backups(self, days_to_keep: int = 7):
173
- """Clean up backups older than specified days"""
174
  try:
175
- cutoff_date = datetime.utcnow() - timedelta(days=days_to_keep)
176
- old_backups = await db.db.backup_history.find({
177
- "created_at": {"$lt": cutoff_date}
178
- }).to_list(None)
 
 
 
 
 
 
 
179
 
180
- for old_backup in old_backups:
181
- await backup.delete_backup(str(old_backup["_id"]))
182
-
183
- logger.info(f"Cleaned up {len(old_backups)} old backups")
184
  except Exception as e:
185
- logger.error(f"Backup cleanup failed: {str(e)}")
186
- raise
187
 
188
- async def rotate_log_files(self):
189
- """Rotate and archive log files"""
 
 
 
190
  try:
191
- log_dir = "logs"
192
- if not os.path.exists(log_dir):
193
- return
194
-
195
- current_date = datetime.utcnow().strftime("%Y%m%d")
196
  for filename in os.listdir(log_dir):
197
- if filename.endswith(".log"):
198
- src_path = os.path.join(log_dir, filename)
199
- dst_path = os.path.join(log_dir, f"{filename}.{current_date}")
 
 
200
 
201
- if os.path.exists(src_path):
202
- os.rename(src_path, dst_path)
203
-
204
- logger.info("Log rotation completed successfully")
205
  except Exception as e:
206
- logger.error(f"Log rotation failed: {str(e)}")
207
- raise
208
 
209
- async def manage_storage_quotas(self):
210
- """Check and manage storage quotas"""
211
  try:
212
  results = {
213
- "status": "ok",
214
  "warnings": [],
215
  "cleaned": 0
216
  }
217
 
218
- # Check uploads directory size
219
- uploads_dir = "uploads"
220
- if os.path.exists(uploads_dir):
221
- total_size = sum(
222
- os.path.getsize(os.path.join(dirpath, filename))
223
- for dirpath, _, filenames in os.walk(uploads_dir)
224
- for filename in filenames
225
- )
226
-
227
- # Alert if total size exceeds 90% of quota (e.g., 10GB)
228
- quota_limit = 10 * 1024 * 1024 * 1024 # 10GB in bytes
229
- if total_size > (quota_limit * 0.9):
230
- warning_msg = f"Storage quota nearly reached: {total_size / quota_limit:.1%}"
231
- results["warnings"].append(warning_msg)
232
- await self._notify_resource_warning(warning_msg)
233
 
234
- # Check database size
235
- db_stats = await db.db.command("dbStats")
236
- db_size = db_stats["dataSize"] + db_stats["indexSize"]
 
237
 
238
- # Alert if database size exceeds 90% of quota (e.g., 5GB)
239
- db_quota = 5 * 1024 * 1024 * 1024 # 5GB in bytes
240
- if db_size > (db_quota * 0.9):
241
- warning_msg = f"Database quota nearly reached: {db_size / db_quota:.1%}"
242
- results["warnings"].append(warning_msg)
243
- await self._notify_resource_warning(warning_msg)
244
-
245
- # Clean up temporary uploads older than 24 hours
246
- temp_dir = os.path.join("uploads", "temp")
247
- if os.path.exists(temp_dir):
248
- current_time = datetime.utcnow()
249
- for file_name in os.listdir(temp_dir):
250
- file_path = os.path.join(temp_dir, file_name)
251
- file_age = datetime.fromtimestamp(os.path.getctime(file_path))
252
-
253
- if current_time - file_age > timedelta(hours=24):
254
- os.remove(file_path)
255
- results["cleaned"] += 1
256
 
257
  return results
258
  except Exception as e:
259
- logger.error(f"Error managing storage quotas: {str(e)}")
260
  return {"error": str(e)}
261
 
262
- async def _notify_resource_warning(self, message: str):
263
- """Send notification for resource warnings"""
264
- try:
265
- # Get admin users
266
- admin_users = await db.db["users"].find(
267
- {"roles": "admin"}
268
- ).to_list(None)
269
-
270
- # Send notifications
271
- for admin in admin_users:
272
- await notifications.create_notification(
273
- user_id=str(admin["_id"]),
274
- title="System Resource Warning",
275
- message=message,
276
- notification_type="system_warning"
277
- )
278
- except Exception as e:
279
- logger.error(f"Error sending resource warning: {str(e)}")
280
-
281
- async def _get_directory_size(self, path: str) -> int:
282
- """Get the total size of a directory in bytes"""
283
- from pathlib import Path
284
- return sum(f.stat().st_size for f in Path(path).glob('**/*') if f.is_file())
285
-
286
  maintenance = MaintenanceService()
 
1
  import os
2
+ import shutil
3
  from datetime import datetime, timedelta
4
+ from typing import Dict, Any, Optional
5
+ from sqlalchemy import select, delete, text
6
  from ..db.database import db
7
  from ..utils.logger import logger
8
+ from ..core.config import settings
9
+ from ..services.websocket import create_and_broadcast_notification
 
 
 
10
 
11
  class MaintenanceService:
12
  async def cleanup_expired_sessions(self) -> int:
13
+ """Clean up expired sessions"""
14
+ cutoff = datetime.utcnow() - timedelta(days=7)
15
+ result = await db.db["sessions"].delete_many({
16
+ "last_activity": {"$lt": cutoff}
17
+ })
18
+ return result.deleted_count
 
 
 
 
 
19
 
20
  async def archive_old_data(self) -> Dict[str, int]:
21
+ """Archive old data"""
22
  try:
23
+ cutoff = datetime.utcnow() - timedelta(days=365)
24
+ archived = {}
25
 
26
  # Archive old orders
27
+ result = await db.db["orders"].update_many(
28
+ {
29
+ "created_at": {"$lt": cutoff},
30
+ "status": {"$in": ["completed", "cancelled"]}
31
+ },
32
+ {"$set": {"archived": True}}
33
+ )
34
+ archived["orders"] = result.modified_count
 
 
 
35
 
36
  # Archive old notifications
37
+ result = await db.db["notifications"].update_many(
38
+ {
39
+ "created_at": {"$lt": cutoff},
40
+ "read": True
41
+ },
42
+ {"$set": {"archived": True}}
43
+ )
44
+ archived["notifications"] = result.modified_count
45
+
46
+ return archived
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  except Exception as e:
48
  logger.error(f"Error archiving old data: {str(e)}")
49
+ return None
50
 
51
  async def check_system_health(self) -> Dict[str, Any]:
52
+ """Check system health"""
53
  try:
54
+ # Check database connection
55
+ await db.db.command("ping")
 
 
 
 
56
 
57
+ # Get database stats
58
+ stats = await db.db.command("dbStats")
59
+ disk_size = stats.get("dataSize", 0) / (1024 * 1024) # Convert to MB
 
 
 
 
60
 
61
+ # Check disk space
62
+ total_space = shutil.disk_usage("/").total / (1024 * 1024 * 1024) # GB
63
+ free_space = shutil.disk_usage("/").free / (1024 * 1024 * 1024) # GB
64
+
65
+ health_data = {
66
+ "status": "healthy",
67
+ "database": {
68
  "connected": True,
69
+ "size_mb": disk_size
70
+ },
71
+ "disk": {
72
+ "total_gb": total_space,
73
+ "free_gb": free_space,
74
+ "usage_percent": ((total_space - free_space) / total_space) * 100
75
+ },
76
+ "timestamp": datetime.utcnow()
 
 
77
  }
 
78
 
79
+ # Send alert if disk space is low
80
+ if free_space < 5: # Less than 5GB free
81
+ await create_and_broadcast_notification(
82
+ user_id="admin",
83
+ title="Low Disk Space Alert",
84
+ message=f"Server is running low on disk space. Only {free_space:.2f}GB remaining.",
85
+ notification_type="system_alert",
86
+ data={"free_space_gb": free_space}
87
+ )
88
 
89
+ return health_data
 
 
 
 
 
 
 
 
90
  except Exception as e:
91
+ logger.error(f"Health check error: {str(e)}")
92
+ return {"status": "unhealthy", "error": str(e)}
93
 
94
  async def monitor_system_resources(self) -> Dict[str, Any]:
95
+ """Monitor system resources"""
96
  try:
97
+ # Monitor database connections
98
+ server_status = await db.db.command("serverStatus")
99
+ current_connections = server_status.get("connections", {}).get("current", 0)
100
 
101
+ resources = {
102
+ "database": {
103
+ "connections": current_connections,
104
+ },
105
  "timestamp": datetime.utcnow()
106
  }
 
 
 
 
 
 
 
 
 
 
 
107
 
108
+ # Alert if too many connections
109
+ if current_connections > settings.MAX_DB_CONNECTIONS * 0.9:
110
+ await create_and_broadcast_notification(
111
+ user_id="admin",
112
+ title="High Database Connections",
113
+ message=f"Database has {current_connections} active connections",
114
+ notification_type="system_alert",
115
+ data={"connections": current_connections}
116
+ )
117
+
118
+ return resources
119
  except Exception as e:
120
+ logger.error(f"Resource monitoring error: {str(e)}")
121
+ return {"error": str(e)}
122
 
123
+ async def perform_database_maintenance(self) -> Dict[str, Any]:
124
+ """Perform database maintenance tasks"""
125
  try:
126
+ # Run VACUUM ANALYZE
127
+ await db.db.command("vacuum")
128
+
129
+ # Clean up temporary collections
130
+ temp_collections = [
131
+ "temp_imports",
132
+ "temp_exports",
133
+ "temp_reports"
134
+ ]
135
+ for collection in temp_collections:
136
+ await db.db[collection].drop()
137
 
138
+ return {"status": "success"}
 
 
 
139
  except Exception as e:
140
+ logger.error(f"Database maintenance error: {str(e)}")
141
+ return {"error": str(e)}
142
 
143
+ async def rotate_log_files(self) -> None:
144
+ """Rotate log files"""
145
+ log_dir = "logs"
146
+ max_log_size = 10 * 1024 * 1024 # 10MB
147
+
148
  try:
 
 
 
 
 
149
  for filename in os.listdir(log_dir):
150
+ filepath = os.path.join(log_dir, filename)
151
+ if os.path.getsize(filepath) > max_log_size:
152
+ # Archive old log
153
+ archive_name = f"{filename}.{datetime.now().strftime('%Y%m%d')}"
154
+ shutil.move(filepath, os.path.join(log_dir, archive_name))
155
 
156
+ # Create new log file
157
+ open(filepath, 'a').close()
158
+ logger.info(f"Rotated log file: {filename}")
 
159
  except Exception as e:
160
+ logger.error(f"Log rotation error: {str(e)}")
 
161
 
162
+ async def manage_storage_quotas(self) -> Dict[str, Any]:
163
+ """Manage storage quotas and cleanup"""
164
  try:
165
  results = {
 
166
  "warnings": [],
167
  "cleaned": 0
168
  }
169
 
170
+ # Check and clean upload directories
171
+ upload_dirs = ["uploads/documents", "uploads/images"]
172
+ for directory in upload_dirs:
173
+ if os.path.exists(directory):
174
+ total_size = sum(
175
+ os.path.getsize(os.path.join(directory, f))
176
+ for f in os.listdir(directory)
177
+ if os.path.isfile(os.path.join(directory, f))
178
+ ) / (1024 * 1024) # Convert to MB
 
 
 
 
 
 
179
 
180
+ if total_size > settings.MAX_UPLOAD_DIR_SIZE_MB:
181
+ results["warnings"].append(
182
+ f"Upload directory {directory} exceeds size limit"
183
+ )
184
 
185
+ # Clean up temporary files
186
+ temp_dirs = ["uploads/temp", "backups/temp"]
187
+ for directory in temp_dirs:
188
+ if os.path.exists(directory):
189
+ # Remove files older than 24 hours
190
+ cutoff = datetime.now() - timedelta(days=1)
191
+ for filename in os.listdir(directory):
192
+ filepath = os.path.join(directory, filename)
193
+ if os.path.getctime(filepath) < cutoff.timestamp():
194
+ os.remove(filepath)
195
+ results["cleaned"] += 1
 
 
 
 
 
 
 
196
 
197
  return results
198
  except Exception as e:
199
+ logger.error(f"Storage quota management error: {str(e)}")
200
  return {"error": str(e)}
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  maintenance = MaintenanceService()
app/services/notifications.py CHANGED
@@ -3,7 +3,7 @@ from datetime import datetime
3
  from ..db.database import db
4
  from ..core.config import settings
5
  from ..utils.cache import cache
6
- from ..main import broadcast_notification
7
 
8
  class NotificationService:
9
  async def create_notification(
@@ -15,29 +15,13 @@ class NotificationService:
15
  data: Dict[str, Any] = None
16
  ):
17
  """Create and store a notification"""
18
- notification = {
19
- "user_id": user_id,
20
- "title": title,
21
- "message": message,
22
- "type": notification_type,
23
- "data": data,
24
- "created_at": datetime.utcnow(),
25
- "read": False
26
- }
27
-
28
- # Store in database
29
- await db.db["notifications"].insert_one(notification)
30
-
31
- # Broadcast to connected clients
32
- await broadcast_notification({
33
- "type": "notification",
34
- "data": notification
35
- })
36
-
37
- # Clear user's notification cache
38
- await cache.delete_cache(f"user_notifications:{user_id}")
39
-
40
- return notification
41
 
42
  async def get_user_notifications(
43
  self,
 
3
  from ..db.database import db
4
  from ..core.config import settings
5
  from ..utils.cache import cache
6
+ from ..services.websocket import create_and_broadcast_notification
7
 
8
  class NotificationService:
9
  async def create_notification(
 
15
  data: Dict[str, Any] = None
16
  ):
17
  """Create and store a notification"""
18
+ return await create_and_broadcast_notification(
19
+ user_id=user_id,
20
+ title=title,
21
+ message=message,
22
+ notification_type=notification_type,
23
+ data=data
24
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  async def get_user_notifications(
27
  self,
app/services/websocket.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict, Any
2
+ from fastapi import WebSocket
3
+ from datetime import datetime
4
+ from ..db.database import db
5
+ from ..utils.cache import cache
6
+
7
+ # Store active WebSocket connections
8
+ active_connections: List[WebSocket] = []
9
+
10
+ async def connect(websocket: WebSocket):
11
+ """Accept a new WebSocket connection"""
12
+ await websocket.accept()
13
+ active_connections.append(websocket)
14
+
15
+ async def disconnect(websocket: WebSocket):
16
+ """Remove a WebSocket connection"""
17
+ if websocket in active_connections:
18
+ active_connections.remove(websocket)
19
+
20
+ async def broadcast_message(message: dict):
21
+ """Broadcast a message to all connected clients"""
22
+ disconnected = []
23
+ for connection in active_connections:
24
+ try:
25
+ await connection.send_json(message)
26
+ except:
27
+ disconnected.append(connection)
28
+
29
+ # Clean up disconnected clients
30
+ for connection in disconnected:
31
+ await disconnect(connection)
32
+
33
+ async def create_and_broadcast_notification(
34
+ user_id: str,
35
+ title: str,
36
+ message: str,
37
+ notification_type: str,
38
+ data: Dict[str, Any] = None
39
+ ) -> Dict[str, Any]:
40
+ """Create and broadcast a notification"""
41
+ notification = {
42
+ "user_id": user_id,
43
+ "title": title,
44
+ "message": message,
45
+ "type": notification_type,
46
+ "data": data,
47
+ "created_at": datetime.utcnow(),
48
+ "read": False
49
+ }
50
+
51
+ # Store in database
52
+ await db.db["notifications"].insert_one(notification)
53
+
54
+ # Broadcast to connected clients
55
+ await broadcast_message({
56
+ "type": "notification",
57
+ "data": notification
58
+ })
59
+
60
+ # Clear user's notification cache
61
+ await cache.delete_cache(f"user_notifications:{user_id}")
62
+
63
+ return notification
app/utils/__pycache__/cache.cpython-312.pyc ADDED
Binary file (3.37 kB). View file
 
app/utils/__pycache__/file_storage.cpython-312.pyc ADDED
Binary file (4.16 kB). View file
 
app/utils/__pycache__/logger.cpython-312.pyc ADDED
Binary file (2.85 kB). View file
 
app/utils/__pycache__/rate_limiter.cpython-312.pyc ADDED
Binary file (2.1 kB). View file
 
app/utils/__pycache__/tasks.cpython-312.pyc ADDED
Binary file (8.46 kB). View file
 
app/utils/tasks.py CHANGED
@@ -1,167 +1,146 @@
1
- from datetime import datetime, timedelta
2
- from ..db.database import db
3
- from ..services.maintenance import maintenance
4
- from ..utils.logger import logger
5
- from ..services.websocket import broadcast_notification
6
- import asyncio
7
- from sqlalchemy import select
8
- from ..db.models import Event, User
9
-
10
- async def notify_users(user_ids: list, title: str, message: str, notification_type: str, data: dict = None):
11
- """Send notification to users"""
12
- for user_id in user_ids:
13
- notification = {
14
- "user_id": user_id,
15
- "title": title,
16
- "message": message,
17
- "type": notification_type,
18
- "data": data,
19
- "created_at": datetime.utcnow(),
20
- "read": False
21
- }
22
-
23
- # Store in database
24
- await db.db["notifications"].insert_one(notification)
25
-
26
- # Broadcast to connected clients
27
- await broadcast_notification({
28
- "type": "notification",
29
- "data": notification
30
- })
31
-
32
- async def check_event_reminders():
33
- """Check and send event reminders"""
34
- try:
35
- now = datetime.utcnow()
36
- # Find events happening soon that haven't sent reminders
37
- stmt = select(Event).where(
38
- Event.start_time > now,
39
- Event.start_time <= now + timedelta(minutes=30),
40
- Event.reminder_sent == False
41
- )
42
-
43
- async with db.session() as session:
44
- result = await session.execute(stmt)
45
- upcoming_events = result.scalars().all()
46
-
47
- for event in upcoming_events:
48
- # Get event owner
49
- user_stmt = select(User).where(User.id == event.user_id)
50
- user_result = await session.execute(user_stmt)
51
- user = user_result.scalar_one_or_none()
52
-
53
- if user:
54
- # Send reminder to event owner and attendees
55
- reminder_users = [str(user.id)] + event.attendees
56
- await notify_users(
57
- user_ids=reminder_users,
58
- title=f"Event Reminder: {event.title}",
59
- message=f"Your event '{event.title}' starts in {event.reminder_minutes} minutes",
60
- notification_type="event_reminder",
61
- data={"event_id": str(event.id)}
62
- )
63
-
64
- # Mark reminder as sent
65
- event.reminder_sent = True
66
- await session.commit()
67
-
68
- except Exception as e:
69
- logger.error(f"Error in event reminder check: {str(e)}")
70
-
71
- async def cleanup_old_notifications():
72
- """Clean up old notifications"""
73
- try:
74
- # Delete notifications older than 30 days
75
- cutoff = datetime.utcnow() - timedelta(days=30)
76
- result = await db.db["notifications"].delete_many({
77
- "created_at": {"$lt": cutoff},
78
- "read": True # Only delete read notifications
79
- })
80
- logger.info(f"Cleaned up {result.deleted_count} old notifications")
81
- except Exception as e:
82
- logger.error(f"Error in cleanup_old_notifications: {str(e)}")
83
-
84
- async def perform_daily_maintenance():
85
- """Perform daily system maintenance tasks"""
86
- try:
87
- # Clean up expired sessions
88
- deleted_sessions = await maintenance.cleanup_expired_sessions()
89
- logger.info(f"Cleaned up {deleted_sessions} expired sessions")
90
-
91
- # Archive old data
92
- archived = await maintenance.archive_old_data()
93
- if archived:
94
- logger.info(f"Archived data: {archived}")
95
-
96
- # Check system health
97
- health_data = await maintenance.check_system_health()
98
- if "error" not in health_data:
99
- logger.info("System health check completed successfully")
100
- else:
101
- logger.error(f"System health check error: {health_data['error']}")
102
-
103
- # Monitor system resources
104
- resources = await maintenance.monitor_system_resources()
105
- if "error" not in resources:
106
- logger.info("System resource monitoring completed successfully")
107
- else:
108
- logger.error(f"Resource monitoring error: {resources['error']}")
109
-
110
- except Exception as e:
111
- logger.error(f"Error in daily maintenance: {str(e)}")
112
-
113
- async def perform_weekly_maintenance():
114
- """Perform weekly system maintenance tasks"""
115
- try:
116
- # Perform database maintenance
117
- await maintenance.perform_database_maintenance()
118
- logger.info("Database maintenance completed successfully")
119
-
120
- # Rotate log files
121
- await maintenance.rotate_log_files()
122
- logger.info("Log rotation completed successfully")
123
-
124
- # Manage storage quotas
125
- quota_results = await maintenance.manage_storage_quotas()
126
- if quota_results.get("warnings"):
127
- for warning in quota_results["warnings"]:
128
- logger.warning(warning)
129
- logger.info(f"Storage cleanup: removed {quota_results.get('cleaned', 0)} temporary files")
130
-
131
- except Exception as e:
132
- logger.error(f"Error in weekly maintenance: {str(e)}")
133
-
134
- async def run_periodic_tasks():
135
- """Run periodic maintenance tasks"""
136
- daily_maintenance_run = False
137
- weekly_maintenance_run = False
138
-
139
- while True:
140
- try:
141
- now = datetime.utcnow()
142
-
143
- # Check event reminders every minute
144
- await check_event_reminders()
145
-
146
- # Clean up old notifications daily
147
- await cleanup_old_notifications()
148
-
149
- # Run daily maintenance at 2 AM
150
- if now.hour == 2 and not daily_maintenance_run:
151
- await perform_daily_maintenance()
152
- daily_maintenance_run = True
153
- elif now.hour != 2:
154
- daily_maintenance_run = False
155
-
156
- # Run weekly maintenance on Sunday at 3 AM
157
- if now.weekday() == 6 and now.hour == 3 and not weekly_maintenance_run:
158
- await perform_weekly_maintenance()
159
- weekly_maintenance_run = True
160
- elif now.weekday() != 6 or now.hour != 3:
161
- weekly_maintenance_run = False
162
-
163
- # Wait before next check
164
- await asyncio.sleep(60) # 1 minute
165
- except Exception as e:
166
- logger.error(f"Error in periodic tasks: {str(e)}")
167
- await asyncio.sleep(60) # Wait before retrying
 
1
+ from datetime import datetime, timedelta
2
+ from ..db.database import db
3
+ from ..services.maintenance import maintenance
4
+ from ..utils.logger import logger
5
+ from ..services.websocket import create_and_broadcast_notification
6
+ import asyncio
7
+ from sqlalchemy import select
8
+ from ..db.models import Event, User
9
+
10
+ async def check_event_reminders():
11
+ """Check and send event reminders"""
12
+ try:
13
+ now = datetime.utcnow()
14
+ # Find events happening soon that haven't sent reminders
15
+ stmt = select(Event).where(
16
+ Event.start_time > now,
17
+ Event.start_time <= now + timedelta(minutes=30),
18
+ Event.reminder_sent == False
19
+ )
20
+
21
+ async with db.session() as session:
22
+ result = await session.execute(stmt)
23
+ upcoming_events = result.scalars().all()
24
+
25
+ for event in upcoming_events:
26
+ # Get event owner
27
+ user_stmt = select(User).where(User.id == event.user_id)
28
+ user_result = await session.execute(user_stmt)
29
+ user = user_result.scalar_one_or_none()
30
+
31
+ if user:
32
+ # Send reminder to event owner and attendees
33
+ reminder_users = [str(user.id)] + event.attendees
34
+ for user_id in reminder_users:
35
+ await create_and_broadcast_notification(
36
+ user_id=user_id,
37
+ title=f"Event Reminder: {event.title}",
38
+ message=f"Your event '{event.title}' starts in {event.reminder_minutes} minutes",
39
+ notification_type="event_reminder",
40
+ data={"event_id": str(event.id)}
41
+ )
42
+
43
+ # Mark reminder as sent
44
+ event.reminder_sent = True
45
+ await session.commit()
46
+
47
+ except Exception as e:
48
+ logger.error(f"Error in event reminder check: {str(e)}")
49
+
50
+ async def cleanup_old_notifications():
51
+ """Clean up old notifications"""
52
+ try:
53
+ # Delete notifications older than 30 days
54
+ cutoff = datetime.utcnow() - timedelta(days=30)
55
+ result = await db.db["notifications"].delete_many({
56
+ "created_at": {"$lt": cutoff},
57
+ "read": True # Only delete read notifications
58
+ })
59
+ logger.info(f"Cleaned up {result.deleted_count} old notifications")
60
+ except Exception as e:
61
+ logger.error(f"Error in cleanup_old_notifications: {str(e)}")
62
+
63
+ async def perform_daily_maintenance():
64
+ """Perform daily system maintenance tasks"""
65
+ try:
66
+ # Clean up expired sessions
67
+ deleted_sessions = await maintenance.cleanup_expired_sessions()
68
+ logger.info(f"Cleaned up {deleted_sessions} expired sessions")
69
+
70
+ # Archive old data
71
+ archived = await maintenance.archive_old_data()
72
+ if archived:
73
+ logger.info(f"Archived data: {archived}")
74
+
75
+ # Check system health
76
+ health_data = await maintenance.check_system_health()
77
+ if "error" not in health_data:
78
+ logger.info("System health check completed successfully")
79
+ else:
80
+ logger.error(f"System health check error: {health_data['error']}")
81
+
82
+ # Monitor system resources
83
+ resources = await maintenance.monitor_system_resources()
84
+ if "error" not in resources:
85
+ logger.info("System resource monitoring completed successfully")
86
+ else:
87
+ logger.error(f"Resource monitoring error: {resources['error']}")
88
+
89
+ except Exception as e:
90
+ logger.error(f"Error in daily maintenance: {str(e)}")
91
+
92
+ async def perform_weekly_maintenance():
93
+ """Perform weekly system maintenance tasks"""
94
+ try:
95
+ # Perform database maintenance
96
+ await maintenance.perform_database_maintenance()
97
+ logger.info("Database maintenance completed successfully")
98
+
99
+ # Rotate log files
100
+ await maintenance.rotate_log_files()
101
+ logger.info("Log rotation completed successfully")
102
+
103
+ # Manage storage quotas
104
+ quota_results = await maintenance.manage_storage_quotas()
105
+ if quota_results.get("warnings"):
106
+ for warning in quota_results["warnings"]:
107
+ logger.warning(warning)
108
+ logger.info(f"Storage cleanup: removed {quota_results.get('cleaned', 0)} temporary files")
109
+
110
+ except Exception as e:
111
+ logger.error(f"Error in weekly maintenance: {str(e)}")
112
+
113
+ async def run_periodic_tasks():
114
+ """Run periodic maintenance tasks"""
115
+ daily_maintenance_run = False
116
+ weekly_maintenance_run = False
117
+
118
+ while True:
119
+ try:
120
+ now = datetime.utcnow()
121
+
122
+ # Check event reminders every minute
123
+ await check_event_reminders()
124
+
125
+ # Clean up old notifications daily
126
+ await cleanup_old_notifications()
127
+
128
+ # Run daily maintenance at 2 AM
129
+ if now.hour == 2 and not daily_maintenance_run:
130
+ await perform_daily_maintenance()
131
+ daily_maintenance_run = True
132
+ elif now.hour != 2:
133
+ daily_maintenance_run = False
134
+
135
+ # Run weekly maintenance on Sunday at 3 AM
136
+ if now.weekday() == 6 and now.hour == 3 and not weekly_maintenance_run:
137
+ await perform_weekly_maintenance()
138
+ weekly_maintenance_run = True
139
+ elif now.weekday() != 6 or now.hour != 3:
140
+ weekly_maintenance_run = False
141
+
142
+ # Wait before next check
143
+ await asyncio.sleep(60) # 1 minute
144
+ except Exception as e:
145
+ logger.error(f"Error in periodic tasks: {str(e)}")
146
+ await asyncio.sleep(60) # Wait before retrying
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
logs/admin_dashboard.log ADDED
File without changes