Initial commit - mini cms final

This commit is contained in:
Aya Tess tess
2025-11-03 21:53:58 +01:00
parent a366336dc1
commit a01f620ef9
14 changed files with 422 additions and 197 deletions

1
.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/infrastructure" vcs="Git" />
</component> </component>
</project> </project>

View File

@@ -1,19 +1,24 @@
<?php <?php
session_start();
require_once 'config.php'; require_once 'config.php';
// ✅ Vérification que l'utilisateur est connecté
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['id'])) {
header("Location: login.php");
exit;
}
$author_id = $_SESSION['user']['id'];
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = trim($_POST['title'] ?? ''); $title = trim($_POST['title'] ?? '');
$content = trim($_POST['content'] ?? ''); $content = trim($_POST['content'] ?? '');
$author_id = $_SESSION['user_id'] ?? null;
if (!$author_id) {
echo "❌ Erreur : utilisateur non connecté.";
exit;
}
$imageUrl = null; $imageUrl = null;
global $s3Client, $bucketName; global $s3Client, $bucketName;
// ✅ Upload de l'image sur MinIO
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$fileName = time() . '_' . basename($_FILES['image']['name']); $fileName = time() . '_' . basename($_FILES['image']['name']);
$tmpPath = $_FILES['image']['tmp_name']; $tmpPath = $_FILES['image']['tmp_name'];
@@ -27,19 +32,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
]); ]);
$imageUrl = "http://localhost:9000/$bucketName/$fileName"; $imageUrl = "http://localhost:9000/$bucketName/$fileName";
} catch (Exception $e) { } catch (Exception $e) {
echo "❌ Erreur upload MinIO : " . $e->getMessage(); $message = "❌ Erreur upload MinIO : " . $e->getMessage();
exit;
} }
} }
// ✅ Insertion de l'article dans la base de données
if ($title && $content) {
try { try {
$stmt = $pdo->prepare("INSERT INTO posts (title, content, image_url, user_id, date_creation) $stmt = $pdo->prepare("
VALUES (?, ?, ?, ?, NOW())"); INSERT INTO posts (title, content, image_url, user_id, date_creation)
VALUES (?, ?, ?, ?, NOW())
");
$stmt->execute([$title, $content, $imageUrl, $author_id]); $stmt->execute([$title, $content, $imageUrl, $author_id]);
header("Location: dashboard.php");
// ✅ Redirection vers la page d'accueil
header("Location: index.php");
exit; exit;
} catch (PDOException $e) { } catch (PDOException $e) {
echo "❌ Erreur base de données : " . $e->getMessage(); $message = "❌ Erreur base de données : " . $e->getMessage();
}
} else {
$message = "⚠️ Tous les champs doivent être remplis.";
} }
} }
?> ?>
@@ -140,6 +153,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<div class="container"> <div class="container">
<h2>📝 Publier un nouvel article</h2> <h2>📝 Publier un nouvel article</h2>
<?php if ($message): ?>
<p style="color:red;"><?= htmlspecialchars($message) ?></p>
<?php endif; ?>
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<label for="title">Titre :</label> <label for="title">Titre :</label>
<input type="text" id="title" name="title" required> <input type="text" id="title" name="title" required>
@@ -153,7 +170,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<button type="submit">Publier larticle</button> <button type="submit">Publier larticle</button>
</form> </form>
<?php if ($_SESSION['user']['role'] === 'admin'): ?>
<a href="dashboard.php" class="retour">⬅ Retour au tableau de bord</a> <a href="dashboard.php" class="retour">⬅ Retour au tableau de bord</a>
<?php else: ?>
<a href="index.php" class="retour">⬅ Retour à laccueil</a>
<?php endif; ?>
</div> </div>
<footer>🕒 <?= date('Y') ?> — Mini CMS by Aya 💖</footer> <footer>🕒 <?= date('Y') ?> — Mini CMS by Aya 💖</footer>

View File

@@ -4,10 +4,9 @@ if (session_status() === PHP_SESSION_NONE) {
} }
require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/vendor/autoload.php';
use Aws\S3\S3Client; use Aws\S3\S3Client;
// ✅ Connexion PDO // ✅ Connexion PDO MySQL UTF-8 universelle
try { try {
$pdo = new PDO( $pdo = new PDO(
'mysql:host=mysql;dbname=forum_database;charset=utf8mb4', 'mysql:host=mysql;dbname=forum_database;charset=utf8mb4',
@@ -15,14 +14,15 @@ try {
'mypassword', 'mypassword',
[ [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
] ]
); );
} catch (PDOException $e) { } catch (PDOException $e) {
die("Erreur de connexion MySQL : " . $e->getMessage()); die("Erreur de connexion MySQL : " . $e->getMessage());
} }
// ✅ Client MinIO // ✅ Configuration du client MinIO (S3-compatible)
$s3Client = new S3Client([ $s3Client = new S3Client([
'version' => 'latest', 'version' => 'latest',
'region' => 'us-east-1', 'region' => 'us-east-1',
@@ -35,3 +35,36 @@ $s3Client = new S3Client([
]); ]);
$bucketName = 'bucketforum'; $bucketName = 'bucketforum';
// ✅ Sassurer que le bucket existe (création auto si absent)
try {
$buckets = $s3Client->listBuckets();
$bucketExists = false;
foreach ($buckets['Buckets'] as $bucket) {
if ($bucket['Name'] === $bucketName) {
$bucketExists = true;
break;
}
}
if (!$bucketExists) {
$s3Client->createBucket(['Bucket' => $bucketName]);
// Rendre le bucket public automatiquement
$policy = json_encode([
'Version' => '2012-10-17',
'Statement' => [[
'Effect' => 'Allow',
'Principal' => '*',
'Action' => ['s3:GetObject'],
'Resource' => "arn:aws:s3:::{$bucketName}/*"
]]
]);
$s3Client->putBucketPolicy([
'Bucket' => $bucketName,
'Policy' => $policy
]);
}
} catch (Exception $e) {
echo "⚠️ Impossible de vérifier/créer le bucket MinIO : " . $e->getMessage();
}
?>

View File

@@ -1,11 +1,9 @@
<?php <?php
// Démarre la session et le tampon proprement (évite les erreurs de headers)
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {
session_start(); session_start();
} }
if (!ob_get_level()) ob_start(); if (!ob_get_level()) ob_start();
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
@@ -42,40 +40,60 @@ if (!ob_get_level()) ob_start();
color: #fff8f9; color: #fff8f9;
text-shadow: 0 0 5px white; text-shadow: 0 0 5px white;
} }
/* ✅ Zone cliquable agrandie et fluide */
.profile-menu { .profile-menu {
position: relative; position: relative;
display: inline-block; display: inline-block;
cursor: pointer;
padding: 5px 10px;
border-radius: 25px;
transition: background 0.3s;
} }
.profile-menu:hover {
background: rgba(255, 255, 255, 0.2);
}
.profile-pic { .profile-pic {
width: 40px; width: 42px;
height: 40px; height: 42px;
border-radius: 50%; border-radius: 50%;
object-fit: cover; object-fit: cover;
border: 2px solid white; border: 2px solid white;
cursor: pointer; vertical-align: middle;
} }
.dropdown { .dropdown {
display: none; display: none;
position: absolute; position: absolute;
right: 0; right: 0;
top: 55px;
background: white; background: white;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 3px 10px rgba(0,0,0,0.15); box-shadow: 0 3px 12px rgba(0,0,0,0.15);
margin-top: 8px; min-width: 180px;
min-width: 160px; overflow: hidden;
z-index: 1000;
} }
.dropdown a { .dropdown a {
color: #ff91a4; color: #ff91a4;
padding: 10px 15px; padding: 12px 16px;
text-decoration: none; text-decoration: none;
display: block; display: block;
transition: 0.2s;
} }
.dropdown a:hover { .dropdown a:hover {
background: #ffe5ec; background: #ffe5ec;
} }
.profile-menu:hover .dropdown {
/* ✅ Ouverture plus fluide (hover ou clic sur toute la zone) */
.profile-menu:hover .dropdown,
.profile-menu:focus-within .dropdown {
display: block; display: block;
} }
.logo { .logo {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -105,7 +123,7 @@ if (!ob_get_level()) ob_start();
<a href="dashboard.php">Gestion</a> <a href="dashboard.php">Gestion</a>
<?php endif; ?> <?php endif; ?>
<a href="add_article.php">Publier un article</a> <a href="add_article.php">Publier un article</a>
<div class="profile-menu"> <div class="profile-menu" tabindex="0">
<?php if (!empty($_SESSION['user']['profile_picture'])): ?> <?php if (!empty($_SESSION['user']['profile_picture'])): ?>
<img src="<?= htmlspecialchars($_SESSION['user']['profile_picture']) ?>" alt="Profil" class="profile-pic"> <img src="<?= htmlspecialchars($_SESSION['user']['profile_picture']) ?>" alt="Profil" class="profile-pic">
<?php else: ?> <?php else: ?>
@@ -117,7 +135,6 @@ if (!ob_get_level()) ob_start();
</div> </div>
</div> </div>
<?php else: ?> <?php else: ?>
<!-- Boutons connexion / inscription -->
<a href="login.php">Connexion</a> <a href="login.php">Connexion</a>
<a href="register.php">Inscription</a> <a href="register.php">Inscription</a>
<?php endif; ?> <?php endif; ?>

View File

@@ -1,59 +1,195 @@
<?php <?php
// /var/www/html/profile.php
session_start(); session_start();
require_once 'config.php'; require_once 'config.php';
include 'header.php';
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['id'])) { if (!isset($_SESSION['user']) || !isset($_SESSION['user']['id'])) {
header('Location: login.php'); exit; header("Location: login.php");
exit;
} }
$userId = (int)$_SESSION['user']['id'];
$userId = $_SESSION['user']['id'];
$message = ''; $message = '';
// POST handling (update or delete) // ✅ Mise à jour de la bio et/ou de la photo
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$uploadDir = __DIR__ . '/uploads/profiles/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
// delete?
if (isset($_POST['delete_picture'])) {
// remove file if any
if (!empty($_SESSION['user']['profile_picture']) && file_exists(__DIR__ . '/' . $_SESSION['user']['profile_picture'])) {
@unlink(__DIR__ . '/' . $_SESSION['user']['profile_picture']);
}
$stmt = $pdo->prepare("UPDATE utilisateurs SET profile_picture = NULL WHERE id = ?");
$stmt->execute([$userId]);
$_SESSION['user']['profile_picture'] = null;
header('Location: profile.php'); exit;
}
// update (bio + optional file)
$bio = trim($_POST['bio'] ?? ''); $bio = trim($_POST['bio'] ?? '');
$profilePath = $_SESSION['user']['profile_picture'] ?? null; $profilePictureUrl = $_SESSION['user']['profile_picture'] ?? null;
if (!empty($_FILES['profile_picture']['name'])) {
$fname = time() . '_' . preg_replace('/[^A-Za-z0-9_.-]/', '', basename($_FILES['profile_picture']['name'])); global $s3Client, $bucketName;
$target = $uploadDir . $fname;
if (in_array(strtolower(pathinfo($fname, PATHINFO_EXTENSION)), ['jpg','jpeg','png']) && move_uploaded_file($_FILES['profile_picture']['tmp_name'], $target)) { // 📸 Upload d'une nouvelle image
$profilePath = 'uploads/profiles/' . $fname; if (isset($_FILES['profile_picture']) && $_FILES['profile_picture']['error'] === UPLOAD_ERR_OK) {
} else { $fileName = 'profile_' . $userId . '_' . time() . '_' . basename($_FILES['profile_picture']['name']);
$message = "❌ Problème lors de l'upload (format jpg/png uniquement)."; $tmpPath = $_FILES['profile_picture']['tmp_name'];
try {
$s3Client->putObject([
'Bucket' => $bucketName,
'Key' => $fileName,
'SourceFile' => $tmpPath,
'ACL' => 'public-read'
]);
$profilePictureUrl = "http://localhost:9000/$bucketName/$fileName";
} catch (Exception $e) {
$message = "❌ Erreur upload MinIO : " . $e->getMessage();
} }
} }
// update DB
// 🧹 Suppression de la photo
if (isset($_POST['delete_picture'])) {
$profilePictureUrl = null;
}
try {
$stmt = $pdo->prepare("UPDATE utilisateurs SET bio = ?, profile_picture = ? WHERE id = ?"); $stmt = $pdo->prepare("UPDATE utilisateurs SET bio = ?, profile_picture = ? WHERE id = ?");
$stmt->execute([$bio, $profilePath, $userId]); $stmt->execute([$bio, $profilePictureUrl, $userId]);
// sync session
$_SESSION['user']['bio'] = $bio; $_SESSION['user']['bio'] = $bio;
$_SESSION['user']['profile_picture'] = $profilePath; $_SESSION['user']['profile_picture'] = $profilePictureUrl;
header('Location: profile.php'); exit;
$message = "✅ Profil mis à jour avec succès !";
} catch (PDOException $e) {
$message = "❌ Erreur base de données : " . $e->getMessage();
}
} }
// fetch fresh data // ✅ Récupération des infos utilisateur
$stmt = $pdo->prepare("SELECT username, bio, profile_picture FROM utilisateurs WHERE id = ?"); $stmt = $pdo->prepare("SELECT username, role, bio, profile_picture FROM utilisateurs WHERE id = ?");
$stmt->execute([$userId]); $stmt->execute([$userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC); $user = $stmt->fetch();
?>
<!-- include the HTML/CSS you prefer (same as earlier). -->
<?php include 'footer.php'; ?>
// ✅ Détermine la redirection correcte
if ($user['role'] === 'admin') {
$redirectPage = "dashboard.php"; // Admin → tableau de bord admin
} else {
$redirectPage = "index.php"; // Utilisateur → accueil public
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Profil de <?= htmlspecialchars($user['username']) ?></title>
<style>
body {
font-family: 'Poppins', sans-serif;
background: linear-gradient(120deg, #fff4f7, #ffe6ea);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 6px 18px rgba(255, 100, 150, 0.2);
text-align: center;
width: 400px;
}
h2 {
color: #ff69b4;
margin-bottom: 15px;
}
.profile-picture {
width: 120px;
height: 120px;
border-radius: 50%;
object-fit: cover;
border: 3px solid #ff69b4;
margin-bottom: 10px;
}
form {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 15px;
}
textarea {
padding: 10px;
border-radius: 8px;
border: 1px solid #ddd;
resize: vertical;
font-family: 'Poppins', sans-serif;
}
button {
background: linear-gradient(45deg, #ff69b4, #ffa07a);
border: none;
color: white;
padding: 10px;
border-radius: 20px;
cursor: pointer;
font-weight: 600;
transition: 0.3s;
}
button:hover {
opacity: 0.9;
}
.actions {
display: flex;
justify-content: center;
gap: 10px;
}
.message {
margin: 10px 0;
color: #ff69b4;
font-weight: bold;
}
a {
color: #ff69b4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h2>Profil de <?= htmlspecialchars($user['username']) ?></h2>
<?php if (!empty($user['profile_picture'])): ?>
<img src="<?= htmlspecialchars($user['profile_picture']) ?>" class="profile-picture" alt="Photo de profil">
<?php else: ?>
<div style="font-size:60px;">👤</div>
<?php endif; ?>
<p><strong>Rôle :</strong> <?= htmlspecialchars($user['role']) ?></p>
<form method="POST" enctype="multipart/form-data">
<label for="bio">Votre bio :</label>
<textarea name="bio" id="bio" rows="4"><?= htmlspecialchars($user['bio'] ?? '') ?></textarea>
<label for="profile_picture">Changer la photo :</label>
<input type="file" name="profile_picture" id="profile_picture" accept="image/png, image/jpeg">
<div class="actions">
<button type="submit" name="update_profile">Mettre à jour</button>
<button type="submit" name="delete_picture" style="background:#ddd;color:#333;">Supprimer la photo</button>
</div>
</form>
<?php if ($message): ?>
<div class="message"><?= htmlspecialchars($message) ?></div>
<?php endif; ?>
<p style="margin-top:20px;">
<a href="<?= $redirectPage ?>">⬅ Retour au tableau de bord</a>
</p>
</div>
</body>
</html>

View File

@@ -10,13 +10,11 @@ if ($id <= 0) {
exit; exit;
} }
// fetch user
$stmt = $pdo->prepare("SELECT id, username, bio, profile_picture FROM utilisateurs WHERE id = ?"); $stmt = $pdo->prepare("SELECT id, username, bio, profile_picture FROM utilisateurs WHERE id = ?");
$stmt->execute([$id]); $stmt->execute([$id]);
$u = $stmt->fetch(PDO::FETCH_ASSOC); $u = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$u) { echo "Utilisateur introuvable."; exit; } if (!$u) { echo "Utilisateur introuvable."; exit; }
// fetch posts by user
$stmt2 = $pdo->prepare("SELECT id, title, content, image_url, date_creation FROM posts WHERE user_id = ? ORDER BY date_creation DESC"); $stmt2 = $pdo->prepare("SELECT id, title, content, image_url, date_creation FROM posts WHERE user_id = ? ORDER BY date_creation DESC");
$stmt2->execute([$id]); $stmt2->execute([$id]);
$posts = $stmt2->fetchAll(PDO::FETCH_ASSOC); $posts = $stmt2->fetchAll(PDO::FETCH_ASSOC);

View File

@@ -1,26 +1,28 @@
<?php <?php
session_start(); session_start();
require_once "config.php"; require_once 'config.php';
include "header.php";
// Vérifier si le formulaire a été soumis $message = '';
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$username = trim($_POST["username"]);
$password = password_hash($_POST["password"], PASSWORD_BCRYPT);
$role = "auteur"; // Par défaut, tout nouvel utilisateur est auteur
// Vérifier si le nom d'utilisateur existe déjà if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmt = $pdo->prepare("SELECT id FROM utilisateurs WHERE username = ?"); $username = trim($_POST['username'] ?? '');
$stmt->execute([$username]); $email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if ($stmt->rowCount() > 0) { if ($username && $email && $password) {
echo "<p style='color:red;text-align:center;'>⚠️ Ce nom d'utilisateur existe déjà.</p>"; $hashedPassword = password_hash($password, PASSWORD_BCRYPT);
$role = 'auteur'; // ✅ tout nouvel utilisateur est un auteur
try {
$stmt = $pdo->prepare("INSERT INTO utilisateurs (username, email, password, role) VALUES (?, ?, ?, ?)");
$stmt->execute([$username, $email, $hashedPassword, $role]);
header("Location: login.php");
exit;
} catch (PDOException $e) {
$message = "❌ Erreur lors de l'inscription : " . $e->getMessage();
}
} else { } else {
// Insérer le nouvel utilisateur $message = "⚠️ Tous les champs sont obligatoires.";
$stmt = $pdo->prepare("INSERT INTO utilisateurs (username, password, role) VALUES (?, ?, ?)");
$stmt->execute([$username, $password, $role]);
echo "<p style='color:green;text-align:center;'>✅ Inscription réussie ! Vous pouvez maintenant vous connecter.</p>";
} }
} }
?> ?>
@@ -29,70 +31,58 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Inscription - Mini CMS</title> <title>Créer un compte ✨</title>
<style> <style>
body { body {
background: linear-gradient(135deg, #ffb6c1, #ffa07a);
font-family: 'Poppins', sans-serif; font-family: 'Poppins', sans-serif;
margin: 0; background: linear-gradient(120deg, #ffe0ec, #fff6f8);
height: 100vh;
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
} }
.container { .container {
background: white; background: white;
padding: 40px; padding: 40px;
border-radius: 15px; border-radius: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1); box-shadow: 0 4px 12px rgba(0,0,0,0.1);
width: 400px; width: 380px;
text-align: center; text-align: center;
} }
h2 { input {
color: #ff69b4;
margin-bottom: 20px;
}
input[type=text], input[type=password] {
width: 90%; width: 90%;
padding: 10px; padding: 10px;
margin: 10px 0; margin: 10px 0;
border: 1px solid #ddd;
border-radius: 8px; border-radius: 8px;
border: 1px solid #ccc;
} }
button { button {
background: linear-gradient(45deg, #ff69b4, #ffa07a); background: linear-gradient(45deg, #ff69b4, #ffa07a);
color: white;
border: none; border: none;
padding: 12px 25px; color: white;
border-radius: 25px; padding: 10px 20px;
border-radius: 20px;
cursor: pointer; cursor: pointer;
font-weight: bold;
}
button:hover {
opacity: 0.9;
}
a {
color: #ff69b4;
text-decoration: none;
font-weight: 600; font-weight: 600;
width: 95%;
} }
a:hover { button:hover { opacity: 0.9; }
text-decoration: underline; .message { color: red; margin-bottom: 10px; }
} a { color: #ff69b4; text-decoration: none; }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h2>Créer un compte</h2> <h2>Créer un compte</h2>
<?php if ($message): ?><div class="message"><?= $message ?></div><?php endif; ?>
<form method="POST"> <form method="POST">
<input type="text" name="username" placeholder="Nom d'utilisateur" required><br> <input type="text" name="username" placeholder="Nom d'utilisateur" required><br>
<input type="email" name="email" placeholder="Email" required><br>
<input type="password" name="password" placeholder="Mot de passe" required><br> <input type="password" name="password" placeholder="Mot de passe" required><br>
<button type="submit">S'inscrire</button> <button type="submit">S'inscrire</button>
</form> </form>
<p>Déjà un compte ? <a href="login.php">Se connecter</a></p> <p>Déjà inscrit ? <a href="login.php">Connectez-vous</a></p>
</div> </div>
</body> </body>
</html> </html>
<?php include "footer.php"; ?>

View File

@@ -1,4 +1,4 @@
/* 🎨 — Thème global rose/orangé élégant */
body { body {
font-family: "Poppins", sans-serif; font-family: "Poppins", sans-serif;
background: #fffafc; background: #fffafc;
@@ -7,7 +7,7 @@ body {
padding: 0; padding: 0;
} }
/* 🌟 — En-tête du site */
header { header {
background: linear-gradient(90deg, #ffa45b, #ff6fa7); background: linear-gradient(90deg, #ffa45b, #ff6fa7);
color: white; color: white;
@@ -26,7 +26,7 @@ header a:hover {
text-decoration: underline; text-decoration: underline;
} }
/* 👤 — Auteur dans la liste darticles */
.article-meta { .article-meta {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -51,7 +51,7 @@ header a:hover {
text-decoration: underline; text-decoration: underline;
} }
/* 🧍‍♀️ — Page de profil publique (profile_view.php) */
.profile-view { .profile-view {
max-width: 900px; max-width: 900px;
margin: 60px auto; margin: 60px auto;
@@ -91,7 +91,7 @@ header a:hover {
margin-top: 5px; margin-top: 5px;
} }
/* 📰 — Articles de lauteur */
.user-articles h3 { .user-articles h3 {
color: #ff6fa7; color: #ff6fa7;
margin-bottom: 15px; margin-bottom: 15px;
@@ -140,7 +140,7 @@ header a:hover {
opacity: 0.9; opacity: 0.9;
} }
/* 📱 — Responsive */
@media (max-width: 768px) { @media (max-width: 768px) {
header { header {
flex-direction: column; flex-direction: column;

View File

@@ -1,7 +1,7 @@
<?php <?php
require_once __DIR__ . '/config.php'; // doit définir $s3Client et $bucketName require_once __DIR__ . '/config.php'; // doit définir $s3Client et $bucketName
// crée un petit fichier temporaire
$tmp = '/tmp/test_minio_upload_'.time().'.txt'; $tmp = '/tmp/test_minio_upload_'.time().'.txt';
file_put_contents($tmp, "test upload ".time()); file_put_contents($tmp, "test upload ".time());

0
infrastructure/README.md Normal file
View File

View File

@@ -12,10 +12,9 @@ services:
- ../forum-project:/var/www/html - ../forum-project:/var/www/html
env_file: env_file:
- .env - .env
depends_on: depends_on:
- mysql - mysql
- minio
mysql: mysql:
image: mysql:8.1 image: mysql:8.1
@@ -33,6 +32,7 @@ services:
volumes: volumes:
- mysql_data:/var/lib/mysql - mysql_data:/var/lib/mysql
- ./setup-mysql:/docker-entrypoint-initdb.d - ./setup-mysql:/docker-entrypoint-initdb.d
- ./setup-mysql/my.cnf:/etc/mysql/conf.d/my.cnf
minio: minio:
@@ -64,5 +64,3 @@ services:
volumes: volumes:
mysql_data: mysql_data:
minio_data: minio_data:

29
infrastructure/setup-minio/init-minio.sh Normal file → Executable file
View File

@@ -1,13 +1,26 @@
#!/bin/sh #!/bin/sh
set -e
# Attendre que MinIO démarre # Attendre que MinIO démarre
echo "Waiting for MinIO to start..." echo "⏳ Attente du démarrage de MinIO..."
sleep 10 sleep 5
# Créer un alias pour MinIO # Configuration du client MinIO (mc)
mc alias set localminio http://minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD echo "🔧 Configuration du client MinIO..."
mc alias set myminio http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
# Créer le bucket (ignore erreur si existe déjà) # Création du bucket sil nexiste pas déjà
mc mb localminio/bucketforum || true echo "🪣 Vérification du bucket 'bucketforum'..."
if ! mc ls myminio | grep -q "bucketforum"; then
mc mb myminio/bucketforum
echo "✅ Bucket 'bucketforum' créé."
else
echo " Bucket 'bucketforum' déjà existant."
fi
# Définir une politique publique sur le bucket (optionnel) # Rendre le bucket public
mc policy set public localminio/bucketforum echo "🌍 Configuration de la politique publique..."
mc anonymous set public myminio/bucketforum
# Vérification du statut
echo "✅ Bucket 'bucketforum' est public et prêt à être utilisé."

View File

@@ -1,33 +1,41 @@
-- encodage base
CREATE DATABASE IF NOT EXISTS forum_database
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE forum_database;
-- table utilisateurs
CREATE TABLE IF NOT EXISTS utilisateurs ( CREATE TABLE IF NOT EXISTS utilisateurs (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE, username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL,
email VARCHAR(100) UNIQUE, email VARCHAR(100) UNIQUE,
role VARCHAR(50) DEFAULT 'user',
bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
profile_picture VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- table posts
CREATE TABLE IF NOT EXISTS posts ( CREATE TABLE IF NOT EXISTS posts (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL, user_id INT NOT NULL,
title VARCHAR(100) NOT NULL, title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
content TEXT NOT NULL, content LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
image_url VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES utilisateurs(id) date_creation DATETIME DEFAULT CURRENT_TIMESTAMP,
); FOREIGN KEY (user_id) REFERENCES utilisateurs(id) ON DELETE CASCADE
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
INSERT INTO utilisateurs (username, password, email) -- données initiales
INSERT INTO utilisateurs (username, password, email, role)
VALUES VALUES
('aya', 'password123', 'ayae@example.com'), ('admin', '$2y$10$4U8a64xTrD/GQDnj8tCNsemA47765p.5gtflWWBKl6UGl2aj/uOlC', 'admin@example.com', 'admin'),
('tess', 'password456', 'tess@example.com'); ('aya', '$2y$10$vhtGydzlL8AFMbRTeRVC2OtDkud47TZCHt98HcAjjGGVlLlm2Bn66', 'aya@example.com', 'user');
INSERT INTO posts (user_id, title, content) INSERT INTO posts (user_id, title, content)
VALUES VALUES
(1, 'Bienvenue sur le forum', 'Ceci est le premier post !'), (1, 'Bienvenue sur le Mini CMS', 'Ceci est un article de démonstration publié par ladministrateur !'),
(2, 'Deuxième post', 'Un autre test pour vérifier la base.'); (2, 'Un premier article test', 'Bienvenue dans notre CMS simplifié. Partagez vos idées ici !');
ALTER TABLE posts
DROP FOREIGN KEY posts_ibfk_1;
ALTER TABLE posts
ADD CONSTRAINT posts_ibfk_1
FOREIGN KEY (user_id) REFERENCES utilisateurs(id)
ON DELETE CASCADE;

View File

@@ -0,0 +1,10 @@
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
skip-character-set-client-handshake
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4