Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0999609
feat: redis
ns212 Aug 20, 2025
3eb7273
updates
ns212 Aug 20, 2025
8c8672f
updates
ns212 Aug 20, 2025
5d5567e
updates
ns212 Aug 20, 2025
6d8588e
updates
ns212 Aug 20, 2025
a7869b7
eslint
ns212 Aug 20, 2025
4e5405c
cache key prefix
ns212 Aug 20, 2025
b8323e7
fix ci
ns212 Aug 20, 2025
32fd42f
redis lock
ns212 Aug 20, 2025
d48fe46
expressjs: disable x-powered-by header
ns212 Aug 24, 2025
761f487
index cache-control
ns212 Aug 24, 2025
b3c9219
remove block in-memory cache
ns212 Aug 24, 2025
4b14d1a
remove block in-memory cache
ns212 Aug 24, 2025
203f10c
remove block in-memory cache
ns212 Aug 24, 2025
3a350fa
remove sanitizeBigInts
ns212 Aug 25, 2025
cee9d23
Merge branch 'main' into redis
ns212 Aug 25, 2025
b6dce42
improve updateTipData
ns212 Aug 25, 2025
c1a198c
tipInfo
ns212 Aug 25, 2025
244ab22
skip update if no new block
ns212 Aug 25, 2025
401415f
Merge remote-tracking branch 'ho/ho_fix_coinbase_bug' into redis
ns212 Aug 26, 2025
b51a8df
merge master
ns212 Aug 30, 2025
3c36ee7
formatting
ns212 Aug 30, 2025
b195eae
formatting
ns212 Aug 30, 2025
b968ec3
formatting
ns212 Aug 30, 2025
7a9745b
support grpc over https
ns212 Aug 30, 2025
8b37a8c
Merge remote-tracking branch 'origin/main' into redis
ns212 Aug 30, 2025
899a7e6
redis shutdown
ns212 Aug 31, 2025
26b6a6e
npm update
ns212 Aug 31, 2025
e29694c
Merge remote-tracking branch 'origin/main' into redis
ns212 Sep 1, 2025
b1d61a6
fix UDecimalValue
ns212 Sep 1, 2025
45b0f8e
Merge branch 'main' into redis
ns212 Sep 18, 2025
6f83f5b
pino-http: do not log OPTIONS and health check requests
ns212 Sep 18, 2025
9eee816
pino-http: do not log OPTIONS and health check requests
ns212 Sep 18, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/build_docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ jobs:
with:
context: ./tari-explorer/
file: ./tari-explorer/Dockerfile
platforms: linux/arm64, linux/amd64
platforms: linux/amd64 #, linux/arm64
push: true
provenance: false
cache-from: type=gha
Expand All @@ -215,7 +215,7 @@ jobs:
${{ secrets.DOCKER_PROVIDER }}/${{ secrets.DOCKER_REPO }}/${{ env.DOCKER_IMAGE }}:${{ env.VERSION }}
${{ env.TAG_ALIAS }}
outputs: |
type=registry,annotation-manifest-descriptor.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation-manifest-descriptor.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }},annotation.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }},annotation-index.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation-index.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }}
type=registry,annotation-manifest-descriptor.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation-manifest-descriptor.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }},annotation.org.opencontainers.image.title=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }},annotation.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }}

- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
5 changes: 5 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { fileURLToPath } from "node:url";
import { defineConfig } from "eslint/config";
import eslintPluginRecommended from "eslint-plugin-prettier/recommended";
import { includeIgnoreFile } from "@eslint/compat";
import globals from "globals";
import tseslint from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";

const gitignorePath = fileURLToPath(new URL(".gitignore", import.meta.url));

export default defineConfig([
{
files: ["**/*.ts", "**/*.tsx"],
Expand Down Expand Up @@ -33,5 +37,6 @@ export default defineConfig([
},
ignores: ["build", "**/*.js"],
},
includeIgnoreFile(gitignorePath, "Imported .gitignore patterns"),
// eslintPluginRecommended,
]);
10 changes: 10 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@
import { app } from "./app.js";
import debugLib from "debug";
import http from "http";
import { getRedisClient } from "./utils/redisClient.js";

import JSONbig from "json-bigint";
const JSONbigFunc = JSONbig({ useNativeBigInt: true });
global.JSON = JSONbigFunc as any;

const debug = debugLib("tari-explorer:server");

// Initialize Redis connection
try {
getRedisClient();
console.log('Redis client initialized');
} catch (error) {
console.warn('Redis initialization failed, will fallback to gRPC only:', error);
}

/**
* Get port from environment and store in Express.
*/
Expand Down
117 changes: 117 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"@grpc/grpc-js": "^1.13.4",
"@types/ioredis": "^4.28.10",
"adm-zip": "^0.5.16",
"asciichart": "^1.5.25",
"commander": "^14.0.0",
Expand All @@ -27,6 +28,7 @@
"fast-csv": "^5.0.5",
"grpc-promise": "^1.4.0",
"hbs": "^4.1.2",
"ioredis": "^5.7.0",
"json-bigint": "^1.0.0",
"long": "^5.3.2",
"nice-grpc": "^2.1.12",
Expand All @@ -35,6 +37,7 @@
"serve-favicon": "^2.5.0"
},
"devDependencies": {
"@eslint/compat": "^1.3.2",
"@types/adm-zip": "^0.5.7",
"@types/node": "^24.3.0",
"@typescript-eslint/eslint-plugin": "^8.39.1",
Expand Down
35 changes: 7 additions & 28 deletions routes/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ import { createClient } from "../baseNodeClient.js";
import express, { Request, Response } from "express";
import cache from "../cache.js";
import cacheSettings from "../cacheSettings.js";
import cacheService from "../utils/cacheService.js";
import CacheKeys from "../utils/cacheKeys.js";
import { miningStats } from "../utils/stats.js";
import { HistoricalBlock } from "@/grpc-gen/block.js";
import { sanitizeBigInts } from "../utils/sanitizeObject.js";
import { collectAsyncIterable } from "../utils/grpcHelpers.js";
const router = express.Router();

function fromHexString(hexString: string): number[] {
Expand All @@ -39,26 +40,7 @@ function fromHexString(hexString: string): number[] {
}

router.get("/stats", async function (req: Request, res: Response) {
let limit = parseInt((req.query.limit as string | undefined) || "20");
if (limit > 100) {
limit = 100;
}

const client = createClient();
const tipInfo = await client.getTipInfo({});
const tipHeight = tipInfo?.metadata?.best_block_height || 0n;

const blocks = await collectAsyncIterable(
client.getBlocks({ heights: Array.from({ length: limit }, (_, i) => tipHeight - BigInt(i)), })
);

const stats = blocks.map(block => ({
height: block?.block?.header?.height || 0n,
...miningStats(block, false),
}))
.sort((a, b) => Number(b.height - a.height));

res.header("Cache-Control", cacheSettings.index);
const stats = await cacheService.get(CacheKeys.MINING_STATS_RECENT);
res.json(stats);
})

Expand Down Expand Up @@ -277,12 +259,11 @@ router.get("/tip/height", async function (req: Request, res: Response) {
timestamp: 0n
};

const from = 0, limit = 20;
if (res.locals.backgroundUpdater.isHealthy({ from, limit })) {
// load the default page from cache
const cachedTip = await cacheService.get<{height: bigint, timestamp: bigint}>(CacheKeys.TIP_CURRENT);
if (cachedTip) {
tip = {
height: res.locals.backgroundUpdater.getData().indexData.tipInfo.metadata.best_block_height,
timestamp: res.locals.backgroundUpdater.getData().indexData.tipInfo.metadata.timestamp
height: cachedTip.height,
timestamp: cachedTip.timestamp
};
} else {
const client = createClient();
Expand All @@ -292,13 +273,11 @@ router.get("/tip/height", async function (req: Request, res: Response) {
timestamp: tipInfo?.metadata?.timestamp || 0n,
};
}
res.header("Cache-Control", cacheSettings.index);
res.json(tip);
});

router.get("/:height/header", async function (req: Request, res: Response) {
const { height } = req.params;
// Validate that height is a string representing a non-negative integer
if (typeof height !== "string" || !/^\d+$/.test(height)) {
res.status(400).json({ error: "Invalid block height parameter. Must be a non-negative integer." });
return;
Expand Down
Loading