Commit c1ecda602d6d0eeedd9223ee1415e1378f1a1411

Authored by 胡龙江
2 parents 2b029f26 39ebbdce

Merge branch 'feature/image-upload' into release

1 FROM php:8.2-cli 1 FROM php:8.2-cli
2 2
  3 +# -----------------------------------------------------------------------------
  4 +# 1. 替换 Debian 软件源为阿里云镜像 (加速 apt-get)
  5 +# 注意:PHP 8.2 官方镜像基于 Debian Bookworm,源文件通常在 /etc/apt/sources.list.d/debian.sources
  6 +# -----------------------------------------------------------------------------
  7 +RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || \
  8 + sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list
  9 +
3 # Install dependencies and extensions 10 # Install dependencies and extensions
4 -RUN apt-get update && apt-get install -y \ 11 +# 增加 --no-install-recommends 减小体积,增加 rm -rf 清理缓存
  12 +RUN apt-get update && apt-get install -y --no-install-recommends \
5 git \ 13 git \
6 unzip \ 14 unzip \
7 libzip-dev \ 15 libzip-dev \
8 - && docker-php-ext-install pcntl posix sockets zip pdo_mysql 16 + && docker-php-ext-install pcntl posix sockets zip pdo_mysql \
  17 + && apt-get clean \
  18 + && rm -rf /var/lib/apt/lists/*
9 19
10 # Install Composer 20 # Install Composer
11 COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 21 COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
@@ -13,10 +23,16 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer @@ -13,10 +23,16 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
13 # Set working directory 23 # Set working directory
14 WORKDIR /app 24 WORKDIR /app
15 25
  26 +# -----------------------------------------------------------------------------
  27 +# 2. 替换 Composer 镜像源为阿里云镜像 (加速 composer install)
  28 +# -----------------------------------------------------------------------------
  29 +RUN composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
  30 +
16 # Copy dependency definition first 31 # Copy dependency definition first
17 COPY composer.json ./ 32 COPY composer.json ./
  33 +# (Optional) COPY composer.lock ./
18 34
19 -# Install dependencies (if composer.lock exists it would be better, but we start fresh) 35 +# Install dependencies
20 RUN composer install --no-scripts --no-autoloader 36 RUN composer install --no-scripts --no-autoloader
21 37
22 # Copy application source 38 # Copy application source
@@ -30,3 +46,4 @@ EXPOSE 8888 @@ -30,3 +46,4 @@ EXPOSE 8888
30 46
31 # Command to run the Workerman server 47 # Command to run the Workerman server
32 CMD ["php", "start.php", "start"] 48 CMD ["php", "start.php", "start"]
  49 +
@@ -7,7 +7,8 @@ @@ -7,7 +7,8 @@
7 "php": ">=8.0", 7 "php": ">=8.0",
8 "workerman/workerman": "^4.1", 8 "workerman/workerman": "^4.1",
9 "firebase/php-jwt": "^6.10", 9 "firebase/php-jwt": "^6.10",
10 - "predis/predis": "^2.3" 10 + "predis/predis": "^2.3",
  11 + "volcengine/ve-tos-php-sdk": "^2.0"
11 }, 12 },
12 "autoload": { 13 "autoload": {
13 "psr-4": { 14 "psr-4": {
@@ -9,6 +9,8 @@ services: @@ -9,6 +9,8 @@ services:
9 environment: 9 environment:
10 - REDIS_HOST=redis 10 - REDIS_HOST=redis
11 - REDIS_PORT=6379 11 - REDIS_PORT=6379
  12 + volumes:
  13 + - ./:/app
12 14
13 redis: 15 redis:
14 image: redis:alpine 16 image: redis:alpine
1 <?php 1 <?php
2 use Workerman\Worker; 2 use Workerman\Worker;
3 use Workerman\Timer; 3 use Workerman\Timer;
  4 +use Tos\TosClient;
  5 +use Tos\Exception\TosClientException;
  6 +use Tos\Exception\TosServerException;
  7 +use Tos\Model\PutObjectInput;
4 8
5 require_once __DIR__ . '/vendor/autoload.php'; 9 require_once __DIR__ . '/vendor/autoload.php';
6 10
@@ -146,4 +150,101 @@ $ws_worker->onClose = function ($connection) use (&$clients, &$devices) { @@ -146,4 +150,101 @@ $ws_worker->onClose = function ($connection) use (&$clients, &$devices) {
146 } 150 }
147 }; 151 };
148 152
  153 +// ---------------------------------------------------------
  154 +// [New] HTTP Server for file uploads and static serving
  155 +// ---------------------------------------------------------
  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) {
  159 + // 1. Static File Serving (Simple implementation)
  160 + $path = $request->path();
  161 + if (strpos($path, '/uploads/') === 0) {
  162 + $file = __DIR__ . $path;
  163 + if (is_file($file)) {
  164 + $connection->send(new \Workerman\Protocols\Http\Response(
  165 + 200,
  166 + ['Content-Type' => mime_content_type($file)],
  167 + file_get_contents($file)
  168 + ));
  169 + return;
  170 + }
  171 + }
  172 +
  173 + // 2. Upload Handler
  174 + if ($path === '/upload') {
  175 + $files = $request->file();
  176 +
  177 + if (empty($files['file'])) {
  178 + $connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => 'No file'])));
  179 + return;
  180 + }
  181 +
  182 + $file = $files['file'];
  183 +
  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)'])));
  187 + return;
  188 + }
  189 +
  190 + // Validate Extension
  191 + $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
  192 + // Supported: PDF, Excel, Image, Video
  193 + $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'mp4', 'pdf', 'xls', 'xlsx'];
  194 + if (!in_array($ext, $allowed)) {
  195 + $connection->send(new \Workerman\Protocols\Http\Response(400, [], json_encode(['ok' => false, 'error' => 'File type not allowed'])));
  196 + return;
  197 + }
  198 +
  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';
  205 +
  206 + try {
  207 + $client = new TosClient([
  208 + 'region' => $region,
  209 + 'endpoint' => $endpoint,
  210 + 'ak' => $ak,
  211 + 'sk' => $sk,
  212 + ]);
  213 +
  214 + // Generate Key
  215 + $uuid = bin2hex(random_bytes(8));
  216 + // TODO: 暂时使用 'guest',等待后续对接用户手机号功能
  217 + $userPhone = 'guest';
  218 + $objectKey = "clawdbot/{$userPhone}/{$uuid}.{$ext}";
  219 +
  220 + // Read file content
  221 + $contentFn = fopen($file['tmp_name'], 'r');
  222 +
  223 + // Upload using Object Input
  224 + $input = new PutObjectInput($bucket, $objectKey, $contentFn);
  225 + $input->setACL('public-read');
  226 +
  227 + $client->putObject($input);
  228 +
  229 + if (is_resource($contentFn)) {
  230 + fclose($contentFn);
  231 + }
  232 +
  233 + // Generate URL
  234 + $url = "https://{$bucket}.{$endpoint}/{$objectKey}";
  235 +
  236 + $connection->send(json_encode([
  237 + 'ok' => true,
  238 + 'url' => $url
  239 + ]));
  240 +
  241 + } catch (Exception $e) {
  242 + $connection->send(new \Workerman\Protocols\Http\Response(500, [], json_encode(['ok' => false, 'error' => 'Upload failed: ' . $e->getMessage()])));
  243 + }
  244 + return;
  245 + }
  246 +
  247 + $connection->send("Moltbot Relay HTTP Server");
  248 +};
  249 +
149 Worker::runAll(); 250 Worker::runAll();
Please register or login to post a comment