Skip to content

fix(fortnox): use dedicated atomic locks collection for refresh + add concurrency tests#1079

Open
Patrikbjoh wants to merge 1 commit intostack-auth:mainfrom
Patrikbjoh:fix/fortnox-locking
Open

fix(fortnox): use dedicated atomic locks collection for refresh + add concurrency tests#1079
Patrikbjoh wants to merge 1 commit intostack-auth:mainfrom
Patrikbjoh:fix/fortnox-locking

Conversation

@Patrikbjoh
Copy link

This PR adds a dedicated collection with a unique index and TTL index, refactors to create/remove locks atomically during token refresh, and adds concurrency integration tests simulating lock collisions. Migration included: .

Copilot AI review requested due to automatic review settings December 30, 2025 07:16
@vercel
Copy link

vercel bot commented Dec 30, 2025

@Patrikbjoh is attempting to deploy a commit to the Stack Auth Team on Vercel.

A member of the Team first needs to authorize it.

@cmux-agent
Copy link

cmux-agent bot commented Dec 30, 2025

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 30, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ N2D4
❌ Patrikbjoh
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link

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 includes significant refactoring and new features, though the title suggests it's focused on Fortnox atomic locks (which are not visible in the provided diffs). The main changes include:

  • Configuration CRUD refactoring by removing the generic CRUD handlers in favor of direct route implementations
  • Cookie package upgrade from 0.6.0 to 0.7.0 across multiple packages
  • Introduction of a new dev-stats feature for monitoring API performance during development
  • Database schema improvements with new indexes for performance optimization
  • Build and script refactoring with environment-specific command variants
  • Extensive test refactoring replacing Auth.Otp.signIn with Auth.fastSignUp

Reviewed changes

Copilot reviewed 58 out of 58 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
turbo.json Removed cache configuration for generate-openapi-fumadocs task
packages/*/package.json Updated cookie dependency from 0.6.0 to 0.7.0
packages/stack-shared/src/interface/crud/config.ts Deleted config CRUD interface definitions
packages/stack-shared/src/interface/admin-interface.ts Updated config methods to remove CRUD type dependencies
packages/template/src/lib/stack-app/apps/implementations/admin-app-impl.ts Simplified type signatures for config handling
package.json Refactored build scripts, removed generate-openapi-fumadocs, added codegen:backend
apps/backend/src/lib/dev-request-stats.tsx New file implementing request statistics tracking for development
apps/backend/src/app/dev-stats/page.tsx New UI page for displaying development request statistics
apps/backend/src/app/dev-stats/api/route.tsx New API route for dev stats data retrieval and clearing
apps/backend/src/route-handlers/smart-route-handler.tsx Added request stats recording integration
apps/backend/src/proxy.tsx Minor refactoring moving URL creation earlier in execution
apps/backend/src/app/api/latest/internal/config/*.tsx Refactored config routes from CRUD to direct implementations
apps/backend/scripts/run-email-queue.ts Added 5-second startup delay
apps/backend/package.json Major script refactoring with environment-specific variants
apps/backend/prisma/schema.prisma Added indexes for Project.ownerTeamId and TeamMember selection
apps/backend/prisma/migrations/*.sql New migration for email outbox partial indices and new indexes
apps/e2e/tests/**/*.test.ts Mass refactoring of tests replacing Auth.Otp.signIn with Auth.fastSignUp
.github/workflows/check-prisma-migrations.yaml Updated to use prisma:dev script variant
docs/scripts/generate-functional-api-docs.mjs Updated error message referencing new codegen command
docker/server/Dockerfile Removed debug cat command for pnpm-lock.yaml

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

Comment on lines 25 to 28
export function recordRequestStats(method: string, path: string, durationMs: number): void {
if (getNodeEnvironment() !== "development") {
return;
}
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The recordRequestStats function is called unconditionally in the smart-route-handler, but only records in development mode. This means every request in production will make an unnecessary function call and environment check. Consider moving the environment check to the handler level to avoid this overhead in production.

Copilot uses AI. Check for mistakes.
Comment on lines 11 to 12
// Wait a few seconds to make sure the server is fully started
await wait(5_000);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The wait(5_000) call adds a 5-second delay on every script startup. While the comment explains this is to ensure the server is fully started, this is a brittle solution. Consider using a retry mechanism or health check polling instead of a fixed delay, which would be both more reliable and potentially faster.

Copilot uses AI. Check for mistakes.
Comment on lines 1588 to 1591
expect(enableAppResponse).toMatchInlineSnapshot(`
NiceResponse {
"status": 200,
"body": {},
"body": { "success": true },
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The response body expectation has changed from an empty object to include a "success" field. This is a good improvement for API clarity, but ensure this change is documented and that any API consumers are updated to expect this new response format.

Copilot uses AI. Check for mistakes.
body: { config_override_string: JSON.stringify(config) },
});
expect(response.body).toMatchInlineSnapshot(`{}`);
expect(response.body).toMatchInlineSnapshot(`{ "success": true }`);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The test expectation has changed to remove the "config_override_string" property that was previously returned. However, the backend-helpers.ts file still expects the body to match a snapshot with "success: true". This inconsistency should be verified - either both should return success: true, or the snapshot expectations should be aligned.

Suggested change
expect(response.body).toMatchInlineSnapshot(`{ "success": true }`);

Copilot uses AI. Check for mistakes.
Comment on lines 75 to 77
"^build"
]
},
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The PR title mentions "fix(fortnox): use dedicated atomic locks collection for refresh + add concurrency tests" but none of the file changes in this PR appear to be related to Fortnox or atomic locks. The actual changes include:

  • Removal of config CRUD handlers
  • Cookie package version updates
  • Test helper refactoring (Auth.Otp.signIn → Auth.fastSignUp)
  • New dev-stats feature
  • Database index additions
  • Build script refactoring

This suggests either the PR description is incorrect or the Fortnox-related changes are missing from the diff.

Copilot uses AI. Check for mistakes.
Comment on lines 6 to 7
// note that we run this only in CI environments
it.runIf(!!process.env.CI)("completes successfully", async ({ expect }) => {
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The test is now conditional on the CI environment variable. While this makes sense for reducing local test execution time, ensure that the setup wizard is still adequately tested in your CI pipeline. Also consider adding a comment explaining why this test is CI-only.

Copilot uses AI. Check for mistakes.
Comment on lines 75 to 77
"^build"
]
},
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The removal of the "generate-openapi-fumadocs" task from turbo.json doesn't appear to be coordinated with the changes in package.json, where the script "generate-openapi-fumadocs" is being removed and replaced with "codegen" script. This cache: false configuration removal might cause caching issues if the task still exists elsewhere in the codebase.

Copilot uses AI. Check for mistakes.
// Create second user
const user2Mailbox = await bumpEmailAddress();
const { userId: user2Id } = await Auth.Otp.signIn();
const { userId: user2Id } = await Auth.fastSignUp();
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Unused variable user2Id.

Suggested change
const { userId: user2Id } = await Auth.fastSignUp();
await Auth.fastSignUp();

Copilot uses AI. Check for mistakes.

backendContext.set({ mailbox: createMailbox() });
const user2 = await Auth.Otp.signIn();
const user2 = await Auth.fastSignUp();
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Unused variable user2.

Copilot uses AI. Check for mistakes.

it("lets users be on multiple teams", async ({ expect }) => {
const { userId: creatorUserId } = await Auth.Otp.signIn();
const { userId: creatorUserId } = await Auth.fastSignUp();
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Unused variable creatorUserId.

Copilot uses AI. Check for mistakes.
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 30, 2025

Greptile Summary

PR Title Mismatch: The PR description mentions "fortnox" and "atomic locks collection", but the actual changes are unrelated - this appears to be a mislabeled PR.

Actual Changes:

  • Bumped cookie package from ^0.6.0 to ^0.7.0 across all packages to address a security advisory
  • Added database partial indices for EmailOutbox (rendering/sending in progress) and TeamMember/Project lookups to improve query performance
  • Introduced dev-only request statistics tracking system using Map<string, RequestStat> stored in globalThis for hot-reload persistence
  • Created new /dev-stats UI page and /dev-stats/api endpoint for viewing request metrics (count, timing, etc.) - only available in development mode
  • Migrated /internal/config endpoints from CRUD handlers to SmartRouteHandler pattern per custom rule ab376f82-1d3d-4684-a503-9fb6824b49c2
  • Changed PATCH /internal/config/override to return success response instead of config body, updated client code accordingly
  • Updated test expectations to match new API response formats

Key Improvements:

  • Security: Addresses cookie package vulnerability
  • Performance: Database indices optimize email queue and team member queries
  • Developer Experience: New dev-stats page helps identify slow/frequent endpoints during development
  • Code Quality: Config endpoints now follow SmartRouteHandler pattern consistently

Confidence Score: 4/5

  • Safe to merge with one minor consideration about empty string handling
  • The changes are well-implemented with proper error handling, dev-only guards, and correct use of Map for dynamic keys. Security advisory addressed, database indices follow best practices, and SmartRouteHandler migration aligns with custom rules. Score is 4 instead of 5 due to: (1) significant PR title/description mismatch that could cause confusion, and (2) minor suggestion about empty string validation in config override endpoint.
  • Check apps/backend/src/app/api/latest/internal/config/override/route.tsx for empty string handling clarification

Important Files Changed

Filename Overview
apps/backend/prisma/migrations/20251230020000_email_outbox_partial_indices/migration.sql Added partial indices for EmailOutbox and TeamMember queries to improve performance
apps/backend/src/lib/dev-request-stats.tsx New dev-only request tracking system using globalThis for hot reload persistence
apps/backend/src/app/dev-stats/api/route.tsx New dev-only API endpoint for querying and clearing request stats
apps/backend/src/route-handlers/smart-route-handler.tsx Integrated request stats recording into route handler middleware
apps/backend/src/app/api/latest/internal/config/override/route.tsx Converted config override PATCH endpoint from CRUD handler to SmartRouteHandler, now returns success instead of config

Sequence Diagram

sequenceDiagram
    participant Client
    participant RouteHandler as Smart Route Handler
    participant StatsLib as dev-request-stats
    participant ConfigAPI as /internal/config
    participant DevStatsAPI as /dev-stats/api
    participant DevStatsUI as /dev-stats (UI)

    Note over Client,DevStatsUI: Request Flow with Stats Recording
    Client->>RouteHandler: HTTP Request (any endpoint)
    RouteHandler->>RouteHandler: Execute handler
    RouteHandler->>StatsLib: recordRequestStats(method, path, duration)
    StatsLib->>StatsLib: Update in-memory Map (dev only)
    RouteHandler->>Client: Response

    Note over Client,DevStatsUI: Dev Stats Viewing
    DevStatsUI->>DevStatsAPI: GET /dev-stats/api
    DevStatsAPI->>StatsLib: getAggregateStats()
    DevStatsAPI->>StatsLib: getMostCommonRequests()
    DevStatsAPI->>StatsLib: getMostTimeConsumingRequests()
    DevStatsAPI->>StatsLib: getSlowestRequests()
    StatsLib-->>DevStatsAPI: Stats data
    DevStatsAPI-->>DevStatsUI: JSON response
    DevStatsUI->>DevStatsUI: Render tables & charts

    Note over Client,DevStatsUI: Config Endpoints Migration
    Client->>ConfigAPI: GET /internal/config
    ConfigAPI->>ConfigAPI: SmartRouteHandler (was CRUD)
    ConfigAPI-->>Client: config_string

    Client->>ConfigAPI: PATCH /internal/config/override
    ConfigAPI->>ConfigAPI: SmartRouteHandler (was CRUD)
    ConfigAPI->>ConfigAPI: Validate & save override
    ConfigAPI-->>Client: success (no body)
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Additional Comments (1)

  1. apps/backend/src/app/api/latest/internal/config/override/route.tsx, line 27-36 (link)

    style: Consider validating that config_override_string is not an empty string if that's not a valid input. Currently "" would pass the if check but might not be intended behavior.

58 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

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