create-isotope-app 1.2.6 → 1.2.8
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/package.json
CHANGED
|
@@ -4,7 +4,7 @@ export const readmeMd = () => `<div align="center">
|
|
|
4
4
|
|
|
5
5
|
# Isotope: Atomic Fusion
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/create-isotope-app)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
[](https://github.com/atoms-gaming/isotope)
|
|
10
10
|
[](https://www.php.net/)
|
|
@@ -51,6 +51,21 @@ export default function Page({ message }) {
|
|
|
51
51
|
}
|
|
52
52
|
\`\`\`
|
|
53
53
|
|
|
54
|
+
## 🌉 Isotope Bridge (Local Proxy)
|
|
55
|
+
|
|
56
|
+
Bypass remote database connection restrictions (e.g., Lolipop/Shared Hosting) by proxying SQL queries over HTTP(S).
|
|
57
|
+
|
|
58
|
+
### Configuration (.env)
|
|
59
|
+
|
|
60
|
+
\`\`\`env
|
|
61
|
+
# Isotope Bridge Settings
|
|
62
|
+
ISX_BRIDGE_URL=https://your-production-domain.com/core/Bridge.php
|
|
63
|
+
ISX_BRIDGE_TOKEN=your_secure_random_token
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
- **How it works**: When \`ISX_BRIDGE_URL\` is set, Isotope automatically proxies \`Database::query()\` calls to the production server.
|
|
67
|
+
- **Security**: Always use a strong \`ISX_BRIDGE_TOKEN\` and keep it secret.
|
|
68
|
+
|
|
54
69
|
### ⚛️ Quantum Fusion (SSR/CSR)
|
|
55
70
|
- **Server Components (Default)**: Rendered by PHP. Zero JS overhead.
|
|
56
71
|
- **Client Components**: Interactive UI using React hydration.
|
|
@@ -4,11 +4,24 @@ namespace Isotope;
|
|
|
4
4
|
use PDO;
|
|
5
5
|
use PDOException;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* リモートからのレスポンスをPDOStatement風に扱うためのクラス
|
|
9
|
+
*/
|
|
10
|
+
class RemoteStatement {
|
|
11
|
+
private $data;
|
|
12
|
+
public function __construct($data) { $this->data = $data; }
|
|
13
|
+
public function fetchAll() { return $this->data; }
|
|
14
|
+
public function execute() { return true; }
|
|
15
|
+
}
|
|
16
|
+
|
|
7
17
|
class Database {
|
|
8
18
|
private static $instance = null;
|
|
9
19
|
private $connection;
|
|
10
20
|
|
|
11
21
|
private function __construct() {
|
|
22
|
+
// ブリッジが有効な場合はPDO接続をスキップ
|
|
23
|
+
if ($this->shouldBridge()) return;
|
|
24
|
+
|
|
12
25
|
$host = $_ENV['DB_HOST'] ?? 'localhost';
|
|
13
26
|
$db = $_ENV['DB_NAME'] ?? 'isotope_db';
|
|
14
27
|
$user = $_ENV['DB_USER'] ?? 'root';
|
|
@@ -26,11 +39,14 @@ class Database {
|
|
|
26
39
|
try {
|
|
27
40
|
$this->connection = new PDO($dsn, $user, $pass, $options);
|
|
28
41
|
} catch (PDOException $e) {
|
|
29
|
-
// In a real app, you might want to log this instead of throwing
|
|
30
42
|
throw new PDOException($e->getMessage(), (int)$e->getCode());
|
|
31
43
|
}
|
|
32
44
|
}
|
|
33
45
|
|
|
46
|
+
private function shouldBridge() {
|
|
47
|
+
return !empty($_ENV['ISX_BRIDGE_URL']) && (isset($_ENV['DB_HOST']) && str_ends_with($_ENV['DB_HOST'], '.lan') || ($_ENV['APP_ENV'] ?? '') === 'development');
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
public static function getInstance() {
|
|
35
51
|
if (self::$instance === null) {
|
|
36
52
|
self::$instance = new self();
|
|
@@ -38,14 +54,49 @@ class Database {
|
|
|
38
54
|
return self::$instance->connection;
|
|
39
55
|
}
|
|
40
56
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
private static function getInstanceInstance() {
|
|
58
|
+
if (self::$instance === null) {
|
|
59
|
+
self::$instance = new self();
|
|
60
|
+
}
|
|
61
|
+
return self::$instance;
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
public static function query($sql, $params = []) {
|
|
45
|
-
$
|
|
65
|
+
$instance = self::getInstanceInstance();
|
|
66
|
+
|
|
67
|
+
// ブリッジ条件に合致する場合はHTTPリクエストを発行
|
|
68
|
+
if ($instance->shouldBridge()) {
|
|
69
|
+
return self::bridgeQuery($sql, $params);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$db = $instance->connection;
|
|
46
73
|
$stmt = $db->prepare($sql);
|
|
47
74
|
$stmt->execute($params);
|
|
48
75
|
return $stmt;
|
|
49
76
|
}
|
|
77
|
+
|
|
78
|
+
private static function bridgeQuery($sql, $params) {
|
|
79
|
+
$url = $_ENV['ISX_BRIDGE_URL'];
|
|
80
|
+
$token = $_ENV['ISX_BRIDGE_TOKEN'] ?? '';
|
|
81
|
+
|
|
82
|
+
$ch = curl_init($url);
|
|
83
|
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
84
|
+
curl_setopt($ch, CURLOPT_POST, true);
|
|
85
|
+
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
|
86
|
+
'action' => 'db_query',
|
|
87
|
+
'params' => ['sql' => $sql, 'params' => $params],
|
|
88
|
+
'token' => $token
|
|
89
|
+
]));
|
|
90
|
+
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
91
|
+
|
|
92
|
+
$response = curl_exec($ch);
|
|
93
|
+
$result = json_decode($response, true);
|
|
94
|
+
|
|
95
|
+
if (!$result || !($result['success'] ?? false)) {
|
|
96
|
+
throw new \\Exception("Bridge Error: " . ($result['error'] ?? 'Unknown error'));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return new RemoteStatement($result['data']);
|
|
100
|
+
}
|
|
50
101
|
}
|
|
51
102
|
`;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
export const kernelPhp = (styleChoice) => `<?php
|
|
2
2
|
namespace Isotope;
|
|
3
3
|
|
|
4
|
+
// Simple Autoloader for Core Classes
|
|
5
|
+
spl_autoload_register(function ($class) {
|
|
6
|
+
if (str_starts_with($class, 'Isotope\\\\')) {
|
|
7
|
+
$name = str_replace('Isotope\\\\', '', $class);
|
|
8
|
+
$file = __DIR__ . "/$name.php";
|
|
9
|
+
if (file_exists($file)) require_once $file;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
4
13
|
class Kernel {
|
|
5
14
|
public static function boot() {
|
|
6
15
|
self::loadEnv();
|
|
@@ -1,15 +1,37 @@
|
|
|
1
1
|
export const bridgePhp = () => `<?php
|
|
2
2
|
header('Content-Type: application/json');
|
|
3
|
+
require_once 'Kernel.php';
|
|
4
|
+
\\Isotope\\Kernel::loadEnv();
|
|
5
|
+
|
|
3
6
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
4
7
|
$input = json_decode(file_get_contents('php://input'), true);
|
|
5
8
|
$action = $input['action'] ?? null;
|
|
6
9
|
$params = $input['params'] ?? [];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
echo json_encode(['success' => false, 'error' => '
|
|
10
|
+
$token = $input['token'] ?? null;
|
|
11
|
+
|
|
12
|
+
// トークンによる保護
|
|
13
|
+
$expectedToken = $_ENV['ISX_BRIDGE_TOKEN'] ?? '';
|
|
14
|
+
if ($expectedToken && $token !== $expectedToken) {
|
|
15
|
+
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
|
16
|
+
exit;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
if ($action === 'db_query') {
|
|
21
|
+
$sql = $params['sql'];
|
|
22
|
+
$sqlParams = $params['params'] ?? [];
|
|
23
|
+
// サーバー内部ネットワークから本物のDBへクエリ実行
|
|
24
|
+
$result = \\Isotope\\Database::query($sql, $sqlParams)->fetchAll();
|
|
25
|
+
echo json_encode(['success' => true, 'data' => $result]);
|
|
26
|
+
} elseif ($action && is_callable($action)) {
|
|
27
|
+
// その他のRPC呼び出し
|
|
28
|
+
$result = $action(...$params);
|
|
29
|
+
echo json_encode(['success' => true, 'data' => $result]);
|
|
30
|
+
} else {
|
|
31
|
+
echo json_encode(['success' => false, 'error' => 'Action not found']);
|
|
32
|
+
}
|
|
33
|
+
} catch (\\Exception $e) {
|
|
34
|
+
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
|
13
35
|
}
|
|
14
36
|
exit;
|
|
15
37
|
}
|