chat implementiert
This commit is contained in:
10
Dockerfile
10
Dockerfile
@@ -1,13 +1,19 @@
|
||||
# Stage 1: Hugo bauen
|
||||
FROM klakegg/hugo:alpine AS builder
|
||||
# WICHTIG: Kopiere das gesamte Verzeichnis, nicht nur den Inhalt
|
||||
# Kopiere das gesamte Verzeichnis
|
||||
COPY . /src
|
||||
# Bauen der Seite
|
||||
RUN hugo
|
||||
|
||||
# Stage 2: Nginx ausliefern
|
||||
FROM nginx:alpine
|
||||
# Kopieren der generierten Seite
|
||||
|
||||
# 1. Nginx Konfigurationsdatei kopieren (wichtig für Reverse Proxy)
|
||||
#COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY default.conf.template /etc/nginx/conf.d/default.conf.template
|
||||
|
||||
# 2. Kopieren der generierten Seite
|
||||
COPY --from=builder /src/public /usr/share/nginx/html
|
||||
|
||||
# Nginx läuft auf Port 80
|
||||
EXPOSE 80
|
||||
@@ -7,7 +7,10 @@ services:
|
||||
HUGO_BASEURL: https://${DOMAIN_MAIN}/ # Nimmt den Wert aus deiner .env
|
||||
container_name: webpage
|
||||
restart: unless-stopped
|
||||
#environment:
|
||||
command: /bin/sh -c "envsubst '\$$TOKEN' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
|
||||
#command: /bin/sh -c "envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"
|
||||
environment:
|
||||
- TOKEN=${OPEN_WEBUI_TOKEN}
|
||||
# - TITLE=${TITLE}
|
||||
# - DOMAIN_MAIN=${DOMAIN_MAIN}
|
||||
# - NGINX_ENVSUBST_OUTPUT_DIR=/usr/share/nginx/html
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
baseURL = "/"
|
||||
languageCode = "de"
|
||||
defaultContentLanguage = "de"
|
||||
defaultContentLanguageInSubdir = true
|
||||
defaultContentLanguageInSubdir = false
|
||||
|
||||
[languages]
|
||||
[languages.de]
|
||||
|
||||
8
content/de/chat.md
Normal file
8
content/de/chat.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "AI Demo Lab"
|
||||
flag: "🇩🇪"
|
||||
welcome_message: "Hallo! Ich bin die Demo-KI von Ground Zero Lab. Wie kann ich dir helfen?"
|
||||
placeholder: "Nachricht eingeben..."
|
||||
sent_btn: Senden
|
||||
layout: "chat"
|
||||
---
|
||||
8
content/en/chat.md
Normal file
8
content/en/chat.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "AI Demo Lab"
|
||||
flag: "🇬🇧"
|
||||
welcome_message: "Hello! I am the demo AI from Ground Zero Lab. How can I help you?"
|
||||
placeholder: "Enter message..."
|
||||
sent_btn: Send
|
||||
layout: "chat"
|
||||
---
|
||||
8
content/es/chat.md
Normal file
8
content/es/chat.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "AI Demo Lab"
|
||||
flag: "🇪🇸"
|
||||
welcome_message: "Здравствуйте! Я демонстрационный ИИ Ground Zero Lab. Чем могу помочь?"
|
||||
placeholder: "Введите сообщение..."
|
||||
sent_btn: Отправить
|
||||
layout: "chat"
|
||||
---
|
||||
8
content/fr/chat.md
Normal file
8
content/fr/chat.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "AI Demo Lab"
|
||||
flag: "🇫🇷"
|
||||
welcome_message: "Bonjour ! Je suis l'IA de démonstration de Ground Zero Lab. Comment puis-je vous aider ?"
|
||||
placeholder: "Entrez un message..."
|
||||
sent_btn: Envoyer
|
||||
layout: "chat"
|
||||
---
|
||||
8
content/ru/chat.md
Normal file
8
content/ru/chat.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: "AI Demo Lab"
|
||||
flag: "🇷🇺"
|
||||
welcome_message: "Здравствуйте! Я демонстрационный ИИ Ground Zero Lab. Чем могу помочь?"
|
||||
placeholder: "Введите сообщение..."
|
||||
sent_btn: Отправить
|
||||
layout: "chat"
|
||||
---
|
||||
44
default.conf.template
Normal file
44
default.conf.template
Normal file
@@ -0,0 +1,44 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# 1. Deine statischen Hugo-Dateien
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# 2. Der API-Proxy (für den Button-Klick)
|
||||
location /api/ {
|
||||
proxy_pass http://open-webui:8080/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Authorization "Bearer ${TOKEN}";
|
||||
}
|
||||
|
||||
# 3. Der Chat-Proxy (für das Iframe)
|
||||
location /c/ {
|
||||
proxy_pass http://open-webui:8080/c/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Authorization "Bearer ${TOKEN}";
|
||||
# Wichtig, damit das Iframe nicht blockiert wird
|
||||
proxy_hide_header X-Frame-Options;
|
||||
proxy_hide_header Content-Security-Policy;
|
||||
}
|
||||
|
||||
# 4. DIE NEUEN PFADE (Damit das Design/Layout lädt)
|
||||
location /_app/ {
|
||||
proxy_pass http://open-webui:8080/_app/;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location /assets/ {
|
||||
proxy_pass http://open-webui:8080/assets/;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location /static/ {
|
||||
proxy_pass http://open-webui:8080/static/;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
108
layouts/_default/chat.html
Normal file
108
layouts/_default/chat.html
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Lang }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Demo Chat | {{ .Title }}</title>
|
||||
<link rel="stylesheet" href="{{ "style.css" | relURL }}">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="container">
|
||||
<a href="/" class="logo">🏗 Ground Zero Lab</a>
|
||||
|
||||
<div class="nav-right">
|
||||
<div class="theme-header-btn">
|
||||
<button class="lang-btn" id="langBtn">
|
||||
{{ .Params.flag }}
|
||||
</button>
|
||||
<div class="lang-dropdown" id="langDropdown">
|
||||
{{ range .Site.Languages }}
|
||||
<a href="#" class="theme-option" data-lang="{{ .Lang }}">
|
||||
{{ if eq .Lang "de" }}🇩🇪 Deutsch{{ end }}
|
||||
{{ if eq .Lang "en" }}🇬🇧 English{{ end }}
|
||||
{{ if eq .Lang "fr" }}🇫🇷 Français{{ end }}
|
||||
{{ if eq .Lang "es" }}🇪🇸 Español{{ end }}
|
||||
{{ if eq .Lang "ru" }}🇷🇺 Русский{{ end }}
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="theme-header-btn">
|
||||
<button class="theme-btn" id="themeBtn" title="Design wählen">
|
||||
<span class="theme-icon-active">🌓</span>
|
||||
</button>
|
||||
<div class="theme-dropdown" id="themeDropdown">
|
||||
<button class="theme-option" data-theme="setlight">☀️ Hell</button>
|
||||
<button class="theme-option" data-theme="setdark">🌙 Dunkel</button>
|
||||
<button class="theme-option" data-theme="setauto">🌓 Auto</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="chat-container">
|
||||
<div id="chat-messages">
|
||||
<div class="message ai-message">{{ .Params.welcome_message }}</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-input-area">
|
||||
<input type="text" id="user-input" placeholder="{{ .Params.placeholder }}" onkeypress="if(event.key === 'Enter') sendMessage()">
|
||||
<button class="send-btn" onclick="sendMessage()">{{ .Params.sent_btn }} </button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
const userInput = document.getElementById('user-input');
|
||||
|
||||
async function sendMessage() {
|
||||
const text = userInput.value.trim();
|
||||
if (!text) return;
|
||||
|
||||
// User Nachricht anzeigen
|
||||
appendMessage('user', text);
|
||||
userInput.value = '';
|
||||
|
||||
try {
|
||||
// Hier sprechen wir deinen Nginx /api/ Proxy an
|
||||
const response = await fetch('/api/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Der Token wird vom Nginx Server-seitig gesetzt,
|
||||
// oder du sendest ihn hier mit, falls Nginx ihn nicht fix setzt.
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"model": "arcee-ai/trinity-large-preview:free",
|
||||
"messages": [{"role": "user", "content": text}],
|
||||
"stream": false // Erstmal ohne Streaming für einfachere Logik
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const aiText = data.choices[0].message.content;
|
||||
appendMessage('ai', aiText);
|
||||
|
||||
} catch (err) {
|
||||
appendMessage('ai', 'Fehler: Verbindung zum Server fehlgeschlagen.');
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
function appendMessage(role, text) {
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.className = `message ${role}-message`;
|
||||
msgDiv.innerText = text;
|
||||
chatMessages.appendChild(msgDiv);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
</script>
|
||||
<script src="/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -105,7 +105,7 @@
|
||||
<div class="demo-section">
|
||||
<p class="text">{{ .Params.demo_text }}</p>
|
||||
<div class="demo-buttons">
|
||||
<a href="#" class="demo-button">🚧 {{ .Params.demo_btn1 }}</a>
|
||||
<button onclick="startDemoChat()" >🚧 {{ .Params.demo_btn1 }}</button>
|
||||
<a href="#" class="demo-button">🚧 {{ .Params.demo_btn2 }}</a>
|
||||
<a href="#" class="demo-button">🚧 {{ .Params.demo_btn3 }}</a>
|
||||
</div>
|
||||
|
||||
@@ -119,3 +119,44 @@ function applyTheme(theme) {
|
||||
}
|
||||
}
|
||||
|
||||
async function startDemoChat() {
|
||||
try {
|
||||
const response = await fetch('/api/v1/chats/new', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
// WICHTIG: Wenn dein Nginx den Token nicht setzt,
|
||||
// muss er hier hin: 'Authorization': 'Bearer DEIN_TOKEN'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"title": "Demo Chat",
|
||||
"chat": { "model": "arcee-ai/trinity-large-preview:free"}
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
console.error('API Fehlerdetails:', errorData);
|
||||
throw new Error(`Fehler: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const sessionId = data.id;
|
||||
|
||||
// --- DER KORREKTE DYNAMISCHE PFAD ---
|
||||
|
||||
// 1. Hole den aktuellen Sprachpfad (z.B. "/de/", "/en/")
|
||||
// split('/') macht aus "/de/about/" -> ["", "de", "about", ""]
|
||||
const pathSegments = window.location.pathname.split('/');
|
||||
const lang = pathSegments[1] || 'de'; // Fallback auf "de", falls kein Pfad da ist
|
||||
|
||||
// 2. Leite auf den Pfad MIT Sprache und Session-ID um
|
||||
// Das ergibt dann z.B. /de/chat/?session=...
|
||||
window.location.href = `/${lang}/chat/?session=${sessionId}`;
|
||||
//window.location.href = `/de/chat/?session=${sessionId}`;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Starten:', error);
|
||||
alert('Demo-Chat konnte nicht gestartet werden.');
|
||||
}
|
||||
}
|
||||
|
||||
135
static/style.css
135
static/style.css
@@ -258,23 +258,54 @@ nav {
|
||||
}
|
||||
|
||||
/* ==========================================
|
||||
4. BUTTONS
|
||||
4. BUTTONS & INPUTS (Neu gestylt)
|
||||
========================================== */
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
background-color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
padding: 1rem 2rem;
|
||||
text-decoration: none;
|
||||
|
||||
/* Basis Styling für alle Buttons (CTA, Demo, Send) */
|
||||
.cta-button,
|
||||
.send-btn,
|
||||
.demo-button,
|
||||
.demo-buttons button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.8rem 1.8rem;
|
||||
border-radius: 50px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
transform: translateY(-2px);
|
||||
/* Haupt-Buttons (Akzentfarbe) */
|
||||
.cta-button,
|
||||
.send-btn,
|
||||
.demo-buttons button {
|
||||
background-color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
}
|
||||
|
||||
.cta-button:hover,
|
||||
.send-btn:hover,
|
||||
.demo-buttons button:hover {
|
||||
background-color: var(--accent-dark);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Sekundär-Buttons (Grau/Alt) */
|
||||
.demo-button {
|
||||
background-color: var(--bg-section-alt);
|
||||
color: var(--text-main);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.demo-button:hover {
|
||||
background-color: var(--border-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.demo-buttons {
|
||||
@@ -286,18 +317,22 @@ nav {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.demo-button {
|
||||
background-color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
padding: 0.6rem 1.4rem;
|
||||
text-decoration: none;
|
||||
border-radius: 25px;
|
||||
transition: all 0.3s ease;
|
||||
/* Eingabefeld (Chat) */
|
||||
#user-input {
|
||||
flex: 1;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--border-input);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-input);
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.demo-button:hover {
|
||||
background-color: var(--accent-dark);
|
||||
transform: translateY(-2px);
|
||||
#user-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
/* ==========================================
|
||||
@@ -308,7 +343,7 @@ section {
|
||||
background-color: var(--bg-body);
|
||||
}
|
||||
|
||||
.section-dark {
|
||||
.section-dark .chat-input-area{
|
||||
background-color: var(--bg-section-alt);
|
||||
}
|
||||
|
||||
@@ -371,6 +406,60 @@ h2 {
|
||||
}
|
||||
|
||||
|
||||
/* Chat-Container Hintergrund korrigiert */
|
||||
.chat-container {
|
||||
max-width: 800px;
|
||||
margin: 100px auto 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 140px);
|
||||
background: var(--card-bg); /* Nutzt Card Background Variable */
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 25px var(--card-shadow);
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.message {
|
||||
max-width: 80%;
|
||||
padding: 12px 16px;
|
||||
border-radius: 15px;
|
||||
line-height: 1.5;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.user-message {
|
||||
align-self: flex-end;
|
||||
background-color: var(--accent);
|
||||
color: var(--accent-text);
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.ai-message {
|
||||
align-self: flex-start;
|
||||
background-color: var(--bg-section-alt);
|
||||
color: var(--text-main);
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.chat-input-area {
|
||||
padding: 20px;
|
||||
background-color: var(--card-bg);
|
||||
border-top: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ==========================================
|
||||
6. FOOTER
|
||||
========================================== */
|
||||
|
||||
Reference in New Issue
Block a user