11import logging
22from typing import List , Optional
33
4- from fastapi import APIRouter , Depends , HTTPException , Query
4+ from fastapi import APIRouter , BackgroundTasks , Depends , Query
55
66from primary .services .database_access .snapshot_access .types import (
77 NewSnapshot ,
8- SnapshotUpdate ,
9- SortBy ,
10- SortDirection ,
8+ SnapshotSortBy ,
9+ SnapshotAccessLogSortBy ,
1110)
1211from primary .middleware .add_browser_cache import no_cache
1312from primary .services .database_access .snapshot_access .snapshot_access import SnapshotAccess
14- from primary .services .database_access .snapshot_access .snapshot_logs_access import SnapshotLogsAccess
15- from primary .services .database_access .snapshot_access .query_collation_options import QueryCollationOptions
13+ from primary .services .database_access .snapshot_access .snapshot_log_access import SnapshotLogAccess
14+ from primary .services .database_access .query_collation_options import QueryCollationOptions , SortDirection
15+ from primary .services .database_access .workers .mark_logs_deleted import mark_logs_deleted_worker
1616
1717
1818from primary .auth .auth_helper import AuthHelper , AuthenticatedUser
3333@router .get ("/recent_snapshots" , response_model = list [schemas .SnapshotAccessLog ])
3434async def get_recent_snapshots (
3535 user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user ),
36- sort_by : Optional [SortBy ] = Query (SortBy . LAST_VISIT , description = "Sort the result by" ),
37- sort_direction : Optional [SortDirection ] = Query (SortDirection . DESC , description = "Sort direction: 'asc' or 'desc'" ),
38- limit : Optional [int ] = Query (5 , ge = 1 , le = 100 , description = "Limit the number of results" ),
39- offset : Optional [int ] = Query (0 , ge = 0 , description = "The offset of the results" ),
36+ sort_by : Optional [SnapshotAccessLogSortBy ] = Query (None , description = "Sort the result by" ),
37+ sort_direction : Optional [SortDirection ] = Query (None , description = "Sort direction: 'asc' or 'desc'" ),
38+ limit : Optional [int ] = Query (None , ge = 1 , le = 100 , description = "Limit the number of results" ),
39+ offset : Optional [int ] = Query (None , ge = 0 , description = "The offset of the results" ),
4040) -> list [schemas .SnapshotAccessLog ]:
41- async with (
42- SnapshotAccess .create (user .get_user_id ()) as snapshot_access ,
43- SnapshotLogsAccess .create (user .get_user_id ()) as log_access ,
44- ):
41+ async with SnapshotLogAccess .create (user .get_user_id ()) as log_access :
4542 collation_options = QueryCollationOptions (sort_by = sort_by , sort_dir = sort_direction , limit = limit , offset = offset )
4643
4744 recent_logs = await log_access .get_access_logs_for_user_async (collation_options )
4845
49- payload : list [schemas .SnapshotAccessLog ] = []
50-
51- for log in recent_logs :
52- metadata = await snapshot_access .get_snapshot_metadata_async (log .snapshot_id , log .snapshot_owner_id )
53-
54- payload .append (to_api_snapshot_access_log (log , metadata ))
55-
56- return payload
46+ return [to_api_snapshot_access_log (log ) for log in recent_logs ]
5747
5848
5949@router .get ("/snapshots" , response_model = List [schemas .SnapshotMetadata ])
6050@no_cache
6151async def get_snapshots_metadata (
6252 user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user ),
63- sort_by : Optional [SortBy ] = Query (SortBy . LAST_VISIT , description = "Sort the result by" ),
53+ sort_by : Optional [SnapshotSortBy ] = Query (SnapshotSortBy . UPDATED_AT , description = "Sort the result by" ),
6454 sort_direction : Optional [SortDirection ] = Query (SortDirection .DESC , description = "Sort direction: 'asc' or 'desc'" ),
6555 limit : Optional [int ] = Query (10 , ge = 1 , le = 100 , description = "Limit the number of results" ),
6656) -> List [schemas .SnapshotMetadata ]:
6757 access = SnapshotAccess .create (user .get_user_id ())
6858 async with access :
6959 items = await access .get_filtered_snapshots_metadata_for_user_async (
70- sort_by = sort_by , sort_direction = sort_direction , limit = limit
60+ sort_by = sort_by , sort_direction = sort_direction , limit = limit , offset = 0
7161 )
7262 return [to_api_snapshot_metadata_summary (item ) for item in items ]
7363
@@ -77,58 +67,54 @@ async def get_snapshots_metadata(
7767async def get_snapshot (
7868 snapshot_id : str , user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user )
7969) -> schemas .Snapshot :
80- access = SnapshotAccess .create (user .get_user_id ())
81- logs_access = SnapshotLogsAccess .create (user_id = user .get_user_id ())
82-
83- async with access , logs_access :
84- snapshot = await access .get_snapshot_by_id_async (snapshot_id )
85- if not snapshot :
86- raise HTTPException (status_code = 404 , detail = "Snapshot not found" )
87-
88- await logs_access .log_snapshot_visit_async (snapshot_id , snapshot .owner_id )
89-
70+ snapshot_access = SnapshotAccess .create (user .get_user_id ())
71+ log_access = SnapshotLogAccess .create (user_id = user .get_user_id ())
72+
73+ async with snapshot_access , log_access :
74+ snapshot = await snapshot_access .get_snapshot_by_id_async (snapshot_id )
75+ # Should we clear the log if a snapshot was not found? This could mean that the snapshot was
76+ # deleted but deletion of logs has failed
77+ await log_access .log_snapshot_visit_async (snapshot_id , snapshot .owner_id )
9078 return to_api_snapshot (snapshot )
9179
9280
9381@router .get ("/snapshots/metadata/{snapshot_id}" , response_model = schemas .SnapshotMetadata )
9482@no_cache
95- async def get_snapshot_metadata (snapshot_id : str , user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user )):
83+ async def get_snapshot_metadata (
84+ snapshot_id : str , user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user )
85+ ) -> schemas .SnapshotMetadata :
9686 access = SnapshotAccess .create (user .get_user_id ())
9787 async with access :
9888 metadata = await access .get_snapshot_metadata_async (snapshot_id )
99- if not metadata :
100- raise HTTPException (status_code = 404 , detail = "Session metadata not found" )
10189 return to_api_snapshot_metadata (metadata )
10290
10391
10492@router .post ("/snapshots" , response_model = str )
10593async def create_snapshot (
10694 session : NewSnapshot , user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user )
10795) -> str :
108- access = SnapshotAccess .create (user .get_user_id ())
109- logs_access = SnapshotLogsAccess .create (user .get_user_id ())
96+ snapshot_access = SnapshotAccess .create (user .get_user_id ())
97+ log_access = SnapshotLogAccess .create (user .get_user_id ())
11098
111- async with access , logs_access :
112- snapshot_id = await access .insert_snapshot_async (session )
99+ async with snapshot_access , log_access :
100+ snapshot_id = await snapshot_access .insert_snapshot_async (session )
113101
114- # We count snapshot creation as implicit visit. This also makes it so
115- await logs_access .log_snapshot_visit_async (snapshot_id = snapshot_id , snapshot_owner_id = user .get_user_id ())
102+ # We count snapshot creation as implicit visit. This also makes it so we can get recently created ones alongside other shared screenshots
103+ await log_access .log_snapshot_visit_async (snapshot_id = snapshot_id , snapshot_owner_id = user .get_user_id ())
116104 return snapshot_id
117105
118106
119- @router .put ("/snapshots/{snapshot_id}" )
120- async def update_snapshot (
107+ @router .delete ("/snapshots/{snapshot_id}" )
108+ async def delete_snapshot (
121109 snapshot_id : str ,
122- snapshot_update : SnapshotUpdate ,
110+ background_tasks : BackgroundTasks ,
123111 user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user ),
124- ):
125- access = SnapshotAccess .create (user .get_user_id ())
126- async with access :
127- await access .update_snapshot_metadata_async (snapshot_id , snapshot_update )
128-
129-
130- @router .delete ("/snapshots/{snapshot_id}" )
131- async def delete_snapshot (snapshot_id : str , user : AuthenticatedUser = Depends (AuthHelper .get_authenticated_user )):
132- access = SnapshotAccess .create (user .get_user_id ())
133- async with access :
134- await access .delete_snapshot_async (snapshot_id )
112+ ) -> None :
113+ snapshot_access = SnapshotAccess .create (user .get_user_id ())
114+ async with snapshot_access :
115+ await snapshot_access .delete_snapshot_async (snapshot_id )
116+
117+ # This is the fastest solution for the moment. As we are expecting <= 150 logs per snapshot
118+ # and consistency is not critical, we can afford to do this in the background and without
119+ # a safety net. We can later consider adding this to a queue for better reliability.
120+ background_tasks .add_task (mark_logs_deleted_worker , snapshot_id = snapshot_id )
0 commit comments