Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 dashboard/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ RUN chown 1000:1000 /app/dcrpulse
USER 1000

# Expose port
EXPOSE 8080
EXPOSE 8735

# Run the application
CMD ["./dcrpulse"]
Expand Down
2 changes: 1 addition & 1 deletion dashboard/web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dcrpulse-web",
"private": true,
"version": "1.0.0",
"version": "1.0.2",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
74 changes: 49 additions & 25 deletions dashboard/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Header } from './components/Header';
import { Footer } from './components/Footer';
import { NodeDashboard } from './pages/NodeDashboard';
import { WalletDashboard } from './pages/WalletDashboard';
import { ExplorerLanding } from './pages/ExplorerLanding';
Expand All @@ -13,41 +14,64 @@ import { TransactionDetail } from './pages/TransactionDetail';
import { AddressView } from './pages/AddressView';
import { MempoolView } from './pages/MempoolView';
import { GovernanceDashboard } from './pages/GovernanceDashboard';
import { getDashboardData } from './services/api';
import { getDashboardData, getWalletStatus } from './services/api';

function App() {
function AppContent() {
const location = useLocation();
const [nodeVersion, setNodeVersion] = useState<string>('');
const [walletVersion, setWalletVersion] = useState<string>('');
const [lastUpdate, setLastUpdate] = useState<string>('');

// Fetch node version for header
// Fetch versions for header and footer
useEffect(() => {
const fetchVersion = async () => {
const fetchVersions = async () => {
try {
const data = await getDashboardData();
setNodeVersion(data.nodeStatus?.version || '');
// Fetch node version and last update
const dashboardData = await getDashboardData();
setNodeVersion(dashboardData.nodeStatus?.version || '');
if (dashboardData.lastUpdate) {
setLastUpdate(new Date(dashboardData.lastUpdate).toLocaleString());
}

// Fetch wallet version
try {
const walletStatus = await getWalletStatus();
setWalletVersion(walletStatus.version || '');
} catch (walletErr) {
// Wallet might not be available, that's ok
console.debug('Wallet version not available:', walletErr);
}
} catch (err) {
console.error('Error fetching node version:', err);
console.error('Error fetching versions:', err);
}
};
fetchVersion();
}, []);
fetchVersions();
}, [location.pathname]);

return (
<BrowserRouter>
<div className="min-h-screen bg-background p-6">
<div className="max-w-7xl mx-auto space-y-6">
<Header nodeVersion={nodeVersion} />
<Routes>
<Route path="/" element={<NodeDashboard />} />
<Route path="/wallet" element={<WalletDashboard />} />
<Route path="/explorer" element={<ExplorerLanding />} />
<Route path="/explorer/block/:heightOrHash" element={<BlockDetail />} />
<Route path="/explorer/tx/:txhash" element={<TransactionDetail />} />
<Route path="/explorer/address/:address" element={<AddressView />} />
<Route path="/explorer/mempool" element={<MempoolView />} />
<Route path="/governance" element={<GovernanceDashboard />} />
</Routes>
</div>
<div className="min-h-screen bg-background p-6">
<div className="max-w-7xl mx-auto space-y-6">
<Header nodeVersion={nodeVersion} />
<Routes>
<Route path="/" element={<NodeDashboard />} />
<Route path="/wallet" element={<WalletDashboard />} />
<Route path="/explorer" element={<ExplorerLanding />} />
<Route path="/explorer/block/:heightOrHash" element={<BlockDetail />} />
<Route path="/explorer/tx/:txhash" element={<TransactionDetail />} />
<Route path="/explorer/address/:address" element={<AddressView />} />
<Route path="/explorer/mempool" element={<MempoolView />} />
<Route path="/governance" element={<GovernanceDashboard />} />
</Routes>
<Footer dcrdVersion={nodeVersion} dcrwalletVersion={walletVersion} lastUpdate={lastUpdate} />
</div>
</div>
);
}

function App() {
return (
<BrowserRouter>
<AppContent />
</BrowserRouter>
);
}
Expand Down
63 changes: 63 additions & 0 deletions dashboard/web/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) 2015-2025 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

import packageJson from '../../package.json';

interface FooterProps {
dcrdVersion?: string;
dcrwalletVersion?: string;
lastUpdate?: string;
}

export const Footer = ({ dcrdVersion, dcrwalletVersion, lastUpdate }: FooterProps) => {
const dashboardVersion = packageJson.version;

return (
<footer className="mt-8">
<div className="flex flex-wrap items-center justify-center gap-2 text-sm text-muted-foreground">
{lastUpdate && (
<>
<span>Last updated: {lastUpdate}</span>
<span className="text-border">•</span>
</>
)}
<a
href="https://github.com/karamble/dcrpulse"
target="_blank"
rel="noopener noreferrer"
className="hover:text-primary transition-colors"
>
Decred Pulse v{dashboardVersion}
</a>
{dcrdVersion && (
<>
<span className="text-border">•</span>
<a
href="https://github.com/decred/dcrd"
target="_blank"
rel="noopener noreferrer"
className="hover:text-primary transition-colors"
>
dcrd v{dcrdVersion}
</a>
</>
)}
{dcrwalletVersion && (
<>
<span className="text-border">•</span>
<a
href="https://github.com/decred/dcrwallet"
target="_blank"
rel="noopener noreferrer"
className="hover:text-primary transition-colors"
>
dcrwallet v{dcrwalletVersion}
</a>
</>
)}
</div>
</footer>
);
};

7 changes: 0 additions & 7 deletions dashboard/web/src/pages/NodeDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,6 @@ export const NodeDashboard = () => {
<MempoolActivity data={data?.mempoolInfo} />
<PeersList peers={data?.peers} />
</div>

{/* Last Update */}
{data && (
<div className="text-center text-sm text-muted-foreground animate-fade-in">
Last updated: {new Date(data.lastUpdate).toLocaleString()}
</div>
)}
</div>
);
};
Expand Down
7 changes: 0 additions & 7 deletions dashboard/web/src/pages/WalletDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -392,13 +392,6 @@ export const WalletDashboard = () => {
<AddressBookmarksCard />
</div>
)}

{/* Last Update */}
{data && (
<div className="text-center text-sm text-muted-foreground animate-fade-in">
Last updated: {new Date(data.lastUpdate).toLocaleString()}
</div>
)}
</>
)}

Expand Down
2 changes: 1 addition & 1 deletion dcrd/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RUN apk add --no-cache git openssl
WORKDIR /go/src/github.com/decred

# Clone the dcrd repository
ARG DCRD_VERSION=release-v2.1.0
ARG DCRD_VERSION=release-v2.1.2
RUN git clone --depth 1 --branch ${DCRD_VERSION} https://github.com/decred/dcrd.git

# Build dcrd
Expand Down
23 changes: 5 additions & 18 deletions dcrpulse-umbrel/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ services:
app_proxy:
environment:
APP_HOST: dcrpulse_dashboard_1
APP_PORT: 8080
APP_PORT: 8735

dcrd:
image: ghcr.io/karamble/dcrpulse-dcrd:2.1.0@sha256:63dde66bb5c06107192e12bc7431ef7f8f25be49c8be29b070aba92e691d4e24
image: ghcr.io/karamble/dcrpulse-dcrd:2.1.2@sha256:63dde66bb5c06107192e12bc7431ef7f8f25be49c8be29b070aba92e691d4e24
user: "1000:1000"
restart: on-failure
stop_grace_period: 5m
Expand All @@ -26,9 +26,6 @@ services:
--rpclisten=0.0.0.0:9109
--rpccert=/app-data/dcrd/rpc.cert
--rpckey=/app-data/dcrd/rpc.key
networks:
- default
- dcrpulse_internal
healthcheck:
test: ["CMD", "sh", "-c", "curl -k -u ${APP_SEED}:${APP_SEED} -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"1.0\",\"method\":\"getinfo\",\"params\":[],\"id\":1}' https://127.0.0.1:9109 || exit 1"]
interval: 30s
Expand All @@ -37,7 +34,7 @@ services:
start_period: 120s

dcrwallet:
image: ghcr.io/karamble/dcrpulse-dcrwallet:2.1.0@sha256:32a2336a07f72627e1ba82177f72cca20671c808606c08325b0cdda5c585a386
image: ghcr.io/karamble/dcrpulse-dcrwallet:2.1.2@sha256:32a2336a07f72627e1ba82177f72cca20671c808606c08325b0cdda5c585a386
user: "1000:1000"
restart: on-failure
stop_grace_period: 1m
Expand All @@ -57,9 +54,6 @@ services:
depends_on:
dcrd:
condition: service_healthy
networks:
- default
- dcrpulse_internal
healthcheck:
test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 9110 && nc -z 127.0.0.1 9111 || exit 1"]
interval: 10s
Expand All @@ -68,13 +62,13 @@ services:
start_period: 10s

dashboard:
image: ghcr.io/karamble/dcrpulse-dashboard:1.0.0@sha256:c7b0bd714e2dac2a3a5ae73cd6c27b2f8c595370e3377672cb6ee802a8fc5981
image: ghcr.io/karamble/dcrpulse-dashboard:1.0.2@sha256:c7b0bd714e2dac2a3a5ae73cd6c27b2f8c595370e3377672cb6ee802a8fc5981
user: "1000:1000"
restart: on-failure
volumes:
- ${APP_DATA_DIR}/data:/app-data:ro
environment:
PORT: 8080
PORT: 8735
DCRD_RPC_HOST: dcrd
DCRD_RPC_PORT: 9109
DCRD_RPC_USER: ${APP_SEED}
Expand All @@ -91,10 +85,3 @@ services:
condition: service_healthy
dcrwallet:
condition: service_healthy
networks:
- default
- dcrpulse_internal

networks:
dcrpulse_internal:
internal: true
26 changes: 7 additions & 19 deletions dcrpulse-umbrel/umbrel-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,27 @@ manifestVersion: 1
id: dcrpulse
name: Decred Pulse
tagline: Full Decred node with watch-only wallet and database-free block explorer
category: finance
version: "1.0.0"
port: 8080
category: crypto
version: "1.0.2"
port: 8735
icon: ""
description: >-
description: |-
Decred Pulse is a comprehensive dashboard for running a full Decred node.


It includes:

- Full dcrd blockchain node with transaction indexing

- dcrwallet with watch-only wallet support

- Block explorer with mempool and tspends monitoring

- Watch-Only Wallet management with xpub import support


Perfect for Decred node and wallet monitoring, exploring the blockchain, or monitoring Decred network activity.

Features:

- Full Decred node (dcrd v2.1.0)

- Wallet support (dcrwallet v2.1.0)

- Full Decred node (dcrd v2.1.2)
- Wallet support (dcrwallet v2.1.2)
- Modern block explorer

- Mempool monitoring

- Watch-only wallet with xpub import


developer: Decred
website: https://decred.org
Expand All @@ -47,5 +35,5 @@ releaseNotes: ""
path: ""
defaultUsername: ""
defaultPassword: ""
submission: https://github.com/getumbrel/umbrel-apps/pull/TBD
submission: https://github.com/getumbrel/umbrel-apps/pull/4121

2 changes: 1 addition & 1 deletion dcrwallet/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RUN apk add --no-cache git openssl
WORKDIR /go/src/github.com/decred

# Clone the dcrwallet repository
ARG DCRWALLET_VERSION=release-v2.1.0
ARG DCRWALLET_VERSION=release-v2.1.2
RUN git clone --depth 1 --branch ${DCRWALLET_VERSION} https://github.com/decred/dcrwallet.git

# Build dcrwallet
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
build:
context: ./dcrd
args:
DCRD_VERSION: ${DCRD_VERSION:-release-v2.1.0}
DCRD_VERSION: ${DCRD_VERSION:-release-v2.1.2}
container_name: dcrpulse-dcrd
ports:
- "9108:9108"
Expand Down Expand Up @@ -41,7 +41,7 @@ services:
build:
context: ./dcrwallet
args:
DCRWALLET_VERSION: ${DCRWALLET_VERSION:-release-v2.1.0}
DCRWALLET_VERSION: ${DCRWALLET_VERSION:-release-v2.1.2}
container_name: dcrpulse-dcrwallet
ports:
- "9110:9110"
Expand Down