Skip to content

feat(website): sync user with backend on login, use internal user ID#1202

Open
fhennig wants to merge 6 commits into
feat/user-tablefrom
feat/user-frontend
Open

feat(website): sync user with backend on login, use internal user ID#1202
fhennig wants to merge 6 commits into
feat/user-tablefrom
feat/user-frontend

Conversation

@fhennig
Copy link
Copy Markdown
Contributor

@fhennig fhennig commented May 6, 2026

resolves #1190

Summary

  • On GitHub login, mapProfileToUser calls POST /users/sync to upsert the user in the backend and stores the returned internal ID as gsUserId in the better-auth session (stateless JWE cookie)
  • If the sync fails, login is aborted (no silent fallback)
  • Replaces the use of the GitHub ID for ownership checks — collections and subscriptions now use the internal numeric user ID throughout
  • Owner name is resolved via GET /users/{id} and displayed on the collection detail page

Changes

Auth & middleware

  • auth.ts: mapProfileToUser syncs user with backend, stores gsUserId (number); throws on failure to abort login
  • authMiddleware.ts: reads gsUserId from session into Astro.locals
  • backendProxy.ts: forwards gsUserId as userId query param

Types & API

  • types/Collection.ts: ownedBy changed from string to number to match backend Long
  • types/PublicUser.ts: new Zod schema for GET /users/{id} response
  • backendService.ts: adds getUser() to resolve owner names server-side
  • pages/api/users/[id].ts: new proxy route for public user lookup

Collection pages

  • Detail page: fetches and displays owner name (falls back to numeric ID on error); ownerName is always a string
  • Edit page: uses gsUserId for ownership check

Tests

  • Unit/browser specs: fix ownedBy from string to number in fixtures
  • E2E tests: sync E2E user via POST /users/sync in beforeAll to obtain internal ID for collection ownership

Notes

This branch depends on the backend changes in feat/user-table (users table, POST /users/sync, GET /users/{id} endpoints).

🤖 Generated with Claude Code

fhennig and others added 5 commits May 5, 2026 16:19
Adds a users_table (BIGSERIAL PK, nullable github_id) with upsert via
POST /users/sync and lookup via GET /users/{id}. Migrates owned_by and
user_id columns in collections and subscriptions from GitHub ID strings
to BIGINT foreign keys referencing the new table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…/users/{id}

Extracts the shared now() instant helper into util/InstantProvider.kt to
avoid duplication across models. Adds PublicUser DTO (id + name only) so
the public GET /users/{id} endpoint does not expose email.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…trollerTest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboards Ready Ready Preview, Comment May 8, 2026 5:37pm

Request Review

…for ownership

On GitHub login, mapProfileToUser calls POST /users/sync to upsert the user
in the backend and stores the returned internal Long ID as internalUserId in
the better-auth session (stateless JWE cookie). This replaces the previous
use of the GitHub ID for ownership checks on collections and subscriptions.

- auth.ts: async mapProfileToUser syncs user, adds internalUserId additional field
- authMiddleware.ts: reads internalUserId from session user
- backendProxy.ts: forwards internalUserId as userId query param instead of githubId
- Collection.ts: ownedBy changed from string to number to match backend Long
- PublicUser.ts: new Zod schema for GET /users/{id} response
- backendService.ts: adds getUser() to resolve owner names
- pages/api/users/[id].ts: new proxy route for public user lookup
- collection detail/edit pages: use internalUserId for ownership, display owner name
- E2E tests: sync user via POST /users/sync in beforeAll to get internal ID

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

fix(website): fix lint errors in auth.ts and authMiddleware.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

refactor(website): rename internalUserId to gsUserId

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

fix(website): make login fail if backend user sync fails, simplify gsUserId handling

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

fix(website): make ownerName non-optional, simplify collection detail page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

refactor(website): simplify gsUserId assignment in authMiddleware

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

foo
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the website’s authentication + ownership model to use a backend-issued internal numeric user ID (synced on GitHub login) instead of the GitHub user ID, and uses that to display a human-readable owner name on collection detail pages.

Changes:

  • Sync GitHub users to the backend on login (POST /users/sync) and store the returned internal ID (gsUserId) in the session; propagate gsUserId via middleware and backend proxy.
  • Switch collection/subscription ownership logic to use numeric internal user IDs (ownedBy: number), and resolve/display owner name via GET /users/{id}.
  • Update unit/browser/E2E tests and fixtures for the new numeric ownership ID model.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
website/src/auth.ts Sync user on GitHub login and store internal gsUserId in the session
website/src/middleware/authMiddleware.ts Copy gsUserId into Astro.locals and switch locals “logged out” to undefined
website/src/env.d.ts Update locals typing (undefined + add gsUserId)
website/src/backendApi/backendProxy.ts Forward internal numeric user id as userId query param
website/src/backendApi/backendService.ts Add getUser() for owner name resolution
website/src/types/PublicUser.ts New Zod schema/type for public user response
website/src/types/Collection.ts Change ownedBy from string to number
website/src/pages/api/users/[id].ts Add unauthenticated proxy route for public user lookup
website/src/pages/collections/[organism]/[id]/index.astro Redirect handling + owner name lookup + internal ID ownership check
website/src/pages/collections/[organism]/[id]/edit.astro Use internal gsUserId for ownership checks
website/src/components/collections/detail/CollectionDetail.tsx Display resolved owner name (string)
website/src/pages/collections/[organism]/index.astro Switch “logged in” checks to user !== undefined
website/src/pages/collections/[organism]/create.astro Switch “logged in” checks to user !== undefined
website/src/pages/subscriptions/index.astro Switch “logged in” checks to user !== undefined
website/src/pages/subscriptions/create.astro Switch “logged in” checks to user !== undefined
website/src/layouts/base/header/HamburgerMenu.astro Switch “logged in” checks to user !== undefined
website/src/backendApi/backendService.spec.ts Update fixture to numeric ownedBy
website/src/components/collections/overview/CollectionsOverview.browser.spec.tsx Update fixtures to numeric ownedBy
website/tests/helpers/auth.ts Update E2E session cookie payload to use gsUserId
website/tests/collections/collectionForm.spec.ts Sync E2E user via backend and use internal id for collection operations
website/tests/collections/collectionDetail.spec.ts Sync E2E user via backend and use internal id for collection operations
Comments suppressed due to low confidence (1)

website/src/pages/collections/[organism]/[id]/index.astro:83

  • Because the server code above now returns early when collection === undefined, this render path can never hit the fallback branch. Simplify the template by removing the now-unreachable conditional/fallback (or revert the early redirect if you still want to show a “failed to load” message).
                lapisConfig={lapisConfig}
                isOwner={userIsOwner}
                ownerName={ownerName}
                client:load
            />

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread website/src/auth.ts

if (response.ok) {
const user = (await response.json()) as { id: number };
return { gsUserId: String(user.id) };
createdAt: now.toISOString(),
updatedAt: now.toISOString(),
githubId: '1234567',
gsUserId: '1',
Comment on lines 37 to +41
logger.error(`Failed to fetch collection ${id}: ${getErrorLogMessage(error)}`);
}

const collectionTitle = collection !== undefined ? `#${id} ${collection.name}` : `Collection #${id}`;
if (collection === undefined) {
return Astro.redirect('/404');
@fhennig fhennig force-pushed the feat/user-table branch from 39678b6 to 808d0b4 Compare May 14, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants