Showing
5 changed files
with
129 additions
and
64 deletions
| 1 | +# MySQL 数据库配置 | |
| 1 | 2 | DB_HOST=140.210.199.111 |
| 2 | 3 | DB_PORT=3306 |
| 3 | 4 | DB_USER=root |
| 4 | 5 | DB_PASSWORD=Qnbar123! |
| 5 | 6 | DB_NAME=ai |
| 6 | 7 | |
| 8 | +# Redis 配置 | |
| 7 | 9 | REDIS_HOST=10.10.1.232 |
| 8 | 10 | REDIS_PORT=6379 |
| 9 | 11 | REDIS_PASSWORD=qnbarcom |
| 10 | 12 | REDIS_PREFIX=ai_ |
| 13 | + | |
| 14 | +# TOS (火山引擎对象存储) 配置 | |
| 15 | +# TOS_AK=your_access_key | |
| 16 | +# TOS_SK=your_secret_key | |
| 17 | +# TOS_ENDPOINT=tos-cn-shanghai.volces.com | |
| 18 | +# TOS_REGION=cn-shanghai | |
| 19 | +# TOS_BUCKET=ocxun | |
| 20 | + | |
| 21 | +# 服务器配置 | |
| 22 | +# WS_HOST=0.0.0.0 | |
| 23 | +# WS_PORT=8888 | |
| 24 | +# HTTP_HOST=0.0.0.0 | |
| 25 | +# HTTP_PORT=8889 | |
| 26 | +# WORKER_COUNT=1 | |
| 27 | + | |
| 28 | +# 心跳配置 | |
| 29 | +# HEARTBEAT_INTERVAL=30 | |
| 30 | +# HEARTBEAT_CHECK_INTERVAL=10 | ... | ... |
config.php
0 → 100644
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * 配置文件 | |
| 4 | + * 优先级: 环境变量 > 配置文件默认值 | |
| 5 | + */ | |
| 6 | + | |
| 7 | +return [ | |
| 8 | + // Redis 配置 | |
| 9 | + 'redis' => [ | |
| 10 | + 'host' => getenv('REDIS_HOST') ?: '127.0.0.1', | |
| 11 | + 'port' => (int) (getenv('REDIS_PORT') ?: 6379), | |
| 12 | + 'password' => getenv('REDIS_PASSWORD') ?: null, | |
| 13 | + 'prefix' => getenv('REDIS_PREFIX') ?: 'ai_', | |
| 14 | + ], | |
| 15 | + | |
| 16 | + // 数据库配置 | |
| 17 | + 'database' => [ | |
| 18 | + 'host' => getenv('DB_HOST') ?: '127.0.0.1', | |
| 19 | + 'port' => (int) (getenv('DB_PORT') ?: 3306), | |
| 20 | + 'username' => getenv('DB_USER') ?: 'root', | |
| 21 | + 'password' => getenv('DB_PASSWORD') ?: '', | |
| 22 | + 'database' => getenv('DB_NAME') ?: 'ai', | |
| 23 | + ], | |
| 24 | + | |
| 25 | + // TOS (火山引擎对象存储) 配置 | |
| 26 | + 'tos' => [ | |
| 27 | + 'ak' => getenv('TOS_AK') ?: 'AKLTZjkyMzliYjQ5N2IyNDFjNDliMTBiY2E2ZmU5ODhjNTM', | |
| 28 | + 'sk' => getenv('TOS_SK') ?: 'WldKbE5XUmpPRGxqWmpZM05EUTBObUpqTTJSa01qVTNNMkprWmpsbU9Uaw==', | |
| 29 | + 'endpoint' => getenv('TOS_ENDPOINT') ?: 'tos-cn-shanghai.volces.com', | |
| 30 | + 'region' => getenv('TOS_REGION') ?: 'cn-shanghai', | |
| 31 | + 'bucket' => getenv('TOS_BUCKET') ?: 'ocxun', | |
| 32 | + ], | |
| 33 | + | |
| 34 | + // 服务器配置 | |
| 35 | + 'server' => [ | |
| 36 | + 'websocket_host' => getenv('WS_HOST') ?: '0.0.0.0', | |
| 37 | + 'websocket_port' => (int) (getenv('WS_PORT') ?: 8888), | |
| 38 | + 'http_host' => getenv('HTTP_HOST') ?: '0.0.0.0', | |
| 39 | + 'http_port' => (int) (getenv('HTTP_PORT') ?: 8889), | |
| 40 | + 'worker_count' => (int) (getenv('WORKER_COUNT') ?: 1), | |
| 41 | + ], | |
| 42 | + | |
| 43 | + // 文件上传配置 | |
| 44 | + 'upload' => [ | |
| 45 | + 'max_size' => 50 * 1024 * 1024, // 50MB | |
| 46 | + 'allowed_extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'pdf', 'xls', 'xlsx'], | |
| 47 | + 'default_user' => 'guest', // TODO: 等待后续对接用户手机号功能 | |
| 48 | + ], | |
| 49 | + | |
| 50 | + // 心跳配置 | |
| 51 | + 'heartbeat' => [ | |
| 52 | + 'interval' => (int) (getenv('HEARTBEAT_INTERVAL') ?: 30), | |
| 53 | + 'check_interval' => (int) (getenv('HEARTBEAT_CHECK_INTERVAL') ?: 10), | |
| 54 | + ], | |
| 55 | +]; | ... | ... |
| ... | ... | @@ -8,38 +8,40 @@ use Tos\Model\PutObjectInput; |
| 8 | 8 | |
| 9 | 9 | require_once __DIR__ . '/vendor/autoload.php'; |
| 10 | 10 | |
| 11 | +// 加载配置文件 | |
| 12 | +$config = require __DIR__ . '/config.php'; | |
| 13 | + | |
| 11 | 14 | // Define Heartbeat Interval |
| 12 | -define('HEARTBEAT_TIME', 30); | |
| 15 | +define('HEARTBEAT_TIME', $config['heartbeat']['interval']); | |
| 13 | 16 | |
| 14 | 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 | 22 | // Emulate simple routing/state for now |
| 18 | 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 | 26 | // Redis Connection |
| 22 | 27 | $redis = null; |
| 23 | 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 | 31 | $params = [ |
| 30 | 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 | 41 | // Predis Client |
| 40 | - $redis = new Predis\Client($params, ['prefix' => $redis_prefix]); | |
| 42 | + $redis = new Predis\Client($params, ['prefix' => $redis_config['prefix']]); | |
| 41 | 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 | 45 | } catch (Exception $e) { |
| 44 | 46 | echo "⚠️ Redis Connection Failed: " . $e->getMessage() . "\n"; |
| 45 | 47 | } |
| ... | ... | @@ -48,11 +50,14 @@ try { |
| 48 | 50 | $clients = []; // ClientID -> Connection |
| 49 | 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 | 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 | 61 | $time_now = time(); |
| 57 | 62 | foreach ($worker->connections as $connection) { |
| 58 | 63 | // Check if connection is alive possibly? |
| ... | ... | @@ -153,9 +158,11 @@ $ws_worker->onClose = function ($connection) use (&$clients, &$devices) { |
| 153 | 158 | // --------------------------------------------------------- |
| 154 | 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 | 166 | // 1. Static File Serving (Simple implementation) |
| 160 | 167 | $path = $request->path(); |
| 161 | 168 | if (strpos($path, '/uploads/') === 0) { |
| ... | ... | @@ -181,41 +188,38 @@ $http_worker->onMessage = function ($connection, $request) { |
| 181 | 188 | |
| 182 | 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 | 196 | return; |
| 188 | 197 | } |
| 189 | 198 | |
| 190 | 199 | // Validate Extension |
| 191 | 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 | 202 | if (!in_array($ext, $allowed)) { |
| 195 | 203 | $connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => 'File type not allowed']))); |
| 196 | 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 | 210 | try { |
| 207 | 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 | 218 | // Generate Key |
| 215 | 219 | $uuid = bin2hex(random_bytes(8)); |
| 216 | - // TODO: 暂时使用 'guest',等待后续对接用户手机号功能 | |
| 217 | - $userPhone = 'guest'; | |
| 220 | + $userPhone = $config['upload']['default_user']; | |
| 218 | 221 | $objectKey = "clawdbot/{$userPhone}/{$uuid}.{$ext}"; |
| 222 | + $bucket = $tos_config['bucket']; | |
| 219 | 223 | |
| 220 | 224 | // Read file content |
| 221 | 225 | $contentFn = fopen($file['tmp_name'], 'r'); |
| ... | ... | @@ -231,7 +235,7 @@ $http_worker->onMessage = function ($connection, $request) { |
| 231 | 235 | } |
| 232 | 236 | |
| 233 | 237 | // Generate URL |
| 234 | - $url = "https://{$bucket}.{$endpoint}/{$objectKey}"; | |
| 238 | + $url = "https://{$bucket}.{$tos_config['endpoint']}/{$objectKey}"; | |
| 235 | 239 | |
| 236 | 240 | $connection->send(json_encode([ |
| 237 | 241 | 'ok' => true, |
| ... | ... | @@ -250,44 +254,40 @@ $http_worker->onMessage = function ($connection, $request) { |
| 250 | 254 | $filename = $query['filename'] ?? 'file_' . time(); |
| 251 | 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 | 259 | if (!in_array($ext, $allowed) && $ext !== '') { |
| 256 | 260 | // If no extension providing, we might just allow it or fail. |
| 257 | 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 | 267 | try { |
| 268 | 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 | 275 | $uuid = bin2hex(random_bytes(8)); |
| 276 | - // TODO: User ID from token | |
| 277 | - $userPhone = 'guest'; | |
| 276 | + $userPhone = $config['upload']['default_user']; | |
| 278 | 277 | // Ensure filename is safe |
| 279 | 278 | $safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename); |
| 280 | 279 | if (!$safeName) |
| 281 | 280 | $safeName = 'unnamed'; |
| 282 | 281 | |
| 283 | 282 | $objectKey = "clawdbot/{$userPhone}/direct_{$uuid}_{$safeName}"; |
| 283 | + $bucket = $tos_config['bucket']; | |
| 284 | 284 | |
| 285 | 285 | // Generate Pre-Signed PUT URL (Valid for 15 mins) |
| 286 | 286 | // Note: The SDK method name might vary slightly based on version, |
| 287 | 287 | // but `preSignedURL` is standard for TOS PHP SDK v2. |
| 288 | 288 | $input = new \Tos\Model\PreSignedURLInput( |
| 289 | 289 | 'PUT', |
| 290 | - $bucket, | |
| 290 | + $tos_config['bucket'], | |
| 291 | 291 | $objectKey, |
| 292 | 292 | 300 // 5 minutes validity |
| 293 | 293 | ); |
| ... | ... | @@ -300,7 +300,7 @@ $http_worker->onMessage = function ($connection, $request) { |
| 300 | 300 | |
| 301 | 301 | // Public Access URL (Assuming bucket is public-read or we use signed Get URL) |
| 302 | 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 | 305 | $connection->send(json_encode([ |
| 306 | 306 | 'ok' => true, | ... | ... |
Please
register
or
login
to post a comment