Lupus Browser
This commit is contained in:
parent
2bd3f2be22
commit
b5d943c690
101
api/chat.php
Normal file
101
api/chat.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
require_once __DIR__ . '/../ai/LocalAIApi.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$message = $input['message'] ?? '';
|
||||||
|
$context = $input['context'] ?? null; // New: get context
|
||||||
|
|
||||||
|
if (empty(trim($message))) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Message cannot be empty.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New: Handle simulation context
|
||||||
|
if ($context === 'vpn' || $context === 'antivirus') {
|
||||||
|
$ai_input = [['role' => 'user', 'content' => $message]];
|
||||||
|
$ai_response = LocalAIApi::createResponse(['input' => $ai_input]);
|
||||||
|
$ai_message = LocalAIApi::extractText($ai_response);
|
||||||
|
|
||||||
|
if (!empty($ai_message)) {
|
||||||
|
echo json_encode(['success' => true, 'data' => $ai_message]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Failed to get AI response.']);
|
||||||
|
}
|
||||||
|
exit; // Stop execution for simulations
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Existing Chat Logic ---
|
||||||
|
$personality = $input['personality'] ?? '';
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO messages (user_id, message) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$user_id, $message]);
|
||||||
|
|
||||||
|
// --- AI CONTEXT PREPARATION ---
|
||||||
|
|
||||||
|
// 1. Prepare the system prompt (personality)
|
||||||
|
$final_ai_input = [];
|
||||||
|
if (!empty(trim($personality))) {
|
||||||
|
$final_ai_input[] = ['role' => 'system', 'content' => $personality];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Get last 5 messages for conversation history
|
||||||
|
$stmt = $pdo->query("SELECT m.message, u.username FROM messages m JOIN users u ON m.user_id = u.id ORDER BY m.created_at DESC LIMIT 5");
|
||||||
|
$recent_messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$conversation_history = array_map(function($msg) {
|
||||||
|
return ['role' => $msg['username'] === 'AI' ? 'assistant' : 'user', 'content' => $msg['message']];
|
||||||
|
}, array_reverse($recent_messages));
|
||||||
|
|
||||||
|
// 3. Combine system prompt with conversation history
|
||||||
|
$final_ai_input = array_merge($final_ai_input, $conversation_history);
|
||||||
|
|
||||||
|
// --- CALL THE AI ---
|
||||||
|
$ai_response = LocalAIApi::createResponse(['input' => $final_ai_input]);
|
||||||
|
$ai_message = LocalAIApi::extractText($ai_response);
|
||||||
|
|
||||||
|
if (!empty($ai_message)) {
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = 'AI'");
|
||||||
|
$stmt->execute();
|
||||||
|
$ai_user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($ai_user) {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO messages (user_id, message) VALUES (?, ?)");
|
||||||
|
$stmt->execute([$ai_user['id'], $ai_message]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Message saved.']);
|
||||||
|
|
||||||
|
} elseif ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'reset') {
|
||||||
|
// Truncate the messages table to reset the chat
|
||||||
|
$pdo->query("TRUNCATE TABLE messages");
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Chat history has been cleared.']);
|
||||||
|
} else {
|
||||||
|
$stmt = $pdo->query("SELECT m.id, m.message, m.created_at, u.username, LEFT(u.username, 1) as user_initial FROM messages m JOIN users u ON m.user_id = u.id ORDER BY m.created_at ASC");
|
||||||
|
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
echo json_encode(['success' => true, 'messages' => $messages, 'currentUser' => $_SESSION['username']]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Method Not Allowed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
235
assets/css/custom.css
Normal file
235
assets/css/custom.css
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
:root {
|
||||||
|
--primary-color: #007bff;
|
||||||
|
--secondary-color: #6c757d;
|
||||||
|
--light-color: #f8f9fa;
|
||||||
|
--dark-color: #343a40;
|
||||||
|
--body-bg: #f4f7f6;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--sent-bg: #007bff;
|
||||||
|
--received-bg: #e9ecef;
|
||||||
|
--font-family: 'Poppins', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
font-family: var(--font-family);
|
||||||
|
padding-top: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat Page Styles */
|
||||||
|
.chat-container {
|
||||||
|
padding-top: 2rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-card {
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-box {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
max-width: 75%;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 500;
|
||||||
|
color: white;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-body {
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-text {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-time {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: #adb5bd;
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent {
|
||||||
|
align-self: flex-end;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent .message-body {
|
||||||
|
background: var(--sent-bg);
|
||||||
|
color: white;
|
||||||
|
border-radius: 1.5rem 1.5rem 0.5rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.sent .message-time {
|
||||||
|
color: var(--light-color);
|
||||||
|
opacity: 0.8;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.received {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.received .message-body {
|
||||||
|
background-color: var(--received-bg);
|
||||||
|
color: var(--dark-color);
|
||||||
|
border-radius: 1.5rem 1.5rem 1.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.received .avatar {
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-form .form-control {
|
||||||
|
border-radius: 2rem;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#message-form .form-control:focus {
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#send-button {
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typing Indicator Styles */
|
||||||
|
.typing-indicator-container {
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typing-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--received-bg);
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typing-indicator span {
|
||||||
|
height: 8px;
|
||||||
|
width: 8px;
|
||||||
|
background-color: #999;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 2px;
|
||||||
|
animation: typing 1.2s infinite ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typing-indicator span:nth-of-type(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typing-indicator span:nth-of-type(3) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes typing {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.error .message-body {
|
||||||
|
background: #dc3545a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Security Page Styles */
|
||||||
|
.security-tool-card {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-tool-card h5 {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-tool-card p {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.security-tool-card .form-control {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vpn-status p,
|
||||||
|
#antivirus-result p {
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--light-color);
|
||||||
|
margin-bottom: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vpn-status p.connecting,
|
||||||
|
#antivirus-result p.scanning {
|
||||||
|
background-color: #fff3cd; /* Bootstrap warning yellow */
|
||||||
|
color: #664d03;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vpn-status p.connected {
|
||||||
|
background-color: #d1e7dd; /* Bootstrap success green */
|
||||||
|
color: #0f5132;
|
||||||
|
}
|
||||||
280
assets/js/main.js
Normal file
280
assets/js/main.js
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const messageForm = document.getElementById('message-form');
|
||||||
|
const messageInput = document.getElementById('message-input');
|
||||||
|
const chatBox = document.getElementById('chat-box');
|
||||||
|
const typingIndicator = document.getElementById('typing-indicator');
|
||||||
|
|
||||||
|
let currentUser = '';
|
||||||
|
let lastAnimatedMessageId = null;
|
||||||
|
|
||||||
|
function showTypingIndicator() {
|
||||||
|
if (typingIndicator) {
|
||||||
|
typingIndicator.style.display = 'block';
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideTypingIndicator() {
|
||||||
|
if (typingIndicator) {
|
||||||
|
typingIndicator.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAvatar(message) {
|
||||||
|
const avatar = document.createElement('div');
|
||||||
|
avatar.classList.add('avatar');
|
||||||
|
const initial = message.user_initial || (message.username === 'AI' ? 'A' : '?');
|
||||||
|
avatar.textContent = initial;
|
||||||
|
// Add a color based on the username
|
||||||
|
const colors = ['#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722', '#795548', '#607d8b'];
|
||||||
|
const colorIndex = message.username.charCodeAt(0) % colors.length;
|
||||||
|
avatar.style.backgroundColor = colors[colorIndex];
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMessage(message) {
|
||||||
|
const isSent = message.username === currentUser;
|
||||||
|
if (isSent || message.username === 'AI') {
|
||||||
|
hideTypingIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageElement = document.createElement('div');
|
||||||
|
messageElement.classList.add('message', isSent ? 'sent' : 'received');
|
||||||
|
messageElement.dataset.messageId = message.id;
|
||||||
|
|
||||||
|
const avatar = createAvatar(message);
|
||||||
|
messageElement.appendChild(avatar);
|
||||||
|
|
||||||
|
const messageBody = document.createElement('div');
|
||||||
|
messageBody.classList.add('message-body');
|
||||||
|
|
||||||
|
if (!isSent) {
|
||||||
|
const usernameElement = document.createElement('div');
|
||||||
|
usernameElement.classList.add('message-username');
|
||||||
|
usernameElement.textContent = message.username;
|
||||||
|
messageBody.appendChild(usernameElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageP = document.createElement('p');
|
||||||
|
messageP.classList.add('message-text');
|
||||||
|
messageP.textContent = message.message;
|
||||||
|
|
||||||
|
const messageTime = document.createElement('span');
|
||||||
|
messageTime.classList.add('message-time');
|
||||||
|
const time = new Date(message.created_at);
|
||||||
|
messageTime.textContent = time.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||||
|
|
||||||
|
messageBody.appendChild(messageP);
|
||||||
|
messageBody.appendChild(messageTime);
|
||||||
|
messageElement.appendChild(messageBody);
|
||||||
|
chatBox.appendChild(messageElement);
|
||||||
|
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMessageWithStreaming(message) {
|
||||||
|
renderMessage({ ...message, message: '' }); // Render the container first
|
||||||
|
const messageP = chatBox.querySelector(`[data-message-id="${message.id}"] .message-text`);
|
||||||
|
if (!messageP) return;
|
||||||
|
|
||||||
|
const words = message.message.split(' ');
|
||||||
|
let i = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (i < words.length) {
|
||||||
|
messageP.textContent += (i > 0 ? ' ' : '') + words[i];
|
||||||
|
chatBox.scrollTop = chatBox.scrollHeight;
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchMessages() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat.php');
|
||||||
|
if (!response.ok) {
|
||||||
|
if (response.status === 401) window.location.href = 'login.php';
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.success) {
|
||||||
|
currentUser = data.currentUser;
|
||||||
|
const messages = data.messages || [];
|
||||||
|
|
||||||
|
const existingMessageIds = new Set([...chatBox.querySelectorAll('.message')].map(m => m.dataset.messageId));
|
||||||
|
|
||||||
|
messages.forEach(message => {
|
||||||
|
if (!existingMessageIds.has(message.id.toString())) {
|
||||||
|
const isAI = message.username === 'AI';
|
||||||
|
const isNewer = message.id > lastAnimatedMessageId;
|
||||||
|
|
||||||
|
if (isAI && isNewer) {
|
||||||
|
lastAnimatedMessageId = message.id;
|
||||||
|
renderMessageWithStreaming(message);
|
||||||
|
} else {
|
||||||
|
renderMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (messages.length > 0) {
|
||||||
|
const lastMessage = messages[messages.length - 1];
|
||||||
|
if (lastMessage.username !== 'AI') {
|
||||||
|
hideTypingIndicator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching messages:', error);
|
||||||
|
hideTypingIndicator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetChat() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat.php?action=reset');
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success) {
|
||||||
|
chatBox.innerHTML = '';
|
||||||
|
} else {
|
||||||
|
console.error('Error resetting chat:', result.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error resetting chat:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatBox) {
|
||||||
|
fetchMessages();
|
||||||
|
setInterval(fetchMessages, 3000);
|
||||||
|
|
||||||
|
if (messageForm) {
|
||||||
|
messageForm.addEventListener('submit', async function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const messageText = messageInput.value.trim();
|
||||||
|
if (messageText === '/reset') {
|
||||||
|
messageInput.value = '';
|
||||||
|
await resetChat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageText !== '') {
|
||||||
|
messageInput.value = '';
|
||||||
|
messageInput.focus();
|
||||||
|
showTypingIndicator();
|
||||||
|
|
||||||
|
// Optimistically render user's message
|
||||||
|
const tempId = `temp_${new Date().getTime()}`;
|
||||||
|
renderMessage({
|
||||||
|
id: tempId,
|
||||||
|
username: currentUser,
|
||||||
|
message: messageText,
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
user_initial: currentUser.charAt(0).toUpperCase()
|
||||||
|
});
|
||||||
|
|
||||||
|
const personality = document.getElementById('ai-personality').value.trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ message: messageText, personality: personality })
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (!result.success) {
|
||||||
|
console.error('Error saving message:', result.error);
|
||||||
|
// Optionally remove the optimistic message or show an error
|
||||||
|
const failedMsg = chatBox.querySelector(`[data-message-id="${tempId}"]`);
|
||||||
|
if(failedMsg) failedMsg.classList.add('error');
|
||||||
|
}
|
||||||
|
// The poller will fetch the confirmed message from the server
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error sending message:', error);
|
||||||
|
hideTypingIndicator();
|
||||||
|
const failedMsg = chatBox.querySelector(`[data-message-id="${tempId}"]`);
|
||||||
|
if(failedMsg) failedMsg.classList.add('error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Security Panel Logic
|
||||||
|
const vpnConnectBtn = document.getElementById('vpn-connect-btn');
|
||||||
|
const vpnStatus = document.getElementById('vpn-status');
|
||||||
|
const vpnCountrySelect = document.getElementById('vpn-country');
|
||||||
|
|
||||||
|
const antivirusScanBtn = document.getElementById('antivirus-scan-btn');
|
||||||
|
const antivirusResult = document.getElementById('antivirus-result');
|
||||||
|
|
||||||
|
let isVpnConnected = false;
|
||||||
|
|
||||||
|
if (vpnConnectBtn) {
|
||||||
|
vpnConnectBtn.addEventListener('click', async () => {
|
||||||
|
isVpnConnected = !isVpnConnected;
|
||||||
|
|
||||||
|
if (isVpnConnected) {
|
||||||
|
const selectedCountry = vpnCountrySelect.value;
|
||||||
|
vpnConnectBtn.textContent = 'Desconectar';
|
||||||
|
vpnConnectBtn.classList.remove('btn-primary');
|
||||||
|
vpnConnectBtn.classList.add('btn-danger');
|
||||||
|
vpnStatus.innerHTML = '<p class="text-primary">Conectando...</p>';
|
||||||
|
|
||||||
|
const prompt = `Simule que você está estabelecendo uma conexão VPN para o país ${selectedCountry}. Descreva o processo e, ao final, confirme a conexão segura e forneça um endereço de IP fictício para esse país.`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ message: prompt, context: 'vpn' }) // Add context for the API
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success && result.data) {
|
||||||
|
vpnStatus.innerHTML = `<p class="text-success">${result.data.replace(/\n/g, '<br>')}</p>`;
|
||||||
|
} else {
|
||||||
|
vpnStatus.innerHTML = '<p class="text-danger">Falha ao conectar. Tente novamente.</p>';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('VPN connection error:', error);
|
||||||
|
vpnStatus.innerHTML = '<p class="text-danger">Ocorreu um erro na simulação.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
vpnConnectBtn.textContent = 'Conectar';
|
||||||
|
vpnConnectBtn.classList.remove('btn-danger');
|
||||||
|
vpnConnectBtn.classList.add('btn-primary');
|
||||||
|
vpnStatus.innerHTML = '<p class="text-muted">Status: Desconectado</p>';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (antivirusScanBtn) {
|
||||||
|
antivirusScanBtn.addEventListener('click', async () => {
|
||||||
|
antivirusScanBtn.disabled = true;
|
||||||
|
antivirusResult.innerHTML = '<p class="text-primary">Verificando sistema... Isso pode levar um momento.</p>';
|
||||||
|
|
||||||
|
const prompt = `Simule uma verificação completa de um sistema de computador em busca de vírus e malware. Descreva as etapas da verificação (ex: verificação de memória, arquivos de inicialização, registro, arquivos do sistema). Ao final, apresente um relatório com os resultados, informando se alguma ameaça foi encontrada ou se o sistema está limpo.`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ message: prompt, context: 'antivirus' }) // Add context for the API
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success && result.data) {
|
||||||
|
antivirusResult.innerHTML = `<p class="text-success">${result.data.replace(/\n/g, '<br>')}</p>`;
|
||||||
|
} else {
|
||||||
|
antivirusResult.innerHTML = '<p class="text-danger">A verificação falhou. Tente novamente.</p>';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Antivirus scan error:', error);
|
||||||
|
antivirusResult.innerHTML = '<p class="text-danger">Ocorreu um erro na simulação da verificação.</p>';
|
||||||
|
} finally {
|
||||||
|
antivirusScanBtn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
49
chat.php
Normal file
49
chat.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main class="chat-container">
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card" id="chat-card">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h3 class="h5 mb-0">Chat Room</h3>
|
||||||
|
</div>
|
||||||
|
<div class="px-4 py-2 bg-light">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ai-personality" class="fw-bold">Personalidade da IA:</label>
|
||||||
|
<input type="text" id="ai-personality" class="form-control" placeholder="Ex: Você é um especialista em marketing.">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body" id="chat-box">
|
||||||
|
<!-- Messages will be loaded here dynamically -->
|
||||||
|
</div>
|
||||||
|
<div id="typing-indicator" class="typing-indicator-container" style="display: none;">
|
||||||
|
<div class="typing-indicator">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<form id="message-form">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="message-input" class="form-control" placeholder="Type your message...">
|
||||||
|
<button class="btn btn-primary" type="submit" id="send-button">Send</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
49
db/migrate.php
Normal file
49
db/migrate.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
function run_migrations() {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
if (!$pdo) {
|
||||||
|
throw new Exception("Failed to connect to the database.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$migrationsDir = __DIR__ . '/migrations';
|
||||||
|
if (!is_dir($migrationsDir)) {
|
||||||
|
mkdir($migrationsDir, 0775, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = glob($migrationsDir . '/*.sql');
|
||||||
|
sort($files);
|
||||||
|
|
||||||
|
$output = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$sql = file_get_contents($file);
|
||||||
|
if (!empty(trim($sql))) {
|
||||||
|
$pdo->exec($sql);
|
||||||
|
$output[] = "Executed migration: " . basename($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['success' => true, 'output' => $output];
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return ['success' => false, 'error' => "Database migration failed: " . $e->getMessage()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the script is run directly from the command line, execute the migrations.
|
||||||
|
if (php_sapi_name() === 'cli') {
|
||||||
|
$result = run_migrations();
|
||||||
|
if ($result['success']) {
|
||||||
|
foreach ($result['output'] as $line) {
|
||||||
|
echo $line . "
|
||||||
|
";
|
||||||
|
}
|
||||||
|
echo "Migrations completed successfully.
|
||||||
|
";
|
||||||
|
} else {
|
||||||
|
echo $result['error'] . "
|
||||||
|
";
|
||||||
|
}
|
||||||
|
}
|
||||||
5
db/migrations/001_create_subscribers_table.sql
Normal file
5
db/migrations/001_create_subscribers_table.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS subscribers (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
email VARCHAR(255) NOT NULL UNIQUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
5
db/migrations/002_create_messages_table.sql
Normal file
5
db/migrations/002_create_messages_table.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `messages` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`message` TEXT NOT NULL,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
6
db/migrations/003_create_users_table.sql
Normal file
6
db/migrations/003_create_users_table.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`username` VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
`password_hash` VARCHAR(255) NOT NULL,
|
||||||
|
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
4
db/migrations/004_add_user_id_to_messages.sql
Normal file
4
db/migrations/004_add_user_id_to_messages.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE `messages`
|
||||||
|
ADD COLUMN `user_id` INT NULL,
|
||||||
|
ADD KEY `user_id` (`user_id`),
|
||||||
|
ADD CONSTRAINT `fk_messages_user_id` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL;
|
||||||
4
db/migrations/005_create_ai_user.sql
Normal file
4
db/migrations/005_create_ai_user.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
-- Add AI user if it doesn't exist
|
||||||
|
INSERT INTO users (username, password)
|
||||||
|
SELECT 'AI', ''
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'AI');
|
||||||
11
includes/footer.php
Normal file
11
includes/footer.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<footer class="py-4 text-center text-muted">
|
||||||
|
<div class="container">
|
||||||
|
<p>© <?php echo date("Y"); ?> Daemon Lupus. All Rights Reserved.</p>
|
||||||
|
<p>Built with ❤️ by Flatlogic.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
96
includes/header.php
Normal file
96
includes/header.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// This section is for the subscribe form, which is on the index page.
|
||||||
|
// We'll only process it if the request is to index.php.
|
||||||
|
if (basename($_SERVER['SCRIPT_NAME']) === 'index.php') {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['email'])) {
|
||||||
|
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
|
||||||
|
|
||||||
|
if ($email) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO subscribers (email) VALUES (:email)");
|
||||||
|
$stmt->execute(['email' => $email]);
|
||||||
|
$_SESSION['subscribe_message'] = "Thank you for joining the waitlist! We'll be in touch.";
|
||||||
|
$_SESSION['subscribe_message_type'] = 'success';
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if ($e->errorInfo[1] == 1062) {
|
||||||
|
$_SESSION['subscribe_message'] = "This email address is already on our waitlist.";
|
||||||
|
$_SESSION['subscribe_message_type'] = 'warning';
|
||||||
|
} else {
|
||||||
|
$_SESSION['subscribe_message'] = "An error occurred. Please try again later.";
|
||||||
|
$_SESSION['subscribe_message_type'] = 'danger';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$_SESSION['subscribe_message'] = "Please enter a valid email address.";
|
||||||
|
$_SESSION['subscribe_message_type'] = 'danger';
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: " . $_SERVER['PHP_SELF'] . "#subscribe");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SESSION['subscribe_message'])) {
|
||||||
|
$subscribe_message = $_SESSION['subscribe_message'];
|
||||||
|
$subscribe_message_type = $_SESSION['subscribe_message_type'];
|
||||||
|
unset($_SESSION['subscribe_message']);
|
||||||
|
unset($_SESSION['subscribe_message_type']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$subscribe_message = null;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>Daemon Lupus Browser</title>
|
||||||
|
<meta name="description" content="The next-generation Android browser with a soul: private, fast, and with integrated, decentralized chat. Built with Flatlogic Generator.">
|
||||||
|
<meta name="keywords" content="android browser, chat browser, private browser, secure browser, daemon lupus, fast browser, web3 browser, decentralized chat, encrypted chat, flatlogic generator, private web browser, secure messaging">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Daemon Lupus Browser">
|
||||||
|
<meta property="og:description" content="The next-generation Android browser with a soul: private, fast, and with integrated, decentralized chat. Built with Flatlogic Generator.">
|
||||||
|
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand fw-bold" href="index.php">
|
||||||
|
<i class="bi bi-moon-stars-fill"></i>
|
||||||
|
Daemon Lupus
|
||||||
|
</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item"><a class="nav-link" href="index.php#features">Features</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="chat.php">Chat</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="security.php">Painel de Segurança</a></li>
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="profile.php">Profile</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="logout.php">Logout</a></li>
|
||||||
|
<?php else: ?>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="login.php">Login</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="register.php">Register</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="index.php#subscribe">Join Waitlist</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
212
index.php
212
index.php
@ -1,150 +1,72 @@
|
|||||||
<?php
|
<?php require_once 'includes/header.php'; ?>
|
||||||
declare(strict_types=1);
|
|
||||||
@ini_set('display_errors', '1');
|
<header class="hero-section text-white text-center">
|
||||||
@error_reporting(E_ALL);
|
<div class="container">
|
||||||
@date_default_timezone_set('UTC');
|
<h1 class="display-4 fw-bold">The Private Browser with a Chat Soul.</h1>
|
||||||
|
<p class="lead my-4">Experience a new way to browse. Fast, secure, and with a revolutionary integrated chat right at your fingertips.</p>
|
||||||
|
<a href="#subscribe" class="btn btn-primary btn-lg rounded-pill">Join the Waitlist</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>New Style</title>
|
|
||||||
<?php
|
|
||||||
// Read project preview data from environment
|
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($projectImageUrl): ?>
|
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<!-- Twitter image -->
|
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% { background-position: 0% 0%; }
|
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
<main>
|
||||||
<div class="card">
|
<section id="features" class="py-5">
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<div class="container text-center">
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<h2 class="mb-5">Why Daemon Lupus?</h2>
|
||||||
<span class="sr-only">Loading…</span>
|
<div class="row">
|
||||||
|
<div class="col-md-4 mb-4">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<i class="bi bi-shield-lock-fill display-3 text-primary"></i>
|
||||||
|
<h3 class="h4 my-3">Ultimate Privacy</h3>
|
||||||
|
<p class="text-muted">Block trackers, ads, and fingerprinting by default. Your data is yours alone.</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-4">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<i class="bi bi-chat-dots-fill display-3 text-primary"></i>
|
||||||
|
<h3 class="h4 my-3">Integrated Chat</h3>
|
||||||
|
<p class="text-muted">Chat with friends or communities directly in your browser. Secure and seamless.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 mb-4">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body">
|
||||||
|
<i class="bi bi-speedometer2 display-3 text-primary"></i>
|
||||||
|
<h3 class="h4 my-3">Blazing Speed</h3>
|
||||||
|
<p class="text-muted">A lightweight core and smart resource management make for a faster web.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="subscribe" class="py-5 bg-light">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-6 text-center">
|
||||||
|
<h2>Get Early Access</h2>
|
||||||
|
<p class="lead text-muted mb-4">Be the first to know when Daemon Lupus is ready. No spam, we promise.</p>
|
||||||
|
|
||||||
|
<?php if ($subscribe_message): ?>
|
||||||
|
<div class="alert alert-<?php echo htmlspecialchars($subscribe_message_type, ENT_QUOTES, 'UTF-8'); ?>" role="alert">
|
||||||
|
<?php echo htmlspecialchars($subscribe_message, ENT_QUOTES, 'UTF-8'); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form id="subscribe-form" method="POST" action="index.php#subscribe">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="email" name="email" id="email-input" class="form-control form-control-lg" placeholder="Enter your email" required>
|
||||||
|
<button class="btn btn-primary" type="submit">Subscribe</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
67
login.php
Normal file
67
login.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: chat.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$username = trim($_POST['username']);
|
||||||
|
$password = $_POST['password'];
|
||||||
|
|
||||||
|
if (empty($username) || empty($password)) {
|
||||||
|
$error = 'Please fill in all fields.';
|
||||||
|
} else {
|
||||||
|
$stmt = db()->prepare("SELECT id, password FROM users WHERE username = ?");
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['username'] = $username;
|
||||||
|
header("Location: chat.php");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$error = 'Invalid username or password.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-center">Login</h3>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<form action="login.php" method="post">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||||
|
</form>
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<p>Don't have an account? <a href="register.php">Register here</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
7
logout.php
Normal file
7
logout.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
|
?>
|
||||||
89
profile.php
Normal file
89
profile.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
$message = '';
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$username = trim($_POST['username']);
|
||||||
|
$email = trim($_POST['email']);
|
||||||
|
|
||||||
|
if (empty($username) || empty($email)) {
|
||||||
|
$error = 'Username and email are required.';
|
||||||
|
} else {
|
||||||
|
// Check for duplicate username
|
||||||
|
$stmt = db()->prepare("SELECT id FROM users WHERE username = ? AND id != ?");
|
||||||
|
$stmt->execute([$username, $user_id]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$error = 'Username already taken.';
|
||||||
|
} else {
|
||||||
|
// Check for duplicate email
|
||||||
|
$stmt = db()->prepare("SELECT id FROM users WHERE email = ? AND id != ?");
|
||||||
|
$stmt->execute([$email, $user_id]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$error = 'Email already in use.';
|
||||||
|
} else {
|
||||||
|
// Update user
|
||||||
|
$stmt = db()->prepare("UPDATE users SET username = ?, email = ? WHERE id = ?");
|
||||||
|
if ($stmt->execute([$username, $email, $user_id])) {
|
||||||
|
$message = 'Profile updated successfully!';
|
||||||
|
} else {
|
||||||
|
$error = 'Failed to update profile.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = db()->prepare("SELECT username, email FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container mt-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2>User Profile</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="alert alert-success"><?php echo $message; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($user): ?>
|
||||||
|
<form method="POST" action="profile.php">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" value="<?php echo htmlspecialchars($user['username']); ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($user['email']); ?>" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Update Profile</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="text-danger">Could not retrieve user information.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
67
register.php
Normal file
67
register.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$username = trim($_POST['username']);
|
||||||
|
$password = $_POST['password'];
|
||||||
|
|
||||||
|
if (empty($username) || empty($password)) {
|
||||||
|
$error = 'Please fill in all fields.';
|
||||||
|
} else {
|
||||||
|
// Check if username already exists
|
||||||
|
$stmt = db()->prepare("SELECT id FROM users WHERE username = ?");
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$error = 'Username already taken.';
|
||||||
|
} else {
|
||||||
|
// Insert new user
|
||||||
|
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$stmt = db()->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
|
||||||
|
if ($stmt->execute([$username, $hashed_password])) {
|
||||||
|
$success = 'Registration successful! You can now <a href="login.php">login</a>.';
|
||||||
|
} else {
|
||||||
|
$error = 'Something went wrong. Please try again.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title text-center">Register</h3>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form action="register.php" method="post">
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Register</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<p>Already have an account? <a href="login.php">Login here</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
65
security.php
Normal file
65
security.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'includes/header.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="container-fluid h-100">
|
||||||
|
<div class="row justify-content-center h-100">
|
||||||
|
<div class="col-md-8 col-xl-9 chat">
|
||||||
|
<div class="card chat-card">
|
||||||
|
<div class="card-header msg_head">
|
||||||
|
<div class="d-flex bd-highlight">
|
||||||
|
<div class="img_cont">
|
||||||
|
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img">
|
||||||
|
<span class="online_icon"></span>
|
||||||
|
</div>
|
||||||
|
<div class="user_info">
|
||||||
|
<span>Painel de Segurança</span>
|
||||||
|
<p>VPN e Antivírus Simulados</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<!-- VPN Simulator -->
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="security-tool-card">
|
||||||
|
<h5>VPN Simulada</h5>
|
||||||
|
<p>Simule uma conexão a uma rede privada virtual para proteger sua identidade online.</p>
|
||||||
|
<div class="form-group mb-3">
|
||||||
|
<label for="vpn-country">Escolha um país:</label>
|
||||||
|
<select class="form-control" id="vpn-country">
|
||||||
|
<option>Estados Unidos</option>
|
||||||
|
<option>Canadá</option>
|
||||||
|
<option>Alemanha</option>
|
||||||
|
<option>Japão</option>
|
||||||
|
<option>Brasil</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button id="vpn-connect-btn" class="btn btn-primary">Conectar</button>
|
||||||
|
<div id="vpn-status" class="mt-3">
|
||||||
|
<p>Status: Desconectado</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Antivirus Simulator -->
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="security-tool-card">
|
||||||
|
<h5>Antivírus Simulado</h5>
|
||||||
|
<p>Execute uma verificação simulada para encontrar e eliminar ameaças em seu sistema.</p>
|
||||||
|
<button id="antivirus-scan-btn" class="btn btn-primary">Verificar Agora</button>
|
||||||
|
<div id="antivirus-result" class="mt-3">
|
||||||
|
<p>Nenhuma verificação realizada ainda.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
require_once 'includes/footer.php';
|
||||||
|
?>
|
||||||
Loading…
x
Reference in New Issue
Block a user