Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
5 changes: 5 additions & 0 deletions .changeset/sad-planets-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ensindexer": minor
---

BREAKING: Removed ENSNODE_PUBLIC_URL, ENSADMIN_URL, PORT configuration variables. PORT is still overridable, and defaults to Ponder's default (of 42069) as before. Removes "ENSAdmin Loopback" behavior when accessing ENSIndexer at '/'.
1 change: 0 additions & 1 deletion .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ jobs:
RPC_URL_42161: ${{ secrets.ARBITRUM_RPC_URL || 'https://arbitrum.drpc.org' }}
RPC_URL_534352: ${{ secrets.SCROLL_RPC_URL || 'https://scroll.drpc.org' }}
ENSRAINBOW_URL: https://api.ensrainbow.io
ENSNODE_PUBLIC_URL: http://localhost:42069
ENSINDEXER_URL: http://localhost:42069
# healthcheck script env variables
HEALTH_CHECK_TIMEOUT: 60
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { useActiveConnection } from "@/hooks/active/use-active-connection";
import { useSelectedConnection } from "@/hooks/active/use-selected-connection";
import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param";
import { cn } from "@/lib/utils";
import type { ENSNamespaceId } from "@ensnode/datasources";
Expand All @@ -19,7 +21,7 @@ import {
} from "@ensnode/ensnode-sdk";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { useRecentRegistrations } from "./hooks";

/**
Expand Down Expand Up @@ -108,7 +110,7 @@ export function RecentRegistrations({
);
}

const { ensNodePublicUrl: ensNodeUrl, namespace: namespaceId } = ensIndexerConfig;
const { namespace: namespaceId } = ensIndexerConfig;

return (
<Card className="w-full">
Expand All @@ -118,20 +120,13 @@ export function RecentRegistrations({
</CardTitle>
</CardHeader>
<CardContent>
{isClient && (
<RegistrationsList
ensNodeUrl={ensNodeUrl}
namespaceId={namespaceId}
maxRecords={maxRecords}
/>
)}
{isClient && <RegistrationsList namespaceId={namespaceId} maxRecords={maxRecords} />}
</CardContent>
</Card>
);
}

interface RegistrationsListProps {
ensNodeUrl: URL;
namespaceId: ENSNamespaceId;
maxRecords: number;
}
Expand All @@ -142,9 +137,10 @@ interface RegistrationsListProps {
* @param ensNodeMetadata data about connected ENSNode instance necessary for fetching registrations
* @param ensNodeUrl URL of currently selected ENSNode instance
*/
function RegistrationsList({ ensNodeUrl, namespaceId, maxRecords }: RegistrationsListProps) {
function RegistrationsList({ namespaceId, maxRecords }: RegistrationsListProps) {
const { rawSelectedConnection } = useSelectedConnection();
const recentRegistrationsQuery = useRecentRegistrations({
ensNodeUrl,
ensNodeUrl: useMemo(() => new URL(rawSelectedConnection), [rawSelectedConnection]),
Comment thread
shrugs marked this conversation as resolved.
Outdated
namespaceId,
maxRecords,
});
Expand Down
19 changes: 2 additions & 17 deletions apps/ensindexer/.env.local.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# The port ENSIndexer listens on for HTTP requests.
# Optional. If this is not set, the default value is set to `DEFAULT_PORT` (42069).
# The port ENSIndexer serves its API on.
# Optional. If this is not set, the default value is Ponder's default of 42069.
PORT=42069

# RPC configuration
Expand Down Expand Up @@ -177,12 +177,6 @@ LABEL_SET_ID=subgraph
# and LABEL_SET_VERSION must be set to 0.
LABEL_SET_VERSION=0

# The ENSIndexer public service URL for handling external API requests
# Required. When the root route `/` of ENSIndexer receives a request, ENSIndexer redirects to
# the configured ENSADMIN_URL with an instruction for that ENSAdmin instance to connect back
# to this provided URL for querying state about the ENSNode instance.
ENSNODE_PUBLIC_URL=http://localhost:42069

# The "primary" ENSIndexer service URL
# Required. This must be an instance of ENSIndexer using either `ponder start`
# or `ponder dev`, and not `ponder serve`.
Expand All @@ -197,15 +191,6 @@ ENSNODE_PUBLIC_URL=http://localhost:42069
# `ponder dev` that is writing to the same ENSDb.
ENSINDEXER_URL=http://localhost:42069

# The ENSAdmin service URL
# Optional. When the root route `/` of ENSIndexer receives a request, ENSIndexer redirects
# to this provided ENSAdmin URL with an instruction for that ENSAdmin instance to connect
# back to the configured ENSNODE_PUBLIC_URL.
#
# If this is not set, DEFAULT_ENSADMIN_URL (https://admin.ensnode.io) will be used
# to provide easy access to an ENSAdmin UI.
ENSADMIN_URL=https://admin.ensnode.io

# A feature flag to enable/disable ENSIndexer's Subgraph Compatible Indexing Behavior
# Optional. If this is not set, the default value is set to `DEFAULT_SUBGRAPH_COMPAT` (false).
#
Expand Down
40 changes: 40 additions & 0 deletions apps/ensindexer/src/api/handlers/metadata-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import packageJson from "@/../package.json";

import { db, publicClients } from "ponder:api";
import config from "@/config";

import {
fetchEnsRainbowVersion,
fetchFirstBlockToIndexByChainId,
fetchPonderStatus,
fetchPrometheusMetrics,
} from "@/lib/ponder-metadata-provider";
import { ponderMetadata } from "@ensnode/ponder-metadata";

import { Hono } from "hono";

const app = new Hono();

app.get(
ponderMetadata({
app: {
name: packageJson.name,
version: packageJson.version,
},
env: {
PLUGINS: config.plugins.join(","),
DATABASE_SCHEMA: config.databaseSchemaName,
NAMESPACE: config.namespace,
},
db,
query: {
firstBlockToIndexByChainId: fetchFirstBlockToIndexByChainId,
prometheusMetrics: fetchPrometheusMetrics,
ensRainbowVersion: fetchEnsRainbowVersion,
ponderStatus: fetchPonderStatus,
},
publicClients,
}),
);

export default app;
82 changes: 82 additions & 0 deletions apps/ensindexer/src/api/handlers/subgraph-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { db, publicClients } from "ponder:api";
import schema from "ponder:schema";
import { Hono } from "hono";
import { createDocumentationMiddleware } from "ponder-enrich-gql-docs-middleware";

import config from "@/config";
import { makeSubgraphApiDocumentation } from "@/lib/api-documentation";
import { filterSchemaByPrefix } from "@/lib/filter-schema-by-prefix";
import { fixContentLengthMiddleware } from "@/lib/fix-content-length-middleware";
import { makePonderMetadataProvider } from "@/lib/ponder-metadata-provider";
import { buildGraphQLSchema, subgraphGraphQLMiddleware } from "@ensnode/ponder-subgraph";

import { makeDrizzle } from "@/api/lib/handlers/drizzle";

// generate a subgraph-specific subset of the schema
const subgraphSchema = filterSchemaByPrefix("subgraph_", schema);
// and a drizzle db object that accesses it
const subgaphDrizzle = makeDrizzle({
schema: subgraphSchema,
databaseUrl: config.databaseUrl,
databaseSchema: config.databaseSchemaName,
});

const app = new Hono();

// hotfix content length after documentation injection
app.use(fixContentLengthMiddleware);

// inject api documentation into graphql introspection requests
app.use(createDocumentationMiddleware(makeSubgraphApiDocumentation(), { path: "/subgraph" }));

// use our custom graphql middleware
app.use(
subgraphGraphQLMiddleware({
drizzle: subgaphDrizzle,
graphqlSchema: buildGraphQLSchema({
schema: subgraphSchema,
// provide PonderMetadataProvider to power `_meta` field
metadataProvider: makePonderMetadataProvider({ db, publicClients }),
// describes the polymorphic (interface) relationships in the schema
polymorphicConfig: {
types: {
DomainEvent: [
subgraphSchema.transfer,
subgraphSchema.newOwner,
subgraphSchema.newResolver,
subgraphSchema.newTTL,
subgraphSchema.wrappedTransfer,
subgraphSchema.nameWrapped,
subgraphSchema.nameUnwrapped,
subgraphSchema.fusesSet,
subgraphSchema.expiryExtended,
],
RegistrationEvent: [
subgraphSchema.nameRegistered,
subgraphSchema.nameRenewed,
subgraphSchema.nameTransferred,
],
ResolverEvent: [
subgraphSchema.addrChanged,
subgraphSchema.multicoinAddrChanged,
subgraphSchema.nameChanged,
subgraphSchema.abiChanged,
subgraphSchema.pubkeyChanged,
subgraphSchema.textChanged,
subgraphSchema.contenthashChanged,
subgraphSchema.interfaceChanged,
subgraphSchema.authorisationChanged,
subgraphSchema.versionChanged,
],
},
fields: {
"Domain.events": "DomainEvent",
"Registration.events": "RegistrationEvent",
"Resolver.events": "ResolverEvent",
},
},
}),
}),
);

export default app;
130 changes: 12 additions & 118 deletions apps/ensindexer/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
import packageJson from "@/../package.json";

import { db, publicClients } from "ponder:api";
import schema from "ponder:schema";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { createDocumentationMiddleware } from "ponder-enrich-gql-docs-middleware";

import { sdk } from "@/api/lib/tracing/instrumentation";
import config from "@/config";
import { makeSubgraphApiDocumentation } from "@/lib/api-documentation";
import { filterSchemaByPrefix } from "@/lib/filter-schema-by-prefix";
import { fixContentLengthMiddleware } from "@/lib/fix-content-length-middleware";
import {
fetchEnsRainbowVersion,
fetchFirstBlockToIndexByChainId,
fetchPonderStatus,
fetchPrometheusMetrics,
makePonderMetadataProvider,
} from "@/lib/ponder-metadata-provider";
import { ponderMetadata } from "@ensnode/ponder-metadata";
import { buildGraphQLSchema, subgraphGraphQLMiddleware } from "@ensnode/ponder-subgraph";

import ensNodeApi from "@/api/handlers/ensnode-api";
import { makeDrizzle } from "@/api/lib/handlers/drizzle";

// generate a subgraph-specific subset of the schema
const subgraphSchema = filterSchemaByPrefix("subgraph_", schema);
// and a drizzle db object that accesses it
const subgaphDrizzle = makeDrizzle({
schema: subgraphSchema,
databaseUrl: config.databaseUrl,
databaseSchema: config.databaseSchemaName,
});
import ensNodeApi from "./handlers/ensnode-api";
import metadataApi from "./handlers/metadata-api";
import subgraphApi from "./handlers/subgraph-api";

const app = new Hono();

Expand All @@ -44,103 +20,21 @@ app.use(async (ctx, next) => {
// use CORS middleware
app.use(cors({ origin: "*" }));

// use ENSNode Metadata API at /metadata
app.route("/metadata", metadataApi);

// use ENSNode HTTP API at /api
app.route("/api", ensNodeApi);

// use Subgraph GraphQL API at /subgraph
app.route("/subgraph", subgraphApi);

// log hono errors to console
app.onError((error, ctx) => {
console.error(error);
return ctx.text("Internal server error", 500);
});

// use root to redirect to the environment's ENSAdmin URL configured to connect back to the environment's ENSNode Public URL
app.use("/", async (ctx) => {
const ensAdminRedirectUrl = new URL(config.ensAdminUrl);
ensAdminRedirectUrl.searchParams.set("connection", config.ensNodePublicUrl.href);

return ctx.redirect(ensAdminRedirectUrl);
});

// use ENSNode middleware at /metadata
app.get(
"/metadata",
ponderMetadata({
app: {
name: packageJson.name,
version: packageJson.version,
},
env: {
PLUGINS: config.plugins.join(","),
DATABASE_SCHEMA: config.databaseSchemaName,
NAMESPACE: config.namespace,
},
db,
query: {
firstBlockToIndexByChainId: fetchFirstBlockToIndexByChainId,
prometheusMetrics: fetchPrometheusMetrics,
ensRainbowVersion: fetchEnsRainbowVersion,
ponderStatus: fetchPonderStatus,
},
publicClients,
}),
);

// use ENSNode HTTP API at /api
app.route("/api", ensNodeApi);

// at /subgraph
app.use(
"/subgraph",
// hotfix content length after documentation injection
fixContentLengthMiddleware,
// inject api documentation into graphql introspection requests
createDocumentationMiddleware(makeSubgraphApiDocumentation(), { path: "/subgraph" }),
// use our custom graphql middleware
subgraphGraphQLMiddleware({
drizzle: subgaphDrizzle,
graphqlSchema: buildGraphQLSchema({
schema: subgraphSchema,
// provide PonderMetadataProvider to power `_meta` field
metadataProvider: makePonderMetadataProvider({ db, publicClients }),
// describes the polymorphic (interface) relationships in the schema
polymorphicConfig: {
types: {
DomainEvent: [
subgraphSchema.transfer,
subgraphSchema.newOwner,
subgraphSchema.newResolver,
subgraphSchema.newTTL,
subgraphSchema.wrappedTransfer,
subgraphSchema.nameWrapped,
subgraphSchema.nameUnwrapped,
subgraphSchema.fusesSet,
subgraphSchema.expiryExtended,
],
RegistrationEvent: [
subgraphSchema.nameRegistered,
subgraphSchema.nameRenewed,
subgraphSchema.nameTransferred,
],
ResolverEvent: [
subgraphSchema.addrChanged,
subgraphSchema.multicoinAddrChanged,
subgraphSchema.nameChanged,
subgraphSchema.abiChanged,
subgraphSchema.pubkeyChanged,
subgraphSchema.textChanged,
subgraphSchema.contenthashChanged,
subgraphSchema.interfaceChanged,
subgraphSchema.authorisationChanged,
subgraphSchema.versionChanged,
],
},
fields: {
"Domain.events": "DomainEvent",
"Registration.events": "RegistrationEvent",
"Resolver.events": "ResolverEvent",
},
},
}),
}),
);

// start ENSNode API OpenTelemetry SDK
sdk.start();

Expand Down
Loading