Skip to content

chore: promote main to master (auth security fix, signin UX, txt resumes, loading skeletons)#92

Merged
RikepilB merged 5 commits into
masterfrom
main
Jun 11, 2026
Merged

chore: promote main to master (auth security fix, signin UX, txt resumes, loading skeletons)#92
RikepilB merged 5 commits into
masterfrom
main

Conversation

@RikepilB

@RikepilB RikepilB commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Promotes the following to production:

  • Security: dev credentials provider (any email → ADMIN) no longer enabled when Google OAuth is unconfigured — it was live on this deployment
  • Sign-in page: Google button hidden when unconfigured, NextAuth errors surfaced, clear notice when no method available
  • .txt resume support end-to-end
  • Route-level loading skeletons for all admin segments + careers pages; toast feedback for re-score and parse-retry actions

Production env vars (AUTH_GOOGLE_*, RESEND_API_KEY, EMAIL_FROM, OPENROUTER_MODEL) have been set in Vercel.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added TXT file support for resume uploads alongside existing formats
    • Implemented loading skeleton UI across admin pages and career postings for improved perceived performance
    • Enhanced sign-in form with error messaging and authentication configuration awareness
  • Bug Fixes

    • Improved authentication security to prevent unauthorized dev login in production
    • Enhanced error handling and user feedback for admin operations (rescore and parsing retry)
  • Documentation

    • Updated environment variable documentation with dev login configuration guidance
    • Clarified worker/inline execution behavior in setup documentation

RikepilB and others added 5 commits June 11, 2026 13:36
- docs/SETUP.md: correct RESUME_PARSE_MODE default (queue, not
  queue-and-inline) and document JOB_RUNNER=inline behavior without workers
- sign-in.test.ts: regression test proving lookalike/subdomain emails are
  rejected by the AUTH_ALLOWED_EMAIL_DOMAIN gate (endsWith(@ + domain)
  anchors on the @ separator, so no bypass exists)
- dispatch.test.ts: test documenting that inline admin fan-out is
  fire-and-forget — send failures land in EmailLog, not the returned result
The dev credentials provider (any email -> ADMIN) was registered whenever
Google OAuth credentials were missing, which left admin sign-in wide open
on deployments without AUTH_GOOGLE_* set. It is now restricted to
NODE_ENV=development, with an explicit ALLOW_DEV_LOGIN=1 escape hatch for
local production-build smoke tests.

Also on the signin page:
- hide the Google button when OAuth is not configured (clicking it
  previously bounced through /api/auth/signin back to /signin with no
  feedback - the reported refresh loop)
- surface NextAuth ?error= codes as a visible alert
- show a clear notice when no sign-in method is available

And accept .txt resumes end-to-end (upload guard, schema message, file
input accept list); text extraction and inline preview already support
plain text.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add loading.tsx fallbacks for every admin segment (shared
  AdminPageSkeleton), the public careers job page, and the root job
  board, so navigation shows immediate skeleton/branded loading states
  instead of a frozen screen while server components stream.
- Add a shadcn-style Skeleton primitive.
- RescoreButton: surface success/error via toast (failures were
  silently swallowed); RetryParsingButton: toast on success/failure in
  addition to the inline error text.

Co-Authored-By: Claude <noreply@anthropic.com>
…tions

fix(auth): never enable dev login on production deployments
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
scoutlane Ready Ready Preview, Comment Jun 11, 2026 8:31pm

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR integrates loading UI skeletons across admin and public routes, hardens dev login with explicit configuration, improves sign-in error handling and auth gating, expands resume uploads to support TXT files, adds toast feedback for async operations, and includes test coverage and documentation updates.

Changes

Loading UI, Auth, Features, and UX Improvements

Layer / File(s) Summary
Loading UI skeleton system
src/components/ui/skeleton.tsx, src/app/(admin)/_components/AdminPageSkeleton.tsx, src/app/(admin)/**/loading.tsx, src/app/loading.tsx, src/app/(public)/careers/[slug]/loading.tsx
Skeleton component provides reusable pulsing placeholder styling. AdminPageSkeleton combines header, card grid, and content skeletons with configurable card count. All admin routes and public career page add loading.tsx segments that render AdminPageSkeleton; app root adds top-level Loading with branded gradient animation.
Dev login production-safe configuration
.env.example, src/lib/auth/auth.config.ts, src/lib/auth/auth.config.test.ts
ALLOW_DEV_LOGIN environment variable added with security documentation. isDevLoginAllowed() no longer enables dev credentials implicitly when Google auth is missing; instead requires NODE_ENV === "development" or ALLOW_DEV_LOGIN === "1". Tests verify production disables dev login without explicit flag and rejects non-"1" values.
Sign-in form error display and Google auth gating
src/app/signin/_components/SignInForm.tsx, src/app/signin/page.tsx, src/lib/auth/sign-in.test.ts
SignInForm now accepts googleEnabled boolean prop and derives error message from query parameter via AUTH_ERROR_MESSAGES map with fallback. Conditionally renders error alert and "Google not configured" warning. SignInPage computes and passes googleEnabled based on auth configuration. New test verifies AUTH_ALLOWED_EMAIL_DOMAIN rejects lookalike domains while allowing exact match.
Resume file format expansion to TXT
src/lib/storage/upload-limits.ts, src/schemas/application.ts, src/components/public/ApplicationForm.tsx
ALLOWED_RESUME_MIME and ALLOWED_RESUME_EXTENSIONS expand to include text/plain and .txt. ApplicationForm file input and helper text updated to communicate TXT as allowed format alongside PDF, DOC, DOCX, CSV.
Toast notifications for async operations
src/app/(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RescoreButton.tsx, src/app/(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RetryParsingButton.tsx
RescoreButton wraps rescore fetch in try/catch, parses error payloads, and emits success/failure toasts; refreshes router only on success. RetryParsingButton shows error toast with response or fallback message and success toast on completion before refresh.
Email dispatch inline failure handling test
src/server/jobs/dispatch.test.ts
New test mocks dispatchEmailJob failures, verifies inline delivery attempts occur for both admin emails, asserts failed: [] in return result (failures not surfaced), and checks console.error invoked twice.
Documentation updates
docs/SETUP.md, docs/session-reports/2026-06-11-resume-pipeline-oauth-emails.md
SETUP.md clarifies JOB_RUNNER=inline default on Vercel and RESUME_PARSE_MODE timing behavior. Session report documents PR #89 landing items, E2E smoke verification, CodeRabbit triage, remaining deploy steps, and risks around Resend delivery and inline LLM timeouts.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • RikepilB/ScoutLane#88: Introduces upload-limits module that this PR extends with .txt/text/plain support.
  • RikepilB/ScoutLane#83: Modifies isDevLoginAllowed and Google configuration auth gating that this PR refines with ALLOW_DEV_LOGIN flag.
  • RikepilB/ScoutLane#90: Overlaps on Google OAuth email-domain validation tests and sign-in flow improvements.

🐰 Loading states bloom like spring grass,
Dev login now locked tight—no more oops, alas!
Toast and skeleton dance the admin way,
Resume format grows, and form errors display! ✨📝

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: a production promotion (chore) covering auth security fix, signin UX improvements, txt resume support, and loading skeletons.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

Caution

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

⚠️ Outside diff range comments (1)
src/app/(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RetryParsingButton.tsx (1)

22-35: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle thrown fetch errors and stop refreshing after failed retries.

Line 24 can throw (network/offline), but this path is uncaught, so users may get no failure feedback. Also, after the non-OK branch, Line 34 still refreshes and can immediately clear the inline error set at Line 29.

Suggested patch
   function handleRetry() {
     startTransition(async () => {
       setError(null);
-      const response = await fetch(`/api/admin/jobs/parse-retry/${applicantId}?mode=inline`, {
-        method: "POST",
-      });
-      const body = (await response.json().catch(() => null)) as { error?: string } | null;
-      if (!response.ok) {
-        setError(body?.error ?? "Resume parsing failed.");
-        toast.error(body?.error ?? "Resume parsing failed.");
-      } else {
-        toast.success("Resume parsed. Refreshing applicant data…");
-      }
-      router.refresh();
+      try {
+        const response = await fetch(`/api/admin/jobs/parse-retry/${applicantId}?mode=inline`, {
+          method: "POST",
+        });
+        const body = (await response.json().catch(() => null)) as { error?: string } | null;
+        if (!response.ok) {
+          const message = body?.error ?? "Resume parsing failed.";
+          setError(message);
+          toast.error(message);
+          return;
+        }
+        toast.success("Resume parsed. Refreshing applicant data…");
+        router.refresh();
+      } catch {
+        const message = "Resume parsing failed. Check your connection and try again.";
+        setError(message);
+        toast.error(message);
+      }
     });
   }
🤖 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
`@src/app/`(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RetryParsingButton.tsx
around lines 22 - 35, Wrap the network call in startTransition with a try/catch
around the fetch/response parsing so thrown fetch errors are caught and handled
by setError and toast.error (e.g., catch network/offline exceptions from the
fetch and JSON parsing), and move the router.refresh call so it only runs on
successful parsing (inside the response.ok branch) to avoid clearing inline
error state after failures; update the logic in RetryParsingButton's
startTransition handler (references: startTransition, setError,
fetch(`/api/admin/jobs/parse-retry/${applicantId}?mode=inline`), toast.error,
toast.success, router.refresh) accordingly.
🤖 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.

Outside diff comments:
In
`@src/app/`(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RetryParsingButton.tsx:
- Around line 22-35: Wrap the network call in startTransition with a try/catch
around the fetch/response parsing so thrown fetch errors are caught and handled
by setError and toast.error (e.g., catch network/offline exceptions from the
fetch and JSON parsing), and move the router.refresh call so it only runs on
successful parsing (inside the response.ok branch) to avoid clearing inline
error state after failures; update the logic in RetryParsingButton's
startTransition handler (references: startTransition, setError,
fetch(`/api/admin/jobs/parse-retry/${applicantId}?mode=inline`), toast.error,
toast.success, router.refresh) accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03d92666-32f0-4f3b-8a8a-3e6423e8f49f

📥 Commits

Reviewing files that changed from the base of the PR and between cc053a6 and 0da9ead.

📒 Files selected for processing (29)
  • .env.example
  • docs/SETUP.md
  • docs/session-reports/2026-06-11-resume-pipeline-oauth-emails.md
  • src/app/(admin)/_components/AdminPageSkeleton.tsx
  • src/app/(admin)/admin/applicants/loading.tsx
  • src/app/(admin)/admin/email-templates/loading.tsx
  • src/app/(admin)/admin/integrations/loading.tsx
  • src/app/(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RescoreButton.tsx
  • src/app/(admin)/admin/jobs/[id]/applicants/[applicantId]/_components/RetryParsingButton.tsx
  • src/app/(admin)/admin/jobs/[id]/applicants/[applicantId]/loading.tsx
  • src/app/(admin)/admin/jobs/[id]/applicants/loading.tsx
  • src/app/(admin)/admin/jobs/[id]/loading.tsx
  • src/app/(admin)/admin/jobs/loading.tsx
  • src/app/(admin)/admin/loading.tsx
  • src/app/(admin)/admin/notifications/loading.tsx
  • src/app/(admin)/admin/settings/loading.tsx
  • src/app/(admin)/admin/templates/loading.tsx
  • src/app/(public)/careers/[slug]/loading.tsx
  • src/app/loading.tsx
  • src/app/signin/_components/SignInForm.tsx
  • src/app/signin/page.tsx
  • src/components/public/ApplicationForm.tsx
  • src/components/ui/skeleton.tsx
  • src/lib/auth/auth.config.test.ts
  • src/lib/auth/auth.config.ts
  • src/lib/auth/sign-in.test.ts
  • src/lib/storage/upload-limits.ts
  • src/schemas/application.ts
  • src/server/jobs/dispatch.test.ts

@RikepilB RikepilB merged commit 01fefa9 into master Jun 11, 2026
4 checks passed
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.

1 participant