hzruo commited on
Commit
30f6f10
·
verified ·
1 Parent(s): 9153ff1

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +61 -726
Dockerfile CHANGED
@@ -1,726 +1,61 @@
1
- from fastapi import FastAPI, Request, Depends, HTTPException
2
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3
- from fastapi.responses import StreamingResponse, HTMLResponse
4
- from fastapi.background import BackgroundTasks
5
- from contextlib import asynccontextmanager
6
- import requests
7
- from curl_cffi import requests as cffi_requests
8
- import uuid
9
- import json
10
- import time
11
- from typing import Optional
12
- import asyncio
13
- import base64
14
- import tempfile
15
- import os
16
- import re
17
- import threading
18
- from DrissionPage import ChromiumPage, ChromiumOptions
19
- import logging
20
- from dotenv import load_dotenv
21
-
22
- # 加载环境变量
23
- load_dotenv(override=True)
24
-
25
- # 配置日志
26
- logging.basicConfig(
27
- level=logging.INFO, # 改为 INFO 级别
28
- format='%(asctime)s - %(levelname)s - %(message)s'
29
- )
30
- logger = logging.getLogger(__name__)
31
-
32
- # 修改全局数据存储
33
- global_data = {
34
- "cookie": None,
35
- "cookies": None,
36
- "last_update": 0,
37
- "cookie_expires": 0 # 添加 cookie 过期时间
38
- }
39
-
40
- @asynccontextmanager
41
- async def lifespan(app: FastAPI):
42
- # 启动时获取 cookie
43
- threading.Thread(target=get_cookie).start()
44
- yield
45
- # 关闭时清理资源
46
- global_data["cookie"] = None
47
- global_data["cookies"] = None
48
- global_data["last_update"] = 0
49
-
50
- app = FastAPI(lifespan=lifespan)
51
- security = HTTPBearer()
52
-
53
- # OpenAI API Key 配置,可以通过环境变量覆盖
54
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", None)
55
- logger.info(f"OPENAI_API_KEY is set: {OPENAI_API_KEY is not None}")
56
- logger.info(f"OPENAI_API_KEY value: {OPENAI_API_KEY}")
57
-
58
- def get_cookie():
59
- try:
60
- logger.info("Starting cookie retrieval process...")
61
-
62
- # 创建配置对象
63
- options = ChromiumOptions()
64
-
65
- # 设置浏览器路径
66
- chrome_path = os.getenv('CHROME_PATH', '/usr/bin/google-chrome-stable')
67
- logger.info(f"Using Chrome path: {chrome_path}")
68
- options.set_browser_path(chrome_path)
69
-
70
- # 设置用户数据目录
71
- user_data_dir = '/tmp/chrome-data'
72
- logger.info(f"Using user data directory: {user_data_dir}")
73
- options.set_argument(f'--user-data-dir={user_data_dir}')
74
-
75
- # 设置无头模式和其他参数
76
- options.set_argument('--headless=new')
77
- options.set_argument('--no-sandbox')
78
- options.set_argument('--disable-dev-shm-usage')
79
- options.set_argument('--disable-gpu')
80
- options.set_argument('--disable-software-rasterizer')
81
- options.set_argument('--disable-extensions')
82
- options.set_argument('--disable-setuid-sandbox')
83
- options.set_argument('--no-first-run')
84
- options.set_argument('--no-zygote')
85
- options.set_argument('--single-process')
86
- options.set_argument('--remote-debugging-port=9222')
87
- options.set_argument('--window-size=1920,1080')
88
- options.set_argument('--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
89
-
90
- logger.info("Creating ChromiumPage instance...")
91
- page = ChromiumPage(addr_or_opts=options, show_log=True)
92
-
93
- logger.info("Navigating to target website...")
94
- page.get("https://chat.akash.network/")
95
-
96
- logger.info("Waiting for page load...")
97
- time.sleep(10)
98
-
99
- logger.info("Getting cookies...")
100
- cookies = page.cookies()
101
- if not cookies:
102
- logger.error("No cookies found")
103
- page.quit()
104
- return None
105
-
106
- cookie_dict = {cookie['name']: cookie['value'] for cookie in cookies}
107
- if 'cf_clearance' not in cookie_dict:
108
- logger.error("cf_clearance cookie not found")
109
- page.quit()
110
- return None
111
-
112
- cookie_str = '; '.join([f"{cookie['name']}={cookie['value']}" for cookie in cookies])
113
- global_data["cookie"] = cookie_str
114
- global_data["last_update"] = time.time()
115
-
116
- expires = min([cookie.get('expires', float('inf')) for cookie in cookies])
117
- if expires != float('inf'):
118
- global_data["cookie_expires"] = expires
119
- else:
120
- global_data["cookie_expires"] = time.time() + 3600
121
-
122
- logger.info("Successfully retrieved cookies")
123
- page.quit()
124
- return cookie_str
125
-
126
- except Exception as e:
127
- logger.error(f"Error fetching cookie: {str(e)}")
128
- logger.error(f"Error type: {type(e)}")
129
- import traceback
130
- logger.error(f"Traceback: {traceback.format_exc()}")
131
- return None
132
-
133
- # 添加刷新 cookie 的函数
134
- async def refresh_cookie():
135
- logger.info("Refreshing cookie due to 401 error")
136
- # 标记 cookie 为过期
137
- global_data["cookie_expires"] = 0
138
- # 获取新的 cookie
139
- return get_cookie()
140
-
141
- async def check_and_update_cookie(background_tasks: BackgroundTasks):
142
- # 如果 cookie 不存在或已过期,则更新
143
- current_time = time.time()
144
- if not global_data["cookie"] or current_time >= global_data["cookie_expires"]:
145
- logger.info("Cookie expired or not available, refreshing...")
146
- background_tasks.add_task(get_cookie)
147
- else:
148
- logger.info("Using existing cookie")
149
-
150
- async def get_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
151
- token = credentials.credentials
152
- logger.info(f"Received token: {token}")
153
-
154
- # 如果设置了 OPENAI_API_KEY,则需要验证
155
- if OPENAI_API_KEY is not None:
156
- # 去掉 Bearer 前缀后再比较
157
- clean_token = token.replace("Bearer ", "") if token.startswith("Bearer ") else token
158
- logger.info(f"Clean token: {clean_token}")
159
- if clean_token != OPENAI_API_KEY:
160
- logger.error(f"Token mismatch. Expected: {OPENAI_API_KEY}, Got: {clean_token}")
161
- raise HTTPException(
162
- status_code=401,
163
- detail="Invalid API key"
164
- )
165
- logger.info("API key validation passed")
166
-
167
- return True
168
-
169
- async def validate_cookie(background_tasks: BackgroundTasks):
170
- # 检查并更新 cookie(如果需要)
171
- await check_and_update_cookie(background_tasks)
172
-
173
- # 等待 cookie 初始化完成
174
- max_wait = 30 # 最大等待时间(秒)
175
- start_time = time.time()
176
- while not global_data["cookie"] and time.time() - start_time < max_wait:
177
- await asyncio.sleep(1)
178
- logger.info("Waiting for cookie initialization...")
179
-
180
- # 检查是否有有效的 cookie
181
- if not global_data["cookie"]:
182
- logger.error("Cookie not available after waiting")
183
- raise HTTPException(
184
- status_code=503,
185
- detail="Service temporarily unavailable - Cookie not available"
186
- )
187
-
188
- logger.info("Cookie validation passed")
189
- return global_data["cookie"]
190
-
191
- async def check_image_status(session: requests.Session, job_id: str, headers: dict) -> Optional[str]:
192
- """检查图片生成状态并获取生成的图片"""
193
- max_retries = 30
194
- for attempt in range(max_retries):
195
- try:
196
- print(f"\nAttempt {attempt + 1}/{max_retries} for job {job_id}")
197
- response = session.get(
198
- f'https://chat.akash.network/api/image-status?ids={job_id}',
199
- headers=headers
200
- )
201
- print(f"Status response code: {response.status_code}")
202
- status_data = response.json()
203
-
204
- if status_data and isinstance(status_data, list) and len(status_data) > 0:
205
- job_info = status_data[0]
206
- status = job_info.get('status')
207
- print(f"Job status: {status}")
208
-
209
- # 只有当状态为 completed 时才处理结果
210
- if status == "completed":
211
- result = job_info.get("result")
212
- if result and not result.startswith("Failed"):
213
- print("Got valid result, attempting upload...")
214
- image_url = await upload_to_xinyew(result, job_id)
215
- if image_url:
216
- print(f"Successfully uploaded image: {image_url}")
217
- return image_url
218
- print("Image upload failed")
219
- return None
220
- print("Invalid result received")
221
- return None
222
- elif status == "failed":
223
- print(f"Job {job_id} failed")
224
- return None
225
-
226
- # 如果状态是其他(如 pending),继续等待
227
- await asyncio.sleep(1)
228
- continue
229
-
230
- except Exception as e:
231
- print(f"Error checking status: {e}")
232
- return None
233
-
234
- print(f"Timeout waiting for job {job_id}")
235
- return None
236
-
237
- @app.get("/", response_class=HTMLResponse)
238
- async def health_check():
239
- """Health check endpoint"""
240
- status = {
241
- "status": "ok",
242
- "version": "1.0.0",
243
- "cookie_status": {
244
- "available": global_data["cookie"] is not None,
245
- "last_update": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(global_data["last_update"])) if global_data["last_update"] > 0 else None,
246
- "expires": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(global_data["cookie_expires"])) if global_data["cookie_expires"] > 0 else None
247
- }
248
- }
249
-
250
- html = f"""
251
- <!DOCTYPE html>
252
- <html>
253
- <head>
254
- <title>Akash API Status</title>
255
- <style>
256
- body {{
257
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
258
- line-height: 1.6;
259
- margin: 0;
260
- padding: 20px;
261
- background-color: #f5f5f5;
262
- }}
263
- .container {{
264
- max-width: 800px;
265
- margin: 0 auto;
266
- background-color: white;
267
- padding: 20px;
268
- border-radius: 8px;
269
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
270
- }}
271
- h1 {{
272
- color: #333;
273
- margin-top: 0;
274
- }}
275
- .status {{
276
- display: inline-block;
277
- padding: 4px 8px;
278
- border-radius: 4px;
279
- font-weight: bold;
280
- background-color: #4CAF50;
281
- color: white;
282
- }}
283
- .info {{
284
- margin-top: 20px;
285
- }}
286
- .info-item {{
287
- margin-bottom: 10px;
288
- }}
289
- .label {{
290
- font-weight: bold;
291
- color: #666;
292
- }}
293
- .value {{
294
- color: #333;
295
- }}
296
- .cookie-status {{
297
- margin-top: 20px;
298
- padding: 15px;
299
- background-color: #f8f9fa;
300
- border-radius: 4px;
301
- }}
302
- .cookie-status .available {{
303
- color: {"#4CAF50" if status["cookie_status"]["available"] else "#f44336"};
304
- }}
305
- </style>
306
- </head>
307
- <body>
308
- <div class="container">
309
- <h1>Akash API Status <span class="status">{status["status"]}</span></h1>
310
-
311
- <div class="info">
312
- <div class="info-item">
313
- <span class="label">Version:</span>
314
- <span class="value">{status["version"]}</span>
315
- </div>
316
- </div>
317
-
318
- <div class="cookie-status">
319
- <h2>Cookie Status</h2>
320
- <div class="info-item">
321
- <span class="label">Available:</span>
322
- <span class="value available">{str(status["cookie_status"]["available"])}</span>
323
- </div>
324
- <div class="info-item">
325
- <span class="label">Last Update:</span>
326
- <span class="value">{status["cookie_status"]["last_update"] or "Never"}</span>
327
- </div>
328
- <div class="info-item">
329
- <span class="label">Expires:</span>
330
- <span class="value">{status["cookie_status"]["expires"] or "Unknown"}</span>
331
- </div>
332
- </div>
333
- </div>
334
- </body>
335
- </html>
336
- """
337
-
338
- return html
339
-
340
- @app.post("/v1/chat/completions")
341
- async def chat_completions(
342
- request: Request,
343
- background_tasks: BackgroundTasks,
344
- api_key: bool = Depends(get_api_key),
345
- cookie: str = Depends(validate_cookie)
346
- ):
347
- try:
348
- data = await request.json()
349
-
350
- chat_id = str(uuid.uuid4()).replace('-', '')[:16]
351
-
352
- akash_data = {
353
- "id": chat_id,
354
- "messages": data.get('messages', []),
355
- "model": data.get('model', "DeepSeek-R1"),
356
- "system": data.get('system_message', "You are a helpful assistant."),
357
- "temperature": data.get('temperature', 0.6),
358
- "topP": data.get('top_p', 0.95)
359
- }
360
-
361
- # 构建请求头
362
- headers = {
363
- "Content-Type": "application/json",
364
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
365
- "Accept": "*/*",
366
- "Accept-Language": "en-US,en;q=0.9",
367
- "Accept-Encoding": "gzip, deflate, br",
368
- "Origin": "https://chat.akash.network",
369
- "Referer": "https://chat.akash.network/",
370
- "Sec-Fetch-Dest": "empty",
371
- "Sec-Fetch-Mode": "cors",
372
- "Sec-Fetch-Site": "same-origin",
373
- "Connection": "keep-alive"
374
- }
375
-
376
- # 设置 Cookie
377
- headers["Cookie"] = cookie
378
-
379
- with requests.Session() as session:
380
- response = session.post(
381
- 'https://chat.akash.network/api/chat',
382
- json=akash_data,
383
- headers=headers,
384
- stream=True
385
- )
386
-
387
- # 检查响应状态码,如果是 401,尝试刷新 cookie 并重试
388
- if response.status_code == 401:
389
- logger.info("Cookie expired, refreshing...")
390
- new_cookie = await refresh_cookie()
391
- if new_cookie:
392
- headers["Cookie"] = new_cookie
393
- response = session.post(
394
- 'https://chat.akash.network/api/chat',
395
- json=akash_data,
396
- headers=headers,
397
- stream=True
398
- )
399
-
400
- if response.status_code != 200:
401
- logger.error(f"Akash API error: {response.text}")
402
- raise HTTPException(
403
- status_code=response.status_code,
404
- detail=f"Akash API error: {response.text}"
405
- )
406
-
407
- def generate():
408
- content_buffer = ""
409
- for line in response.iter_lines():
410
- if not line:
411
- continue
412
-
413
- try:
414
- line_str = line.decode('utf-8')
415
- msg_type, msg_data = line_str.split(':', 1)
416
-
417
- if msg_type == '0':
418
- if msg_data.startswith('"') and msg_data.endswith('"'):
419
- msg_data = msg_data.replace('\\"', '"')
420
- msg_data = msg_data[1:-1]
421
- msg_data = msg_data.replace("\\n", "\n")
422
-
423
- # 在处理消息时先判断模型类型
424
- if data.get('model') == 'AkashGen' and "<image_generation>" in msg_data:
425
- # 图片生成模型的特殊处理
426
- async def process_and_send():
427
- messages = await process_image_generation(msg_data, session, headers, chat_id)
428
- if messages:
429
- return messages
430
- return None
431
-
432
- # 创建新的事件循环
433
- loop = asyncio.new_event_loop()
434
- asyncio.set_event_loop(loop)
435
- try:
436
- result_messages = loop.run_until_complete(process_and_send())
437
- finally:
438
- loop.close()
439
-
440
- if result_messages:
441
- for message in result_messages:
442
- yield f"data: {json.dumps(message)}\n\n"
443
- continue
444
-
445
- content_buffer += msg_data
446
-
447
- chunk = {
448
- "id": f"chatcmpl-{chat_id}",
449
- "object": "chat.completion.chunk",
450
- "created": int(time.time()),
451
- "model": data.get('model'),
452
- "choices": [{
453
- "delta": {"content": msg_data},
454
- "index": 0,
455
- "finish_reason": None
456
- }]
457
- }
458
- yield f"data: {json.dumps(chunk)}\n\n"
459
-
460
- elif msg_type in ['e', 'd']:
461
- chunk = {
462
- "id": f"chatcmpl-{chat_id}",
463
- "object": "chat.completion.chunk",
464
- "created": int(time.time()),
465
- "model": data.get('model'),
466
- "choices": [{
467
- "delta": {},
468
- "index": 0,
469
- "finish_reason": "stop"
470
- }]
471
- }
472
- yield f"data: {json.dumps(chunk)}\n\n"
473
- yield "data: [DONE]\n\n"
474
- break
475
-
476
- except Exception as e:
477
- print(f"Error processing line: {e}")
478
- continue
479
-
480
- return StreamingResponse(
481
- generate(),
482
- media_type='text/event-stream',
483
- headers={
484
- 'Cache-Control': 'no-cache',
485
- 'Connection': 'keep-alive',
486
- 'Content-Type': 'text/event-stream'
487
- }
488
- )
489
-
490
- except Exception as e:
491
- print(f"Error in chat_completions: {e}")
492
- import traceback
493
- print(traceback.format_exc())
494
- return {"error": str(e)}
495
-
496
- @app.get("/v1/models")
497
- async def list_models(
498
- background_tasks: BackgroundTasks,
499
- cookie: str = Depends(validate_cookie)
500
- ):
501
- try:
502
- headers = {
503
- "accept": "application/json",
504
- "accept-language": "en-US,en;q=0.9",
505
- "sec-ch-ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
506
- "sec-ch-ua-mobile": "?0",
507
- "sec-ch-ua-platform": '"macOS"',
508
- "sec-fetch-dest": "document",
509
- "sec-fetch-mode": "cors",
510
- "sec-fetch-site": "same-origin",
511
- "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
512
- "referer": "https://chat.akash.network/"
513
- }
514
-
515
- # 设置 Cookie
516
- headers["Cookie"] = cookie
517
-
518
- print(f"Using cookie: {headers.get('Cookie', 'None')}")
519
- print("Sending request to get models...")
520
-
521
- response = requests.get(
522
- 'https://chat.akash.network/api/models',
523
- headers=headers
524
- )
525
-
526
- print(f"Models response status: {response.status_code}")
527
- print(f"Models response headers: {response.headers}")
528
-
529
- if response.status_code == 401:
530
- print("Authentication failed. Please check your API key.")
531
- return {"error": "Authentication failed. Please check your API key."}
532
-
533
- akash_response = response.json()
534
-
535
- # 添加错误处理和调试信息
536
- print(f"Akash API response: {akash_response}")
537
-
538
- # 检查响应格式并适配
539
- models_list = []
540
- if isinstance(akash_response, list):
541
- # 如果直接是列表
542
- models_list = akash_response
543
- elif isinstance(akash_response, dict):
544
- # 如果是字典格式
545
- models_list = akash_response.get("models", [])
546
- else:
547
- print(f"Unexpected response format: {type(akash_response)}")
548
- models_list = []
549
-
550
- # 转换为标准 OpenAI 格式
551
- openai_models = {
552
- "object": "list",
553
- "data": [
554
- {
555
- "id": model["id"] if isinstance(model, dict) else model,
556
- "object": "model",
557
- "created": int(time.time()),
558
- "owned_by": "akash",
559
- "permission": [{
560
- "id": f"modelperm-{model['id'] if isinstance(model, dict) else model}",
561
- "object": "model_permission",
562
- "created": int(time.time()),
563
- "allow_create_engine": False,
564
- "allow_sampling": True,
565
- "allow_logprobs": True,
566
- "allow_search_indices": False,
567
- "allow_view": True,
568
- "allow_fine_tuning": False,
569
- "organization": "*",
570
- "group": None,
571
- "is_blocking": False
572
- }]
573
- } for model in models_list
574
- ]
575
- }
576
-
577
- return openai_models
578
-
579
- except Exception as e:
580
- print(f"Error in list_models: {e}")
581
- import traceback
582
- print(traceback.format_exc())
583
- return {"error": str(e)}
584
-
585
- async def process_image_generation(msg_data: str, session: requests.Session, headers: dict, chat_id: str) -> Optional[list]:
586
- """处理图片生成的逻辑,返回多个消息块"""
587
- match = re.search(r"jobId='([^']+)' prompt='([^']+)' negative='([^']*)'", msg_data)
588
- if match:
589
- job_id, prompt, negative = match.groups()
590
- print(f"Starting image generation process for job_id: {job_id}")
591
-
592
- # 记录开始时间
593
- start_time = time.time()
594
-
595
- # 发送思考开始的消息
596
- think_msg = "<think>\n"
597
- think_msg += "🎨 Generating image...\n\n"
598
- think_msg += f"Prompt: {prompt}\n"
599
-
600
- # 检查图片状态和上传
601
- result = await check_image_status(session, job_id, headers)
602
-
603
- # 计算实际花费的时间
604
- elapsed_time = time.time() - start_time
605
-
606
- # 完成思考部分
607
- think_msg += f"\n🤔 Thinking for {elapsed_time:.1f}s...\n"
608
- think_msg += "</think>"
609
-
610
- # 返回两个独立的消息块
611
- messages = []
612
-
613
- # 第一个消息块:思考过程
614
- messages.append({
615
- "id": f"chatcmpl-{chat_id}-think",
616
- "object": "chat.completion.chunk",
617
- "created": int(time.time()),
618
- "model": "AkashGen",
619
- "choices": [{
620
- "delta": {"content": think_msg},
621
- "index": 0,
622
- "finish_reason": None
623
- }]
624
- })
625
-
626
- # 第二个消息块:图片结果
627
- if result:
628
- image_msg = f"\n\n![Generated Image]({result})"
629
- messages.append({
630
- "id": f"chatcmpl-{chat_id}-image",
631
- "object": "chat.completion.chunk",
632
- "created": int(time.time()),
633
- "model": "AkashGen",
634
- "choices": [{
635
- "delta": {"content": image_msg},
636
- "index": 0,
637
- "finish_reason": None
638
- }]
639
- })
640
- else:
641
- fail_msg = "\n\n*Image generation or upload failed.*"
642
- messages.append({
643
- "id": f"chatcmpl-{chat_id}-fail",
644
- "object": "chat.completion.chunk",
645
- "created": int(time.time()),
646
- "model": "AkashGen",
647
- "choices": [{
648
- "delta": {"content": fail_msg},
649
- "index": 0,
650
- "finish_reason": None
651
- }]
652
- })
653
-
654
- return messages
655
- return None
656
-
657
- async def upload_to_xinyew(image_base64: str, job_id: str) -> Optional[str]:
658
- """上传图片到新野图床并返回URL"""
659
- try:
660
- print(f"\n=== Starting image upload for job {job_id} ===")
661
- print(f"Base64 data length: {len(image_base64)}")
662
-
663
- # 解码base64图片数据
664
- try:
665
- image_data = base64.b64decode(image_base64.split(',')[1] if ',' in image_base64 else image_base64)
666
- print(f"Decoded image data length: {len(image_data)} bytes")
667
- except Exception as e:
668
- print(f"Error decoding base64: {e}")
669
- print(f"First 100 chars of base64: {image_base64[:100]}...")
670
- return None
671
-
672
- # 创建临时文件
673
- with tempfile.NamedTemporaryFile(suffix='.jpeg', delete=False) as temp_file:
674
- temp_file.write(image_data)
675
- temp_file_path = temp_file.name
676
-
677
- try:
678
- filename = f"{job_id}.jpeg"
679
- print(f"Using filename: {filename}")
680
-
681
- # 准备文件上传
682
- files = {
683
- 'file': (filename, open(temp_file_path, 'rb'), 'image/jpeg')
684
- }
685
-
686
- print("Sending request to xinyew.cn...")
687
- response = requests.post(
688
- 'https://api.xinyew.cn/api/jdtc',
689
- files=files,
690
- timeout=30
691
- )
692
-
693
- print(f"Upload response status: {response.status_code}")
694
- if response.status_code == 200:
695
- result = response.json()
696
- print(f"Upload response: {result}")
697
-
698
- if result.get('errno') == 0:
699
- url = result.get('data', {}).get('url')
700
- if url:
701
- print(f"Successfully got image URL: {url}")
702
- return url
703
- print("No URL in response data")
704
- else:
705
- print(f"Upload failed: {result.get('message')}")
706
- else:
707
- print(f"Upload failed with status {response.status_code}")
708
- print(f"Response content: {response.text}")
709
- return None
710
-
711
- finally:
712
- # 清理临时文件
713
- try:
714
- os.unlink(temp_file_path)
715
- except Exception as e:
716
- print(f"Error removing temp file: {e}")
717
-
718
- except Exception as e:
719
- print(f"Error in upload_to_xinyew: {e}")
720
- import traceback
721
- print(traceback.format_exc())
722
- return None
723
-
724
- if __name__ == '__main__':
725
- import uvicorn
726
- uvicorn.run(app, host='0.0.0.0', port=9000)
 
1
+ FROM python:3.11-slim
2
+
3
+ # 设置为中国国内源(针对 Bookworm/Debian 12)
4
+ RUN rm -rf /etc/apt/sources.list.d/* && \
5
+ echo "deb http://mirrors.ustc.edu.cn/debian bookworm main" > /etc/apt/sources.list && \
6
+ echo "deb http://mirrors.ustc.edu.cn/debian bookworm-updates main" >> /etc/apt/sources.list && \
7
+ echo "deb http://mirrors.ustc.edu.cn/debian-security bookworm-security main" >> /etc/apt/sources.list
8
+
9
+ WORKDIR /app
10
+
11
+ # Install system dependencies
12
+ RUN apt-get update && apt-get install -y \
13
+ build-essential \
14
+ curl \
15
+ libcurl4-openssl-dev \
16
+ libssl-dev \
17
+ # Chrome dependencies
18
+ wget \
19
+ gnupg \
20
+ fonts-liberation \
21
+ libasound2 \
22
+ libatk-bridge2.0-0 \
23
+ libatk1.0-0 \
24
+ libatspi2.0-0 \
25
+ libcups2 \
26
+ libdbus-1-3 \
27
+ libdrm2 \
28
+ libgbm1 \
29
+ libgtk-3-0 \
30
+ libnspr4 \
31
+ libnss3 \
32
+ libxcomposite1 \
33
+ libxdamage1 \
34
+ libxfixes3 \
35
+ libxkbcommon0 \
36
+ libxrandr2 \
37
+ xdg-utils \
38
+ # Install Chrome
39
+ && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
40
+ && echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \
41
+ && apt-get update \
42
+ && apt-get install -y google-chrome-stable \
43
+ && rm -rf /var/lib/apt/lists/*
44
+
45
+ # Copy requirements first to leverage Docker cache
46
+ COPY requirements.txt .
47
+ RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
48
+
49
+ # Copy the rest of the application
50
+ COPY . .
51
+
52
+ # Expose the port the app runs on
53
+ EXPOSE 7860
54
+
55
+ # Environment variables with defaults
56
+ ENV OPENAI_API_KEY=None
57
+ ENV ENVIRONMENT="production"
58
+ ENV PORT=7860
59
+
60
+ # Command to run the application
61
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]