Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
3195359
fix(openalex-importer): guard against empty arrays after dedup filtering
hubsmoke Apr 4, 2026
e213d22
refactor(openalex-importer): clean up unused mock scaffolding in save…
hubsmoke Apr 4, 2026
19d5fde
feat(openalex-importer): add Telegram status notifications
hubsmoke Apr 4, 2026
fd6bfd4
feat(openalex-importer): add /status and /help Telegram bot commands
hubsmoke Apr 4, 2026
1d0c0c2
fix(openalex-importer): address CodeRabbit review comments
hubsmoke Apr 4, 2026
3e57795
Merge pull request #1272 from desci-labs/fix/openalex-importer-null-p…
hubsmoke Apr 4, 2026
500d2b6
ci: add GitHub Actions workflow for openalex-importer
hubsmoke Apr 4, 2026
a7c9948
fix(openalex-importer): bump Node to 20.19.1 for rolldown native bind…
hubsmoke Apr 4, 2026
8a10f2e
fix(openalex-importer): fix TS error — signal type must be AbortSigna…
hubsmoke Apr 4, 2026
b518059
feat(openalex-importer): announce bot online on startup
hubsmoke Apr 4, 2026
2d42e51
feat(openalex-importer): enrich status with last import time & days b…
hubsmoke Apr 4, 2026
88094fe
feat(openalex-importer): add record counts to status, use lightweight…
hubsmoke Apr 4, 2026
d5f812e
feat(openalex-importer): add 30d record counts and time-ago to status
hubsmoke Apr 4, 2026
a6153df
feat(openalex-importer): notify Telegram on shutdown and uncaught exc…
hubsmoke Apr 4, 2026
d33d516
fix(openalex-importer): harden shutdown handlers
hubsmoke Apr 4, 2026
b8de9fe
fix: make external CID test self-contained
hubsmoke Apr 4, 2026
329fd64
feat(openalex-importer): add /pipelines command for downstream health
hubsmoke Apr 4, 2026
b2f6d9c
fix(openalex-importer): address CodeRabbit review — NaN guard and Pre…
hubsmoke Apr 4, 2026
5ce5849
Merge pull request #1273 from desci-labs/feat/openalex-pipeline-status
hubsmoke Apr 4, 2026
b0dc6fc
fix(openalex-importer): simplify pipeline health — healthy if ran + d…
hubsmoke Apr 4, 2026
ad8ab5f
Merge pull request #1274 from desci-labs/feat/openalex-pipeline-status
hubsmoke Apr 4, 2026
c1e0a96
fix(openalex-importer): break single-day transaction into per-chunk c…
hubsmoke Apr 6, 2026
0d27c57
fix(openalex-importer): add error logging with batchId on pipeline fa…
hubsmoke Apr 6, 2026
d85bf39
fix(openalex-importer): address PR review — auth guard, concurrent cl…
hubsmoke Apr 6, 2026
64185a6
fix(openalex-importer): address CodeRabbit follow-up — migration, pin…
hubsmoke Apr 6, 2026
eccbbce
Merge pull request #1276 from desci-labs/fix/chunked-pipeline-commits
hubsmoke Apr 6, 2026
b774de9
fix(desci-server): don't crash the pod on unhandled promise rejections
hubsmoke Apr 10, 2026
24c14e5
fix(desci-server): address CodeRabbit review on #1277
hubsmoke Apr 10, 2026
9da5e0e
Merge pull request #1277 from desci-labs/hotfix/unhandled-rejection-n…
hubsmoke Apr 10, 2026
4bbf638
update prisma
ogbanugot Apr 14, 2026
2b77b17
remove deprecated import and feature
ogbanugot Apr 14, 2026
211d1e6
fix prisma 6 or shape
ogbanugot Apr 14, 2026
61d4b3c
Merge pull request #1278 from desci-labs/update-prisma
ogbanugot Apr 14, 2026
ff219c9
update prisma sentry
ogbanugot Apr 14, 2026
7351243
update sentry version
ogbanugot Apr 14, 2026
40296dc
Merge pull request #1279 from desci-labs/update-prisma
ogbanugot Apr 14, 2026
9a04ae9
perf: cache /v1/pub/versions in Redis (7s → <100ms)
hubsmoke Apr 15, 2026
6104ac3
feat: add warm-versions-cache script to pre-fill Redis
hubsmoke Apr 19, 2026
3297198
Remove legacy desci-server load balancers
ogbanugot Apr 22, 2026
21306f7
Merge pull request #1282 from desci-labs/remove-legacy-lbs
ogbanugot Apr 22, 2026
3875380
Merge pull request #1281 from desci-labs/perf/cache-pub-versions
hubsmoke Apr 29, 2026
15be957
perf: cache /v1/data/pubTree and /<uuid>/<v> manifest resolve in Redis
hubsmoke May 3, 2026
66d5e85
Merge pull request #1286 from desci-labs/hotfix/cache-resolve-pubtree
hubsmoke May 3, 2026
e3b113b
add revenuecat bundle support
ogbanugot May 5, 2026
7221c49
fix active limit check
ogbanugot May 5, 2026
900931d
Merge pull request #1288 from desci-labs/mobile-chat-bundles
ogbanugot May 6, 2026
68c58bf
fix usage rollover
ogbanugot May 6, 2026
a3ae7a0
fix broken tests
ogbanugot May 6, 2026
adda2fa
Merge pull request #1289 from desci-labs/mobile-chat-bundles
ogbanugot May 6, 2026
d05ec9d
fix(nodes-media): share image renders raw markdown / titles show <sup…
hubsmoke May 7, 2026
996a020
Merge pull request #1290 from desci-labs/fix/share-image-renderer-on-…
hubsmoke May 7, 2026
240c05c
trim betterstack logs
ogbanugot May 14, 2026
c6c3908
strip log url
ogbanugot May 15, 2026
93d7ba5
Merge pull request #1296 from desci-labs/betterstack-patch
ogbanugot May 15, 2026
63700b1
add auto replenish bundles
ogbanugot May 19, 2026
8de71e0
fix rely vi mock on non-isolated run
ogbanugot May 19, 2026
0736481
Merge pull request #1298 from desci-labs/autorewnew-bundles
ogbanugot May 19, 2026
8d8f8fd
fix draft manifest persistence and cleanup
ogbanugot Jul 20, 2026
d608e5a
fix desci-server CI build memory
ogbanugot Jul 20, 2026
8e5f7e4
fix desci-server TypeScript build errors
ogbanugot Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ NODE_ENV=test
PORT=5421

IPFS_NODE_URL=http://host.docker.internal:5003
PUBLIC_IPFS_RESOLVER=https://pub.desci.com
PUBLIC_IPFS_RESOLVER=http://host.docker.internal:5003
GUEST_IPFS_NODE_URL=http://host.docker.internal:5003 # Share the same kubo node

### Database - Postgres
Expand Down
95 changes: 95 additions & 0 deletions .github/workflows/build-openalex-importer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
on:
push:
paths:
- .github/workflows/build-openalex-importer.yaml
- openalex-importer/**
branches:
- main
- develop

name: Build openalex-importer

env:
AWS_DEFAULT_REGION: us-east-2
AWS_DEFAULT_OUTPUT: json
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
CONTAINER_IMAGE: openalex-importer
DOCKER_BUILDKIT: 1

jobs:
build-and-push:
name: Build and push image
runs-on: blacksmith-4vcpu-ubuntu-2204
outputs:
image_tag: ${{ steps.image-tag.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Generate image tag
id: image-tag
run: echo "tag=$(echo ${{ github.sha }} | cut -c1-7)-$(date +%s)" >> $GITHUB_OUTPUT

- name: Build and tag the image
run: |
docker build \
-t $CONTAINER_IMAGE:latest \
-t $CONTAINER_IMAGE:${{ steps.image-tag.outputs.tag }} \
-t $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE \
./openalex-importer

- name: Push to ECR
run: |
aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
docker tag $CONTAINER_IMAGE:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }}
docker tag $CONTAINER_IMAGE:latest $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:latest
docker tag $CONTAINER_IMAGE:${{ steps.image-tag.outputs.tag }} $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ steps.image-tag.outputs.tag }}
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ github.sha }}
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:latest
docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$CONTAINER_IMAGE:${{ steps.image-tag.outputs.tag }}

verify-deployment:
name: Verify deployment
needs: build-and-push
if: always() && needs.build-and-push.result == 'success'
runs-on: blacksmith-4vcpu-ubuntu-2204
timeout-minutes: 10
steps:
- name: Verify image exists in ECR
run: |
IMAGE_META=$(aws ecr describe-images \
--repository-name $CONTAINER_IMAGE \
--image-ids imageTag=${{ github.sha }} \
--region $AWS_DEFAULT_REGION 2>&1)
if [ $? -eq 0 ]; then
echo "Image $CONTAINER_IMAGE:${{ github.sha }} verified in ECR"
echo "$IMAGE_META" | jq '.imageDetails[0].imageTags'
else
echo "::error::Image not found in ECR: $IMAGE_META"
exit 1
fi

- name: Tag image for Flux
run: |
MANIFEST=$(aws ecr batch-get-image \
--repository-name $CONTAINER_IMAGE \
--image-ids imageTag=${{ github.sha }} \
--region $AWS_DEFAULT_REGION \
--query 'images[0].imageManifest' --output text)
for TAG in latest ${{ needs.build-and-push.outputs.image_tag }}; do
OUTPUT=$(aws ecr put-image \
--repository-name $CONTAINER_IMAGE \
--image-tag "$TAG" \
--image-manifest "$MANIFEST" \
--region $AWS_DEFAULT_REGION 2>&1) || {
if echo "$OUTPUT" | grep -q "ImageAlreadyExistsException"; then
echo "Tag :$TAG already points to this digest — ok"
else
echo "::error::Failed to tag :$TAG — $OUTPUT"
exit 1
fi
}
echo "Tagged as :$TAG"
done
2 changes: 1 addition & 1 deletion desci-server/Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ COPY ./vitest.config.ts ./vitest.config.ts
COPY ./tsconfig.json ./tsconfig.json

RUN --mount=type=secret,id=SENTRY_AUTH_TOKEN,env=SENTRY_AUTH_TOKEN \
yarn build
NODE_OPTIONS=--max-old-space-size=4096 yarn build

# ── Test stage (used by docker-compose.test.yml) ─────────────
FROM builder AS test
Expand Down
40 changes: 0 additions & 40 deletions desci-server/kubernetes/deployment_dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -187,43 +187,3 @@ spec:
periodSeconds: 1

serviceAccountName: 'vault-auth'
---
apiVersion: v1
kind: Service
metadata:
name: desci-server-dev-service
labels:
App: DesciServerDev
spec:
type: LoadBalancer
selector:
App: DesciServerDev
ports:
- protocol: TCP
name: api
port: 80
targetPort: 5420
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: desci-server-dev-ingress-new
annotations:
kubernetes.io/ingress.class: 'alb'
alb.ingress.kubernetes.io/scheme: 'internet-facing'
alb.ingress.kubernetes.io/target-type: 'ip'
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-2:523044037273:certificate/b6ddf370-5957-4996-bc09-4f48894c0c1f
alb.ingress.kubernetes.io/ssl-redirect: '443'
spec:
rules:
- host: 'nodes-api-dev.desci.com'
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: desci-server-dev-service
port:
number: 80
41 changes: 0 additions & 41 deletions desci-server/kubernetes/deployment_prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,44 +186,3 @@ spec:
failureThreshold: 200
periodSeconds: 1
serviceAccountName: 'vault-auth'
---
apiVersion: v1
kind: Service
metadata:
name: desci-server-prod-service
labels:
App: DesciServer
spec:
type: LoadBalancer
selector:
App: DesciServer
ports:
- protocol: TCP
name: api
port: 80
targetPort: 5420
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: desci-server-prod-ingress-new
annotations:
kubernetes.io/ingress.class: 'alb'
alb.ingress.kubernetes.io/scheme: 'internet-facing'
alb.ingress.kubernetes.io/target-type: 'ip'
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
# alb.ingress.kubernetes.io/ssl-redirect: '443'
# If you have a certificate for SSL termination at ALB
alb.ingress.kubernetes.io/certificate-arn: 'arn:aws:acm:us-east-2:523044037273:certificate/9b192c61-a321-4f65-9309-b2f326b99d05'
spec:
rules:
- host: 'nodes-api.desci.com'
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: desci-server-prod-service
port:
number: 80
11 changes: 6 additions & 5 deletions desci-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"script:backfill-radar": "tsx ./src/scripts/backfill-radar.ts",
"script:pruneExternalPublications": "tsx ./src/scripts/pruneExternalPublications.ts",
"script:email-reminders": "tsx --inspect=0.0.0.0:9277 ./src/workers/emailReminderRunner.ts",
"build": "rimraf dist && tsc && yarn copy-files; if [ \"$SENTRY_AUTH_TOKEN\" ]; then yarn sentry:sourcemaps; else echo 'SENTRY_AUTH_TOKEN not set, sourcemaps will not upload'; fi",
"build": "rimraf dist && tsc && yarn copy-files && if [ \"$SENTRY_AUTH_TOKEN\" ]; then yarn sentry:sourcemaps; else echo 'SENTRY_AUTH_TOKEN not set, sourcemaps will not upload'; fi",
"copy-files": "copyfiles -u 1 src/**/*.cjs dist/",
"generate": "npx prisma generate",
"migrate:local": "DATABASE_URL=postgresql://walter:white@localhost:5433/boilerplate npx prisma migrate dev",
Expand Down Expand Up @@ -77,11 +77,12 @@
"@opentelemetry/api": "^1.8.0",
"@opentelemetry/auto-instrumentations-node": "^0.37",
"@penseapp/discord-notification": "^2.0.9",
"@prisma/client": "4.10.1",
"@prisma/client": "6.15.0",
"@prisma/instrumentation": "6.15.0",
"@react-email/components": "0.0.15",
"@sendgrid/mail": "^7.7.0",
"@sentry/node": "8.29.0",
"@sentry/profiling-node": "8.32.0",
"@sentry/node": "8.55.0",
"@sentry/profiling-node": "8.55.0",
"@sentry/tracing": "^7.12.0",
"@socket.io/redis-adapter": "^8.3.0",
"arweave": "^1.10.18",
Expand Down Expand Up @@ -128,7 +129,7 @@
"pino-pretty": "^10.0.0",
"pino-std-serializers": "^7.0.0",
"posthog-node": "^5.24.5",
"prisma": "4.10.1",
"prisma": "6.15.0",
"qs": "^6.12.0",
"react-email": "2.1.0",
"redis": "^4.6.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
CREATE TYPE "RevenueCatPurchaseFulfillmentType" AS ENUM ('BUNDLE_CHATS', 'LIFETIME_UNLOCK');

CREATE TABLE "RevenueCatPurchaseFulfillment" (
"id" SERIAL NOT NULL,
"userId" INTEGER NOT NULL,
"revenueCatEventId" TEXT,
"revenueCatTransactionId" TEXT NOT NULL,
"revenueCatOriginalTransactionId" TEXT,
"revenueCatProductId" TEXT NOT NULL,
"revenueCatStore" TEXT,
"amountPaid" DOUBLE PRECISION,
"currency" TEXT,
"fulfillmentType" "RevenueCatPurchaseFulfillmentType" NOT NULL,
"purchasedUnits" INTEGER NOT NULL,
"grantedUnits" INTEGER NOT NULL,
"skipReason" TEXT,
"details" JSONB,
"reversedAt" TIMESTAMP(3),
"reversalReason" TEXT,
"reversalDetails" JSONB,
"fulfilledAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "RevenueCatPurchaseFulfillment_pkey" PRIMARY KEY ("id")
);

CREATE UNIQUE INDEX "RevenueCatPurchaseFulfillment_revenueCatTransactionId_key"
ON "RevenueCatPurchaseFulfillment"("revenueCatTransactionId");

CREATE INDEX "RevenueCatPurchaseFulfillment_userId_idx"
ON "RevenueCatPurchaseFulfillment"("userId");

CREATE INDEX "RevenueCatPurchaseFulfillment_revenueCatProductId_idx"
ON "RevenueCatPurchaseFulfillment"("revenueCatProductId");

CREATE INDEX "RevenueCatPurchaseFulfillment_fulfilledAt_idx"
ON "RevenueCatPurchaseFulfillment"("fulfilledAt");

ALTER TABLE "RevenueCatPurchaseFulfillment"
ADD CONSTRAINT "RevenueCatPurchaseFulfillment_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
CREATE TABLE "BundleAutoReplenishment" (
"id" SERIAL NOT NULL,
"userId" INTEGER NOT NULL,
"isEnabled" BOOLEAN NOT NULL DEFAULT false,
"threshold" INTEGER NOT NULL DEFAULT 5,
"replenishmentInProgress" BOOLEAN NOT NULL DEFAULT false,
"lastAttemptedAt" TIMESTAMP(3),
"lastSucceededAt" TIMESTAMP(3),
"lastFailedAt" TIMESTAMP(3),
"lastFailureReason" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "BundleAutoReplenishment_pkey" PRIMARY KEY ("id")
);

CREATE UNIQUE INDEX "BundleAutoReplenishment_userId_key" ON "BundleAutoReplenishment"("userId");
CREATE INDEX "BundleAutoReplenishment_isEnabled_replenishmentInProgress_idx" ON "BundleAutoReplenishment"("isEnabled", "replenishmentInProgress");

ALTER TABLE "BundleAutoReplenishment"
ADD CONSTRAINT "BundleAutoReplenishment_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
57 changes: 55 additions & 2 deletions desci-server/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["tracing"]
provider = "prisma-client-js"
}

datasource db {
Expand Down Expand Up @@ -284,8 +283,10 @@ model User {
Invoice Invoice[]
ImportTaskQueue ImportTaskQueue[]
AbandonedCheckout AbandonedCheckout[]
BundleAutoReplenishment BundleAutoReplenishment?
accountDeletionRequest AccountDeletionRequest?
StripeCheckoutFulfillment StripeCheckoutFulfillment[]
RevenueCatPurchaseFulfillment RevenueCatPurchaseFulfillment[]

@@index([orcid])
@@index([walletAddress])
Expand Down Expand Up @@ -1517,6 +1518,24 @@ model PaymentMethod {
@@index([stripePaymentMethodId])
}

model BundleAutoReplenishment {
id Int @id @default(autoincrement())
userId Int @unique
isEnabled Boolean @default(false)
threshold Int @default(5)
replenishmentInProgress Boolean @default(false)
lastAttemptedAt DateTime?
lastSucceededAt DateTime?
lastFailedAt DateTime?
lastFailureReason String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

user User @relation(fields: [userId], references: [id])

@@index([isEnabled, replenishmentInProgress])
}

model Invoice {
id Int @id @default(autoincrement())
userId Int
Expand Down Expand Up @@ -1635,6 +1654,35 @@ model StripeCheckoutFulfillment {
@@index([fulfilledAt])
}

model RevenueCatPurchaseFulfillment {
id Int @id @default(autoincrement())
userId Int
revenueCatEventId String?
revenueCatTransactionId String @unique
revenueCatOriginalTransactionId String?
revenueCatProductId String
revenueCatStore String?
amountPaid Float?
currency String?
fulfillmentType RevenueCatPurchaseFulfillmentType
purchasedUnits Int
grantedUnits Int
skipReason String?
details Json?
reversedAt DateTime?
reversalReason String?
reversalDetails Json?
fulfilledAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

user User @relation(fields: [userId], references: [id])

@@index([userId])
@@index([revenueCatProductId])
@@index([fulfilledAt])
}

enum ImportTaskQueueStatus {
PENDING
IN_PROGRESS
Expand All @@ -1646,6 +1694,11 @@ enum StripeCheckoutFulfillmentType {
BUNDLE_CHATS
}

enum RevenueCatPurchaseFulfillmentType {
BUNDLE_CHATS
LIFETIME_UNLOCK
}

enum Feature {
REFEREE_FINDER
RESEARCH_ASSISTANT
Expand Down
Loading
Loading