|
@@ -8,38 +8,40 @@ use Tos\Model\PutObjectInput; |
|
@@ -8,38 +8,40 @@ use Tos\Model\PutObjectInput; |
|
8
|
|
8
|
|
|
9
|
require_once __DIR__ . '/vendor/autoload.php';
|
9
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
10
|
|
10
|
|
|
|
|
11
|
+// 加载配置文件
|
|
|
|
12
|
+$config = require __DIR__ . '/config.php';
|
|
|
|
13
|
+
|
|
11
|
// Define Heartbeat Interval
|
14
|
// Define Heartbeat Interval
|
|
12
|
-define('HEARTBEAT_TIME', 30);
|
15
|
+define('HEARTBEAT_TIME', $config['heartbeat']['interval']);
|
|
13
|
|
16
|
|
|
14
|
// Create a WebSocket worker
|
17
|
// Create a WebSocket worker
|
|
15
|
-$ws_worker = new Worker("websocket://0.0.0.0:8888");
|
18
|
+$ws_host = $config['server']['websocket_host'];
|
|
|
|
19
|
+$ws_port = $config['server']['websocket_port'];
|
|
|
|
20
|
+$ws_worker = new Worker("websocket://{$ws_host}:{$ws_port}");
|
|
16
|
|
21
|
|
|
17
|
// Emulate simple routing/state for now
|
22
|
// Emulate simple routing/state for now
|
|
18
|
// In production, use Redis for distributed state
|
23
|
// In production, use Redis for distributed state
|
|
19
|
-$ws_worker->count = 1; // Single process for dev simplicity
|
24
|
+$ws_worker->count = $config['server']['worker_count'];
|
|
20
|
|
25
|
|
|
21
|
// Redis Connection
|
26
|
// Redis Connection
|
|
22
|
$redis = null;
|
27
|
$redis = null;
|
|
23
|
try {
|
28
|
try {
|
|
24
|
- $redis_host = getenv('REDIS_HOST') ?: '127.0.0.1';
|
|
|
|
25
|
- $redis_port = getenv('REDIS_PORT') ?: 6379;
|
|
|
|
26
|
- $redis_auth = getenv('REDIS_PASSWORD');
|
|
|
|
27
|
- $redis_prefix = getenv('REDIS_PREFIX') ?: 'ai_';
|
29
|
+ $redis_config = $config['redis'];
|
|
28
|
|
30
|
|
|
29
|
$params = [
|
31
|
$params = [
|
|
30
|
'scheme' => 'tcp',
|
32
|
'scheme' => 'tcp',
|
|
31
|
- 'host' => $redis_host,
|
|
|
|
32
|
- 'port' => $redis_port,
|
33
|
+ 'host' => $redis_config['host'],
|
|
|
|
34
|
+ 'port' => $redis_config['port'],
|
|
33
|
];
|
35
|
];
|
|
34
|
|
36
|
|
|
35
|
- if ($redis_auth) {
|
|
|
|
36
|
- $params['password'] = $redis_auth;
|
37
|
+ if (!empty($redis_config['password'])) {
|
|
|
|
38
|
+ $params['password'] = $redis_config['password'];
|
|
37
|
}
|
39
|
}
|
|
38
|
|
40
|
|
|
39
|
// Predis Client
|
41
|
// Predis Client
|
|
40
|
- $redis = new Predis\Client($params, ['prefix' => $redis_prefix]);
|
42
|
+ $redis = new Predis\Client($params, ['prefix' => $redis_config['prefix']]);
|
|
41
|
$redis->connect();
|
43
|
$redis->connect();
|
|
42
|
- echo "✅ Connected to Redis at $redis_host ($redis_prefix)\n";
|
44
|
+ echo "✅ Connected to Redis at {$redis_config['host']}:{$redis_config['port']} ({$redis_config['prefix']})\n";
|
|
43
|
} catch (Exception $e) {
|
45
|
} catch (Exception $e) {
|
|
44
|
echo "⚠️ Redis Connection Failed: " . $e->getMessage() . "\n";
|
46
|
echo "⚠️ Redis Connection Failed: " . $e->getMessage() . "\n";
|
|
45
|
}
|
47
|
}
|
|
@@ -48,11 +50,14 @@ try { |
|
@@ -48,11 +50,14 @@ try { |
|
48
|
$clients = []; // ClientID -> Connection
|
50
|
$clients = []; // ClientID -> Connection
|
|
49
|
$devices = []; // DeviceID -> Connection
|
51
|
$devices = []; // DeviceID -> Connection
|
|
50
|
|
52
|
|
|
51
|
-$ws_worker->onWorkerStart = function ($worker) {
|
|
|
|
52
|
- echo "Relay Server Started on 0.0.0.0:8888\n";
|
53
|
+$ws_worker->onWorkerStart = function ($worker) use ($config) {
|
|
|
|
54
|
+ $ws_host = $config['server']['websocket_host'];
|
|
|
|
55
|
+ $ws_port = $config['server']['websocket_port'];
|
|
|
|
56
|
+ echo "Relay Server Started on {$ws_host}:{$ws_port}\n";
|
|
53
|
|
57
|
|
|
54
|
// Heartbeat check
|
58
|
// Heartbeat check
|
|
55
|
- Timer::add(10, function () use ($worker) {
|
59
|
+ $check_interval = $config['heartbeat']['check_interval'];
|
|
|
|
60
|
+ Timer::add($check_interval, function () use ($worker) {
|
|
56
|
$time_now = time();
|
61
|
$time_now = time();
|
|
57
|
foreach ($worker->connections as $connection) {
|
62
|
foreach ($worker->connections as $connection) {
|
|
58
|
// Check if connection is alive possibly?
|
63
|
// Check if connection is alive possibly?
|
|
@@ -153,9 +158,11 @@ $ws_worker->onClose = function ($connection) use (&$clients, &$devices) { |
|
@@ -153,9 +158,11 @@ $ws_worker->onClose = function ($connection) use (&$clients, &$devices) { |
|
153
|
// ---------------------------------------------------------
|
158
|
// ---------------------------------------------------------
|
|
154
|
// [New] HTTP Server for file uploads and static serving
|
159
|
// [New] HTTP Server for file uploads and static serving
|
|
155
|
// ---------------------------------------------------------
|
160
|
// ---------------------------------------------------------
|
|
156
|
-$http_worker = new Worker("http://0.0.0.0:8889");
|
|
|
|
157
|
-$http_worker->count = 1; // Single process for uploads
|
|
|
|
158
|
-$http_worker->onMessage = function ($connection, $request) {
|
161
|
+$http_host = $config['server']['http_host'];
|
|
|
|
162
|
+$http_port = $config['server']['http_port'];
|
|
|
|
163
|
+$http_worker = new Worker("http://{$http_host}:{$http_port}");
|
|
|
|
164
|
+$http_worker->count = $config['server']['worker_count'];
|
|
|
|
165
|
+$http_worker->onMessage = function ($connection, $request) use ($config) {
|
|
159
|
// 1. Static File Serving (Simple implementation)
|
166
|
// 1. Static File Serving (Simple implementation)
|
|
160
|
$path = $request->path();
|
167
|
$path = $request->path();
|
|
161
|
if (strpos($path, '/uploads/') === 0) {
|
168
|
if (strpos($path, '/uploads/') === 0) {
|
|
@@ -181,41 +188,38 @@ $http_worker->onMessage = function ($connection, $request) { |
|
@@ -181,41 +188,38 @@ $http_worker->onMessage = function ($connection, $request) { |
|
181
|
|
188
|
|
|
182
|
$file = $files['file'];
|
189
|
$file = $files['file'];
|
|
183
|
|
190
|
|
|
184
|
- // Validate Size (50MB)
|
|
|
|
185
|
- if ($file['size'] > 50 * 1024 * 1024) {
|
|
|
|
186
|
- $connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => 'File too large (Max 50MB)'])));
|
191
|
+ // Validate Size
|
|
|
|
192
|
+ $max_size = $config['upload']['max_size'];
|
|
|
|
193
|
+ if ($file['size'] > $max_size) {
|
|
|
|
194
|
+ $max_mb = round($max_size / 1024 / 1024);
|
|
|
|
195
|
+ $connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => "File too large (Max {$max_mb}MB)"])));
|
|
187
|
return;
|
196
|
return;
|
|
188
|
}
|
197
|
}
|
|
189
|
|
198
|
|
|
190
|
// Validate Extension
|
199
|
// Validate Extension
|
|
191
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
200
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
192
|
- // Supported: PDF, Excel, Image, Video
|
|
|
|
193
|
- $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'pdf', 'xls', 'xlsx'];
|
201
|
+ $allowed = $config['upload']['allowed_extensions'];
|
|
194
|
if (!in_array($ext, $allowed)) {
|
202
|
if (!in_array($ext, $allowed)) {
|
|
195
|
$connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => 'File type not allowed'])));
|
203
|
$connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => 'File type not allowed'])));
|
|
196
|
return;
|
204
|
return;
|
|
197
|
}
|
205
|
}
|
|
198
|
|
206
|
|
|
199
|
- // TOS Configuration (Keys from hsobs.php)
|
|
|
|
200
|
- $ak = 'AKLTZjkyMzliYjQ5N2IyNDFjNDliMTBiY2E2ZmU5ODhjNTM';
|
|
|
|
201
|
- $sk = 'WldKbE5XUmpPRGxqWmpZM05EUTBObUpqTTJSa01qVTNNMkprWmpsbU9Uaw==';
|
|
|
|
202
|
- $endpoint = 'tos-cn-shanghai.volces.com';
|
|
|
|
203
|
- $region = 'cn-shanghai';
|
|
|
|
204
|
- $bucket = 'ocxun';
|
207
|
+ // TOS Configuration
|
|
|
|
208
|
+ $tos_config = $config['tos'];
|
|
205
|
|
209
|
|
|
206
|
try {
|
210
|
try {
|
|
207
|
$client = new TosClient([
|
211
|
$client = new TosClient([
|
|
208
|
- 'region' => $region,
|
|
|
|
209
|
- 'endpoint' => $endpoint,
|
|
|
|
210
|
- 'ak' => $ak,
|
|
|
|
211
|
- 'sk' => $sk,
|
212
|
+ 'region' => $tos_config['region'],
|
|
|
|
213
|
+ 'endpoint' => $tos_config['endpoint'],
|
|
|
|
214
|
+ 'ak' => $tos_config['ak'],
|
|
|
|
215
|
+ 'sk' => $tos_config['sk'],
|
|
212
|
]);
|
216
|
]);
|
|
213
|
|
217
|
|
|
214
|
// Generate Key
|
218
|
// Generate Key
|
|
215
|
$uuid = bin2hex(random_bytes(8));
|
219
|
$uuid = bin2hex(random_bytes(8));
|
|
216
|
- // TODO: 暂时使用 'guest',等待后续对接用户手机号功能
|
|
|
|
217
|
- $userPhone = 'guest';
|
220
|
+ $userPhone = $config['upload']['default_user'];
|
|
218
|
$objectKey = "clawdbot/{$userPhone}/{$uuid}.{$ext}";
|
221
|
$objectKey = "clawdbot/{$userPhone}/{$uuid}.{$ext}";
|
|
|
|
222
|
+ $bucket = $tos_config['bucket'];
|
|
219
|
|
223
|
|
|
220
|
// Read file content
|
224
|
// Read file content
|
|
221
|
$contentFn = fopen($file['tmp_name'], 'r');
|
225
|
$contentFn = fopen($file['tmp_name'], 'r');
|
|
@@ -231,7 +235,7 @@ $http_worker->onMessage = function ($connection, $request) { |
|
@@ -231,7 +235,7 @@ $http_worker->onMessage = function ($connection, $request) { |
|
231
|
}
|
235
|
}
|
|
232
|
|
236
|
|
|
233
|
// Generate URL
|
237
|
// Generate URL
|
|
234
|
- $url = "https://{$bucket}.{$endpoint}/{$objectKey}";
|
238
|
+ $url = "https://{$bucket}.{$tos_config['endpoint']}/{$objectKey}";
|
|
235
|
|
239
|
|
|
236
|
$connection->send(json_encode([
|
240
|
$connection->send(json_encode([
|
|
237
|
'ok' => true,
|
241
|
'ok' => true,
|
|
@@ -250,44 +254,40 @@ $http_worker->onMessage = function ($connection, $request) { |
|
@@ -250,44 +254,40 @@ $http_worker->onMessage = function ($connection, $request) { |
|
250
|
$filename = $query['filename'] ?? 'file_' . time();
|
254
|
$filename = $query['filename'] ?? 'file_' . time();
|
|
251
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
255
|
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
|
252
|
|
256
|
|
|
253
|
- // Validation - Keep it simple for demo
|
|
|
|
254
|
- $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'pdf', 'xls', 'xlsx'];
|
257
|
+ // Validation
|
|
|
|
258
|
+ $allowed = $config['upload']['allowed_extensions'];
|
|
255
|
if (!in_array($ext, $allowed) && $ext !== '') {
|
259
|
if (!in_array($ext, $allowed) && $ext !== '') {
|
|
256
|
// If no extension providing, we might just allow it or fail.
|
260
|
// If no extension providing, we might just allow it or fail.
|
|
257
|
// Ideally client should provide full filename with extension.
|
261
|
// Ideally client should provide full filename with extension.
|
|
258
|
}
|
262
|
}
|
|
259
|
|
263
|
|
|
260
|
- // TOS Configuration (Keys from hsobs.php)
|
|
|
|
261
|
- $ak = 'AKLTZjkyMzliYjQ5N2IyNDFjNDliMTBiY2E2ZmU5ODhjNTM';
|
|
|
|
262
|
- $sk = 'WldKbE5XUmpPRGxqWmpZM05EUTBObUpqTTJSa01qVTNNMkprWmpsbU9Uaw==';
|
|
|
|
263
|
- $endpoint = 'tos-cn-shanghai.volces.com';
|
|
|
|
264
|
- $region = 'cn-shanghai';
|
|
|
|
265
|
- $bucket = 'ocxun';
|
264
|
+ // TOS Configuration
|
|
|
|
265
|
+ $tos_config = $config['tos'];
|
|
266
|
|
266
|
|
|
267
|
try {
|
267
|
try {
|
|
268
|
$client = new TosClient([
|
268
|
$client = new TosClient([
|
|
269
|
- 'region' => $region,
|
|
|
|
270
|
- 'endpoint' => $endpoint,
|
|
|
|
271
|
- 'ak' => $ak,
|
|
|
|
272
|
- 'sk' => $sk,
|
269
|
+ 'region' => $tos_config['region'],
|
|
|
|
270
|
+ 'endpoint' => $tos_config['endpoint'],
|
|
|
|
271
|
+ 'ak' => $tos_config['ak'],
|
|
|
|
272
|
+ 'sk' => $tos_config['sk'],
|
|
273
|
]);
|
273
|
]);
|
|
274
|
|
274
|
|
|
275
|
$uuid = bin2hex(random_bytes(8));
|
275
|
$uuid = bin2hex(random_bytes(8));
|
|
276
|
- // TODO: User ID from token
|
|
|
|
277
|
- $userPhone = 'guest';
|
276
|
+ $userPhone = $config['upload']['default_user'];
|
|
278
|
// Ensure filename is safe
|
277
|
// Ensure filename is safe
|
|
279
|
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);
|
278
|
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);
|
|
280
|
if (!$safeName)
|
279
|
if (!$safeName)
|
|
281
|
$safeName = 'unnamed';
|
280
|
$safeName = 'unnamed';
|
|
282
|
|
281
|
|
|
283
|
$objectKey = "clawdbot/{$userPhone}/direct_{$uuid}_{$safeName}";
|
282
|
$objectKey = "clawdbot/{$userPhone}/direct_{$uuid}_{$safeName}";
|
|
|
|
283
|
+ $bucket = $tos_config['bucket'];
|
|
284
|
|
284
|
|
|
285
|
// Generate Pre-Signed PUT URL (Valid for 15 mins)
|
285
|
// Generate Pre-Signed PUT URL (Valid for 15 mins)
|
|
286
|
// Note: The SDK method name might vary slightly based on version,
|
286
|
// Note: The SDK method name might vary slightly based on version,
|
|
287
|
// but `preSignedURL` is standard for TOS PHP SDK v2.
|
287
|
// but `preSignedURL` is standard for TOS PHP SDK v2.
|
|
288
|
$input = new \Tos\Model\PreSignedURLInput(
|
288
|
$input = new \Tos\Model\PreSignedURLInput(
|
|
289
|
'PUT',
|
289
|
'PUT',
|
|
290
|
- $bucket,
|
290
|
+ $tos_config['bucket'],
|
|
291
|
$objectKey,
|
291
|
$objectKey,
|
|
292
|
300 // 5 minutes validity
|
292
|
300 // 5 minutes validity
|
|
293
|
);
|
293
|
);
|
|
@@ -300,7 +300,7 @@ $http_worker->onMessage = function ($connection, $request) { |
|
@@ -300,7 +300,7 @@ $http_worker->onMessage = function ($connection, $request) { |
|
300
|
|
300
|
|
|
301
|
// Public Access URL (Assuming bucket is public-read or we use signed Get URL)
|
301
|
// Public Access URL (Assuming bucket is public-read or we use signed Get URL)
|
|
302
|
// For this project, we used public-read ACL in previous code, so we assume public access.
|
302
|
// For this project, we used public-read ACL in previous code, so we assume public access.
|
|
303
|
- $publicUrl = "https://{$bucket}.{$endpoint}/{$objectKey}";
|
303
|
+ $publicUrl = "https://{$tos_config['bucket']}.{$tos_config['endpoint']}/{$objectKey}";
|
|
304
|
|
304
|
|
|
305
|
$connection->send(json_encode([
|
305
|
$connection->send(json_encode([
|
|
306
|
'ok' => true,
|
306
|
'ok' => true,
|