Files
Recordly/services/supabase/README.md
T
webadderall 0570f1de47 Fix Recordly 2.0 part 3 review findings
Integrate validated app changes and preserve original stack history.
2026-11-07 14:48:47 +11:00

7.8 KiB

Desktop feedback

See setup status for the applied project configuration, verification, and remaining launch work.

Before releasing feedback submission:

  1. Apply migrations/202609230001_feedback.sql and then migrations/202609230002_feedback_limits.sql in the configured Supabase project. Apply migrations/202610030001_feedback_access.sql after both, including on existing deployments; it revokes inherited client table privileges while preserving owner reads and server inserts.
  2. Deploy the submit-feedback Edge Function from the repository root with the Supabase CLI: supabase functions deploy submit-feedback --workdir services --project-ref YOUR_PROJECT_REF. The function explicitly validates the bearer token with auth.getUser before parsing or storing feedback. The function configuration disables only the gateway's JWT check, not the function's authentication.
  3. Use the project's built-in SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY secrets for the function. Never put a service-role key in the desktop app.

The desktop client calls the function with its existing signed-in session. Direct client report inserts and attachment uploads are disabled by the second migration. The function validates subject/message lengths, diagnostics bytes, and the actual uploaded files (five nonempty files, 10 MB total per submission), and caps the incoming request body. It reserves a per-account UTC daily quota atomically: 10 submission attempts, 25 files, and 50 MB. Failed attempts still consume quota, so repeated upload/delete/retry cycles do not reset it. No Storage schema triggers or modifications are required beyond the access policies.

Feedback is stored in public.feedback_reports. View reports through the Supabase dashboard and retrieve attachments from the private feedback-attachments bucket using each report's attachment paths. Owners can read their own reports; other users cannot. Staff use server-side service-role access. There is no email notification or public issue creation.

On submission, the modal captures up to 100 recent renderer warnings/errors (2,000 characters each), browser/platform details, and a timestamp. Common secrets, URLs, emails, and local home paths are redacted. Arbitrary log objects are not serialized. The final JSON is bounded to 290,000 UTF-8 bytes by dropping oldest entries, below the database's 300,000-byte limit. Diagnostics are automatically included; native-process logs are not collected. Attachments are chosen explicitly.

A local demo account can preview the form but cannot submit without a real Supabase session. Failed submissions retain the draft in memory; closing and reopening the same modal keeps it, but restarting the app does not. The function cleans up failed uploads on a best-effort basis. Periodically remove unattached objects left by interrupted sessions through the Storage API, and prune old feedback_daily_usage rows after their UTC day has ended.

Unit tests exercise the server handler with an injected storage/auth backend. tests/feedback_limits.sql verifies the migration's quotas and permissions in a disposable database with the migrations applied; it rolls back its test data. Live deployment and end-to-end Supabase uploads must be checked separately before release.

Access verification

Run checks/feedback-quota-access.sql as postgres after all feedback migrations in a disposable Supabase database. It uses an existing user with no feedback usage today and rolls back test data. It checks server insertion, owner reads and cross-user isolation after hardening, server-only quota access, authenticated attachment-upload denial for the feedback bucket, client report write denial, anonymous read denial, and all three daily limits. The older checks/feedback-access.sql is only for testing migration 001 in isolation, before migration 002 removes direct client inserts.

Do not put real account IDs, project references, credentials, or operator setup status in this documentation.

User-triggered error reports

Apply 202610020001_export_error_reports.sql, 20261023120000_app_error_report_logs.sql, and 202611070001_export_error_daily_budget.sql in order before deploying the updated submit-export-error function using services/supabase/config.toml (verify_jwt = false). The existing endpoint accepts legacy export summaries and version 2 application reports. The latter contain the failure, a fixed context category, format, and a diagnostic snapshot bounded to 290,000 UTF-8 bytes. Request and stored JSON limits are 1,000,000 bytes to allow for nested JSON escaping. Clients cannot insert or read reports directly. Configure the existing public URL/publishable key in the desktop build; never expose a service-role key.

Report error sends immediately, without a second confirmation or sign-in. Nothing is sent merely because an error occurs. Each button shows sending, confirmed success, or a retryable failure, and prevents repeated clicks while sending. Error notifications remain available until dismissed and keep their existing retry/copy action alongside reporting. Application error pages use the same reporting component as export errors.

Reports include recent renderer warnings/errors, browser/platform metadata, persisted export diagnostics when available, and the displayed failure. Common secrets, emails, URLs, home paths, external-drive paths and temporary paths are redacted before sending; arbitrary console objects are not serialized. Recordings and attachments are not included. Native-process console logs are not collected. The client sends no session Authorization, cookies or referrer, and the function rejects Authorization headers and unexpected report fields. Supabase infrastructure can retain connection metadata separately.

View public.export_error_reports through staff/service-role access. Version 1 reports retain their allowlisted export codes and optional self-reported cloud-plan category. Version 2 reports contain redacted error text and logs and use unknown for the cloud plan. Creation times are rounded to the hour. The service-only quota reserves at most 20 reports per minute, 200 reports per UTC day, and 16 MiB (16,777,216 bytes) of PostgreSQL diagnostics::text per UTC day, globally. Conditional database updates reserve both daily limits atomically; failed inserts consume their reservation. Daily counters contain only the UTC day, attempt count and byte count.

These conservative defaults allow roughly 57 near-290 KB ASCII diagnostic snapshots per day, or up to 200 smaller reports. All callers share the budget, so other callers can exhaust it. Exhaustion returns a retryable 429 REPORT_LIMIT; reports are never truncated to fit or reported as successful before storage. The byte cap bounds serialized diagnostic content, including JSON escaping, rather than total disk usage: row/index overhead, WAL and infrastructure logs are additional. The existing daily Cron job removes reports older than 30 days and prunes minute/day quota counters; deletion is not continuous and depends on Cron running.

Coordinate the daily-budget migration and function rollout: the migration removes the old no-argument quota RPC and grants only the new reserve_export_error_quota(report jsonb) signature to the service role. Apply the migration, then deploy the matching function promptly. An older function during this interval fails closed with 500 SUBMISSION_FAILED; do not keep the old RPC available as a fallback, since it bypasses the daily budget. No desktop change is needed for this quota update. Run tests/export_error_reports.sql in a disposable migrated database to check privileges, storage limits, quota and retention. Handler tests inject the storage backend; browser tests intercept the network request and verify actual log content, retry and confirmed success. These checks do not establish that the live service has been deployed.