Skip to content
Draft
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
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.git
.next
node_modules
npm-debug.log*
.env*.local
/tmp
*.db
30 changes: 19 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
FROM oven/bun:1 AS deps
FROM node:20-alpine AS deps
WORKDIR /app
COPY ui/package.json ui/bun.lock ./
RUN bun install --frozen-lockfile
# better-sqlite3 requires native compilation
RUN apk add --no-cache python3 make g++
COPY package.json package-lock.json ./
RUN npm ci

FROM oven/bun:1 AS builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY ./ui .
COPY . .

ENV NEXT_TELEMETRY_DISABLED=1

RUN bun run build
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
Expand All @@ -21,12 +23,18 @@ ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

RUN mkdir .next
RUN chown nextjs:nodejs .next
RUN mkdir -p .next /data
RUN chown nextjs:nodejs .next /data

# Full node_modules (not standalone) so indexer.mjs can resolve viem + better-sqlite3
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/scripts ./scripts
COPY --from=builder /app/docker/migrations ./docker/migrations

RUN chown -R nextjs:nodejs node_modules && chmod +x scripts/start.sh

USER nextjs

Expand All @@ -35,4 +43,4 @@ EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]
CMD ["sh", "scripts/start.sh"]
65 changes: 65 additions & 0 deletions docker/migrations/0001_init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
-- vibescan indexer schema (ported from crates/vibenet/explorer).
-- Everything derivable from RPC stays in the node; the only persisted state
-- is the cursor + the address-activity join index that plain JSON-RPC cannot
-- serve efficiently.

CREATE TABLE IF NOT EXISTS cursor (
id INTEGER PRIMARY KEY CHECK (id = 0),
last_indexed_block INTEGER NOT NULL,
last_indexed_hash TEXT NOT NULL,
updated_at INTEGER NOT NULL
);

CREATE TABLE IF NOT EXISTS blocks (
number INTEGER PRIMARY KEY,
hash TEXT NOT NULL UNIQUE,
timestamp INTEGER NOT NULL,
miner TEXT NOT NULL,
tx_count INTEGER NOT NULL,
gas_used INTEGER NOT NULL,
gas_limit INTEGER NOT NULL,
base_fee TEXT
);

CREATE TABLE IF NOT EXISTS txs (
hash TEXT PRIMARY KEY,
block_num INTEGER NOT NULL,
tx_index INTEGER NOT NULL,
from_addr TEXT NOT NULL,
to_addr TEXT,
value TEXT NOT NULL,
status INTEGER NOT NULL,
created TEXT
);
CREATE INDEX IF NOT EXISTS idx_txs_block_num ON txs (block_num DESC, tx_index DESC);

-- address -> activity feed. role values:
-- 0 = sender, 1 = recipient, 2 = creator
-- 3 = erc20/721 log from, 4 = erc20/721 log to
CREATE TABLE IF NOT EXISTS address_activity (
address TEXT NOT NULL,
block_num INTEGER NOT NULL,
tx_index INTEGER NOT NULL,
log_index INTEGER NOT NULL DEFAULT -1,
tx_hash TEXT NOT NULL,
role INTEGER NOT NULL,
token TEXT,
PRIMARY KEY (address, block_num, tx_index, log_index, role)
);
CREATE INDEX IF NOT EXISTS idx_activity_addr_block
ON address_activity (address, block_num DESC, tx_index DESC, log_index DESC);

CREATE TABLE IF NOT EXISTS addresses (
address TEXT PRIMARY KEY
);

CREATE TABLE IF NOT EXISTS explorer_stats (
id INTEGER PRIMARY KEY CHECK (id = 0),
blocks INTEGER NOT NULL,
txs INTEGER NOT NULL,
addresses INTEGER NOT NULL
);

INSERT INTO explorer_stats (id, blocks, txs, addresses)
VALUES (0, 0, 0, 0)
ON CONFLICT(id) DO NOTHING;
12 changes: 12 additions & 0 deletions docker/vibenet-env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# base/ui vibenet env — UI-specific config only.
# Infra secrets (chain IDs, faucet key, etc.) live in base/base etc/vibenet/vibenet-env.
# This file is passed as a second --env-file to docker compose and merges with it.

# === Explorer ===
VIBESCAN_DB_PATH=/data/vibescan.db
VIBESCAN_START_BLOCK=0
VIBESCAN_BACKFILL_CONCURRENCY=16

# === Optional: surfaced in explorer footer ===
# VIBESCAN_PUBLIC_RPC_URL=https://rpc.vibes.base.org
# VIBESCAN_PUBLIC_FAUCET_URL=https://faucet.vibes.base.org
3 changes: 2 additions & 1 deletion next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
output: "standalone",
// better-sqlite3 is a native module; exclude it from webpack bundling
serverExternalPackages: ["better-sqlite3"],
};

export default nextConfig;
Loading
Loading