@artilingo/artiframe-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/logo.png +0 -0
- package/assets/logo.svg +22 -0
- package/assets/logo.webp +0 -0
- package/assets/logo@0.5x.png +0 -0
- package/assets/logo@0.75x.png +0 -0
- package/assets/logo@1.5x.png +0 -0
- package/assets/logo@2x.png +0 -0
- package/assets/logo@3x.png +0 -0
- package/assets/logo@4x.png +0 -0
- package/bin/artiframe.js +57 -0
- package/bin/artiframe.php +45 -0
- package/core-stubs/app/ApiControl.php +81 -0
- package/core-stubs/app/Database.php +69 -0
- package/core-stubs/app/DotEnv.php +63 -0
- package/core-stubs/app/R2Manager.php +130 -0
- package/core-stubs/app/ViewControl.php +24 -0
- package/core-stubs/bin/SystemMethod.php +271 -0
- package/core-stubs/bin/ViewMethod.php +354 -0
- package/core-stubs/docs/de.html +951 -0
- package/core-stubs/docs/en.html +952 -0
- package/core-stubs/docs/es.html +947 -0
- package/core-stubs/docs/fr.html +850 -0
- package/core-stubs/docs/tr.html +951 -0
- package/core-stubs/public/assets/css/components/footer.css +0 -0
- package/core-stubs/public/assets/css/components/header.css +0 -0
- package/core-stubs/public/assets/css/components/mobilenav.css +0 -0
- package/core-stubs/public/assets/css/components/sidebar.css +0 -0
- package/core-stubs/public/assets/css/components/theme-modal.css +0 -0
- package/core-stubs/public/assets/css/root/app.css +61 -0
- package/core-stubs/public/assets/js/components/header.js +0 -0
- package/core-stubs/public/assets/js/components/mobilenav.js +0 -0
- package/core-stubs/public/assets/js/components/sidebar.js +0 -0
- package/core-stubs/public/assets/js/components/theme-modal.js +0 -0
- package/core-stubs/public/assets/js/root/app.js +30 -0
- package/core-stubs/public/includes/footer.php +5 -0
- package/core-stubs/public/includes/head.php +26 -0
- package/core-stubs/public/includes/header.php +5 -0
- package/core-stubs/public/includes/mobilenav.php +5 -0
- package/core-stubs/public/includes/sidebar.php +5 -0
- package/core-stubs/public/includes/theme-modal.php +5 -0
- package/core-stubs/readme/de.md +29 -0
- package/core-stubs/readme/en.md +29 -0
- package/core-stubs/readme/es.md +29 -0
- package/core-stubs/readme/fr.md +29 -0
- package/core-stubs/readme/tr.md +29 -0
- package/package.json +49 -0
- package/scripts/postinstall.js +21 -0
- package/src/App.php +266 -0
- package/src/Commands/MakeApiCommand.php +71 -0
- package/src/Commands/MakeClassCommand.php +88 -0
- package/src/Commands/MakeViewCommand.php +95 -0
- package/src/Commands/NewProjectCommand.php +633 -0
- package/src/Commands/VersionCommand.php +92 -0
- package/src/Lang/de.php +53 -0
- package/src/Lang/en.php +53 -0
- package/src/Lang/es.php +53 -0
- package/src/Lang/fr.php +53 -0
- package/src/Lang/tr.php +53 -0
- package/src/Services/Safeguard.php +48 -0
- package/src/Services/Translator.php +52 -0
package/assets/logo.png
ADDED
|
Binary file
|
package/assets/logo.svg
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg id="katman_2" data-name="katman 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144.49 94.09">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>
|
|
5
|
+
.cls-1 {
|
|
6
|
+
fill: #099772;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.cls-2 {
|
|
10
|
+
fill: #0a9872;
|
|
11
|
+
}
|
|
12
|
+
</style>
|
|
13
|
+
</defs>
|
|
14
|
+
<g id="katman_1" data-name="katman 1">
|
|
15
|
+
<g>
|
|
16
|
+
<path class="cls-2" d="M131.15,38.06c.67-.18,1.4-.1,2.26.14.32-3.12,2.59-5.34,5.55-5.33s5.5,2.5,5.53,5.43-2.23,5.38-5.12,5.59c-3.09.22-5.71-2.08-5.9-5.29-34.52,2.36-67.93,12.42-97.08,31.26-5.61,3.7-11,7.44-16,11.83l-6.13,12.38-14.26-.02,13.95-27.62,7.14-14.06L47.82.06l21.37,41.64c-3.96,1.03-7.73,2.29-11.62,3.65l-9.74-19.89-2.9,5.99-14.53,29.82c25.42-14.09,52.79-21.29,81.35-23.22l5.69-.27,11.17.03c.86,0,1.58.49,2.52.24Z"/>
|
|
17
|
+
<polygon class="cls-2" points="64.24 58.84 75.54 54.2 90.45 83.55 112.61 83.52 112.61 44.51 122.99 43.06 122.99 94.08 81.55 94.09 72.23 75.24 64.24 58.84"/>
|
|
18
|
+
<path class="cls-1" d="M112.61,33.97V10.34s-53.64.02-53.64.02L54.05.7c.04-.06.09-.22.35-.22h68.33s0,33.6,0,33.6l-10.13-.11Z"/>
|
|
19
|
+
<path class="cls-2" d="M112.61,33.97l10.13.11V.48s-68.34,0-68.34,0c-.26,0-.31.16-.35.22-.3-.09-.39-.7.07-.7h68.84s0,34.31,0,34.31h-7.91c-.8.25-1.56.34-2.36.21-.07-.2-.1-.34-.1-.56Z"/>
|
|
20
|
+
</g>
|
|
21
|
+
</g>
|
|
22
|
+
</svg>
|
package/assets/logo.webp
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/artiframe.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ArtiFrame CLI — Node.js wrapper
|
|
5
|
+
*
|
|
6
|
+
* This file is the npm entry point. It checks that PHP is installed
|
|
7
|
+
* on the system, then delegates execution to the PHP CLI bootstrap.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execFileSync, spawnSync } = require('child_process');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
// ── 1. Verify PHP is available ──────────────────────────────────
|
|
15
|
+
function findPhp() {
|
|
16
|
+
const candidates = ['php', 'php8', 'php81', 'php82', 'php83', 'php84'];
|
|
17
|
+
for (const bin of candidates) {
|
|
18
|
+
const result = spawnSync(bin, ['--version'], { encoding: 'utf8', shell: true });
|
|
19
|
+
if (result.status === 0) return bin;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const php = findPhp();
|
|
25
|
+
|
|
26
|
+
if (!php) {
|
|
27
|
+
console.error('\n\x1b[1;31m[ArtiFrame] PHP Not Found!\x1b[0m');
|
|
28
|
+
console.error('\x1b[33mArtiFrame CLI requires PHP 8.1 or higher.\x1b[0m\n');
|
|
29
|
+
console.error('Install PHP:');
|
|
30
|
+
console.error(' Windows : https://windows.php.net/download/');
|
|
31
|
+
console.error(' macOS : brew install php');
|
|
32
|
+
console.error(' Linux : sudo apt install php8.2-cli\n');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ── 2. Verify PHP version >= 8.1 ───────────────────────────────
|
|
37
|
+
const versionResult = spawnSync(php, ['-r', 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;'], {
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
shell: true
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const [major, minor] = (versionResult.stdout || '0.0').split('.').map(Number);
|
|
43
|
+
if (major < 8 || (major === 8 && minor < 1)) {
|
|
44
|
+
console.error('\n\x1b[1;31m[ArtiFrame] PHP version too old!\x1b[0m');
|
|
45
|
+
console.error(`\x1b[33mFound PHP ${major}.${minor} — ArtiFrame requires PHP 8.1+\x1b[0m\n`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── 3. Delegate to the PHP bootstrap ───────────────────────────
|
|
50
|
+
const phpScript = path.join(__dirname, '..', 'bin', 'artiframe.php');
|
|
51
|
+
|
|
52
|
+
const result = spawnSync(php, [phpScript, ...process.argv.slice(2)], {
|
|
53
|
+
stdio: 'inherit',
|
|
54
|
+
shell: false
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
process.exit(result.status ?? 1);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env php
|
|
2
|
+
<?php
|
|
3
|
+
/**
|
|
4
|
+
* ArtiFrame CLI
|
|
5
|
+
*
|
|
6
|
+
* @package ArtiFrame
|
|
7
|
+
* @author Artilingo
|
|
8
|
+
* @license AGPLv3 (Attribution-ShareAlike Required)
|
|
9
|
+
* @link https://artiframe.org
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Define constants
|
|
13
|
+
define('ARTIFRAME_CLI_ROOT', dirname(__DIR__));
|
|
14
|
+
define('ARTIFRAME_VERSION', '1.0.0');
|
|
15
|
+
|
|
16
|
+
// Register a simple autoloader for the CLI's own classes
|
|
17
|
+
spl_autoload_register(function ($class) {
|
|
18
|
+
// Prefix for CLI classes
|
|
19
|
+
$prefix = 'ArtiFrame\\Cli\\';
|
|
20
|
+
$base_dir = ARTIFRAME_CLI_ROOT . '/src/';
|
|
21
|
+
|
|
22
|
+
// Does the class use the namespace prefix?
|
|
23
|
+
$len = strlen($prefix);
|
|
24
|
+
if (strncmp($prefix, $class, $len) !== 0) {
|
|
25
|
+
return; // no, move to the next registered autoloader
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
$relative_class = substr($class, $len);
|
|
29
|
+
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
|
30
|
+
|
|
31
|
+
if (file_exists($file)) {
|
|
32
|
+
require $file;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
use ArtiFrame\Cli\App;
|
|
37
|
+
|
|
38
|
+
// Bootstrap and run the application
|
|
39
|
+
try {
|
|
40
|
+
$app = new App($argv);
|
|
41
|
+
$app->run();
|
|
42
|
+
} catch (\Exception $e) {
|
|
43
|
+
echo "[ERROR] " . $e->getMessage() . PHP_EOL;
|
|
44
|
+
exit(1);
|
|
45
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* ArtiFrame Core Engine
|
|
4
|
+
*
|
|
5
|
+
* @package ArtiFrame
|
|
6
|
+
* @author Artilingo
|
|
7
|
+
* @license AGPLv3 (Attribution-ShareAlike Required)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
require_once __DIR__ . '/../vendor/autoload.php';
|
|
11
|
+
|
|
12
|
+
// 1. Ortam değişkenlerini yükle (.env)
|
|
13
|
+
\App\DotEnv::load(__DIR__ . '/../.env');
|
|
14
|
+
|
|
15
|
+
// 2. Uygulama sabitlerini yükle (APP_ENV, APP_VERSION)
|
|
16
|
+
require_once __DIR__ . '/../config/app-version.php';
|
|
17
|
+
|
|
18
|
+
// 3. Sadece API'ye özgü metodları yükle
|
|
19
|
+
require_once __DIR__ . '/../bin/SystemMethod.php';
|
|
20
|
+
|
|
21
|
+
// 4. API Konfigürasyonları (JSON Response Header vs.)
|
|
22
|
+
header('Content-Type: application/json; charset=utf-8');
|
|
23
|
+
|
|
24
|
+
// 5. İzin Verilen HTTP Metodlarının Kontrolü
|
|
25
|
+
// API uç noktalarında tanımlanan $allowedMethods dizisini baz alır. Tanımsızsa sadece POST kabul eder.
|
|
26
|
+
$allowed = isset($allowedMethods) && is_array($allowedMethods) ? $allowedMethods : ['POST'];
|
|
27
|
+
$requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN';
|
|
28
|
+
|
|
29
|
+
// Preflight istekleri (OPTIONS) CORS için hayati öneme sahiptir, her halükarda 200 dönüp çıkış yapmasını sağlarız.
|
|
30
|
+
if ($requestMethod === 'OPTIONS') {
|
|
31
|
+
http_response_code(200);
|
|
32
|
+
exit;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!in_array($requestMethod, $allowed)) {
|
|
36
|
+
http_response_code(405);
|
|
37
|
+
echo json_encode([
|
|
38
|
+
'status' => 'error',
|
|
39
|
+
'message' => "Geçersiz istek metodu. Sadece " . implode(', ', $allowed) . " kabul edilir."
|
|
40
|
+
]);
|
|
41
|
+
exit;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
// -------------------------------------------------------------------------
|
|
46
|
+
// 6. CORS (Cross-Origin Resource Sharing) Kuralları
|
|
47
|
+
// -------------------------------------------------------------------------
|
|
48
|
+
// Başka domainlerden veya mobil uygulamalardan gelen API isteklerini kabul
|
|
49
|
+
// etmek isterseniz aşağıdaki satırları aktifleştirin. "*" yerine kendi
|
|
50
|
+
// domaininizi (örn: "https://artilingo.com") yazarak güvenliği artırabilirsiniz.
|
|
51
|
+
|
|
52
|
+
header("Access-Control-Allow-Origin: *");
|
|
53
|
+
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
|
|
54
|
+
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
// -------------------------------------------------------------------------
|
|
59
|
+
// 7. Rate Limiting (Basit IP Tabanlı Hız Sınırlandırma - Redis Örneği)
|
|
60
|
+
// -------------------------------------------------------------------------
|
|
61
|
+
// Kötü niyetli kişilerin veya botların API'nizi flood etmesini engeller.
|
|
62
|
+
// Redis servisinizi aktif ettiyseniz bu bloğu kullanabilirsiniz.
|
|
63
|
+
// Aşağıdaki örnekte, aynı IP adresinin 1 dakikada 60'tan fazla istek yapması engellenir.
|
|
64
|
+
|
|
65
|
+
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
|
66
|
+
$rateKey = 'rate_limit:' . $ip;
|
|
67
|
+
$limit = 60; // 1 dakikadaki maksimum istek sayısı
|
|
68
|
+
$redis = \Src\Service\RedisService::getInstance();
|
|
69
|
+
|
|
70
|
+
$currentCount = $redis->get($rateKey);
|
|
71
|
+
if ($currentCount !== null && $currentCount >= $limit) {
|
|
72
|
+
http_response_code(429); // 429 Too Many Requests
|
|
73
|
+
echo json_encode(['status' => 'error', 'message' => 'Çok fazla istek gönderdiniz. Lütfen daha sonra tekrar deneyin.']);
|
|
74
|
+
exit;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
$redis->incr($rateKey);
|
|
78
|
+
if ($currentCount === null) {
|
|
79
|
+
$redis->expire($rateKey, 60); // Sayacı 60 saniye sonra sıfırla
|
|
80
|
+
}
|
|
81
|
+
*/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* ArtiFrame Core Engine
|
|
4
|
+
*
|
|
5
|
+
* @package ArtiFrame
|
|
6
|
+
* @author Artilingo
|
|
7
|
+
* @license AGPLv3 (Attribution-ShareAlike Required)
|
|
8
|
+
* @link https://artiframe.org
|
|
9
|
+
*
|
|
10
|
+
* NOTICE: This file is part of the ArtiFrame ecosystem.
|
|
11
|
+
* Any derivative works or patches MUST retain this original copyright notice
|
|
12
|
+
* and remain open-source under the AGPLv3 license.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
namespace App;
|
|
16
|
+
|
|
17
|
+
use PDO;
|
|
18
|
+
use PDOException;
|
|
19
|
+
|
|
20
|
+
class Database
|
|
21
|
+
{
|
|
22
|
+
private static ?PDO $instance = null;
|
|
23
|
+
|
|
24
|
+
private function __construct() {}
|
|
25
|
+
private function __clone() {}
|
|
26
|
+
|
|
27
|
+
public static function getInstance(): PDO
|
|
28
|
+
{
|
|
29
|
+
if (self::$instance === null) {
|
|
30
|
+
|
|
31
|
+
// .env henüz yüklenmediyse tam ve temiz yoldan otomatik yükle
|
|
32
|
+
if (empty($_ENV['DB_HOST'])) {
|
|
33
|
+
// src/ katmanından bir üst klasöre (htdocs köküne) direkt geçiş:
|
|
34
|
+
$envPath = dirname(__DIR__) . '/.env';
|
|
35
|
+
DotEnv::load($envPath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
$host = $_ENV['DB_HOST'] ?? 'localhost';
|
|
39
|
+
$dbname = $_ENV['DB_NAME'] ?? 'portfobase';
|
|
40
|
+
$user = $_ENV['DB_USER'] ?? 'root';
|
|
41
|
+
$pass = $_ENV['DB_PASS'] ?? '';
|
|
42
|
+
|
|
43
|
+
$dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8mb4", $host, $dbname);
|
|
44
|
+
|
|
45
|
+
$options = [
|
|
46
|
+
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
47
|
+
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
48
|
+
PDO::ATTR_EMULATE_PREPARES => false,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
self::$instance = new PDO($dsn, $user, $pass, $options);
|
|
53
|
+
} catch (PDOException $e) {
|
|
54
|
+
$isDev = (defined('APP_ENV') && APP_ENV == 1);
|
|
55
|
+
|
|
56
|
+
$msg = !$isDev
|
|
57
|
+
? 'Veritabanı bağlantı hatası. Sistem yöneticisi ile görüşün.'
|
|
58
|
+
: 'Veritabanı Hatası (Dev Mod): ' . $e->getMessage();
|
|
59
|
+
|
|
60
|
+
header('Content-Type: application/json; charset=utf-8');
|
|
61
|
+
http_response_code(500);
|
|
62
|
+
echo json_encode(['success' => false, 'message' => $msg]);
|
|
63
|
+
exit;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return self::$instance;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* ArtiFrame Core Engine
|
|
4
|
+
*
|
|
5
|
+
* @package ArtiFrame
|
|
6
|
+
* @author Artilingo
|
|
7
|
+
* @license AGPLv3 (Attribution-ShareAlike Required)
|
|
8
|
+
* @link https://artiframe.org
|
|
9
|
+
*
|
|
10
|
+
* NOTICE: This file is part of the ArtiFrame ecosystem.
|
|
11
|
+
* Any derivative works or patches MUST retain this original copyright notice
|
|
12
|
+
* and remain open-source under the AGPLv3 license.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
namespace App;
|
|
16
|
+
|
|
17
|
+
class DotEnv
|
|
18
|
+
{
|
|
19
|
+
/**
|
|
20
|
+
* Belirtilen dizindeki .env dosyasını okur ve çevresel değişkenlere ekler.
|
|
21
|
+
*/
|
|
22
|
+
public static function load(string $path): void
|
|
23
|
+
{
|
|
24
|
+
// 1. Yol içindeki ".." ve bağıl (relative) katmanları temizle
|
|
25
|
+
$realPath = realpath($path);
|
|
26
|
+
$targetPath = $realPath !== false ? $realPath : $path;
|
|
27
|
+
|
|
28
|
+
// 2. Kontrolü temizlenmiş yol ($targetPath) üzerinden yap
|
|
29
|
+
if (!file_exists($targetPath) || !is_readable($targetPath)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$lines = file($targetPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
34
|
+
|
|
35
|
+
foreach ($lines as $line) {
|
|
36
|
+
$line = trim($line);
|
|
37
|
+
|
|
38
|
+
// Yorum satırlarını es geç
|
|
39
|
+
if (str_starts_with($line, '#')) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Atama içermeyen satırları atla
|
|
44
|
+
if (!str_contains($line, '=')) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
list($name, $value) = explode('=', $line, 2);
|
|
49
|
+
$name = trim($name);
|
|
50
|
+
$value = trim($value);
|
|
51
|
+
|
|
52
|
+
// Tırnak işaretlerini temizle
|
|
53
|
+
$value = trim($value, '"\'');
|
|
54
|
+
|
|
55
|
+
// Zaten tanımlı değilse aktar
|
|
56
|
+
if (!array_key_exists($name, $_SERVER) && !array_key_exists($name, $_ENV)) {
|
|
57
|
+
putenv(sprintf('%s=%s', $name, $value));
|
|
58
|
+
$_ENV[$name] = $value;
|
|
59
|
+
$_SERVER[$name] = $value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* ArtiFrame Core Engine
|
|
4
|
+
*
|
|
5
|
+
* @package ArtiFrame
|
|
6
|
+
* @author Artilingo
|
|
7
|
+
* @license AGPLv3 (Attribution-ShareAlike Required)
|
|
8
|
+
* @link https://artiframe.org
|
|
9
|
+
*
|
|
10
|
+
* NOTICE: This file is part of the ArtiFrame ecosystem.
|
|
11
|
+
* Any derivative works or patches MUST retain this original copyright notice
|
|
12
|
+
* and remain open-source under the AGPLv3 license.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
namespace App;
|
|
16
|
+
|
|
17
|
+
use Aws\S3\S3Client;
|
|
18
|
+
use Aws\Exception\AwsException;
|
|
19
|
+
|
|
20
|
+
class R2Manager
|
|
21
|
+
{
|
|
22
|
+
private S3Client $client;
|
|
23
|
+
private string $bucketName;
|
|
24
|
+
|
|
25
|
+
public function __construct()
|
|
26
|
+
{
|
|
27
|
+
$accountId = $_ENV['R2_ACCOUNT_ID'] ?? '';
|
|
28
|
+
$accessKey = $_ENV['R2_ACCESS_KEY'] ?? '';
|
|
29
|
+
$secretKey = $_ENV['R2_SECRET_KEY'] ?? '';
|
|
30
|
+
$this->bucketName = $_ENV['R2_BUCKET_NAME'] ?? '';
|
|
31
|
+
|
|
32
|
+
$this->client = new S3Client([
|
|
33
|
+
'version' => 'latest',
|
|
34
|
+
'region' => 'auto',
|
|
35
|
+
'endpoint' => "https://{$accountId}.r2.cloudflarestorage.com",
|
|
36
|
+
'credentials' => [
|
|
37
|
+
'key' => $accessKey,
|
|
38
|
+
'secret' => $secretKey,
|
|
39
|
+
],
|
|
40
|
+
// R2 için gerekli olabiliyor
|
|
41
|
+
'use_path_style_endpoint' => true,
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* R2 bucket'a dosya yükler.
|
|
47
|
+
*
|
|
48
|
+
* @param string $sourcePath Yerel dosya yolu (veya tmp_name)
|
|
49
|
+
* @param string $destinationKey Hedef R2 path/isim
|
|
50
|
+
* @param string $contentType MIME tipi
|
|
51
|
+
* @return string|false Yükleme başarılıysa dosyanın R2 adresi döner, aksi halde false.
|
|
52
|
+
*/
|
|
53
|
+
public function upload(string $sourcePath, string $destinationKey, string $contentType = 'application/octet-stream')
|
|
54
|
+
{
|
|
55
|
+
try {
|
|
56
|
+
$result = $this->client->putObject([
|
|
57
|
+
'Bucket' => $this->bucketName,
|
|
58
|
+
'Key' => $destinationKey,
|
|
59
|
+
'SourceFile' => $sourcePath,
|
|
60
|
+
'ContentType' => $contentType,
|
|
61
|
+
// Public okuma yetkisi vermek gerekirse ACL eklenebilir, ancak R2 genelde bucket policy kullanır.
|
|
62
|
+
// 'ACL' => 'public-read',
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
// Cloudflare public dev.url veya bucket url döndürür. Projeye göre public URL yapılandırılır.
|
|
66
|
+
// Örnek public bucket path (Eğer R2'ye özel domain bağlıysa onu dönebilirsiniz)
|
|
67
|
+
// https://pub-xxxxxx.r2.dev/destinationKey gibi
|
|
68
|
+
return $destinationKey;
|
|
69
|
+
} catch (AwsException $e) {
|
|
70
|
+
error_log("R2 Yükleme Hatası: " . $e->getMessage());
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Base64 string'i bucket'a kaydeder.
|
|
77
|
+
*
|
|
78
|
+
* @param string $base64String
|
|
79
|
+
* @param string $destinationKey
|
|
80
|
+
* @param string $contentType
|
|
81
|
+
* @return string|false
|
|
82
|
+
*/
|
|
83
|
+
public function uploadBase64(string $base64String, string $destinationKey, string $contentType = 'image/jpeg')
|
|
84
|
+
{
|
|
85
|
+
// Data URI şeması varsa temizle (data:image/png;base64,...)
|
|
86
|
+
if (strpos($base64String, ',') !== false) {
|
|
87
|
+
$parts = explode(',', $base64String);
|
|
88
|
+
$base64String = $parts[1];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
$decodedData = base64_decode($base64String);
|
|
92
|
+
if ($decodedData === false) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
$result = $this->client->putObject([
|
|
98
|
+
'Bucket' => $this->bucketName,
|
|
99
|
+
'Key' => $destinationKey,
|
|
100
|
+
'Body' => $decodedData,
|
|
101
|
+
'ContentType' => $contentType,
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
return $destinationKey;
|
|
105
|
+
} catch (AwsException $e) {
|
|
106
|
+
error_log("R2 Yükleme Hatası: " . $e->getMessage());
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* R2 bucket'tan dosya siler.
|
|
113
|
+
*
|
|
114
|
+
* @param string $key Silinecek dosyanın yolu/anahtarı (örn: avatars/123.jpg)
|
|
115
|
+
* @return bool Başarılıysa true, değilse false
|
|
116
|
+
*/
|
|
117
|
+
public function delete(string $key)
|
|
118
|
+
{
|
|
119
|
+
try {
|
|
120
|
+
$this->client->deleteObject([
|
|
121
|
+
'Bucket' => $this->bucketName,
|
|
122
|
+
'Key' => $key,
|
|
123
|
+
]);
|
|
124
|
+
return true;
|
|
125
|
+
} catch (AwsException $e) {
|
|
126
|
+
error_log("R2 Silme Hatası: " . $e->getMessage());
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* ArtiFrame Core Engine
|
|
4
|
+
*
|
|
5
|
+
* @package ArtiFrame
|
|
6
|
+
* @author Artilingo
|
|
7
|
+
* @license AGPLv3 (Attribution-ShareAlike Required)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
require_once __DIR__ . '/../vendor/autoload.php';
|
|
11
|
+
|
|
12
|
+
// 1. Ortam değişkenlerini yükle (.env)
|
|
13
|
+
\App\DotEnv::load(__DIR__ . '/../.env');
|
|
14
|
+
|
|
15
|
+
// 2. Uygulama sabitlerini yükle (APP_ENV, APP_VERSION)
|
|
16
|
+
require_once __DIR__ . '/../config/app-version.php';
|
|
17
|
+
|
|
18
|
+
// 3. Sadece View'a özgü metodları yükle
|
|
19
|
+
require_once __DIR__ . '/../bin/ViewMethod.php';
|
|
20
|
+
|
|
21
|
+
// 4. Oturum (Session) ve View Konfigürasyonları
|
|
22
|
+
if (session_status() === PHP_SESSION_NONE) {
|
|
23
|
+
session_start();
|
|
24
|
+
}
|