Showing
4 changed files
with
125 additions
and
4 deletions
| 1 | 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 | 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 | 13 | git \ |
| 6 | 14 | unzip \ |
| 7 | 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 | 20 | # Install Composer |
| 11 | 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 | 23 | # Set working directory |
| 14 | 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 | 31 | # Copy dependency definition first |
| 17 | 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 | 36 | RUN composer install --no-scripts --no-autoloader |
| 21 | 37 | |
| 22 | 38 | # Copy application source |
| ... | ... | @@ -30,3 +46,4 @@ EXPOSE 8888 |
| 30 | 46 | |
| 31 | 47 | # Command to run the Workerman server |
| 32 | 48 | CMD ["php", "start.php", "start"] |
| 49 | + | ... | ... |
| 1 | 1 | <?php |
| 2 | 2 | use Workerman\Worker; |
| 3 | 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 | 9 | require_once __DIR__ . '/vendor/autoload.php'; |
| 6 | 10 | |
| ... | ... | @@ -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 | 250 | Worker::runAll(); | ... | ... |
Please
register
or
login
to post a comment