Skip to content

Membuat api - #3

Closed
migthyhbb wants to merge 3 commits into
mainfrom
membuat-api
Closed

Membuat api#3
migthyhbb wants to merge 3 commits into
mainfrom
membuat-api

Conversation

@migthyhbb

@migthyhbb migthyhbb commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added secure login with role-aware authentication and error handling.
    • Added agent and company registration with validation and profile creation.
    • Added profile retrieval for authenticated users and administrators.
    • Added admin verification updates and digital contract approval for authorized agents.
    • Added role-based dashboard access and automatic login redirects.
  • Bug Fixes

    • Improved handling of unauthorized access, invalid requests, rate limits, and server errors.
    • Prevented incomplete registrations when profile creation fails.
  • Chores

    • Removed the connectivity test endpoint.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@migthyhbb, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 9 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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

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

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 479ef139-d3d4-4319-9ccc-0cc7c91cc499

📥 Commits

Reviewing files that changed from the base of the PR and between d5fac69 and 3a1abae.

📒 Files selected for processing (4)
  • app/api/admin/calon-mitra/route.ts
  • app/api/admin/verifikasi/route.ts
  • app/api/auth/login/route.ts
  • middleware.ts
📝 Walkthrough

Walkthrough

The 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.

Changes

Supabase authentication and partner workflow

Layer / File(s) Summary
Supabase SSR foundation
lib/supabase/server.ts, package.json
Adds server and admin Supabase clients with cookie handling, environment validation, and updated Supabase dependencies.
Login and partner registration
app/api/auth/login/route.ts, app/api/registrasi_agen/route.ts, app/api/registrasi_perusahaan/route.ts
Adds credential login and agent or company registration. Registration persists pending profiles and removes Auth users when profile creation fails.
Protected profile and approval APIs
app/api/profil/me/route.ts, app/api/admin/calon-mitra/route.ts, app/api/admin/verifikasi/route.ts, app/api/legal/e-contract/route.ts
Adds role-based profile retrieval, admin verification updates, and agent E-Contract approval updates.
Authentication and role routing
middleware.ts
Adds Supabase cookie refresh, login and dashboard redirects, role-based dashboard restrictions, and route matching exclusions.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the API changes but is too generic to identify the primary change. Use a specific title that summarizes the main API changes, such as "Add authentication and registration API routes".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch membuat-api

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 18

🧹 Nitpick comments (6)
lib/supabase/server.ts (2)

5-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate admin client creation on this helper.

app/api/registrasi_agen/route.ts (Lines 3, 50-53) and app/api/registrasi_perusahaan/route.ts (Lines 3, 49-52) import createClient from @supabase/supabase-js directly and rebuild the same admin client. This helper appears unused. Move the environment reads and validation into createAdminClient, 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 win

Migrate lib/supabase/server.ts to getAll/setAll.

@supabase/ssr@0.12.4 deprecates get, set, and remove. The deprecated adapter can miss session-cookie chunks. Match middleware.ts, then remove the unused CookieOptions import.

🤖 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 lift

Extract the shared registration flow.

This handler and app/api/registrasi_agen/route.ts differ 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 as registerPartner({ role, table, mapProfile }) in lib/ 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 win

Validate the field formats, not only their presence.

The check only rejects falsy values. A malformed email, a short password, or a non-numeric nikNib reaches Supabase and the agen table unchecked. Add format and length validation. A schema validator such as zod keeps this route and app/api/registrasi_perusahaan/route.ts consistent.

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

message is computed and never used.

Line 89 assigns message, but Line 90 logs err and 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 win

Validate SUPABASE_SERVICE_ROLE_KEY before you construct the admin client.

The non-null assertions hide a missing variable. If SUPABASE_SERVICE_ROLE_KEY is unset, the client is created with undefined and fails later with an unclear error. createClient in lib/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

📥 Commits

Reviewing files that changed from the base of the PR and between 45c84a3 and d5fac69.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • app/api/admin/calon-mitra/route.ts
  • app/api/admin/verifikasi/route.ts
  • app/api/auth/login/route.ts
  • app/api/legal/e-contract/route.ts
  • app/api/profil/me/route.ts
  • app/api/registrasi_agen/route.ts
  • app/api/registrasi_perusahaan/route.ts
  • app/api/test-koneksi/route.ts
  • lib/supabase.ts
  • lib/supabase/server.ts
  • middleware.ts
  • package.json
💤 Files with no reviewable changes (2)
  • lib/supabase.ts
  • app/api/test-koneksi/route.ts

Comment thread app/api/admin/calon-mitra/route.ts Outdated
Comment thread app/api/admin/calon-mitra/route.ts Outdated
Comment on lines +21 to +43
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 });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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:


🏁 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" app

Repository: 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}")
PY

Repository: 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.

Comment on lines +8 to +33
// 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.
}
}
}
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 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:


🏁 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 -200

Repository: 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")))
PY

Repository: 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.

Comment thread app/api/admin/verifikasi/route.ts Outdated
Comment thread app/api/auth/login/route.ts Outdated
Comment on lines +18 to +23
if (!email || !password || !namaPt || !npwp || !alamatKantor || !noTelepon) {
return NextResponse.json(
{ error: 'Email, password, nama PT, dan NPWP wajib diisi!' },
{ status: 400 }
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
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.

Comment thread middleware.ts
}
)

const { data: { user } } = await supabase.auth.getUser()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 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.

Comment thread middleware.ts
Comment on lines +32 to +33
// PERBAIKAN: Gunakan app_metadata untuk keamanan tingkat tinggi, bukan user_metadata
const role = user?.app_metadata?.role;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 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 -500

Repository: 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:


🏁 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 -800

Repository: 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.

Comment thread middleware.ts Outdated
Comment thread middleware.ts
@migthyhbb migthyhbb closed this Aug 7, 2026
This was referenced Aug 7, 2026
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