diff --git a/packages/stream-metadata/.env.localhost b/packages/stream-metadata/.env.local.sample similarity index 100% rename from packages/stream-metadata/.env.localhost rename to packages/stream-metadata/.env.local.sample diff --git a/packages/stream-metadata/.prettierrc.cjs b/packages/stream-metadata/.prettierrc.cjs new file mode 100644 index 000000000..0bd0b4deb --- /dev/null +++ b/packages/stream-metadata/.prettierrc.cjs @@ -0,0 +1,7 @@ +/** + * @see https://prettier.io/docs/en/configuration.html + * @type {import("prettier").Config} + */ +module.exports = { + ...require("@river-build/prettier-config"), +}; diff --git a/packages/stream-metadata/README.md b/packages/stream-metadata/README.md index d91379e4c..5f0e52787 100644 --- a/packages/stream-metadata/README.md +++ b/packages/stream-metadata/README.md @@ -18,5 +18,6 @@ Start the river image service: # if the dependencies are not present, run: # .//scripts/start_dev.sh +cp .env.local.sample .env.local yarn dev ``` diff --git a/packages/stream-metadata/package.json b/packages/stream-metadata/package.json index eb42cc84b..b00388875 100644 --- a/packages/stream-metadata/package.json +++ b/packages/stream-metadata/package.json @@ -8,25 +8,29 @@ "scripts": { "build": "node ./esbuild.config.mjs", "dev": "yarn build && NODE_ENV=development node --experimental-detect-module ./dist/node_esbuild.cjs", - "prettier": "prettier --config ../../packages/prettier-config/config.js --ignore-path .prettierignore .", + "prettier": "prettier --config ../prettier-config/config.js --ignore-path .prettierignore .", "start": "NODE_ENV=production node --experimental-detect-module ./dist/node_esbuild.cjs" }, "dependencies": { "@bufbuild/buf": "^1.32.0", "@bufbuild/protobuf": "^1.9.0", + "@connectrpc/connect": "^1.4.0", "@connectrpc/connect-web": "^1.4.0", "@fastify/cors": "^9.0.1", "@river-build/generated": "workspace:^", "@river-build/proto": "workspace:^", "@river-build/sdk": "workspace:^", + "@river-build/web3": "workspace:^", "dotenv": "^16.4.5", "ethers": "^5.7.2", "fastify": "^4.28.1", "magic-bytes.js": "^1.10.0", - "viem": "^1.18.2" + "zod": "^3.21.4" }, "devDependencies": { + "@river-build/prettier-config": "workspace:^", "@types/node": "^20.5.0", + "@typescript-eslint/parser": "^7.14.1", "esbuild": "^0.21.5", "rollup": "^4.18.1", "ts-node": "^10.9.1", diff --git a/packages/stream-metadata/src/contracts.ts b/packages/stream-metadata/src/contracts.ts deleted file mode 100644 index 842c4bac9..000000000 --- a/packages/stream-metadata/src/contracts.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Address } from './types' -import NodeRegistryAbi from '@river-build/generated/dev/abis/NodeRegistry.abi' -import StreamRegistryAbi from '@river-build/generated/dev/abis/StreamRegistry.abi' -import { config } from './environment' - -export function getNodeRegistryAbi() { - return NodeRegistryAbi -} - -export function getStreamRegistryAbi() { - return StreamRegistryAbi -} - -export function getAddress(): Address { - if (!config.river.addresses.riverRegistry) { - throw new Error(`no riverRegistry address`) - } - return config.river.addresses.riverRegistry as Address -} diff --git a/packages/stream-metadata/src/environment.ts b/packages/stream-metadata/src/environment.ts index 7912ffee3..196a75693 100644 --- a/packages/stream-metadata/src/environment.ts +++ b/packages/stream-metadata/src/environment.ts @@ -1,25 +1,32 @@ import * as dotenv from 'dotenv' +import { getWeb3Deployment } from '@river-build/web3' +import { z } from 'zod' import { Config } from './types' -import { getWeb3Deployment } from '@river-build/web3' -const isDev = process.env.NODE_ENV === 'development' -const envFile = isDev ? '.env.localhost' : '.env' +const IntStringSchema = z.string().regex(/^[0-9]+$/) +const NumberFromIntStringSchema = IntStringSchema.transform((str) => parseInt(str, 10)) + +const envSchema = z.object({ + RIVER_ENV: z.string(), + RIVER_CHAIN_RPC_URL: z.string().url(), + PORT: NumberFromIntStringSchema, +}) dotenv.config({ - path: envFile, + path: ['.env', '.env.local'], }) -export const SERVER_PORT = parseInt(process.env.PORT ?? '443', 10) -export const config = makeConfig(process.env.RIVER_ENV, process.env.RIVER_CHAIN_RPC_URL) +// eslint-disable-next-line no-process-env -- this is the only line where we're allowed to use process.env +const env = envSchema.parse(process.env) + +export const config = makeConfig(env.RIVER_ENV, env.RIVER_CHAIN_RPC_URL, env.PORT) -function makeConfig( - riverEnv = 'omega', - riverChainRpcUrl = 'https://mainnet.rpc.river.build/http', -): Config { +function makeConfig(riverEnv: string, riverChainRpcUrl: string, port: number): Config { const web3Config = getWeb3Deployment(riverEnv) return { ...web3Config, + port, riverEnv, riverChainRpcUrl, } @@ -28,6 +35,7 @@ function makeConfig( console.log('config', { riverEnv: config.riverEnv, chainId: config.river.chainId, + port: config.port, riverRegistry: config.river.addresses.riverRegistry, riverChainRpcUrl: config.riverChainRpcUrl, }) diff --git a/packages/stream-metadata/src/node.ts b/packages/stream-metadata/src/node.ts index 7221c1a0c..ac181e44e 100644 --- a/packages/stream-metadata/src/node.ts +++ b/packages/stream-metadata/src/node.ts @@ -1,9 +1,11 @@ -import Fastify from 'fastify' import { Server as HTTPSServer } from 'https' -import { SERVER_PORT } from './environment' + +import Fastify from 'fastify' import cors from '@fastify/cors' + import { handleImageRequest } from './handleImageRequest' import { handleMetadataRequest } from './handleMetadataRequest' +import { config } from './environment' // Set the process title to 'fetch-image' so it can be easily identified // or killed with `pkill fetch-image` @@ -13,10 +15,20 @@ const server = Fastify({ logger: true, }) -server.register(cors, { - origin: '*', // Allow any origin - methods: ['GET'], // Allowed HTTP methods -}) +async function registerPlugins() { + try { + await server.register(cors, { + origin: '*', // Allow any origin + methods: ['GET'], // Allowed HTTP methods + }) + console.info('CORS registered successfully') + } catch (err) { + console.error('Error registering CORS', err) + process.exit(1) // Exit the process if registration fails + } +} + +registerPlugins() server.get('/space/:spaceAddress', async (request, reply) => { const { spaceAddress } = request.params as { spaceAddress?: string } @@ -49,8 +61,8 @@ function getServerInfo() { } // Type guard to check if error has code property -function isAddressInUseError(err: any): err is NodeJS.ErrnoException { - return err && typeof err === 'object' && 'code' in err +function isAddressInUseError(err: unknown): err is NodeJS.ErrnoException { + return err instanceof Error && 'code' in err && err.code === 'EADDRINUSE' } // Function to start the server on the first available port @@ -62,9 +74,9 @@ async function startServer(port: number) { server.log.info(`Server listening on ${addressInfo.address}:${addressInfo.port}`) } } catch (err) { - if (isAddressInUseError(err) && err.code === 'EADDRINUSE') { + if (isAddressInUseError(err)) { server.log.warn(`Port ${port} is in use, trying port ${port + 1}`) - startServer(port + 1) // Try the next port + await startServer(port + 1) // Try the next port } else { server.log.error(err) process.exit(1) @@ -84,4 +96,11 @@ process.on('SIGTERM', async () => { }) // Start the server on the port set in the .env, or the next available port -startServer(SERVER_PORT) +startServer(config.port) + .then(() => { + console.log('Server started') + }) + .catch((err) => { + console.error('Error starting server', err) + process.exit(1) + }) diff --git a/packages/stream-metadata/src/types.d.ts b/packages/stream-metadata/src/types.d.ts index 8b600a595..14ad00034 100644 --- a/packages/stream-metadata/src/types.d.ts +++ b/packages/stream-metadata/src/types.d.ts @@ -1,6 +1,7 @@ import { getWeb3Deployment } from '@river-build/web3' export interface Config extends ReturnType { + port: number riverEnv: string riverChainRpcUrl: string } diff --git a/yarn.lock b/yarn.lock index ab57fbe34..1973f462a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18281,12 +18281,16 @@ __metadata: dependencies: "@bufbuild/buf": ^1.32.0 "@bufbuild/protobuf": ^1.9.0 + "@connectrpc/connect": ^1.4.0 "@connectrpc/connect-web": ^1.4.0 "@fastify/cors": ^9.0.1 "@river-build/generated": "workspace:^" + "@river-build/prettier-config": "workspace:^" "@river-build/proto": "workspace:^" "@river-build/sdk": "workspace:^" + "@river-build/web3": "workspace:^" "@types/node": ^20.5.0 + "@typescript-eslint/parser": ^7.14.1 dotenv: ^16.4.5 esbuild: ^0.21.5 ethers: ^5.7.2 @@ -18295,7 +18299,7 @@ __metadata: rollup: ^4.18.1 ts-node: ^10.9.1 typescript: ^5.1.6 - viem: ^1.18.2 + zod: ^3.21.4 languageName: unknown linkType: soft