FadeClip commited on
Commit
93c0ec9
Β·
1 Parent(s): 97f7b3d

Add API key authentication and tech specifications document

Browse files
Files changed (7) hide show
  1. .gitignore +1 -0
  2. Dockerfile +2 -0
  3. chat.html +8 -0
  4. main.py +19 -2
  5. static/script.js +23 -1
  6. static/style.css +35 -0
  7. tech_spec.md +181 -0
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ "__pycache__/"
Dockerfile CHANGED
@@ -8,6 +8,8 @@ ENV OLLAMA_MODEL=qwen3:4b
8
  # --- ENVIRONMENT VARIABLES ---
9
  ENV OLLAMA_HOST=0.0.0.0
10
  ENV OLLAMA_ORIGINS='*'
 
 
11
 
12
  # 1. Install Python and PIP
13
  RUN apt-get update && apt-get install -y python3 python3-pip curl
 
8
  # --- ENVIRONMENT VARIABLES ---
9
  ENV OLLAMA_HOST=0.0.0.0
10
  ENV OLLAMA_ORIGINS='*'
11
+ # Optional: Set your own API key as environment variable, otherwise one will be auto-generated
12
+ # ENV OLLAMA_API_KEY=your_secure_api_key_here
13
 
14
  # 1. Install Python and PIP
15
  RUN apt-get update && apt-get install -y python3 python3-pip curl
chat.html CHANGED
@@ -8,6 +8,14 @@
8
  </head>
9
  <body>
10
  <div id="chat-container">
 
 
 
 
 
 
 
 
11
  <div id="chat-window">
12
  <ul id="message-list"></ul>
13
  </div>
 
8
  </head>
9
  <body>
10
  <div id="chat-container">
11
+ <div id="header">
12
+ <h1>Ollama Chat</h1>
13
+ <div id="api-key-section">
14
+ <label for="api-key-input">API Key:</label>
15
+ <input type="password" id="api-key-input" placeholder="Enter API key">
16
+ <button id="save-api-key">Save</button>
17
+ </div>
18
+ </div>
19
  <div id="chat-window">
20
  <ul id="message-list"></ul>
21
  </div>
main.py CHANGED
@@ -1,16 +1,33 @@
 
 
1
  import requests
2
- from fastapi import FastAPI, Request, HTTPException
3
  from fastapi.responses import FileResponse, RedirectResponse, StreamingResponse
4
  from fastapi.staticfiles import StaticFiles
 
 
 
 
 
5
 
6
  OLLAMA_API_URL = "http://localhost:11434"
7
 
8
  app = FastAPI()
 
9
 
10
  app.mount("/static", StaticFiles(directory="static"), name="static")
11
 
 
 
 
 
 
 
 
 
 
12
  @app.post("/chat_api")
13
- async def chat_endpoint(request: Request):
14
  body = await request.json()
15
  model = body.get("model", "qwen3:4b")
16
  prompt = body.get("prompt")
 
1
+ import secrets
2
+ import os
3
  import requests
4
+ from fastapi import FastAPI, Request, HTTPException, Depends
5
  from fastapi.responses import FileResponse, RedirectResponse, StreamingResponse
6
  from fastapi.staticfiles import StaticFiles
7
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
8
+
9
+ # Generate a secure API key if not provided as environment variable
10
+ API_KEY = os.getenv("OLLAMA_API_KEY") or secrets.token_urlsafe(32)
11
+ print(f"API Key: {API_KEY}") # This will be shown in the console during startup
12
 
13
  OLLAMA_API_URL = "http://localhost:11434"
14
 
15
  app = FastAPI()
16
+ security = HTTPBearer()
17
 
18
  app.mount("/static", StaticFiles(directory="static"), name="static")
19
 
20
+ def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
21
+ """Verify that the provided API key is valid"""
22
+ if credentials.credentials != API_KEY:
23
+ raise HTTPException(
24
+ status_code=401,
25
+ detail="Invalid API key"
26
+ )
27
+ return credentials.credentials
28
+
29
  @app.post("/chat_api")
30
+ async def chat_endpoint(request: Request, api_key: str = Depends(verify_api_key)):
31
  body = await request.json()
32
  model = body.get("model", "qwen3:4b")
33
  prompt = body.get("prompt")
static/script.js CHANGED
@@ -1,7 +1,28 @@
1
  const messageList = document.getElementById('message-list');
2
  const messageInput = document.getElementById('message-input');
3
  const sendButton = document.getElementById('send-button');
 
 
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  sendButton.addEventListener('click', sendMessage);
6
  messageInput.addEventListener('keydown', (event) => {
7
  if (event.key === 'Enter') {
@@ -23,7 +44,8 @@ function sendMessage() {
23
  fetch('/chat_api', {
24
  method: 'POST',
25
  headers: {
26
- 'Content-Type': 'application/json'
 
27
  },
28
  body: JSON.stringify({
29
  model: 'qwen3:4b', // You can change the model here
 
1
  const messageList = document.getElementById('message-list');
2
  const messageInput = document.getElementById('message-input');
3
  const sendButton = document.getElementById('send-button');
4
+ const apiKeyInput = document.getElementById('api-key-input');
5
+ const saveApiKeyButton = document.getElementById('save-api-key');
6
 
7
+ // Get API key from localStorage
8
+ let apiKey = localStorage.getItem('ollama_api_key');
9
+ if (apiKey) {
10
+ apiKeyInput.value = apiKey;
11
+ }
12
+
13
+ // Save API key button event
14
+ saveApiKeyButton.addEventListener('click', () => {
15
+ const newApiKey = apiKeyInput.value.trim();
16
+ if (newApiKey) {
17
+ localStorage.setItem('ollama_api_key', newApiKey);
18
+ apiKey = newApiKey;
19
+ alert('API key saved successfully!');
20
+ } else {
21
+ alert('Please enter a valid API key');
22
+ }
23
+ });
24
+
25
+ // Send message function
26
  sendButton.addEventListener('click', sendMessage);
27
  messageInput.addEventListener('keydown', (event) => {
28
  if (event.key === 'Enter') {
 
44
  fetch('/chat_api', {
45
  method: 'POST',
46
  headers: {
47
+ 'Content-Type': 'application/json',
48
+ 'Authorization': `Bearer ${apiKey}`
49
  },
50
  body: JSON.stringify({
51
  model: 'qwen3:4b', // You can change the model here
static/style.css CHANGED
@@ -15,6 +15,41 @@ body {
15
  border-right: 1px solid #ccc;
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  #chat-window {
19
  flex-grow: 1;
20
  overflow-y: auto;
 
15
  border-right: 1px solid #ccc;
16
  }
17
 
18
+ #header {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+ padding: 10px 20px;
23
+ border-bottom: 1px solid #ccc;
24
+ background-color: #f9f9f9;
25
+ }
26
+
27
+ #header h1 {
28
+ margin: 0;
29
+ font-size: 1.5em;
30
+ }
31
+
32
+ #api-key-section {
33
+ display: flex;
34
+ align-items: center;
35
+ gap: 10px;
36
+ }
37
+
38
+ #api-key-input {
39
+ padding: 5px;
40
+ border: 1px solid #ccc;
41
+ border-radius: 3px;
42
+ }
43
+
44
+ #save-api-key {
45
+ background-color: #007cba;
46
+ color: white;
47
+ border: none;
48
+ padding: 5px 10px;
49
+ border-radius: 3px;
50
+ cursor: pointer;
51
+ }
52
+
53
  #chat-window {
54
  flex-grow: 1;
55
  overflow-y: auto;
tech_spec.md ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OllamaSpace Technical Specifications
2
+
3
+ ## Project Overview
4
+ OllamaSpace is a web-based chat application that serves as a frontend interface for interacting with Ollama language models. The application provides a real-time chat interface where users can communicate with AI models through a web browser.
5
+
6
+ ## Architecture
7
+
8
+ ### Backend
9
+ - **Framework**: FastAPI (Python)
10
+ - **API Gateway**: Acts as a proxy between the frontend and Ollama API
11
+ - **Streaming**: Supports real-time streaming of model responses
12
+ - **Default Model**: qwen3:4b
13
+
14
+ ### Frontend
15
+ - **Technology**: Pure HTML/CSS/JavaScript (no frameworks)
16
+ - **Interface**: Simple chat interface with message history
17
+ - **Interaction**: Real-time message streaming with typing indicators
18
+ - **Styling**: Clean, minimal design with distinct user/bot message styling
19
+
20
+ ## Components
21
+
22
+ ### main.py
23
+ - **Framework**: FastAPI
24
+ - **Authentication**: Implements Bearer token authentication using HTTPBearer
25
+ - **Endpoints**:
26
+ - `GET /` - Redirects to `/chat`
27
+ - `GET /chat` - Serves the chat HTML page
28
+ - `POST /chat_api` - API endpoint that forwards requests to Ollama (requires authentication)
29
+ - **Functionality**:
30
+ - Proxies requests to local Ollama API (http://localhost:11434)
31
+ - Streams model responses back to the frontend
32
+ - Handles error cases and validation
33
+ - Auto-generates secure API key if not provided via environment variable
34
+
35
+ ### chat.html
36
+ - **Template**: HTML structure for the chat interface with API key management
37
+ - **Layout**:
38
+ - Header with API key input and save button
39
+ - Chat window area with message history
40
+ - Message input field
41
+ - Send button
42
+ - **Static Assets**: Links to CSS and JavaScript files
43
+
44
+ ### static/script.js
45
+ - **Features**:
46
+ - Real-time message streaming from the API
47
+ - Message display in chat format
48
+ - Enter key support for sending messages
49
+ - Stream parsing to handle JSON responses
50
+ - API key management with localStorage persistence
51
+ - API key input UI with save functionality
52
+ - **API Communication**:
53
+ - Includes API key in Authorization header as Bearer token
54
+ - POSTs to `/chat_api` endpoint
55
+ - Receives streaming responses and displays incrementally
56
+ - Handles error cases gracefully
57
+
58
+ ### static/style.css
59
+ - **Design**: Minimal, clean chat interface with API key management section
60
+ - **Styling**:
61
+ - Distinct colors for user vs. bot messages
62
+ - Responsive layout
63
+ - API key section in header with input field and save button
64
+ - Auto-scrolling to latest messages
65
+
66
+ ## Deployment
67
+
68
+ ### Dockerfile
69
+ - **Base Image**: ollama/ollama
70
+ - **Environment**: Sets up Ollama server and FastAPI gateway
71
+ - **Port Configuration**: Listens on port 7860 (Hugging Face Spaces default)
72
+ - **Model Setup**: Downloads specified model during build process
73
+ - **Dependencies**: Installs Python, FastAPI, and related libraries
74
+
75
+ ### start.sh
76
+ - **Initialization Sequence**:
77
+ 1. Starts Ollama server in background
78
+ 2. Health checks the Ollama server
79
+ 3. Starts FastAPI gateway on port 7860
80
+ - **Error Handling**: Waits for Ollama to be ready before starting the gateway
81
+ - **API Key**: If auto-generated, the API key will be displayed in the console logs during startup
82
+
83
+ ## Configuration
84
+
85
+ ### Environment Variables
86
+ - `OLLAMA_HOST`: 0.0.0.0 (allows external connections)
87
+ - `OLLAMA_ORIGINS`: '*' (allows CORS requests)
88
+ - `OLLAMA_MODEL`: qwen3:4b (default model, can be overridden)
89
+ - `OLLAMA_API_KEY`: (optional) Secure API key (auto-generated if not provided)
90
+
91
+ ### Default Model
92
+ - **Model**: qwen3:4b
93
+ - **Fallback**: If no model specified in request, uses qwen3:4b
94
+
95
+ ### API Key Management
96
+ - **Generation**: If no OLLAMA_API_KEY environment variable is set, a cryptographically secure random key is generated at startup
97
+ - **Access**: Generated API key is displayed in the application logs during startup
98
+ - **Frontend Storage**: API key is stored in browser's localStorage after being entered once
99
+ - **Authentication**: All API requests require a valid Bearer token in the Authorization header
100
+
101
+ ## API Specification
102
+
103
+ ### `/chat_api` Endpoint
104
+ - **Method**: POST
105
+ - **Authentication**: Requires Bearer token in Authorization header
106
+ - **Content-Type**: application/json
107
+ - **Request Headers**:
108
+ - `Authorization`: Bearer {your_api_key}
109
+ - `Content-Type`: application/json
110
+ - **Request Body**:
111
+ ```json
112
+ {
113
+ "model": "string (optional, defaults to qwen3:4b)",
114
+ "prompt": "string (required)"
115
+ }
116
+ ```
117
+ - **Response**: Streaming response with incremental model output
118
+ - **Error Handling**:
119
+ - Returns 401 for invalid API key
120
+ - Returns 400 for missing prompt
121
+
122
+ ### Data Flow
123
+ 1. Frontend sends user message to `/chat_api`
124
+ 2. Backend forwards request to local Ollama API
125
+ 3. Ollama processes request with specified model
126
+ 4. Response is streamed back to frontend in real-time
127
+ 5. Frontend displays response incrementally as it arrives
128
+
129
+ ## Security Considerations
130
+ - **API Key Authentication**: Required for all API access using Bearer token authentication
131
+ - **Secure Key Generation**: API key is auto-generated using cryptographically secure random generator (secrets.token_urlsafe(32))
132
+ - **Configurable Keys**: API key can be set via environment variable (OLLAMA_API_KEY) or auto-generated
133
+ - **Storage**: Client-side API key stored in browser's localStorage
134
+ - **CORS**: Enabled for all origins (potential security concern in production)
135
+ - **Input Validation**: Validates presence of prompt parameter
136
+ - **Local API**: Communicates with Ollama through localhost only
137
+ - **Key Exposure**: Auto-generated API key is displayed in console logs during startup (should be secured in production)
138
+
139
+ ## Performance Features
140
+ - **Streaming**: Real-time response streaming for better UX
141
+ - **Client-side Display**: Incremental message display as responses arrive
142
+ - **Efficient Communication**: Uses streaming HTTP responses to minimize latency
143
+
144
+ ## Security Features
145
+ - **Authentication**: Bearer token authentication for all API endpoints
146
+ - **Key Generation**: Cryptographically secure random API key generation using secrets module
147
+ - **Key Storage**: API key stored in browser localStorage (with option to enter via UI)
148
+ - **Transport Security**: API key transmitted via Authorization header (should use HTTPS in production)
149
+
150
+ ## Technologies Used
151
+ - **Backend**: Python, FastAPI
152
+ - **Frontend**: HTML5, CSS3, JavaScript (ES6+)
153
+ - **Containerization**: Docker
154
+ - **AI Model**: Ollama with qwen3:4b by default
155
+ - **Web Server**: Uvicorn ASGI server
156
+
157
+ ## File Structure
158
+ ```
159
+ OllamaSpace/
160
+ β”œβ”€β”€ main.py (FastAPI application)
161
+ β”œβ”€β”€ chat.html (Chat interface)
162
+ β”œβ”€β”€ start.sh (Container startup script)
163
+ β”œβ”€β”€ Dockerfile (Container configuration)
164
+ β”œβ”€β”€ README.md (Project description)
165
+ β”œβ”€β”€ static/
166
+ β”‚ β”œβ”€β”€ script.js (Frontend JavaScript)
167
+ β”‚ └── style.css (Frontend styling)
168
+ ```
169
+
170
+ ## Build Process
171
+ 1. Container built with Ollama and Python dependencies
172
+ 2. Model specified by OLLAMA_MODEL environment variable is pre-pulled
173
+ 3. Application files are copied into container
174
+ 4. FastAPI dependencies are installed
175
+ 5. Container starts with Ollama server and FastAPI gateway
176
+
177
+ ## Deployment Target
178
+ - **Platform**: Designed for Hugging Face Spaces
179
+ - **Port**: 7860 (standard for Hugging Face Spaces)
180
+ - **Runtime**: Docker container
181
+ - **Model Serving**: Ollama with FastAPI gateway