Initial commit: existing turf_saas codebase

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
ML Engineer
2026-04-25 17:18:43 +02:00
commit ed07c8a3d1
137 changed files with 36398 additions and 0 deletions

281
n8n-chat.html Normal file
View File

@@ -0,0 +1,281 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>N8N Workflow Chat - H3R7Tech</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Syne:wght@400;700;800&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0d0f1a;
--bg2: #13162a;
--bg3: #1a1f38;
--accent: #e94560;
--cyan: #00d9ff;
--green: #2ec4b6;
--yellow: #f4c542;
--muted: #4a5080;
--text: #e8eaf6;
--text2: #8892b0;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Syne', sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
}
.header {
background: linear-gradient(135deg, #0d0f1a 0%, #1a1f38 100%);
border-bottom: 1px solid var(--muted);
padding: 16px 24px;
display: flex;
align-items: center;
gap: 16px;
}
.header h1 { font-size: 20px; font-weight: 800; color: var(--cyan); }
.header .status { margin-left: auto; font-family: 'Space Mono', monospace; font-size: 12px; color: var(--text2); }
.status-dot {
display: inline-block; width: 8px; height: 8px; border-radius: 50%;
background: var(--accent); margin-right: 6px;
}
.status-dot.connected { background: var(--green); }
.content { max-width: 800px; margin: 0 auto; padding: 20px; }
.card {
background: var(--bg2);
border: 1px solid var(--bg3);
border-radius: 12px;
padding: 20px;
margin-bottom: 20px;
}
.chat-container {
height: 400px;
overflow-y: auto;
background: var(--bg);
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
}
.message {
margin-bottom: 12px;
padding: 12px 16px;
border-radius: 12px;
max-width: 85%;
}
.message.user {
background: rgba(0, 217, 255, 0.15);
border: 1px solid rgba(0, 217, 255, 0.3);
margin-left: auto;
}
.message.system {
background: rgba(46, 196, 182, 0.15);
border: 1px solid rgba(46, 196, 182, 0.3);
}
.message.error {
background: rgba(233, 69, 96, 0.15);
border: 1px solid rgba(233, 69, 96, 0.3);
color: var(--accent);
}
.message .role {
font-size: 10px; font-weight: 700; text-transform: uppercase;
color: var(--text2); margin-bottom: 4px;
}
.message .content { padding: 0; max-width: 100%; }
.input-row {
display: flex;
gap: 12px;
}
.input-row input {
flex: 1;
padding: 14px 18px;
border-radius: 8px;
border: 1px solid var(--muted);
background: var(--bg);
color: var(--text);
font-family: 'Syne', sans-serif;
font-size: 14px;
}
.input-row input:focus {
outline: none;
border-color: var(--cyan);
}
.input-row button {
padding: 14px 24px;
border-radius: 8px;
border: none;
background: var(--cyan);
color: #000;
font-weight: 700;
cursor: pointer;
font-family: 'Syne', sans-serif;
transition: all .2s;
}
.input-row button:hover { transform: translateY(-2px); }
.input-row button:disabled { background: var(--muted); cursor: not-allowed; }
.loader {
display: flex; align-items: center; gap: 8px; padding: 20px;
}
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cyan); animation: bounce .8s infinite; }
.dot:nth-child(2) { animation-delay: .15s; }
.dot:nth-child(3) { animation-delay: .3s; }
@keyframes bounce {
0%,100% { transform: translateY(0); opacity: .4; }
50% { transform: translateY(-8px); opacity: 1; }
}
.quick-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.quick-actions button {
padding: 8px 14px;
border-radius: 20px;
border: 1px solid var(--muted);
background: var(--bg2);
color: var(--text2);
font-size: 12px;
cursor: pointer;
transition: all .2s;
}
.quick-actions button:hover {
border-color: var(--cyan);
color: var(--cyan);
}
.home-btn{position:fixed;top:10px;left:10px;z-index:9999;background:linear-gradient(135deg,#00d9ff,#7b2cbf);color:#fff;border:none;border-radius:8px;padding:10px 15px;font-size:14px;cursor:pointer;text-decoration:none;display:flex;align-items:center;gap:8px;box-shadow:0 2px 10px rgba(0,217,255,0.3);transition:all 0.3s;font-family:-apple-system,BlinkMacSystemFont,sans-serif}.home-btn:hover{transform:translateY(-2px);box-shadow:0 4px 15px rgba(0,217,255,0.5)}
</style>
</head>
<body>
<a href="https://portal-kolifee.duckdns.org/" class="home-btn" title="Retour au portail"><span style="font-size:18px">🏠</span><span>Accueil</span></a>
<div class="header">
<h1>🤖 N8N Workflow Chat</h1>
<div class="status">
<span class="status-dot" id="status-dot"></span>
<span id="status-text">Connecting...</span>
</div>
</div>
<div class="content">
<div class="card">
<div class="quick-actions">
<button onclick="sendQuick('Quelle est la date du jour?')">📅 Date</button>
<button onclick="sendQuick('Liste les 5 derniers workflows actifs')">📋 Workflows</button>
<button onclick="sendQuick('Affiche les statistiques du système')">📊 Stats</button>
<button onclick="sendQuick('Test de connexion')">✅ Test</button>
</div>
<div class="chat-container" id="chat-container">
<div class="message system">
<div class="role">Système</div>
<div class="content">👋 Bienvenue! Posez vos questions au workflow n8n.</div>
</div>
</div>
<div class="input-row">
<input type="text" id="message-input" placeholder="Votre message..."
onkeypress="if(event.key==='Enter')sendMessage()">
<button id="send-btn" onclick="sendMessage()">Envoyer</button>
</div>
</div>
</div>
<script>
const WEBHOOK_URL = '/turf/api/n8n-proxy';
const CHAT_CONTAINER = document.getElementById('chat-container');
const MESSAGE_INPUT = document.getElementById('message-input');
const SEND_BTN = document.getElementById('send-btn');
function addMessage(role, content, isError = false) {
const div = document.createElement('div');
div.className = `message ${role}${isError ? ' error' : ''}`;
div.innerHTML = `<div class="role">${role}</div><div class="content">${content}</div>`;
CHAT_CONTAINER.appendChild(div);
CHAT_CONTAINER.scrollTop = CHAT_CONTAINER.scrollHeight;
}
function showLoader() {
const div = document.createElement('div');
div.id = 'loader';
div.className = 'message system';
div.innerHTML = `<div class="loader"><div class="dot"></div><div class="dot"></div><div class="dot"></div></div>`;
CHAT_CONTAINER.appendChild(div);
CHAT_CONTAINER.scrollTop = CHAT_CONTAINER.scrollHeight;
}
function hideLoader() {
const loader = document.getElementById('loader');
if (loader) loader.remove();
}
async function sendQuick(msg) {
MESSAGE_INPUT.value = msg;
sendMessage();
}
async function sendMessage() {
const msg = MESSAGE_INPUT.value.trim();
if (!msg) return;
addMessage('user', msg);
MESSAGE_INPUT.value = '';
SEND_BTN.disabled = true;
showLoader();
try {
const response = await fetch(WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: msg,
timestamp: Date.now(),
source: 'n8n-chat-web'
})
});
hideLoader();
try {
const data = await response.json();
let msg = data.message || 'Message envoyé';
addMessage('system', msg);
} catch(e) {
const text = await response.text();
addMessage('system', text || 'Message envoyé');
}
} catch (e) {
hideLoader();
addMessage('system', `Erreur de connexion: ${e.message}`, true);
}
SEND_BTN.disabled = false;
MESSAGE_INPUT.focus();
}
// Test connection on load
async function testConnection() {
try {
const resp = await fetch(WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: 'ping', timestamp: Date.now() })
});
document.getElementById('status-dot').classList.add('connected');
document.getElementById('status-text').textContent = 'Connecté';
} catch (e) {
document.getElementById('status-text').textContent = 'Erreur: ' + e.message;
}
}
testConnection();
</script>
</body>
</html>