1
+ using StackExchange . Redis ;
2
+ using System . Text . Json ;
3
+ using Chatio . Dtos ;
4
+
5
+ namespace Chatio . Data . Hub ;
6
+
7
+ public class HubRepository : IHubRepository
8
+ {
9
+ private readonly IDatabase _database ;
10
+ private const string UserConnectionsKey = "user_connections" ;
11
+ private const string UserDetailsKey = "user_details" ;
12
+ private const string RoomKey = "room" ;
13
+ private const string RoomInfoKey = "room_info" ;
14
+ private const string UserRoomsKey = "user_rooms" ;
15
+
16
+ public HubRepository ( IConnectionMultiplexer redis )
17
+ {
18
+ _database = redis . GetDatabase ( ) ;
19
+ }
20
+
21
+ // Connection Management
22
+ public async Task AddConnectionAsync ( string connectionId , string userId , string username )
23
+ {
24
+ await _database . SetAddAsync ( $ "{ UserConnectionsKey } :{ userId } ", connectionId ) ;
25
+
26
+ // Save user details with username
27
+ var userDetails = new UserDetails
28
+ {
29
+ Id = userId ,
30
+ Username = username
31
+ } ;
32
+ await UpdateUserDetailsAsync ( userId , userDetails ) ;
33
+ }
34
+
35
+
36
+ public async Task RemoveConnectionAsync ( string connectionId , string userId )
37
+ {
38
+ await _database . SetRemoveAsync ( $ "{ UserConnectionsKey } :{ userId } ", connectionId ) ;
39
+
40
+ // Clean up if no more connections exist
41
+ if ( ! ( await _database . SetLengthAsync ( $ "{ UserConnectionsKey } :{ userId } ") > 0 ) )
42
+ {
43
+ await _database . KeyDeleteAsync ( $ "{ UserConnectionsKey } :{ userId } ") ;
44
+ // Don't remove user details as they might be needed later
45
+ }
46
+ }
47
+
48
+ public async Task < IEnumerable < string > > GetUserConnectionsAsync ( string userId )
49
+ {
50
+ var connections = await _database . SetMembersAsync ( $ "{ UserConnectionsKey } :{ userId } ") ;
51
+ return connections . Select ( c => c . ToString ( ) ) ;
52
+ }
53
+
54
+ // User Management
55
+ public async Task < UserDetails > GetUserDetailsAsync ( string userId )
56
+ {
57
+ var userDetailsJson = await _database . StringGetAsync ( $ "{ UserDetailsKey } :{ userId } ") ;
58
+ if ( userDetailsJson . IsNull )
59
+ {
60
+ return new UserDetails
61
+ {
62
+ Id = userId ,
63
+ Username = $ "User_{ userId . Substring ( 0 , 6 ) } " // Default if username not found
64
+ } ;
65
+ }
66
+ return JsonSerializer . Deserialize < UserDetails > ( userDetailsJson . ToString ( ) ) ! ;
67
+ }
68
+
69
+
70
+ public async Task UpdateUserDetailsAsync ( string userId , UserDetails details )
71
+ {
72
+ var json = JsonSerializer . Serialize ( details ) ;
73
+ await _database . StringSetAsync ( $ "{ UserDetailsKey } :{ userId } ", json ) ;
74
+ }
75
+
76
+ // Room Management
77
+ public async Task AddUserToRoomAsync ( string roomId , string userId )
78
+ {
79
+ await _database . SetAddAsync ( $ "{ RoomKey } :{ roomId } ", userId ) ;
80
+ await _database . SetAddAsync ( $ "{ UserRoomsKey } :{ userId } ", roomId ) ;
81
+ }
82
+
83
+ public async Task RemoveUserFromRoomAsync ( string roomId , string userId )
84
+ {
85
+ await _database . SetRemoveAsync ( $ "{ RoomKey } :{ roomId } ", userId ) ;
86
+ await _database . SetRemoveAsync ( $ "{ UserRoomsKey } :{ userId } ", roomId ) ;
87
+
88
+ // Clean up empty room
89
+ if ( ! ( await _database . SetLengthAsync ( $ "{ RoomKey } :{ roomId } ") > 0 ) )
90
+ {
91
+ await _database . KeyDeleteAsync ( $ "{ RoomKey } :{ roomId } ") ;
92
+ await _database . KeyDeleteAsync ( $ "{ RoomInfoKey } :{ roomId } ") ;
93
+ }
94
+ }
95
+
96
+ public async Task < IEnumerable < string > > GetUsersInRoomAsync ( string roomId )
97
+ {
98
+ var userIds = await _database . SetMembersAsync ( $ "{ RoomKey } :{ roomId } ") ;
99
+ return userIds . Select ( u => u . ToString ( ) ) ;
100
+ }
101
+
102
+ public async Task < IEnumerable < string > > GetUserRoomsAsync ( string userId )
103
+ {
104
+ var roomIds = await _database . SetMembersAsync ( $ "{ UserRoomsKey } :{ userId } ") ;
105
+ return roomIds . Select ( r => r . ToString ( ) ) ;
106
+ }
107
+
108
+ public async Task < RoomInfo > GetRoomInfoAsync ( string roomId )
109
+ {
110
+ var roomInfoJson = await _database . StringGetAsync ( $ "{ RoomInfoKey } :{ roomId } ") ;
111
+ if ( roomInfoJson . IsNull )
112
+ {
113
+ // Return default room info if not found
114
+ return new RoomInfo
115
+ {
116
+ Id = roomId ,
117
+ Name = $ "Room_{ roomId } ",
118
+ Type = "group" ,
119
+ CreatedAt = DateTime . UtcNow
120
+ } ;
121
+ }
122
+ return JsonSerializer . Deserialize < RoomInfo > ( roomInfoJson . ToString ( ) ) ! ;
123
+ }
124
+
125
+ public async Task UpdateRoomInfoAsync ( string roomId , RoomInfo info )
126
+ {
127
+ var json = JsonSerializer . Serialize ( info ) ;
128
+ await _database . StringSetAsync ( $ "{ RoomInfoKey } :{ roomId } ", json ) ;
129
+ }
130
+
131
+ // Helper methods for bulk operations
132
+ public async Task < List < UserInfoDto > > GetParticipantsAsync ( IEnumerable < string > userIds )
133
+ {
134
+ var participants = new List < UserInfoDto > ( ) ;
135
+ foreach ( var userId in userIds )
136
+ {
137
+ var details = await GetUserDetailsAsync ( userId ) ;
138
+ participants . Add ( new UserInfoDto
139
+ {
140
+ Id = userId ,
141
+ Username = details . Username
142
+ } ) ;
143
+ }
144
+ return participants ;
145
+ }
146
+ public async Task < List < UserInfoDto > > GetRoomParticipantsAsync ( string roomId )
147
+ {
148
+ // Get user IDs in the room
149
+ var userIds = await GetUsersInRoomAsync ( roomId ) ;
150
+
151
+ // Get participant details for each user ID
152
+ return await GetParticipantsAsync ( userIds ) ;
153
+ }
154
+
155
+ // Optional: Methods for handling message history
156
+ public async Task SaveMessageAsync ( string roomId , MessageDto message )
157
+ {
158
+ var messageJson = JsonSerializer . Serialize ( message ) ;
159
+ await _database . ListRightPushAsync ( $ "messages:{ roomId } ", messageJson ) ;
160
+ // Optionally trim the list to maintain a maximum history
161
+ await _database . ListTrimAsync ( $ "messages:{ roomId } ", 0 , 99 ) ; // Keep last 100 messages
162
+ }
163
+
164
+ public async Task < List < MessageDto > > GetMessageHistoryAsync ( string roomId , int count = 50 )
165
+ {
166
+ var messages = await _database . ListRangeAsync ( $ "messages:{ roomId } ", - count , - 1 ) ;
167
+ return messages
168
+ . Select ( m => JsonSerializer . Deserialize < MessageDto > ( m . ToString ( ) ) ! )
169
+ . ToList ( ) ;
170
+ }
171
+
172
+ public async Task < string ? > GetUserIdByConnectionAsync ( string connectionId )
173
+ {
174
+ var server = _database . Multiplexer . GetServer ( _database . Multiplexer . GetEndPoints ( ) . First ( ) ) ;
175
+
176
+ foreach ( var key in server . Keys ( pattern : $ "{ UserConnectionsKey } :*") )
177
+ {
178
+ if ( await _database . SetContainsAsync ( key , connectionId ) )
179
+ {
180
+ return key . ToString ( ) . Split ( ':' ) . Last ( ) ; // Extract userId from the key
181
+ }
182
+ }
183
+
184
+ return null ;
185
+ }
186
+
187
+ public async Task < IEnumerable < string > > GetConnectionIdsByUserIdAsync ( string userId )
188
+ {
189
+ var connections = await _database . SetMembersAsync ( $ "{ UserConnectionsKey } :{ userId } ") ;
190
+ return connections . Select ( c => c . ToString ( ) ) ;
191
+ }
192
+
193
+ }
0 commit comments