premières bases du projet

This commit is contained in:
Esenjin 2025-02-14 17:12:59 +01:00
parent 9f919674de
commit 303194437c
10 changed files with 373 additions and 0 deletions

55
admin/index.php Normal file
View File

@ -0,0 +1,55 @@
<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
require_once '../includes/stories.php';
// Vérification de l'authentification
if (!Auth::check()) {
header('Location: login.php');
exit;
}
$stories = Stories::getAll();
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Administration</title>
<link rel="stylesheet" href="../assets/css/admin.css">
</head>
<body>
<nav class="admin-nav">
<div class="nav-brand">Administration</div>
<div class="nav-menu">
<a href="story-edit.php" class="button">Nouveau roman</a>
<form method="POST" action="logout.php" class="logout-form">
<button type="submit">Déconnexion</button>
</form>
</div>
</nav>
<main class="admin-main">
<h1>Gestion des romans</h1>
<div class="stories-list">
<?php foreach ($stories as $story): ?>
<div class="story-item">
<img src="<?= htmlspecialchars($story['cover']) ?>" alt="" class="story-cover">
<div class="story-info">
<h2><?= htmlspecialchars($story['title']) ?></h2>
<p>Dernière modification : <?= htmlspecialchars($story['updated']) ?></p>
</div>
<div class="story-actions">
<a href="story-edit.php?id=<?= htmlspecialchars($story['id']) ?>" class="button">Modifier</a>
<button type="button" class="button delete-story" data-id="<?= htmlspecialchars($story['id']) ?>">Supprimer</button>
</div>
</div>
<?php endforeach; ?>
</div>
</main>
<script src="../assets/js/admin.js"></script>
</body>
</html>

50
admin/login.php Normal file
View File

@ -0,0 +1,50 @@
<?php
require_once '../includes/config.php';
require_once '../includes/auth.php';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if (Auth::login($username, $password)) {
header('Location: index.php');
exit;
} else {
$error = 'Identifiants incorrects';
}
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Connexion - Administration</title>
<link rel="stylesheet" href="../assets/css/admin.css">
</head>
<body class="login-page">
<div class="login-container">
<h1>Connexion</h1>
<?php if ($error): ?>
<div class="error-message"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
<form method="POST" class="login-form">
<div class="form-group">
<label for="username">Identifiant</label>
<input type="text" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password">Mot de passe</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Se connecter</button>
</form>
</div>
</body>
</html>

6
admin/logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
require_once '../includes/auth.php';
Auth::logout();
header('Location: login.php');
exit;

140
assets/css/admin.css Normal file
View File

@ -0,0 +1,140 @@
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #f5f5f5;
}
/* Login Page */
.login-page {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background-color: #2c1810;
}
.login-container {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
.login-form .form-group {
margin-bottom: 1rem;
}
.login-form label {
display: block;
margin-bottom: 0.5rem;
color: #333;
}
.login-form input {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.login-form button {
width: 100%;
padding: 0.75rem;
background-color: #8b4513;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.login-form button:hover {
background-color: #703610;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 0.75rem;
border-radius: 4px;
margin-bottom: 1rem;
}
/* Admin Dashboard */
.admin-nav {
background-color: #2c1810;
color: white;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-brand {
font-size: 1.25rem;
font-weight: bold;
}
.nav-menu {
display: flex;
gap: 1rem;
}
.button {
padding: 0.5rem 1rem;
background-color: #8b4513;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
}
.button:hover {
background-color: #703610;
}
.admin-main {
padding: 2rem;
}
.stories-list {
display: grid;
gap: 1rem;
margin-top: 2rem;
}
.story-item {
background: white;
padding: 1rem;
border-radius: 8px;
display: flex;
align-items: center;
gap: 1rem;
}
.story-cover {
width: 100px;
height: 150px;
object-fit: cover;
border-radius: 4px;
}
.story-info {
flex: 1;
}
.story-actions {
display: flex;
gap: 0.5rem;
}
.delete-story {
background-color: #c62828;
}
.delete-story:hover {
background-color: #b71c1c;
}

BIN
assets/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

13
config.json Normal file
View File

@ -0,0 +1,13 @@
{
"site": {
"name": "Nom du Site",
"description": "Description du site"
},
"users": [
{
"id": "admin",
"password": "$2y$10$pfYTH9z.ZvdgTJTj779X7.wL6m8S4.vSznQEiPdy6coaz.MeJkT76",
"comment": "Mot de passe par défaut « admin ». À changer dès la première connexion."
}
]
}

26
includes/auth.php Normal file
View File

@ -0,0 +1,26 @@
<?php
require_once 'config.php';
class Auth {
public static function check() {
return isset($_SESSION['user_id']);
}
public static function login($username, $password) {
$config = Config::load();
$users = $config['users'];
foreach ($users as $user) {
if ($user['id'] === $username && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
return true;
}
}
return false;
}
public static function logout() {
unset($_SESSION['user_id']);
session_destroy();
}
}

22
includes/config.php Normal file
View File

@ -0,0 +1,22 @@
<?php
session_start();
class Config {
private static $config = null;
public static function load() {
if (self::$config === null) {
$configFile = __DIR__ . '/../config.json';
if (!file_exists($configFile)) {
throw new Exception('Configuration file not found');
}
self::$config = json_decode(file_get_contents($configFile), true);
}
return self::$config;
}
public static function get($key, $default = null) {
$config = self::load();
return $config[$key] ?? $default;
}
}

26
includes/stories.php Normal file
View File

@ -0,0 +1,26 @@
<?php
class Stories {
private static $storiesDir = __DIR__ . '/../stories/';
public static function getAll() {
$stories = [];
foreach (glob(self::$storiesDir . '*.json') as $file) {
$story = json_decode(file_get_contents($file), true);
$stories[] = $story;
}
return $stories;
}
public static function get($id) {
$file = self::$storiesDir . $id . '.json';
if (!file_exists($file)) {
return null;
}
return json_decode(file_get_contents($file), true);
}
public static function save($story) {
$file = self::$storiesDir . $story['id'] . '.json';
return file_put_contents($file, json_encode($story, JSON_PRETTY_PRINT));
}
}

35
index.php Normal file
View File

@ -0,0 +1,35 @@
<?php
require_once 'includes/config.php';
require_once 'includes/stories.php';
$config = Config::load();
$stories = Stories::getAll();
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($config['site']['name']) ?></title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header>
<h1><?= htmlspecialchars($config['site']['name']) ?></h1>
<p><?= htmlspecialchars($config['site']['description']) ?></p>
</header>
<main>
<div class="stories-grid">
<?php foreach ($stories as $story): ?>
<article class="story-card">
<img src="<?= htmlspecialchars($story['cover']) ?>" alt="">
<h2><?= htmlspecialchars($story['title']) ?></h2>
<p><?= htmlspecialchars($story['description']) ?></p>
<a href="story.php?id=<?= htmlspecialchars($story['id']) ?>">Lire</a>
</article>
<?php endforeach; ?>
</div>
</main>
</body>
</html>