@@ -13,6 +13,7 @@ import (
1313// UserStats represents a user's game statistics
1414type UserStats struct {
1515 Username string
16+ SSHKeyFingerprint string
1617 GamesPlayed int
1718 GamesWon int
1819 GamesLost int
@@ -54,7 +55,8 @@ func NewStore(dbPath string, logger *log.Logger) (*Store, error) {
5455func (s * Store ) initSchema () error {
5556 schema := `
5657 CREATE TABLE IF NOT EXISTS user_stats (
57- username TEXT PRIMARY KEY,
58+ username TEXT NOT NULL,
59+ ssh_key_fingerprint TEXT NOT NULL,
5860 games_played INTEGER DEFAULT 0,
5961 games_won INTEGER DEFAULT 0,
6062 games_lost INTEGER DEFAULT 0,
@@ -71,11 +73,13 @@ func (s *Store) initSchema() error {
7173 last_word_date TEXT,
7274 last_game_result TEXT,
7375 created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
74- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
76+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
77+ PRIMARY KEY (username, ssh_key_fingerprint)
7578 );
7679
7780 CREATE INDEX IF NOT EXISTS idx_last_played ON user_stats(last_played);
7881 CREATE INDEX IF NOT EXISTS idx_games_won ON user_stats(games_won DESC);
82+ CREATE INDEX IF NOT EXISTS idx_ssh_key ON user_stats(ssh_key_fingerprint);
7983 `
8084
8185 if _ , err := s .db .Exec (schema ); err != nil {
@@ -94,6 +98,7 @@ func (s *Store) scanUserStats(scanner interface {
9498
9599 err := scanner .Scan (
96100 & stats .Username ,
101+ & stats .SSHKeyFingerprint ,
97102 & stats .GamesPlayed ,
98103 & stats .GamesWon ,
99104 & stats .GamesLost ,
@@ -122,50 +127,72 @@ func (s *Store) scanUserStats(scanner interface {
122127 return nil
123128}
124129
125- // GetUserStats retrieves statistics for a user
126- func (s * Store ) GetUserStats (username string ) (* UserStats , error ) {
130+ // GetUserStats retrieves statistics for a user by username AND ssh_key_fingerprint pair
131+ func (s * Store ) GetUserStats (username string , sshKeyFingerprint string ) (* UserStats , error ) {
132+ s .logger .Debug ("Reading user stats" , "username" , username , "ssh_key_fingerprint" , sshKeyFingerprint )
133+
127134 query := `
128- SELECT username, games_played, games_won, games_lost, current_streak, max_streak,
135+ SELECT username, ssh_key_fingerprint, games_played, games_won, games_lost, current_streak, max_streak,
129136 guess_dist_1, guess_dist_2, guess_dist_3, guess_dist_4, guess_dist_5, guess_dist_6,
130137 total_guesses, last_played, COALESCE(last_word_date, ''), COALESCE(last_game_result, '')
131138 FROM user_stats
132- WHERE username = ?
139+ WHERE username = ? AND ssh_key_fingerprint = ?
133140 `
134141
135142 var stats UserStats
136143
137- err := s .scanUserStats (s .db .QueryRow (query , username ), & stats )
144+ err := s .scanUserStats (s .db .QueryRow (query , username , sshKeyFingerprint ), & stats )
138145 if errors .Is (err , sql .ErrNoRows ) {
139146 // Return empty stats for new user
147+ s .logger .Debug ("No existing stats found, returning empty stats for new user" , "username" , username )
140148 return & UserStats {
141- Username : username ,
149+ Username : username ,
150+ SSHKeyFingerprint : sshKeyFingerprint ,
142151 }, nil
143152 }
144153
145154 if err != nil {
146155 return nil , fmt .Errorf ("failed to get user stats: %w" , err )
147156 }
148157
158+ s .logger .Debug ("Successfully retrieved user stats" ,
159+ "username" , username ,
160+ "games_played" , stats .GamesPlayed ,
161+ "games_won" , stats .GamesWon ,
162+ "current_streak" , stats .CurrentStreak ,
163+ "last_word_date" , stats .LastWordDate ,
164+ )
165+
149166 return & stats , nil
150167}
151168
152169// HasPlayedToday checks if the user has already played today's word
153- func (s * Store ) HasPlayedToday (username string , wordDate string ) (bool , error ) {
154- stats , err := s .GetUserStats (username )
170+ func (s * Store ) HasPlayedToday (username string , sshKeyFingerprint string , wordDate string ) (bool , error ) {
171+ s .logger .Debug ("Checking if user has played today" , "username" , username , "word_date" , wordDate )
172+
173+ stats , err := s .GetUserStats (username , sshKeyFingerprint )
155174 if err != nil {
156175 return false , err
157176 }
158177
159- return stats .LastWordDate == wordDate , nil
178+ hasPlayed := stats .LastWordDate == wordDate
179+ s .logger .Debug ("Played today check result" ,
180+ "username" , username ,
181+ "has_played" , hasPlayed ,
182+ "last_word_date" , stats .LastWordDate ,
183+ "current_word_date" , wordDate ,
184+ )
185+
186+ return hasPlayed , nil
160187}
161188
162189// RecordWin records a winning game for a user
163- func (s * Store ) RecordWin (username string , guesses int , wordDate string , gameResult string ) error {
190+ func (s * Store ) RecordWin (username string , sshKeyFingerprint string , guesses int , wordDate string , gameResult string ) error {
164191 if guesses < 1 || guesses > 6 {
165192 return fmt .Errorf ("invalid number of guesses: %d" , guesses )
166193 }
167194
168- stats , err := s .GetUserStats (username )
195+ stats , err := s .GetUserStats (username , sshKeyFingerprint )
169196 if err != nil {
170197 return err
171198 }
@@ -192,8 +219,8 @@ func (s *Store) RecordWin(username string, guesses int, wordDate string, gameRes
192219}
193220
194221// RecordLoss records a losing game for a user
195- func (s * Store ) RecordLoss (username string , wordDate string , gameResult string ) error {
196- stats , err := s .GetUserStats (username )
222+ func (s * Store ) RecordLoss (username string , sshKeyFingerprint string , wordDate string , gameResult string ) error {
223+ stats , err := s .GetUserStats (username , sshKeyFingerprint )
197224 if err != nil {
198225 return err
199226 }
@@ -215,13 +242,27 @@ func (s *Store) RecordLoss(username string, wordDate string, gameResult string)
215242
216243// saveUserStats saves or updates user statistics
217244func (s * Store ) saveUserStats (stats * UserStats ) error {
245+ // Create a hash of the SSH public key for logging
246+
247+ s .logger .Debug ("Saving user stats" ,
248+ "username" , stats .Username ,
249+ "ssh_key_fingerprint" , stats .SSHKeyFingerprint ,
250+ "games_played" , stats .GamesPlayed ,
251+ "games_won" , stats .GamesWon ,
252+ "games_lost" , stats .GamesLost ,
253+ "current_streak" , stats .CurrentStreak ,
254+ "max_streak" , stats .MaxStreak ,
255+ "total_guesses" , stats .TotalGuesses ,
256+ "last_word_date" , stats .LastWordDate ,
257+ )
258+
218259 query := `
219260 INSERT INTO user_stats (
220- username, games_played, games_won, games_lost, current_streak, max_streak,
261+ username, ssh_key_fingerprint, games_played, games_won, games_lost, current_streak, max_streak,
221262 guess_dist_1, guess_dist_2, guess_dist_3, guess_dist_4, guess_dist_5, guess_dist_6,
222263 total_guesses, last_played, last_word_date, last_game_result, updated_at
223- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
224- ON CONFLICT(username) DO UPDATE SET
264+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
265+ ON CONFLICT(username, ssh_key_fingerprint ) DO UPDATE SET
225266 games_played = excluded.games_played,
226267 games_won = excluded.games_won,
227268 games_lost = excluded.games_lost,
@@ -242,6 +283,7 @@ func (s *Store) saveUserStats(stats *UserStats) error {
242283
243284 _ , err := s .db .Exec (query ,
244285 stats .Username ,
286+ stats .SSHKeyFingerprint ,
245287 stats .GamesPlayed ,
246288 stats .GamesWon ,
247289 stats .GamesLost ,
@@ -263,6 +305,7 @@ func (s *Store) saveUserStats(stats *UserStats) error {
263305 return fmt .Errorf ("failed to save user stats: %w" , err )
264306 }
265307
308+ s .logger .Debug ("Successfully saved user stats" , "username" , stats .Username , "ssh_key_fingerprint" , stats .SSHKeyFingerprint )
266309 return nil
267310}
268311
0 commit comments