Implement edit profile screen, and fix username routing for public routes - #2
Implement edit profile screen, and fix username routing for public routes#2sherucon wants to merge 4 commits into
Conversation
…rofile management
…implement user sign-out options
📝 WalkthroughWalkthroughThe PR adds profile editing with draggable dossier sections, profile skeleton loading, authenticated user refresh, updated profile schema fields, shared UI changes, upload type support, database diagnostic scripts, and related navigation and configuration updates. ChangesProfile management
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ProfileScreen
participant AuthContext
participant Supabase
ProfileScreen->>Supabase: fetch profile
ProfileScreen->>AuthContext: refresh own authenticated user
AuthContext->>Supabase: fetch authenticated profile
Supabase-->>AuthContext: return profile fields
Supabase-->>ProfileScreen: return profile data
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (2)
src/components/text-input.tsx (1)
8-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
StyleProp<ViewStyle>forcontainerStyle.
containerStyleis used as a React NativeViewprop, which accepts plain style objects, arrays,StyleSheetstyles, or undefined. Typing it asViewStylerejects valid array orStyleSheetcallers at compile time.Proposed type fix
-import { StyleSheet, TextInput as RNTextInput, View, type TextInputProps, type ViewStyle } from 'react-native'; +import { StyleSheet, TextInput as RNTextInput, View, type StyleProp, type TextInputProps, type ViewStyle } from 'react-native'; export type CustomTextInputProps = TextInputProps & { error?: boolean; - containerStyle?: ViewStyle; + containerStyle?: StyleProp<ViewStyle>; };🤖 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/components/text-input.tsx` around lines 8 - 10, Update the CustomTextInputProps.containerStyle type to use React Native’s StyleProp<ViewStyle> instead of ViewStyle, preserving support for plain styles, arrays, StyleSheet styles, and undefined wherever containerStyle is passed to the View.test-db.js (1)
5-6: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winBound and redact the full-table diagnostics.
Both scripts materialize and print every profile. Query and log size grow with the database, and shared logs retain username and plan metadata.
test-db.js#L5-L6: usefindFirst/take: 1or explicit pagination, and avoid logging raw profiles.test-supabase.js#L7-L9: addlimit/pagination and log only a redacted sample or aggregate count.🤖 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 `@test-db.js` around lines 5 - 6, Bound both diagnostic queries and prevent sensitive profile metadata from being written to logs: in test-db.js lines 5-6, update the Prisma profile query near profiles to fetch at most one record or use explicit pagination, then log only a redacted sample or aggregate count; in test-supabase.js lines 7-9, add an equivalent limit or pagination and replace raw profile logging with a redacted sample or aggregate count.
🤖 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 `@check_schema.js`:
- Around line 1-4: The diagnostics scripts cannot run through the documented
project environment because the JavaScript entrypoint directly requires
TypeScript and neither entrypoint has a documented invocation. Update
check_schema.js and check_schema.ts to use the repository’s supported
Node/TypeScript loader setup, add package.json scripts for both diagnostics, and
document those commands alongside the existing Expo usage in README; apply the
corresponding loader-compatible change at check_schema.js and check_schema.ts,
preserving the schema query behavior.
In `@prisma/schema.prisma`:
- Line 23: Establish one canonical persisted section-order contract across all
affected sites: in prisma/schema.prisma:23-23, explicitly decide whether contact
and retired writing are persisted, adding the required migration or retirement
handling; in src/app/edit-profile.tsx:37-62, include every section allowed by
that contract in the editor data; and in src/app/edit-profile.tsx:116-128,
preserve fixed or unknown IDs when saving reordered sections instead of dropping
them.
- Line 14: Update the migration history for the Profile.plan schema change by
adding a data migration that converts existing persisted plan string values,
mapping the prior free/default value to false and the applicable paid or enabled
values to true before enforcing the Boolean column type. Keep the schema
declaration and existing migration conventions consistent.
In `@src/app/`[username].tsx:
- Around line 480-498: The Feed/Content Container currently renders only the
empty state, leaving populated tabs blank. Update the JSX around the
activeTab/isDossierEmpty, isPostsEmpty, and isArticlesEmpty condition to render
the corresponding dossier, post, or article content when that tab is non-empty,
while preserving the existing empty-state branch for empty tabs.
In `@src/app/edit-profile.tsx`:
- Around line 134-136: Implement the General option in handleOptionPress by
navigating to its intended sub-screen using the existing navigation pattern, or
remove/hide the General control until that destination is available. Ensure
tapping General no longer invokes a no-op handler.
- Around line 116-131: Update handleSaveOrder so the Supabase update is wrapped
in try/catch/finally, with setIsSavingOrder(false) executed in finally even when
the promise rejects. Keep setIsReordered(false) and updateUser within the
successful no-error path, and preserve error logging for failures.
In `@src/components/text-input.tsx`:
- Line 25: Update the shared TextInput usage in EditorialInput to forward its
error prop as error={error}, while preserving the existing style and value
handling so the shared input controls the error border state.
In `@src/context/AuthContext.tsx`:
- Around line 117-121: Update fetchProfileAndUpdateUser, used by refreshUser, to
handle profile-query errors separately without replacing the existing user
state. Only call mapUser and setUser when the profile is confirmed absent;
preserve the current user on transient query failures while retaining the
existing successful-profile behavior.
In `@test-db.js`:
- Line 8: Update the main() promise chain so a rejected query preserves a
failing process exit status by setting process.exitCode to 1 or rethrowing after
logging, and ensure prisma.$disconnect() is awaited during final cleanup.
In `@test-simulate.js`:
- Around line 12-18: Update the diagnostic logging inside the users iteration to
avoid emitting sessionUser.id, sessionUser.email, the full profile, or raw error
details. Replace per-account output with aggregate counts and sanitized error
codes, and only retain per-user tracing behind a local-only flag with redacted
identifiers.
- Around line 4-7: Update the client setup used by main so
auth.admin.listUsers() runs through a separate server-only Supabase client
initialized with the non-public service-role credential, rather than
EXPO_PUBLIC_SUPABASE_ANON_KEY. Keep the public client separate and ensure the
admin call uses the server client.
- Around line 7-12: Update the user-loading flow around
supabase.auth.admin.listUsers() to paginate through every available page before
processing users. Use the returned lastPage or nextPage metadata to continue
fetching and aggregate users across pages, while preserving the existing
authError/no-users handling and subsequent sessionUser processing.
In `@tsconfig.json`:
- Around line 14-17: Add CI coverage for the Supabase Edge Function sources
currently excluded by tsconfig.json: add a step that runs Deno’s type check
against supabase/functions, using the repository’s existing CI configuration and
Deno setup. If that directory contains generated artifacts instead, reorganize
the configuration so only generated files remain excluded and the source is
type-checked.
---
Nitpick comments:
In `@src/components/text-input.tsx`:
- Around line 8-10: Update the CustomTextInputProps.containerStyle type to use
React Native’s StyleProp<ViewStyle> instead of ViewStyle, preserving support for
plain styles, arrays, StyleSheet styles, and undefined wherever containerStyle
is passed to the View.
In `@test-db.js`:
- Around line 5-6: Bound both diagnostic queries and prevent sensitive profile
metadata from being written to logs: in test-db.js lines 5-6, update the Prisma
profile query near profiles to fetch at most one record or use explicit
pagination, then log only a redacted sample or aggregate count; in
test-supabase.js lines 7-9, add an equivalent limit or pagination and replace
raw profile logging with a redacted sample or aggregate count.
🪄 Autofix
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 Plus
Run ID: bccd7774-a55f-40cf-b31e-af53bb2eaea9
⛔ Files ignored due to path filters (2)
assets/images/support.pngis excluded by!**/*.pngpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (19)
check_schema.jscheck_schema.tspackage.jsonprisma/schema.prismasrc/app/(auth)/welcome.tsxsrc/app/(tabs)/_layout.tsxsrc/app/[username].tsxsrc/app/_layout.tsxsrc/app/edit-profile.tsxsrc/components/button.tsxsrc/components/editorial-input.tsxsrc/components/text-input.tsxsrc/context/AuthContext.tsxsrc/hooks/useImageUpload.tstest-col.jstest-db.jstest-simulate.jstest-supabase.jstsconfig.json
💤 Files with no reviewable changes (1)
- src/app/(tabs)/_layout.tsx
| const { supabase } = require('./src/lib/supabase.ts'); | ||
|
|
||
| async function checkSchema() { | ||
| const { data, error } = await supabase.from('Project').select('*').limit(1); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for file in package.json .nvmrc .node-version .tool-versions tsconfig.json; do
if [ -f "$file" ]; then
echo "== $file =="
rg -n 'tsx|ts-node|node --import|module|type|dotenv|check_schema' "$file" || true
fi
done
rg -n -C3 'ExpoSecureStoreAdapter|createClient|EXPO_PUBLIC_SUPABASE_(URL|ANON_KEY)' \
src/lib/supabase.ts check_schema.js check_schema.tsRepository: sherucon/toll
Length of output: 1385
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -a '^(check_schema\.(js|ts)|package\.json|tsconfig\.json|readme|README|docs|scripts)$|schema|script' . | head -100 || true
echo "== root files =="
for f in package.json tsconfig.json README.md readmi docs scripts check_schema.js check_schema.ts; do
[ -e "$f" ] && echo "--- $f" && wc -l "$f"
done
echo "== scripts and imports =="
python3 - <<'PY'
from pathlib import Path
for p in ['package.json','tsconfig.json','check_schema.js','check_schema.ts','README.md']:
if Path(p).exists():
print(f'-- {p} --')
print(Path(p).read_text()[:2000])
PY
echo "== relevant searches =="
rg -n -S --glob '!node_modules' --glob '!dist' --glob '!build' 'check_schema|EXPO_PUBLIC_SUPERBASE|dotenv|tsx|ts-node|node -r|react-native-url-polyfill|createClient|supabaseUrl|supabaseAnonKey' . || true
echo "== package scripts =="
python3 - <<'PY'
import json, pathlib
p=pathlib.Path('package.json')
if p.exists():
data=json.loads(p.read_text())
for key in ['scripts','dependencies','devDependencies']:
print(f'[{key}]')
for k,v in data.get(key,{}).items():
if 'supabase' in k.lower() or k in ['typescript','tsx','ts-node','expo','expo-secure-store','react-native-url-polyfill','dotenv','vite','react-native'] or 'check_schema' in str(v):
print(k, v)
PYRepository: sherucon/toll
Length of output: 538
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== script files =="
for f in check_schema.js check_schema.ts; do
if [ -f "$f" ]; then
echo "--- $f ($(wc -l < "$f") lines) ---"
cat -n "$f"
fi
done
echo "== package and config =="
printf '\n--- package.json ---\n'
cat -n package.json
printf '\n--- tsconfig.json ---\n'
cat -n tsconfig.json
printf '\n--- README.md ---\n'
cat -n README.md
echo "== scripts directory =="
find scripts -maxdepth 2 -type f -print -exec sh -c 'echo "--- $1 ---"; wc -l "$1"; sed -n "1,120p" "$1"' _ {} \;
echo "== relevant search =="
rg -n -S --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!*.lock' \
'check_schema|EXPO_PUBLIC_SUPABASE|dotenv|tsx|ts-node|node -r|react-native-url-polyfill|createClient|supabaseUrl|supabaseAnonKey|package:.*check_schema|scripts' . || true
echo "== package metadata =="
python3 - <<'PY'
import json, pathlib
data=json.loads(pathlib.Path("package.json").read_text())
for key in ["scripts","dependencies","devDependencies"]:
print(f"[{key}]")
for k,v in data.get(key,{}).items():
if k.lower() in ["tsx","ts-node","typescript","expo","expo-secure-store","react-native-url-polyfill","dotenv"] \
or k.lower().startswith("`@supabase`") \
or any(s in str(v).lower() for s in ["check_schema","supabase","dotenv","node --import","ts-node","tsx"]):
print(k, v)
PYRepository: sherucon/toll
Length of output: 16244
Make these diagnostics scripts runnable in the documented project environment.
package.json has no check_schema script, and the README only mentions npx expo start. check_schema.js also requires .ts directly from CommonJS. Add a documented script/Invocation path for both .js and .ts, or align the diagnostics with the repo’s Node loader setup so the scripts do not fail before the schema query.
📍 Affects 2 files
check_schema.js#L1-L4(this comment)check_schema.ts#L1-L4
🤖 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 `@check_schema.js` around lines 1 - 4, The diagnostics scripts cannot run
through the documented project environment because the JavaScript entrypoint
directly requires TypeScript and neither entrypoint has a documented invocation.
Update check_schema.js and check_schema.ts to use the repository’s supported
Node/TypeScript loader setup, add package.json scripts for both diagnostics, and
document those commands alongside the existing Expo usage in README; apply the
corresponding loader-compatible change at check_schema.js and check_schema.ts,
preserving the schema query behavior.
Source: MCP tools
| name String | ||
| username String @unique | ||
| plan String @default("free") // "free", "pro", "studio" | ||
| plan Boolean @default(false) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -H -t f . prisma | sort
rg -n -C 5 'ALTER TABLE.*Profile|ALTER COLUMN.*plan|sectionOrder|plan' prismaRepository: sherucon/toll
Length of output: 1504
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== git tracked prisma files =="
git ls-files prisma | sort
echo "== schema around Profile =="
sed -n '1,45p' prisma/schema.prisma
echo "== references to Profile.plan and plan assignments/reads =="
rg -n -C 3 '\bplan\b|Profile\.plan' . --glob '!node_modules' --glob '!dist' --glob '!build' --glob '!coverage'
echo "== migrations files =="
fd -H -t f . prisma | sortRepository: sherucon/toll
Length of output: 8717
Add a data migration for Profile.plan.
Profile.plan changed from a persisted string default such as "free" to a Boolean, and there is no migration that maps existing profile plan values. Add a migration that explicitly converts existing values before deploying this schema.
🤖 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 `@prisma/schema.prisma` at line 14, Update the migration history for the
Profile.plan schema change by adding a data migration that converts existing
persisted plan string values, mapping the prior free/default value to false and
the applicable paid or enabled values to true before enforcing the Boolean
column type. Keep the schema declaration and existing migration conventions
consistent.
| socialLinks Json? // Stores an array or object of links | ||
| setupComplete Boolean @default(false) // Tracks onboarding completion | ||
| sectionOrder String[] @default(["projects", "sideProjects", "writing", "awards", "workExperience", "volunteering", "education", "certifications", "contact"]) | ||
| sectionOrder String[] @default(["projects", "sideProjects", "awards", "workExperience", "volunteering", "education", "certifications", "contact"]) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Use one canonical section-order contract.
The schema defaults include contact, but the editor cannot display or preserve it. Saving any reordered profile writes only the draggable IDs and removes contact from persisted sectionOrder. Existing writing IDs also need an explicit migration or retirement path.
prisma/schema.prisma#L23-L23: define whethercontactand retiredwritingsections belong in persisted ordering.src/app/edit-profile.tsx#L37-L62: include every reorderable persisted section in the editor data.src/app/edit-profile.tsx#L116-L128: preserve fixed or unknown section IDs when writing an edited order.
📍 Affects 2 files
prisma/schema.prisma#L23-L23(this comment)src/app/edit-profile.tsx#L37-L62src/app/edit-profile.tsx#L116-L128
🤖 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 `@prisma/schema.prisma` at line 23, Establish one canonical persisted
section-order contract across all affected sites: in prisma/schema.prisma:23-23,
explicitly decide whether contact and retired writing are persisted, adding the
required migration or retirement handling; in src/app/edit-profile.tsx:37-62,
include every section allowed by that contract in the editor data; and in
src/app/edit-profile.tsx:116-128, preserve fixed or unknown IDs when saving
reordered sections instead of dropping them.
| {/* Feed/Content Container */} | ||
| <View style={styles.feedContainer}> | ||
| {((activeTab === "Dossier" && isDossierEmpty) || | ||
| (activeTab === "Posts" && isPostsEmpty) || | ||
| (activeTab === "Articles" && isArticlesEmpty)) && ( | ||
| <View style={styles.emptyStateContainer}> | ||
| <Image | ||
| source={require("@/assets/images/not-found.png")} | ||
| style={styles.emptyStateImage} | ||
| resizeMode="contain" | ||
| /> | ||
| <ThemedText style={styles.emptyStateText}> | ||
| {isOwnProfile | ||
| ? `Add sections to your ${activeTab.toLowerCase()}` | ||
| : "Quacks!"} | ||
| </ThemedText> | ||
| </View> | ||
| )} | ||
| </View> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Render populated tab content.
When the active tab contains data, the condition at Lines 482-484 is false. The feed container then renders no dossiers, posts, or articles.
Add the tab-specific content renderer alongside this empty-state branch. Otherwise every non-empty profile tab appears blank.
🤖 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/`[username].tsx around lines 480 - 498, The Feed/Content Container
currently renders only the empty state, leaving populated tabs blank. Update the
JSX around the activeTab/isDossierEmpty, isPostsEmpty, and isArticlesEmpty
condition to render the corresponding dossier, post, or article content when
that tab is non-empty, while preserving the existing empty-state branch for
empty tabs.
| const handleSaveOrder = async () => { | ||
| if (!user?.id) return; | ||
| setIsSavingOrder(true); | ||
| const newOrder = dossierData.map((item) => item.id); | ||
| const { error } = await supabase | ||
| .from("Profile") | ||
| .update({ sectionOrder: newOrder }) | ||
| .eq("id", user.id); | ||
|
|
||
| setIsSavingOrder(false); | ||
| if (!error) { | ||
| setIsReordered(false); | ||
| updateUser({ sectionOrder: newOrder }); | ||
| } else { | ||
| console.error(error); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Always clear isSavingOrder.
If the update promise rejects, Line 125 does not run. The Save Order control then remains disabled and displays Saving... until the screen is remounted.
Move setIsSavingOrder(false) into a finally block. Keep the success-only state updates inside the successful path.
Proposed fix
setIsSavingOrder(true);
- const newOrder = dossierData.map((item) => item.id);
- const { error } = await supabase
- .from("Profile")
- .update({ sectionOrder: newOrder })
- .eq("id", user.id);
-
- setIsSavingOrder(false);
- if (!error) {
- setIsReordered(false);
- updateUser({ sectionOrder: newOrder });
- } else {
- console.error(error);
+ try {
+ const newOrder = dossierData.map((item) => item.id);
+ const { error } = await supabase
+ .from("Profile")
+ .update({ sectionOrder: newOrder })
+ .eq("id", user.id);
+
+ if (error) {
+ console.error(error);
+ return;
+ }
+
+ setIsReordered(false);
+ updateUser({ sectionOrder: newOrder });
+ } catch (error) {
+ console.error(error);
+ } finally {
+ setIsSavingOrder(false);
}📝 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.
| const handleSaveOrder = async () => { | |
| if (!user?.id) return; | |
| setIsSavingOrder(true); | |
| const newOrder = dossierData.map((item) => item.id); | |
| const { error } = await supabase | |
| .from("Profile") | |
| .update({ sectionOrder: newOrder }) | |
| .eq("id", user.id); | |
| setIsSavingOrder(false); | |
| if (!error) { | |
| setIsReordered(false); | |
| updateUser({ sectionOrder: newOrder }); | |
| } else { | |
| console.error(error); | |
| } | |
| const handleSaveOrder = async () => { | |
| if (!user?.id) return; | |
| setIsSavingOrder(true); | |
| try { | |
| const newOrder = dossierData.map((item) => item.id); | |
| const { error } = await supabase | |
| .from("Profile") | |
| .update({ sectionOrder: newOrder }) | |
| .eq("id", user.id); | |
| if (error) { | |
| console.error(error); | |
| return; | |
| } | |
| setIsReordered(false); | |
| updateUser({ sectionOrder: newOrder }); | |
| } catch (error) { | |
| console.error(error); | |
| } finally { | |
| setIsSavingOrder(false); | |
| } |
🧰 Tools
🪛 React Doctor (0.9.3)
[error] 125-125: This resets a loading/busy flag only on the success path: if the awaited call rejects the reset never runs and the flag stays stuck truthy (a spinner that never stops, a button disabled forever). Move the reset into a finally block, or mirror it on every catch, so it clears on rejection too.
A trailing setLoading(false) after an await never runs if the awaited call rejects, so the flag stays stuck truthy; reset it in a finally block (or mirror the reset on every catch) so it clears on both paths.
(no-loading-flag-reset-outside-finally)
🤖 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/edit-profile.tsx` around lines 116 - 131, Update handleSaveOrder so
the Supabase update is wrapped in try/catch/finally, with
setIsSavingOrder(false) executed in finally even when the promise rejects. Keep
setIsReordered(false) and updateUser within the successful no-error path, and
preserve error logging for failures.
Source: Linters/SAST tools
| const profiles = await prisma.profile.findMany({ select: { username: true, plan: true } }); | ||
| console.log(profiles); | ||
| } | ||
| main().catch(console.error).finally(() => prisma.$disconnect()); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Preserve a failing exit status.
catch(console.error) logs a rejected query and then resolves successfully. CI or shell callers can receive exit code 0 for a database failure. Set process.exitCode = 1 or rethrow after logging, and await $disconnect() during cleanup.
Proposed fix
-main().catch(console.error).finally(() => prisma.$disconnect());
+main()
+ .catch((error) => {
+ console.error(error);
+ process.exitCode = 1;
+ })
+ .finally(async () => {
+ await prisma.$disconnect();
+ });📝 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.
| main().catch(console.error).finally(() => prisma.$disconnect()); | |
| main() | |
| .catch((error) => { | |
| console.error(error); | |
| process.exitCode = 1; | |
| }) | |
| .finally(async () => { | |
| await prisma.$disconnect(); | |
| }); |
🤖 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 `@test-db.js` at line 8, Update the main() promise chain so a rejected query
preserves a failing process exit status by setting process.exitCode to 1 or
rethrowing after logging, and ensure prisma.$disconnect() is awaited during
final cleanup.
| const supabase = createClient(process.env.EXPO_PUBLIC_SUPABASE_URL, process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY); | ||
|
|
||
| async function main() { | ||
| const { data: { users }, error: authError } = await supabase.auth.admin.listUsers(); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,120p'
echo "== test-simulate.js =="
if [ -f test-simulate.js ]; then
cat -n test-simulate.js
else
fd -i '^test-simulate\.js$' -x sh -c 'echo "--- $1"; cat -n "$1"' sh {}
fi
echo "== env references =="
rg -n "EXPO_PUBLIC_SUPABASE_(URL|ANON_KEY)|SUPABASE_(SECRET|SERVICE_ROLE)_KEY|auth\.admin\.listUsers|createClient" . --glob '!node_modules' --glob '!dist' --glob '!build' || trueRepository: sherucon/toll
Length of output: 7438
🌐 Web query:
Supabase JavaScript Auth Admin listUsers serverOnly service_role key documentation
💡 Result:
To list users in Supabase using the JavaScript client, you should use the supabase.auth.admin.listUsers() method [1][2]. Critical Security Considerations: 1. Server-Side Only: This method is intended exclusively for server-side environments [1][2][3]. 2. Service Role Key: You must initialize the Supabase client using your project's service_role API key to access administrative functions [4][5][6]. 3. Never Expose in Browser: The service_role key bypasses Row Level Security (RLS) and provides full administrative access to your project [4]. You must never expose this key in client-side code (e.g., frontend browser applications), as doing so will compromise your database [1][2][3]. Implementation Example: You should create a separate, dedicated Supabase client on your server using the service_role key, distinct from any client used for standard frontend operations [4]. // Server-side code example import { createClient } from '@supabase/supabase-js' const supabaseAdmin = createClient( process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY // Use server-side environment variables) const { data: { users }, error } = await supabaseAdmin.auth.admin.listUsers({ page: 1, perPage: 100 }) if (error) { console.error('Error fetching users:', error) } else { console.log(users) } Method Signature: The listUsers method accepts an optional parameters object for pagination [1][2]: - page (number): The page number to retrieve [1][5]. - perPage (number): The number of users to return per page [1][5]. For further details, refer to the official Supabase documentation for the JavaScript Auth Admin API [1][7].
Citations:
- 1: https://supabase.com/docs/reference/javascript/auth-admin-listusers
- 2: https://supabase.github.io/auth-js/v2/classes/GoTrueAdminApi.html
- 3: https://github.com/supabase/supabase-js/blob/bd024171/packages/core/auth-js/src/GoTrueAdminApi.ts
- 4: https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z
- 5: https://apis.io/apis/supabase/supabase-admin-api/
- 6: https://raw.githubusercontent.com/api-evangelist/supabase/refs/heads/main/openapi/supabase-admin-api-openapi.yml
- 7: https://supabase.com/docs/reference/javascript/admin-api
Use a server-only credential for Auth Admin.
auth.admin.listUsers() requires a Supabase service_role key, but this client uses the public EXPO_PUBLIC_SUPABASE_ANON_KEY. Create a separate server client with a non-public Supabase key before calling supabase.auth.admin.listUsers().
🤖 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 `@test-simulate.js` around lines 4 - 7, Update the client setup used by main so
auth.admin.listUsers() runs through a separate server-only Supabase client
initialized with the non-public service-role credential, rather than
EXPO_PUBLIC_SUPABASE_ANON_KEY. Keep the public client separate and ensure the
admin call uses the server client.
Source: MCP tools
| const { data: { users }, error: authError } = await supabase.auth.admin.listUsers(); | ||
| if (authError || !users) { | ||
| console.log('No users found or error:', authError); | ||
| return; | ||
| } | ||
| for (const sessionUser of users) { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | sed -n '1,120p'
echo "== target file =="
if [ -f test-simulate.js ]; then
nl -ba test-simulate.js | sed -n '1,120p'
else
fd -a 'test-simulate\.js' .
fi
echo "== listUsers mentions =="
rg -n "listUsers|perPage|page|users\]" . || trueRepository: sherucon/toll
Length of output: 5237
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== test-simulate.js =="
awk '{printf "%6d\t%s\n", NR, $0}' test-simulate.js | sed -n '1,180p'
echo "== listUsers/user profile code =="
rg -n "listUsers|profile|profiles|supabase=|createClient|users\]" . -g '*.js' -g '*.ts' -g '*.tsx' || trueRepository: sherucon/toll
Length of output: 6354
🌐 Web query:
Supabase JavaScript Auth Admin listUsers page perPage pagination docs
💡 Result:
In the Supabase JavaScript client, the auth.admin.listUsers method supports server-side pagination using an optional params object [1][2]. Usage and Parameters: The method accepts a params object with the following optional properties [1]: - page (number): The page number to retrieve [1][2]. - perPage (number): The number of results to return per page [1][2]. By default, listUsers returns 50 users per page if no pagination parameters are provided [2][3]. Example Implementation: const { data: { users }, error } = await supabase.auth.admin.listUsers({ page: 1, perPage: 1000 }) Security Note: This function is intended for server-side use only. Never expose your service_role key in client-side code (e.g., browser environments), as it bypasses Row Level Security (RLS) [1][2]. Metadata: The listUsers method also returns pagination metadata in the response object, including [4][3]: - total: The total number of records [4][3]. - nextPage: The next page index, or null if no further results exist [4][3]. - lastPage: The index of the final page [4][3]. These fields are derived from the API response headers (x-total-count and Link headers) [4][3]. When using this method, ensure your version of the Supabase client is up-to-date to avoid potential bugs associated with parsing multi-digit page numbers from the Link header [5].
Citations:
- 1: https://supabase.com/docs/reference/javascript/auth-admin-listusers
- 2: https://supabase.github.io/auth-js/v2/classes/GoTrueAdminApi.html
- 3: https://github.com/supabase/supabase-js/blob/21e9d4b7/packages/core/auth-js/src/GoTrueAdminApi.ts
- 4: feat: return pagination data for the
listUsers()method supabase/auth-js#544 - 5: fix(auth): read full page number from listUsers Link header supabase/supabase-js#2465
Paginate the user list.
supabase.auth.admin.listUsers() returns total, nextPage, and lastPage, but test-simulate.js only calls it once. If the project has more than one page of users, later users are skipped. Loop through pages using lastPage or nextPage before querying profiles.
🤖 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 `@test-simulate.js` around lines 7 - 12, Update the user-loading flow around
supabase.auth.admin.listUsers() to paginate through every available page before
processing users. Use the returned lastPage or nextPage metadata to continue
fetching and aggregate users across pages, while preserving the existing
authError/no-users handling and subsequent sessionUser processing.
Source: MCP tools
| for (const sessionUser of users) { | ||
| const { data: profile, error } = await supabase | ||
| .from('Profile') | ||
| .select('username, name, avatar, setupComplete, plan') | ||
| .eq('id', sessionUser.id) | ||
| .single(); | ||
| console.log(`User ${sessionUser.id} (${sessionUser.email}):`, { profile, error }); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Remove user data from diagnostic logs.
Line 18 writes sessionUser.id, sessionUser.email, the full profile, and error for every account. Shared CI logs can retain these values. Log aggregate counts and sanitized error codes. If per-user tracing is required, gate it behind a local-only flag and redact identifiers.
🧰 Tools
🪛 ast-grep (0.45.0)
[warning] 17-17: Avoid logging sensitive data
Context: console.log(User ${sessionUser.id} (${sessionUser.email}):, { profile, error })
Note: [CWE-532] Insertion of Sensitive Information into Log File.
(log-sensitive-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 `@test-simulate.js` around lines 12 - 18, Update the diagnostic logging inside
the users iteration to avoid emitting sessionUser.id, sessionUser.email, the
full profile, or raw error details. Replace per-account output with aggregate
counts and sanitized error codes, and only retain per-user tracing behind a
local-only flag with redacted identifiers.
Source: Linters/SAST tools
| "exclude": [ | ||
| "node_modules", | ||
| "supabase/functions" | ||
| ], |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
if [ -d supabase/functions ]; then
fd -t f . supabase/functions
fi
rg -n 'supabase/functions|typecheck|tsc|deno' \
package.json .github tsconfig.json 2>/dev/null || trueRepository: sherucon/toll
Length of output: 337
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- package.json relevant scripts ---\n'
if [ -f package.json ]; then
jq '.scripts // {}' package.json
fi
printf '\n--- tsconfig.json ---\n'
cat -n tsconfig.json
printf '\n--- Supabase function files ---\n'
for f in $(fd -t f . supabase/functions); do
echo "--- $f ---"
sed -n '1,120p' "$f"
doneRepository: sherucon/toll
Length of output: 242
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "--- package.json relevant scripts ---"
if [ -f package.json ]; then
jq '.scripts // {}' package.json
fi
echo
echo "--- tsconfig.json ---"
cat -n tsconfig.json
echo
echo "--- Supabase function files ---"
while IFS= read -r f; do
echo "--- $f ---"
sed -n '1,120p' "$f"
done < <(fd -t f . supabase/functions)Repository: sherucon/toll
Length of output: 1021
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "--- Supabase function files ---"
fd -t f . supabase/functions | while IFS= read -r f; do
echo "--- $f ---"
sed -n '1,120p' "$f"
done
echo
echo "--- package.json dev scripts and deps ---"
jq '{scripts, devDependencies, dependencies}' package.json
echo
echo "--- type-check/deno-related files ---"
fd -i '^(deno\.json|deno\.jsonc|tsconfig.*\.json)$|package\.json$' . | sortRepository: sherucon/toll
Length of output: 6187
Add a Deno type check for Supabase Edge Functions.
supabase/functions is deployed Edge Function source, and this config excludes it. package.json has no scripts for Deno/type-checking that directory. Add a CI step using Deno’s type check for supabase/functions, or reorganize if those files are generated artifacts.
🤖 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 `@tsconfig.json` around lines 14 - 17, Add CI coverage for the Supabase Edge
Function sources currently excluded by tsconfig.json: add a step that runs
Deno’s type check against supabase/functions, using the repository’s existing CI
configuration and Deno setup. If that directory contains generated artifacts
instead, reorganize the configuration so only generated files remain excluded
and the source is type-checked.
Summary by CodeRabbit
New Features
Bug Fixes
Style