Feature/profile schema integration#4
Open
amormousa wants to merge 5 commits into
Open
Conversation
- Add UpdateProfileStats to upsert cached aggregate stats after session processing - Add GetProfileStats model function for reading cached profile stats - Add RevokeAllUserRefreshTokens and CleanupExpiredRefreshTokens for token management - Add POST /api/auth/logout endpoint to revoke refresh token and clear cookie - Add periodic cleanup of expired refresh tokens on token refresh - Populate user_profile_stats table from session processor background job - Extend User model with all profile fields from migrations
This reverts commit 670b0eb.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Profile Page: Database Schema + Backend Bug Fixes
Summary
Adds all database schema needed to power the new
/api/profilepage, andfixes five correctness bugs in the session-processing and auth code that the
profile page depends on — bugs that would otherwise have caused wrong or
missing numbers on the profile screen.
1. Database schema for the profile page
Why: the profile page (bio card, metrics row, activity heatmap,
achievements list, recent activity feed, personal-info completion ring)
needs data that didn't exist in the schema before —
usersonly had genericaccount fields, and there was no table for cached stats, achievements, or an
activity feed.
What changed:
005_extend_users.sqlfirst_name,role,location,university,time_zone,last_active_at,gender,languagesonusers006_profile_stats.sqluser_profile_stats(total_coding_seconds,total_active_days,current_streak,max_streak)007_achievements.sqlachievement_types+user_achievements008_activity_log.sqlactivity_log, plus indexes onsessions(user_id, start_time)andheartbeats(user_id, time)009_problem_stats_placeholder.sqluser_problem_stats, explicitly marked unusedsolved/totalProblems/attemptingwithout inventing a fake problem-solving feature — nothing populates this table yetseed_profile.sqltestuseraccountWhen used:
user_profile_statsis written by the background session processor(
UpdateProfileStats) every time it processes new heartbeats, notrecomputed live on each profile view.
user_achievements/activity_logare read-only in this PR — nothingwrites to them outside the seed script, since achievement-awarding logic
is a separate feature.
user_problem_statsis not read or written anywhere yet; it exists so the/api/profileresponse shape can include those fields without a latermigration, and will show zeros until a real feature lands.
2. Swagger:
/api/profiledocumentedAdded the
/api/profilepath (BearerAuth, 200/401) and fulldefinitionsfor
models.ProfileResponseand its nested types (Achievement,ActivityLogItem,HeatmapCell,ProfileMetric,ProfileInfoField), sothe response shape is fully typed in the docs instead of a generic
helpers.APIResponse.3. Bug fixes affecting profile-page data accuracy
Not new features — fixes for cases where Total Coding Time, Active Days, or
Streak could come out wrong, or the page could 500 for new users.
3a.
session_processor.go— session building could double-count timesingle DB transaction, so a mid-run failure can't leave orphaned sessions
that get rebuilt and duplicated on the next run.
pg_try_advisory_lock, key918273)around the whole run, so the manual
POST /api/admin/process-sessionstrigger can never run concurrently with the periodic background run. If
the lock is already held, the run logs that it skipped and returns
cleanly, not as an error.
user_id, project, timetouser_id, timeonly, so sessions are built from true chronological order. Previously, a
user switching between two projects in the same window could produce two
sessions with overlapping start/end times, double-counting duration when
summed into
total_coding_seconds.3b.
models/profile.go—GetProfileStats500'd for brand-new usersA user with no row yet in
user_profile_stats(no sessions processed) usedto hit
pgx.ErrNoRowsas a real error. Now matches the pattern already usedelsewhere in the codebase (
models/user.go): returns(nil, nil), a normalempty state.
3c.
models/profile.go—UpdateProfileStatssilently wrote wrong data on query failureThe two aggregate queries (
SUM(duration_seconds),COUNT(DISTINCT start_time::date)) had their errors discarded; a transientfailure would write zeroed stats over a user's real coding-time history.
Both now return their error instead of being ignored.
3d.
handlers/auth.go— cookieSameSitemismatch broke refresh tokens locallyBoth
setRefreshTokenCookieand the newLogouthandler now consistentlyuse
SameSite: "Lax"(previously"None", which browsers reject onhttpunless
Secureis also true — silently breaking cookie set/clear in localdev).
3e.
handlers/auth.go— unsafeuserIDtype assertions could panicReplaced
userID := c.Locals("userID").(string)with a checked assertionreturning
401 Unauthorizedon failure, applied consistently across everyauthenticated handler project-wide —
auth.go,filters.go,goals.go,heartbeat.go,settings.go,stats.go.3f.
handlers/auth.go—CompleteSignupignored username-lookup errorsFindUserByUsername's error is now checked and returns500if the lookupitself failed, instead of silently treating a failed lookup as "username
available."
Also included
POST /api/auth/logout: revokes the current refresh token and clears thecookie.
RefreshAccessTokennow callsCleanupExpiredRefreshTokensafter eachrefresh so
refresh_tokensdoesn't grow unbounded.RevokeAllUserRefreshTokensadded tomodels/refresh_token.go(not yetcalled anywhere — available for a future "log out everywhere" feature).
apps/web/angular.json/package.json/bun.lock: picked up theanalyticsid, thefdirdependency, and consistent JSON formatting frommerging in the profile-page frontend branch. No conflict markers remain.
Testing
seed_profile.sql, confirmtestusershows realistic profile data.POST /api/admin/process-sessionstwice in quick succession,confirm no duplicate sessions and the second call logs "skipped".
instead of a panic.
http, confirm the refresh-token cookie is setand
/api/auth/refreshand/api/auth/logoutboth work.bun installandgo build ./...both succeed from a clean checkout.