diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90fff2d..306b886 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,6 @@ jobs: oauth-client-browser: ${{ steps.filter.outputs.oauth-client-browser }} oauth-client-node: ${{ steps.filter.outputs.oauth-client-node }} lex-agent: ${{ steps.filter.outputs.lex-agent }} - postgres: ${{ steps.filter.outputs.postgres }} steps: - name: Checkout repository uses: actions/checkout@v6 @@ -49,9 +48,6 @@ jobs: lex-agent: - 'packages/oauth-client/**' - 'packages/lex-agent/**' - postgres: - - 'docker/Dockerfile.postgres' - - 'docker/init-databases.sh' # --------------------------------------------------------------------------- # Server — unit tests, e2e tests, frontend build, lint @@ -626,43 +622,6 @@ jobs: # ${{ env.ATCR_IMAGE }}@${{ steps.build-atcr.outputs.digest }} # fi - # --------------------------------------------------------------------------- - # Docker — Postgres image (only when Postgres Docker files change) - # --------------------------------------------------------------------------- - docker-postgres: - needs: changes - if: >- - needs.changes.outputs.postgres == 'true' - runs-on: depot-ubuntu-24.04 - permissions: - contents: read - packages: write - env: - GHCR_POSTGRES_IMAGE: ghcr.io/${{ github.repository }}-postgres - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: docker - file: docker/Dockerfile.postgres - push: true - tags: ${{ env.GHCR_POSTGRES_IMAGE }}:latest - cache-from: type=gha,scope=postgres - cache-to: type=gha,scope=postgres,mode=max,ignore-error=true - # --------------------------------------------------------------------------- # Mirror to Tangled # --------------------------------------------------------------------------- diff --git a/docker-compose.yml b/docker-compose.yml index 4fc0374..c147bc3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,6 @@ services: # - "5432:5432" # volumes: # - pgdata:/var/lib/postgresql/data - # - ./docker/init-databases.sh:/docker-entrypoint-initdb.d/init-databases.sh # healthcheck: # test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] # interval: 5s diff --git a/docker/Dockerfile.postgres b/docker/Dockerfile.postgres deleted file mode 100644 index 4e5214e..0000000 --- a/docker/Dockerfile.postgres +++ /dev/null @@ -1,2 +0,0 @@ -FROM ghcr.io/railwayapp-templates/postgres-ssl:17 -COPY init-databases.sh /docker-entrypoint-initdb.d/ diff --git a/docker/init-databases.sh b/docker/init-databases.sh deleted file mode 100755 index 9bdd905..0000000 --- a/docker/init-databases.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE DATABASE tap; -EOSQL -- 2.51.2 From d129d2667f1db871e59ae599f31f853c8d628e6e Mon Sep 17 00:00:00 2001 From: Trezy Date: Mon, 11 May 2026 19:55:45 -0500 Subject: [PATCH 2/5] fix: move record info into sheets Signed-off-by: Trezy Signed-off-by: Trezy --- src/admin/records.rs | 81 ++++++++++++------- web/src/app/dashboard/records/page.tsx | 108 ++++++++++++++++++++----- web/src/types/records.ts | 4 + 3 files changed, 140 insertions(+), 53 deletions(-) diff --git a/src/admin/records.rs b/src/admin/records.rs index b931134..f771848 100644 --- a/src/admin/records.rs +++ b/src/admin/records.rs @@ -36,6 +36,10 @@ pub(super) struct RecordLabel { pub(super) struct RecordEntry { pub uri: String, pub did: String, + pub collection: String, + pub rkey: String, + pub cid: String, + pub indexed_at: Option, pub record: Value, pub labels: Vec, } @@ -47,6 +51,16 @@ pub(super) struct ListRecordsResponse { pub cursor: Option, } +type RecordRow = ( + String, + String, + String, + String, + String, + Option, + String, +); + /// GET /admin/records?collection=X&limit=N&cursor=C — browse records by collection. pub(super) async fn list_records( State(state): State, @@ -63,10 +77,10 @@ pub(super) async fn list_records( .unwrap_or(0); let sql = adapt_sql( - "SELECT uri, did, record FROM records WHERE collection = ? ORDER BY indexed_at DESC LIMIT ? OFFSET ?", + "SELECT uri, did, collection, rkey, cid, indexed_at, record FROM records WHERE collection = ? ORDER BY indexed_at DESC LIMIT ? OFFSET ?", backend, ); - let rows: Vec<(String, String, String)> = sqlx::query_as(&sql) + let rows: Vec = sqlx::query_as(&sql) .bind(¶ms.collection) .bind(limit + 1) .bind(offset) @@ -75,13 +89,12 @@ pub(super) async fn list_records( .map_err(|e| AppError::Internal(format!("failed to list records: {e}")))?; let has_more = rows.len() as i64 > limit; - let visible_rows: Vec<(String, String, String)> = - rows.into_iter().take(limit as usize).collect(); + let visible_rows: Vec = rows.into_iter().take(limit as usize).collect(); // Batch-query external labels for all visible URIs let uris: Vec<&str> = visible_rows .iter() - .map(|(uri, _, _)| uri.as_str()) + .map(|(uri, _, _, _, _, _, _)| uri.as_str()) .collect(); let label_rows: Vec<(String, String, String, String)> = if uris.is_empty() { @@ -114,34 +127,40 @@ pub(super) async fn list_records( let records: Vec = visible_rows .into_iter() - .map(|(uri, did, record_str)| { - let record: Value = serde_json::from_str(&record_str).unwrap_or_default(); - let mut labels = labels_by_uri.remove(&uri).unwrap_or_default(); - - // Extract self-labels from record JSONB - if let Some(values) = record - .get("labels") - .and_then(|l| l.get("values")) - .and_then(|v| v.as_array()) - { - for entry in values { - if let Some(val) = entry.get("val").and_then(|v| v.as_str()) { - labels.push(RecordLabel { - src: did.clone(), - val: val.to_string(), - cts: String::new(), - }); + .map( + |(uri, did, collection, rkey, cid, indexed_at, record_str)| { + let record: Value = serde_json::from_str(&record_str).unwrap_or_default(); + let mut labels = labels_by_uri.remove(&uri).unwrap_or_default(); + + // Extract self-labels from record JSONB + if let Some(values) = record + .get("labels") + .and_then(|l| l.get("values")) + .and_then(|v| v.as_array()) + { + for entry in values { + if let Some(val) = entry.get("val").and_then(|v| v.as_str()) { + labels.push(RecordLabel { + src: did.clone(), + val: val.to_string(), + cts: String::new(), + }); + } } } - } - - RecordEntry { - uri, - did, - record, - labels, - } - }) + + RecordEntry { + uri, + did, + collection, + rkey, + cid, + indexed_at, + record, + labels, + } + }, + ) .collect(); let cursor = if has_more { diff --git a/web/src/app/dashboard/records/page.tsx b/web/src/app/dashboard/records/page.tsx index 462eeb4..01cbc40 100644 --- a/web/src/app/dashboard/records/page.tsx +++ b/web/src/app/dashboard/records/page.tsx @@ -28,6 +28,12 @@ import { ResponsiveDialogHeader, ResponsiveDialogTitle, } from "@/components/ui/responsive-dialog"; +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet"; import { Checkbox } from "@/components/ui/checkbox"; import { CodeBlock } from "@/components/code-block"; import { DataTable } from "@/components/data-table/data-table"; @@ -430,29 +436,87 @@ export default function RecordsPage() { )} - {viewRecord && ( - setViewRecord(null)}> - - - - {viewRecord.uri} - - - - {hasPermission("records:delete") && ( -
- + { + if (!open) setViewRecord(null); + }} + > + + {viewRecord && ( + <> + + Record Detail + +
+
+
+ URI +

{viewRecord.uri}

+
+
+ DID +

{viewRecord.did}

+
+
+ Collection +

{viewRecord.collection}

+
+
+ Record Key +

{viewRecord.rkey}

+
+
+ CID +

{viewRecord.cid}

+
+ {viewRecord.indexed_at && ( +
+ Indexed +

+ {new Date(viewRecord.indexed_at).toLocaleString()} +

+
+ )} + {viewRecord.labels.length > 0 && ( +
+ Labels +
+ {viewRecord.labels.map((l, i) => ( + + {l.val} + + ))} +
+
+ )} +
+ +
+ Record +
+ +
+
- )} - - - )} + {hasPermission("records:delete") && ( +
+ +
+ )} + + )} +
+
labels: RecordLabel[] } -- 2.51.2 From 50a378624dcd78a6ff0de35808ccfb1580bf70b8 Mon Sep 17 00:00:00 2001 From: Trezy Date: Tue, 12 May 2026 09:58:05 -0500 Subject: [PATCH 3/5] fix: prevent dead letters from being created without an ID fixes #20 Signed-off-by: Trezy Signed-off-by: Trezy --- src/lua/execute.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lua/execute.rs b/src/lua/execute.rs index c2c0c0c..2e32cb9 100644 --- a/src/lua/execute.rs +++ b/src/lua/execute.rs @@ -957,12 +957,13 @@ pub async fn execute_hook_script(event: &HookEvent<'_>) -> Option { .map(|r| serde_json::to_string(r).unwrap_or_default()); let dead_letter_sql = adapt_sql( r#" - INSERT INTO dead_letter_hooks (lexicon_id, uri, did, collection, rkey, action, record, error, attempts, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + INSERT INTO dead_letter_hooks (id, lexicon_id, uri, did, collection, rkey, action, record, error, attempts, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "#, backend, ); if let Err(e) = sqlx::query(&dead_letter_sql) + .bind(uuid::Uuid::new_v4().to_string()) .bind(event.lexicon_id) .bind(event.uri) .bind(event.did) -- 2.51.2 From 671a753f772a8bcbf25d7c5e1a7656b7ee7abc9c Mon Sep 17 00:00:00 2001 From: Trezy Date: Tue, 12 May 2026 09:58:46 -0500 Subject: [PATCH 4/5] fix: generate missing IDs for dead letters fixes #20 Signed-off-by: Trezy Signed-off-by: Trezy --- migrations/postgres/20260512000000_fix_null_dead_letter_ids.sql | 1 + migrations/sqlite/20260512000000_fix_null_dead_letter_ids.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 migrations/postgres/20260512000000_fix_null_dead_letter_ids.sql create mode 100644 migrations/sqlite/20260512000000_fix_null_dead_letter_ids.sql diff --git a/migrations/postgres/20260512000000_fix_null_dead_letter_ids.sql b/migrations/postgres/20260512000000_fix_null_dead_letter_ids.sql new file mode 100644 index 0000000..42239fa --- /dev/null +++ b/migrations/postgres/20260512000000_fix_null_dead_letter_ids.sql @@ -0,0 +1 @@ +UPDATE dead_letter_hooks SET id = gen_random_uuid() WHERE id IS NULL; diff --git a/migrations/sqlite/20260512000000_fix_null_dead_letter_ids.sql b/migrations/sqlite/20260512000000_fix_null_dead_letter_ids.sql new file mode 100644 index 0000000..715dfc2 --- /dev/null +++ b/migrations/sqlite/20260512000000_fix_null_dead_letter_ids.sql @@ -0,0 +1 @@ +UPDATE dead_letter_hooks SET id = lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))) WHERE id IS NULL; -- 2.51.2 From 705c575f568c1f1523a4a7c27c1a41fa41ff24b2 Mon Sep 17 00:00:00 2001 From: Trezy Date: Tue, 12 May 2026 23:13:35 -0500 Subject: [PATCH 5/5] feat: publish node sdk Signed-off-by: Trezy Signed-off-by: Trezy --- packages/oauth-client-node/.releaserc.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/oauth-client-node/.releaserc.json diff --git a/packages/oauth-client-node/.releaserc.json b/packages/oauth-client-node/.releaserc.json new file mode 100644 index 0000000..02e61cd --- /dev/null +++ b/packages/oauth-client-node/.releaserc.json @@ -0,0 +1,16 @@ +{ + "extends": "semantic-release-monorepo", + "branches": [ + "main", + { "name": "dev", "prerelease": "dev" } + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + ["@semantic-release/exec", { + "prepareCmd": "node -e \"const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version='${nextRelease.version}';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\"", + "publishCmd": "npm view $(node -p \"require('./package.json').name\")@${nextRelease.version} version 2>/dev/null && echo 'Version already published, skipping' || bun publish --access public --tag ${nextRelease.channel || 'latest'}" + }], + "@semantic-release/github" + ] +}