Membuat api - #3
Conversation
|
Warning Review limit reached
Next review available in: 9 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. 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, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe PR adds Supabase SSR support, login and partner registration APIs, role-based middleware, and authenticated profile, verification, and E-Contract endpoints. It also removes the legacy connectivity endpoint and Supabase client module. ChangesSupabase authentication and partner workflow
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant RegistrationRoute
participant SupabaseAuth
participant AdminClient
participant ProfileTable
Client->>RegistrationRoute: Submit registration data
RegistrationRoute->>SupabaseAuth: Create user with role metadata
SupabaseAuth-->>RegistrationRoute: Return user ID
RegistrationRoute->>AdminClient: Insert pending profile
AdminClient->>ProfileTable: Persist partner profile
ProfileTable-->>AdminClient: Return insert result
RegistrationRoute-->>Client: Return registration response
Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 18
🧹 Nitpick comments (6)
lib/supabase/server.ts (2)
5-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsolidate admin client creation on this helper.
app/api/registrasi_agen/route.ts(Lines 3, 50-53) andapp/api/registrasi_perusahaan/route.ts(Lines 3, 49-52) importcreateClientfrom@supabase/supabase-jsdirectly and rebuild the same admin client. This helper appears unused. Move the environment reads and validation intocreateAdminClient, then call it from both routes.♻️ Proposed refactor
-export function createAdminClient(url: string, key: string) { - return createSupabaseAdmin(url, key, { +export function createAdminClient() { + const url = process.env.NEXT_PUBLIC_SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!url || !key) { + throw new Error('Supabase admin environment variables are missing!'); + } + return createSupabaseAdmin(url, key, { auth: { autoRefreshToken: false, persistSession: false, }, }); }🤖 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/supabase/server.ts` around lines 5 - 12, Update createAdminClient to read and validate the required Supabase environment variables before creating the admin client, using the existing configuration conventions. Replace the direct `@supabase/supabase-js` admin-client construction in both registrasi_agen and registrasi_perusahaan routes with this helper, removing their duplicated environment reads and client setup.
25-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMigrate
lib/supabase/server.tstogetAll/setAll.
@supabase/ssr@0.12.4deprecatesget,set, andremove. The deprecated adapter can miss session-cookie chunks. Matchmiddleware.ts, then remove the unusedCookieOptionsimport.🤖 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/supabase/server.ts` around lines 25 - 44, Update the Supabase server client cookie adapter to use getAll and setAll, matching the implementation in middleware.ts so all session-cookie chunks are handled; translate the returned cookies and preserve the existing set options during writes. Remove the now-unused CookieOptions import and delete the deprecated get, set, and remove methods.app/api/registrasi_perusahaan/route.ts (1)
5-92: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftExtract the shared registration flow.
This handler and
app/api/registrasi_agen/route.tsdiffer only in the role string, the table name, and the column mapping. The parsing, validation, sign-up, admin client creation, rollback, and error handling are identical. Every fix must currently land twice, as the duplicate comment above shows. Extract a helper such asregisterPartner({ role, table, mapProfile })inlib/and let both routes call it.🤖 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/registrasi_perusahaan/route.ts` around lines 5 - 92, Extract the duplicated registration workflow from POST into a shared lib helper such as registerPartner, parameterized by role, profile table, and mapProfile. Move shared parsing, validation, Supabase sign-up, admin-client creation, profile insertion, rollback, and error handling into the helper, then update both company and agent POST handlers to call it with their route-specific configuration while preserving existing responses and mappings.app/api/registrasi_agen/route.ts (1)
16-24: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winValidate the field formats, not only their presence.
The check only rejects falsy values. A malformed
password, or a non-numericnikNibreaches Supabase and theagentable unchecked. Add format and length validation. A schema validator such aszodkeeps this route andapp/api/registrasi_perusahaan/route.tsconsistent.The message on Line 21 also contains a typo: "mengsisi" should be "mengisi".
🤖 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/registrasi_agen/route.ts` around lines 16 - 24, Extend the validation in the registration handler around the body fields to validate email format, enforce the required password length, and require nikNib to be numeric, preferably using the same Zod schema approach as registrasi_perusahaan; retain the existing required-field checks and reject invalid input before Supabase is called. Correct the validation error message from “mengsisi” to “mengisi”.app/api/admin/verifikasi/route.ts (2)
88-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
messageis computed and never used.Line 89 assigns
message, but Line 90 logserrand Line 92 returns a fixed string. Remove the assignment or log it.♻️ Proposed fix
- } catch (err: unknown) { // Perbaikan tipe 'any' ke 'unknown' sesuai saran CodeRabbit - const message = err instanceof Error ? err.message : 'Unknown server error'; - console.error("Error di verifikasi:", err); // Log di server + } catch (err: unknown) { + const message = err instanceof Error ? err.message : 'Unknown server error'; + console.error('Error di verifikasi:', message); return NextResponse.json(🤖 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/admin/verifikasi/route.ts` around lines 88 - 94, Remove the unused message assignment from the catch block in the verification route, or use it in the server-side console.error call; keep the public response as the existing fixed error message without exposing err.message.
65-68: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winValidate
SUPABASE_SERVICE_ROLE_KEYbefore you construct the admin client.The non-null assertions hide a missing variable. If
SUPABASE_SERVICE_ROLE_KEYis unset, the client is created withundefinedand fails later with an unclear error.createClientinlib/supabase/server.ts(Lines 15-17) already validates its variables. Add the same check for the service-role key.🤖 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/admin/verifikasi/route.ts` around lines 65 - 68, Validate SUPABASE_SERVICE_ROLE_KEY before calling createAdminClient in the admin client setup, using the existing validation pattern from createClient in lib/supabase/server.ts. Remove reliance on the non-null assertion for this key and fail immediately with the established missing-variable error behavior.
🤖 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 `@app/api/admin/calon-mitra/route.ts`:
- Around line 4-50: Replace the caller-profile logic in GET with an admin
authorization gate matching app/api/admin/verifikasi/route.ts, checking
app_metadata.role === 'admin' and returning 401/403 consistently for
unauthorized callers. After authorization, use the service-role Supabase client
to query and return the pending calon mitra list rather than selecting a single
row from agen or perusahaan_industri by the caller’s auth_id; preserve
appropriate error handling and response status behavior.
- Around line 21-43: Update both profile queries in the role branches of the
admin calon-mitra handler to use maybeSingle() instead of single(). After each
query, preserve error throwing for actual query failures, but return a 404
response when data is null before assigning profileData.
In `@app/api/admin/verifikasi/route.ts`:
- Around line 79-86: Update the Supabase update chain in the admin verification
route to request the affected row count, then return HTTP 404 when zero rows
match id_target; keep the existing success response only when an update occurs,
while preserving error propagation for Supabase errors.
- Around line 8-33: Update the shared Supabase server helper to use the
getAll/setAll cookie adapter, then replace the inline createServerClient setup
in the route with await createClient(). Remove the duplicated identity comment
and the now-unnecessary inline cookie/client configuration.
In `@app/api/auth/login/route.ts`:
- Around line 33-44: Update the error handling in the login route so only
recognized Supabase credential errors return the 401 invalid-credentials
response; preserve the existing 429 handling. Return an appropriate server-error
response for other failures such as outages, network errors, or configuration
issues, using the error details or established error classification available in
the surrounding login flow.
In `@app/api/legal/e-contract/route.ts`:
- Around line 39-43: Update the catch block in the E-Contract route to use
unknown for err and return a generic client-safe error message instead of
concatenating err.message. Follow the established handling pattern in the admin
verification route, keeping the 500 response status while preventing raw
Supabase or schema details from reaching the response.
- Around line 24-37: Update the Supabase update in the e-contract route to
return the affected agen row, then treat an absent returned row as an error
instead of returning the success response. Keep the existing success payload
unchanged when a matching user.id record is updated, and ensure the no-row case
cannot report legal approval.
- Around line 16-18: Provision agent roles server-side into app_metadata.role,
migrate existing users from user_metadata.role, and update the role checks in
the e-contract route and profil/me route to read user.app_metadata.role instead
of user.user_metadata.role. Ensure registration flows use the admin provisioning
path so legitimate agents retain access.
In `@app/api/profil/me/route.ts`:
- Around line 4-50: Extract the shared role-based profile lookup from GET and
the corresponding admin route into a reusable helper, such as lib/profil.ts.
Centralize the agen and perusahaan role-to-table mapping, auth_id filtering, and
single-record lookup there, then update both routes to call the helper while
preserving their existing authentication, invalid-role, and response behavior.
- Around line 52-57: Update the catch block in the profile route to use unknown
instead of any and return a generic server-error message without exposing
err.message. Follow the safe error-handling pattern already used by the admin
calon-mitra route while preserving the 500 status.
In `@app/api/registrasi_agen/route.ts`:
- Around line 50-53: Update the handler’s initial setup to validate
NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY before signUp and before
calling createAdminClient. Reuse or add the shared validation in the server
helper exposed by lib/supabase/server.ts, and return an HTTP 500 response
immediately when either variable is missing so no Auth user is created with
invalid configuration.
- Around line 69-77: Update the rollback handling in the registration route
around the profileError branch to capture the result of
supabaseAdmin.auth.admin.deleteUser(userId), log any deletion failure for
operator reconciliation, and return a generic registration-failure message
instead of exposing profileError.message. Apply the same changes to the
corresponding rollback block in the company registration route.
- Around line 29-37: Move registration role assignment from signup options user
metadata to app metadata. In both registration routes, call
supabaseAdmin.auth.admin.updateUserById after signup, delete the newly created
user if that update fails, and only then insert the profile. Update role reads
in the login, profile, calon-mitra, and e-contract route handlers to use
user.app_metadata?.role consistently.
In `@app/api/registrasi_perusahaan/route.ts`:
- Around line 18-23: Update the validation response in the registration route’s
required-field check to mention every field enforced by the condition, including
alamatKantor and noTelepon, while preserving the existing 400 response behavior.
In `@middleware.ts`:
- Line 30: Update the middleware authentication flow around
supabase.auth.getUser to retain and handle its error separately from a genuinely
missing user session, avoiding an automatic /login redirect when the auth
request fails or times out; also add an appropriate short-lived cache or bounded
retry strategy for this per-request network call.
- Around line 74-78: Update the matcher in config so the api exclusion matches
only the /api segment, requiring a following slash or end-of-path boundary; keep
unrelated paths such as /apidocs and /apiary eligible for middleware processing.
- Around line 61-69: Update the dashboard authorization logic around the
existing role-based redirects so access is allowlisted: permit
`/dashboard/admin` only for the recognized admin role, `/dashboard/agen` only
for `agen`, and `/dashboard/perusahaan` only for `perusahaan`; redirect
undefined, null, or unrecognized roles to a safe non-admin destination. Preserve
the existing valid-role routing while ensuring no authenticated user can reach
an unauthorized dashboard.
- Around line 32-33: Update the registration flows to assign roles through the
server-side admin API or a trusted database trigger so the resulting users have
app_metadata.role instead of options.data.role in user_metadata. Then update
every authorization role reader, including middleware Rules B–D and both
registration paths, to consistently read app_metadata.role and never use
user_metadata for authorization.
---
Nitpick comments:
In `@app/api/admin/verifikasi/route.ts`:
- Around line 88-94: Remove the unused message assignment from the catch block
in the verification route, or use it in the server-side console.error call; keep
the public response as the existing fixed error message without exposing
err.message.
- Around line 65-68: Validate SUPABASE_SERVICE_ROLE_KEY before calling
createAdminClient in the admin client setup, using the existing validation
pattern from createClient in lib/supabase/server.ts. Remove reliance on the
non-null assertion for this key and fail immediately with the established
missing-variable error behavior.
In `@app/api/registrasi_agen/route.ts`:
- Around line 16-24: Extend the validation in the registration handler around
the body fields to validate email format, enforce the required password length,
and require nikNib to be numeric, preferably using the same Zod schema approach
as registrasi_perusahaan; retain the existing required-field checks and reject
invalid input before Supabase is called. Correct the validation error message
from “mengsisi” to “mengisi”.
In `@app/api/registrasi_perusahaan/route.ts`:
- Around line 5-92: Extract the duplicated registration workflow from POST into
a shared lib helper such as registerPartner, parameterized by role, profile
table, and mapProfile. Move shared parsing, validation, Supabase sign-up,
admin-client creation, profile insertion, rollback, and error handling into the
helper, then update both company and agent POST handlers to call it with their
route-specific configuration while preserving existing responses and mappings.
In `@lib/supabase/server.ts`:
- Around line 5-12: Update createAdminClient to read and validate the required
Supabase environment variables before creating the admin client, using the
existing configuration conventions. Replace the direct `@supabase/supabase-js`
admin-client construction in both registrasi_agen and registrasi_perusahaan
routes with this helper, removing their duplicated environment reads and client
setup.
- Around line 25-44: Update the Supabase server client cookie adapter to use
getAll and setAll, matching the implementation in middleware.ts so all
session-cookie chunks are handled; translate the returned cookies and preserve
the existing set options during writes. Remove the now-unused CookieOptions
import and delete the deprecated get, set, and remove methods.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b440c690-9b11-4a82-9235-ad0fb85a83bc
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (12)
app/api/admin/calon-mitra/route.tsapp/api/admin/verifikasi/route.tsapp/api/auth/login/route.tsapp/api/legal/e-contract/route.tsapp/api/profil/me/route.tsapp/api/registrasi_agen/route.tsapp/api/registrasi_perusahaan/route.tsapp/api/test-koneksi/route.tslib/supabase.tslib/supabase/server.tsmiddleware.tspackage.json
💤 Files with no reviewable changes (2)
- lib/supabase.ts
- app/api/test-koneksi/route.ts
| if (role === 'agen') { | ||
| const { data, error } = await supabase | ||
| .from('agen') | ||
| .select('*') | ||
| .eq('auth_id', user.id) | ||
| .single(); | ||
|
|
||
| if (error) throw error; | ||
| profileData = data; | ||
|
|
||
| } else if (role === 'perusahaan') { | ||
| const { data, error } = await supabase | ||
| .from('perusahaan_industri') | ||
| .select('*') | ||
| .eq('auth_id', user.id) | ||
| .single(); | ||
|
|
||
| if (error) throw error; | ||
| profileData = data; | ||
|
|
||
| } else { | ||
| return NextResponse.json({ error: 'Role pengguna tidak valid' }, { status: 400 }); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Supabase PostgREST maybeSingle vs single PGRST116 zero rows error
💡 Result:
In Supabase and PostgREST, the difference between .single() and .maybeSingle() lies in how they handle queries that do not return exactly one row [1][2][3]. The PGRST116 error is a 406 Not Acceptable status code returned by PostgREST when a singular response is requested (via the Accept header), but the database returns either zero rows or multiple rows [2][4][5]. Comparison of Methods:.single - Expectation: Exactly one row must be returned [1][6]. - Behavior on 0 rows: Throws a PGRST116 error [1][2][7]. - Behavior on 1 row: Returns the data object [1][6]. - Behavior on 2+ rows: Throws a PGRST116 error [1][2]. - Use Case: Use only when you are certain the record exists and its absence would indicate a failure in application logic or data integrity [1][3][7]..maybeSingle - Expectation: Zero or one row is acceptable [1][8][3]. - Behavior on 0 rows: Returns null (no error) [1][2][7]. - Behavior on 1 row: Returns the data object [1][6]. - Behavior on 2+ rows: Throws a PGRST116 error (because it still enforces a maximum of one row) [1][2][3]. - Use Case: Default choice for lookups where the record might not exist, such as checking if a user or resource exists [1][3][7]. In summary, if you are encountering a PGRST116 error when a query returns zero rows, you should switch from.single to.maybeSingle to gracefully handle the empty result as a null value rather than an error [1][2][3]. [1][8][2][3][7][5]
Citations:
- 1: https://github.com/bdougie/contributor.info/blob/main/docs/debugging/supabase-single-vs-maybesingle.md
- 2: https://supabase.com/docs/reference/swift/maybesingle
- 3: https://github.com/bdougie/contributor.info/blob/main/docs/technical/supabase-query-patterns.md
- 4: https://docs.postgrest.org/en/latest/references/api/resource_representation.html
- 5: https://docs.postgrest.org/en/stable/references/errors.html
- 6: https://github.com/supabase/postgrest-js/blob/bcd4f43/src/lib/PostgrestTransformBuilder.ts
- 7: https://www.hemantasundaray.com/blog/supabase-single-maybesingle
- 8: https://supabase.com/docs/reference/javascript/using-modifiers-maybesingle
🏁 Script executed:
#!/bin/bash
set -e
file="app/api/admin/calon-mitra/route.ts"
printf '%s\n' "=== target file ==="
cat -n "$file"
printf '%s\n' "=== related route patterns ==="
rg -n --glob '*.ts' --glob '*.tsx' "maybeSingle\(\)|\.single\(\)|PGRST116|Role pengguna tidak valid|calon-mitra" appRepository: migthyhbb/it_techno-project
Length of output: 3248
🏁 Script executed:
#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
p = Path("app/api/admin/calon-mitra/route.ts")
s = p.read_text()
checks = {
"agen uses single": ".from('agen')" in s and ".single();" in s[s.index(".from('agen')"):s.index("} else if")],
"perusahaan uses single": ".from('perusahaan_industri')" in s and ".single();" in s[s.index(".from('perusahaan_industri')"):s.index("} else {")],
"query errors are thrown": s.count("if (error) throw error;") == 2,
"catch returns 500": "{ status: 500 }" in s,
"missing-profile 404 exists": "{ status: 404 }" in s,
}
for name, value in checks.items():
print(f"{name}: {value}")
PYRepository: migthyhbb/it_techno-project
Length of output: 305
Return HTTP 404 when the profile is missing.
Replace .single() with .maybeSingle(), then return HTTP 404 when data is null; otherwise, the thrown PGRST116 error reaches the catch block and returns HTTP 500.
🤖 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/admin/calon-mitra/route.ts` around lines 21 - 43, Update both profile
queries in the role branches of the admin calon-mitra handler to use
maybeSingle() instead of single(). After each query, preserve error throwing for
actual query failures, but return a 404 response when data is null before
assigning profileData.
| // 1. CEK IDENTITAS (SATPAM) | ||
| // 1. CEK IDENTITAS (SATPAM) | ||
| const cookieStore = await cookies(); // <-- Wajib tambah 'await' di sini untuk Next.js 15 | ||
|
|
||
| const supabaseUser = createServerClient( | ||
| process.env.NEXT_PUBLIC_SUPABASE_URL!, | ||
| process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, | ||
| { | ||
| cookies: { | ||
| getAll() { | ||
| return cookieStore.getAll() | ||
| }, | ||
| setAll(cookiesToSet) { | ||
| try { | ||
| cookiesToSet.forEach(({ name, value, options }) => { | ||
| // Format penulisan .set yang benar (dipisah koma, bukan di dalam kurung kurawal) | ||
| cookieStore.set(name, value, options) | ||
| }) | ||
| } catch (error) { | ||
| // Kalau Next.js protes "readonly", biarkan saja (diabaikan). | ||
| // Karena Middleware kita yang sebenarnya bertugas merestart/mengupdate cookie-nya. | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
@supabase/ssr createServerClient get set remove deprecated getAll setAll
💡 Result:
In the @supabase/ssr library, the cookie methods get, set, and remove are deprecated [1][2]. They have been replaced by the getAll and setAll methods [3][4]. The library has transitioned to getAll and setAll to provide better support for cookie chunk management, correct handling of cookie encoding, and improved developer ergonomics—particularly when managing headers and session state across different server-side frameworks [4]. Key details regarding this change include: 1. Deprecation Status: While get, set, and remove may continue to work for some time, they are explicitly deprecated [1][3]. The library's internal documentation warns that they fail to cover important edge cases and are not recommended [1][2]. These methods will be removed in a future major version [1][2]. 2. Recommended Implementation: You should now implement the cookies configuration using getAll and setAll [3][5]: - getAll: Must return an array of all available cookies [3]. - setAll(cookiesToSet, headers): Must apply the provided cookies and, when applicable, the accompanying headers to the response to prevent caching issues and ensure session integrity [5][6][7]. 3. Risks of Improper Implementation: Failing to implement these methods correctly, or omitting them entirely, can lead to significant authentication issues, such as random logouts, session termination, or JSON parsing errors [2]. Official Supabase documentation now centers its examples around the getAll and setAll pattern across various frameworks like Next.js and Astro [5][6][8].
Citations:
- 1: https://github.com/supabase/ssr/blob/main/src/createServerClient.ts
- 2: https://github.com/supabase/ssr/blob/a1b60ba8/src/createServerClient.ts
- 3: https://supabase.com/changelog/27037-supabase-ssr-updates-and-roadmap-towards-v1-0-0
- 4: feat: full rewrite using
getAllandsetAllcookie methods supabase/ssr#1 - 5: https://supabase.com/docs/guides/auth/server-side/creating-a-client
- 6: https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=server-component
- 7: https://supabase.com/docs/guides/auth/server-side/creating-a-client?framework=nextjs&queryGroups=framework
- 8: https://github.com/supabase/supabase/blob/master/examples/prompts/nextjs-supabase-auth.md
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(app/api/admin/verifikasi/route\.ts|lib/supabase/server\.ts)$'
printf '%s\n' '--- server helper outline ---'
ast-grep outline lib/supabase/server.ts 2>/dev/null || true
printf '%s\n' '--- route excerpt ---'
cat -n app/api/admin/verifikasi/route.ts | sed -n '1,90p'
printf '%s\n' '--- helper excerpt ---'
cat -n lib/supabase/server.ts | sed -n '1,100p'
printf '%s\n' '--- helper usages ---'
rg -n 'createClient|createAdminClient' app lib --glob '*.ts' --glob '*.tsx' | head -200Repository: migthyhbb/it_techno-project
Length of output: 7787
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- package metadata ---'
fd -i -t f 'package.json|pnpm-lock.yaml|package-lock.json|yarn.lock|bun.lockb|bun.lock' . | sort
for f in $(fd -i -t f 'package.json' . | head -20); do
printf '%s\n' "--- $f ---"
cat "$f" | rg -n '"`@supabase/ssr`"|"next"|"typescript"'
done
printf '%s\n' '--- all server helper call sites ---'
for f in $(rg -l "from ['\"]`@/lib/supabase/server`['\"]" app lib --glob '*.ts' --glob '*.tsx'); do
printf '%s\n' "--- $f ---"
cat -n "$f" | sed -n '1,80p'
done
printf '%s\n' '--- cookie API usage ---'
rg -n 'createServerClient|getAll|setAll|cookieStore\.(get|set|delete)|cookies\.(get|set|delete)' app lib --glob '*.ts' --glob '*.tsx'Repository: migthyhbb/it_techno-project
Length of output: 19353
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
import json
from pathlib import Path
route = Path("app/api/admin/verifikasi/route.ts").read_text()
helper = Path("lib/supabase/server.ts").read_text()
pkg = json.loads(Path("package.json").read_text())
lock = json.loads(Path("package-lock.json").read_text())
print("route_inline_createServerClient:", route.count("createServerClient(") == 1)
print("route_uses_getAll_setAll:", "getAll()" in route and "setAll(cookiesToSet)" in route)
print("helper_exports_async_createClient:", "export async function createClient()" in helper)
print("helper_uses_legacy_cookie_methods:",
"get(name: string)" in helper and
"set(name: string" in helper and
"remove(name: string" in helper)
print("helper_uses_getAll_setAll:", "getAll()" in helper and "setAll(" in helper)
print("declared_supabase_ssr:", pkg["dependencies"].get("`@supabase/ssr`"))
packages = lock.get("packages", {})
print("locked_supabase_ssr:", packages.get("node_modules/@supabase/ssr", {}).get("version"))
print("shared_helper_call_sites:",
sum("from '`@/lib/supabase/server`'" in p.read_text()
for p in Path("app").rglob("*.ts")))
PYRepository: migthyhbb/it_techno-project
Length of output: 434
Migrate the shared helper before reusing it
Update lib/supabase/server.ts to use getAll/setAll, then replace this inline client with await createClient(). Remove the duplicate comments on lines 8–9.
🤖 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/admin/verifikasi/route.ts` around lines 8 - 33, Update the shared
Supabase server helper to use the getAll/setAll cookie adapter, then replace the
inline createServerClient setup in the route with await createClient(). Remove
the duplicated identity comment and the now-unnecessary inline cookie/client
configuration.
| if (!email || !password || !namaPt || !npwp || !alamatKantor || !noTelepon) { | ||
| return NextResponse.json( | ||
| { error: 'Email, password, nama PT, dan NPWP wajib diisi!' }, | ||
| { status: 400 } | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
The error message does not list all required fields.
The condition also requires alamatKantor and noTelepon. A client that omits the address receives a message that names only email, password, nama PT, and NPWP. Name every required field, or report the specific missing field.
🐛 Proposed fix
if (!email || !password || !namaPt || !npwp || !alamatKantor || !noTelepon) {
return NextResponse.json(
- { error: 'Email, password, nama PT, dan NPWP wajib diisi!' },
+ { error: 'Email, password, nama PT, NPWP, alamat kantor, dan no telepon wajib diisi!' },
{ status: 400 }
);
}📝 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.
| if (!email || !password || !namaPt || !npwp || !alamatKantor || !noTelepon) { | |
| return NextResponse.json( | |
| { error: 'Email, password, nama PT, dan NPWP wajib diisi!' }, | |
| { status: 400 } | |
| ); | |
| } | |
| if (!email || !password || !namaPt || !npwp || !alamatKantor || !noTelepon) { | |
| return NextResponse.json( | |
| { error: 'Email, password, nama PT, NPWP, alamat kantor, dan no telepon wajib diisi!' }, | |
| { status: 400 } | |
| ); | |
| } |
🤖 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/registrasi_perusahaan/route.ts` around lines 18 - 23, Update the
validation response in the registration route’s required-field check to mention
every field enforced by the condition, including alamatKantor and noTelepon,
while preserving the existing 400 response behavior.
| } | ||
| ) | ||
|
|
||
| const { data: { user } } = await supabase.auth.getUser() |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Handle the getUser failure and note the per-request cost.
Line 30 discards the error value. getUser performs a network call to the Supabase Auth server on every matched request. If that call fails or times out, user is undefined, and the middleware treats an authenticated visitor as a signed-out visitor. The visitor is then redirected to /login and loses the page they requested.
Destructure error and distinguish an auth failure from a missing session. A short cache or a retry budget also limits the load, because this call runs on every page navigation and every prefetch.
🤖 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 `@middleware.ts` at line 30, Update the middleware authentication flow around
supabase.auth.getUser to retain and handle its error separately from a genuinely
missing user session, avoiding an automatic /login redirect when the auth
request fails or times out; also add an appropriate short-lived cache or bounded
retry strategy for this per-request network call.
| // PERBAIKAN: Gunakan app_metadata untuk keamanan tingkat tinggi, bukan user_metadata | ||
| const role = user?.app_metadata?.role; |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find every writer and reader of the role claim.
echo "=== app_metadata writers/readers ==="
rg -nP --type=ts -C3 'app_metadata'
echo "=== user_metadata writers/readers ==="
rg -nP --type=ts -C3 'user_metadata'
echo "=== signUp / updateUserById calls ==="
rg -nP --type=ts -C6 '\b(signUp|updateUserById|admin\.createUser)\s*\('
echo "=== SQL migrations or triggers touching raw_app_meta_data ==="
rg -nP -C3 'raw_app_meta_data|raw_user_meta_data' --glob '*.sql'Repository: migthyhbb/it_techno-project
Length of output: 202
🏁 Script executed:
#!/bin/bash
set +e
printf '%s\n' '=== tracked candidate files ==='
git ls-files | rg '(^|/)(middleware\.ts|route\.ts|.*\.(sql|ts))$' | rg '(middleware\.ts|registrasi_agen|registrasi_perusahaan|supabase|migration|\.sql$)' | head -200
printf '%s\n' '=== middleware.ts ==='
if [ -f middleware.ts ]; then
cat -n middleware.ts
else
find . -name middleware.ts -print
fi
printf '%s\n' '=== registration routes ==='
for f in app/api/registrasi_agen/route.ts app/api/registrasi_perusahaan/route.ts; do
if [ -f "$f" ]; then
printf '\n--- %s ---\n' "$f"
cat -n "$f"
fi
done
printf '%s\n' '=== metadata and role references ==='
rg -n -C4 --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' --glob '*.sql' \
'(app_metadata|user_metadata|raw_app_meta_data|raw_user_meta_data|\brole\b|signUp|updateUserById|createUser)' . \
| head -500Repository: migthyhbb/it_techno-project
Length of output: 21037
🌐 Web query:
Supabase JavaScript auth.signUp options data user_metadata app_metadata admin updateUserById official documentation
💡 Result:
In Supabase, user_metadata and app_metadata serve different purposes and are managed using different methods [1][2]. auth.signUp (Client-Side) When using supabase.auth.signUp on the client side, you can pass user-specific metadata via the options object [3][4][5]. const { data, error } = await supabase.auth.signUp({ email: 'example@email.com', password: 'example-password', options: { data: { first_name: 'John', age: 27, }, }, }) The data object inside options maps to the user_metadata column (raw_user_meta_data in the database) [3][5][2]. Note that client-side sign-up cannot directly set app_metadata, as that is reserved for administrative or server-side control [2]. admin.updateUserById (Server-Side) To update user_metadata or app_metadata for an existing user, you must use the admin.updateUserById method, which requires a service role key and should only be executed in a secure server-side environment (such as Edge Functions or a backend API) [6][7][2]. // Server-side (Edge Function) const { data, error } = await supabase.auth.admin.updateUserById( 'user-uuid', { user_metadata: { preferences: { theme: 'dark' } }, app_metadata: { plan: 'pro' } }) Key Differences user_metadata (raw_user_meta_data): Intended for user-specific information like names, preferences, or profile details [1][2]. app_metadata (app_metadata): Intended for application-specific information such as roles, access levels, or external identity provider details [1][2]. Only users with administrative privileges (using the service_role key) should modify this [6][2]. After updating these values via the admin API, client-side sessions may need to be refreshed using supabase.auth.refreshSession to reflect the changes in the user object retrieved by the client [6][7].
Citations:
- 1: https://supabase.com/docs/guides/auth/jwt-fields
- 2: https://supabase.github.io/auth-js/v2/interfaces/AdminUserAttributes.html
- 3: https://supabase.com/docs/guides/auth/managing-user-data?language=js&queryGroups=language
- 4: https://supabase.com/docs/reference/javascript/auth-signup
- 5: https://supabase.com/docs/guides/auth/managing-user-data
- 6: https://supabase.com/docs/reference/javascript/auth-admin-updateuserbyid?example=updates-a-users-app-metadata
- 7: https://supabase.com/docs/reference/javascript/auth-admin-updateuserbyid
🏁 Script executed:
#!/bin/bash
set +e
printf '%s\n' '=== dashboard and authorization files ==='
git ls-files | rg '(^|/)(dashboard|admin|agen|perusahaan|auth|middleware|.*\.sql$)' | head -300
printf '%s\n' '=== role and auth checks in dashboard/admin code ==='
rg -n -C4 --glob '*.ts' --glob '*.tsx' \
'(getUser|getSession|app_metadata|user_metadata|role|redirect|forbidden|Unauthorized|admin)' \
app 'middleware.ts' 2>/dev/null | head -800Repository: migthyhbb/it_techno-project
Length of output: 13277
Store roles in app_metadata and use it consistently for authorization. Both registration routes write options.data.role to user_metadata, so registered users have no app_metadata.role and middleware Rules B–D do not run. Set the claim with the server-side admin API or a trusted database trigger. Update the other role readers to use app_metadata; do not use user_metadata for authorization.
🤖 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 `@middleware.ts` around lines 32 - 33, Update the registration flows to assign
roles through the server-side admin API or a trusted database trigger so the
resulting users have app_metadata.role instead of options.data.role in
user_metadata. Then update every authorization role reader, including middleware
Rules B–D and both registration paths, to consistently read app_metadata.role
and never use user_metadata for authorization.
Summary by CodeRabbit
New Features
Bug Fixes
Chores