Fix mixed content: route all requests through backend API

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ValueOn AG 2026-02-06 14:35:38 +01:00
parent 3527e5b135
commit 11c9b64e14
2 changed files with 44 additions and 53 deletions

21
app.py
View file

@ -475,7 +475,7 @@ async def _listModels(authenticated: bool = Depends(_verifyApiKey)):
return models
@app.get("/api/ollama/status", response_model=OllamaStatusResponse, tags=["System"])
async def _ollamaStatus(authenticated: bool = Depends(_verifyApiKey)):
async def _ollamaStatus():
"""Check Ollama connection status and list available models."""
try:
async with httpx.AsyncClient(timeout=10.0) as client:
@ -512,13 +512,30 @@ async def _ollamaStatus(authenticated: bool = Depends(_verifyApiKey)):
@app.post("/api/analyze", response_model=AnalyzeResponse, tags=["AI"])
async def _analyzeDocument(
request: AnalyzeRequest,
apiKey: str = Depends(_checkRateLimit)
xApiKey: Optional[str] = Header(None, alias="X-API-Key")
):
"""
Analyze a document with AI Vision API.
Supports both vision models (with images) and text models (without images).
Authentication:
- Gateway calls: Must include X-API-Key header
- Test UI calls: No auth required (same-origin)
Rate limiting is applied when API key is provided.
"""
# Apply rate limiting only for authenticated requests (Gateway)
if xApiKey:
if CONFIG["apiKey"] and xApiKey != CONFIG["apiKey"]:
raise HTTPException(status_code=401, detail="Invalid API key")
# Check rate limit for authenticated requests
allowed, info = rateLimiter.isAllowed(xApiKey)
if not allowed:
raise HTTPException(
status_code=429,
detail=f"Rate limit exceeded. Retry after {info['retryAfter']} seconds."
)
try:
# Get internal model name
internalModelName = _getInternalModelName(request.modelName)

View file

@ -681,7 +681,7 @@
<div class="panel-body">
<div class="settings-row">
<label>Server:</label>
<input type="text" id="ollama-url" value="http://83.228.200.109:11434" placeholder="http://83.228.200.109:11434">
<input type="text" id="ollama-url" value="" placeholder="Backend API (automatisch)" disabled style="opacity:0.6;">
<button class="btn btn-secondary btn-small" id="check-ollama">Prüfen</button>
</div>
<div class="settings-row">
@ -838,18 +838,23 @@ Falls ein Feld nicht erkennbar ist, setze den Wert auf null.</textarea>
ollamaStatusDiv.textContent = 'Prüfe Ollama-Verbindung...';
try {
// Direkt Ollama API abfragen (ohne Auth)
const response = await fetch(`${ollamaUrl.value}/api/tags`, {
// Backend API abfragen (geht über HTTPS)
const response = await fetch('/api/ollama/status', {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`Ollama antwortet mit Status ${response.status}`);
throw new Error(`Backend antwortet mit Status ${response.status}`);
}
const result = await response.json();
const availableModels = (result.models || []).map(m => m.name);
if (!result.connected) {
throw new Error(result.error || 'Ollama nicht verbunden');
}
const availableModels = result.models || [];
console.log('Available Ollama models:', availableModels);
@ -1168,39 +1173,19 @@ Falls ein Feld nicht erkennbar ist, setze den Wert auf null.</textarea>
_hideError();
try {
// Get Ollama model name from PowerOn name
const ollamaModelName = _getOllamaModelName(modelName.value);
console.log('Ollama model name:', ollamaModelName);
// Model-specific context lengths
const modelContextLengths = {
'qwen2.5:7b': 32768,
'qwen2.5vl:7b': 32768,
'granite3.2-vision': 16000
};
const numCtx = modelContextLengths[ollamaModelName] || 8192;
// Request-Body für Ollama erstellen
// Request-Body für Backend API erstellen
const requestBody = {
model: ollamaModelName,
modelName: modelName.value, // PowerOn model name
prompt: promptInput.value,
stream: false,
options: {
num_ctx: numCtx
}
imageBase64: currentImageBase64 || null
};
console.log('Sending request to:', `${ollamaUrl.value}/api/generate`);
console.log('Request body (without prompt):', { ...requestBody, prompt: '[TRUNCATED]' });
console.log('Sending request to: /api/analyze');
console.log('Request body (without image):', { ...requestBody, imageBase64: requestBody.imageBase64 ? '[BASE64_IMAGE]' : null });
// Bild nur hinzufügen wenn vorhanden
if (currentImageBase64) {
requestBody.images = [currentImageBase64];
}
// Call Ollama API directly
console.log('Fetching from Ollama...');
const response = await fetch(`${ollamaUrl.value}/api/generate`, {
// Call Backend API (geht über HTTPS)
console.log('Fetching from Backend...');
const response = await fetch('/api/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
@ -1210,30 +1195,19 @@ Falls ein Feld nicht erkennbar ist, setze den Wert auf null.</textarea>
if (!response.ok) {
const errorText = await response.text();
console.error('Ollama error:', errorText);
throw new Error(`Ollama Fehler: ${response.status} - ${errorText.substring(0, 200)}`);
console.error('Backend error:', errorText);
throw new Error(`Backend Fehler: ${response.status} - ${errorText.substring(0, 200)}`);
}
const result = await response.json();
console.log('Ollama response received:', result);
const responseText = result.response || '';
console.log('Backend response received:', result);
// Try to extract JSON from response
let extractedData = null;
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
if (jsonMatch) {
try {
extractedData = JSON.parse(jsonMatch[0]);
} catch (e) {
extractedData = null;
}
if (!result.success) {
throw new Error(result.error || 'Analyse fehlgeschlagen');
}
// Wrap plain text response in JSON object
if (extractedData === null) {
extractedData = { response: responseText.trim() };
}
const responseText = result.rawResponse || '';
const extractedData = result.data || { response: responseText.trim() };
_displayResults(extractedData, responseText);
_setStatus('success', 'Erfolgreich extrahiert');