[FEAT] Full function and deployed version
This commit is contained in:
3
api/.env
3
api/.env
@@ -1 +1,2 @@
|
||||
JWT_SECRET=ohmj_test_secret_key_change_in_production_12345
|
||||
JWT_SECRET=6jh/MWqVplwXQKsiwlKahE19TSavfR1dNCawsQFixus=
|
||||
SCORES_PATH=/data/scores/
|
||||
|
||||
@@ -70,7 +70,8 @@ GET /scores
|
||||
{
|
||||
"id": "102",
|
||||
"name": "A Legend from Yao",
|
||||
"compositor": "Yeh Shu-Han"
|
||||
"compositor": "Yeh Shu-Han",
|
||||
"ressource": "https://youtube.com/watch?v=xxx"
|
||||
},
|
||||
{
|
||||
"id": "390",
|
||||
@@ -100,6 +101,7 @@ GET /scores/390
|
||||
"id": "102",
|
||||
"name": "A Legend from Yao",
|
||||
"compositor": "Yeh Shu-Han",
|
||||
"ressource": "https://youtube.com/watch?v=xxx",
|
||||
"instruments": [
|
||||
{
|
||||
"id": "cla",
|
||||
@@ -307,10 +309,16 @@ Content-Type: application/json
|
||||
```json
|
||||
{
|
||||
"name": "Nouveau nom",
|
||||
"compositor": "Nouveau compositeur"
|
||||
"compositor": "Nouveau compositeur",
|
||||
"ressource": "https://youtube.com/watch?v=xxx"
|
||||
}
|
||||
```
|
||||
|
||||
**Paramètres optionnels :**
|
||||
- `name` - Nom du morceau
|
||||
- `compositor` - Nom du compositeur
|
||||
- `ressource` - Lien externe (YouTube, site éditeur, etc.)
|
||||
|
||||
---
|
||||
|
||||
### DELETE /admin/scores/:id
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
<?php
|
||||
// Load .env file if it exists
|
||||
if (file_exists(__DIR__ . '/.env')) {
|
||||
$lines = file(__DIR__ . '/.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
foreach ($lines as $line) {
|
||||
if (strpos($line, '#') === 0) continue;
|
||||
if (strpos($line, '=') !== false) {
|
||||
list($key, $value) = explode('=', $line, 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value);
|
||||
if (!getenv($key)) {
|
||||
putenv("$key=$value");
|
||||
$_ENV[$key] = $value;
|
||||
$_SERVER[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increase upload limits for this script
|
||||
ini_set('upload_max_filesize', '64M');
|
||||
ini_set('post_max_size', '64M');
|
||||
@@ -12,11 +30,16 @@ header('Referrer-Policy: strict-origin-when-cross-origin');
|
||||
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
|
||||
|
||||
// CORS - Restrict to allowed origins
|
||||
$allowedOrigins = ['http://localhost:5173', 'http://localhost:3000', 'https://ohmj2.free.fr', 'https://partitions.ohmj.fr'];
|
||||
$allowedOrigins = ['http://localhost:5173', 'http://localhost:3000', 'http://localhost:4173', 'https://ohmj2.free.fr', 'https://partitions.c.nadal-fr.com', 'https://ohmj-api.c.nadal-fr.com'];
|
||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||
|
||||
// Always send CORS headers for preflight requests
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
} else {
|
||||
// For preflight without matching origin, still allow (for debugging)
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
}
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Authorization, Content-Type');
|
||||
@@ -70,7 +93,11 @@ require_once __DIR__ . '/lib/Auth.php';
|
||||
require_once __DIR__ . '/lib/ScoreScanner.php';
|
||||
|
||||
$auth = new Auth();
|
||||
$scanner = new ScoreScanner('/home/jbnadal/sources/jb/ohmj/ohmj2/legacy/Scores/');
|
||||
$scoresPath = getenv('SCORES_PATH');
|
||||
if ($scoresPath === false || $scoresPath === '') {
|
||||
$scoresPath = __DIR__ . '/../legacy/Scores/';
|
||||
}
|
||||
$scanner = new ScoreScanner($scoresPath);
|
||||
|
||||
// Get Authorization header
|
||||
$token = null;
|
||||
@@ -124,7 +151,7 @@ if (preg_match('#^download/([^?]+)#', $path, $matches) && $method === 'GET') {
|
||||
exit;
|
||||
}
|
||||
|
||||
$basePath = '/home/jbnadal/sources/jb/ohmj/ohmj2/legacy/Scores/';
|
||||
$basePath = getenv('SCORES_PATH') ?: __DIR__ . '/../legacy/Scores/';
|
||||
$fullPath = $basePath . $filePath;
|
||||
|
||||
// Security: Verify resolved path is within allowed directory
|
||||
@@ -198,6 +225,11 @@ if ($user === null) {
|
||||
// GET /scores - List all scores
|
||||
if ($path === 'scores' && $method === 'GET') {
|
||||
$scores = $scanner->listScores();
|
||||
if (isset($scores['error'])) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $scores['error']]);
|
||||
exit;
|
||||
}
|
||||
echo json_encode(['success' => true, 'scores' => $scores]);
|
||||
exit;
|
||||
}
|
||||
@@ -297,8 +329,9 @@ if (preg_match('#^admin/scores/(\d+)$#', $path, $matches) && $method === 'PUT')
|
||||
|
||||
$name = $input['name'] ?? null;
|
||||
$compositor = $input['compositor'] ?? null;
|
||||
$ressource = $input['ressource'] ?? '';
|
||||
|
||||
$result = $scanner->updateScore($scoreId, $name, $compositor);
|
||||
$result = $scanner->updateScore($scoreId, $name, $compositor, $ressource);
|
||||
|
||||
if ($result['success']) {
|
||||
echo json_encode(['success' => true]);
|
||||
@@ -332,13 +365,47 @@ if (preg_match('#^admin/scores/(\d+)$#', $path, $matches) && $method === 'DELETE
|
||||
if (preg_match('#^admin/scores/(\d+)/upload$#', $path, $matches) && $method === 'POST') {
|
||||
$scoreId = $matches[1];
|
||||
|
||||
// Check for upload errors
|
||||
if (!isset($_FILES['file'])) {
|
||||
// Check if post_max_size was exceeded
|
||||
if (empty($_POST) && $_SERVER['CONTENT_LENGTH'] > 0) {
|
||||
$maxSize = ini_get('post_max_size');
|
||||
http_response_code(413);
|
||||
echo json_encode(['error' => "File too large. Maximum size is $maxSize"]);
|
||||
exit;
|
||||
}
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'No file uploaded']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$file = $_FILES['file'];
|
||||
|
||||
// Check for PHP upload errors
|
||||
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||
$errorMsg = 'Upload failed';
|
||||
switch ($file['error']) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$maxSize = ini_get('upload_max_filesize');
|
||||
$errorMsg = "File too large. Maximum size is $maxSize";
|
||||
http_response_code(413);
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$errorMsg = 'File was only partially uploaded';
|
||||
http_response_code(400);
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$errorMsg = 'No file was uploaded';
|
||||
http_response_code(400);
|
||||
break;
|
||||
default:
|
||||
http_response_code(400);
|
||||
}
|
||||
echo json_encode(['error' => $errorMsg]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$piece = $_POST['piece'] ?? '1';
|
||||
$instrument = $_POST['instrument'] ?? '';
|
||||
$version = $_POST['version'] ?? '1';
|
||||
|
||||
@@ -7,6 +7,10 @@ class ScoreScanner {
|
||||
}
|
||||
|
||||
public function getAllScores() {
|
||||
if (!is_dir($this->scoresPath)) {
|
||||
return ['error' => 'Scores directory not found: ' . $this->scoresPath];
|
||||
}
|
||||
|
||||
$scores = [];
|
||||
$directories = scandir($this->scoresPath);
|
||||
|
||||
|
||||
@@ -17,6 +17,25 @@ if (file_exists($envFile)) {
|
||||
}
|
||||
}
|
||||
|
||||
// CORS headers - MUST be sent before any other output
|
||||
$allowedOrigins = ['http://localhost:5173', 'http://localhost:3000', 'http://localhost:4173', 'https://ohmj2.free.fr', 'https://partitions.c.nadal-fr.com', 'https://ohmj-api.c.nadal-fr.com'];
|
||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||
|
||||
if (in_array($origin, $allowedOrigins)) {
|
||||
header("Access-Control-Allow-Origin: $origin");
|
||||
header('Access-Control-Allow-Credentials: true');
|
||||
} else {
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
}
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Authorization, Content-Type');
|
||||
|
||||
// Handle OPTIONS preflight immediately
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Router script for PHP built-in server
|
||||
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user