|
| 1 | +from app.schemas.information import InstanceStatisticsOut |
1 | 2 | import contextlib |
2 | 3 | import json |
3 | 4 | import platform |
| 5 | +from datetime import datetime, timedelta, timezone |
4 | 6 |
|
5 | 7 | from fastapi import ( |
6 | 8 | APIRouter, |
7 | 9 | Request, |
8 | 10 | __version__ as fastapi_version, |
9 | 11 | ) |
10 | 12 | from sqlalchemy import text |
| 13 | +from sqlmodel import func, select |
11 | 14 |
|
12 | 15 | from app.deps import RedisDep, SessionDep |
13 | | -from app.models.information import InformationOut |
| 16 | +from app.models.files import ( |
| 17 | + File, |
| 18 | +) |
| 19 | +from app.schemas.information import InformationOut |
14 | 20 |
|
15 | 21 | router = APIRouter() |
16 | 22 |
|
@@ -66,3 +72,78 @@ async def get_instance_information( |
66 | 72 | commit=build_info["commit"], |
67 | 73 | is_release=build_info["is_release"], |
68 | 74 | ) |
| 75 | + |
| 76 | + |
| 77 | +@router.get("/instance/statistics") |
| 78 | +async def get_instance_statistics( |
| 79 | + session: SessionDep, |
| 80 | + redis: RedisDep, |
| 81 | +): |
| 82 | + now = datetime.now(timezone.utc) |
| 83 | + soon = now + timedelta(days=1) |
| 84 | + |
| 85 | + # Redis Execution |
| 86 | + active_rooms_keys = await redis.keys("chithi:room:*") |
| 87 | + active_rooms = len(active_rooms_keys) |
| 88 | + |
| 89 | + # Total bytes |
| 90 | + sum_bytes_query = select(func.coalesce(func.sum(File.size), 0)).select_from(File) |
| 91 | + total_bytes = (await session.exec(sum_bytes_query)).one() |
| 92 | + |
| 93 | + # Total files |
| 94 | + total_files_query = select(func.count()).select_from(File) |
| 95 | + total_files = (await session.exec(total_files_query)).one() |
| 96 | + |
| 97 | + # Total downloads |
| 98 | + total_downloads_query = select(func.coalesce(func.sum(File.download_count), 0)).select_from(File) |
| 99 | + total_downloads = (await session.exec(total_downloads_query)).one() |
| 100 | + |
| 101 | + # Active URLs |
| 102 | + active_urls_query = ( |
| 103 | + select(func.count()) |
| 104 | + .select_from(File) |
| 105 | + .where( |
| 106 | + (File.expires_at >= now) |
| 107 | + & (File.download_count < File.expire_after_n_download) |
| 108 | + ) |
| 109 | + ) |
| 110 | + active_urls = (await session.exec(active_urls_query)).one() |
| 111 | + |
| 112 | + # Expiring soon (within 24h and not already expired) |
| 113 | + expiring_soon_query = ( |
| 114 | + select(func.count()) |
| 115 | + .select_from(File) |
| 116 | + .where( |
| 117 | + (File.expires_at >= now) |
| 118 | + & (File.expires_at <= soon) |
| 119 | + & (File.download_count < File.expire_after_n_download) |
| 120 | + ) |
| 121 | + ) |
| 122 | + expiring_soon = (await session.exec(expiring_soon_query)).one() |
| 123 | + |
| 124 | + # Links with download caps |
| 125 | + # Assuming any file has a download cap as it's not nullable in DB |
| 126 | + links_with_download_caps_query = select(func.count()).select_from(File) |
| 127 | + links_with_download_caps = ( |
| 128 | + await session.exec(links_with_download_caps_query) |
| 129 | + ).one() |
| 130 | + |
| 131 | + # Latest expiry |
| 132 | + latest_expiry_query = select(func.max(File.expires_at)).where( |
| 133 | + (File.expires_at >= now) & (File.download_count < File.expire_after_n_download) |
| 134 | + ) |
| 135 | + latest_expiry = (await session.exec(latest_expiry_query)).one() |
| 136 | + |
| 137 | + meta = { |
| 138 | + "total_bytes": total_bytes, |
| 139 | + "total_files": total_files, |
| 140 | + "total_downloads": total_downloads, |
| 141 | + "active_urls": active_urls, |
| 142 | + "active_rooms": active_rooms, |
| 143 | + "links_with_download_caps": links_with_download_caps, |
| 144 | + "expiring_soon": expiring_soon, |
| 145 | + } |
| 146 | + if latest_expiry: |
| 147 | + meta["latest_expiry"] = int(latest_expiry.timestamp()) |
| 148 | + |
| 149 | + return InstanceStatisticsOut(**meta) |
0 commit comments