Skip to content

fix admin credit grant flow and stats#1621

Open
riderx wants to merge 17 commits intomainfrom
riderx/fix-credit-admin-panel
Open

fix admin credit grant flow and stats#1621
riderx wants to merge 17 commits intomainfrom
riderx/fix-credit-admin-panel

Conversation

@riderx
Copy link
Member

@riderx riderx commented Feb 11, 2026

Summary (AI generated)

  • Fix admin credit grant failure (42501) by restoring service_role execute permission on public.top_up_usage_credits(...)
  • Add GET /private/admin_credits/org-stats/:orgId plus CORS preflight handling for admin credits routes
  • Add credit buy/use/grant/expiry and per-metric usage stats UI to admin/dashboard/credits

Motivation (AI generated)

Admin credit grants were failing in production and the admin credits page was missing actionable usage/purchase stats.

Business Impact (AI generated)

Restores manual grant operations for support/admin teams and improves decision-making with immediate credit analytics visibility.

Test Plan (AI generated)

  • bun lint
  • bun lint:backend
  • bunx eslint tests/admin-credits.test.ts
  • bunx vitest run tests/admin-credits.test.ts (local test target returned 404 for the new route during this run)

Screenshots (AI generated)

  • Admin credits page now includes summary cards and per-metric usage stats after selecting an organization

Checklist (AI generated)

  • My code follows the code style of this project and passes
    bun run lint:backend && bun run lint.
  • My change requires a change to the documentation.
  • I have updated the documentation
    accordingly.
  • My change has adequate E2E test coverage.
  • I have tested my code manually, and I have provided steps how to reproduce
    my tests

Generated with AI

Summary by CodeRabbit

  • New Features

    • Admin dashboard: shows organization credit totals and per-metric usage with 30-day values; balances refresh after grants/top-ups.
    • Plans/settings: a credits-only state adjusts CTA and plan highlighting to reflect credit-based accounts.
  • Bug Fixes

    • Restored top-up execution so top-up/grant flows complete reliably.
  • Localization

    • Added credits-only informational text in multiple languages.
  • Tests

    • Added tests for admin stats endpoint access and CORS.

Copilot AI review requested due to automatic review settings February 11, 2026 04:01
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Adds organization-level credit statistics: frontend UI and types, a new admin-only backend endpoint that aggregates credit transactions/consumptions, DB migrations restoring function EXECUTE privileges, RPC permission updates, tests, translations, and minor related backend/util tweaks.

Changes

Cohort / File(s) Summary
Admin Dashboard Frontend
src/pages/admin/dashboard/credits.vue
Adds types (CreditStatsAggregate, UsageMetricStats, OrgCreditStats), state (orgStats, isLoadingStats), metric ordering/labeling, orderedUsageStats computed, loadOrgStats(orgId) and metricLabel(), refreshes stats after grants, and UI blocks to show totals and per-metric 30-day stats with loading states.
Credits CTA & Plans UI
src/components/CreditsCta.vue, src/pages/settings/organization/Plans.vue
Adds creditsOnly prop to CreditsCta and conditional CTA rendering; Plans.vue computes isCreditsOnly and passes :credits-only="isCreditsOnly", and adjusts plan highlighting logic to avoid highlighting for credits-only orgs.
Organization Store Logic
src/stores/organization.ts
Includes can_use_more in currentOrganizationFailed logic so failure detection considers available credit usage.
Backend - Admin Stats Endpoint
supabase/functions/_backend/private/admin_credits.ts
Adds CORS preflight, new interfaces (CreditStatsAggregate, UsageMetricStats, OrgCreditStatsPayload), helpers (createBaseMetricStats, toFiniteNumber, normalizeAggregate/normalizeMetrics), and GET /org-stats/:orgId admin-only route that calls RPC get_admin_org_credit_stats, normalizes results and returns JSON with totals, last_30_days, and usage_by_metric.
Backend - Credits RPC Caller Update
supabase/functions/_backend/private/credits.ts
Switches top_up_usage_credits RPC invocation to use supabaseAdmin (service_role) for elevated execute permission.
Backend Utils
supabase/functions/_backend/utils/cloudflare.ts, supabase/functions/_backend/plugins/stats.ts
Modified device projection to use argMax(blob5, timestamp) (removes non-empty guard) and simplified allow_device_custom_id access to schema.apps.allow_device_custom_id (removed JSON fallback).
Version / Package
supabase/functions/_backend/utils/version.ts, package.json
Bumps exported version constant to 12.110.6 and updates package.json version accordingly.
Database Migrations
supabase/migrations/20260213181740_add_admin_org_credit_stats_function.sql, supabase/migrations/20260214054927_restore_top_up_usage_credits_for_service_role.sql, supabase/migrations/20260211034635_restore_top_up_usage_credits_service_role_execute.sql
Adds new SQL function get_admin_org_credit_stats(p_org_id, p_since) returning aggregated JSON stats and comments/ACLs; restores/grants EXECUTE on public.top_up_usage_credits to service_role via migrations with explanatory comments.
Tests
tests/admin-credits.test.ts
Adds tests for GET /private/admin_credits/org-stats/:orgId (401 missing auth, 400 not_admin), includes CORS OPTIONS tests, and extends not_admin/unauthorized endpoint lists to include org-stats.
Translations
messages/*.json (multiple locales)
Adds three new localization keys across many languages: credits-only-info-description, credits-only-info-link, and credits-only-info-title.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin Client
    participant API as Admin Backend<br/>/private/admin_credits
    participant RPC as DB Function<br/>get_admin_org_credit_stats
    participant DB as Database

    Admin->>API: GET /org-stats/:orgId (admin auth)
    API->>API: verify admin auth & validate orgId
    API->>RPC: call get_admin_org_credit_stats(p_org_id, p_since)
    RPC->>DB: aggregate usage_credit_* tables
    DB-->>RPC: aggregated json
    RPC-->>API: json payload
    API->>API: normalize & format (totals, last_30_days, usage_by_metric)
    API-->>Admin: JSON response
    Admin->>Admin: render dashboard metrics and per-metric 30-day values
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 Hopping through stats with a cheerful twitch,
Totals and metrics in a tidy stitch.
Admins peek, numbers bright,
Credits balanced, all in sight,
Rabbits cheer — hop, count, and switch! 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
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.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix admin credit grant flow and stats' directly and clearly addresses the main changes: restoring the broken admin credit grant functionality and adding credit statistics to the admin dashboard.
Description check ✅ Passed The pull request description includes a summary of changes, motivation, business impact, test plan with checkboxes, and a checklist section. While the test plan notes an incomplete local vitest run (404 error on new route), the description follows the template structure with all major sections present and completed.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch riderx/fix-credit-admin-panel

Warning

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

🔧 SQLFluff (4.0.4)
supabase/migrations/20260214054927_restore_top_up_usage_credits_for_service_role.sql

User Error: No dialect was specified. You must configure a dialect or specify one on the command line using --dialect after the command. Available dialects:
ansi, athena, bigquery, clickhouse, databricks, db2, doris, duckdb, exasol, flink, greenplum, hive, impala, mariadb, materialize, mysql, oracle, postgres, redshift, snowflake, soql, sparksql, sqlite, starrocks, teradata, trino, tsql, vertica

supabase/migrations/20260213181740_add_admin_org_credit_stats_function.sql

User Error: No dialect was specified. You must configure a dialect or specify one on the command line using --dialect after the command. Available dialects:
ansi, athena, bigquery, clickhouse, databricks, db2, doris, duckdb, exasol, flink, greenplum, hive, impala, mariadb, materialize, mysql, oracle, postgres, redshift, snowflake, soql, sparksql, sqlite, starrocks, teradata, trino, tsql, vertica


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a02b90f83

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 391 to 393
if (transaction.transaction_type === 'purchase') {
const value = Math.max(amount, 0)
totals.purchased += value

Choose a reason for hiding this comment

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

P2 Badge Separate manual grants from purchase totals

This branch classifies every purchase transaction as a paid top-up, but admin grants created by /private/admin_credits/grant are written through top_up_usage_credits and are recorded as purchase transactions as well, so manual support grants will inflate purchased and leave granted at zero. In environments where admins use this grant flow, the new stats card will report incorrect buy vs. grant numbers.

Useful? React with 👍 / 👎.

Comment on lines 347 to 349
.from('usage_credit_transactions')
.select('transaction_type, amount, occurred_at')
.eq('org_id', orgId),

Choose a reason for hiding this comment

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

P2 Badge Avoid loading full credit history in org-stats endpoint

The new org-stats endpoint pulls all rows for an org and then aggregates in application code, which means large orgs with long credit history will transfer and iterate potentially massive datasets on every page load; this can cause slow responses or timeouts for admins. Doing the aggregation in SQL (or at least adding bounded windows for heavy parts) would prevent this scalability regression.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/pages/admin/dashboard/credits.vue`:
- Around line 517-527: The "deduction" card label
(t('credit-transaction-deduction')) is showing consumption values
(orgStats.totals.used / orgStats.last_30_days.used); update the template in
credits.vue to render the transaction deduction fields instead by replacing
orgStats.totals.used and orgStats.last_30_days.used with
orgStats.totals.deducted and orgStats.last_30_days.deducted so the displayed
data matches the label (or, alternatively, change the label to a usage wording
if you prefer the current fields).
🧹 Nitpick comments (4)
supabase/functions/_backend/private/admin_credits.ts (1)

345-354: Unbounded queries may cause performance issues for high-volume orgs.

Both queries fetch all rows from usage_credit_transactions and usage_credit_consumptions for the org with no LIMIT or server-side date filter. For orgs with extensive history, this could return thousands of rows, increasing memory usage and latency.

Consider pushing the 30-day filter to the DB for last_30_days stats (or at minimum adding a reasonable row limit), and computing all-time totals from a materialized/cached source if the data grows large. Since this is admin-only and low-traffic, it's not urgent but worth noting for future scale.

src/pages/admin/dashboard/credits.vue (2)

231-256: Missing toast notification on stats load failure.

loadOrgBalance shows toast.error(...) on failure (Line 224), but loadOrgStats silently swallows the error with only a console.error. For consistency and admin visibility, consider adding a toast here as well.

Proposed fix
   catch (error) {
     console.error('Stats load error:', error)
     orgStats.value = null
+    toast.error(t('admin-credits-stats-error'))
   }

561-563: Reuses admin-credits-no-balance i18n key for missing stats.

This key is semantically intended for missing balance data. Consider using a distinct key (e.g., admin-credits-no-stats) for the stats section to avoid confusion if the strings diverge later.

tests/admin-credits.test.ts (1)

42-42: Consider using it.concurrent() for parallel test execution.

The coding guidelines recommend using it.concurrent() instead of it() for tests within the same file. Most tests in this file are independent and could run concurrently. This applies to both the new and existing tests, so it can be deferred.

As per coding guidelines: "Design tests for parallel execution; use it.concurrent() instead of it() to run tests in parallel within the same file."

Also applies to: 128-128, 140-140, 150-150, 161-161, 205-205, 239-239, 251-251, 275-275, 287-287

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes an admin credit grant production failure by restoring service_role permissions for the top_up_usage_credits(...) RPC, and extends the admin credits dashboard with an org-level stats endpoint plus UI to display credit/usage analytics.

Changes:

  • Add a migration to restore service_role EXECUTE on public.top_up_usage_credits(...).
  • Add GET /private/admin_credits/org-stats/:orgId and CORS preflight handling for admin credits routes.
  • Extend the admin credits UI to fetch and display org credit/usage statistics.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tests/admin-credits.test.ts Adds access-control coverage for the new org-stats route and adds an OPTIONS/CORS preflight test for admin credits endpoints.
supabase/migrations/20260211034635_restore_top_up_usage_credits_service_role_execute.sql Restores service_role execute permission on the credit top-up RPC to fix admin/server grant failures.
supabase/functions/_backend/private/admin_credits.ts Adds a wildcard OPTIONS handler plus a new admin-only org stats endpoint that aggregates transactions and consumptions.
src/pages/admin/dashboard/credits.vue Fetches org stats on org selection / after grants, and renders summary cards + per-metric usage stats.

Comment on lines 391 to 405
if (transaction.transaction_type === 'purchase') {
const value = Math.max(amount, 0)
totals.purchased += value
if (isRecent)
last30Days.purchased += value
continue
}

if (transaction.transaction_type === 'grant' || transaction.transaction_type === 'manual_grant') {
const value = Math.max(amount, 0)
totals.granted += value
if (isRecent)
last30Days.granted += value
continue
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The stats aggregation treats transaction_type === 'purchase' as purchased credits and transaction_type in ('grant','manual_grant') as granted credits, but public.top_up_usage_credits(...) always records credit top-ups as transaction_type = 'purchase' (even for manual/admin grants). This will cause granted to stay 0 and inflate purchased. Consider deriving purchased-vs-granted from usage_credit_grants.source (e.g., stripe_top_up vs manual) by joining usage_credit_transactions to usage_credit_grants (or querying grants directly) instead of relying on transaction_type.

Copilot uses AI. Check for mistakes.
Comment on lines 344 to 353

const [transactionsResult, consumptionsResult] = await Promise.all([
adminSupabase
.from('usage_credit_transactions')
.select('transaction_type, amount, occurred_at')
.eq('org_id', orgId),
adminSupabase
.from('usage_credit_consumptions')
.select('credits_used, metric, applied_at')
.eq('org_id', orgId),
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

This endpoint fetches all usage_credit_transactions and usage_credit_consumptions rows for the org and aggregates them in memory. For orgs with long histories this can become slow and memory-heavy, and it also increases egress from PostgREST. Consider doing aggregation in SQL (SUM/GROUP BY + time-window filtering) and only returning the already-aggregated totals/30-day totals/per-metric stats.

Suggested change
const [transactionsResult, consumptionsResult] = await Promise.all([
adminSupabase
.from('usage_credit_transactions')
.select('transaction_type, amount, occurred_at')
.eq('org_id', orgId),
adminSupabase
.from('usage_credit_consumptions')
.select('credits_used, metric, applied_at')
.eq('org_id', orgId),
const sinceIso = new Date(sinceMs).toISOString()
const [transactionsResult, consumptionsResult] = await Promise.all([
adminSupabase
.from('usage_credit_transactions')
.select('transaction_type, amount, occurred_at')
.eq('org_id', orgId)
.gte('occurred_at', sinceIso),
adminSupabase
.from('usage_credit_consumptions')
.select('credits_used, metric, applied_at')
.eq('org_id', orgId)
.gte('applied_at', sinceIso),

Copilot uses AI. Check for mistakes.
</div>

<div v-else class="mt-2 text-gray-500 dark:text-gray-400">
{{ t('admin-credits-no-balance') }}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

In the stats panel fallback, the UI shows t('admin-credits-no-balance'), which is misleading because this section is about statistics (and balance has its own panel above). Use a dedicated translation key/message for “no stats available” (or reuse a generic “no data” message) so users aren’t confused.

Suggested change
{{ t('admin-credits-no-balance') }}
{{ t('no-data') }}

Copilot uses AI. Check for mistakes.
WcaleNieWolny and others added 15 commits February 11, 2026 14:55
argMaxIf is not supported by Cloudflare Analytics Engine SQL API,
causing the entire device list query to fail silently and return [].
This broke /private/devices for all apps since 5a4828b.
…1627)

* Initial plan

* Fix COALESCE query to handle NULL values when column doesn't exist

The previous query `COALESCE((to_jsonb(apps) ->> 'allow_device_custom_id')::boolean, true)`
would fail when the column doesn't exist because:
1. to_jsonb(apps) ->> 'allow_device_custom_id' returns NULL (as text)
2. NULL::text cast to ::boolean fails with an error
3. COALESCE never gets a chance to provide the default

The fix: `COALESCE(to_jsonb(apps) ->> 'allow_device_custom_id', 'true')::boolean`
1. Provide the default 'true' string BEFORE the boolean cast
2. COALESCE returns either the column value or 'true' as text
3. Cast the resulting text to boolean

Co-authored-by: riderx <4084527+riderx@users.noreply.github.com>

* Fix has_usage_credits COALESCE query with same issue

Applied the same fix to the has_usage_credits query in pg.ts to prevent
potential failures when the column doesn't exist on replicas.

Co-authored-by: riderx <4084527+riderx@users.noreply.github.com>

* Simplify allow_device_custom_id query - remove unnecessary COALESCE

Since the column is NOT NULL DEFAULT true, we can query it directly without
the to_jsonb workaround. Reverted pg.ts change as well since that was based
on the same incorrect assumption.

Co-authored-by: riderx <4084527+riderx@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: riderx <4084527+riderx@users.noreply.github.com>
…ce-role

fix(db): restore service_role EXECUTE on top_up_usage_credits
…top-up endpoint (#1632)

Migration 20260104120000 revoked EXECUTE on top_up_usage_credits from all
roles including authenticated. PR #1630 restored service_role but not
authenticated, breaking the complete-top-up endpoint which called the RPC
via supabaseClient (authenticated role).

Switch to supabaseAdmin (service_role) for the RPC call only. All
authorization is already enforced before the call: JWT auth, RBAC
permission check (org.update_billing / super_admin), Stripe session
ownership + payment verification, and idempotency guards.

This matches how the other two callers (stripe_event.ts webhook and
admin_credits.ts) already invoke the same function.

Closes #1631
* feat(frontend): improve plans page UX for credits-only orgs

- Replace misleading 'Don't want to upgrade?' CTA with info banner for
  orgs using credits without a subscription plan
- Remove blue border highlight on Solo plan for credits-only orgs
- Use information icon instead of currency icon for credits-only banner
- Add new translation keys in all 15 languages via DeepL

* fix(frontend): address CodeRabbit review feedback for credits-only UX

- Fix Indonesian: use 'kredit' instead of 'pulsa' for terminology consistency
- Fix Italian: use informal singular and imperative CTA form
- Fix Russian: use plural 'кредитов' for consistency
- Fix Chinese: use '积分' instead of '信贷' for consistency
- Fix Polish: use imperative mood for CTA link
- Improve accessibility: use <button> instead of <div> for clickable banners
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (2)
supabase/functions/_backend/private/credits.ts (1)

450-459: ⚠️ Potential issue | 🟠 Major

Use authenticated client for user-facing /complete-top-up endpoint.

Line 452 uses supabaseAdmin(c) to call the RPC, which bypasses JWT-based authentication at the RPC level and violates the backend security guideline for user-facing APIs. Although authentication and org-level permission checks (org.update_billing) occur before the RPC call, the admin SDK should not be used for user-facing endpoints.

Fix by either: (1) granting EXECUTE permission on top_up_usage_credits to the authenticated role and switching to supabaseClient(c, token).rpc(...), or (2) moving the RPC logic to an internal-only endpoint guarded by middlewareAPISecret.

As per coding guidelines: "Never use the Supabase admin SDK (with service key) for user-facing APIs; always use the client SDK with user authentication to enforce RLS policies; admin SDK should only be used for internal operations, triggers, and CRON jobs."

src/components/CreditsCta.vue (1)

31-91: ⚠️ Potential issue | 🟡 Minor

Use DaisyUI buttons for the new interactive elements.

Both new/updated <button> elements should include d-btn to comply with the UI guidelines for interactive components.
As per coding guidelines, "Use DaisyUI components (d-btn, d-input, d-card) for interactive elements in Vue components".

♻️ Suggested adjustment
-  <button
+  <button
     v-if="props.creditsOnly"
     type="button"
-    class="flex items-center w-full p-4 text-left transition-all duration-200 border cursor-pointer bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 hover:border-blue-300 dark:hover:border-blue-700 rounded-xl group"
+    class="d-btn flex items-center w-full p-4 text-left transition-all duration-200 border cursor-pointer bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 hover:border-blue-300 dark:hover:border-blue-700 rounded-xl group"
     `@click`="goToCredits"
   >
-  <button
+  <button
     v-else
     type="button"
-    class="flex items-center w-full p-4 text-left transition-all duration-200 border cursor-pointer bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-700 hover:border-blue-300 dark:hover:border-blue-700 rounded-xl group"
+    class="d-btn flex items-center w-full p-4 text-left transition-all duration-200 border cursor-pointer bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-700 hover:border-blue-300 dark:hover:border-blue-700 rounded-xl group"
     `@click`="goToCredits"
   >
🤖 Fix all issues with AI agents
In `@package.json`:
- Line 4: Remove the manual version bump from package.json by reverting the
change to the "version" field ("version": "12.110.6"); leave the version value
as it was on the base branch (or restore the original file state) so release
automation (semantic-release) can manage versioning, changelog, and deployments
instead of committing any manual version changes in package.json.

In `@supabase/functions/_backend/utils/version.ts`:
- Line 1: The exported constant version ('version') was manually bumped; undo
this manual edit and restore the auto-generated value (or revert the file to the
version from main/HEAD that the generator produces) so release automation
controls version.ts; do not commit manual changes to the version constant and,
if needed, re-run the project's generation script or reset this file to the
auto-generated state before pushing.

In
`@supabase/migrations/20260214054927_restore_top_up_usage_credits_for_service_role.sql`:
- Around line 16-18: Update the stale comment that lists caller `#3` to indicate
the RPC is now invoked via supabaseAdmin (service_role) instead of
supabaseClient (authenticated); specifically edit the comment referencing
"supabase/functions/_backend/private/credits.ts — Frontend complete-top-up
endpoint (user JWT)" to reflect that the complete-top-up endpoint now calls the
RPC using supabaseAdmin (service_role).
- Around line 23-30: The GRANT EXECUTE statement for the function
public.top_up_usage_credits(...) is redundant; remove the duplicate GRANT
line(s) that grant EXECUTE to "service_role" for top_up_usage_credits (the
signature uses timestamptz/timestamp with time zone aliases) so only the earlier
migration restores that privilege—delete the GRANT block referencing
top_up_usage_credits(p_org_id uuid, p_amount numeric, p_expires_at timestamp
with time zone, p_source text, p_source_ref jsonb, p_notes text).
🧹 Nitpick comments (2)
supabase/functions/_backend/private/admin_credits.ts (1)

317-319: as any cast bypasses type safety on the RPC call.

The cast suppresses TypeScript errors but means parameter name mismatches (e.g., p_org_id vs orgId) or RPC renames won't be caught at compile time. Consider adding the function signature to your Supabase generated types instead.

src/pages/admin/dashboard/credits.vue (1)

231-256: Silent failure — loadOrgStats swallows errors without user feedback.

loadOrgBalance shows toast.error(...) on failure, but loadOrgStats only logs to console. If the stats endpoint fails, the admin sees a silent "No data" state with no indication of an error.

Proposed fix — add a toast on failure
   catch (error) {
     console.error('Stats load error:', error)
     orgStats.value = null
+    toast.error(t('failed-to-fetch-statistics'))
   }

"name": "capgo-app",
"type": "module",
"version": "12.110.1",
"version": "12.110.6",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Revert manual version bump in package.json.

Version changes should be left to release automation; please remove this bump from the PR.

Based on learnings: Do not manually deploy or commit version bumps; CI/CD handles version bumping in package.json, CHANGELOG.md generation via semantic-release, and deployment after merge to main.

🤖 Prompt for AI Agents
In `@package.json` at line 4, Remove the manual version bump from package.json by
reverting the change to the "version" field ("version": "12.110.6"); leave the
version value as it was on the base branch (or restore the original file state)
so release automation (semantic-release) can manage versioning, changelog, and
deployments instead of committing any manual version changes in package.json.

@@ -1,3 +1,3 @@
export const version = '12.110.1'
export const version = '12.110.6'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid manual version bumps in generated version.ts.

This file is marked as auto-generated; please let release automation update it.

Based on learnings: Do not manually deploy or commit version bumps; CI/CD handles version bumping in package.json, CHANGELOG.md generation via semantic-release, and deployment after merge to main.

🤖 Prompt for AI Agents
In `@supabase/functions/_backend/utils/version.ts` at line 1, The exported
constant version ('version') was manually bumped; undo this manual edit and
restore the auto-generated value (or revert the file to the version from
main/HEAD that the generator produces) so release automation controls
version.ts; do not commit manual changes to the version constant and, if needed,
re-run the project's generation script or reset this file to the auto-generated
state before pushing.

Comment on lines +16 to +18
-- It is also called via supabaseClient (authenticated) from:
-- 3. supabase/functions/_backend/private/credits.ts (line ~450)
-- — Frontend complete-top-up endpoint (user JWT)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stale comment: caller #3 was switched to supabaseAdmin.

The PR commit messages indicate the complete-top-up endpoint was explicitly switched to call the RPC via supabaseAdmin (service_role). This comment still states it uses supabaseClient (authenticated), which is now misleading. Update to reflect the current state.

🤖 Prompt for AI Agents
In
`@supabase/migrations/20260214054927_restore_top_up_usage_credits_for_service_role.sql`
around lines 16 - 18, Update the stale comment that lists caller `#3` to indicate
the RPC is now invoked via supabaseAdmin (service_role) instead of
supabaseClient (authenticated); specifically edit the comment referencing
"supabase/functions/_backend/private/credits.ts — Frontend complete-top-up
endpoint (user JWT)" to reflect that the complete-top-up endpoint now calls the
RPC using supabaseAdmin (service_role).

Comment on lines +23 to +30
GRANT EXECUTE ON FUNCTION "public"."top_up_usage_credits"(
"p_org_id" "uuid",
"p_amount" numeric,
"p_expires_at" timestamp with time zone,
"p_source" "text",
"p_source_ref" "jsonb",
"p_notes" "text"
) TO "service_role";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for duplicate/similar migration files granting EXECUTE on top_up_usage_credits
fd -e sql . supabase/migrations | xargs grep -l 'top_up_usage_credits' 2>/dev/null | sort
echo "---"
# Show the content of the earlier migration if it exists
fd 'restore_top_up_usage_credits_service_role_execute' supabase/migrations --exec cat {}

Repository: Cap-go/capgo

Length of output: 678


Remove the redundant GRANT statement.

This migration duplicates a GRANT from 20260211034635_restore_top_up_usage_credits_service_role_execute.sql. Both grant EXECUTE on public.top_up_usage_credits(uuid, numeric, timestamptz, text, jsonb, text) to service_role. Although timestamptz and timestamp with time zone are aliases, they resolve to the same function signature. Since the earlier migration already restores this privilege, this grant is redundant and should be removed.

🤖 Prompt for AI Agents
In
`@supabase/migrations/20260214054927_restore_top_up_usage_credits_for_service_role.sql`
around lines 23 - 30, The GRANT EXECUTE statement for the function
public.top_up_usage_credits(...) is redundant; remove the duplicate GRANT
line(s) that grant EXECUTE to "service_role" for top_up_usage_credits (the
signature uses timestamptz/timestamp with time zone aliases) so only the earlier
migration restores that privilege—delete the GRANT block referencing
top_up_usage_credits(p_org_id uuid, p_amount numeric, p_expires_at timestamp
with time zone, p_source text, p_source_ref jsonb, p_notes text).

@sonarqubecloud
Copy link

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.

3 participants