diff --git a/docker/Dockerfile b/docker/Dockerfile index b26288b6..dabd529d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -49,7 +49,7 @@ FROM web-base AS web-deps RUN apk add --no-cache libc6-compat WORKDIR /app/web -# Copy the package.json and package-lock.json files +# Copy the package.json and yarn.lock files COPY web/yarn.lock web/package.json web/.yarnrc.yml ./ # Install dependencies diff --git a/docker/README.md b/docker/README.md index 0ab27687..1e0c3937 100644 --- a/docker/README.md +++ b/docker/README.md @@ -82,6 +82,17 @@ yourself: CREATE DATABASE dolt_workbench; ``` +### Specifying a different GraphQL API URL + +When running this Docker image locally, GraphQL API requests are routed to the GraphQL +server running at `http://localhost:9002/graphql`. In some cases you may want to specify a +different url to route GraphQL API requests, and you can do so using the following +environment variable: + +```bash +% docker run -p 9002:9002 -p 3000:3000 -e GRAPHQLAPI_URL="[your-host]:9002/graphql" dolthub/dolt-workbench:latest +``` + ## Connecting to an internet accessible database If your database is already internet accessible (i.e. your database is hosted on a service like [AWS RDS](https://aws.amazon.com/rds/) or [Hosted Dolt](https://hosted.doltdb.com)), simply enter your database connection information through the UI. diff --git a/web/contexts/serverConfig.tsx b/web/contexts/serverConfig.tsx new file mode 100644 index 00000000..5f6daa13 --- /dev/null +++ b/web/contexts/serverConfig.tsx @@ -0,0 +1,44 @@ +import ErrorMsg from "@components/ErrorMsg"; +import Loader from "@components/Loader"; +import { createContextWithDisplayName } from "@dolthub/react-contexts"; +import { ReactNode, useContext } from "react"; +import useSWR from "swr"; +import { ServerConfig } from "../pages/api/config"; + +export type ServerConfigContextValue = Partial; + +export const ServerConfigContext = + createContextWithDisplayName( + {}, + "ServerConfigContext", + ); + +type Props = { + children: ReactNode; +}; + +// ServerConfigProvider needs to wrap every page, and is only used in _app +export function ServerConfigProvider({ children }: Props): JSX.Element { + const { data, error } = useSWR("/api/config"); + + if (error) { + return ( + <> + + {children} + + ); + } + + return data ? ( + + {children} + + ) : ( + + ); +} + +export function useServerConfig(): ServerConfigContextValue { + return useContext(ServerConfigContext); +} diff --git a/web/lib/apollo.tsx b/web/lib/apollo.tsx index ff7528dc..925c1a48 100644 --- a/web/lib/apollo.tsx +++ b/web/lib/apollo.tsx @@ -10,7 +10,7 @@ import { IncomingMessage } from "http"; import fetch from "isomorphic-unfetch"; import { NextPage, NextPageContext } from "next"; -const graphqlApiUrl = "http://localhost:9002/graphql"; +const defaultGraphqlApiUrl = "http://localhost:9002/graphql"; export function createApolloClient( uri: string, @@ -50,6 +50,7 @@ let globalApolloClient: ApolloClient | undefined; export const initApolloClient = ( initialState?: NormalizedCacheObject, req?: IncomingMessage, + graphqlApiUrl = defaultGraphqlApiUrl, ): ApolloClient => { // Make sure to create a new client for every server-side request so that data // isn't shared between connections (which would be bad) @@ -108,7 +109,7 @@ export const initOnContext = (ctx: NextPageContext): ContextWithClient => { */ export function withApollo< P extends Record = Record, ->() { +>(graphqlApiUrl = defaultGraphqlApiUrl) { // eslint-disable-next-line @typescript-eslint/naming-convention return (PageComponent: NextPage

) => { const WithApollo = ({ @@ -122,7 +123,7 @@ export function withApollo< client = apolloClient; } else { // Happens on: next.js csr - client = initApolloClient(apolloState, undefined); + client = initApolloClient(apolloState, undefined, graphqlApiUrl); } return ( diff --git a/web/package.json b/web/package.json index 249d9573..18d31c78 100644 --- a/web/package.json +++ b/web/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@apollo/client": "^3.7.0", + "@dolthub/react-contexts": "^0.1.0", "@dolthub/react-hooks": "^0.1.6", "@dolthub/web-utils": "^0.1.2", "@react-icons/all-files": "^4.1.0", @@ -59,6 +60,7 @@ "reactflow": "^11.10.1", "reactjs-popup": "^2.0.6", "sharp": "^0.33.1", + "swr": "^2.2.4", "tailwindcss": "^3.3.5" }, "devDependencies": { diff --git a/web/pages/_app.tsx b/web/pages/_app.tsx index c82ebf76..617a8243 100644 --- a/web/pages/_app.tsx +++ b/web/pages/_app.tsx @@ -1,13 +1,30 @@ import "@components/AceEditor/ace-editor.css"; import "@components/util/KeyNav/index.css"; +import { ServerConfigProvider, useServerConfig } from "@contexts/serverConfig"; import { withApollo } from "@lib/apollo"; import { colors } from "@lib/tailwind"; import "github-markdown-css/github-markdown-light.css"; import App from "next/app"; import Head from "next/head"; import "react-tooltip/dist/react-tooltip.css"; +import { SWRConfig } from "swr"; import "../styles/global.css"; +// configure fetch for use with SWR +const fetcher = async (input: RequestInfo, init: RequestInit) => { + const res = await fetch(input, init); + if (!res.ok) { + throw await res.json(); + } + return res.json(); +}; + +function Inner(props: { pageProps: any; Component: any }) { + const { graphqlApiUrl } = useServerConfig(); + const WrappedPage = withApollo(graphqlApiUrl)(props.Component); + return ; +} + export default class DoltSQLWorkbench extends App { public render() { const { Component, pageProps, router } = this.props; @@ -19,7 +36,6 @@ export default class DoltSQLWorkbench extends App { ...router.query, }; - const WrappedPage = withApollo()(Component); return ( <> @@ -57,7 +73,11 @@ export default class DoltSQLWorkbench extends App { /> - + + + + + ); } diff --git a/web/pages/api/config.ts b/web/pages/api/config.ts new file mode 100644 index 00000000..55f6dcb7 --- /dev/null +++ b/web/pages/api/config.ts @@ -0,0 +1,15 @@ +import { NextApiHandler } from "next"; + +const cfg = { + graphqlApiUrl: process.env.GRAPHQLAPI_URL, +}; + +export type ServerConfig = typeof cfg; + +const handler: NextApiHandler = (_req, res) => { + res.statusCode = 200; + res.setHeader("Content-Type", "application/json"); + res.end(JSON.stringify(cfg)); +}; + +export default handler; diff --git a/web/yarn.lock b/web/yarn.lock index 5f496ce5..6ef2a146 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1281,6 +1281,7 @@ __metadata: resolution: "@dolt-sql-workbench/web@workspace:." dependencies: "@apollo/client": "npm:^3.7.0" + "@dolthub/react-contexts": "npm:^0.1.0" "@dolthub/react-hooks": "npm:^0.1.6" "@dolthub/web-utils": "npm:^0.1.2" "@graphql-codegen/cli": "npm:^5.0.0" @@ -1361,6 +1362,7 @@ __metadata: sharp: "npm:^0.33.1" stylelint: "npm:^15.11.0" stylelint-config-recommended: "npm:^14.0.0" + swr: "npm:^2.2.4" tailwindcss: "npm:^3.3.5" ts-jest: "npm:^29.1.1" typescript: "npm:^5.2.2" @@ -1368,6 +1370,18 @@ __metadata: languageName: unknown linkType: soft +"@dolthub/react-contexts@npm:^0.1.0": + version: 0.1.0 + resolution: "@dolthub/react-contexts@npm:0.1.0" + dependencies: + "@dolthub/react-hooks": "npm:^0.1.6" + peerDependencies: + react: ^18.2.0 + react-dom: ^18.2.0 + checksum: 6c28e440d845546440e590887fffc683f210fe761fa6e7dbf0d56063f4ad9825ed991dd4146de9c6d6e6c47b78ee1d234146aaf3d4455f26d755eaf941fabc02 + languageName: node + linkType: hard + "@dolthub/react-hooks@npm:^0.1.6": version: 0.1.6 resolution: "@dolthub/react-hooks@npm:0.1.6" @@ -5305,7 +5319,7 @@ __metadata: languageName: node linkType: hard -"client-only@npm:0.0.1": +"client-only@npm:0.0.1, client-only@npm:^0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" checksum: 9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 @@ -13054,6 +13068,18 @@ __metadata: languageName: node linkType: hard +"swr@npm:^2.2.4": + version: 2.2.4 + resolution: "swr@npm:2.2.4" + dependencies: + client-only: "npm:^0.0.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + checksum: ad7d3205f2b4969f6727b25819f2f999af792083b63a25c54fae10bc841c94827fb06652764ef94fa7a46cf9dda25a41f088809919d070dd0fec5b0005a3e839 + languageName: node + linkType: hard + "symbol-observable@npm:^4.0.0": version: 4.0.0 resolution: "symbol-observable@npm:4.0.0" @@ -13718,7 +13744,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:1.2.0": +"use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" peerDependencies: