From 4522070fb745cfe2f6f85a4a444312de0e2c2cfb Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 11:46:00 -0300 Subject: [PATCH 1/9] feat: improve docker for deployment --- deployment/Dockerfile | 2 +- packages/graphql/.env.example | 4 +- packages/graphql/.env.production | 3 +- packages/graphql/codegen.fuel.ts | 8 +-- packages/graphql/package.json | 4 +- packages/graphql/src/bin.ts | 73 ------------------------ packages/graphql/src/bin/index.ts | 9 +++ packages/graphql/src/bin/server.ts | 73 ++++++++++++++++++++++++ packages/graphql/src/domains/Network.ts | 12 +++- packages/graphql/src/server.ts | 27 ++++++++- packages/graphql/src/utils/cache.ts | 25 +++++++++ packages/graphql/src/utils/index.ts | 1 + packages/graphql/tsup.config.mjs | 39 +++++++------ pnpm-lock.yaml | 75 +++++-------------------- 14 files changed, 191 insertions(+), 164 deletions(-) delete mode 100644 packages/graphql/src/bin.ts create mode 100644 packages/graphql/src/bin/index.ts create mode 100644 packages/graphql/src/bin/server.ts create mode 100644 packages/graphql/src/utils/cache.ts diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 564c0d5ad..d27705965 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -16,7 +16,7 @@ WORKDIR /app-explorer RUN pnpm install --frozen-lockfile -EXPOSE 4444 +EXPOSE ${PORT} WORKDIR /app-explorer/packages/graphql diff --git a/packages/graphql/.env.example b/packages/graphql/.env.example index a252fa096..8764f5a6a 100644 --- a/packages/graphql/.env.example +++ b/packages/graphql/.env.example @@ -1,2 +1,2 @@ -FUEL_PROVIDER_BETA5=https://beta-5.fuel.network/graphql -SERVER_PORT=4000 +FUEL_PROVIDER=https://beta-5.fuel.network/graphql +SERVER_PORT=4444 diff --git a/packages/graphql/.env.production b/packages/graphql/.env.production index ac0560954..8764f5a6a 100644 --- a/packages/graphql/.env.production +++ b/packages/graphql/.env.production @@ -1 +1,2 @@ -FUEL_PROVIDER_BETA5=https://beta-5.fuel.network/graphql +FUEL_PROVIDER=https://beta-5.fuel.network/graphql +SERVER_PORT=4444 diff --git a/packages/graphql/codegen.fuel.ts b/packages/graphql/codegen.fuel.ts index 463e48278..79c64efc6 100644 --- a/packages/graphql/codegen.fuel.ts +++ b/packages/graphql/codegen.fuel.ts @@ -1,13 +1,13 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; -console.log(`process.env.FUEL_PROVIDER_BETA5`, process.env.FUEL_PROVIDER_BETA5); +import { requireEnv } from './src/utils/requireEnv'; + +const { FUEL_PROVIDER } = requireEnv(['FUEL_PROVIDER']); const config: CodegenConfig = { generates: { './src/schemas/fuelcore.graphql': { - schema: - process.env.FUEL_PROVIDER_BETA5 || - 'https://beta-5.fuel.network/graphql', + schema: FUEL_PROVIDER, plugins: ['schema-ast'], config: { includeDirectives: true, diff --git a/packages/graphql/package.json b/packages/graphql/package.json index de00b8184..cc7b79364 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -15,7 +15,6 @@ "codegen:fuel": "gql-gen -r dotenv/config --config codegen.fuel.ts", "codegen:app": "gql-gen -r dotenv/config --config codegen.ts", "dev": "pnpm build:watch", - "start": "pnpm dev", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", "prepare": "pnpm build" @@ -43,7 +42,8 @@ "graphql-tag": "2.12.6", "lodash": "^4.17.21", "tai64": "1.0.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "wait-port": "1.1.0" }, "devDependencies": { "@babel/cli": "7.23.9", diff --git a/packages/graphql/src/bin.ts b/packages/graphql/src/bin.ts deleted file mode 100644 index 5f03709fc..000000000 --- a/packages/graphql/src/bin.ts +++ /dev/null @@ -1,73 +0,0 @@ -import chokidar from 'chokidar'; -import { execa } from 'execa'; -import { createServer } from 'http'; -import { resolve } from 'path'; - -import app from './server'; -import { requireEnv } from './utils/requireEnv'; - -const { SERVER_PORT } = requireEnv(['SERVER_PORT']); -const { WATCH } = requireEnv(['WATCH']); - -const isWatching = WATCH === 'true'; - -async function codegen() { - console.log('⌛️ Generating GraphQL code...'); - await execa('pnpm', ['codegen:app'], { stdio: 'inherit' }); - console.log('✅ GraphQL code generated!'); - console.log(`🚀 Server running at http://localhost:${SERVER_PORT}/graphql`); - isWatching && console.log('👀 Watching for GraphQL changes...'); -} - -const server = createServer(app); -const cwd = resolve(__dirname, '../'); -const gqlWatcher = chokidar.watch(['src/**/*.graphql'], { - cwd, - ignoreInitial: true, - ignored: ['src/schemas'], -}); - -async function runServer() { - return new Promise((resolve) => { - server.listen(SERVER_PORT, async () => { - resolve(null); - }); - }); -} - -async function closeServer() { - return new Promise((resolve) => { - server.close(() => { - resolve(null); - console.log('🛑 GraphQL server stopped!'); - }); - }); -} - -export async function exec() { - await runServer(); - await codegen(); - - async function exitHandler() { - await closeServer(); - gqlWatcher.close(); - } - - if (!isWatching) { - await exitHandler(); - return; - } - - process.on('exit', exitHandler); - process.on('SIGTERM', exitHandler); - process.on('SIGINT', exitHandler); - process.on('SIGUSR1', exitHandler); - - gqlWatcher.on('all', async () => { - await closeServer(); - await runServer(); - await codegen(); - }); -} - -exec(); diff --git a/packages/graphql/src/bin/index.ts b/packages/graphql/src/bin/index.ts new file mode 100644 index 000000000..fb9da6ba6 --- /dev/null +++ b/packages/graphql/src/bin/index.ts @@ -0,0 +1,9 @@ +import { runDevelopment, runServer } from './server'; + +const { WATCH } = process.env; + +if (WATCH === 'true') { + runDevelopment(); +} else { + runServer(); +} diff --git a/packages/graphql/src/bin/server.ts b/packages/graphql/src/bin/server.ts new file mode 100644 index 000000000..d5a8c5c53 --- /dev/null +++ b/packages/graphql/src/bin/server.ts @@ -0,0 +1,73 @@ +import chokidar from 'chokidar'; +import { execa } from 'execa'; +import { createServer } from 'http'; +import { resolve } from 'path'; + +import app from '../server'; +import { requireEnv } from '../utils/requireEnv'; + +const { SERVER_PORT = 4444 } = requireEnv(['SERVER_PORT']); + +const server = createServer(app); + +export async function runServer() { + return new Promise((resolve) => { + server.listen(SERVER_PORT, async () => { + console.log( + `🚀 Server running at http://localhost:${SERVER_PORT}/graphql` + ); + resolve(null); + }); + }); +} + +export async function closeServer() { + return new Promise((resolve) => { + server.close(() => { + resolve(null); + console.log('🛑 GraphQL server stopped!'); + }); + }); +} + +export async function runDevelopment() { + const cwd = resolve(__dirname, '../'); + const gqlWatcher = chokidar.watch(['src/**/*.graphql'], { + cwd, + ignoreInitial: true, + ignored: ['src/schemas'], + }); + + async function codegen() { + console.log('⌛️ Generating GraphQL code...'); + try { + await execa('pnpm', ['codegen:app'], { stdio: 'inherit' }); + console.log('✅ GraphQL code generated!'); + } catch (err) { + console.log('❌ GraphQL error!'); + } + + console.log('👀 Watching for GraphQL changes...'); + } + + await runServer(); + await codegen(); + + async function exitHandler() { + await closeServer(); + gqlWatcher.close(); + } + + process.on('exit', exitHandler); + process.on('SIGTERM', exitHandler); + process.on('SIGINT', exitHandler); + process.on('SIGUSR1', exitHandler); + + gqlWatcher.on('all', async () => { + await closeServer(); + await runServer(); + await codegen(); + }); + + return exitHandler; +} diff --git a/packages/graphql/src/domains/Network.ts b/packages/graphql/src/domains/Network.ts index 44b937c08..993ce77d5 100644 --- a/packages/graphql/src/domains/Network.ts +++ b/packages/graphql/src/domains/Network.ts @@ -1,8 +1,18 @@ import { Provider } from 'fuels'; +import { Cache } from '../utils'; + +const CHAIN_CACHE = 43200000; + export class NetworkDomain { + static cache = new Cache(); + static async getChainInfo(url: string) { - const provider = await Provider.create(url); + let provider = this.cache.get(url); + if (!provider) { + provider = await Provider.create(url); + } + this.cache.put(url, provider, CHAIN_CACHE); return provider.getChain(); } } diff --git a/packages/graphql/src/server.ts b/packages/graphql/src/server.ts index ed0fb3f2d..813b56ed2 100644 --- a/packages/graphql/src/server.ts +++ b/packages/graphql/src/server.ts @@ -8,7 +8,9 @@ import { createSchema } from './schema'; import { createGraphqlFetch } from './utils/executor'; import { requireEnv } from './utils/requireEnv'; -const { FUEL_PROVIDER_BETA5 } = requireEnv(['FUEL_PROVIDER_BETA5']); +const { FUEL_PROVIDER = 'https://beta-5.fuel.network/graphql' } = requireEnv([ + 'FUEL_PROVIDER', +]); // Create a server: const app = express(); @@ -26,7 +28,7 @@ app.get( }) ); -const executor = createGraphqlFetch(FUEL_PROVIDER_BETA5); +const executor = createGraphqlFetch(FUEL_PROVIDER); const schema = createSchema(executor); app.post( @@ -34,9 +36,28 @@ app.post( createHandler({ schema, async context() { - return ContextDomain.createContext(FUEL_PROVIDER_BETA5); + return ContextDomain.createContext(FUEL_PROVIDER); }, }) ); +// Check health of the graphql endpoint and the fuel provider +app.get('/health', async (_, res) => { + let providerUp = null; + try { + providerUp = ( + await fetch(`${FUEL_PROVIDER.replace('/graphql', '/health')}`).then( + (res) => res.json() + ) + ).up; + } catch (e) { + providerUp = false; + } + + res.status(200).send({ + up: true, + providerUp: providerUp, + }); +}); + export default app; diff --git a/packages/graphql/src/utils/cache.ts b/packages/graphql/src/utils/cache.ts new file mode 100644 index 000000000..3bf320585 --- /dev/null +++ b/packages/graphql/src/utils/cache.ts @@ -0,0 +1,25 @@ +export class Cache { + private _cache: Record = {}; + + put(key: string, value: T, ttl: number) { + const expire = Date.now() + ttl; + this._cache[key] = { value, expire }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get(key: string): T | null { + const data = this._cache[key]; + if (data) { + if (Date.now() < data.expire) { + return data.value as T; + } else { + delete this._cache[key]; + } + } + return null; + } + + delete(key: string) { + delete this._cache[key]; + } +} diff --git a/packages/graphql/src/utils/index.ts b/packages/graphql/src/utils/index.ts index 394654171..3a3c9fd82 100644 --- a/packages/graphql/src/utils/index.ts +++ b/packages/graphql/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './executor'; export * from './getFieldsValues'; +export * from './cache'; diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index 064526aa9..e8399f96a 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -4,6 +4,8 @@ import getPort from 'get-port'; import { defineConfig } from 'tsup'; const graphqlLoaderPlugin = graphqlLoaderPluginPkg.default; +// Assign a single port for the process +const port = await getPort({ port: 4444 }); export default defineConfig((options) => ({ outDir: 'dist', @@ -12,23 +14,26 @@ export default defineConfig((options) => ({ sourcemap: true, clean: false, esbuildPlugins: [graphqlLoaderPlugin()], - entry: { index: 'src/bin.ts' }, + entry: { index: 'src/bin/index.ts' }, async onSuccess() { - const port = await getPort({ port: 4444 }); - const cmd = execa('node', ['--import', 'tsx/esm', './dist/index.js'], { - stdio: 'inherit', - cleanup: true, - env: { - SERVER_PORT: port, - WATCH: Boolean(options.watch), - FUEL_PROVIDER_BETA5: - process.env.FUEL_PROVIDER_BETA5 || - 'https://beta-5.fuel.network/graphql', - }, - }); - - return () => { - cmd.kill('SIGTERM'); - }; + if (options.watch) { + const cmd = execa('node', ['--import', 'tsx/esm', './dist/index.js'], { + stdio: 'inherit', + cleanup: true, + env: { + SERVER_PORT: port, + WATCH: Boolean(options.watch), + FUEL_PROVIDER: process.env.FUEL_PROVIDER, + }, + }); + // Wait process to close until restarting + return async () => { + const killProcess = new Promise((resolve) => { + cmd.on('close', () => resolve(true)); + }); + cmd.kill('SIGTERM'); + await killProcess; + }; + } }, })); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e1663bad..9c29ea65a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -682,6 +682,9 @@ importers: typescript: specifier: 5.3.3 version: 5.3.3 + wait-port: + specifier: 1.1.0 + version: 1.1.0 devDependencies: '@babel/cli': specifier: 7.23.9 @@ -741,12 +744,6 @@ importers: specifier: 4.7.0 version: 4.7.0 - packages/indexer: - devDependencies: - nodemon: - specifier: ^3.0.2 - version: 3.0.3 - packages/notification-service: dependencies: '@fuel-bridge/solidity-contracts': @@ -14171,10 +14168,6 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} deprecated: Use your platform's native atob() and btoa() methods instead - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true - /abitype@0.8.7(typescript@4.9.5): resolution: {integrity: sha512-wQ7hV8Yg/yKmGyFpqrNZufCxbszDe5es4AZGYPBitocfSqXtjrTG9JMWFcc4N30ukl2ve48aBTwt7NJxVQdU3w==} peerDependencies: @@ -20363,10 +20356,6 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - /ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - dev: true - /ignore@5.3.0: resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} engines: {node: '>= 4'} @@ -23125,34 +23114,10 @@ packages: engines: {node: '>=6.0.0'} dev: false - /nodemon@3.0.3: - resolution: {integrity: sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - chokidar: 3.5.3 - debug: 4.3.4(supports-color@5.5.0) - ignore-by-default: 1.0.1 - minimatch: 3.1.2 - pstree.remy: 1.1.8 - semver: 7.5.4 - simple-update-notifier: 2.0.0 - supports-color: 5.5.0 - touch: 3.1.0 - undefsafe: 2.0.5 - dev: true - /nofilter@3.1.0: resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} engines: {node: '>=12.19'} - /nopt@1.0.10: - resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} - hasBin: true - dependencies: - abbrev: 1.1.1 - dev: true - /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -24384,10 +24349,6 @@ packages: /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - /pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - dev: true - /public-encrypt@4.0.3: resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} dependencies: @@ -26004,13 +25965,6 @@ packages: dependencies: is-arrayish: 0.3.2 - /simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - dependencies: - semver: 7.5.4 - dev: true - /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -27214,13 +27168,6 @@ packages: /toposort@2.0.2: resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} - /touch@3.1.0: - resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} - hasBin: true - dependencies: - nopt: 1.0.10 - dev: true - /tough-cookie@2.4.3: resolution: {integrity: sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==} engines: {node: '>=0.8'} @@ -27815,10 +27762,6 @@ packages: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} dev: false - /undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - dev: true - /underscore@1.13.6: resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} dev: true @@ -28585,6 +28528,18 @@ packages: - debug dev: true + /wait-port@1.1.0: + resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chalk: 4.1.2 + commander: 9.5.0 + debug: 4.3.4(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + dev: false + /walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: From d462aa3cbcaf95e0447244f2d223225df521a6ef Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 13:56:42 -0300 Subject: [PATCH 2/9] feat: improve graphql docker iamge for deployment --- .github/actions/docker-publish/action.yaml | 6 +++++- .github/workflows/docker-publish.yml | 1 + deployment/Dockerfile | 8 ++++---- packages/app-explorer/src/app/api/graphql/route.ts | 4 +++- .../app-explorer/src/systems/utils/requireEnv.ts | 12 ++++++++++++ packages/graphql/package.json | 5 ++++- packages/graphql/tsup.config.mjs | 2 ++ pnpm-lock.yaml | 6 ++++++ 8 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 packages/app-explorer/src/systems/utils/requireEnv.ts diff --git a/.github/actions/docker-publish/action.yaml b/.github/actions/docker-publish/action.yaml index 5a8eb4c12..9e9c0c637 100644 --- a/.github/actions/docker-publish/action.yaml +++ b/.github/actions/docker-publish/action.yaml @@ -19,6 +19,10 @@ inputs: dockerfile: description: 'Path to the Dockerfile' required: true + context: + description: 'Path to the Context' + default: . + required: true build-args: description: 'List of build-time variables' required: false @@ -65,7 +69,7 @@ runs: uses: docker/build-push-action@v4 id: publish with: - context: . + context: ${{ inputs.context }} file: ${{ inputs.dockerfile }} push: true tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 83aac02bc..745d12e07 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -30,3 +30,4 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} image: ghcr.io/fuellabs/fuel-explorer dockerfile: deployment/Dockerfile + context: ./packages/graphql diff --git a/deployment/Dockerfile b/deployment/Dockerfile index d27705965..7bfea8907 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -4,7 +4,7 @@ FROM node:20-slim AS base # Expose the ENVs to the env of the container ENV PORT="${PORT}" -ENV FUEL_PROVIDER_BETA5="${FUEL_PROVIDER_BETA5}" +ENV FUEL_PROVIDER="${FUEL_PROVIDER:-https://beta-5.fuel.network/graphql}" ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" @@ -14,10 +14,10 @@ RUN corepack enable COPY . /app-explorer WORKDIR /app-explorer -RUN pnpm install --frozen-lockfile +RUN pnpm install EXPOSE ${PORT} -WORKDIR /app-explorer/packages/graphql +WORKDIR /app-explorer -CMD ["pnpm", "start"] +CMD ["pnpm", "server:start"] diff --git a/packages/app-explorer/src/app/api/graphql/route.ts b/packages/app-explorer/src/app/api/graphql/route.ts index 679262220..dd2a9e100 100644 --- a/packages/app-explorer/src/app/api/graphql/route.ts +++ b/packages/app-explorer/src/app/api/graphql/route.ts @@ -1,8 +1,10 @@ import { createExecutor, createSchema } from '@fuel-explorer/graphql'; import { ContextDomain } from '@fuel-explorer/graphql/src/domains/Context'; import { createYoga } from 'graphql-yoga'; +import { requireEnv } from '~/systems/utils/requireEnv'; + +const { FUEL_PROVIDER_BETA5: url } = requireEnv(['FUEL_PROVIDER_BETA5']); -const url = process.env.FUEL_PROVIDER_BETA5!; const executor = createExecutor(async ({ body }) => { return fetch(url, { body, diff --git a/packages/app-explorer/src/systems/utils/requireEnv.ts b/packages/app-explorer/src/systems/utils/requireEnv.ts new file mode 100644 index 000000000..0be150313 --- /dev/null +++ b/packages/app-explorer/src/systems/utils/requireEnv.ts @@ -0,0 +1,12 @@ +export function requireEnv< + A extends string[], + B extends { [key in A[number]]: string }, +>(keys: string[]): B { + return keys.reduce((ret, key) => { + if (!process.env[key]) { + throw new Error(`Environment variable ${key} is required`); + } + ret[key] = process.env[key]!; + return ret; + }, {} as B); +} diff --git a/packages/graphql/package.json b/packages/graphql/package.json index cc7b79364..c800825e2 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -10,11 +10,12 @@ "typings": "./src/index.ts", "scripts": { "build": "run-s codegen:fuel build:lib", - "build:lib": "tsup --dts", + "build:lib": "tsup", "build:watch": "pnpm build:lib --watch", "codegen:fuel": "gql-gen -r dotenv/config --config codegen.fuel.ts", "codegen:app": "gql-gen -r dotenv/config --config codegen.ts", "dev": "pnpm build:watch", + "server:start": "node ./dist/index.js", "fix:generated": "node ./scripts/fix-generated.mjs", "ts:check": "tsc --noEmit", "prepare": "pnpm build" @@ -63,7 +64,9 @@ "execa": "8.0.1", "graphql-codegen-typescript-common": "0.18.2", "graphql-codegen-typescript-mock-data": "3.7.1", + "npm-run-all": "^4.1.5", "tsconfig-paths": "^4.2.0", + "tsup": "8.0.1", "tsx": "4.7.0" } } diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index e8399f96a..6c1097855 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -13,6 +13,8 @@ export default defineConfig((options) => ({ format: ['esm', 'cjs'], sourcemap: true, clean: false, + dts: false, + minify: false, esbuildPlugins: [graphqlLoaderPlugin()], entry: { index: 'src/bin/index.ts' }, async onSuccess() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c29ea65a..9252da346 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -737,9 +737,15 @@ importers: graphql-codegen-typescript-mock-data: specifier: 3.7.1 version: 3.7.1(graphql@16.8.1) + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 + tsup: + specifier: 8.0.1 + version: 8.0.1(@swc/core@1.3.106)(ts-node@10.9.2)(typescript@5.3.3) tsx: specifier: 4.7.0 version: 4.7.0 From 16f21ecb56074d3571c705357763d12696b1342b Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 14:24:58 -0300 Subject: [PATCH 3/9] feat: change env configuration for codegen server --- packages/graphql/src/bin/index.ts | 8 ++++---- packages/graphql/src/bin/server.ts | 8 +++++++- packages/graphql/tsup.config.mjs | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/graphql/src/bin/index.ts b/packages/graphql/src/bin/index.ts index fb9da6ba6..9f45d44e3 100644 --- a/packages/graphql/src/bin/index.ts +++ b/packages/graphql/src/bin/index.ts @@ -1,9 +1,9 @@ -import { runDevelopment, runServer } from './server'; +import { runServerCodegen, runServer } from './server'; -const { WATCH } = process.env; +const { CODE_GEN = 'false' } = process.env; -if (WATCH === 'true') { - runDevelopment(); +if (CODE_GEN === 'true') { + runServerCodegen(); } else { runServer(); } diff --git a/packages/graphql/src/bin/server.ts b/packages/graphql/src/bin/server.ts index d5a8c5c53..629d8a4c7 100644 --- a/packages/graphql/src/bin/server.ts +++ b/packages/graphql/src/bin/server.ts @@ -7,6 +7,7 @@ import app from '../server'; import { requireEnv } from '../utils/requireEnv'; const { SERVER_PORT = 4444 } = requireEnv(['SERVER_PORT']); +const { WATCH = 'false' } = process.env; const server = createServer(app); @@ -30,7 +31,7 @@ export async function closeServer() { }); } -export async function runDevelopment() { +export async function runServerCodegen() { const cwd = resolve(__dirname, '../'); const gqlWatcher = chokidar.watch(['src/**/*.graphql'], { cwd, @@ -58,6 +59,11 @@ export async function runDevelopment() { gqlWatcher.close(); } + if (WATCH !== 'true') { + exitHandler(); + return; + } + process.on('exit', exitHandler); process.on('SIGTERM', exitHandler); process.on('SIGINT', exitHandler); diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index 6c1097855..515e0675c 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -24,6 +24,7 @@ export default defineConfig((options) => ({ cleanup: true, env: { SERVER_PORT: port, + CODE_GEN: true, WATCH: Boolean(options.watch), FUEL_PROVIDER: process.env.FUEL_PROVIDER, }, From 56e78524e1b900222e4a42587d7f72268fe5ae5c Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 15:19:14 -0300 Subject: [PATCH 4/9] fix: build --- packages/graphql/src/bin/server.ts | 2 +- packages/graphql/src/utils/requireEnv.ts | 23 +++++++------- packages/graphql/tsup.config.mjs | 38 +++++++++++------------- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/packages/graphql/src/bin/server.ts b/packages/graphql/src/bin/server.ts index 629d8a4c7..f83d357cd 100644 --- a/packages/graphql/src/bin/server.ts +++ b/packages/graphql/src/bin/server.ts @@ -6,7 +6,7 @@ import { resolve } from 'path'; import app from '../server'; import { requireEnv } from '../utils/requireEnv'; -const { SERVER_PORT = 4444 } = requireEnv(['SERVER_PORT']); +const { SERVER_PORT } = requireEnv([['SERVER_PORT', '4444']]); const { WATCH = 'false' } = process.env; const server = createServer(app); diff --git a/packages/graphql/src/utils/requireEnv.ts b/packages/graphql/src/utils/requireEnv.ts index 8388d9018..84a39dd10 100644 --- a/packages/graphql/src/utils/requireEnv.ts +++ b/packages/graphql/src/utils/requireEnv.ts @@ -2,14 +2,17 @@ import dotenv from 'dotenv'; dotenv.config(); export function requireEnv< - A extends string[], - B extends { [key in A[number]]: string }, ->(keys: string[]): B { - return keys.reduce((ret, key) => { - if (!process.env[key]) { - throw new Error(`Environment variable ${key} is required`); - } - ret[key] = process.env[key]!; - return ret; - }, {} as B); + A extends (string | [key: string, defaultValue: string])[], +>(keys: A) { + return keys.reduce( + (ret, value) => { + const [key, defaultValue] = Array.isArray(value) ? value : [value]; + if (!process.env[key] && defaultValue == undefined) { + throw new Error(`Environment variable ${key} is required`); + } + ret[key] = (process.env[key] ? process.env[key] : defaultValue)!; + return ret; + }, + {} as Record + ); } diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index 515e0675c..10afaf13f 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -13,30 +13,28 @@ export default defineConfig((options) => ({ format: ['esm', 'cjs'], sourcemap: true, clean: false, - dts: false, + dts: true, minify: false, esbuildPlugins: [graphqlLoaderPlugin()], entry: { index: 'src/bin/index.ts' }, async onSuccess() { - if (options.watch) { - const cmd = execa('node', ['--import', 'tsx/esm', './dist/index.js'], { - stdio: 'inherit', - cleanup: true, - env: { - SERVER_PORT: port, - CODE_GEN: true, - WATCH: Boolean(options.watch), - FUEL_PROVIDER: process.env.FUEL_PROVIDER, - }, + const cmd = execa('node', ['--import', 'tsx/esm', './dist/index.js'], { + stdio: 'inherit', + cleanup: true, + env: { + SERVER_PORT: port, + CODE_GEN: true, + WATCH: Boolean(options.watch), + FUEL_PROVIDER: process.env.FUEL_PROVIDER, + }, + }); + // Wait process to close until restarting + return async () => { + const killProcess = new Promise((resolve) => { + cmd.on('close', () => resolve(true)); }); - // Wait process to close until restarting - return async () => { - const killProcess = new Promise((resolve) => { - cmd.on('close', () => resolve(true)); - }); - cmd.kill('SIGTERM'); - await killProcess; - }; - } + cmd.kill('SIGTERM'); + await killProcess; + }; }, })); From 1ae853329f68bc5ec447fed3dc6b5989ddd16b12 Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 16:03:37 -0300 Subject: [PATCH 5/9] fix: docker build --- deployment/Dockerfile | 1 + packages/graphql/.dockerignore | 3 +++ packages/graphql/README.md | 10 ++++++++++ packages/graphql/tsconfig.json | 4 +++- packages/graphql/tsup.config.mjs | 6 +++++- 5 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 packages/graphql/.dockerignore diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 7bfea8907..a97636797 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -7,6 +7,7 @@ ENV PORT="${PORT}" ENV FUEL_PROVIDER="${FUEL_PROVIDER:-https://beta-5.fuel.network/graphql}" ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" +ENV SERVER_BUILD=true # Enable pnpm using corepack form node.js RUN corepack enable diff --git a/packages/graphql/.dockerignore b/packages/graphql/.dockerignore new file mode 100644 index 000000000..61d78d6f7 --- /dev/null +++ b/packages/graphql/.dockerignore @@ -0,0 +1,3 @@ +node_moduels +.turbo +dist diff --git a/packages/graphql/README.md b/packages/graphql/README.md index 536ea4e92..fe8306588 100644 --- a/packages/graphql/README.md +++ b/packages/graphql/README.md @@ -7,3 +7,13 @@ This is a mock api for block-explorer ```sh pnpm dev ``` + +## Docker images + +``` +docker run \ + -e FUEL_PROVIDER=https://beta-5.fuel.network/graphql \ + -e SERVER_PORT=3000 \ + -p 3333:3000 \ + +``` diff --git a/packages/graphql/tsconfig.json b/packages/graphql/tsconfig.json index acdd622e7..9a24ee503 100644 --- a/packages/graphql/tsconfig.json +++ b/packages/graphql/tsconfig.json @@ -1,5 +1,6 @@ { - "extends": "../../tsconfig.base.json", + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@fuels/ts-config/base.json", "compilerOptions": { "esModuleInterop": true, "isolatedModules": true, @@ -11,5 +12,6 @@ } ] }, + "exclude": ["node_modules"], "include": ["src", "scripts", "*.config.*", "codegen.*"] } diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index 10afaf13f..25597d06b 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -6,6 +6,9 @@ import { defineConfig } from 'tsup'; const graphqlLoaderPlugin = graphqlLoaderPluginPkg.default; // Assign a single port for the process const port = await getPort({ port: 4444 }); +const { SERVER_BUILD } = process.env; + +const isServerBuild = SERVER_BUILD === 'true'; export default defineConfig((options) => ({ outDir: 'dist', @@ -13,11 +16,12 @@ export default defineConfig((options) => ({ format: ['esm', 'cjs'], sourcemap: true, clean: false, - dts: true, + dts: !isServerBuild, minify: false, esbuildPlugins: [graphqlLoaderPlugin()], entry: { index: 'src/bin/index.ts' }, async onSuccess() { + if (isServerBuild) return; const cmd = execa('node', ['--import', 'tsx/esm', './dist/index.js'], { stdio: 'inherit', cleanup: true, From dfca23aeefc0f7542adc31a4e32fecbc90ce10e3 Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 16:10:15 -0300 Subject: [PATCH 6/9] feat: change env name and add default value --- contracts/predicate/scripts/run-predicate.ts | 6 +++--- packages/app-explorer/.env.example | 2 +- packages/app-explorer/.env.production | 2 +- packages/app-explorer/src/app/api/graphql/route.ts | 2 +- packages/graphql/tsup.config.mjs | 5 ++++- turbo.json | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/contracts/predicate/scripts/run-predicate.ts b/contracts/predicate/scripts/run-predicate.ts index 049557662..9c696278e 100644 --- a/contracts/predicate/scripts/run-predicate.ts +++ b/contracts/predicate/scripts/run-predicate.ts @@ -2,17 +2,17 @@ import { BaseAssetId, Predicate, Provider, Wallet, bn, hexlify } from 'fuels'; import { promises as fs } from 'node:fs'; import { resolve } from 'node:path'; -const { FUEL_PROVIDER_BETA5, PRIVATE_KEY } = process.env; +const { FUEL_PROVIDER, PRIVATE_KEY } = process.env; const BIN_PATH = resolve(__dirname, '../out/debug/predicate-app.bin'); const AMOUNT = 300_000; -if (!FUEL_PROVIDER_BETA5 || !PRIVATE_KEY) { +if (!FUEL_PROVIDER || !PRIVATE_KEY) { throw new Error('Missing some config on your .env file'); } async function main() { const binHex = hexlify(await fs.readFile(BIN_PATH)); - const provider = await Provider.create(FUEL_PROVIDER_BETA5!); + const provider = await Provider.create(FUEL_PROVIDER!); const wallet = Wallet.fromPrivateKey(PRIVATE_KEY!, provider); const { minGasPrice: gasPrice } = wallet.provider.getGasConfig(); const walletAddress = wallet.address.toB256(); diff --git a/packages/app-explorer/.env.example b/packages/app-explorer/.env.example index ac0560954..50b65d2bb 100644 --- a/packages/app-explorer/.env.example +++ b/packages/app-explorer/.env.example @@ -1 +1 @@ -FUEL_PROVIDER_BETA5=https://beta-5.fuel.network/graphql +FUEL_PROVIDER=https://beta-5.fuel.network/graphql diff --git a/packages/app-explorer/.env.production b/packages/app-explorer/.env.production index ac0560954..50b65d2bb 100644 --- a/packages/app-explorer/.env.production +++ b/packages/app-explorer/.env.production @@ -1 +1 @@ -FUEL_PROVIDER_BETA5=https://beta-5.fuel.network/graphql +FUEL_PROVIDER=https://beta-5.fuel.network/graphql diff --git a/packages/app-explorer/src/app/api/graphql/route.ts b/packages/app-explorer/src/app/api/graphql/route.ts index dd2a9e100..25bcd9254 100644 --- a/packages/app-explorer/src/app/api/graphql/route.ts +++ b/packages/app-explorer/src/app/api/graphql/route.ts @@ -3,7 +3,7 @@ import { ContextDomain } from '@fuel-explorer/graphql/src/domains/Context'; import { createYoga } from 'graphql-yoga'; import { requireEnv } from '~/systems/utils/requireEnv'; -const { FUEL_PROVIDER_BETA5: url } = requireEnv(['FUEL_PROVIDER_BETA5']); +const { FUEL_PROVIDER: url } = requireEnv(['FUEL_PROVIDER']); const executor = createExecutor(async ({ body }) => { return fetch(url, { diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index 25597d06b..e4ae95a4c 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -29,7 +29,10 @@ export default defineConfig((options) => ({ SERVER_PORT: port, CODE_GEN: true, WATCH: Boolean(options.watch), - FUEL_PROVIDER: process.env.FUEL_PROVIDER, + // TODO: remove the requirement of fetching the fuel provider + // at build time. + FUEL_PROVIDER: + process.env.FUEL_PROVIDER || 'https://beta-5.fuel.network/graphql', }, }); // Wait process to close until restarting diff --git a/turbo.json b/turbo.json index 023cb59a0..b56920f39 100644 --- a/turbo.json +++ b/turbo.json @@ -1,6 +1,6 @@ { "$schema": "https://turbo.build/schema.json", - "globalEnv": ["NODE_ENV", "IS_PREVIEW", "FUEL_PROVIDER_BETA5"], + "globalEnv": ["NODE_ENV", "IS_PREVIEW", "FUEL_PROVIDER"], "pipeline": { "ts:check": { "dependsOn": ["^build"], From 0fc64576326b6fd64377b9f9ba81b3bce8ecfed2 Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 16:16:46 -0300 Subject: [PATCH 7/9] feat: add default env on codegen --- packages/graphql/README.md | 4 ++-- packages/graphql/codegen.fuel.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/graphql/README.md b/packages/graphql/README.md index fe8306588..8f409d210 100644 --- a/packages/graphql/README.md +++ b/packages/graphql/README.md @@ -8,12 +8,12 @@ This is a mock api for block-explorer pnpm dev ``` -## Docker images +## Docker ``` docker run \ -e FUEL_PROVIDER=https://beta-5.fuel.network/graphql \ -e SERVER_PORT=3000 \ -p 3333:3000 \ - + ghcr.io/fuellabs/fuel-explorer:main ``` diff --git a/packages/graphql/codegen.fuel.ts b/packages/graphql/codegen.fuel.ts index 79c64efc6..ea2a94112 100644 --- a/packages/graphql/codegen.fuel.ts +++ b/packages/graphql/codegen.fuel.ts @@ -2,7 +2,9 @@ import type { CodegenConfig } from '@graphql-codegen/cli'; import { requireEnv } from './src/utils/requireEnv'; -const { FUEL_PROVIDER } = requireEnv(['FUEL_PROVIDER']); +const { FUEL_PROVIDER } = requireEnv([ + ['FUEL_PROVIDER', 'https://beta-5.fuel.network/graphql'], +]); const config: CodegenConfig = { generates: { From 05bea02bbea8eb9c73911f985b00c266db9dd2c9 Mon Sep 17 00:00:00 2001 From: luizstacio Date: Thu, 1 Feb 2024 19:13:01 -0300 Subject: [PATCH 8/9] chore: use default requireEnv value --- packages/graphql/src/server.ts | 4 ++-- packages/graphql/tsup.config.mjs | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/graphql/src/server.ts b/packages/graphql/src/server.ts index 813b56ed2..0b26015d9 100644 --- a/packages/graphql/src/server.ts +++ b/packages/graphql/src/server.ts @@ -8,8 +8,8 @@ import { createSchema } from './schema'; import { createGraphqlFetch } from './utils/executor'; import { requireEnv } from './utils/requireEnv'; -const { FUEL_PROVIDER = 'https://beta-5.fuel.network/graphql' } = requireEnv([ - 'FUEL_PROVIDER', +const { FUEL_PROVIDER } = requireEnv([ + ['FUEL_PROVIDER', 'https://beta-5.fuel.network/graphql'], ]); // Create a server: diff --git a/packages/graphql/tsup.config.mjs b/packages/graphql/tsup.config.mjs index e4ae95a4c..25597d06b 100644 --- a/packages/graphql/tsup.config.mjs +++ b/packages/graphql/tsup.config.mjs @@ -29,10 +29,7 @@ export default defineConfig((options) => ({ SERVER_PORT: port, CODE_GEN: true, WATCH: Boolean(options.watch), - // TODO: remove the requirement of fetching the fuel provider - // at build time. - FUEL_PROVIDER: - process.env.FUEL_PROVIDER || 'https://beta-5.fuel.network/graphql', + FUEL_PROVIDER: process.env.FUEL_PROVIDER, }, }); // Wait process to close until restarting From 457b608724d1cae30cbb8c05b666c5afeb6e4584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio=20=7C=20stacio=2Eeth?= Date: Thu, 1 Feb 2024 19:33:10 -0300 Subject: [PATCH 9/9] Update contracts/predicate/scripts/run-predicate.ts Co-authored-by: Luiz Gomes - LuizAsFight.eth <8636507+LuizAsFight@users.noreply.github.com> --- contracts/predicate/scripts/run-predicate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/predicate/scripts/run-predicate.ts b/contracts/predicate/scripts/run-predicate.ts index 9c696278e..e0c01e4ab 100644 --- a/contracts/predicate/scripts/run-predicate.ts +++ b/contracts/predicate/scripts/run-predicate.ts @@ -7,7 +7,7 @@ const BIN_PATH = resolve(__dirname, '../out/debug/predicate-app.bin'); const AMOUNT = 300_000; if (!FUEL_PROVIDER || !PRIVATE_KEY) { - throw new Error('Missing some config on your .env file'); + throw new Error('Missing some config in .env file. Should have FUEL_PROVIDER and PRIVATE_KEY'); } async function main() {