浏览代码

Merge pull request #111 from ollama-webui/dev

WIP feat: backend reverse proxy
Timothy Jaeryang Baek 1 年之前
父节点
当前提交
29788ca3d6
共有 18 个文件被更改,包括 228 次插入66 次删除
  1. 22 6
      Dockerfile
  2. 43 47
      README.md
  3. 2 0
      backend/.gitignore
  4. 55 0
      backend/apps/ollama/main.py
  5. 15 0
      backend/config.py
  6. 1 0
      backend/dev.sh
  7. 51 0
      backend/main.py
  8. 19 0
      backend/requirements.txt
  9. 1 0
      backend/start.sh
  10. 4 4
      compose.yaml
  11. 二进制
      demo.gif
  12. 2 2
      package.json
  13. 1 1
      run.sh
  14. 1 1
      src/lib/constants.ts
  15. 7 1
      src/routes/+error.svelte
  16. 2 2
      src/routes/+layout.js
  17. 1 1
      src/routes/+page.svelte
  18. 1 1
      svelte.config.js

+ 22 - 6
Dockerfile

@@ -1,20 +1,36 @@
 # syntax=docker/dockerfile:1
 # syntax=docker/dockerfile:1
 
 
-FROM node:alpine
-WORKDIR /app
+FROM node:alpine as build
 
 
-ARG OLLAMA_API_BASE_URL=''
+ARG OLLAMA_API_BASE_URL='/ollama/api'
 RUN echo $OLLAMA_API_BASE_URL
 RUN echo $OLLAMA_API_BASE_URL
 
 
-ENV ENV prod
-
 ENV PUBLIC_API_BASE_URL $OLLAMA_API_BASE_URL
 ENV PUBLIC_API_BASE_URL $OLLAMA_API_BASE_URL
 RUN echo $PUBLIC_API_BASE_URL
 RUN echo $PUBLIC_API_BASE_URL
 
 
+WORKDIR /app
+
 COPY package.json package-lock.json ./ 
 COPY package.json package-lock.json ./ 
 RUN npm ci
 RUN npm ci
 
 
 COPY . .
 COPY . .
 RUN npm run build
 RUN npm run build
 
 
-CMD [ "npm", "run", "start"]
+FROM python:3.11-slim-buster as base
+
+ARG OLLAMA_API_BASE_URL='/ollama/api'
+
+ENV ENV=prod
+ENV OLLAMA_API_BASE_URL $OLLAMA_API_BASE_URL
+
+WORKDIR /app
+COPY --from=build /app/build /app/build
+
+WORKDIR /app/backend
+
+COPY ./backend/requirements.txt ./requirements.txt
+RUN pip3 install -r requirements.txt
+
+COPY ./backend .
+
+CMD [ "sh", "start.sh"]

+ 43 - 47
README.md

@@ -47,21 +47,19 @@ ChatGPT-Style Web Interface for Ollama 🦙
 
 
 - ⚙️ **Fine-Tuned Control with Advanced Parameters**: Gain a deeper level of control by adjusting parameters such as temperature and defining your system prompts to tailor the conversation to your specific preferences and needs.
 - ⚙️ **Fine-Tuned Control with Advanced Parameters**: Gain a deeper level of control by adjusting parameters such as temperature and defining your system prompts to tailor the conversation to your specific preferences and needs.
 
 
+- 🔐 **Auth Header Support**: Effortlessly enhance security by adding Authorization headers to Ollama requests directly from the web UI settings, ensuring access to secured Ollama servers.
+
 - 🔗 **External Ollama Server Connection**: Seamlessly link to an external Ollama server hosted on a different address by configuring the environment variable during the Docker build phase. Additionally, you can also set the external server connection URL from the web UI post-build.
 - 🔗 **External Ollama Server Connection**: Seamlessly link to an external Ollama server hosted on a different address by configuring the environment variable during the Docker build phase. Additionally, you can also set the external server connection URL from the web UI post-build.
 
 
-- 🔑 **Auth Header Support**: Securely access Ollama servers with added Authorization headers for enhanced authentication.
+- 🔒 **Backend Reverse Proxy Support**: Strengthen security by enabling direct communication between Ollama Web UI backend and Ollama, eliminating the need to expose Ollama over LAN.
 
 
 - 🌟 **Continuous Updates**: We are committed to improving Ollama Web UI with regular updates and new features.
 - 🌟 **Continuous Updates**: We are committed to improving Ollama Web UI with regular updates and new features.
 
 
 ## How to Install 🚀
 ## How to Install 🚀
 
 
-### Prerequisites
-
-Make sure you have the latest version of Ollama installed before proceeding with the installation. You can find the latest version of Ollama at [https://ollama.ai/](https://ollama.ai/).
-
-#### Installing Both Ollama and Ollama Web UI Using Docker Compose
+### Installing Both Ollama and Ollama Web UI Using Docker Compose
 
 
-If you don't have Ollama installed, you can also use the provided Docker Compose file for a hassle-free installation. Simply run the following command:
+If you don't have Ollama installed yet, you can use the provided Docker Compose file for a hassle-free installation. Simply run the following command:
 
 
 ```bash
 ```bash
 docker compose up --build
 docker compose up --build
@@ -69,86 +67,84 @@ docker compose up --build
 
 
 This command will install both Ollama and Ollama Web UI on your system. Ensure to modify the `compose.yaml` file for GPU support if needed.
 This command will install both Ollama and Ollama Web UI on your system. Ensure to modify the `compose.yaml` file for GPU support if needed.
 
 
-#### Checking Ollama
+### Installing Ollama Web UI Only
 
 
-After installing, verify that Ollama is running by accessing the following link in your web browser: [http://127.0.0.1:11434/](http://127.0.0.1:11434/). Note that the port number may differ based on your system configuration.
+#### Prerequisites
 
 
-#### Accessing Ollama Web Interface over LAN
+Make sure you have the latest version of Ollama installed before proceeding with the installation. You can find the latest version of Ollama at [https://ollama.ai/](https://ollama.ai/).
 
 
-If you want to access the Ollama web interface over LAN, for example, from your phone, run Ollama using the following command:
+##### Checking Ollama
 
 
-```bash
-OLLAMA_HOST=0.0.0.0 OLLAMA_ORIGINS=* ollama serve
-```
+After installing Ollama, verify that Ollama is running by accessing the following link in your web browser: [http://127.0.0.1:11434/](http://127.0.0.1:11434/). Note that the port number may differ based on your system configuration.
 
 
-In case you encounter any issues running the command and encounter errors, ensure to turn off any existing Ollama service that might be running in the background before retrying.
+#### Using Docker 🐳
 
 
-If you're running Ollama via Docker:
+If Ollama is hosted on your local machine and accessible at [http://127.0.0.1:11434/](http://127.0.0.1:11434/), run the following command:
 
 
 ```bash
 ```bash
-docker run -d -v ollama:/root/.ollama -p 11434:11434 -e OLLAMA_ORIGINS="*" --name ollama ollama/ollama
+docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main
 ```
 ```
 
 
-### Using Docker 🐳
-
-If Ollama is hosted on your local machine, run the following command:
+Alternatively, if you prefer to build the container yourself, use the following command:
 
 
 ```bash
 ```bash
-docker run -d -p 3000:8080 --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main
+docker build -t ollama-webui .
+docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway --name ollama-webui --restart always ollama-webui
 ```
 ```
 
 
-Alternatively, if you prefer to build the container yourself, use the following command:
+Your Ollama Web UI should now be hosted at [http://localhost:3000](http://localhost:3000) and accessible over LAN (or Network). Enjoy! 😄
 
 
-```bash
-docker build --build-arg OLLAMA_API_BASE_URL='' -t ollama-webui .
-docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui
-```
+#### Accessing External Ollama on a Different Server
 
 
-Your Ollama Web UI should now be hosted at [http://localhost:3000](http://localhost:3000). Enjoy! 😄
+Change `OLLAMA_API_BASE_URL` environment variable to match the external Ollama Server url:
 
 
-#### Connecting to Ollama on a Different Server
+```bash
+docker run -d -p 3000:8080 -e OLLAMA_API_BASE_URL=https://example.com/api --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main
+```
 
 
-If Ollama is hosted on a server other than your local machine, change `OLLAMA_API_BASE_URL` to match:
+Alternatively, if you prefer to build the container yourself, use the following command:
 
 
 ```bash
 ```bash
-docker build --build-arg OLLAMA_API_BASE_URL='https://example.com/api' -t ollama-webui .
-docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui
+docker build -t ollama-webui .
+docker run -d -p 3000:8080 -e OLLAMA_API_BASE_URL=https://example.com/api --name ollama-webui --restart always ollama-webui
 ```
 ```
 
 
 ## How to Build for Static Deployment
 ## How to Build for Static Deployment
 
 
-1. Install `node`
+1. Clone & Enter the project
 
 
    ```sh
    ```sh
-   # Mac, Linux
-   curl https://webi.sh/node@lts | sh
-   source ~/.config/envman/PATH.env
+   git clone https://github.com/ollama-webui/ollama-webui.git
+   pushd ./ollama-webui/
    ```
    ```
 
 
-   ```pwsh
-   # Windows
-   curl.exe https://webi.ms/node@lts | powershell
-   ```
+2. Create and edit `.env`
 
 
-2. Clone & Enter the project
    ```sh
    ```sh
-   git clone https://github.com/ollama-webui/ollama-webui.git
-   pushd ./ollama-webui/
+   cp -RPp example.env .env
    ```
    ```
-3. Create and edit `.env`
+
+3. Install node dependencies
+
    ```sh
    ```sh
-   cp -RPp example.env .env
+   npm i
    ```
    ```
+
 4. Run in dev mode, or build the site for deployment
 4. Run in dev mode, or build the site for deployment
+
    - Test in Dev mode:
    - Test in Dev mode:
+
      ```sh
      ```sh
      npm run dev
      npm run dev
      ```
      ```
-   - Build for Deploy: \
-     (`PUBLIC_API_BASE_URL` will overwrite the value in `.env`)
+
+   - Build for Deploy:
+
      ```sh
      ```sh
+     #`PUBLIC_API_BASE_URL` will overwrite the value in `.env`
      PUBLIC_API_BASE_URL='https://example.com/api' npm run build
      PUBLIC_API_BASE_URL='https://example.com/api' npm run build
      ```
      ```
+
 5. Test the build with `caddy` (or the server of your choice)
 5. Test the build with `caddy` (or the server of your choice)
 
 
    ```sh
    ```sh
@@ -168,9 +164,9 @@ See [TROUBLESHOOTING.md](/TROUBLESHOOTING.md) for information on how to troubles
 
 
 Here are some exciting tasks on our to-do list:
 Here are some exciting tasks on our to-do list:
 
 
+- 🔐 **Access Control**: Securely manage requests to Ollama by utilizing the backend as a reverse proxy gateway, ensuring only authenticated users can send specific requests.
 - 🧪 **Research-Centric Features**: Empower researchers in the fields of LLM and HCI with a comprehensive web UI for conducting user studies. Stay tuned for ongoing feature enhancements (e.g., surveys, analytics, and participant tracking) to facilitate their research.
 - 🧪 **Research-Centric Features**: Empower researchers in the fields of LLM and HCI with a comprehensive web UI for conducting user studies. Stay tuned for ongoing feature enhancements (e.g., surveys, analytics, and participant tracking) to facilitate their research.
 - 📈 **User Study Tools**: Providing specialized tools, like heat maps and behavior tracking modules, to empower researchers in capturing and analyzing user behavior patterns with precision and accuracy.
 - 📈 **User Study Tools**: Providing specialized tools, like heat maps and behavior tracking modules, to empower researchers in capturing and analyzing user behavior patterns with precision and accuracy.
-- 🌐 **Web Browser Extension**: Seamlessly integrate our services into your browsing experience with our convenient browser extension.
 - 📚 **Enhanced Documentation**: Elevate your setup and customization experience with improved, comprehensive documentation.
 - 📚 **Enhanced Documentation**: Elevate your setup and customization experience with improved, comprehensive documentation.
 
 
 Feel free to contribute and help us make Ollama Web UI even better! 🙌
 Feel free to contribute and help us make Ollama Web UI even better! 🙌

+ 2 - 0
backend/.gitignore

@@ -0,0 +1,2 @@
+__pycache__
+.env

+ 55 - 0
backend/apps/ollama/main.py

@@ -0,0 +1,55 @@
+from flask import Flask, request, Response
+from flask_cors import CORS
+
+
+import requests
+import json
+
+
+from config import OLLAMA_API_BASE_URL
+
+app = Flask(__name__)
+CORS(
+    app
+)  # Enable Cross-Origin Resource Sharing (CORS) to allow requests from different domains
+
+# Define the target server URL
+TARGET_SERVER_URL = OLLAMA_API_BASE_URL
+
+
+@app.route("/", defaults={"path": ""}, methods=["GET", "POST", "PUT", "DELETE"])
+@app.route("/<path:path>", methods=["GET", "POST", "PUT", "DELETE"])
+def proxy(path):
+    # Combine the base URL of the target server with the requested path
+    target_url = f"{TARGET_SERVER_URL}/{path}"
+    print(target_url)
+
+    # Get data from the original request
+    data = request.get_data()
+    headers = dict(request.headers)
+
+    # Make a request to the target server
+    target_response = requests.request(
+        method=request.method,
+        url=target_url,
+        data=data,
+        headers=headers,
+        stream=True,  # Enable streaming for server-sent events
+    )
+
+    # Proxy the target server's response to the client
+    def generate():
+        for chunk in target_response.iter_content(chunk_size=8192):
+            yield chunk
+
+    response = Response(generate(), status=target_response.status_code)
+
+    # Copy headers from the target server's response to the client's response
+    for key, value in target_response.headers.items():
+        response.headers[key] = value
+
+    return response
+
+
+if __name__ == "__main__":
+    app.run(debug=True)

+ 15 - 0
backend/config.py

@@ -0,0 +1,15 @@
+import sys
+import os
+from dotenv import load_dotenv, find_dotenv
+
+load_dotenv(find_dotenv())
+
+ENV = os.environ.get("ENV", "dev")
+
+OLLAMA_API_BASE_URL = os.environ.get(
+    "OLLAMA_API_BASE_URL", "http://localhost:11434/api"
+)
+
+if ENV == "prod":
+    if OLLAMA_API_BASE_URL == "/ollama/api":
+        OLLAMA_API_BASE_URL = "http://host.docker.internal:11434/api"

+ 1 - 0
backend/dev.sh

@@ -0,0 +1 @@
+uvicorn main:app --port 8080 --reload

+ 51 - 0
backend/main.py

@@ -0,0 +1,51 @@
+import time
+import sys
+
+from fastapi import FastAPI, Request
+from fastapi.staticfiles import StaticFiles
+
+from fastapi import HTTPException
+from starlette.exceptions import HTTPException as StarletteHTTPException
+
+from fastapi.middleware.wsgi import WSGIMiddleware
+from fastapi.middleware.cors import CORSMiddleware
+
+from apps.ollama.main import app as ollama_app
+
+
+class SPAStaticFiles(StaticFiles):
+    async def get_response(self, path: str, scope):
+        try:
+            return await super().get_response(path, scope)
+        except (HTTPException, StarletteHTTPException) as ex:
+            if ex.status_code == 404:
+                return await super().get_response("index.html", scope)
+            else:
+                raise ex
+
+
+app = FastAPI()
+
+origins = ["*"]
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=origins,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+
+@app.middleware("http")
+async def check_url(request: Request, call_next):
+    start_time = int(time.time())
+    response = await call_next(request)
+    process_time = int(time.time()) - start_time
+    response.headers["X-Process-Time"] = str(process_time)
+
+    return response
+
+
+app.mount("/ollama/api", WSGIMiddleware(ollama_app))
+app.mount("/", SPAStaticFiles(directory="../build", html=True), name="spa-static-files")

+ 19 - 0
backend/requirements.txt

@@ -0,0 +1,19 @@
+fastapi
+uvicorn[standard]
+pydantic
+python-multipart
+
+flask
+flask_cors
+
+python-socketio
+python-jose
+passlib[bcrypt]
+uuid
+
+requests
+pymongo
+bcrypt
+
+PyJWT
+pyjwt[crypto]

+ 1 - 0
backend/start.sh

@@ -0,0 +1 @@
+uvicorn main:app --host 0.0.0.0 --port 8080

+ 4 - 4
compose.yaml

@@ -15,8 +15,6 @@ services:
       - ollama:/root/.ollama
       - ollama:/root/.ollama
     ports:
     ports:
       - 11434:11434
       - 11434:11434
-    environment:
-      - 'OLLAMA_ORIGINS=*'
     container_name: ollama
     container_name: ollama
     pull_policy: always
     pull_policy: always
     tty: true
     tty: true
@@ -24,16 +22,18 @@ services:
     image: ollama/ollama:latest
     image: ollama/ollama:latest
 
 
   ollama-webui:
   ollama-webui:
-    restart: unless-stopped
     build:
     build:
       context: .
       context: .
       args:
       args:
-        OLLAMA_API_BASE_URL: ''
+        OLLAMA_API_BASE_URL: '/ollama/api'
       dockerfile: Dockerfile
       dockerfile: Dockerfile
     image: ollama-webui:latest
     image: ollama-webui:latest
     container_name: ollama-webui
     container_name: ollama-webui
     ports:
     ports:
       - 3000:8080
       - 3000:8080
+    extra_hosts:
+        - host.docker.internal:host-gateway
+    restart: unless-stopped
 
 
 volumes:
 volumes:
   ollama: {}
   ollama: {}

二进制
demo.gif


+ 2 - 2
package.json

@@ -3,7 +3,7 @@
 	"version": "0.0.1",
 	"version": "0.0.1",
 	"private": true,
 	"private": true,
 	"scripts": {
 	"scripts": {
-		"start": "http-server ./build",
+		"start": "http-server ./build -P http://localhost:8080?",
 		"dev": "vite dev --host",
 		"dev": "vite dev --host",
 		"build": "vite build",
 		"build": "vite build",
 		"preview": "vite preview",
 		"preview": "vite preview",
@@ -50,4 +50,4 @@
 		"svelte-french-toast": "^1.2.0",
 		"svelte-french-toast": "^1.2.0",
 		"uuid": "^9.0.1"
 		"uuid": "^9.0.1"
 	}
 	}
-}
+}

+ 1 - 1
run.sh

@@ -1,5 +1,5 @@
 docker stop ollama-webui || true
 docker stop ollama-webui || true
 docker rm ollama-webui || true
 docker rm ollama-webui || true
 docker build -t ollama-webui .
 docker build -t ollama-webui .
-docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui
+docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway --name ollama-webui --restart always ollama-webui
 docker image prune -f
 docker image prune -f

+ 1 - 1
src/lib/constants.ts

@@ -8,7 +8,7 @@ export const API_BASE_URL =
 			: `http://localhost:11434/api`
 			: `http://localhost:11434/api`
 		: PUBLIC_API_BASE_URL;
 		: PUBLIC_API_BASE_URL;
 
 
-export const WEB_UI_VERSION = 'v1.0.0-alpha.5';
+export const WEB_UI_VERSION = 'v1.0.0-alpha.6';
 
 
 // Source: https://kit.svelte.dev/docs/modules#$env-static-public
 // Source: https://kit.svelte.dev/docs/modules#$env-static-public
 // This feature, akin to $env/static/private, exclusively incorporates environment variables
 // This feature, akin to $env/static/private, exclusively incorporates environment variables

+ 7 - 1
src/routes/+error.svelte

@@ -2,4 +2,10 @@
 	import { page } from '$app/stores';
 	import { page } from '$app/stores';
 </script>
 </script>
 
 
-<div>{$page.status}: {$page.error.message}</div>
+<div class=" bg-white dark:bg-gray-800 min-h-screen">
+	<div class=" flex h-full">
+		<div class="m-auto my-10 dark:text-gray-300 text-3xl font-semibold">
+			{$page.status}: {$page.error.message}
+		</div>
+	</div>
+</div>

+ 2 - 2
src/routes/+layout.js

@@ -1,13 +1,13 @@
 // if you want to generate a static html file
 // if you want to generate a static html file
 // for your page.
 // for your page.
 // Documentation: https://kit.svelte.dev/docs/page-options#prerender
 // Documentation: https://kit.svelte.dev/docs/page-options#prerender
-export const prerender = true;
+// export const prerender = true;
 
 
 // if you want to Generate a SPA
 // if you want to Generate a SPA
 // you have to set ssr to false.
 // you have to set ssr to false.
 // This is not the case (so set as true or comment the line)
 // This is not the case (so set as true or comment the line)
 // Documentation: https://kit.svelte.dev/docs/page-options#ssr
 // Documentation: https://kit.svelte.dev/docs/page-options#ssr
-// export const ssr = false;
+export const ssr = false;
 
 
 // How to manage the trailing slashes in the URLs
 // How to manage the trailing slashes in the URLs
 // the URL for about page witll be /about with 'ignore' (default)
 // the URL for about page witll be /about with 'ignore' (default)

+ 1 - 1
src/routes/+page.svelte

@@ -1184,7 +1184,7 @@
 									</button>
 									</button>
 								{:else}
 								{:else}
 									<button
 									<button
-										class="  self-center dark:hover:text-gray-300 disabled:text-gray-600 disabled:hover:text-gray-600 {selectedModelIdx ===
+										class="  self-center disabled:text-gray-600 disabled:hover:text-gray-600 {selectedModelIdx ===
 										0
 										0
 											? 'mr-3'
 											? 'mr-3'
 											: 'mr-7'}"
 											: 'mr-7'}"

+ 1 - 1
svelte.config.js

@@ -14,7 +14,7 @@ const config = {
 		adapter: adapter({
 		adapter: adapter({
 			pages: 'build',
 			pages: 'build',
 			assets: 'build',
 			assets: 'build',
-			fallback: null
+			fallback: 'index.html'
 		})
 		})
 	}
 	}
 };
 };