mirror of
https://github.com/logos-blockchain/logos-blockchain-block-explorer-template.git
synced 2026-04-03 01:13:12 +00:00
Lints and prettify.
This commit is contained in:
parent
98c56fadb7
commit
9b56a4c8d8
2
.github/workflows/publish-docker-image.yml
vendored
2
.github/workflows/publish-docker-image.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
|
||||
@ -24,13 +24,15 @@ async def list_blocks(
|
||||
blocks, total_count = await request.app.state.block_repository.get_paginated(page, page_size, fork=fork)
|
||||
total_pages = (total_count + page_size - 1) // page_size # ceiling division
|
||||
|
||||
return JSONResponse({
|
||||
"blocks": [BlockRead.from_block(block).model_dump(mode="json") for block in blocks],
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_count": total_count,
|
||||
"total_pages": total_pages,
|
||||
})
|
||||
return JSONResponse(
|
||||
{
|
||||
"blocks": [BlockRead.from_block(block).model_dump(mode="json") for block in blocks],
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_count": total_count,
|
||||
"total_pages": total_pages,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def _get_blocks_stream_serialized(
|
||||
|
||||
@ -47,18 +47,18 @@ async def list_transactions(
|
||||
page_size: int = Query(10, ge=1, le=100, alias="page-size"),
|
||||
fork: int = Query(...),
|
||||
) -> Response:
|
||||
transactions, total_count = await request.app.state.transaction_repository.get_paginated(
|
||||
page, page_size, fork=fork
|
||||
)
|
||||
transactions, total_count = await request.app.state.transaction_repository.get_paginated(page, page_size, fork=fork)
|
||||
total_pages = (total_count + page_size - 1) // page_size
|
||||
|
||||
return JSONResponse({
|
||||
"transactions": [TransactionRead.from_transaction(tx).model_dump(mode="json") for tx in transactions],
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_count": total_count,
|
||||
"total_pages": total_pages,
|
||||
})
|
||||
return JSONResponse(
|
||||
{
|
||||
"transactions": [TransactionRead.from_transaction(tx).model_dump(mode="json") for tx in transactions],
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"total_count": total_count,
|
||||
"total_pages": total_pages,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def get(request: NBERequest, transaction_hash: str, fork: int = Query(...)) -> Response:
|
||||
|
||||
@ -3,16 +3,13 @@ from asyncio import sleep
|
||||
from typing import AsyncIterator, Dict, List
|
||||
|
||||
from rusty_results import Empty, Option, Some
|
||||
from sqlalchemy import Result, Select
|
||||
from sqlalchemy import Result, Select, func as sa_func
|
||||
from sqlalchemy.orm import aliased
|
||||
from sqlmodel import select
|
||||
|
||||
from sqlalchemy import func as sa_func
|
||||
|
||||
from db.clients import DbClient
|
||||
from models.block import Block
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -25,33 +22,18 @@ def chain_block_ids_cte(*, fork: int):
|
||||
from fork 0 at height 50, the CTE returns fork 1 blocks (50+) AND the
|
||||
ancestor fork 0 blocks (0–49).
|
||||
"""
|
||||
tip_hash = (
|
||||
select(Block.hash)
|
||||
.where(Block.fork == fork)
|
||||
.order_by(Block.height.desc())
|
||||
.limit(1)
|
||||
.scalar_subquery()
|
||||
)
|
||||
tip_hash = select(Block.hash).where(Block.fork == fork).order_by(Block.height.desc()).limit(1).scalar_subquery()
|
||||
|
||||
base = select(Block.id, Block.hash, Block.parent_block).where(
|
||||
Block.hash == tip_hash
|
||||
)
|
||||
base = select(Block.id, Block.hash, Block.parent_block).where(Block.hash == tip_hash)
|
||||
cte = base.cte(name="chain", recursive=True)
|
||||
|
||||
recursive = select(Block.id, Block.hash, Block.parent_block).where(
|
||||
Block.hash == cte.c.parent_block
|
||||
)
|
||||
recursive = select(Block.id, Block.hash, Block.parent_block).where(Block.hash == cte.c.parent_block)
|
||||
return cte.union_all(recursive)
|
||||
|
||||
|
||||
def get_latest_statement(limit: int, *, fork: int, output_ascending: bool = True) -> Select:
|
||||
chain = chain_block_ids_cte(fork=fork)
|
||||
base = (
|
||||
select(Block)
|
||||
.join(chain, Block.id == chain.c.id)
|
||||
.order_by(Block.height.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
base = select(Block).join(chain, Block.id == chain.c.id).order_by(Block.height.desc()).limit(limit)
|
||||
if not output_ascending:
|
||||
return base
|
||||
|
||||
@ -191,9 +173,7 @@ class BlockRepository:
|
||||
parent_hashes_in_batch = {b.parent_block for b in blocks_to_add}
|
||||
parents_with_children: set[bytes] = set()
|
||||
if parent_hashes_in_batch:
|
||||
stmt = select(Block.parent_block).where(
|
||||
Block.parent_block.in_(parent_hashes_in_batch)
|
||||
).distinct()
|
||||
stmt = select(Block.parent_block).where(Block.parent_block.in_(parent_hashes_in_batch)).distinct()
|
||||
for ph in session.exec(stmt).all():
|
||||
parents_with_children.add(ph)
|
||||
|
||||
@ -310,9 +290,7 @@ class BlockRepository:
|
||||
|
||||
while True:
|
||||
statement = (
|
||||
select(Block)
|
||||
.where(Block.fork == fork, Block.height >= height_cursor)
|
||||
.order_by(Block.height.asc())
|
||||
select(Block).where(Block.fork == fork, Block.height >= height_cursor).order_by(Block.height.asc())
|
||||
)
|
||||
|
||||
with self.client.session() as session:
|
||||
|
||||
@ -74,7 +74,9 @@ class TransactionRepository:
|
||||
if limit == 0:
|
||||
return []
|
||||
|
||||
statement = get_latest_statement(limit, fork=fork, output_ascending=ascending, preload_relationships=preload_relationships)
|
||||
statement = get_latest_statement(
|
||||
limit, fork=fork, output_ascending=ascending, preload_relationships=preload_relationships
|
||||
)
|
||||
|
||||
with self.client.session() as session:
|
||||
results: Result[Transaction] = session.exec(statement)
|
||||
|
||||
@ -83,5 +83,9 @@ class SignedTransactionSerializer(NbeSerializer, FromRandom):
|
||||
n = len(transaction.operations_contents)
|
||||
operations_proofs = [OperationProofSerializer.from_random() for _ in range(n)]
|
||||
return cls.model_validate(
|
||||
{"mantle_tx": transaction, "ops_proofs": operations_proofs, "ledger_tx_proof": Groth16ProofSerializer.from_random()}
|
||||
{
|
||||
"mantle_tx": transaction,
|
||||
"ops_proofs": operations_proofs,
|
||||
"ledger_tx_proof": Groth16ProofSerializer.from_random(),
|
||||
}
|
||||
)
|
||||
|
||||
@ -155,7 +155,9 @@ async def subscribe_to_new_blocks(app: "NBE"):
|
||||
# Re-check if parent now exists after backfill
|
||||
parent_exists = (await app.state.block_repository.get_by_hash(block.parent_block)).is_some
|
||||
if not parent_exists:
|
||||
logger.warning(f"Parent block still not found after backfill for block at slot {block.slot}. Skipping block.")
|
||||
logger.warning(
|
||||
f"Parent block still not found after backfill for block at slot {block.slot}. Skipping block."
|
||||
)
|
||||
continue
|
||||
|
||||
# Capture values before create() detaches the block from the session
|
||||
|
||||
@ -9,11 +9,11 @@ import TransactionDetailPage from './pages/TransactionDetail.js';
|
||||
|
||||
const ROOT = document.getElementById('app');
|
||||
|
||||
// Detect the Base Path from the HTML <base> tag.
|
||||
// If the tag is missing or equals "__BASE_PATH__", default to root "/".
|
||||
// Detect the Base Path from the HTML <base> tag.
|
||||
// If the tag is missing or equals "__BASE_PATH__", default to root "/".
|
||||
const BASE_PATH = (() => {
|
||||
const baseHref = document.querySelector('base')?.getAttribute('href');
|
||||
if (!baseHref || baseHref === "__BASE_PATH__") return '/';
|
||||
if (!baseHref || baseHref === '__BASE_PATH__') return '/';
|
||||
|
||||
return baseHref.endsWith('/') ? baseHref : `${baseHref}/`;
|
||||
})();
|
||||
|
||||
@ -210,7 +210,12 @@ export default function BlocksTable({ live, onDisableLive }) {
|
||||
h(
|
||||
'div',
|
||||
{ class: 'card-header', style: 'display:flex; justify-content:space-between; align-items:center;' },
|
||||
h('div', null, h('strong', null, 'Blocks '), !live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount))),
|
||||
h(
|
||||
'div',
|
||||
null,
|
||||
h('strong', null, 'Blocks '),
|
||||
!live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount)),
|
||||
),
|
||||
),
|
||||
h(
|
||||
'div',
|
||||
|
||||
@ -262,7 +262,12 @@ export default function TransactionsTable({ live, onDisableLive }) {
|
||||
h(
|
||||
'div',
|
||||
{ class: 'card-header', style: 'display:flex; justify-content:space-between; align-items:center;' },
|
||||
h('div', null, h('strong', null, 'Transactions '), !live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount))),
|
||||
h(
|
||||
'div',
|
||||
null,
|
||||
h('strong', null, 'Transactions '),
|
||||
!live && totalCount > 0 && h('span', { class: 'pill' }, String(totalCount)),
|
||||
),
|
||||
),
|
||||
h(
|
||||
'div',
|
||||
@ -310,7 +315,11 @@ export default function TransactionsTable({ live, onDisableLive }) {
|
||||
h(
|
||||
'span',
|
||||
{ style: 'color:var(--muted); font-size:13px;' },
|
||||
live ? 'Streaming live transactions...' : totalPages > 0 ? `Page ${page + 1} of ${totalPages}` : 'No transactions',
|
||||
live
|
||||
? 'Streaming live transactions...'
|
||||
: totalPages > 0
|
||||
? `Page ${page + 1} of ${totalPages}`
|
||||
: 'No transactions',
|
||||
),
|
||||
h(
|
||||
'button',
|
||||
|
||||
@ -15,8 +15,7 @@ const TRANSACTIONS_STREAM = joinUrl(API_PREFIX, 'transactions/stream');
|
||||
const FORK_CHOICE = joinUrl(API_PREFIX, 'fork-choice');
|
||||
|
||||
const BLOCK_DETAIL_BY_HASH = (hash) => joinUrl(API_PREFIX, 'blocks', encodeHash(hash));
|
||||
const BLOCKS_STREAM = (fork) =>
|
||||
`${joinUrl(API_PREFIX, 'blocks/stream')}?fork=${encodeURIComponent(fork)}`;
|
||||
const BLOCKS_STREAM = (fork) => `${joinUrl(API_PREFIX, 'blocks/stream')}?fork=${encodeURIComponent(fork)}`;
|
||||
const BLOCKS_LIST = (page, pageSize, fork) =>
|
||||
`${joinUrl(API_PREFIX, 'blocks/list')}?page=${encodeURIComponent(page)}&page-size=${encodeURIComponent(pageSize)}&fork=${encodeURIComponent(fork)}`;
|
||||
|
||||
|
||||
@ -40,7 +40,9 @@ export default function HomeView() {
|
||||
live ? 'LIVE \u2022' : 'LIVE',
|
||||
),
|
||||
),
|
||||
h('section', { class: 'two-columns twocol' },
|
||||
h(
|
||||
'section',
|
||||
{ class: 'two-columns twocol' },
|
||||
h(BlocksTable, { live, onDisableLive: () => setLive(false) }),
|
||||
h(TransactionsTable, { live, onDisableLive: () => setLive(false) }),
|
||||
),
|
||||
|
||||
@ -319,10 +319,7 @@ function tryDecodeUtf8Hex(hex) {
|
||||
}
|
||||
|
||||
/** Human-friendly label for a content field key */
|
||||
const fieldLabel = (key) =>
|
||||
key
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
const fieldLabel = (key) => key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
|
||||
/** Render the value of a single content field */
|
||||
function FieldValue({ value }) {
|
||||
@ -390,10 +387,16 @@ function OperationProof({ proof }) {
|
||||
entries.length > 0 &&
|
||||
h(
|
||||
'div',
|
||||
{ style: 'margin-top:4px; display:grid; grid-template-columns:auto 1fr; gap:4px 12px; align-items:baseline;' },
|
||||
{
|
||||
style: 'margin-top:4px; display:grid; grid-template-columns:auto 1fr; gap:4px 12px; align-items:baseline;',
|
||||
},
|
||||
...entries.flatMap(([key, value]) => [
|
||||
h('span', { style: 'color:var(--muted); font-size:12px; white-space:nowrap;' }, fieldLabel(key)),
|
||||
h('span', { class: 'mono', style: 'font-size:12px; overflow-wrap:anywhere; word-break:break-all;' }, renderBytes(value)),
|
||||
h(
|
||||
'span',
|
||||
{ class: 'mono', style: 'font-size:12px; overflow-wrap:anywhere; word-break:break-all;' },
|
||||
renderBytes(value),
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
@ -562,6 +565,13 @@ export default function TransactionDetail({ parameters }) {
|
||||
!tx && !err && h('p', null, 'Loading…'),
|
||||
|
||||
// Success
|
||||
tx && h(Fragment, null, h(Summary, { tx }), h(Operations, { operations: tx.operations }), h(Ledger, { ledger: tx.ledger })),
|
||||
tx &&
|
||||
h(
|
||||
Fragment,
|
||||
null,
|
||||
h(Summary, { tx }),
|
||||
h(Operations, { operations: tx.operations }),
|
||||
h(Ledger, { ledger: tx.ledger }),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -161,6 +161,16 @@ button.pill:disabled {
|
||||
}
|
||||
|
||||
@keyframes live-pulse {
|
||||
0%, 100% { box-shadow: 0 0 4px #ff4444, 0 0 8px #ff4444; }
|
||||
50% { box-shadow: 0 0 8px #ff4444, 0 0 16px #ff4444, 0 0 24px #ff6666; }
|
||||
0%,
|
||||
100% {
|
||||
box-shadow:
|
||||
0 0 4px #ff4444,
|
||||
0 0 8px #ff4444;
|
||||
}
|
||||
50% {
|
||||
box-shadow:
|
||||
0 0 8px #ff4444,
|
||||
0 0 16px #ff4444,
|
||||
0 0 24px #ff6666;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,6 +259,7 @@ def test_batch_with_fork_and_chain(client, repo):
|
||||
def test_fork_choice_empty_db(client, repo):
|
||||
"""Fork choice returns Empty when no blocks exist."""
|
||||
from rusty_results import Empty
|
||||
|
||||
result = asyncio.run(repo.get_fork_choice())
|
||||
assert isinstance(result, Empty)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user