Skip to content

Feature/go db pool 893#1018

Open
NayansiDupare wants to merge 14 commits into
Muneerali199:mainfrom
NayansiDupare:feature/go-db-pool-893
Open

Feature/go db pool 893#1018
NayansiDupare wants to merge 14 commits into
Muneerali199:mainfrom
NayansiDupare:feature/go-db-pool-893

Conversation

@NayansiDupare

@NayansiDupare NayansiDupare commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

##program-gssoc26

Description

This PR adds a PostgreSQL/Supabase database layer to the Go backend using pgx/v5 and its connection pooling helper pgxpool. This allows migrating endpoints from Next.js to Go to query and persist data, replacing the stub server implementation.

Fixes #893

Type of Change

  • New feature (e.g., new page, component, or functionality)
  • Bug fix (non-breaking change that fixes an issue)
  • UI/UX improvement (design, layout, or styling updates)
  • Performance optimization (e.g., code splitting, caching)
  • Documentation update (README, contribution guidelines, etc.)
  • Other (please specify)

Changes Made

  • Dependency Addition: Added github.com/jackc/pgx/v5 v5.6.0 to backend/go.mod.
  • Database Wrapper: Created backend/go/internal/database/database.go wrapping *pgxpool.Pool for repositories.
  • Connection Pool: Created backend/go/internal/database/pool.go implementing:
    • Config parsing for environment variables (DATABASE_URL, DB_MAX_CONNS, DB_MIN_CONNS, DB_MAX_CONN_LIFETIME, DB_MAX_CONN_IDLE_TIME).
    • Connection/Ping validation at startup with exponential backoff retry logic (up to 5 attempts, doubling backoff duration).
  • Server Integration: Modified backend/go/cmd/server/main.go to validate presence of DATABASE_URL, initialize the pool on startup, and drain all connections in the pool on graceful shutdown.
  • Environment Example: Updated .env.example with the new environment variables and defaults.
  • Configuration Tests: Added unit tests in pool_test.go to test database config loading, default values, and overrides.

Dependencies

  • github.com/jackc/pgx/v5 v5.6.0

Checklist

  • My code follows the style guidelines of this project.
  • I have written or updated related tests, if necessary.
  • This is already assigned Issue to me, not an unassigned issue. (GSSOC 2026)

Summary by CodeRabbit

  • New Features

    • Added health check and readiness monitoring endpoints for service observability
    • Added version information endpoint for deployment tracking
    • Implemented JWT authentication middleware for API security
    • Introduced user credits management and billing foundations
  • Chores

    • Configured Docker containerization for backend deployment
    • Established automated CI/CD pipeline for Go backend
    • Implemented database connection pooling with configurable parameters

PiyushRai98 and others added 13 commits May 17, 2026 21:48
* feat(backend): add Go CI workflow and Docker setup

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(ci): address backend workflow and Docker review comments

* fix(review): address CodeRabbit suggestions

* fix(ci): update workflow branch target

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* feat: add PostgreSQL repository abstraction layer

* refactor: improve repository abstraction contract
…199#509)

* feat: initialize Go backend module and JWT auth middleware

* fix: address CodeRabbit review feedback on JWT version, timeouts, and missing secrets
* Setup Go backend module structure for migration

* Fix Go backend structure

* Restore Go module to backend root
test: add Go backend auth and middleware coverage
@netlify

netlify Bot commented Jun 17, 2026

Copy link
Copy Markdown

👷 Deploy request for docmagic-muneer pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 48fd85c

@netlify

netlify Bot commented Jun 17, 2026

Copy link
Copy Markdown

👷 Deploy request for docmagic1 pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 48fd85c

@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

@NayansiDupare is attempting to deploy a commit to the muneerali199's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@NayansiDupare, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 30 minutes and 27 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3a95da26-e4dc-4895-bbe2-4e797f0969fd

📥 Commits

Reviewing files that changed from the base of the PR and between bfabbe9 and 48fd85c.

📒 Files selected for processing (6)
  • .env.example
  • .github/workflows/backend-ci.yml
  • backend/go/Dockerfile
  • backend/go/cmd/server/main.go
  • backend/go/go.mod
  • backend/go/pkg/config/config.go
📝 Walkthrough

Walkthrough

This PR introduces a complete Go HTTP backend with Chi router, JWT/Supabase auth middleware, pgxpool database connection layer with retry backoff, shared error utilities, Docker multi-stage build, and a GitHub Actions CI workflow. It also adds four Next.js observability routes, a TypeScript Supabase credits repository, and migration strategy documentation.

Changes

Go Backend Foundation

Layer / File(s) Summary
Module setup, build tooling, and Docker infrastructure
backend/go.mod, backend/Makefile, backend/Dockerfile, backend/.dockerignore, docker-compose.yml, .github/workflows/backend-ci.yml, .env.example, backend/go/README.md
Establishes the Go module, Makefile run/test/lint targets, multi-stage Alpine Docker build with non-root user, Docker Compose service, CI workflow with tidy/fmt/vet/test/build steps, and database pool env var examples.
Shared JSON error response utility
backend/go/utils/errors.go, backend/go/utils/errors_test.go
Defines ErrorResponse struct and WriteJSONError used across handlers, with a unit test asserting status code, Content-Type, and JSON body fields.
HTTP middleware: RequestID, Recovery, Timeout, CORS
backend/go/middleware/middleware.go, backend/go/middleware/middleware_test.go, docs/go-migration/router-middleware-foundation.md
Implements four middleware functions and tests covering UUID header injection, panic-to-500 recovery, 503 timeout cancellation, and CORS preflight short-circuit.
JWT auth context and RequireAuth middleware
backend/pkg/auth/context.go, backend/pkg/auth/middleware.go, backend/pkg/auth/middleware_test.go
Adds User struct and context helpers, then RequireAuth middleware that validates Supabase HS256 JWTs and injects claims; tests cover valid, expired, wrong algorithm, missing header, and malformed token cases.
pgxpool Config, LoadConfig, and NewPool with retry backoff
backend/go/internal/database/pool.go, backend/go/internal/database/pool_test.go
Adds Config struct, LoadConfig (env-driven pool settings with defaults), and NewPool (parse pgxpool config, apply overrides, exponential-backoff ping with context cancellation); tests assert default and overridden values.
Router setup and server entrypoint with graceful shutdown
backend/go/router/router.go, backend/go/cmd/server/main.go
SetupRouter composes middleware on ServeMux; main validates env, initializes DB pool, builds Chi router with public /healthz and authenticated /api/user, configures HTTP timeouts, and shuts down gracefully on SIGINT/SIGTERM.

Next.js Observability Routes and TypeScript Credits Repository

Layer / File(s) Summary
Next.js healthz, readyz, and version routes
app/healthz/route.ts, app/readyz/route.ts, app/api/readyz/route.ts, app/api/version/route.ts
Adds four force-dynamic GET routes returning JSON status/uptime/timestamp or version metadata, all with no-cache headers.
TypeScript credits repository and migration docs
lib/repositories/credits-repository.ts, docs/migrations/postgres-repository-migration.md
Adds UserCredits interface and getUserCredits/createUserCredits/resetUserCredits Supabase-backed functions; migration doc describes a three-phase repository abstraction rollout.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant RequireAuth as RequireAuth Middleware
  participant JWTLib as golang-jwt/jwt
  participant AuthContext as auth.WithUser
  participant APIHandler as /api/user Handler
  participant DBPool as pgxpool

  Client->>RequireAuth: GET /api/user (Authorization: Bearer <token>)
  RequireAuth->>RequireAuth: os.Getenv(SUPABASE_JWT_SECRET)
  RequireAuth->>JWTLib: ParseWithClaims(token, HS256)
  JWTLib-->>RequireAuth: MapClaims or error
  RequireAuth->>AuthContext: WithUser(ctx, &User{ID, Email, Role})
  AuthContext-->>RequireAuth: enriched context
  RequireAuth->>APIHandler: ServeHTTP(w, r with user context)
  APIHandler->>DBPool: (future queries)
  APIHandler-->>Client: 200 JSON {user}
Loading
sequenceDiagram
  participant main
  participant LoadConfig as database.LoadConfig
  participant NewPool as database.NewPool
  participant pgxpool
  participant httpServer as http.Server
  participant OS

  main->>LoadConfig: read DATABASE_URL + DB_* env vars
  LoadConfig-->>main: *Config
  main->>NewPool: NewPool(ctx, cfg)
  loop retry with backoff
    NewPool->>pgxpool: pool.Ping(ctx)
    pgxpool-->>NewPool: ok / error
  end
  NewPool-->>main: *pgxpool.Pool
  main->>httpServer: ListenAndServe (goroutine)
  OS-->>main: SIGINT/SIGTERM
  main->>httpServer: Shutdown(30s ctx)
  main->>pgxpool: pool.Close()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Muneerali199/Draftdeckai#478: Implements the same Next.js dynamic='force-dynamic' + GET() JSON status/uptime/timestamp pattern for healthz, readyz, and version routes.
  • Muneerali199/Draftdeckai#493: Shares identical or near-identical implementations of .github/workflows/backend-ci.yml, backend/.dockerignore, backend/Dockerfile, and docker-compose.yml.

Suggested labels

enhancement, feature, type:feature, level:advanced

Suggested reviewers

  • Muneerali199

Poem

🐇 Hoppity-hop, a new server appears,
With Go routes and JWTs calming my fears,
The pgxpool pings with a retry delight,
Middleware stacked to the left and the right,
CORS says hello and Recovery stands tall,
A healthz that answers each prod-ready call!

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Feature/go db pool 893' is vague and unhelpful; it references an issue number but lacks clarity on what feature was actually implemented. Use a descriptive title like 'Add PostgreSQL connection pooling to Go backend' that clearly conveys the main change without relying on issue numbers.
Docstring Coverage ⚠️ Warning Docstring coverage is 19.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Multiple out-of-scope changes detected: health check endpoints (app/healthz, app/readyz, app/api/readyz, app/api/version), middleware layer, router setup, authentication, and repository implementations are beyond the #893 scope of database pooling. Clarify whether these components (health endpoints, auth middleware, router, repositories) are intentional additions for the backend foundation or should be moved to separate PRs focused on their respective features.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive and follows the template, covering type of change, detailed changes made, dependencies, and addressing the linked issue #893.
Linked Issues check ✅ Passed All acceptance criteria from issue #893 are met: pgx/v5 added, database package created, connection pool initialized, health checks implemented, graceful shutdown configured, and pool configurability provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/api/generate/resume/route.ts (1)

292-299: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix schema mismatch in credit_usage_log insert.

Line 296 uses the column name action, but the credit_usage_log table schema expects action_type. This mismatch will cause the insert to silently fail, breaking credit usage audit logs and analytics.

🔧 Proposed fix
         await supabaseAdmin
           .from('credit_usage_log')
           .insert({
             user_id: user.id,
-            action: 'resume',
+            action_type: 'resume',
             credits_used: creditCost,
             metadata: { prompt_length: sanitizedPrompt.length }
           });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/generate/resume/route.ts` around lines 292 - 299, The
credit_usage_log insert statement in the resume route uses the incorrect column
name `action` when it should use `action_type` to match the table schema. Update
the insert object in the supabaseAdmin.from('credit_usage_log').insert() call to
change the key from `action` to `action_type` while keeping the value as
`'resume'` to ensure credit usage is properly logged and the insert succeeds.

Source: Learnings

.env.example (1)

1-1: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

GEMINI_API_KEY is duplicated in the example env file.

Having the same key twice is error-prone when users edit one occurrence and miss the other.

Also applies to: 41-41

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.example at line 1, The GEMINI_API_KEY configuration variable is
duplicated in the .env.example file, appearing at both line 1 and line 41.
Remove one of the duplicate GEMINI_API_KEY entries to ensure this configuration
variable appears only once in the example file. This prevents confusion and
potential inconsistencies when users are setting up their environment variables
by copying from the example file.
🧹 Nitpick comments (4)
backend/Makefile (1)

1-8: ⚡ Quick win

Declare Make targets as .PHONY to avoid accidental no-op runs.

If files named run, test, or lint appear, Make may skip these targets unexpectedly.

Proposed patch
+.PHONY: run test lint
+
 run:
 	go run go/cmd/server/main.go
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/Makefile` around lines 1 - 8, Add a .PHONY declaration at the top of
the Makefile to declare the run, test, and lint targets as phony targets. This
prevents Make from treating them as file-based targets and ensures they execute
unconditionally even if files with these names exist in the directory. Place the
.PHONY directive before the target definitions listing all three target names.

Source: Linters/SAST tools

backend/pkg/auth/middleware_test.go (1)

49-121: ⚡ Quick win

Add a test for the missing SUPABASE_JWT_SECRET branch.

RequireAuth has a distinct Line 30-37 path that returns HTTP 500 when the secret is unset, but this branch is currently untested. Adding one case will lock in startup/config error behavior and prevent silent regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/pkg/auth/middleware_test.go` around lines 49 - 121, Add a new test
function to cover the missing branch where SUPABASE_JWT_SECRET is not set.
Create a test (e.g., TestRequireAuthReturnErrorWhenSecretNotSet) that does NOT
set the SUPABASE_JWT_SECRET environment variable, then call
runRequireAuthRequest with any token and verify that the response status code is
http.StatusInternalServerError (500). This will ensure the configuration error
path in RequireAuth is properly tested and prevent regressions in startup/config
error handling.
backend/go/middleware/middleware.go (1)

18-20: Use an unexported typed key instead of raw string for context values.

The "request_id" key in line 18 is collision-prone. Replace with an unexported typed key and provide a getter helper function:

Suggested refactor
+type requestIDContextKey struct{}
+
+var requestIDKey requestIDContextKey
+
+func RequestIDFromContext(ctx context.Context) (string, bool) {
+	v, ok := ctx.Value(requestIDKey).(string)
+	return v, ok
+}
+
 func RequestID(next http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		requestID := uuid.NewString()
@@
-		ctx := context.WithValue(r.Context(), "request_id", requestID)
+		ctx := context.WithValue(r.Context(), requestIDKey, requestID)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/go/middleware/middleware.go` around lines 18 - 20, Replace the raw
string `"request_id"` used as a context key in the context.WithValue call with
an unexported typed key to avoid collision risks. Create an unexported type
(using an empty struct or a custom type) to serve as the context key, update the
context.WithValue call to use this typed key instead of the string, and provide
a getter helper function that accepts a context and returns the request_id value
using this typed key for retrieving the value safely throughout the codebase.
backend/go/internal/database/pool_test.go (1)

10-12: ⚡ Quick win

Harden env-based tests with t.Setenv and assert idle-time default.

Use t.Setenv to avoid cross-test env leakage, and add an assertion for MaxConnIdleTime in the defaults test so all documented defaults are covered.

Suggested fix
 func TestLoadConfig_Defaults(t *testing.T) {
-	os.Setenv("DATABASE_URL", "postgres://user:pass@localhost:5432/dbname")
-	defer os.Unsetenv("DATABASE_URL")
+	t.Setenv("DATABASE_URL", "postgres://user:pass@localhost:5432/dbname")
@@
 	if cfg.MaxConnLifetime != 30*time.Minute {
 		t.Errorf("expected MaxConnLifetime to be 30m, got %v", cfg.MaxConnLifetime)
 	}
+	if cfg.MaxConnIdleTime != 15*time.Minute {
+		t.Errorf("expected MaxConnIdleTime to be 15m, got %v", cfg.MaxConnIdleTime)
+	}
 }
 
 func TestLoadConfig_Overrides(t *testing.T) {
-	os.Setenv("DATABASE_URL", "postgres://user:pass@localhost:5432/dbname")
-	os.Setenv("DB_MAX_CONNS", "25")
-	os.Setenv("DB_MIN_CONNS", "5")
-	os.Setenv("DB_MAX_CONN_LIFETIME", "1h")
-	os.Setenv("DB_MAX_CONN_IDLE_TIME", "10m")
-
-	defer func() {
-		os.Unsetenv("DATABASE_URL")
-		os.Unsetenv("DB_MAX_CONNS")
-		os.Unsetenv("DB_MIN_CONNS")
-		os.Unsetenv("DB_MAX_CONN_LIFETIME")
-		os.Unsetenv("DB_MAX_CONN_IDLE_TIME")
-	}()
+	t.Setenv("DATABASE_URL", "postgres://user:pass@localhost:5432/dbname")
+	t.Setenv("DB_MAX_CONNS", "25")
+	t.Setenv("DB_MIN_CONNS", "5")
+	t.Setenv("DB_MAX_CONN_LIFETIME", "1h")
+	t.Setenv("DB_MAX_CONN_IDLE_TIME", "10m")

Also applies to: 24-27, 30-42

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/go/internal/database/pool_test.go` around lines 10 - 12, Replace all
instances of os.Setenv combined with defer os.Unsetenv throughout the
pool_test.go file (including the section at lines 10-12, 24-27, and 30-42) with
t.Setenv calls, which automatically handle cleanup and prevent environment
variable leakage between tests. Additionally, locate the defaults test and add
an assertion to verify that the MaxConnIdleTime field is set to its documented
default value, ensuring comprehensive coverage of all default configuration
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/backend-ci.yml:
- Around line 4-7: The pull_request trigger in the workflow has an overly
restrictive branches filter that only allows the feature/go-backend branch,
preventing CI from running on pull requests from other branches. Remove or
modify the branches filter under the pull_request section to allow PRs from all
branches (or the relevant branches your team uses), unless there's a specific
reason to limit it to only feature/go-backend.
- Around line 26-32: The actions/checkout and actions/setup-go action references
use mutable version tags (v4 and v5 respectively) instead of pinned commit SHAs,
creating supply chain vulnerabilities. Replace both action references with their
full commit SHA hashes to ensure immutability and prevent potential malicious
code injection. Additionally, add a with parameter to the actions/checkout step
to disable credential persistence, preventing the GITHUB_TOKEN from being stored
in .git/config since this workflow only performs read-only git operations.

In `@backend/Dockerfile`:
- Around line 10-22: The Docker build path in the go build command is incorrect,
referencing ./cmd when the actual server entrypoint is under ./go/cmd/server.
Additionally, the runtime stage sets WORKDIR to /root/ but then switches to
appuser, which will not have permissions to access the root directory. Fix this
by updating the go build command to use the correct path to ./go/cmd/server, and
change the WORKDIR in the runtime stage to a directory that appuser has access
to (such as /app). Ensure the COPY command copies the built binary to match the
new WORKDIR path so that appuser can execute it properly.

In `@backend/go.mod`:
- Line 9: The github.com/jackc/pgx/v5 dependency in the go.mod file is pinned to
version v5.6.0, which contains critical memory-safety and SQL injection
vulnerabilities. Update the version number for github.com/jackc/pgx/v5 from
v5.6.0 to v5.9.2 or later in the go.mod file to address all known security
advisories, then run go mod tidy to update the go.sum file accordingly.

In `@backend/go/cmd/server/main.go`:
- Around line 119-125: The goroutine running srv.ListenAndServe() is calling
os.Exit(1) directly when an error occurs, which bypasses the normal shutdown
orchestration and prevents cleanup. Create an error channel to capture server
errors from the goroutine, send any non-nil errors to this channel instead of
calling os.Exit(1), and then handle the error in the main execution path
alongside signal handling so that all shutdown logic is centralized and cleanup
procedures execute properly.
- Around line 50-58: The database pool initialization uses an unbounded
context.Background() which can cause the application to hang indefinitely during
network or database connectivity issues. Replace the context.Background()
assignment with a context that has a timeout using context.WithTimeout, and pass
this bounded context to the database.NewPool function call. This ensures the
pool initialization will fail fast if the database is unreachable or
unresponsive.

In `@backend/go/internal/database/pool.go`:
- Around line 38-52: After parsing DB_MAX_CONNS and DB_MIN_CONNS from
environment variables and assigning the values to cfg.MaxConns and cfg.MinConns
respectively, add validation logic to ensure the pool size invariants are
satisfied. Validate that cfg.MaxConns is not negative, cfg.MinConns is not
negative, and crucially that cfg.MinConns does not exceed cfg.MaxConns. If any
invariant is violated, log a warning and use sensible default values to prevent
downstream startup or runtime failures.
- Around line 95-116: The database ping retry loop continues to wait for the
backoff delay even after the final retry attempt fails, adding unnecessary
startup delay. Add a check to determine if the current iteration is the last
retry (when i equals maxRetries - 1), and if the ping fails on that final
attempt, skip the backoff sleep by either breaking out of the loop or returning
the error directly instead of entering the select statement with
time.After(backoff).

In `@backend/go/README.md`:
- Around line 5-9: The bash code block in the README.md contains incorrect shell
syntax. Replace the `set` command with `export` for the SUPABASE_JWT_SECRET
variable so the environment variable is properly exported when running `make
run`. Additionally, add a second `export` statement for the required but
undocumented DATABASE_URL environment variable with an appropriate PostgreSQL
connection string (e.g., postgres://postgres:password@localhost:5432/postgres)
to complete the local setup instructions.

In `@backend/pkg/auth/middleware.go`:
- Around line 14-24: The ErrorResponse struct and writeError function in the
middleware are using an inconsistent error response schema that only includes an
error field. Update the ErrorResponse struct to match the shared error contract
defined in backend/go/utils/errors.go by including all required fields (error,
message, status). Then modify the writeError function to populate the
ErrorResponse struct with all the required fields according to the unified error
contract instead of only setting the error message.

In `@docker-compose.yml`:
- Around line 14-15: The docker-compose.yml service environment section is
missing the required DATABASE_URL environment variable that
database.LoadConfig() depends on. Add DATABASE_URL to the environment section
alongside the existing PORT configuration, setting it to the appropriate
database connection string for your application to start successfully.

In `@docs/migrations/postgres-repository-migration.md`:
- Around line 53-56: The Phase 3 bullet point "Introduce connection pooling and
transactional services" is misleading because connection pooling already exists
in the current Go backend foundation. Clarify this bullet point by rephrasing it
to indicate that Phase 3 is enhancing or optimizing the existing pooling for
PostgreSQL-native backend compatibility, rather than introducing pooling as a
new feature. This will eliminate ambiguity about whether pooling is being added
for the first time or is being adapted/upgraded as part of the migration.

In `@lib/repositories/credits-repository.ts`:
- Around line 1-6: The supabaseAdmin client creation lacks a server-only guard
despite using a sensitive service-role key, creating a risk of accidental
client-side exposure. Add the server-only package to your dependencies, then
import it at the very top of lib/repositories/credits-repository.ts before the
createClient call for supabaseAdmin. This will enforce that the module can only
be imported on the server, preventing future regressions if this module is
accidentally referenced in client-side code.
- Around line 37-51: The createUserCredits function throws an error when it
encounters a unique constraint violation (error code 23505) from concurrent
requests, making it non-idempotent. Modify the error handling logic to check if
the error code is 23505 (unique constraint violation on the user_id field), and
instead of throwing, call the existing getUserCredits(userId) method to fetch
and return the existing record. This allows the function to safely handle
duplicate or concurrent creation attempts without failing on subsequent
requests.

---

Outside diff comments:
In @.env.example:
- Line 1: The GEMINI_API_KEY configuration variable is duplicated in the
.env.example file, appearing at both line 1 and line 41. Remove one of the
duplicate GEMINI_API_KEY entries to ensure this configuration variable appears
only once in the example file. This prevents confusion and potential
inconsistencies when users are setting up their environment variables by copying
from the example file.

In `@app/api/generate/resume/route.ts`:
- Around line 292-299: The credit_usage_log insert statement in the resume route
uses the incorrect column name `action` when it should use `action_type` to
match the table schema. Update the insert object in the
supabaseAdmin.from('credit_usage_log').insert() call to change the key from
`action` to `action_type` while keeping the value as `'resume'` to ensure credit
usage is properly logged and the insert succeeds.

---

Nitpick comments:
In `@backend/go/internal/database/pool_test.go`:
- Around line 10-12: Replace all instances of os.Setenv combined with defer
os.Unsetenv throughout the pool_test.go file (including the section at lines
10-12, 24-27, and 30-42) with t.Setenv calls, which automatically handle cleanup
and prevent environment variable leakage between tests. Additionally, locate the
defaults test and add an assertion to verify that the MaxConnIdleTime field is
set to its documented default value, ensuring comprehensive coverage of all
default configuration values.

In `@backend/go/middleware/middleware.go`:
- Around line 18-20: Replace the raw string `"request_id"` used as a context key
in the context.WithValue call with an unexported typed key to avoid collision
risks. Create an unexported type (using an empty struct or a custom type) to
serve as the context key, update the context.WithValue call to use this typed
key instead of the string, and provide a getter helper function that accepts a
context and returns the request_id value using this typed key for retrieving the
value safely throughout the codebase.

In `@backend/Makefile`:
- Around line 1-8: Add a .PHONY declaration at the top of the Makefile to
declare the run, test, and lint targets as phony targets. This prevents Make
from treating them as file-based targets and ensures they execute
unconditionally even if files with these names exist in the directory. Place the
.PHONY directive before the target definitions listing all three target names.

In `@backend/pkg/auth/middleware_test.go`:
- Around line 49-121: Add a new test function to cover the missing branch where
SUPABASE_JWT_SECRET is not set. Create a test (e.g.,
TestRequireAuthReturnErrorWhenSecretNotSet) that does NOT set the
SUPABASE_JWT_SECRET environment variable, then call runRequireAuthRequest with
any token and verify that the response status code is
http.StatusInternalServerError (500). This will ensure the configuration error
path in RequireAuth is properly tested and prevent regressions in startup/config
error handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d9d7010-d26b-46bc-94f4-5e0d706ca3d5

📥 Commits

Reviewing files that changed from the base of the PR and between 9c2da20 and bfabbe9.

⛔ Files ignored due to path filters (2)
  • backend/go.sum is excluded by !**/*.sum
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • .env.example
  • .github/workflows/backend-ci.yml
  • app/api/generate/resume/route.ts
  • app/api/healthz/route.ts
  • app/api/readyz/route.ts
  • app/api/version/route.ts
  • app/healthz/route.ts
  • app/readyz/route.ts
  • app/version/route.ts
  • backend/.dockerignore
  • backend/Dockerfile
  • backend/Makefile
  • backend/go.mod
  • backend/go/README.md
  • backend/go/cmd/server/main.go
  • backend/go/internal/database/database.go
  • backend/go/internal/database/pool.go
  • backend/go/internal/database/pool_test.go
  • backend/go/middleware/middleware.go
  • backend/go/middleware/middleware_test.go
  • backend/go/router/router.go
  • backend/go/utils/errors.go
  • backend/go/utils/errors_test.go
  • backend/main.go
  • backend/pkg/auth/context.go
  • backend/pkg/auth/middleware.go
  • backend/pkg/auth/middleware_test.go
  • docker-compose.yml
  • docs/go-migration/router-middleware-foundation.md
  • docs/migrations/postgres-repository-migration.md
  • lib/repositories/credits-repository.ts

Comment on lines +4 to +7
pull_request:
branches:
- feature/go-backend
paths:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

CI won’t run for most pull requests due to branch filter.

Lines 5-6 restrict PR CI to feature/go-backend, so PRs from other branches can bypass this workflow.

Proposed patch
   pull_request:
     branches:
-      - feature/go-backend
+      - main
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/backend-ci.yml around lines 4 - 7, The pull_request
trigger in the workflow has an overly restrictive branches filter that only
allows the feature/go-backend branch, preventing CI from running on pull
requests from other branches. Remove or modify the branches filter under the
pull_request section to allow PRs from all branches (or the relevant branches
your team uses), unless there's a specific reason to limit it to only
feature/go-backend.

Comment on lines +26 to +32
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.22"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/backend-ci.yml | sed -n '20,40p'

Repository: Muneerali199/Draftdeckai

Length of output: 647


🌐 Web query:

GitHub Actions pinning commit SHA security best practice NIST

💡 Result:

Pinning GitHub Actions to a full-length commit SHA is a widely recognized security best practice for mitigating supply chain risks [1][2][3]. This approach ensures that workflows execute immutable, verified code, preventing attacks where an adversary modifies an existing tag or branch to inject malicious payloads [2][4][5]. Industry Alignment and NIST Guidance While NIST publications, such as SP 800-204D, do not explicitly mandate "commit SHA pinning" as a single technical instruction, they provide the foundational framework for software supply chain security [6][7][8]. These documents emphasize critical security objectives that SHA pinning directly supports, including: 1. Software Supply Chain Security (SSDF): NIST guidance stresses the importance of integrity, provenance, and the mitigation of risks associated with external dependencies [9][10]. Pinning to a commit SHA provides cryptographic assurance of code integrity, aligning with the requirement to verify the provenance and immutability of build components [2][5]. 2. Secure CI/CD Pipelines: NIST SP 800-204D outlines strategies for integrating security into DevSecOps, specifically addressing the need to secure the "build" stage of pipelines [6][8]. By treating actions as immutable dependencies, organizations reduce the attack surface against unauthorized code changes, which is a key control in securing CI/CD infrastructure [5][7]. Practical Implementation Best Practices To maintain security while managing the overhead of pinned dependencies, organizations typically adopt the following practices: - Enforce at Policy Level: GitHub allows administrators to require full-length commit SHA pinning at the organization or repository level [1][11][5]. When enabled, any workflow attempting to use a mutable tag or branch will fail, enforcing compliance across the organization [11][5]. - Automate Updates: Because SHAs are static, they do not automatically receive security updates. Organizations are strongly encouraged to use automated dependency management tools—such as Dependabot or Renovate—to monitor and create pull requests for updated SHAs [4][5][3]. - Human-Readable Documentation: When pinning to a SHA, include the original version tag as a comment in the workflow file (e.g., uses: actions/checkout@sha123 # v4.0.0) [5]. This maintains context for developers and security reviewers regarding which version is intended [5]. - Recursive Pinning: If you maintain custom actions that consume other actions, you must also pin your internal sub-action references to SHAs. This ensures that your action remains compatible with organizations that enforce strict SHA-pinning policies [11]. By combining cryptographic pinning with automated dependency updates, organizations satisfy the rigorous security standards recommended by NIST for supply chain integrity within modern CI/CD pipelines [5][9][7].

Citations:


🌐 Web query:

GitHub Actions persist-credentials security recommendation

💡 Result:

The persist-credentials input in the official actions/checkout GitHub Action controls whether the action persists the authentication token in the local git configuration of the runner [1][2]. Key security recommendation: It is a widely accepted security best practice to set persist-credentials: false unless your workflow explicitly requires the git CLI to perform authenticated operations like git push or git tag [3][4][5]. Why this is a security risk: - Default Behavior: The input defaults to true, meaning the authentication token (e.g., GITHUB_TOKEN) is automatically stored in the.git/config file of the checked-out repository [1][6]. - Implicit Access: Any subsequent steps in the same job can read this file, potentially exposing the token to malicious or compromised third-party actions [7][8][5]. - Accidental Exposure: If the workflow uploads the repository workspace as a build artifact (e.g., using actions/upload-artifact), the.git/config file—and the sensitive token inside it—may be inadvertently leaked to external environments [9][6]. How to implement: To disable this behavior, explicitly set the input in your workflow YAML: - uses: actions/checkout@v4 with: persist-credentials: false If you do not require git push or similar authenticated git operations, disabling this is recommended to adhere to the principle of least privilege and reduce the attack surface of your CI/CD pipeline [10][4][5].

Citations:


Pin GitHub Actions to commit SHA and disable credential persistence.

Lines 27–30 use mutable tag refs (v4, v5) instead of pinned commit SHAs, creating a supply chain vulnerability where tag modifications could inject malicious code. Additionally, line 27 persists credentials by default, storing the GITHUB_TOKEN in .git/config where subsequent steps can access it—an unnecessary exposure given this workflow only performs read-only git operations.

Suggested hardening
       - name: Checkout repository
-        uses: actions/checkout@v4
+        uses: actions/checkout@a5ac7e51b41094c7d81cd2926d412afa3da67df2  # v4.1.6
+        with:
+          persist-credentials: false

       - name: Set up Go
-        uses: actions/setup-go@v5
+        uses: actions/setup-go@0a12ed9d6470824250f19a37f27f20f213e2f545  # v5.0.0
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Checkout repository
uses: actions/checkout@a5ac7e51b41094c7d81cd2926d412afa3da67df2 # v4.1.6
with:
persist-credentials: false
- name: Set up Go
uses: actions/setup-go@0a12ed9d6470824250f19a37f27f20f213e2f545 # v5.0.0
with:
go-version: "1.22"
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 26-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 30-30: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/backend-ci.yml around lines 26 - 32, The actions/checkout
and actions/setup-go action references use mutable version tags (v4 and v5
respectively) instead of pinned commit SHAs, creating supply chain
vulnerabilities. Replace both action references with their full commit SHA
hashes to ensure immutability and prevent potential malicious code injection.
Additionally, add a with parameter to the actions/checkout step to disable
credential persistence, preventing the GITHUB_TOKEN from being stored in
.git/config since this workflow only performs read-only git operations.

Source: Linters/SAST tools

Comment thread backend/Dockerfile Outdated
Comment on lines +10 to +22
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd

# Runtime stage
FROM alpine:latest

WORKDIR /root/

RUN adduser -D appuser

COPY --from=builder /app/server .

USER appuser

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Container build/start path is broken (./cmd) and runtime directory is incompatible with appuser.

Line 10 builds ./cmd, but this backend’s server entrypoint is under go/cmd/server. Also, Lines 15-22 run as appuser with WORKDIR /root/, which can fail due to directory permissions.

Proposed patch
-RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd
+RUN CGO_ENABLED=0 GOOS=linux go build -o /server ./go/cmd/server

 # Runtime stage
 FROM alpine:latest

-WORKDIR /root/
+WORKDIR /app

 RUN adduser -D appuser

-COPY --from=builder /app/server .
+COPY --from=builder /server /app/server
+RUN chown -R appuser:appuser /app

 USER appuser
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/Dockerfile` around lines 10 - 22, The Docker build path in the go
build command is incorrect, referencing ./cmd when the actual server entrypoint
is under ./go/cmd/server. Additionally, the runtime stage sets WORKDIR to /root/
but then switches to appuser, which will not have permissions to access the root
directory. Fix this by updating the go build command to use the correct path to
./go/cmd/server, and change the WORKDIR in the runtime stage to a directory that
appuser has access to (such as /app). Ensure the COPY command copies the built
binary to match the new WORKDIR path so that appuser can execute it properly.

Comment thread backend/go/go.mod
github.com/go-chi/chi/v5 v5.0.12
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.6.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

What is the first patched github.com/jackc/pgx/v5 version for GO-2026-4771, GO-2026-4772, GHSA-9jj7-4m8r-rfcm, and GHSA-j88v-2chj-qfwx?

💡 Result:

The first patched version for the specified vulnerabilities in github.com/jackc/pgx/v5 is v5.9.0 for GO-2026-4771, GO-2026-4772, and GHSA-9jj7-4m8r-rfcm [1][2], and v5.9.2 for GHSA-j88v-2chj-qfwx [3]. Details: - GO-2026-4771, GO-2026-4772, and GHSA-9jj7-4m8r-rfcm (associated with memory-safety vulnerabilities in the pgproto3 component): These vulnerabilities were addressed in version v5.9.0 [1][4][2]. - GHSA-j88v-2chj-qfwx (associated with a SQL injection vulnerability): This vulnerability was addressed in version v5.9.2 [3].

Citations:


Upgrade pgx/v5 to v5.9.2 or later before merge.

github.com/jackc/pgx/v5 v5.6.0 contains critical vulnerabilities: memory-safety issues (GO-2026-4771, GO-2026-4772, GHSA-9jj7-4m8r-rfcm) and SQL injection (GHSA-j88v-2chj-qfwx). Upgrade to v5.9.2, which addresses all known advisories.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/go.mod` at line 9, The github.com/jackc/pgx/v5 dependency in the
go.mod file is pinned to version v5.6.0, which contains critical memory-safety
and SQL injection vulnerabilities. Update the version number for
github.com/jackc/pgx/v5 from v5.6.0 to v5.9.2 or later in the go.mod file to
address all known security advisories, then run go mod tidy to update the go.sum
file accordingly.

Source: Linters/SAST tools

Comment on lines +50 to +58
ctx := context.Background()
dbCfg, err := database.LoadConfig()
if err != nil {
log.Fatalf("Failed to load database config: %v", err)
}

slog.Info("initializing database pool...")
pool, err := database.NewPool(ctx, dbCfg)
if err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Bound DB bootstrap with a timeout context.

Using context.Background() for pool initialization can leave startup hanging on network/DB stalls. Pass a bounded context into database.NewPool.

Suggested fix
-	ctx := context.Background()
+	startupCtx, startupCancel := context.WithTimeout(context.Background(), 45*time.Second)
+	defer startupCancel()
 	dbCfg, err := database.LoadConfig()
@@
-	pool, err := database.NewPool(ctx, dbCfg)
+	pool, err := database.NewPool(startupCtx, dbCfg)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ctx := context.Background()
dbCfg, err := database.LoadConfig()
if err != nil {
log.Fatalf("Failed to load database config: %v", err)
}
slog.Info("initializing database pool...")
pool, err := database.NewPool(ctx, dbCfg)
if err != nil {
startupCtx, startupCancel := context.WithTimeout(context.Background(), 45*time.Second)
defer startupCancel()
dbCfg, err := database.LoadConfig()
if err != nil {
log.Fatalf("Failed to load database config: %v", err)
}
slog.Info("initializing database pool...")
pool, err := database.NewPool(startupCtx, dbCfg)
if err != nil {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/go/cmd/server/main.go` around lines 50 - 58, The database pool
initialization uses an unbounded context.Background() which can cause the
application to hang indefinitely during network or database connectivity issues.
Replace the context.Background() assignment with a context that has a timeout
using context.WithTimeout, and pass this bounded context to the database.NewPool
function call. This ensures the pool initialization will fail fast if the
database is unreachable or unresponsive.

Comment on lines +14 to +24
// ErrorResponse represents a standard JSON error returned by the API.
type ErrorResponse struct {
Error string `json:"error"`
}

// writeError Helper to write JSON error responses.
func writeError(w http.ResponseWriter, status int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(ErrorResponse{Error: message})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unify auth error responses with the shared API error contract.

Lines 14-24 emit {"error": ...} only, while backend/go/utils/errors.go defines a structured error payload (error, message, status). This creates inconsistent response schemas across endpoints/middleware and can break client-side error parsing during migration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/pkg/auth/middleware.go` around lines 14 - 24, The ErrorResponse
struct and writeError function in the middleware are using an inconsistent error
response schema that only includes an error field. Update the ErrorResponse
struct to match the shared error contract defined in backend/go/utils/errors.go
by including all required fields (error, message, status). Then modify the
writeError function to populate the ErrorResponse struct with all the required
fields according to the unified error contract instead of only setting the error
message.

Comment thread docker-compose.yml Outdated
Comment on lines +14 to +15
environment:
PORT: 8080 No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

DATABASE_URL is missing from service env, so startup will fail.

database.LoadConfig() requires DATABASE_URL; only PORT is provided here.

Proposed patch
     environment:
       PORT: 8080
+      DATABASE_URL: ${DATABASE_URL}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
environment:
PORT: 8080
environment:
PORT: 8080
DATABASE_URL: ${DATABASE_URL}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 14 - 15, The docker-compose.yml service
environment section is missing the required DATABASE_URL environment variable
that database.LoadConfig() depends on. Add DATABASE_URL to the environment
section alongside the existing PORT configuration, setting it to the appropriate
database connection string for your application to start successfully.

Comment on lines +53 to +56
- Prepare PostgreSQL-native backend compatibility
- Align repository contracts with future Go services
- Introduce connection pooling and transactional services

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify Phase 3 wording to avoid contradicting current stack state.

Line 55 says to “introduce connection pooling,” but pooling is already part of the Go backend foundation in this stack. Rephrase to avoid rollout ambiguity.

Suggested wording update
 ### Phase 3
 - Prepare PostgreSQL-native backend compatibility
 - Align repository contracts with future Go services
-- Introduce connection pooling and transactional services
+- Leverage existing connection pooling and introduce transactional services
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/migrations/postgres-repository-migration.md` around lines 53 - 56, The
Phase 3 bullet point "Introduce connection pooling and transactional services"
is misleading because connection pooling already exists in the current Go
backend foundation. Clarify this bullet point by rephrasing it to indicate that
Phase 3 is enhancing or optimizing the existing pooling for PostgreSQL-native
backend compatibility, rather than introducing pooling as a new feature. This
will eliminate ambiguity about whether pooling is being added for the first time
or is being adapted/upgraded as part of the migration.

Comment on lines +1 to +6
import { createClient } from '@supabase/supabase-js';

const supabaseAdmin = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify where this repository is imported.
rg -n --glob '**/*.{ts,tsx}' "from ['\"][^'\"]*credits-repository['\"]" -C2 || true

# For each importer, print whether it is a client module.
python - <<'PY'
from pathlib import Path
import re

pat = re.compile(r"from ['\"][^'\"]*credits-repository['\"]")
for p in list(Path('.').rglob('*.ts')) + list(Path('.').rglob('*.tsx')):
    try:
        txt = p.read_text(encoding='utf-8')
    except Exception:
        continue
    if pat.search(txt):
        head = "\n".join(txt.splitlines()[:8])
        is_client = ("'use client'" in head) or ('"use client"' in head)
        print(f"{p}: use_client={is_client}")
PY

Repository: Muneerali199/Draftdeckai

Length of output: 434


🏁 Script executed:

# First, check the current state of the credits-repository file
head -20 lib/repositories/credits-repository.ts

# Then search exhaustively for ALL importers
echo "=== Searching for all imports of credits-repository ==="
rg -n "credits-repository" --glob '**/*.{ts,tsx,js,jsx}' || true

# Check if server-only is in package.json
echo "=== Checking for server-only in dependencies ==="
rg "server-only" package.json || echo "Not found in package.json"

Repository: Muneerali199/Draftdeckai

Length of output: 766


🏁 Script executed:

# Get the full directory structure to understand project layout
echo "=== Project root files and directories ==="
ls -la

# Check if node_modules has server-only installed
ls -la node_modules/ | grep -i server || echo "Node modules check skipped or not found"

Repository: Muneerali199/Draftdeckai

Length of output: 6612


🏁 Script executed:

# Search for any server-only usage in the codebase
echo "=== Checking for 'server-only' usage ==="
rg "server-only" --glob '**/*.{ts,tsx,js,jsx}' -C 1 || echo "No server-only imports found"

# Check the importing file to confirm it's server-side
echo "=== Contents of the importing file ==="
head -30 app/api/generate/resume/route.ts

# Check if there are other Supabase admin clients
echo "=== Checking for other Supabase admin clients ==="
rg "createClient.*SUPABASE.*KEY" --glob '**/*.{ts,tsx}' || true

# Check package.json to see if there are any related security packages
echo "=== Checking package.json for security-related packages ==="
grep -A 5 '"dependencies"' package.json | head -20

Repository: Muneerali199/Draftdeckai

Length of output: 1709


🏁 Script executed:

# Verify the exact import statement and surrounding context
echo "=== Full import context in app/api/generate/resume/route.ts ==="
head -20 app/api/generate/resume/route.ts

Repository: Muneerali199/Draftdeckai

Length of output: 915


Add a strict server-only guard for the admin Supabase client and consolidate client creation.

The supabaseAdmin client uses a service-role key without a server-only boundary, creating a runtime risk if accidentally imported client-side. Currently, only server-side code imports this module, but adding an explicit guard prevents future regressions.

Additionally, app/api/generate/resume/route.ts duplicates the client creation instead of importing the repository's export.

To implement the fix, add the server-only package to dependencies (it is not currently installed), then import it at the top of lib/repositories/credits-repository.ts to enforce server-only usage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/repositories/credits-repository.ts` around lines 1 - 6, The supabaseAdmin
client creation lacks a server-only guard despite using a sensitive service-role
key, creating a risk of accidental client-side exposure. Add the server-only
package to your dependencies, then import it at the very top of
lib/repositories/credits-repository.ts before the createClient call for
supabaseAdmin. This will enforce that the module can only be imported on the
server, preventing future regressions if this module is accidentally referenced
in client-side code.

Comment on lines +37 to +51
const { data, error } = await supabaseAdmin
.from('user_credits')
.insert({
user_id: userId,
tier: 'free',
credits_total: creditsTotal,
credits_used: 0,
credits_reset_at: resetDate,
})
.select()
.single();

if (error) {
throw new Error(`Failed to create user credits: ${error.message}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether user_credits has uniqueness on user_id in schema/migrations.
rg -n -i -C3 "create table|user_credits|unique|primary key|on conflict"

# Verify call patterns that may do get-then-create and therefore race.
rg -n --glob '**/*.{ts,tsx}' -C3 "getUserCredits\\(|createUserCredits\\("

Repository: Muneerali199/Draftdeckai

Length of output: 50381


🏁 Script executed:

# Find and examine the credits-repository.ts file
fd -t f "credits-repository.ts" -x cat -n {} | head -100

Repository: Muneerali199/Draftdeckai

Length of output: 2167


🏁 Script executed:

# Search for getUserCredits function definition
rg -n "getUserCredits" --type ts --type tsx -B2 -A8

Repository: Muneerali199/Draftdeckai

Length of output: 96


🏁 Script executed:

# Look for call patterns that might race
rg -n "createUserCredits|getUserCredits" --type ts --type tsx -B3 -A3

Repository: Muneerali199/Draftdeckai

Length of output: 96


🏁 Script executed:

# Search for where createUserCredits is called to identify race condition patterns
rg -n "createUserCredits" --type ts -B5 -A5

Repository: Muneerali199/Draftdeckai

Length of output: 2348


🏁 Script executed:

# Also check for any onboarding or user creation flows
rg -n "onAuth|handleNewUser|signup" --type ts -B3 -A5 | head -80

Repository: Muneerali199/Draftdeckai

Length of output: 5605


🏁 Script executed:

# Check Supabase error handling patterns in the codebase
rg -n "error\.code|error\.message" --type ts -B2 -A2 | head -60

Repository: Muneerali199/Draftdeckai

Length of output: 3343


🏁 Script executed:

# Check all locations where createUserCredits is called
rg -n "createUserCredits" --type ts -B8 -A8

Repository: Muneerali199/Draftdeckai

Length of output: 3545


🏁 Script executed:

# Check if there are other similar get-then-create patterns
rg -n "getUserCredits.*createUserCredits|if.*!.*userCredits" --type ts -B5 -A5

Repository: Muneerali199/Draftdeckai

Length of output: 6134


🌐 Web query:

PostgreSQL error code 23505 unique constraint violation

💡 Result:

The PostgreSQL error code 23505, known as unique_violation, is a standard error that occurs when an INSERT or UPDATE operation attempts to violate a unique constraint [1][2][3]. This constraint is enforced by either a UNIQUE index or a PRIMARY KEY definition on a table column or a set of columns [2][4][5]. Common causes for this error include: 1. Inserting or updating a record with a value that already exists in a column defined as UNIQUE or PRIMARY KEY [2][4]. 2. Race conditions in concurrent applications where multiple transactions attempt to insert the same value simultaneously [2][6]. 3. Desynchronization of database sequences (often with SERIAL or IDENTITY columns), where the sequence counter falls behind the actual maximum value in the table [7][5]. Recommended approaches for handling or preventing this error include: - Use INSERT... ON CONFLICT (upsert): This is the preferred method for handling potential duplicates gracefully within a single statement [2][5]. You can specify either DO NOTHING to skip the insert or DO UPDATE to modify the existing record [2][5]. INSERT INTO your_table (column1, column2) VALUES ('value1', 'value2') ON CONFLICT (column1) DO NOTHING; - Application-level exception handling: If you cannot use an upsert, wrap your database operations in a try-catch block to specifically handle the 23505 error code [2][4]. Most database drivers provide a specific class or error code object (e.g., psycopg2.errors.UniqueViolation in Python) to catch this condition [2][5]. - Verify sequence synchronization: If you are seeing this error on a column using a SERIAL or IDENTITY type, check if the underlying sequence has fallen out of sync with the data. You can fix this by resetting the sequence using: SELECT SETVAL('your_table_id_seq'::regclass, (SELECT MAX(id) FROM your_table)); [7] The error message typically provides the name of the violated constraint, which can be used to identify exactly which column or index is causing the conflict [1][2][7].

Citations:


🏁 Script executed:

# Check how Supabase errors are handled elsewhere in the codebase
rg -n "error\.code|error\." lib/repositories --type ts -B2 -A2

Repository: Muneerali199/Draftdeckai

Length of output: 981


🏁 Script executed:

# Check Supabase error structure in other files
rg -n "supabaseAdmin.*error" app/api --type ts -A5 | head -100

Repository: Muneerali199/Draftdeckai

Length of output: 50


🌐 Web query:

Supabase JavaScript error code property unique constraint violation

💡 Result:

When a unique constraint violation occurs in Supabase, the JavaScript client returns a PostgrestError object with the specific error code '23505' [1][2][3]. This code corresponds to the PostgreSQL 'unique_violation' error [1][2]. You should programmatically branch on error.code rather than the error message, as the message can change between versions while the code remains stable [4]. To handle these violations, you can use the following approach in your application code [1]: const { data, error } = await supabase.from('your_table').insert({... }) if (error) { if (error.code === '23505') { // Handle unique constraint violation (e.g., show a user-friendly message) console.error('This record already exists.') } else { // Handle other errors console.error(error.message) } } In addition to the code, the error object includes a details field, which often specifies exactly which key or value caused the violation [4][3]. For common use cases where you want to avoid these errors (such as handling race conditions or form double-submissions), it is recommended to use the.upsert method instead of.insert [5][6]. When using.upsert, you can specify the onConflict option to target the unique column directly [6][3].

Citations:


Make createUserCredits idempotent to safely handle duplicate/concurrent requests.

The function at lines 37-51 throws on conflict instead of returning the existing record. When multiple requests race to create credits for a new user (or retries occur), the second attempt fails. The user_credits table has a UNIQUE constraint on user_id, making this pattern unsafe.

Catch error code 23505 (unique constraint violation) and fetch the existing record via getUserCredits(userId) instead of throwing. This prevents request failures when concurrent create attempts occur.

Suggested fix
 export async function createUserCredits(
   userId: string,
   creditsTotal: number,
   resetDate: string
 ): Promise<UserCredits> {
   const { data, error } = await supabaseAdmin
     .from('user_credits')
     .insert({
       user_id: userId,
       tier: 'free',
       credits_total: creditsTotal,
       credits_used: 0,
       credits_reset_at: resetDate,
     })
     .select()
     .single();

   if (error) {
+    // Duplicate create (e.g., race/retry) → return existing row instead of failing.
+    if (error.code === '23505') {
+      const existing = await getUserCredits(userId);
+      if (existing) return existing;
+    }
     throw new Error(`Failed to create user credits: ${error.message}`);
   }

   return data;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/repositories/credits-repository.ts` around lines 37 - 51, The
createUserCredits function throws an error when it encounters a unique
constraint violation (error code 23505) from concurrent requests, making it
non-idempotent. Modify the error handling logic to check if the error code is
23505 (unique constraint violation on the user_id field), and instead of
throwing, call the existing getUserCredits(userId) method to fetch and return
the existing record. This allows the function to safely handle duplicate or
concurrent creation attempts without failing on subsequent requests.

@gitguardian

gitguardian Bot commented Jun 17, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 3 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

Since your pull request originates from a forked repository, GitGuardian is not able to associate the secrets uncovered with secret incidents on your GitGuardian dashboard.
Skipping this check run and merging your pull request will create secret incidents on your GitGuardian dashboard.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
33127064 Triggered Generic Password 48fd85c tests/lib/validation.test.ts View secret
33127065 Triggered Generic Password 48fd85c tests/lib/validation.test.ts View secret
33127066 Triggered Generic Password 48fd85c tests/lib/validation.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

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.

[Go Backend][Feature]: Add PostgreSQL database layer with pgx connection pooling

7 participants