base de la v3

This commit is contained in:
Esenjin 2025-01-15 15:44:23 +01:00
parent 41c79b7ec3
commit b3c9c43142
50 changed files with 1761 additions and 17492 deletions

60
.htaccess Normal file
View File

@ -0,0 +1,60 @@
# Empêcher l'accès direct aux fichiers PHP
<FilesMatch "\.php$">
<IfModule !mod_authz_core.c>
Order Allow,Deny
Deny from all
</IfModule>
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
</FilesMatch>
<Files "api.php">
<IfModule !mod_authz_core.c>
Order Allow,Deny
Allow from all
</IfModule>
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</Files>
# Autoriser l'accès aux points d'entrée spécifiques
<Files ~ "^(index|admin|login|logout|share)\.php$">
<IfModule !mod_authz_core.c>
Order Allow,Deny
Allow from all
</IfModule>
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</Files>
# Définir les types MIME corrects
AddType text/css .css
AddType application/javascript .js
AddType image/x-icon .ico
AddType image/svg+xml .svg
AddType application/x-font-woff .woff
AddType application/x-font-woff2 .woff2
# Activer la compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/javascript
</IfModule>
# Cache headers
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 month"
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
</IfModule>
# Protection des dossiers
Options -Indexes

368
admin.php Normal file
View File

@ -0,0 +1,368 @@
<?php
// debug :
ob_start();
ini_set('display_errors', 1);
error_reporting(E_ALL);
define('CYLA_CORE', true);
require_once 'core.php';
// Vérifions si des headers ont déjà été envoyés
if (headers_sent($filename, $linenum)) {
exit("Headers already sent in $filename on line $linenum");
}
// Vérifier si l'utilisateur est connecté
if (!Cyla::isLoggedIn()) {
header('Location: login.php');
exit;
}
$error = $_GET['error'] ?? null;
$success = $_GET['success'] ?? null;
// Traitement du changement de mot de passe
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'change_password') {
if (!isset($_POST['csrf_token']) || !Cyla::verifyCSRFToken($_POST['csrf_token'])) {
$error = 'Token de sécurité invalide';
} else {
$current_password = $_POST['current_password'] ?? '';
$new_password = $_POST['new_password'] ?? '';
$confirm_password = $_POST['confirm_password'] ?? '';
if (empty($current_password) || empty($new_password) || empty($confirm_password)) {
$error = 'Tous les champs sont requis';
} else if ($new_password !== $confirm_password) {
$error = 'Les nouveaux mots de passe ne correspondent pas';
} else {
global $ADMIN_USERS;
$username = $_SESSION['user'];
$user = $ADMIN_USERS[$username];
// Vérifier l'ancien mot de passe
if (hash(HASH_ALGO, $current_password . $user['salt']) !== $user['password']) {
$error = 'Mot de passe actuel incorrect';
} else {
// Mettre à jour le mot de passe
$ADMIN_USERS[$username]['password'] = hash(HASH_ALGO, $new_password . $user['salt']);
// Sauvegarder dans le fichier de configuration
$config_content = file_get_contents('config.php');
$config_content = preg_replace(
'/\'password\' => \'' . preg_quote($user['password'], '/') . '\'/',
'\'password\' => \'' . $ADMIN_USERS[$username]['password'] . '\'',
$config_content
);
file_put_contents('config.php', $config_content);
$success = 'Mot de passe modifié avec succès';
}
}
}
}
// Traitement de l'upload de fichier
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upload') {
if (!isset($_POST['csrf_token']) || !Cyla::verifyCSRFToken($_POST['csrf_token'])) {
$error = 'Token de sécurité invalide';
} else if (!isset($_FILES['file'])) {
$error = 'Aucun fichier n\'a été envoyé';
} else {
$validation = Cyla::validateUpload($_FILES['file']);
if (!$validation['valid']) {
$error = $validation['error'];
} else {
$filename = Cyla::generateUniqueFilename($_FILES['file']['name']);
$destination = UPLOAD_DIR . $filename;
if (move_uploaded_file($_FILES['file']['tmp_name'], $destination)) {
$success = 'Fichier uploadé avec succès';
} else {
$error = 'Erreur lors de l\'upload du fichier';
}
}
}
}
// Liste des fichiers
$files = Cyla::listFiles();
// Contenu de la page
$pageTitle = 'Administration';
ob_start(); ?>
<div class="admin-container">
<div class="card">
<h2>Téléversement de fichier</h2>
<div class="upload-zone" id="uploadZone">
<input type="file" id="fileInput" multiple class="file-input" name="files[]">
<div class="upload-content">
<div class="upload-icon">
<svg width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="17 8 12 3 7 8" />
<line x1="12" y1="3" x2="12" y2="15" />
</svg>
</div>
<p class="upload-text">
Glissez-déposez vos fichiers ici<br>
<span class="upload-text-sub">ou cliquez pour sélectionner</span>
</p>
<p class="upload-limit">
Taille maximale : 100 Mo<br>
Extensions : <?php echo implode(', ', ALLOWED_EXTENSIONS); ?>
</p>
</div>
</div>
<div id="fileList" class="file-list"></div>
</div>
<script>
const uploadZone = document.getElementById('uploadZone');
const fileInput = document.getElementById('fileInput');
const fileList = document.getElementById('fileList');
const maxFileSize = <?php echo MAX_FILE_SIZE; ?>;
const allowedExtensions = <?php echo json_encode(ALLOWED_EXTENSIONS); ?>;
// Gestion du drag & drop
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
uploadZone.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
uploadZone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
uploadZone.addEventListener(eventName, unhighlight, false);
});
function highlight(e) {
uploadZone.classList.add('upload-zone-active');
}
function unhighlight(e) {
uploadZone.classList.remove('upload-zone-active');
}
// Gestion du drop
uploadZone.addEventListener('drop', handleDrop, false);
fileInput.addEventListener('change', handleFiles, false);
uploadZone.addEventListener('click', () => fileInput.click());
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles({ target: { files: files } });
}
function isValidFile(file) {
const ext = file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(ext)) {
return { valid: false, error: `Extension .${ext} non autorisée` };
}
if (file.size > maxFileSize) {
return { valid: false, error: 'Fichier trop volumineux (max 100 Mo)' };
}
return { valid: true };
}
function handleFiles(e) {
const files = Array.from(e.target.files);
// Vider la liste précédente
fileList.innerHTML = '';
files.forEach(file => {
const validation = isValidFile(file);
const fileElement = createFileElement(file, validation);
fileList.appendChild(fileElement);
if (validation.valid) {
uploadFile(file, fileElement);
}
});
}
function createFileElement(file, validation) {
const div = document.createElement('div');
div.className = 'file-item ' + (validation.valid ? '' : 'file-item-error');
div.innerHTML = `
<div class="file-item-content">
<div class="file-item-name">${file.name}</div>
<div class="file-item-size">${(file.size / 1024 / 1024).toFixed(2)} Mo</div>
${validation.valid ?
'<div class="file-item-progress"><div class="progress-bar"></div></div>' :
`<div class="file-item-error-message">${validation.error}</div>`
}
</div>
`;
return div;
}
async function uploadFile(file, element) {
const formData = new FormData();
formData.append('file', file);
formData.append('csrf_token', '<?php echo Cyla::generateCSRFToken(); ?>');
try {
const response = await fetch('api.php', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Erreur lors de l\'upload');
}
const result = await response.json();
if (result.success) {
element.classList.add('file-item-success');
} else {
element.classList.add('file-item-error');
}
} catch (error) {
element.classList.add('file-item-error');
console.error('Error:', error);
}
}
</script>
<div class="card">
<h2>Changer le mot de passe</h2>
<form method="POST" class="password-form">
<input type="hidden" name="csrf_token" value="<?php echo Cyla::generateCSRFToken(); ?>">
<input type="hidden" name="action" value="change_password">
<div class="form-group">
<label for="current_password">Mot de passe actuel</label>
<input type="password" id="current_password" name="current_password" required>
</div>
<div class="form-group">
<label for="new_password">Nouveau mot de passe</label>
<input type="password" id="new_password" name="new_password" required>
</div>
<div class="form-group">
<label for="confirm_password">Confirmer le nouveau mot de passe</label>
<input type="password" id="confirm_password" name="confirm_password" required>
</div>
<button type="submit" class="btn">Changer le mot de passe</button>
</form>
</div>
<div class="card">
<h2>Fichiers hébergés</h2>
<?php if (empty($files)): ?>
<p class="text-muted">Aucun fichier hébergé</p>
<?php else: ?>
<div class="file-list">
<?php foreach ($files as $file): ?>
<div class="file-item">
<div class="file-preview">
<?php if ($file['preview_type'] === 'image'): ?>
<img src="fichiers/<?php echo Cyla::escape($file['name']); ?>" alt="<?php echo Cyla::escape($file['name']); ?>">
<?php else: ?>
<div class="preview-placeholder">
<?php echo strtoupper($file['extension']); ?>
</div>
<?php endif; ?>
</div>
<div class="file-info">
<p class="file-name"><?php echo Cyla::escape($file['name']); ?></p>
<p class="file-meta">
<?php echo Cyla::escape(round($file['size'] / 1024, 2)); ?> Ko
· <?php echo date('d/m/Y H:i', $file['uploaded']); ?>
</p>
<div class="file-actions">
<a href="share.php?file=<?php echo urlencode($file['name']); ?>" class="btn btn-secondary" target="_blank">Voir</a>
<button class="btn" onclick="copyShareLink('<?php echo SITE_URL; ?>share.php?file=<?php echo urlencode($file['name']); ?>')">Copier le lien</button>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<style>
/* Styles spécifiques à la page d'administration */
.admin-container {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
}
.admin-container h2 {
color: var(--color-primary);
margin-bottom: var(--spacing-md);
}
.help-text {
font-size: 0.9rem;
color: var(--color-text-muted);
margin-top: var(--spacing-xs);
}
.preview-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-bg);
color: var(--color-text-muted);
font-size: 2rem;
font-weight: bold;
}
.file-name {
font-weight: bold;
word-break: break-all;
}
.file-meta {
font-size: 0.9rem;
color: var(--color-text-muted);
margin: var(--spacing-xs) 0;
}
.file-actions {
display: flex;
gap: var(--spacing-sm);
margin-top: var(--spacing-sm);
}
</style>
<script>
// Fonction pour copier le lien de partage
function copyShareLink(url) {
navigator.clipboard.writeText(url).then(() => {
alert('Lien copié dans le presse-papier');
}).catch(err => {
console.error('Erreur lors de la copie :', err);
alert('Erreur lors de la copie du lien');
});
}
</script>
<?php
$content = ob_get_clean();
require 'layout.php';
ob_end_flush();

50
api.php Normal file
View File

@ -0,0 +1,50 @@
<?php
define('CYLA_CORE', true);
require_once 'core.php';
// Vérifier si l'utilisateur est connecté
if (!Cyla::isLoggedIn()) {
http_response_code(401);
echo json_encode(['error' => 'Non autorisé']);
exit;
}
// Vérifier le token CSRF
if (!isset($_POST['csrf_token']) || !Cyla::verifyCSRFToken($_POST['csrf_token'])) {
http_response_code(403);
echo json_encode(['error' => 'Token CSRF invalide']);
exit;
}
// Gérer l'upload de fichier
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$validation = Cyla::validateUpload($_FILES['file']);
if (!$validation['valid']) {
http_response_code(400);
echo json_encode(['error' => $validation['error']]);
exit;
}
$filename = Cyla::generateUniqueFilename($_FILES['file']['name']);
$destination = UPLOAD_DIR . $filename;
if (move_uploaded_file($_FILES['file']['tmp_name'], $destination)) {
echo json_encode([
'success' => true,
'file' => [
'name' => $filename,
'size' => filesize($destination),
'url' => 'share.php?file=' . urlencode($filename)
]
]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Erreur lors de l\'upload du fichier']);
}
exit;
}
// Méthode non autorisée
http_response_code(405);
echo json_encode(['error' => 'Méthode non autorisée']);

76
config.php Normal file
View File

@ -0,0 +1,76 @@
<?php
// Prevent direct access to this file
if (!defined('CYLA_CORE')) {
header('HTTP/1.0 403 Forbidden');
exit('Accès direct interdit');
}
// Site configuration
define('SITE_NAME', 'Cyla');
define('SITE_VERSION', '3.0.0');
define('SITE_URL', 'https://concepts.esenjin.xyz/cyla/');
// Files configuration
define('UPLOAD_DIR', __DIR__ . '/fichiers/');
define('MAX_FILE_SIZE', 100 * 1024 * 1024); // 100 Mo en octets
define('ALLOWED_EXTENSIONS', [
'png', 'jpg', 'jpeg', 'gif', 'webm', 'mp4', 'wmv',
'mp3', 'flac', 'ogg', 'zip', 'css', 'pdf',
'zip', 'rar', 'm3u', 'm3u8', 'txt'
]);
// Preview configuration
define('PREVIEW_IMAGES', ['png', 'jpg', 'jpeg', 'gif']);
define('PREVIEW_VIDEOS', ['webm', 'mp4', 'wmv']);
define('PREVIEW_AUDIOS', ['mp3', 'flac', 'ogg']);
define('PREVIEW_TEXTS', ['txt', 'css', 'm3u', 'm3u8']);
// Security configuration
define('HASH_ALGO', 'sha256'); // Algorithme de hachage pour les mots de passe
define('SALT_LENGTH', 32); // Longueur du sel pour le hachage
// Admin users (format: 'username' => ['password' => 'hashed_password', 'salt' => 'random_salt'])
$ADMIN_USERS = [
'admin' => [
'password' => 'a94637ad7685d8a3e64c97eddd7751a0ff55434a607361b7304edf41b39ab7f8', // Default: 'password'
'salt' => 'defaultsalt123'
]
];
// Session configuration
define('SESSION_LIFETIME', 3600); // Durée de vie de la session en secondes (1 heure)
define('SESSION_NAME', 'CYLA_SESSION');
// Error reporting
define('DEBUG_MODE', false); // À mettre à false en production
if (DEBUG_MODE) {
error_reporting(E_ALL);
ini_set('display_errors', 1);
} else {
error_reporting(0);
ini_set('display_errors', 0);
}
// Fonction pour vérifier si une extension est autorisée
function isAllowedExtension($extension) {
return in_array(strtolower($extension), ALLOWED_EXTENSIONS);
}
// Fonction pour vérifier si un fichier peut avoir un aperçu
function canPreview($extension) {
$extension = strtolower($extension);
return in_array($extension, PREVIEW_IMAGES) ||
in_array($extension, PREVIEW_VIDEOS) ||
in_array($extension, PREVIEW_AUDIOS) ||
in_array($extension, PREVIEW_TEXTS);
}
// Fonction pour obtenir le type d'aperçu
function getPreviewType($extension) {
$extension = strtolower($extension);
if (in_array($extension, PREVIEW_IMAGES)) return 'image';
if (in_array($extension, PREVIEW_VIDEOS)) return 'video';
if (in_array($extension, PREVIEW_AUDIOS)) return 'audio';
if (in_array($extension, PREVIEW_TEXTS)) return 'text';
return 'none';
}

156
core.php Normal file
View File

@ -0,0 +1,156 @@
<?php
// Charger la configuration avant tout
require_once __DIR__ . '/config.php';
// Démarrer la session
session_name(SESSION_NAME);
session_start();
/**
* Classe principale de Cyla
*/
class Cyla {
/**
* Vérifie si un utilisateur est connecté
* @return bool
*/
public static function isLoggedIn() {
return isset($_SESSION['user']) && isset($_SESSION['last_activity']) &&
(time() - $_SESSION['last_activity']) < SESSION_LIFETIME;
}
/**
* Met à jour le timestamp de dernière activité
*/
public static function updateActivity() {
$_SESSION['last_activity'] = time();
}
/**
* Authentifie un utilisateur
* @param string $username Nom d'utilisateur
* @param string $password Mot de passe
* @return bool
*/
public static function authenticate($username, $password) {
global $ADMIN_USERS;
if (!isset($ADMIN_USERS[$username])) {
return false;
}
$user = $ADMIN_USERS[$username];
$hashed = hash(HASH_ALGO, $password . $user['salt']);
if ($hashed === $user['password']) {
$_SESSION['user'] = $username;
self::updateActivity();
return true;
}
return false;
}
/**
* Déconnecte l'utilisateur
*/
public static function logout() {
session_destroy();
}
/**
* Génère un nom de fichier unique
* @param string $originalName Nom original du fichier
* @return string
*/
public static function generateUniqueFilename($originalName) {
$extension = pathinfo($originalName, PATHINFO_EXTENSION);
return uniqid() . '_' . time() . '.' . $extension;
}
/**
* Vérifie si un fichier peut être uploadé
* @param array $file Informations du fichier ($_FILES)
* @return array ['valid' => bool, 'error' => string|null]
*/
public static function validateUpload($file) {
$result = ['valid' => true, 'error' => null];
// Vérifier les erreurs d'upload PHP
if ($file['error'] !== UPLOAD_ERR_OK) {
$result['valid'] = false;
$result['error'] = 'Erreur lors de l\'upload';
return $result;
}
// Vérifier la taille
if ($file['size'] > MAX_FILE_SIZE) {
$result['valid'] = false;
$result['error'] = 'Le fichier dépasse la taille maximale autorisée';
return $result;
}
// Vérifier l'extension
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!isAllowedExtension($extension)) {
$result['valid'] = false;
$result['error'] = 'Extension de fichier non autorisée';
return $result;
}
return $result;
}
/**
* Sécurise les sorties HTML
* @param string $text Texte à sécuriser
* @return string
*/
public static function escape($text) {
return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}
/**
* Génère un token CSRF
* @return string
*/
public static function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/**
* Vérifie un token CSRF
* @param string $token Token à vérifier
* @return bool
*/
public static function verifyCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
/**
* Liste les fichiers uploadés
* @return array
*/
public static function listFiles() {
$files = [];
foreach (glob(UPLOAD_DIR . '*') as $file) {
$info = pathinfo($file);
$files[] = [
'name' => basename($file),
'size' => filesize($file),
'extension' => strtolower($info['extension']),
'uploaded' => filemtime($file),
'preview_type' => getPreviewType($info['extension'])
];
}
return $files;
}
}
// Vérifier et mettre à jour l'activité si l'utilisateur est connecté
if (Cyla::isLoggedIn()) {
Cyla::updateActivity();
}

345
css/style.css Normal file
View File

@ -0,0 +1,345 @@
/* Variables */
:root {
/* Couleurs principales */
--color-bg: #1a1716;
--color-bg-alt: #231f1d;
--color-text: #e6d5c5;
--color-text-muted: #a18d7a;
--color-primary: #d4846a;
--color-primary-hover: #e6977c;
--color-accent: #8b4b45;
--color-border: #382f2a;
/* Alertes */
--color-error: #ff6b6b;
--color-success: #51cf66;
/* Espacements */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 2rem;
/* Bordures */
--border-radius: 4px;
}
/* Reset et base */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: var(--color-bg);
color: var(--color-text);
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Navigation */
header {
background-color: var(--color-bg-alt);
border-bottom: 1px solid var(--color-border);
padding: var(--spacing-md) var(--spacing-lg);
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
.nav-brand a {
color: var(--color-primary);
font-size: 1.5rem;
font-weight: bold;
text-decoration: none;
}
.nav-menu {
display: flex;
gap: var(--spacing-md);
}
.nav-link {
color: var(--color-text);
text-decoration: none;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
transition: background-color 0.3s ease;
}
.nav-link:hover {
background-color: var(--color-border);
}
/* Main content */
main {
flex: 1;
padding: var(--spacing-lg);
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
/* Cards */
.card {
background-color: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
}
/* Forms */
.form-group {
margin-bottom: var(--spacing-md);
}
label {
display: block;
margin-bottom: var(--spacing-xs);
color: var(--color-text-muted);
}
input[type="text"],
input[type="password"],
textarea {
width: 100%;
padding: var(--spacing-sm);
background-color: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
color: var(--color-text);
}
input[type="text"]:focus,
input[type="password"]:focus,
textarea:focus {
outline: none;
border-color: var(--color-primary);
}
/* Buttons */
.btn {
display: inline-block;
padding: var(--spacing-sm) var(--spacing-md);
background-color: var(--color-primary);
color: var(--color-text);
border: none;
border-radius: var(--border-radius);
cursor: pointer;
text-decoration: none;
transition: background-color 0.3s ease;
}
.btn:hover {
background-color: var(--color-primary-hover);
}
.btn-secondary {
background-color: var(--color-border);
}
.btn-secondary:hover {
background-color: var(--color-text-muted);
}
/* Alerts */
.alert {
padding: var(--spacing-md);
border-radius: var(--border-radius);
margin-bottom: var(--spacing-md);
}
.alert-error {
background-color: var(--color-error);
color: white;
}
.alert-success {
background-color: var(--color-success);
color: white;
}
/* File list */
.file-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: var(--spacing-md);
}
.file-item {
background-color: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: var(--spacing-md);
}
.file-preview {
aspect-ratio: 16/9;
background-color: var(--color-bg);
border-radius: var(--border-radius);
margin-bottom: var(--spacing-sm);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.file-preview img,
.file-preview video {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.file-info {
color: var(--color-text-muted);
font-size: 0.9rem;
}
/* Footer */
footer {
background-color: var(--color-bg-alt);
border-top: 1px solid var(--color-border);
padding: var(--spacing-lg);
text-align: center;
color: var(--color-text-muted);
}
/* Zone d'upload */
.upload-zone {
position: relative;
border: 2px dashed var(--color-border);
border-radius: var(--border-radius);
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.upload-zone:hover {
border-color: var(--color-primary);
}
.upload-zone-active {
border-color: var(--color-primary);
background-color: rgba(212, 132, 106, 0.1);
}
.file-input {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
cursor: pointer;
}
.upload-content {
pointer-events: none;
}
.upload-icon {
margin-bottom: 1rem;
}
.upload-icon svg {
stroke: var(--color-text-muted);
margin: 0 auto;
}
.upload-text {
margin-bottom: 0.5rem;
color: var(--color-text);
}
.upload-text-sub {
font-size: 0.9em;
color: var(--color-text-muted);
}
.upload-limit {
font-size: 0.8em;
color: var(--color-text-muted);
}
/* Liste des fichiers */
.file-list {
margin-top: 1rem;
}
.file-item {
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 0.5rem;
}
.file-item-content {
display: grid;
gap: 0.5rem;
}
.file-item-name {
font-weight: 500;
word-break: break-all;
}
.file-item-size {
color: var(--color-text-muted);
font-size: 0.9em;
}
.file-item-progress {
height: 4px;
background: var(--color-border);
border-radius: 2px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: var(--color-primary);
width: 0;
transition: width 0.3s ease;
}
.file-item-error {
border-color: var(--color-error);
}
.file-item-error .file-item-error-message {
color: var(--color-error);
font-size: 0.9em;
}
.file-item-success {
border-color: var(--color-success);
}
/* Responsive */
@media (max-width: 768px) {
.nav-menu {
display: none;
}
main {
padding: var(--spacing-md);
}
.file-list {
grid-template-columns: 1fr;
}
}

View File

@ -1,32 +0,0 @@
<?php
// EDIT THIS: your auth parameters
$signature = 'a2eefde33d';
// EDIT THIS: the query parameters
$url = '<?php echo $filelocation;?>'; // URL to shrink
$format = 'simple'; // output format: 'json', 'xml' or 'simple'
// EDIT THIS: the URL of the API file
$api_url = 'http://ersatz.xyz/yourls-api.php';
// Init the CURL session
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $api_url );
curl_setopt( $ch, CURLOPT_HEADER, 0 ); // No header in the result
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); // Return, do not echo result
curl_setopt( $ch, CURLOPT_POST, 1 ); // This is a POST request
curl_setopt( $ch, CURLOPT_POSTFIELDS, array( // Data to POST
'url' => $url,
'format' => $format,
'action' => 'shorturl',
'signature' => $signature,
) );
// Fetch and return content
$data = curl_exec($ch);
curl_close($ch);
// Do something with the result. Here, we just echo it.
echo $data;

View File

@ -1 +0,0 @@
Les fichiers seront stockés dans ce dossier.

View File

@ -1,311 +0,0 @@
<?php
// Configuration
define('PASSWORD', 'votre_mot_de_passe');
define('FILES_DIR', './file');
define('FILES_PER_PAGE', 50);
session_start();
$authenticated = isset($_SESSION['authenticated']) && $_SESSION['authenticated'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['password']) && $_POST['password'] === PASSWORD) {
$_SESSION['authenticated'] = true;
$authenticated = true;
} elseif (isset($_POST['page'])) {
// API pour le chargement progressif
$page = intval($_POST['page']);
$sortBy = $_POST['sortBy'] ?? 'date';
$sortDesc = $_POST['sortDesc'] ?? 'true';
$files = getFiles($page, $sortBy, $sortDesc === 'true');
header('Content-Type: application/json');
echo json_encode($files);
exit;
}
}
function getFiles($page = 0, $sortBy = 'date', $sortDesc = true) {
$allFiles = array_diff(scandir(FILES_DIR), ['.', '..']);
$files = [];
foreach ($allFiles as $file) {
$path = FILES_DIR . '/' . $file;
if (is_file($path)) {
$files[] = [
'name' => $file,
'url' => 'file/' . rawurlencode($file),
'shareUrl' => 'share/' . rawurlencode($file),
'date' => filemtime($path),
'type' => strtolower(pathinfo($file, PATHINFO_EXTENSION))
];
}
}
// Tri
usort($files, function($a, $b) use ($sortBy, $sortDesc) {
$result = $sortBy === 'date'
? $b['date'] - $a['date']
: strcasecmp($a['name'], $b['name']);
return $sortDesc ? $result : -$result;
});
// Pagination
$offset = $page * FILES_PER_PAGE;
return array_slice($files, $offset, FILES_PER_PAGE);
}
if (!$authenticated) {
// Afficher le formulaire de connexion
include 'header.php';
?>
<form class="login-form" method="POST">
<input type="password" name="password" placeholder="Mot de passe">
<button type="submit">Accéder</button>
</form>
<?php
include 'footer.php';
exit;
}
$initialFiles = getFiles();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Galerie de fichiers</title>
<style>
:root {
--bg-dark: #1a1a1a;
--bg-card: #2d2d2d;
--color-beige: #d4c4a8;
--color-beige-dark: #8b7355;
}
body {
background: var(--bg-dark);
color: var(--color-beige);
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
.controls {
position: sticky;
top: 0;
background: var(--bg-dark);
padding: 10px 0;
margin-bottom: 20px;
z-index: 100;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.file-card {
background: var(--bg-card);
border-radius: 8px;
overflow: hidden;
}
.preview {
height: 150px;
position: relative;
background: #000;
}
.preview img, .preview video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
}
.preview audio {
position: absolute;
bottom: 10px;
left: 5%;
width: 90%;
}
.info {
padding: 10px;
}
.date {
font-size: 0.8em;
color: var(--color-beige-dark);
}
#loading {
text-align: center;
padding: 20px;
display: none;
}
.file-icon {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 10px;
text-align: center;
word-break: break-word;
font-size: 1.2em;
}
.info .name {
color: var(--color-beige);
text-decoration: none;
word-break: break-all;
display: block;
padding: 5px 0;
}
.info .name:hover {
color: white;
text-decoration: underline;
}
</style>
</head>
<body>
<div class="controls">
<button onclick="changeSortBy('date')" id="sortDate">
Date <?= $sortBy === 'date' ? ($sortDesc ? '↓' : '↑') : '' ?>
</button>
<button onclick="changeSortBy('name')" id="sortName">
Nom <?= $sortBy === 'name' ? ($sortDesc ? '↓' : '↑') : '' ?>
</button>
</div>
<div class="grid" id="fileGrid">
</div>
<div id="loading">Chargement...</div>
<script>
let page = 0;
let loading = false;
let sortBy = 'date';
let sortDesc = true;
let noMoreFiles = false;
function createFileCard(file) {
const card = document.createElement('div');
card.className = 'file-card';
const preview = document.createElement('div');
preview.className = 'preview';
const ext = file.type.toLowerCase();
let previewContent = '';
if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) {
previewContent = `<img src="${file.url}" loading="lazy" alt="${file.name}">`;
} else if (['mp3', 'flac', 'ogg'].includes(ext)) {
previewContent = `<audio controls src="${file.url}"></audio>`;
} else if (['mp4', 'webm', 'wmv'].includes(ext)) {
previewContent = `<video controls loading="lazy"><source src="${file.url}" type="video/${ext}"></video>`;
} else {
const icon = getFileIcon(ext);
previewContent = `<div class="file-icon">${icon} ${file.name}</div>`;
}
card.innerHTML = `
<div class="preview">${previewContent}</div>
<div class="info">
<a href="${file.shareUrl}" target="_blank" class="name" title="Ouvrir dans un nouvel onglet">${file.name}</a>
<div class="date">${new Date(file.date * 1000).toLocaleString()}</div>
</div>
`;
return card;
}
function getFileIcon(ext) {
const icons = {
'pdf': '📄',
'zip': '📦',
'rar': '📦',
'txt': '📝',
'm3u': '🎵',
'm3u8': '🎵',
'css': '📰'
};
return icons[ext] || '📎';
}
async function loadFiles() {
if (loading || noMoreFiles) return;
loading = true;
document.getElementById('loading').style.display = 'block';
const formData = new FormData();
formData.append('page', page);
formData.append('sortBy', sortBy);
formData.append('sortDesc', sortDesc);
try {
const response = await fetch('', {
method: 'POST',
body: formData
});
const files = await response.json();
if (files.length < <?= FILES_PER_PAGE ?>) {
noMoreFiles = true;
}
const grid = document.getElementById('fileGrid');
files.forEach(file => {
grid.appendChild(createFileCard(file));
});
page++;
} catch (error) {
console.error('Erreur de chargement:', error);
} finally {
loading = false;
document.getElementById('loading').style.display = 'none';
}
}
function changeSortBy(newSortBy) {
if (newSortBy === sortBy) {
sortDesc = !sortDesc;
} else {
sortBy = newSortBy;
sortDesc = true;
}
// Réinitialiser
page = 0;
noMoreFiles = false;
document.getElementById('fileGrid').innerHTML = '';
loadFiles();
// Mettre à jour les boutons
document.getElementById('sortDate').textContent =
`Date ${sortBy === 'date' ? (sortDesc ? '↓' : '↑') : ''}`;
document.getElementById('sortName').textContent =
`Nom ${sortBy === 'name' ? (sortDesc ? '↓' : '↑') : ''}`;
}
// Détecter le scroll pour charger plus de fichiers
window.addEventListener('scroll', () => {
if ((window.innerHeight + window.scrollY) >= document.documentElement.scrollHeight - 500) {
loadFiles();
}
});
// Charger les premiers fichiers
loadFiles();
</script>
</body>
</html>

View File

@ -1,57 +0,0 @@
<html>
<head>
<title>Cyla - hébergement de fichiers</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="static/CSS/uploadpage.css" media="screen" title="no title" charset="utf-8">
</head>
<body>
<p class="dltext title">
Cyla
</p>
<p class="dltext subtle">
Un fichier ne peut dépasser 100 Mo. Les extensions autorisées sont 'png', 'jpg', 'jpeg', 'gif', 'webm', 'mp4', 'wmv', 'mp3', 'flac', 'ogg', 'zip', 'css', 'pdf', 'zip', 'rar', 'm3u', 'm3u8', 'txt'.
</p>
<!-- File upload form -->
<form class="uploadinterface" id="uploadform" action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" class="fileselector">
<input type="submit" name="uploadbutton" value="héberger" class="submitbutton">
</form>
<p class="dltext subtle">
En utilisant ce service, vous acceptez les limites situées à la page <a href="md/info.html">info</a>.
</p>
<div class="sidebar">
<div class="separator"><p class="buttontxt">Liens utiles</p></div>
<a href="md/info.html">
<div class="button orangeBG"><p class="buttontxt">Info</p></div>
</a>
<a href="md/faq.html">
<div class="button orangeBG"><p class="buttontxt">F.A.Q</p></div>
</a>
<a href="http://paypal.me/esenjin" target="blank">
<div class="button orangeBG"><p class="buttontxt">Don</p></div>
</a>
<a href="gallery.php">
<div class="button orangeBG"><p class="buttontxt">Admin</p></div>
</a>
</div>
</body>
</html>

207
js/UploadZone.jsx Normal file
View File

@ -0,0 +1,207 @@
import React, { useState, useRef } from 'react';
import { X, Upload, CheckCircle, AlertCircle } from 'lucide-react';
const UploadZone = () => {
const [isDragging, setIsDragging] = useState(false);
const [files, setFiles] = useState([]);
const fileInputRef = useRef(null);
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 Mo
const ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif', 'webm', 'mp4', 'wmv', 'mp3', 'flac', 'ogg', 'zip', 'css', 'pdf', 'rar', 'm3u', 'm3u8', 'txt'];
const handleDragOver = (e) => {
e.preventDefault();
setIsDragging(true);
};
const handleDragLeave = (e) => {
e.preventDefault();
setIsDragging(false);
};
const validateFile = (file) => {
const extension = file.name.split('.').pop().toLowerCase();
if (!ALLOWED_EXTENSIONS.includes(ext