Skip to content

Commit bc3a3a2

Browse files
committed
feat: add DeepSeek integration, Docker, Terraform IaC, and GitHub Actions
- DeepSeek API integration (llm.py) replacing rule engine - Dockerfile for provider service - Terraform IaC for OSS + FaaS - GitHub Actions workflow for Docker build & push - Deployment scripts for studio and provider - OSS credentials use environment variables
1 parent 22783e6 commit bc3a3a2

14 files changed

Lines changed: 533 additions & 90 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Build and Push Docker Image
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'src/provider/**'
9+
10+
jobs:
11+
build-and-push:
12+
runs-on: ubuntu-latest
13+
defaults:
14+
run:
15+
working-directory: src/provider
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Docker Buildx
22+
uses: docker/setup-buildx-action@v3
23+
24+
- name: Login to Alibaba Cloud Container Registry
25+
uses: docker/login-action@v3
26+
with:
27+
registry: crpi-uorshhk4a32pmmio.cn-hangzhou.personal.cr.aliyuncs.com
28+
username: ${{ secrets.ALIYUN_REGISTRY_USERNAME }}
29+
password: ${{ secrets.ALIYUN_REGISTRY_PASSWORD }}
30+
31+
- name: Build and push
32+
uses: docker/build-push-action@v5
33+
with:
34+
context: src/provider
35+
push: true
36+
tags: |
37+
crpi-uorshhk4a32pmmio.cn-hangzhou.personal.cr.aliyuncs.com/quanttide/qtcloud-write-provider:latest
38+
crpi-uorshhk4a32pmmio.cn-hangzhou.personal.cr.aliyuncs.com/quanttide/qtcloud-write-provider:${{ github.sha }}

manifests/iac/main.tf

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
terraform {
2+
required_version = ">= 1.6.0"
3+
4+
required_providers {
5+
alicloud = {
6+
source = "hashicorp/alicloud"
7+
version = "~> 1.230.0"
8+
}
9+
}
10+
11+
backend "oss" {
12+
bucket = "qtcloud-write-terraform-state"
13+
prefix = "state"
14+
region = "cn-hangzhou"
15+
encrypt = true
16+
}
17+
}
18+
19+
provider "alicloud" {
20+
region = var.region
21+
}
22+
23+
module "oss" {
24+
source = "./modules/oss"
25+
26+
bucket_name = "qtcloud-write-studio"
27+
region = var.region
28+
}
29+
30+
module "fc" {
31+
source = "./modules/fc"
32+
33+
service_name = "qtcloud-write"
34+
function_name = "provider"
35+
region = var.region
36+
}

manifests/iac/modules/fc/main.tf

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# 函数计算模块
2+
variable "service_name" {
3+
type = string
4+
default = "qtcloud-write"
5+
}
6+
7+
variable "function_name" {
8+
type = string
9+
default = "provider"
10+
}
11+
12+
variable "region" {
13+
type = string
14+
}
15+
16+
resource "alicloud_fc_service" "this" {
17+
service_name = var.service_name
18+
}
19+
20+
resource "alicloud_fc_function" "this" {
21+
service_name = alicloud_fc_service.this.service_name
22+
function_name = var.function_name
23+
runtime = "custom-container"
24+
handler = "main.handler"
25+
memory_size = 512
26+
timeout = 60
27+
28+
container {
29+
image = "crpi-uorshhk4a32pmmio.cn-hangzhou.personal.cr.aliyuncs.com/quanttide/qtcloud-write-provider:latest"
30+
}
31+
}
32+
33+
resource "alicloud_fc_trigger" "http_trigger" {
34+
service_name = alicloud_fc_service.this.service_name
35+
function_name = alicloud_fc_function.this.function_name
36+
trigger_name = "http-trigger"
37+
trigger_type = "http"
38+
invocation_role = alicloud_ram_role.this.arn
39+
40+
trigger_config = {
41+
authType = "anonymous"
42+
methods = ["GET", "POST", "PUT", "DELETE"]
43+
}
44+
}
45+
46+
resource "alicloud_ram_role" "this" {
47+
name = "${var.service_name}-fc-role"
48+
49+
assume_role_policy = jsonencode({
50+
Version = "1"
51+
Statement = [
52+
{
53+
Action = "sts:AssumeRole"
54+
Effect = "Allow"
55+
Principal = {
56+
Service = ["fc.aliyuncs.com"]
57+
}
58+
}
59+
]
60+
})
61+
}
62+
63+
resource "alicloud_ram_role_policy_attachment" "this" {
64+
role_name = alicloud_ram_role.this.name
65+
policy_name = "AliyunFCFullAccess"
66+
policy_type = "System"
67+
}
68+
69+
output "service_name" {
70+
value = alicloud_fc_service.this.service_name
71+
}
72+
73+
output "function_name" {
74+
value = alicloud_fc_function.this.function_name
75+
}
76+
77+
output "invoke_url" {
78+
value = "https://${alicloud_fc_service.this.service_name}.${var.region}.fc.aliyuncs.com/2016-08-15/proxy/${alicloud_fc_function.this.function_name}/"
79+
}

manifests/iac/modules/oss/main.tf

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# OSS 模块 - 存储 Flutter Web 构建产物
2+
variable "bucket_name" {
3+
type = string
4+
default = "qtcloud-write-studio"
5+
}
6+
7+
variable "region" {
8+
type = string
9+
}
10+
11+
resource "alicloud_oss_bucket" "this" {
12+
bucket = var.bucket_name
13+
acl = "public-read"
14+
15+
lifecycle_rule {
16+
name = "default"
17+
enabled = "true"
18+
precedence = 0
19+
}
20+
}
21+
22+
resource "alicloud_oss_bucket_cors" "this" {
23+
bucket = alicloud_oss_bucket.this.id
24+
25+
rule {
26+
allowed_origins = ["*"]
27+
allowed_methods = ["GET", "HEAD"]
28+
allowed_headers = ["*"]
29+
max_age_seconds = 3600
30+
}
31+
}
32+
33+
output "bucket_name" {
34+
value = alicloud_oss_bucket.this.bucket
35+
}
36+
37+
output "endpoint" {
38+
value = "https://${alicloud_oss_bucket.this.bucket}.oss-${var.region}.aliyuncs.com"
39+
}

manifests/iac/variables.tf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
variable "region" {
2+
description = "阿里云区域"
3+
type = string
4+
default = "cn-hangzhou"
5+
}
6+
7+
variable "environment" {
8+
description = "环境名称"
9+
type = string
10+
default = "prod"
11+
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "qtcloud-write"
33
version = "0.1.0-alpha.1"
44
requires-python = ">=3.11"
5-
dependencies = ["fastapi", "uvicorn"]
5+
dependencies = ["fastapi", "uvicorn", "openai", "pydantic", "pydantic-settings"]
66

77
[tool.uv.workspace]
88
members = ["provider"]

scripts/deploy-provider.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
# 构建并部署 qtcloud-write-provider 到阿里云 FaaS
3+
4+
set -euo pipefall
5+
6+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
8+
PROVIDER_DIR="$PROJECT_ROOT/src/provider"
9+
IMAGE_NAME="qtcloud-write-provider"
10+
REGISTRY="crpi-uorshhk4a32pmmio.cn-hangzhou.personal.cr.aliyuncs.com"
11+
12+
echo "=== 1. 构建 Docker 镜像 ==="
13+
cd "$PROVIDER_DIR"
14+
docker build -t "$IMAGE_NAME:latest" .
15+
16+
echo ""
17+
echo "=== 2. 推送到阿里云容器镜像服务 ==="
18+
docker tag "$IMAGE_NAME:latest" "$REGISTRY/quanttide/$IMAGE_NAME:latest"
19+
docker push "$REGISTRY/quanttide/$IMAGE_NAME:latest"
20+
21+
echo ""
22+
echo "=== 完成 ==="
23+
echo "镜像已推送,请登录阿里云函数计算控制台创建函数"
24+
echo "镜像地址: $REGISTRY/quanttide/$IMAGE_NAME:latest"

scripts/deploy-studio.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env bash
2+
# 构建 Flutter Web 并上传到 OSS
3+
4+
set -euo pipefall
5+
6+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
8+
STUDIO_DIR="$PROJECT_ROOT/src/studio"
9+
10+
echo "=== 1. 构建 Flutter Web ==="
11+
cd "$STUDIO_DIR"
12+
flutter build web --release
13+
14+
echo ""
15+
echo "=== 2. 上传到 OSS ==="
16+
python3 "$SCRIPT_DIR/upload-studio-oss.py"
17+
18+
echo ""
19+
echo "=== 完成 ==="
20+
echo "访问: https://qtcloud-write-studio.oss-cn-hangzhou.aliyuncs.com"

scripts/upload-studio-oss.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
"""上传 qtcloud-write Flutter Web 构建产物到阿里云 OSS"""
3+
4+
import oss2
5+
import os
6+
from pathlib import Path
7+
8+
# 阿里云配置
9+
ACCESS_KEY_ID = os.environ.get("OSS_ACCESS_KEY_ID", "")
10+
ACCESS_KEY_SECRET = os.environ.get("OSS_ACCESS_KEY_SECRET", "")
11+
BUCKET_NAME = "qtcloud-write"
12+
REGION = "cn-hangzhou"
13+
ENDPOINT = f"oss-{REGION}.aliyuncs.com"
14+
15+
# 本地 build 目录
16+
BUILD_DIR = Path(__file__).parent.parent / "src" / "studio" / "build" / "web"
17+
18+
19+
def upload_file(bucket, local_path: Path, remote_path: str):
20+
"""上传单个文件"""
21+
with open(local_path, "rb") as f:
22+
content = f.read()
23+
24+
result = bucket.put_object(remote_path, content)
25+
if result.status == 200:
26+
print(f" OK {remote_path}")
27+
else:
28+
print(f" FAIL {remote_path} (status={result.status})")
29+
return result.status == 200
30+
31+
32+
def main():
33+
print(f"Connecting OSS: {BUCKET_NAME} ({ENDPOINT})...")
34+
35+
auth = oss2.Auth(ACCESS_KEY_ID, ACCESS_KEY_SECRET)
36+
bucket = oss2.Bucket(auth, ENDPOINT, BUCKET_NAME)
37+
38+
if not BUILD_DIR.exists():
39+
print(f"错误: build 目录不存在: {BUILD_DIR}")
40+
print("请先运行: cd src/studio && flutter build web --release")
41+
return
42+
43+
print(f"Uploading from: {BUILD_DIR}")
44+
print("-" * 50)
45+
46+
success = 0
47+
failed = 0
48+
49+
for root, dirs, files in os.walk(BUILD_DIR):
50+
root_path = Path(root)
51+
52+
for filename in files:
53+
local_path = root_path / filename
54+
relative = local_path.relative_to(BUILD_DIR)
55+
remote_path = str(relative).replace("\\", "/")
56+
57+
if upload_file(bucket, local_path, remote_path):
58+
success += 1
59+
else:
60+
failed += 1
61+
62+
print("-" * 50)
63+
print(f"完成: {success} 成功, {failed} 失败")
64+
print(f"访问: https://{BUCKET_NAME}.{ENDPOINT}")
65+
66+
67+
if __name__ == "__main__":
68+
main()

src/provider/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
llm_api_key=your_deepseek_api_key_here
2+
llm_base_url=https://api.deepseek.com

0 commit comments

Comments
 (0)