tools db browser - BevvyTech/BrewskiDocs GitHub Wiki

Tools – DB Browser (/tools/db-browser)

DB Browser

DB Browser Agent – Schema Handling Rules

  1. Dynamic Schema Introspection

    • Never hardcode column names or relationships.
    • Always read live schema data from Postgres using information_schema and pg_catalog.
    • Use this base query to get all visible columns:
      SELECT
        table_name,
        column_name,
        data_type,
        is_nullable,
        column_default
      FROM information_schema.columns
      WHERE table_schema = 'public'
      ORDER BY table_name, ordinal_position;
    • Cache results in memory to reduce load and refresh after migrations.
  2. Foreign Key Discovery

    • Discover foreign keys dynamically:
      SELECT
        kcu.table_name AS source_table,
        kcu.column_name AS fk_column,
        ccu.table_name AS target_table,
        ccu.column_name AS target_column
      FROM information_schema.table_constraints AS tc
      JOIN information_schema.key_column_usage AS kcu
        ON tc.constraint_name = kcu.constraint_name
      JOIN information_schema.constraint_column_usage AS ccu
        ON ccu.constraint_name = tc.constraint_name
      WHERE tc.constraint_type = 'FOREIGN KEY';
    • Merge this into a mapping {source_table.fk_column → {target_table, target_column}}.
  3. Friendly Columns

    • Prefer columns named (case-insensitive): name, reference, title, label, code, description
    • When resolving foreign keys, select one of those fields if available.
    • If no such column exists, fall back to any text field shorter than 100 chars.
    • Avoid IDs, UUIDs, booleans, timestamps, and numeric columns as labels.
  4. Metadata Table (optional but preferred)

    • Use table_labels if it exists:
      CREATE TABLE IF NOT EXISTS table_labels (
        table_name text PRIMARY KEY,
        label_columns text[]
      );
    • Read mappings from there first; only use heuristics when missing.
  5. Runtime Behaviour

    • When rendering tables in the admin browser:
      • Use live schema to build headers and data grids.
      • Automatically join FK targets to display friendly names.
      • Validate columns before running $queryRaw() to avoid “column does not exist” errors.
    • Never assume a fixed list of label columns — always compute dynamically.
  6. Schema Sync

    • Regenerate or refresh schema cache whenever:
      • a migration runs, or
      • prisma migrate deploy completes.
    • Store the cached schema as src/config/schema-map.json for offline operation.
  7. Fail Gracefully

    • If a label column is missing, display the raw ID but log a warning.
    • Never break API routes because of schema drift.

Quick Summary

The DB browser must be entirely data-driven. Discover columns, types, and foreign keys dynamically. Use heuristics for “friendly” display fields. Do not hardcode schema details in code.

Purpose

  • Give super user team members a read-only view of production database tables for diagnostics and support triage.
  • Consolidate table selection, column filtering, and pagination into a single interface so operators do not need direct database access.

Access & Permissions

  • Navigation item, landing card, and page content appear only when the current membership has teamRoleType === "su".
  • Non-super users attempting to load the page are redirected back to /tools.

Layout

  • Split layout: left column (~30% width on desktop) lists tables from the public schema along with row-count pills; right column houses the search controls and data preview.
  • Breadcrumb trail keeps context (Dashboard → Tools → DB Browser), followed by a read-only notice.
  • Right column stacks two cards: search/filter form on top and a results table with pagination beneath.

Key Interactions

  • Selecting a table resets pagination and clears search filters before requesting rows via dbBrowserService.fetchRows.
  • Search form requires both a column (populated from the latest metadata) and a term; submitting triggers a re-fetch, while Reset clears filters and reloads the first page.
  • Results card exposes two tabs (“Rows”, “Columns”); the last selected tab persists in the brewski_tools_db_browser_tab cookie for a year.
  • Rows tab renders an MUI table with sticky headers, displaying JSON-serialised values (nulls shown as ). TablePagination reflects backend total, page, and pageSize.
  • Every visible column header on the Rows tab wraps a TableSortLabel. First click sorts ascending, second click on the same column flips to descending, and selecting a different column resets it to ascending. Each click re-fetches rows with sort=<column> and direction=<asc|desc> so the backend does the ordering.
  • Recognised foreign keys (team_id, client_id, user_id, beer_id, batch_id, brewer_id) surface the related entity name inline, with click-to-copy behaviour for the underlying ID and tooltips when the label differs.
  • Columns tab lists every column with size-aware type labels (e.g., numeric(10,2)) and a visibility checkbox. Toggling a column hides/shows it in the Rows tab and persists the choice per table/column via cookies (brewski_tools_db_browser_{table}_{column}_enabled).
  • Cell content truncates with ellipsis; clicking any row opens a dialog showing the full JSON payload for inspection.
  • Row detail dialogs now show JSON on the left and, when references exist, a related-records card on the right with quick links to the matching Admin pages (users, teams, clients, beers, batches). IDs remain copy-to-clipboard.
  • Columns tab lists every column name and data type returned by the API so operators can review schema context without leaving the tool.
  • Loading states display a spinner and helper copy, and API errors surface through an inline Alert.
  • A timestamp at the bottom reflects the most recent fetch using the active locale.
  • The table footer includes Sorted by {column} ({direction}), where direction is a localized “ascending”/“descending” string that mirrors the server’s reported ordering.

Data & Dependencies

  • dbBrowserService.listTablesGET /tools/db/tables to populate the left-hand list.
  • dbBrowserService.fetchRowsGET /tools/db/tables/:table/rows (with page, pageSize, optional searchColumn/searchTerm, plus sort/direction) to retrieve column metadata and row data.
  • Uses the global auth token; relies on existing unauthorised handler to broadcast brewski:unauthorized when a 401 is returned.
⚠️ **GitHub.com Fallback** ⚠️