This commit is contained in:
2026-03-01 19:29:21 +01:00
parent 6aea41d765
commit 6e2dad9a82
208 changed files with 36579 additions and 2101 deletions
+97
View File
@@ -0,0 +1,97 @@
<?php
require_once __DIR__ . '/config.php';
if (session_status() === PHP_SESSION_NONE) { session_start(); }
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit('Méthode non autorisée');
}
// CSRF
if (empty($_POST['csrf_token']) || $_POST['csrf_token'] !== ($_SESSION['csrf_token'] ?? '')) {
http_response_code(400);
exit('Token CSRF invalide.');
}
// Vérif login
if (empty($_SESSION['user_id'])) {
http_response_code(403);
exit('Tu dois être connecté pour acheter.');
}
$user_id = (int)$_SESSION['user_id'];
$item_id = isset($_POST['item_id']) ? (int)$_POST['item_id'] : 0;
$pdo = pdo_connect();
try {
$pdo->beginTransaction();
// Vérifie si le user possède déjà un item
$stmt = $pdo->prepare("SELECT ui.id, i.name
FROM user_items ui
JOIN items i ON ui.item_id = i.id
WHERE ui.user_id = :uid
LIMIT 1 FOR UPDATE");
$stmt->execute([':uid' => $user_id]);
$existing = $stmt->fetch();
if ($existing) {
$pdo->rollBack();
exit("Tu possèdes déjà un item actif : " . htmlspecialchars($existing['name']));
}
// Récupère item
$stmt = $pdo->prepare("SELECT id, name, price FROM items WHERE id = :id FOR UPDATE");
$stmt->execute([':id' => $item_id]);
$item = $stmt->fetch();
if (!$item) {
$pdo->rollBack();
exit('Item introuvable.');
}
$total = (int)$item['price'];
// Vérifie aura
$stmt = $pdo->prepare("SELECT aura FROM users WHERE id = :uid FOR UPDATE");
$stmt->execute([':uid' => $user_id]);
$u = $stmt->fetch();
if (!$u) {
$pdo->rollBack();
exit('Utilisateur introuvable.');
}
$aura = (int)$u['aura'];
if ($aura < $total) {
$pdo->rollBack();
exit('Tu n\'as pas assez d\'aura pour cet achat.');
}
// Débite l'aura
$stmt = $pdo->prepare("UPDATE users SET aura = aura - :amt WHERE id = :uid");
$stmt->execute([':amt' => $total, ':uid' => $user_id]);
// Ajoute item à user_items
$stmt = $pdo->prepare("INSERT INTO user_items (user_id, item_id) VALUES (:uid, :iid)");
$stmt->execute([':uid' => $user_id, ':iid' => $item_id]);
// Log (version simplifiée, sans actor_discord_id ni type)
$stmt = $pdo->prepare("INSERT INTO logs (user_id, amount, reason)
VALUES (:uid, :amount, :reason)");
$stmt->execute([
':uid' => $user_id,
':amount' => -$total,
':reason' => 'Achat: ' . $item['name']
]);
$pdo->commit();
header('Location: shop.php?buy=ok');
exit;
} catch (Exception $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
echo "Erreur détaillée : " . $e->getMessage();
var_dump($item_id, $user_id, $total, $aura);
exit;
}
+120
View File
@@ -0,0 +1,120 @@
<?php
require_once __DIR__ . '/config.php';
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (!isset($_GET['code']) || !isset($_GET['state'])) {
http_response_code(400);
exit("Paramètres manquants.");
}
// CSRF : vérifier state
if (!isset($_SESSION['oauth2_state']) || $_GET['state'] !== $_SESSION['oauth2_state']) {
unset($_SESSION['oauth2_state']);
http_response_code(400);
exit("Échec de la vérification de sécurité (state).");
}
unset($_SESSION['oauth2_state']);
$code = $_GET['code'];
// Étape 1 : échange du code contre un access_token
$token_url = "https://discord.com/api/oauth2/token";
$post_fields = [
'client_id' => DISCORD_CLIENT_ID,
'client_secret' => DISCORD_CLIENT_SECRET,
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => DISCORD_REDIRECT_URI,
'scope' => 'identify email'
];
$ch = curl_init($token_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/x-www-form-urlencoded'
]);
// ⚠️ remet le SSL, mieux pour la prod
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$response = curl_exec($ch);
if ($response === false) {
exit("Erreur cURL token: " . curl_error($ch));
}
curl_close($ch);
$token_data = json_decode($response, true);
if (!isset($token_data['access_token'])) {
exit("Échec de l'échange de token: " . htmlspecialchars($response));
}
$access_token = $token_data['access_token'];
// Étape 2 : récupérer infos utilisateur
$ch = curl_init("https://discord.com/api/users/@me");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer $access_token"
]);
$user_json = curl_exec($ch);
if ($user_json === false) {
exit("Erreur cURL user: " . curl_error($ch));
}
curl_close($ch);
$user_data = json_decode($user_json, true);
if (!isset($user_data['id'])) {
exit("Impossible de récupérer l'utilisateur Discord. Réponse: " . htmlspecialchars($user_json));
}
// Préparation des données
$discord_id = $user_data['id'];
$username = $user_data['username'] . (isset($user_data['discriminator']) && $user_data['discriminator'] !== "0" ? '#' . $user_data['discriminator'] : "");
$email = $user_data['email'] ?? null;
$avatar = !empty($user_data['avatar'])
? "https://cdn.discordapp.com/avatars/{$discord_id}/{$user_data['avatar']}.png"
: null;
// Étape 3 : DB
try {
$pdo = pdo_connect();
} catch (Exception $e) {
exit("Erreur DB : " . $e->getMessage());
}
$stmt = $pdo->prepare("SELECT id FROM users WHERE discord_id = :did LIMIT 1");
$stmt->execute([':did' => $discord_id]);
$u = $stmt->fetch();
if ($u) {
$stmt = $pdo->prepare("UPDATE users SET username = :username, email = :email, profile_picture = :avatar WHERE discord_id = :did");
$stmt->execute([
':username' => $username,
':email' => $email,
':avatar' => $avatar,
':did' => $discord_id
]);
$user_id = $u['id'];
} else {
$stmt = $pdo->prepare("INSERT INTO users (discord_id, username, email, profile_picture, aura, tier)
VALUES (:did, :username, :email, :avatar, 0, 'Aura')");
$stmt->execute([
':did' => $discord_id,
':username' => $username,
':email' => $email,
':avatar' => $avatar
]);
$user_id = $pdo->lastInsertId();
}
// Étape 4 : session
$_SESSION['user_id'] = $user_id;
$_SESSION['discord_id'] = $discord_id;
$_SESSION['username'] = $username;
$_SESSION['profile_picture'] = $avatar;
// Étape 5 : redirection
header("Location: index.php");
exit;
+25
View File
@@ -0,0 +1,25 @@
<?php
// config.php
session_start();
define('DB_HOST', '127.0.0.1');
define('DB_PORT', '3306'); // ou 8035 si tu as changé
define('DB_NAME', 'banque_aura');
define('DB_USER', 'root');
define('DB_PASS', 'root');
define('DISCORD_CLIENT_ID', '1318190631914307645');
define('DISCORD_CLIENT_SECRET', 'zRXVwXeAYSSL1YbSwPkNVgrgBJN6-i1_');
define('DISCORD_REDIRECT_URI', 'http://128.78.3.237:8082/aura_bank/callback.php'); // adapter
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
];
function pdo_connect(){
global $options;
$dsn = "mysql:host=".DB_HOST.";port=".DB_PORT.";dbname=".DB_NAME.";charset=utf8mb4";
return new PDO($dsn, DB_USER, DB_PASS, $options);
}
+26
View File
@@ -0,0 +1,26 @@
<?php
// functions.php
require_once __DIR__ . '/config.php';
function get_top_users($limit = 10) {
$pdo = pdo_connect();
$stmt = $pdo->prepare("SELECT username, aura, discord_id, tier, profile_picture FROM users ORDER BY aura DESC LIMIT :lim");
$stmt->bindValue(':lim', (int)$limit, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll();
}
function get_user_by_discord($discord_id) {
$pdo = pdo_connect();
$stmt = $pdo->prepare("SELECT * FROM users WHERE discord_id = :did LIMIT 1");
$stmt->execute([':did' => $discord_id]);
return $stmt->fetch();
}
function get_user_rank($aura) {
$pdo = pdo_connect();
$stmt = $pdo->prepare("SELECT COUNT(*) + 1 AS 'rank' FROM users WHERE aura > :aura");
$stmt->execute([':aura' => $aura]);
$r = $stmt->fetch();
return $r ? (int)$r['rank'] : null;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

+78
View File
@@ -0,0 +1,78 @@
<?php
require_once __DIR__ . '/functions.php';
$top = get_top_users(10);
require_once __DIR__ . '/config.php';
$logged = isset($_SESSION['discord_id']);
?>
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Banque de l'Aura — Classement</title>
<style>
body{font-family:Arial,Helvetica,sans-serif;background:#f4f4f9;color:#222;padding:20px}
.card{max-width:800px;margin:0 auto;background:#fff;padding:18px;border-radius:8px;box-shadow:0 6px 18px rgba(0,0,0,.06)}
h1{margin:0 0 12px}
table{width:100%;border-collapse:collapse}
th,td{padding:10px;border-bottom:1px solid #eee;text-align:left;vertical-align:middle}
th{background:#fbfbfc}
.pos-1::before{content:"🥇 ";}
.pos-2::before{content:"🥈 ";}
.pos-3::before{content:"🥉 ";}
.avatar{width:32px;height:32px;border-radius:50%;object-fit:cover;vertical-align:middle;margin-right:8px}
.user-link{display:flex;align-items:center;text-decoration:none;color:#222}
.user-link:hover{text-decoration:underline}
.user-id{font-size:12px;color:#666;margin-left:40px;margin-top:-4px}
.welcome{display:flex;align-items:center;gap:12px;margin-bottom:18px;font-size:16px}
.welcome img{width:40px;height:40px;border-radius:50%;object-fit:cover}
</style>
</head>
<body>
<div class="card">
<?php if ($logged): ?>
<div class="welcome" style="display:flex;align-items:center;gap:12px;margin-bottom:16px;">
<?php if(!empty($_SESSION['profile_picture'])): ?>
<a href="profile.php?discord_id=<?= urlencode($_SESSION['discord_id']) ?>">
<img src="<?= htmlspecialchars($_SESSION['profile_picture']) ?>" alt="Avatar" style="width:40px;height:40px;border-radius:50%;object-fit:cover;">
</a>
<?php endif; ?>
<span style="font-size:16px;">
Bienvenue,
<a href="profile.php?discord_id=<?= urlencode($_SESSION['discord_id']) ?>" style="text-decoration:none;color:#222;">
<strong><?= htmlspecialchars($_SESSION['username']) ?></strong>
</a>
</span>
</div>
<?php else: ?>
<a href="login.php">Se connecter avec Discord</a>
<?php endif; ?>
<h1>🏆 Classement des meilleures auras</h1>
<?php if(empty($top)): ?>
<p>Le classement est vide.</p>
<?php else: ?>
<table>
<thead><tr><th>#</th><th>Utilisateur</th><th>Aura</th><th>Grade</th></tr></thead>
<tbody>
<?php foreach($top as $i => $row): ?>
<?php $pos = $i + 1; $cls = $pos<=3 ? "pos-{$pos}" : ""; ?>
<tr>
<td class="<?= htmlspecialchars($cls) ?>"><?= $pos ?></td>
<td>
<?php if(!empty($row['profile_picture'])): ?>
<img src="<?= htmlspecialchars($row['profile_picture']) ?>" alt="" class="avatar">
<?php endif; ?>
<strong><?= htmlspecialchars($row['username']) ?></strong>
<div class="user-id">ID: <?= htmlspecialchars($row['discord_id']) ?></div>
</td>
<td><?= (int)$row['aura'] ?></td>
<td><?= htmlspecialchars($row['tier']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
<?php
require_once __DIR__ . '/config.php';
// scopes : identify pour ID + username, email si tu veux email
$scope = 'identify email';
// génère un state CSRF
$state = bin2hex(random_bytes(16));
$_SESSION['oauth2_state'] = $state;
$params = http_build_query([
'client_id' => DISCORD_CLIENT_ID,
'redirect_uri' => DISCORD_REDIRECT_URI,
'response_type' => 'code',
'scope' => $scope,
'state' => $state,
'prompt' => 'consent'
]);
$discord_authorize_url = "https://discord.com/oauth2/authorize?$params";
?>
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Login Discord</title></head>
<body>
<a href="<?= htmlspecialchars($discord_authorize_url) ?>">
<img src="https://raw.githubusercontent.com/DiscordAssets/discord-open-graph/main/discord_logo.png" alt="Discord" style="height:28px;vertical-align:middle"> Se connecter avec Discord
</a>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
<?php
require_once __DIR__ . '/config.php';
session_destroy();
header('Location: index.php');
exit;
+102
View File
@@ -0,0 +1,102 @@
<?php
require_once __DIR__ . '/functions.php';
$discord_id = isset($_GET['discord_id']) ? trim($_GET['discord_id']) : null;
if (!$discord_id) {
http_response_code(400);
echo "Paramètre discord_id manquant.";
exit;
}
$user = get_user_by_discord($discord_id);
if (!$user) {
http_response_code(404);
echo "Utilisateur introuvable.";
exit;
}
$rank = get_user_rank($user['aura']);
?>
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Profil de <?= htmlspecialchars($user['username']) ?></title>
<style>
body{font-family:Arial,Helvetica,sans-serif;background:#f4f4f9;color:#222;padding:20px}
.card{max-width:700px;margin:0 auto;background:#fff;padding:18px;border-radius:8px;box-shadow:0 6px 18px rgba(0,0,0,.06)}
.avatar{width:96px;height:96px;border-radius:50%;object-fit:cover}
.header{display:flex;align-items:center;gap:16px;justify-content:space-between}
.user-info{flex:1}
.btn-group{display:flex;gap:8px;margin-top:4px}
.btn{padding:6px 12px;border:none;border-radius:4px;background:#4CAF50;color:white;text-decoration:none;cursor:pointer;}
.btn.logout{background:#f44336;}
table{width:100%;border-collapse:collapse;margin-top:8px;}
th, td{padding:8px;border-bottom:1px solid #ccc;text-align:left;}
</style>
</head>
<body>
<div class="card">
<div class="header">
<div style="display:flex;align-items:center;gap:16px">
<?php if(!empty($user['profile_picture'])): ?>
<img src="<?= htmlspecialchars($user['profile_picture']) ?>" alt="" class="avatar">
<?php endif; ?>
<div class="user-info">
<h2><?= htmlspecialchars($user['username']) ?></h2>
<div>ID Discord: <?= htmlspecialchars($user['discord_id']) ?></div>
<div style="margin-top:8px;"><strong>Aura:</strong> <?= (int)$user['aura'] ?> — <strong>Grade:</strong> <?= htmlspecialchars($user['tier']) ?></div>
<div style="margin-top:6px;"><strong>Rang:</strong> #<?= (int)$rank ?></div>
</div>
</div>
<div class="btn-group">
<a href="index.php" class="btn">Accueil</a>
<a href="shop.php" class="btn">Boutique</a>
<a href="logout.php" class="btn logout">Se déconnecter</a>
</div>
</div>
<hr style="margin:14px 0">
<h3>Historique récent</h3>
<?php
$pdo = pdo_connect();
$stmt = $pdo->prepare("
SELECT amount, reason, created_at
FROM logs
WHERE user_id = :uid OR user_id = :discord_id
ORDER BY created_at DESC
");
$stmt->execute([
':uid' => $user['id'],
':discord_id' => $user['discord_id']
]);
$logs = $stmt->fetchAll();
if (!$logs): ?>
<p>Aucun historique.</p>
<?php else: ?>
<table style="width:100%;border-collapse:collapse;margin-top:8px;">
<thead>
<tr style="background:#f0f0f0;text-align:left;">
<th style="padding:8px;border-bottom:1px solid #ccc;">Date</th>
<th style="padding:8px;border-bottom:1px solid #ccc;">Montant</th>
<th style="padding:8px;border-bottom:1px solid #ccc;">Raison</th>
</tr>
</thead>
<tbody>
<?php foreach($logs as $l):
$amount = (int)$l['amount'];
$bg_color = $amount >= 0 ? '#d4f7d4' : '#f7d4d4'; // vert pale si gain, rouge pale si perte
?>
<tr style="background:<?= $bg_color ?>;border-bottom:1px solid #eee;">
<td style="padding:10px;"><?= htmlspecialchars($l['created_at']) ?></td>
<td style="padding:10px;"><?= $amount ?></td>
<td style="padding:10px;"><?= htmlspecialchars($l['reason']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</body>
</html>
+143
View File
@@ -0,0 +1,143 @@
<?php
require_once __DIR__ . '/config.php'; // contient pdo_connect() et session start
require_once __DIR__ . '/functions.php'; // si nécessaire
// Vérifie session
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$pdo = pdo_connect();
// Récupère les items
$stmt = $pdo->query("SELECT id, name, description, price FROM items ORDER BY price ASC");
$items = $stmt->fetchAll();
// Génère token CSRF simple
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(16));
}
// Récupère saldo de l'utilisateur si connecté
$logged = isset($_SESSION['user_id']);
$userAura = null;
if ($logged) {
$stmt = $pdo->prepare("SELECT aura FROM users WHERE id = :uid LIMIT 1");
$stmt->execute([':uid' => $_SESSION['user_id']]);
$row = $stmt->fetch();
$userAura = $row ? (int)$row['aura'] : 0;
}
?>
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Boutique — Banque de l'Aura</title>
<style>
body{font-family:Arial,Helvetica,sans-serif;background:#f4f4f9;color:#222;padding:20px}
.card{max-width:1000px;margin:0 auto;background:#fff;padding:18px;border-radius:8px;box-shadow:0 6px 18px rgba(0,0,0,.06)}
h1{margin:0 0 12px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}
.item{border:1px solid #eee;padding:12px;border-radius:8px;background:#fafafa;display:flex;flex-direction:column;gap:8px}
.badge{width:32px;height:32px;object-fit:cover}
.item-icon{width:32px;height:32px;object-fit:cover;vertical-align:middle;margin-right:8px}
.price{font-weight:700}
.stock{font-size:13px;color:#666}
.buy-form{margin-top:auto}
.btn{display:inline-block;padding:8px 12px;border-radius:6px;background:#2d8aef;color:#fff;text-decoration:none;border:none;cursor:pointer}
.btn.disabled{opacity:.5;cursor:not-allowed;background:#9bbbed}
.row-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
padding: 10px 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}
.row-top .user-info {
display: flex;
align-items: center;
gap: 10px;
}
.row-top img {
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
}
.balance{font-weight:700}
</style>
</head>
<body>
<div class="card">
<div class="row-top">
<h1>🛒 Boutique</h1>
<?php if ($logged): ?>
<div class="user-info">
<div class="balance">
💎 Solde : <strong><?= htmlspecialchars($userAura ?? 0) ?> aura</strong>
</div>
<div class="active_item">
<?php
$stmt = $pdo->prepare("SELECT i.name
FROM user_items ui
JOIN items i ON ui.item_id = i.id
WHERE ui.user_id = :uid
LIMIT 1");
$stmt->execute([':uid' => $_SESSION['user_id']]);
$activeItem = $stmt->fetch();
?>
<?php if ($activeItem): ?>
🎖️ Item actif : <strong><?= htmlspecialchars($activeItem['name']) ?></strong>
<?php else: ?>
🎖️ Aucun item actif
<?php endif; ?>
</div>
<div class="profile-link">
<a href="profile.php?discord_id=<?= urlencode($_SESSION['discord_id']) ?>">Voir mon profil</a>
</div>
</div>
<?php else: ?>
<div class="login-link">
<a href="login.php">Se connecter avec Discord pour acheter</a>
</div>
<?php endif; ?>
</div>
<div class="grid">
<?php foreach($items as $it): ?>
<div class="item">
<div>
<span class="badge"><img class="item-icon" src="img/items/<?= htmlspecialchars($it['id']) ?>.png"></span>
<strong><?= htmlspecialchars($it['name']) ?></strong>
</div>
<?php if(!empty($it['description'])): ?>
<div style="font-size:14px;color:#333"><?= htmlspecialchars($it['description']) ?></div>
<?php endif; ?>
<div class="price"><?= (int)$it['price'] ?> aura</div>
<form class="buy-form" method="post" action="buy.php">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<input type="hidden" name="item_id" value="<?= (int)$it['id'] ?>">
<?php
$canBuy = $logged && ($userAura !== null && $userAura >= (int)$it['price']);
?>
<button class="btn <?= $canBuy ? '' : 'disabled' ?>" <?= $canBuy ? '' : 'disabled' ?>>
Acheter
</button>
</form>
</div>
<?php endforeach; ?>
</div>
</div>
</body>
</html>