From 985736d5db32e5c25aaed7607bf4a2485031118e Mon Sep 17 00:00:00 2001 From: Raphael Shorser Date: Thu, 26 Oct 2023 06:29:11 -0700 Subject: [PATCH 1/3] initial dockerization --- Dockerfile | 32 +++++++++++++++++++ docker-compose.yaml | 14 ++++++++ server/api.js | 1 + supervisord.conf | 16 ++++++++++ .../react/flowbite/src/views/browse.tsx | 7 ++-- .../react/flowbite/src/views/view.tsx | 7 ++-- .../react/flowbite/vite.config.ts | 12 +++++++ .../react/nextui/src/views/browse.tsx | 5 +-- .../react/nextui/src/views/view.tsx | 7 ++-- webapps-starters/react/nextui/vite.config.ts | 11 +++++++ .../src/components/views/component-view.tsx | 9 +++--- .../react/shadcn/src/views/browse.tsx | 7 ++-- .../react/shadcn/src/views/view.tsx | 8 ++--- webapps-starters/react/shadcn/vite.config.ts | 11 +++++++ 14 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yaml create mode 100644 supervisord.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5b08b0a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM node:20.9.0-alpine + +RUN apk add --update supervisor +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +WORKDIR /app/server + +COPY ./server/package*.json ./ + +RUN npm install + +COPY ./server . + +# Unzip the icons +RUN unzip library/icons/lucide/vectordb/index.zip + +COPY ./webapps-starters /app/webapp +WORKDIR /app/webapp/react/shadcn +RUN npm i + +WORKDIR /app/webapp/react/nextui +RUN npm i + +WORKDIR /app/webapp/react/flowbite +RUN npm i + +WORKDIR /app/server +# Expose port 3000 for the server +EXPOSE 3000 +ENV HOSTNAME 0.0.0.0 + +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..84c344e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,14 @@ +version: '3' +services: + openv0: + image: openv0:latest + ports: + - "5173:5173" + environment: + - OPENAI_API_KEY= + - OPENAI_MODEL=gpt-4 + - PASS__CONTEXT__COMPONENTS_LIBRARY_EXAMPLES__TOKEN_LIMIT=600 + - OPENV0__COLLECT_UIRAY=0 + - OPENV0__API=https://api.openv0.com + - API__GENERATE_ATTEMPTS = 1 + - WEBAPP_ROOT=/app/webapp/react/shadcn diff --git a/server/api.js b/server/api.js index 2b36315..42dd0ce 100644 --- a/server/api.js +++ b/server/api.js @@ -10,6 +10,7 @@ const port = 3000; app.use(cors()); app.use(express.json()); +console.log(`WEBAPP_ROOT=${process.env.WEBAPP_ROOT}`); const sqlite3 = require("sqlite3"); const db = new sqlite3.Database("openv0.sqlite", (err) => { diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..0e07132 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,16 @@ +[supervisord] +nodaemon=true + +[program:server] +command=node api.js +directory=/app/server +autostart=true +autorestart=true +stdout_logfile=/app/server/server.log + +[program:webapp] +command=npm run dev -- --host +directory=%(ENV_WEBAPP_ROOT)s +autostart=true +autorestart=true +stdout_logfile=/app/webapp/webapp.log diff --git a/webapps-starters/react/flowbite/src/views/browse.tsx b/webapps-starters/react/flowbite/src/views/browse.tsx index 09c1651..fd89c44 100644 --- a/webapps-starters/react/flowbite/src/views/browse.tsx +++ b/webapps-starters/react/flowbite/src/views/browse.tsx @@ -15,10 +15,11 @@ export default function Browse() { const [openv0ComponentsList, setOpenv0ComponentsList] = useState([]); const svgStyle = { fill: '#777' }; - + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; + async function fetchComponents(){ const response = await fetch( - `http://localhost:3000/components/list?framework=react&components=flowbite&icons=lucide` + `${SERVER_URL}/components/list?framework=react&components=flowbite&icons=lucide` ); const data = await response.json(); console.log(data) @@ -69,7 +70,7 @@ export default function Browse() { description: generateMode === `description` ? userInputDescription : userInputJson, }) const response = await fetch( - `http://localhost:3000/components/new/${generateMode}` , + `${SERVER_URL}/components/new/${generateMode}` , { method: "POST", headers: { diff --git a/webapps-starters/react/flowbite/src/views/view.tsx b/webapps-starters/react/flowbite/src/views/view.tsx index 6265b4f..fd10569 100644 --- a/webapps-starters/react/flowbite/src/views/view.tsx +++ b/webapps-starters/react/flowbite/src/views/view.tsx @@ -19,6 +19,7 @@ function App() { const [loadedComponents, setLoadedComponents] = useState([]); const svgStyle = { fill: '#777' }; + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; async function shareComponent() { if (processing) return; @@ -26,7 +27,7 @@ function App() { setProcessing(true) const response = await fetch( - `http://localhost:3000/components/share` , + `${SERVER_URL}/components/share` , { method: "POST", headers: { @@ -59,7 +60,7 @@ function App() { setComponentVersions([...[]]) setLoadedComponents([...[]]) const response = await fetch( - `http://localhost:3000/components/get?framework=react&components=flowbite&icons=lucide&name=${name}` + `${SERVER_URL}/components/get?framework=react&components=flowbite&icons=lucide&name=${name}` ); const data = await response.json(); console.log(data) @@ -104,7 +105,7 @@ function App() { setComponentStream('') let received_stream = '' const response = await fetch( - `http://localhost:3000/components/iterate/description` , + `${SERVER_URL}/components/iterate/description` , { method: "POST", headers: { diff --git a/webapps-starters/react/flowbite/vite.config.ts b/webapps-starters/react/flowbite/vite.config.ts index 5a33944..3810cf4 100644 --- a/webapps-starters/react/flowbite/vite.config.ts +++ b/webapps-starters/react/flowbite/vite.config.ts @@ -1,7 +1,19 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +const SERVER_HOST = process.env.SERVER_HOST || 'http://localhost:3000' +console.log('SERVER_HOST', SERVER_HOST) + // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], + server: { + proxy: { + '/api': { + target: `${SERVER_HOST}`, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + } + }, }) diff --git a/webapps-starters/react/nextui/src/views/browse.tsx b/webapps-starters/react/nextui/src/views/browse.tsx index d44bcd9..f78257a 100644 --- a/webapps-starters/react/nextui/src/views/browse.tsx +++ b/webapps-starters/react/nextui/src/views/browse.tsx @@ -15,10 +15,11 @@ export default function Browse() { const [openv0ComponentsList, setOpenv0ComponentsList] = useState([]); const svgStyle = { fill: '#777' }; + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; async function fetchComponents(){ const response = await fetch( - `http://localhost:3000/components/list?framework=react&components=nextui&icons=lucide` + `${SERVER_URL}/components/list?framework=react&components=nextui&icons=lucide` ); const data = await response.json(); console.log(data) @@ -69,7 +70,7 @@ export default function Browse() { description: generateMode === `description` ? userInputDescription : userInputJson, }) const response = await fetch( - `http://localhost:3000/components/new/${generateMode}` , + `${SERVER_URL}/components/new/${generateMode}` , { method: "POST", headers: { diff --git a/webapps-starters/react/nextui/src/views/view.tsx b/webapps-starters/react/nextui/src/views/view.tsx index 0304963..54679a2 100644 --- a/webapps-starters/react/nextui/src/views/view.tsx +++ b/webapps-starters/react/nextui/src/views/view.tsx @@ -19,6 +19,7 @@ function App() { const [loadedComponents, setLoadedComponents] = useState([]); const svgStyle = { fill: '#777' }; + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; async function shareComponent() { if (processing) return; @@ -26,7 +27,7 @@ function App() { setProcessing(true) const response = await fetch( - `http://localhost:3000/components/share` , + `${SERVER_URL}/components/share` , { method: "POST", headers: { @@ -59,7 +60,7 @@ function App() { setComponentVersions([...[]]) setLoadedComponents([...[]]) const response = await fetch( - `http://localhost:3000/components/get?framework=react&components=nextui&icons=lucide&name=${name}` + `${SERVER_URL}/components/get?framework=react&components=nextui&icons=lucide&name=${name}` ); const data = await response.json(); console.log(data) @@ -104,7 +105,7 @@ function App() { setComponentStream('') let received_stream = '' const response = await fetch( - `http://localhost:3000/components/iterate/description` , + `${SERVER_URL}/components/iterate/description` , { method: "POST", headers: { diff --git a/webapps-starters/react/nextui/vite.config.ts b/webapps-starters/react/nextui/vite.config.ts index 5a33944..13bd3e3 100644 --- a/webapps-starters/react/nextui/vite.config.ts +++ b/webapps-starters/react/nextui/vite.config.ts @@ -1,7 +1,18 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +const SERVER_HOST = process.env.SERVER_HOST || 'http://localhost:3000' +console.log('SERVER_HOST', SERVER_HOST) // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], + server: { + proxy: { + '/api': { + target: `${SERVER_HOST}`, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + } + }, }) diff --git a/webapps-starters/react/shadcn/src/components/views/component-view.tsx b/webapps-starters/react/shadcn/src/components/views/component-view.tsx index b38299c..cbd5ba5 100644 --- a/webapps-starters/react/shadcn/src/components/views/component-view.tsx +++ b/webapps-starters/react/shadcn/src/components/views/component-view.tsx @@ -22,9 +22,8 @@ export default function ComponentView() { const location = useLocation(); const searchParams = new URLSearchParams(location.search); const componentId = searchParams.get('componentId') - - - + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; + const handleInputChange = (e) => { setUserInput(e.target.value); }; @@ -39,7 +38,7 @@ export default function ComponentView() { try { // Make the API POST request with the user input - const response = await fetch('http://localhost:3000/component/iterate', { + const response = await fetch(`${SERVER_URL}/component/iterate`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -97,7 +96,7 @@ export default function ComponentView() { setSelectedComponentIndex( components.length-1 ) console.log(selectedComponentIndex) } - fetch(`http://localhost:3000/component/ping/?from=View Component&component=${componentId}`); + fetch(`${SERVER_URL}/component/ping/?from=View Component&component=${componentId}`); }); diff --git a/webapps-starters/react/shadcn/src/views/browse.tsx b/webapps-starters/react/shadcn/src/views/browse.tsx index 630591a..bd49062 100644 --- a/webapps-starters/react/shadcn/src/views/browse.tsx +++ b/webapps-starters/react/shadcn/src/views/browse.tsx @@ -15,10 +15,11 @@ export default function Browse() { const [openv0ComponentsList, setOpenv0ComponentsList] = useState([]); const svgStyle = { fill: '#777' }; - + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; + async function fetchComponents(){ const response = await fetch( - `http://localhost:3000/components/list?framework=react&components=shadcn&icons=lucide` + `${SERVER_URL}/components/list?framework=react&components=shadcn&icons=lucide` ); const data = await response.json(); console.log(data) @@ -69,7 +70,7 @@ export default function Browse() { description: generateMode === `description` ? userInputDescription : userInputJson, }) const response = await fetch( - `http://localhost:3000/components/new/${generateMode}` , + `${SERVER_URL}/components/new/${generateMode}` , { method: "POST", headers: { diff --git a/webapps-starters/react/shadcn/src/views/view.tsx b/webapps-starters/react/shadcn/src/views/view.tsx index 2f27058..fb1232f 100644 --- a/webapps-starters/react/shadcn/src/views/view.tsx +++ b/webapps-starters/react/shadcn/src/views/view.tsx @@ -19,14 +19,14 @@ function App() { const [loadedComponents, setLoadedComponents] = useState([]); const svgStyle = { fill: '#777' }; - + const SERVER_URL = import.meta.env.VITE_SERVER_URL || "/api"; async function shareComponent() { if (processing) return; if (!userApiKey) return; setProcessing(true) const response = await fetch( - `http://localhost:3000/components/share` , + `${SERVER_URL}/components/share` , { method: "POST", headers: { @@ -59,7 +59,7 @@ function App() { setComponentVersions([...[]]) setLoadedComponents([...[]]) const response = await fetch( - `http://localhost:3000/components/get?framework=react&components=shadcn&icons=lucide&name=${name}` + `${SERVER_URL}/components/get?framework=react&components=shadcn&icons=lucide&name=${name}` ); const data = await response.json(); console.log(data) @@ -104,7 +104,7 @@ function App() { setComponentStream('') let received_stream = '' const response = await fetch( - `http://localhost:3000/components/iterate/description` , + `${SERVER_URL}/components/iterate/description` , { method: "POST", headers: { diff --git a/webapps-starters/react/shadcn/vite.config.ts b/webapps-starters/react/shadcn/vite.config.ts index 26f0066..c2cbd52 100644 --- a/webapps-starters/react/shadcn/vite.config.ts +++ b/webapps-starters/react/shadcn/vite.config.ts @@ -1,6 +1,8 @@ import path from "path" import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; +const SERVER_HOST = process.env.SERVER_HOST || 'http://localhost:3000' +console.log('SERVER_HOST', SERVER_HOST) // https://vitejs.dev/config/ export default defineConfig({ @@ -10,4 +12,13 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + server: { + proxy: { + '/api': { + target: `${SERVER_HOST}`, + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + } + }, }); From 218ef668cd3803a0c2856fd750eb3175823195f8 Mon Sep 17 00:00:00 2001 From: Raphael Shorser Date: Thu, 26 Oct 2023 06:38:50 -0700 Subject: [PATCH 2/3] update readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index dd78183..0464c25 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,18 @@ To do so : --- +**Via Docker** - you can also clone this repo and run via docker (using react as a framework). This will run both server and UI in the same container. + +To do so: +* clone and navigate to this repo +* Build the docker image `docker build -f Dockerfile . -t openv0:latest` +* Update the `OPENAI_API_KEY` variable in `docker-compose.yaml` with your OAI key +* Choose which webapp starter by updating the `WEBAPP_ROOT` variable in `docker-compose.yaml` +* Run docker compose: `docker-compose up openv0` +* UI should now be accesible at http://localhost:3000 + + + # Try openv0 You can try openv0 (using React as a framework) with minimal configuration below From ff3919fc0bc18f8521a4ffbdacdb75930245a044 Mon Sep 17 00:00:00 2001 From: Raphael Shorser Date: Fri, 27 Oct 2023 05:49:43 -0700 Subject: [PATCH 3/3] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0464c25..90d72ad 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ To do so: * Update the `OPENAI_API_KEY` variable in `docker-compose.yaml` with your OAI key * Choose which webapp starter by updating the `WEBAPP_ROOT` variable in `docker-compose.yaml` * Run docker compose: `docker-compose up openv0` -* UI should now be accesible at http://localhost:3000 +* UI should now be accesible at http://localhost:5173