This commit is contained in:
Flatlogic Bot 2025-10-06 16:25:16 +00:00
parent 8cb376878c
commit 9dd9dcbc9d
13 changed files with 1049 additions and 142 deletions

239
assets/css/custom.css Normal file
View File

@ -0,0 +1,239 @@
:root {
--background-primary: #36393f;
--background-secondary: #2f3136;
--background-tertiary: #202225;
--channel-color: #8e9297;
--text-normal: #dcddde;
--text-muted: #72767d;
--interactive-normal: #b9bbbe;
--interactive-hover: #dcddde;
--interactive-active: #fff;
--background-modifier-hover: rgba(79,84,92,0.16);
--background-modifier-active: rgba(79,84,92,0.24);
--border-color: rgba(0,0,0,0.2);
}
body, html {
height: 100%;
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background-color: var(--background-primary);
color: var(--text-normal);
overflow: hidden;
}
.app-container {
display: flex;
height: 100vh;
}
/* Servers List */
.servers-list {
width: 72px;
background-color: var(--background-tertiary);
padding-top: 12px;
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
}
.server-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--background-primary);
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
}
.server-icon:hover, .server-icon.active {
border-radius: 16px;
background-color: #5865f2; /* Discord blurple */
}
.server-icon.add-server-btn {
background-color: var(--background-secondary);
color: #43b581; /* Discord green */
font-size: 24px;
}
.server-icon.add-server-btn:hover {
background-color: #43b581;
color: white;
}
/* Channels & User Panel */
.sidebar {
width: 240px;
background-color: var(--background-secondary);
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 0 16px;
height: 48px;
display: flex;
align-items: center;
border-bottom: 1px solid var(--border-color);
font-weight: bold;
font-size: 16px;
}
.channels-list {
flex-grow: 1;
padding: 16px 8px;
overflow-y: auto;
}
.channel {
padding: 6px 8px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
font-weight: 500;
color: var(--channel-color);
}
.channel:before {
content: '#';
margin-right: 6px;
font-size: 20px;
color: var(--text-muted);
}
.channel:hover {
background-color: var(--background-modifier-hover);
color: var(--interactive-hover);
}
.channel.active {
background-color: var(--background-modifier-active);
color: var(--interactive-active);
}
.user-panel {
height: 52px;
background-color: #292b2f;
padding: 0 8px;
display: flex;
align-items: center;
justify-content: space-between;
}
.user-info {
display: flex;
align-items: center;
}
.user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background-color: #7289da;
margin-right: 8px;
}
.user-name {
font-weight: 600;
font-size: 14px;
}
.logout-btn {
color: var(--text-muted);
cursor: pointer;
padding: 4px;
}
.logout-btn:hover {
color: var(--interactive-hover);
}
/* Chat Area */
.chat-container {
flex-grow: 1;
display: flex;
flex-direction: column;
background-color: var(--background-primary);
}
.chat-header {
height: 48px;
padding: 0 16px;
display: flex;
align-items: center;
border-bottom: 1px solid var(--border-color);
font-weight: bold;
}
.chat-header:before {
content: '#';
margin-right: 6px;
font-size: 24px;
color: var(--text-muted);
}
.messages-list {
flex-grow: 1;
overflow-y: auto;
padding: 20px;
}
.message {
display: flex;
margin-bottom: 16px;
}
.message-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #7289da;
margin-right: 16px;
flex-shrink: 0;
}
.message-content {
display: flex;
flex-direction: column;
}
.message-author {
font-weight: 500;
color: var(--text-normal);
margin-bottom: 4px;
}
.message-text {
line-height: 1.4;
}
.chat-input-form {
padding: 0 20px 20px 20px;
}
.chat-input {
width: 100%;
height: 44px;
border-radius: 8px;
background-color: #40444b;
border: none;
padding: 0 16px;
color: var(--text-normal);
font-size: 16px;
box-sizing: border-box;
}
.chat-input::placeholder {
color: var(--text-muted);
}

0
assets/js/main.js Normal file
View File

102
create_server.php Normal file
View File

@ -0,0 +1,102 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'utils.php';
// If the user is not logged in, redirect to the login page
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$user_id = $_SESSION['user_id'];
$error = '';
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$server_name = trim($_POST['server_name']);
if (empty($server_name)) {
$error = 'Server name is required.';
} else {
try {
$pdo = db();
// Start a transaction
$pdo->beginTransaction();
// 1. Insert the new server
$stmt = $pdo->prepare("INSERT INTO servers (name, owner_id) VALUES (:name, :owner_id)");
$stmt->execute(['name' => $server_name, 'owner_id' => $user_id]);
$server_id = $pdo->lastInsertId();
// 2. Add the owner to the server_members table
$stmt = $pdo->prepare("INSERT INTO server_members (server_id, user_id) VALUES (:server_id, :user_id)");
$stmt->execute(['server_id' => $server_id, 'user_id' => $user_id]);
// 3. Create a default 'general' channel for the new server
$stmt = $pdo->prepare("INSERT INTO channels (server_id, name) VALUES (:server_id, :name)");
$stmt->execute(['server_id' => $server_id, 'name' => 'general']);
// Commit the transaction
$pdo->commit();
// Redirect to the main page after creation
header('Location: index.php');
exit;
} catch (PDOException $e) {
// Rollback the transaction if something failed
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$error = "An error occurred while creating the server: " . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create a New Server</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body>
<div class="container-fluid">
<div class="row vh-100">
<div class="col-md-6 offset-md-3 d-flex flex-column justify-content-center">
<div class="card">
<div class="card-body p-5">
<h1 class="text-center mb-4">Create Your Server</h1>
<p class="text-center text-muted mb-4">Give your new server a personality with a name. You can always change it later.</p>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
<?php endif; ?>
<form action="create_server.php" method="POST">
<div class="mb-3">
<label for="server_name" class="form-label">SERVER NAME</label>
<input type="text" class="form-control" id="server_name" name="server_name" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
<div class="text-center mt-3">
<a href="index.php">Back to main page</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

31
db/migrate.php Normal file
View File

@ -0,0 +1,31 @@
<?php
require_once 'config.php';
try {
$pdo = db();
// Migration for servers table
$pdo->exec("CREATE TABLE IF NOT EXISTS servers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
owner_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES users(id)
) ENGINE=INNODB;");
// Migration for server_members table
$pdo->exec("CREATE TABLE IF NOT EXISTS server_members (
id INT AUTO_INCREMENT PRIMARY KEY,
server_id INT NOT NULL,
user_id INT NOT NULL,
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (server_id) REFERENCES servers(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE KEY (server_id, user_id)
) ENGINE=INNODB;");
echo "Database migration completed successfully! Tables 'servers' and 'server_members' are ready.";
} catch (PDOException $e) {
die("Database migration failed: " . $e->getMessage());
}

33
db/setup.php Normal file
View File

@ -0,0 +1,33 @@
<?php
require_once __DIR__ . '/config.php';
try {
// Connect to MySQL server without specifying a database
$pdo = new PDO('mysql:host=' . DB_HOST, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
// Create the database if it doesn't exist
$pdo->exec("CREATE DATABASE IF NOT EXISTS " . DB_NAME);
echo "Database '" . DB_NAME . "' created or already exists.\n";
// Now, connect to the specific database using the helper
$pdo = db();
// Create the users table
$sql = "
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
simple_id VARCHAR(10) NOT NULL UNIQUE,
ultra_id VARCHAR(20) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);";
$pdo->exec($sql);
echo "Table 'users' created successfully (if it didn't exist).\n";
} catch (PDOException $e) {
die("DB SETUP ERROR: " . $e->getMessage());
}
?>

55
get_channels.php Normal file
View File

@ -0,0 +1,55 @@
<?php
session_start();
header('Content-Type: application/json');
require_once 'db/config.php';
// User must be logged in
if (!isset($_SESSION['user_id'])) {
echo json_encode(['error' => 'Authentication required']);
http_response_code(401);
exit;
}
// Server ID must be provided
if (!isset($_GET['server_id'])) {
echo json_encode(['error' => 'Server ID is missing']);
http_response_code(400);
exit;
}
$user_id = $_SESSION['user_id'];
$server_id = (int)$_GET['server_id'];
try {
$pdo = db();
// Security check: Ensure the user is a member of the server
$stmt = $pdo->prepare("SELECT 1 FROM server_members WHERE server_id = :server_id AND user_id = :user_id");
$stmt->execute(['server_id' => $server_id, 'user_id' => $user_id]);
if ($stmt->fetchColumn() === false) {
echo json_encode(['error' => 'Access denied']);
http_response_code(403);
exit;
}
// Fetch server details
$stmt = $pdo->prepare("SELECT name FROM servers WHERE id = :server_id");
$stmt->execute(['server_id' => $server_id]);
$server = $stmt->fetch(PDO::FETCH_ASSOC);
// Fetch channels for the server
$stmt = $pdo->prepare("SELECT id, name FROM channels WHERE server_id = :server_id ORDER BY name ASC");
$stmt->execute(['server_id' => $server_id]);
$channels = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'server' => $server,
'channels' => $channels
]);
} catch (PDOException $e) {
echo json_encode(['error' => 'Database error']);
http_response_code(500);
// In a real app, you would log the error message, not expose it.
}

57
get_messages.php Normal file
View File

@ -0,0 +1,57 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'utils.php';
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit();
}
$user_id = $_SESSION['user_id'];
$channel_id = $_GET['channel_id'] ?? null;
if (!$channel_id) {
http_response_code(400);
echo json_encode(['error' => 'Channel ID is required']);
exit();
}
try {
$pdo = db();
// Verify user has access to this channel's server
$stmt = $pdo->prepare("
SELECT c.id
FROM channels c
JOIN servers s ON c.server_id = s.id
JOIN server_members sm ON s.id = sm.server_id
WHERE c.id = ? AND sm.user_id = ?
");
$stmt->execute([$channel_id, $user_id]);
if ($stmt->fetch() === false) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
exit();
}
// Fetch messages
$stmt = $pdo->prepare("
SELECT m.id, m.content, m.created_at, u.username
FROM messages m
JOIN users u ON m.user_id = u.id
WHERE m.channel_id = ?
ORDER BY m.created_at ASC
");
$stmt->execute([$channel_id]);
$messages = $stmt->fetchAll();
header('Content-Type: application/json');
echo json_encode($messages);
} catch (PDOException $e) {
error_log("Get Messages Error: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Internal Server Error']);
}

372
index.php
View File

@ -1,150 +1,238 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
require_once 'db/config.php';
require_once 'utils.php';
$user_id = $_SESSION['user_id'];
$p_user = get_user_by_id($user_id);
$servers = get_user_servers($user_id);
?>
<!doctype html>
<html lang="en">
<!DOCTYPE html>
<html lang="fr">
<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>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Discord V2</title>
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</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 class="app-container">
<!-- Servers List -->
<div class="servers-list">
<?php foreach ($servers as $server): ?>
<div class="server-icon" data-server-id="<?php echo $server['id']; ?>" title="<?php echo htmlspecialchars($server['name']); ?>">
<?php echo htmlspecialchars(strtoupper(substr($server['name'], 0, 1))); ?>
</div>
<?php endforeach; ?>
<a href="create_server.php" class="server-icon add-server-btn" title="Ajouter un serveur">+</a>
</div>
<!-- Sidebar (Channels & User Panel) -->
<div class="sidebar">
<div class="sidebar-header">
<h2 id="server-name">Sélectionnez un serveur</h2>
</div>
<div class="channels-list" id="channels-list">
<!-- Les canaux seront injectés ici par JS -->
</div>
<div class="user-panel">
<div class="user-info">
<div class="user-avatar"></div>
<span class="user-name"><?php echo htmlspecialchars($p_user['username']); ?></span>
</div>
<a href="logout.php" class="logout-btn" title="Déconnexion">
<svg width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M16 17v-3H9v-4h7V7l5 5-5 5zm-8-4H0v4h8v-4zm0-4H0v4h8V9zm0-4H0v4h8V5z"></path></svg>
</a>
</div>
</div>
<!-- Chat Area -->
<div class="chat-container">
<div class="chat-header">
<span id="channel-name"></span>
</div>
<div class="messages-list" id="messages-list">
<!-- Les messages seront injectés ici -->
<div style="text-align: center; color: var(--text-muted); margin-top: 20px;">
Sélectionnez un canal pour commencer à discuter.
</div>
</div>
<form class="chat-input-form" id="message-form" style="display: none;">
<input type="hidden" id="active-channel-id" name="channel_id">
<input type="text" id="message-input" name="message" class="chat-input" placeholder="Envoyer un message...">
</form>
</div>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<script>
document.addEventListener('DOMContentLoaded', function() {
const servers = document.querySelectorAll('.server-icon[data-server-id]');
const serverNameEl = document.getElementById('server-name');
const channelsListEl = document.getElementById('channels-list');
const channelNameEl = document.getElementById('channel-name');
const messagesListEl = document.getElementById('messages-list');
const messageForm = document.getElementById('message-form');
const messageInput = document.getElementById('message-input');
const activeChannelIdInput = document.getElementById('active-channel-id');
let activeServerId = null;
let activeChannelId = null;
servers.forEach(server => {
server.addEventListener('click', function() {
const serverId = this.dataset.serverId;
if (serverId === activeServerId) return;
// Update active state for server icons
servers.forEach(s => s.classList.remove('active'));
this.classList.add('active');
activeServerId = serverId;
activeChannelId = null; // Reset channel selection
// Clear UI
serverNameEl.textContent = 'Chargement...';
channelsListEl.innerHTML = '';
messagesListEl.innerHTML = '<div style="text-align: center; color: var(--text-muted); margin-top: 20px;">Sélectionnez un canal.</div>';
channelNameEl.textContent = '';
messageForm.style.display = 'none';
// Fetch server data (channels)
fetch(`get_channels.php?server_id=${serverId}`)
.then(response => response.json())
.then(data => {
if (data.error) {
serverNameEl.textContent = 'Erreur';
channelsListEl.innerHTML = `<p style="color: #f04747;">${data.error}</p>`;
return;
}
serverNameEl.textContent = data.server_name;
channelsListEl.innerHTML = ''; // Clear loading state
if (data.channels.length > 0) {
data.channels.forEach(channel => {
const channelEl = document.createElement('div');
channelEl.classList.add('channel');
channelEl.dataset.channelId = channel.id;
channelEl.textContent = channel.name;
channelEl.addEventListener('click', () => selectChannel(channel.id, channel.name));
channelsListEl.appendChild(channelEl);
});
// Automatically select the first channel
selectChannel(data.channels[0].id, data.channels[0].name);
} else {
channelsListEl.innerHTML = '<p style="color: var(--text-muted); padding: 0 16px;">Aucun canal trouvé.</p>';
}
})
.catch(error => {
console.error('Error fetching channels:', error);
serverNameEl.textContent = 'Erreur de connexion';
});
});
});
function selectChannel(channelId, channelName) {
if (channelId === activeChannelId) return;
activeChannelId = channelId;
// Update active state for channel list
document.querySelectorAll('.channel').forEach(c => {
if (c.dataset.channelId === channelId.toString()) {
c.classList.add('active');
} else {
c.classList.remove('active');
}
});
channelNameEl.textContent = channelName;
messagesListEl.innerHTML = '<p style="text-align:center; color: var(--text-muted);">Chargement des messages...</p>';
messageForm.style.display = 'block';
activeChannelIdInput.value = channelId;
messageInput.value = '';
messageInput.focus();
fetchMessages(channelId);
}
function fetchMessages(channelId) {
fetch(`get_messages.php?channel_id=${channelId}`)
.then(response => response.json())
.then(data => {
messagesListEl.innerHTML = '';
if (data.error) {
messagesListEl.innerHTML = `<p style="color: #f04747; text-align:center;">${data.error}</p>`;
return;
}
if (data.length > 0) {
data.forEach(msg => {
const msgEl = document.createElement('div');
msgEl.classList.add('message');
msgEl.innerHTML = `
<div class="message-avatar"></div>
<div class="message-content">
<span class="message-author">${escapeHTML(msg.username)}</span>
<div class="message-text">${escapeHTML(msg.message)}</div>
</div>
`;
messagesListEl.appendChild(msgEl);
});
} else {
messagesListEl.innerHTML = '<p style="text-align:center; color: var(--text-muted);">Aucun message. Soyez le premier à en envoyer un !</p>';
}
// Scroll to bottom
messagesListEl.scrollTop = messagesListEl.scrollHeight;
})
.catch(error => {
console.error('Error fetching messages:', error);
messagesListEl.innerHTML = '<p style="color: #f04747; text-align:center;">Erreur de chargement des messages.</p>';
});
}
messageForm.addEventListener('submit', function(e) {
e.preventDefault();
const message = messageInput.value.trim();
if (!message || !activeChannelId) return;
const formData = new FormData();
formData.append('channel_id', activeChannelId);
formData.append('message', message);
fetch('send_message.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
messageInput.value = '';
fetchMessages(activeChannelId); // Refresh messages
} else {
alert('Erreur lors de l'envoi du message: ' + data.error);
}
})
.catch(error => {
console.error('Error sending message:', error);
alert('Erreur réseau.');
});
});
function escapeHTML(str) {
return str.replace(/[&<>'"/]/g, function(match) {
return {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
}[match];
});
}
});
</script>
</body>
</html>
</html>

96
login.php Normal file
View File

@ -0,0 +1,96 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'utils.php';
// If user is already logged in, redirect to main page
if (isset($_SESSION['user_id'])) {
redirect('index.php');
}
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username)) {
$errors[] = 'Le nom d\'utilisateur est requis.';
}
if (empty($password)) {
$errors[] = 'Le mot de passe est requis.';
}
if (empty($errors)) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
// Password is correct, start session and redirect
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
// Explicitly redirect
header("Location: index.php");
// Fallback with JavaScript redirection
echo '<script>window.location.href = "index.php";</script>';
exit();
} else {
$errors[] = 'Nom d\'utilisateur ou mot de passe incorrect.';
}
} catch (PDOException $e) {
error_log("Login Error: " . $e->getMessage());
$errors[] = 'Une erreur est survenue lors de la connexion. Veuillez réessayer.';
}
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connexion - Discord V2</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Connexion</h3>
</div>
<div class="card-body">
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="login.php" method="POST" novalidate>
<div class="mb-3">
<label for="username" class="form-label">Nom d'utilisateur</label>
<input type="text" class="form-control" id="username" name="username" required value="<?php echo isset($_POST['username']) ? htmlspecialchars($_POST['username']) : '' ?>">
</div>
<div class="mb-3">
<label for="password" class="form-label">Mot de passe</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Se connecter</button>
</form>
</div>
<div class="card-footer text-center">
<a href="register.php">Pas encore de compte ? Inscrivez-vous</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

7
logout.php Normal file
View File

@ -0,0 +1,7 @@
<?php
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit();
?>

114
register.php Normal file
View File

@ -0,0 +1,114 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'utils.php';
$errors = [];
$success = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username)) {
$errors[] = 'Le nom d\'utilisateur est requis.';
}
if (empty($password)) {
$errors[] = 'Le mot de passe est requis.';
}
// Basic password length validation
if (strlen($password) < 8) {
$errors[] = 'Le mot de passe doit contenir au moins 8 caractères.';
}
if (empty($errors)) {
try {
$pdo = db();
// Check if username already exists
$stmt = $pdo->prepare("SELECT COUNT(*) FROM users WHERE username = ?");
$stmt->execute([$username]);
if ($stmt->fetchColumn() > 0) {
$errors[] = 'Ce nom d\'utilisateur est déjà pris.';
} else {
// Generate unique IDs
$simple_id = generateSimpleId();
$ultra_id = generateUltraId();
// Hash password
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Insert user
$stmt = $pdo->prepare("INSERT INTO users (username, password, simple_id, ultra_id) VALUES (?, ?, ?, ?)");
$stmt->execute([$username, $hashed_password, $simple_id, $ultra_id]);
// Get the new user's ID
$user_id = $pdo->lastInsertId();
// Log the user in
$_SESSION['user_id'] = $user_id;
$_SESSION['username'] = $username;
// Redirect to the main page
header("Location: index.php");
exit();
}
} catch (PDOException $e) {
error_log("Registration Error: " . $e->getMessage());
$errors[] = 'Une erreur est survenue lors de l\'inscription. Veuillez réessayer.';
}
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inscription - Discord V2</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Inscription</h3>
</div>
<div class="card-body">
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success">
<p><?php echo htmlspecialchars($success); ?></p>
<p>Redirection vers la page de connexion...</p>
</div>
<?php else: ?>
<form action="register.php" method="POST" novalidate>
<div class="mb-3">
<label for="username" class="form-label">Nom d'utilisateur</label>
<input type="text" class="form-control" id="username" name="username" required value="<?php echo isset($_POST['username']) ? htmlspecialchars($_POST['username']) : '' ?>">
</div>
<div class="mb-3">
<label for="password" class="form-label">Mot de passe</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">S'inscrire</button>
</form>
<?php endif; ?>
</div>
<div class="card-footer text-center">
<a href="login.php">Déjà un compte ? Connectez-vous</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

68
send_message.php Normal file
View File

@ -0,0 +1,68 @@
<?php
session_start();
require_once 'db/config.php';
require_once 'utils.php';
if (!isset($_SESSION['user_id'])) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit();
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit();
}
$user_id = $_SESSION['user_id'];
$data = json_decode(file_get_contents('php://input'), true);
$channel_id = $data['channel_id'] ?? null;
$content = $data['content'] ?? null;
if (!$channel_id || !$content) {
http_response_code(400);
echo json_encode(['error' => 'Channel ID and content are required']);
exit();
}
$content = trim($content);
if (empty($content)) {
http_response_code(400);
echo json_encode(['error' => 'Message content cannot be empty']);
exit();
}
try {
$pdo = db();
// Verify user has access to this channel's server
$stmt = $pdo->prepare("
SELECT c.id
FROM channels c
JOIN servers s ON c.server_id = s.id
JOIN server_members sm ON s.id = sm.server_id
WHERE c.id = ? AND sm.user_id = ?
");
$stmt->execute([$channel_id, $user_id]);
if ($stmt->fetch() === false) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
exit();
}
// Insert message
$stmt = $pdo->prepare("
INSERT INTO messages (channel_id, user_id, content)
VALUES (?, ?, ?)
");
$stmt->execute([$channel_id, $user_id, $content]);
header('Content-Type: application/json');
echo json_encode(['success' => true, 'message' => 'Message sent']);
} catch (PDOException $e) {
error_log("Send Message Error: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Internal Server Error']);
}

17
utils.php Normal file
View File

@ -0,0 +1,17 @@
<?php
function generateSimpleId() {
// Generates a random 4-digit number with a prefix
return 'U' . str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT);
}
function generateUltraId() {
// Generates a more complex, unique ID using random bytes for better uniqueness
return 'ULTRA-' . strtoupper(bin2hex(random_bytes(3))) . '-' . strtoupper(bin2hex(random_bytes(3)));
}
// Function to redirect and exit
function redirect($url) {
header('Location: ' . $url);
exit();
}
?>