Initial commit - DesignerCEP Project with Caddy deployment

This commit is contained in:
zuowei1216
2025-12-19 21:27:17 +08:00
commit 8ea58fe480
170 changed files with 47469 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
# DesignerCEP 认证模块接口文档
本文档描述了后端新增的邮箱注册验证与密码重置相关接口。
## 1. 注册 (Register)
支持传入邮箱进行注册。如果传入邮箱,系统将发送验证码邮件。
- **URL**: `/api/v1/auth/register`
- **Method**: `POST`
- **Request Body**:
```json
{
"username": "user1",
"password": "password123",
"confirm_password": "password123",
"email": "user1@gmail.com", // [新增] 可选,推荐填写
"device_id": "device_unique_id"
}
```
- **Response**:
- Success (200):
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsIn...",
"token_type": "bearer",
"username": "user1"
}
```
- Error (400): 用户名已存在 / 邮箱已存在 / 密码不一致
## 2. 验证邮箱 (Verify Email)
用户收到验证码邮件后,在前端输入验证码进行验证。
- **URL**: `/api/v1/auth/verify-email`
- **Method**: `POST`
- **Request Body**:
```json
{
"username": "user1",
"code": "123456" // 邮件中的6位数字验证码
}
```
- **Response**:
- Success (200):
```json
{
"detail": "验证成功"
}
```
- Error (400): 验证码错误
## 3. 忘记密码 (Forgot Password)
用户输入注册邮箱,请求重置密码。
- **URL**: `/api/v1/auth/forgot-password`
- **Method**: `POST`
- **Request Body**:
```json
{
"email": "user1@gmail.com"
}
```
- **Response**:
- Success (200):
```json
{
"detail": "如果邮箱存在,重置邮件已发送"
}
```
## 4. 重置密码 (Reset Password)
用户点击邮件中的链接或手动输入Token设置新密码。
- **URL**: `/api/v1/auth/reset-password`
- **Method**: `POST`
- **Request Body**:
```json
{
"token": "reset_token_from_email",
"new_password": "new_password123",
"confirm_password": "new_password123"
}
```
- **Response**:
- Success (200):
```json
{
"detail": "密码重置成功"
}
```
- Error (400): Token 无效 / Token 已过期 / 密码不一致
## 流程说明
1. **注册流程**:
- 用户填写注册信息(含邮箱)。
- 提交 `/register`。
- 如果成功,后端自动登录并返回 Token。
- 如果填写了邮箱,后端会发送一封验证邮件。
- 前端提示用户查收邮件并输入验证码。
- 用户输入验证码,前端调用 `/verify-email`。
2. **找回密码流程**:
- 用户点击“忘记密码”。
- 输入邮箱,前端调用 `/forgot-password`。
- 用户收到邮件,包含重置 Token。
- 前端提供重置密码界面,用户输入新密码。
- 前端调用 `/reset-password`(带上 Token 和新密码)。
- 重置成功后,跳转至登录页。

View File

@@ -0,0 +1,339 @@
# API Key 使用指南
## ✅ 已启用 API Key 验证
现在所有对 `/api/v1/jsx_demo/calculate` 的请求都需要提供有效的 API Key。
---
## 🔑 当前可用的 API Keys
### 1. 测试密钥(开发使用)
```
API Key: demo_key_123
名称: 测试密钥
权限: calculate
限制: 100次/小时
```
### 2. 生产密钥(生产环境)
```
API Key: prod_key_xyz789abc
名称: 生产密钥
权限: calculate, admin
限制: 1000次/小时
```
---
## 📝 日志示例
启用 API Key 后,后端日志会显示:
```
============================================================
📥 收到计算请求
时间: 2024-12-16 15:30:45
表达式: 87-98
API Key: demo_key_123
============================================================
✅ API Key 验证通过 | 名称: 测试密钥 | 权限: ['calculate']
🛡️ 安全检查: 验证表达式格式...
✅ 表达式格式验证通过
🔒 开始执行核心算法...
✅ 计算完成: 87-98 = -11
============================================================
```
### 如果 API Key 无效:
```
============================================================
📥 收到计算请求
时间: 2024-12-16 15:30:45
表达式: 87-98
API Key: invalid_key_xxx
============================================================
❌ API Key 验证失败: invalid_key_xxx
```
**前端会收到:** `403 Forbidden: 无效的 API Key`
---
## 🔧 管理 API Keys
### 查看所有 Keys
打开 `Server/app/core/api_keys.py`
```python
VALID_KEYS: Dict[str, dict] = {
"demo_key_123": {
"name": "测试密钥",
"created": "2024-12-16",
"permissions": ["calculate"],
"rate_limit": 100
},
# ... 更多 keys
}
```
### 添加新的 API Key
**方法 1直接编辑配置文件**
`api_keys.py` 中添加:
```python
"your_new_key_456": {
"name": "客户A的密钥",
"created": "2024-12-16",
"permissions": ["calculate"],
"rate_limit": 200
}
```
**方法 2使用 Python 代码**
```python
from app.core.api_keys import APIKeyManager
# 添加新 Key
APIKeyManager.add_key(
api_key="customer_key_789",
name="客户B的密钥",
permissions=["calculate", "export"]
)
```
### 删除 API Key
```python
from app.core.api_keys import APIKeyManager
# 删除 Key
APIKeyManager.remove_key("old_key_123")
```
### 检查权限
```python
from app.core.api_keys import APIKeyManager
# 检查是否有权限
has_permission = APIKeyManager.check_permission("demo_key_123", "calculate")
```
---
## 🔒 前端配置
### 当前配置(已设置)
`Designer/src/api/jsxApi/inline/hybrid-demo.ts`
```typescript
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'demo_key_123' // 🔐 使用测试密钥
},
body: JSON.stringify({
expression: layerName
})
});
```
### 切换到生产密钥
修改 API Key
```typescript
'X-API-Key': 'prod_key_xyz789abc' // 使用生产密钥
```
### 从配置文件读取(推荐)
创建 `Designer/src/config/apiKeys.ts`
```typescript
export const API_KEYS = {
development: 'demo_key_123',
production: 'prod_key_xyz789abc'
};
// 根据环境自动选择
export const getCurrentApiKey = () => {
return import.meta.env.DEV
? API_KEYS.development
: API_KEYS.production;
};
```
然后在 `hybrid-demo.ts` 中使用:
```typescript
import { getCurrentApiKey } from '@/config/apiKeys';
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate`, {
headers: {
'X-API-Key': getCurrentApiKey() // 自动选择正确的 Key
}
});
```
---
## 🧪 测试 API Key 验证
### 测试 1使用有效的 Key
1. 创建图层名称:`87-98`
2. 点击"智能配色"
3. **预期结果:** ✅ 成功计算
**后端日志:**
```
✅ API Key 验证通过 | 名称: 测试密钥
```
---
### 测试 2使用无效的 Key
临时修改前端,使用错误的 Key
```typescript
'X-API-Key': 'wrong_key_xxx'
```
**预期结果:** ❌ 403 错误
**后端日志:**
```
❌ API Key 验证失败: wrong_key_xxx
```
**前端错误:**
```
Message.error('无效的 API Key')
```
---
### 测试 3不提供 Key
注释掉 API Key
```typescript
// 'X-API-Key': 'demo_key_123'
```
**预期结果:** ❌ 403 错误
**后端日志:**
```
❌ API Key 验证失败: None
```
---
## 🛡️ 安全最佳实践
### ✅ 应该做的
1. **不同环境使用不同的 Key**
- 开发环境:`demo_key_123`
- 生产环境:`prod_key_xyz789abc`
2. **定期更换 Key**
```python
# 每季度更新一次
"new_key_2024_q1": {...}
```
3. **使用环境变量**
```python
import os
API_KEY = os.getenv('DESIGNER_API_KEY', 'demo_key_123')
```
4. **记录所有请求**
- 已实现:所有请求都会记录 API Key
5. **限制调用频率**
- 已配置:每个 Key 都有 rate_limit
### ❌ 不应该做的
1. ❌ 把 Key 硬编码在公开的代码中
2. ❌ 在 Git 中提交包含生产 Key 的文件
3. ❌ 多个客户共用同一个 Key
4. ❌ 永远不更换 Key
---
## 🚀 进一步加强
### 1. 数据库存储 API Keys
```python
# models/api_key.py
class APIKey(Base):
__tablename__ = "api_keys"
key = Column(String, primary_key=True)
name = Column(String)
permissions = Column(JSON)
rate_limit = Column(Integer)
created_at = Column(DateTime)
expires_at = Column(DateTime)
is_active = Column(Boolean, default=True)
```
### 2. Key 过期时间
```python
"demo_key_123": {
"expires": "2025-12-31", # Key 会过期
}
```
### 3. 使用量统计
```python
"demo_key_123": {
"usage_count": 0,
"last_used": None,
}
```
### 4. IP 绑定
```python
"demo_key_123": {
"allowed_ips": ["192.168.1.100", "127.0.0.1"]
}
```
---
## 📊 总结
| 功能 | 状态 | 说明 |
|------|------|------|
| API Key 验证 | ✅ 已启用 | 所有请求必须提供有效 Key |
| 多 Key 支持 | ✅ 已实现 | 可配置多个不同权限的 Key |
| 权限控制 | ✅ 已实现 | 每个 Key 可配置不同权限 |
| 日志记录 | ✅ 已实现 | 记录所有 Key 使用情况 |
| Key 管理器 | ✅ 已实现 | 提供增删改查 API |
| 限流配置 | 🔧 已配置 | 待实现实际限流逻辑 |
| 数据库存储 | ⚠️ 未实现 | 当前使用配置文件 |
| Key 过期 | ⚠️ 未实现 | 可以扩展 |
---
**当前 API Key 已启用!所有请求都会被验证和记录!**

View File

@@ -0,0 +1,820 @@
# 🚀 DesignerCEP Caddy 部署指南
## 为什么选择 Caddy
**自动 HTTPS**:无需手动申请证书,自动从 Let's Encrypt 获取并续期
**配置简单**:比 Nginx 简单 10 倍,一看就懂
**开箱即用**:自动处理 HTTP/2, GZIP 压缩
**完美支持 Cloudflare**:自动识别并配置
---
## 📦 1. 安装 Caddy
### Ubuntu/Debian
```bash
# 安装依赖
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
# 添加 Caddy 官方仓库
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# 更新并安装
sudo apt update
sudo apt install caddy
# 验证安装
caddy version
```
### CentOS/RHEL
```bash
# 添加 Caddy 官方仓库
dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy
# 启动服务
sudo systemctl enable caddy
sudo systemctl start caddy
```
---
## 📁 2. 准备项目目录
```bash
# 创建项目目录
sudo mkdir -p /var/www/DesignerCEP/Server/static/{shell,core,downloads}
# 设置权限
sudo chown -R $USER:$USER /var/www/DesignerCEP
chmod -R 755 /var/www/DesignerCEP
```
---
## ⚙️ 3. 配置 Caddy
### 方案 A: 标准部署Let's Encrypt 自动证书)
**适用于**:独立域名,不使用 Cloudflare 代理
```bash
# 创建 Caddyfile
sudo nano /etc/caddy/Caddyfile
```
```caddy
# /etc/caddy/Caddyfile
# ========== 主站配置 ==========
your-domain.com, www.your-domain.com {
# ✅ Caddy 会自动处理 HTTPS 证书
# ========== API 请求 → FastAPI ==========
handle /api/* {
reverse_proxy localhost:8000 {
# 传递真实 IP
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# ========== Shell 在线登录页 ==========
handle /shell/* {
root * /var/www/DesignerCEP/Server/static/shell
try_files {path} {path}/ /shell/index.html
file_server
# HTML 不缓存
@html {
path *.html
}
header @html Cache-Control "no-cache, no-store, must-revalidate"
# JS/CSS 长期缓存
@assets {
path *.js *.css
}
header @assets Cache-Control "public, max-age=31536000, immutable"
}
# ========== Core 核心应用 ==========
handle /core/* {
root * /var/www/DesignerCEP/Server/static/core
file_server
# HTML 不缓存
@html {
path *.html
}
header @html Cache-Control "no-cache, no-store, must-revalidate"
# JS/CSS 长期缓存
@assets {
path *.js *.css
}
header @assets Cache-Control "public, max-age=31536000, immutable"
}
# ========== 下载文件 ==========
handle /downloads/* {
root * /var/www/DesignerCEP/Server/static/downloads
file_server
# 启用断点续传
header Accept-Ranges bytes
# 缓存 1 天
header Cache-Control "public, max-age=86400"
}
# ========== 根路径重定向 ==========
handle / {
redir /shell/ permanent
}
# ========== 通用配置 ==========
# 自动 GZIP 压缩
encode gzip zstd
# 安全头
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
-Server # 隐藏服务器信息
}
# 日志
log {
output file /var/log/caddy/designer-cep.log {
roll_size 100mb
roll_keep 10
}
}
}
```
### 方案 B: Cloudflare 代理部署(推荐)
**适用于**:域名使用 Cloudflare 橙云代理
```bash
sudo nano /etc/caddy/Caddyfile
```
```caddy
# /etc/caddy/Caddyfile (Cloudflare 版本)
{
# ✅ 关闭自动 HTTPSCloudflare 已经提供)
auto_https off
# 或者使用内部证书
# auto_https disable_redirects
}
# ========== HTTP 配置Cloudflare 会加 HTTPS==========
http://your-domain.com, http://www.your-domain.com {
# ========== API 请求 → FastAPI ==========
handle /api/* {
reverse_proxy localhost:8000 {
# ✅ 从 Cloudflare 获取真实 IP
header_up X-Real-IP {header.CF-Connecting-IP}
header_up X-Forwarded-For {header.CF-Connecting-IP}
header_up X-Forwarded-Proto https
}
}
# ========== Shell 在线登录页 ==========
handle /shell/* {
root * /var/www/DesignerCEP/Server/static/shell
try_files {path} {path}/ /shell/index.html
file_server
@html path *.html
header @html Cache-Control "no-cache, no-store, must-revalidate"
@assets path *.js *.css
header @assets Cache-Control "public, max-age=31536000, immutable"
}
# ========== Core 核心应用 ==========
handle /core/* {
root * /var/www/DesignerCEP/Server/static/core
file_server
@html path *.html
header @html Cache-Control "no-cache, no-store, must-revalidate"
@assets path *.js *.css
header @assets Cache-Control "public, max-age=31536000, immutable"
}
# ========== 下载文件 ==========
handle /downloads/* {
root * /var/www/DesignerCEP/Server/static/downloads
file_server
header Accept-Ranges bytes
header Cache-Control "public, max-age=86400"
}
# ========== 根路径重定向 ==========
handle / {
redir /shell/ permanent
}
# ========== 通用配置 ==========
encode gzip zstd
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
-Server
}
log {
output file /var/log/caddy/designer-cep.log {
roll_size 100mb
roll_keep 10
}
}
}
```
---
## 🔧 4. 修改后端代码(支持 CEP 环境)
### 4.1 修改 FastAPI 的 CORS 配置
```python
# Server/app/main.py
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
import os
app = FastAPI(title=settings.PROJECT_NAME)
# 环境判断
IS_DEV = os.getenv("ENV", "development") == "development"
# ========== CORS 配置 ==========
if IS_DEV:
# 开发环境:宽松配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
else:
# 生产环境:严格配置
allowed_origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# ✅ 特殊处理 CEP 环境Origin: null
@app.middleware("http")
async def cep_cors_middleware(request: Request, call_next):
origin = request.headers.get("origin")
# CEP 环境的 Origin 是 null 或 cep://
if origin in ["null", None] or (origin and origin.startswith("cep://")):
response = await call_next(request)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "*"
return response
return await call_next(request)
# ========== API 路由(保持不变)==========
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["authentication"])
app.include_router(client.router, prefix=f"{settings.API_V1_STR}/client", tags=["client"])
app.include_router(admin.router, prefix=f"{settings.API_V1_STR}/admin", tags=["admin"])
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
app.include_router(jsx_demo.router, prefix=f"{settings.API_V1_STR}/jsx_demo", tags=["jsx_demo"])
# ❌ 删除静态文件挂载(交给 Caddy 处理)
# app.mount("/download", ...) # 删除
# app.mount("/shell", ...) # 删除
# app.mount("/core", ...) # 删除
@app.get("/")
def read_root():
return {"message": "DesignerCEP API Server", "version": "1.0.0"}
@app.get("/health")
def health_check():
return {"status": "healthy"}
@app.on_event("startup")
def on_startup():
init_db()
```
### 4.2 配置环境变量
```bash
# Server/.env
ENV=production
PROJECT_NAME=DesignerCEP
API_V1_STR=/api/v1
SECRET_KEY=your-secret-key-here
DATABASE_URL=mysql://user:password@localhost:3306/designer_cep
# 生产环境允许的来源
ALLOWED_ORIGINS=https://your-domain.com,https://www.your-domain.com
# 是否由 FastAPI 提供静态文件(生产环境设为 false
SERVE_STATIC=false
```
---
## 🚀 5. 部署 FastAPI 服务
### 5.1 使用 Systemd 管理服务
```bash
# 创建服务文件
sudo nano /etc/systemd/system/designer-cep.service
```
```ini
[Unit]
Description=DesignerCEP FastAPI Application
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/DesignerCEP/Server
Environment="PATH=/var/www/DesignerCEP/Server/venv/bin"
ExecStart=/var/www/DesignerCEP/Server/venv/bin/gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8000
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
### 5.2 安装依赖并启动
```bash
# 1. 创建虚拟环境
cd /var/www/DesignerCEP/Server
python3 -m venv venv
source venv/bin/activate
# 2. 安装依赖
pip install -r requirements.txt
pip install gunicorn uvicorn[standard]
# 3. 测试运行
uvicorn app.main:app --host 127.0.0.1 --port 8000
# 4. 启动服务
sudo systemctl daemon-reload
sudo systemctl enable designer-cep
sudo systemctl start designer-cep
# 5. 检查状态
sudo systemctl status designer-cep
```
---
## 📤 6. 部署前端文件
### 6.1 配置前端环境
```bash
# Designer/.env.production
VITE_API_SERVER=https://your-domain.com
```
### 6.2 构建前端
```bash
cd Designer
npm install
npm run build
```
### 6.3 上传到服务器
#### 方法 A: 手动上传
```bash
# 在本地执行
cd Designer/dist
# 上传 Shell
scp -r Shell/* user@your-server:/var/www/DesignerCEP/Server/static/shell/
# 上传 Core
scp -r Designer/* user@your-server:/var/www/DesignerCEP/Server/static/core/1.0.6/
# 打包并上传 Shell.zip
zip -r shell-1.0.6.zip Shell/
scp shell-1.0.6.zip user@your-server:/var/www/DesignerCEP/Server/static/downloads/
```
#### 方法 B: 使用自动化脚本
```bash
# 在项目根目录执行
cd AdminTool
# 首次配置
python auto_deploy_core.py --version 1.0.6 --setup
# 输入配置信息:
# - 服务器地址: your-domain.com
# - SSH 端口: 22
# - 用户名: root
# - 密码: ******
# - 远程路径: /var/www/DesignerCEP/Server/static
# 部署
python auto_deploy_core.py --version 1.0.6 --deploy --update-db
```
---
## ✅ 7. 启动 Caddy
```bash
# 检查配置语法
sudo caddy validate --config /etc/caddy/Caddyfile
# 重启 Caddy
sudo systemctl restart caddy
# 查看状态
sudo systemctl status caddy
# 查看日志
sudo journalctl -u caddy -f
```
---
## 🧪 8. 测试部署
### 8.1 测试静态文件
```bash
# 测试 Shell
curl -I https://your-domain.com/shell/
# 期望输出:
# HTTP/2 200
# content-type: text/html
# 测试 Core
curl -I https://your-domain.com/core/1.0.6/
# 测试下载
curl -I https://your-domain.com/downloads/shell-1.0.6.zip
```
### 8.2 测试 API
```bash
# 测试健康检查
curl https://your-domain.com/api/v1/health
# 期望输出:
# {"status":"healthy"}
# 测试登录接口
curl -X POST https://your-domain.com/api/v1/client/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"test","device_id":"test-device"}'
```
### 8.3 测试 CEP CORS
```bash
# 模拟 CEP 环境的预检请求
curl -X OPTIONS https://your-domain.com/api/v1/client/login \
-H "Origin: null" \
-H "Access-Control-Request-Method: POST" \
-v
# 期望输出:
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
```
### 8.4 浏览器测试
1. 打开 `https://your-domain.com/shell/`
2. 输入用户名密码登录
3. 按 F12 打开开发者工具
4. 查看 Network 标签:
- ✅ 所有请求都是 HTTPS
- ✅ API 请求有 `Authorization: Bearer xxx`
- ✅ 没有 CORS 错误
---
## 🔍 9. 常见问题
### Q1: Caddy 自动证书失败
**原因**:域名 DNS 没有解析到服务器
**解决**
```bash
# 检查 DNS 解析
nslookup your-domain.com
# 确保 A 记录指向服务器 IP
dig your-domain.com
```
### Q2: API 请求 502 Bad Gateway
**原因**FastAPI 服务没有启动
**解决**
```bash
# 检查 FastAPI 状态
sudo systemctl status designer-cep
# 查看日志
sudo journalctl -u designer-cep -f
# 重启服务
sudo systemctl restart designer-cep
```
### Q3: 静态文件 404
**原因**:文件路径不对
**解决**
```bash
# 检查文件是否存在
ls -la /var/www/DesignerCEP/Server/static/shell/
ls -la /var/www/DesignerCEP/Server/static/core/1.0.6/
# 检查权限
sudo chown -R www-data:www-data /var/www/DesignerCEP
sudo chmod -R 755 /var/www/DesignerCEP
```
### Q4: Cloudflare 无限重定向
**原因**Cloudflare SSL 模式设置错误
**解决**
1. 进入 Cloudflare 控制台
2. SSL/TLS → Overview
3. 选择 **"Flexible"** 模式(因为 Caddy 配置的是 HTTP
或者修改 Caddyfile 使用 HTTPS
```caddy
# 方法 1: 自签名证书
your-domain.com {
tls internal
# ... 其他配置
}
# 方法 2: Cloudflare Origin Certificate
your-domain.com {
tls /etc/caddy/certs/cloudflare-origin.pem /etc/caddy/certs/cloudflare-origin.key
# ... 其他配置
}
```
---
## 🎯 10. Cloudflare 最佳实践
### 10.1 SSL/TLS 配置
```
Cloudflare 控制台 → SSL/TLS → Overview
选择模式:
- ❌ Off - 不使用 HTTPS
- ✅ Flexible - Cloudflare ←HTTPS→ 用户Cloudflare ←HTTP→ 源站(推荐简单部署)
- ⚠️ Full - 双向 HTTPS但不验证源站证书
- ✅ Full (Strict) - 双向 HTTPS验证证书推荐生产环境
```
**推荐方案**
#### Flexible 模式(最简单)
```caddy
# Caddyfile
{
auto_https off
}
http://your-domain.com {
# ... 配置
}
```
Cloudflare 设置:`Flexible`
#### Full (Strict) 模式(最安全)
1. 生成 Cloudflare Origin Certificate
```
Cloudflare 控制台 → SSL/TLS → Origin Server → Create Certificate
```
2. 下载证书并上传到服务器:
```bash
sudo mkdir -p /etc/caddy/certs
sudo nano /etc/caddy/certs/cloudflare-origin.pem # 粘贴证书
sudo nano /etc/caddy/certs/cloudflare-origin.key # 粘贴私钥
sudo chmod 600 /etc/caddy/certs/*
```
3. 修改 Caddyfile
```caddy
your-domain.com {
tls /etc/caddy/certs/cloudflare-origin.pem /etc/caddy/certs/cloudflare-origin.key
# ... 其他配置
}
```
4. Cloudflare 设置:`Full (Strict)`
### 10.2 性能优化
```
Cloudflare 控制台设置:
1. Speed → Optimization
- ✅ Auto Minify: HTML, CSS, JS
- ✅ Brotli 压缩
2. Caching → Configuration
- ✅ Caching Level: Standard
- ✅ Browser Cache TTL: Respect Existing Headers
3. Network
- ✅ HTTP/2
- ✅ HTTP/3 (QUIC)
```
---
## 📊 11. 监控和日志
### 查看 Caddy 日志
```bash
# 实时日志
sudo journalctl -u caddy -f
# 访问日志
sudo tail -f /var/log/caddy/designer-cep.log
# 错误日志
sudo journalctl -u caddy --since "1 hour ago" | grep -i error
```
### 查看 FastAPI 日志
```bash
# 实时日志
sudo journalctl -u designer-cep -f
# 最近的错误
sudo journalctl -u designer-cep --since "1 hour ago" | grep -i error
```
### 性能监控
```bash
# 检查服务器资源
htop
# 检查端口监听
sudo netstat -tlnp | grep -E '(8000|80|443)'
# 检查 Caddy 进程
ps aux | grep caddy
```
---
## 🔄 12. 更新部署
### 更新前端
```bash
# 方法 A: 手动
cd Designer
npm run build
scp -r dist/Shell/* user@server:/var/www/DesignerCEP/Server/static/shell/
scp -r dist/Designer/* user@server:/var/www/DesignerCEP/Server/static/core/1.0.7/
# 方法 B: 自动化
cd AdminTool
python auto_deploy_core.py --version 1.0.7 --deploy --update-db
```
### 更新后端
```bash
# 1. SSH 到服务器
ssh user@your-server
# 2. 拉取最新代码
cd /var/www/DesignerCEP/Server
git pull
# 3. 更新依赖
source venv/bin/activate
pip install -r requirements.txt
# 4. 重启服务
sudo systemctl restart designer-cep
# 5. 检查状态
sudo systemctl status designer-cep
```
### 更新 Caddy 配置
```bash
# 1. 修改配置
sudo nano /etc/caddy/Caddyfile
# 2. 验证配置
sudo caddy validate --config /etc/caddy/Caddyfile
# 3. 重新加载(不中断服务)
sudo systemctl reload caddy
```
---
## 🎉 完成!
访问地址:
- **Shell 登录页**`https://your-domain.com/shell/`
- **Core 应用**`https://your-domain.com/core/1.0.6/`
- **API 文档**`https://your-domain.com/api/v1/docs`
---
## 📋 部署检查清单
- [ ] Caddy 已安装并启动
- [ ] Caddyfile 配置正确
- [ ] FastAPI 服务运行正常
- [ ] 前端文件已上传
- [ ] 环境变量配置正确
- [ ] CORS 支持 CEP 环境
- [ ] MySQL 数据库已更新
- [ ] HTTPS 证书正常
- [ ] 静态文件缓存正确
- [ ] API 请求正常
- [ ] CEP 扩展测试通过
- [ ] 浏览器访问正常
---
**Caddy 的优势总结**
- ✅ 配置只有 Nginx 的 1/3 长度
- ✅ 自动 HTTPS无需 Certbot
- ✅ 自动续期证书
- ✅ 内置 GZIP/Brotli 压缩
- ✅ 配置更直观易懂
- ✅ 错误提示更友好
**下一步**:运行 `python auto_deploy_core.py --version 1.0.6 --deploy --update-db` 一键部署!

View File

@@ -0,0 +1,193 @@
# JSX 加载失败问题分析与解决方案
## 📋 问题描述
在 Shell + Core 架构分离后Core 应用中的 JSX 脚本无法正常运行,报错:
```
Error: EvalScript error.
```
尽管 JSX 文件路径找到了(`C:/Users/35780/AppData/Roaming/DesignerCache/v1.0.5/jsx/index.js`),但 `evalScript` 执行失败。
## 🔍 根本原因
### 问题1JSX 构建格式错误
**位置**`Designer/plugins/buildJsx/index.ts` 第 118 行
```typescript
await bundle.write({
file: output,
format: 'cjs' // ❌ 问题所在!
});
```
**影响**:生成的 JSX 文件使用了 **CommonJS 格式**,包含:
```javascript
Object.defineProperty(exports, "__esModule", { value: true });
var index_1 = require("./function/index"); // ❌ ExtendScript 不支持 require()
var ActionManager = require("./utils/ActionManager");
```
### 问题2ExtendScript 环境限制
**ExtendScript (Adobe JSX 引擎) 不支持:**
- ❌ CommonJS 的 `require()``exports`
- ❌ ES6 的 `import/export`
-`'use strict'` 严格模式
**ExtendScript 只支持:**
- ✅ 全局函数
- ✅ 立即执行函数表达式 (IIFE)
- ✅ ES3/ES5 语法
## ✅ 解决方案
### 方案 1修改构建配置长期方案
修改 `Designer/plugins/buildJsx/index.ts`
```typescript
await bundle.write({
file: output,
format: 'iife', // ✅ 改为 IIFE 格式
name: 'JSXBundle',
strict: false // ✅ 禁用严格模式
});
```
**同时修改 TypeScript 配置**
```typescript
typescript({
compilerOptions: {
target: 'es5',
strict: false,
moduleResolution: 'node',
esModuleInterop: true,
skipLibCheck: true,
// ...
},
tsconfig: false,
}),
```
### 方案 2临时修复已应用
**已完成的操作:**
1. ✅ 创建了简化版本的 JSX 文件 (`index_fixed.js`)
2. ✅ 移除了所有 CommonJS 语法
3. ✅ 将依赖项Logger, ActionManager内联到单文件中
4. ✅ 备份原文件为 `index.js.backup`
5. ✅ 替换为修复后的版本
**文件位置**
- 修复后:`Designer/dist_core/jsx/index.js`
- 备份:`Designer/dist_core/jsx/index.js.backup`
## 🧪 测试步骤
### 1. 清除客户端缓存
```powershell
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache" -ErrorAction SilentlyContinue
```
### 2. 重启插件
在 Photoshop 中:
1. 关闭当前的 DesignerCEP 插件
2. 重新打开插件
3. 登录账号
### 3. 检查控制台
打开浏览器开发者工具F12查看
**预期成功日志:**
```
[__LDX] Detected Core version v1.0.5
[__LDX] Success: C:/Users/35780/AppData/Roaming/DesignerCache/v1.0.5/jsx/index.js
宿主APP名称Adobe Photoshop 2024
```
**如果仍然失败,查看:**
- 是否有 `EvalScript error`
- ExtendScript 控制台的详细错误信息
### 4. 测试功能
在插件主界面点击"测试 JSX 调用"按钮,验证:
-`getAppName()` 返回 "Adobe Photoshop"
-`createLayer()` 可以创建新图层
-`testMergeLayers()` 可以合并图层
## 📝 后续工作
### 短期(紧急)
- [ ] 测试修复后的 JSX 是否正常工作
- [ ] 如果测试通过,将修复后的文件上传到服务器
### 中期(优化)
- [ ] 修复构建流程,确保未来构建自动生成正确格式
- [ ] 解决 `g_logger``ActionManager` 的依赖打包问题
- [ ] 更新 `deploy_core.py` 脚本,包含 JSX 构建步骤
### 长期(架构改进)
- [ ] 评估是否需要保留完整的 Logger 和 ActionManager
- [ ] 考虑使用 Rollup 的 `inlineDynamicImports` 选项
- [ ] 编写自动化测试验证 JSX 在 ExtendScript 环境中的兼容性
## 🔗 相关文件
| 文件路径 | 说明 |
|:---------|:-----|
| `Designer/plugins/buildJsx/index.ts` | JSX 构建配置已修改format为iife |
| `Designer/dist_core/jsx/index.js` | 修复后的 JSX 文件 |
| `Designer/dist_core/jsx/index.js.backup` | 原始的 CommonJS 格式备份 |
| `Designer/build_jsx_simple.mjs` | 简化的构建脚本(待完善) |
## 💡 技术要点
### ExtendScript 兼容性清单
```javascript
允许:
- var, function, 普通对象
- IIFE: (function(){ ... })();
- 全局赋值: $.global.xxx = function(){}
- ES5 方法: Array.forEach, Object.keys
禁止:
- 'use strict'
- require() / exports / module.exports
- import / export
- const / let (部分版本不支持)
- Promise / async/await ( polyfill)
- 箭头函数 (部分版本)
```
### Rollup 输出格式对比
| 格式 | ExtendScript | 说明 |
|:-----|:------------|:-----|
| `cjs` | ❌ | CommonJS使用 require/exports |
| `esm` | ❌ | ES Module使用 import/export |
| `iife` | ✅ | 立即执行函数,所有代码在一个作用域 |
| `umd` | ⚠️ | 通用模块,体积较大但兼容性好 |
## 📞 联系与支持
如果测试中遇到问题,请提供:
1. 浏览器控制台的完整错误日志
2. ExtendScript Toolkit 的错误信息(如果有)
3. Photoshop 版本和操作系统信息
---
**报告生成时间**2025-12-16
**问题状态**:✅ 已修复(待测试验证)

View File

@@ -0,0 +1,128 @@
# JSX 方法测试指南
## 🎯 目标
测试 5 种不同的 JSX 实现方法,找出在 ExtendScript 环境中能正常运行的方式。
## 📋 测试方法列表
| 方法 | 实现方式 | 特点 |
|:-----|:---------|:-----|
| **方法1** | DOM API | 最简单,直接使用 PS 的 DOM 对象 |
| **方法2** | ActionManager | 使用 ActionDescriptor更底层 |
| **方法3** | Object Helper | 使用对象字面量封装 |
| **方法4** | ES3 Class | 使用原型链的类实现 |
| **方法5** | PSApi Style | 模仿完整的 PSApi 结构 |
## 🧪 快速测试步骤
### 1. 替换 JSX 文件
```powershell
# 方式A直接复制到缓存推荐
Copy-Item Designer\test_jsx_methods.js "$env:APPDATA\DesignerCache\v1.0.7\jsx\index.js" -Force
# 方式B或者重新打包上传
# 如果方式A不行说明缓存路径不对
```
### 2. 在浏览器控制台测试
打开 Photoshop 插件,按 F12 打开控制台,逐个测试:
```javascript
// 测试方法1DOM API
cep.evalScript("createLayerMethod1('测试图层1')").then(r => console.log('方法1:', r))
// 测试方法2ActionManager
cep.evalScript("createLayerMethod2('测试图层2')").then(r => console.log('方法2:', r))
// 测试方法3Object Helper
cep.evalScript("createLayerMethod3('测试图层3')").then(r => console.log('方法3:', r))
// 测试方法4ES3 Class
cep.evalScript("createLayerMethod4('测试图层4')").then(r => console.log('方法4:', r))
// 测试方法5PSApi Style
cep.evalScript("createLayerMethod5('测试图层5')").then(r => console.log('方法5:', r))
// 测试获取图层数量
cep.evalScript("getLayerCount()").then(r => console.log('图层数量:', r))
```
### 3. 观察结果
**成功的结果示例:**
```json
{
"success": true,
"method": "DOM API",
"layerName": "测试图层1"
}
```
**失败的结果示例:**
```json
{
"error": "..."
}
```
## 📊 预期哪个会成功?
根据 ExtendScript 的特性,**最可能成功的是**
1.**方法1DOM API** - 最简单直接
2.**方法3Object Helper** - 使用对象字面量
3. ⚠️ **方法2ActionManager** - 可能因为 API 调用问题失败
4. ⚠️ **方法4ES3 Class** - 依赖原型链
5. ⚠️ **方法5PSApi Style** - 最复杂,可能有嵌套问题
## 🔧 如果都失败了
如果所有方法都失败,可能的原因:
1. **JSX 文件没有被加载**
- 检查控制台是否有 `Test JSX Methods Loaded` 日志
2. **路径不对**
- 检查 `[__LDX]` 日志显示的路径
- 手动找到该路径,确认文件内容
3. **ExtendScript 语法错误**
- 在 ExtendScript Toolkit 中打开文件检查
## 💡 成功后的下一步
找到能工作的方法后:
1. 📝 **记录成功的方法**(如"方法1成功"
2. 🔄 **使用该方法重写所有 JSX 函数**
3. 📦 **重新构建和打包 Core**
4. 🚀 **发布到服务器**
## 🎨 添加前端测试按钮(可选)
`Home.vue` 中添加测试按钮:
```vue
<a-button @click="testMethod1">测试方法1</a-button>
<a-button @click="testMethod2">测试方法2</a-button>
<a-button @click="testMethod3">测试方法3</a-button>
<a-button @click="testMethod4">测试方法4</a-button>
<a-button @click="testMethod5">测试方法5</a-button>
```
```typescript
const testMethod1 = async () => {
const res = await jsxApi.evalScript("createLayerMethod1('测试1')");
console.log('方法1结果:', res);
Message.info(res);
};
// ... 类似地添加其他方法
```
---
**现在开始测试吧!** 告诉我哪个方法能成功 🎯

View File

@@ -0,0 +1,212 @@
# PSApi 功能测试指南
## 📋 已完成的修改
### 1. JSX 端(`src/jsx/index.ts`
**添加的内容:**
```typescript
import { PSApi } from "./function/ps/api" // 导入 PSApi 类
const psApi = new PSApi() // 实例化 PSApi
// 新增测试函数
export function testPSApi() {
try {
const selectLength = psApi.layer.getSelectLength(); // 调用 PSApi 的方法
return JSON_EX.stringify({
success: true,
message: "PSApi 调用成功!",
selectLength: selectLength,
info: "当前选中的图层数量: " + selectLength
});
} catch (error: any) {
return JSON_EX.stringify({ error: error.toString() });
}
}
// 暴露到全局
($.global as any).testPSApi = testPSApi;
```
### 2. 前端 API`src/api/jsxApi/evalJSX.ts`
**添加的内容:**
```typescript
testPSApi: async () => {
const jsonStr = await cep.evalScript("testPSApi()");
const res = JSON.parse(jsonStr);
return res;
}
```
### 3. 前端界面(`src/view/Home.vue`
**添加的内容:**
- ✅ 新增"测试 PSApi"按钮
- ✅ 添加 `handleTestPSApi()` 处理函数
## 🧪 测试步骤
### 步骤 1重新构建如果使用简化版
如果您还在使用临时的简化版 JSX现在可以
**选项 A使用简化版测试推荐先测**
```powershell
# 当前的 dist_core/jsx/index.js 已经包含简化版
# 直接测试即可
```
**选项 B重新构建完整版包含 PSApi**
```powershell
cd D:\main\DesignerCEP\Designer
# 重新构建 Core会包含新的 testPSApi 函数)
npm run build:core
# 如果构建失败,需要先修复构建流程
# 或者手动更新 dist_core/jsx/index.js
```
### 步骤 2清除缓存
```powershell
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache" -ErrorAction SilentlyContinue
```
### 步骤 3在 Photoshop 中测试
1. **打开 Photoshop**
2. **打开 DesignerCEP 插件**
3. **登录账号**(会下载最新的 Core
4. **在主界面点击"测试 PSApi"按钮**
### 步骤 4观察结果
**预期成功的结果:**
**控制台日志:**
```
[__LDX] Detected Core version v1.0.5
[__LDX] Success: C:/Users/.../jsx/index.js
Call testPSApi - Testing PSApi from api.ts
```
**前端界面:**
- 弹出成功提示:`PSApi 调用成功! - 当前选中的图层数量: 1`
- 显示选中图层数(如果没选图层则为 0
**如果失败:**
- 查看浏览器控制台的错误信息
- 查看 ExtendScript Toolkit 的错误(如果有)
## 📊 测试场景
### 场景 1没有选中图层
- **操作**:不选择任何图层,点击"测试 PSApi"
- **预期**:显示"选中图层数: 0"
### 场景 2选中 1 个图层
- **操作**:在 PS 中选中 1 个图层,点击"测试 PSApi"
- **预期**:显示"选中图层数: 1"
### 场景 3选中多个图层
- **操作**:按住 Ctrl/Cmd 选中多个图层,点击"测试 PSApi"
- **预期**:显示"选中图层数: N"N 为实际选中数量)
## 🎯 测试的意义
### 如果测试成功,证明:
1.**可以在 JSX 中使用完整的 `api.ts`**
2.**PSApi 类的方法可以正常调用**
3.**面向对象的写法在 ExtendScript 中运行正常**
4.**可以放心使用 api.ts 中的所有功能**
### 如果测试成功,您可以:
```typescript
// 在 src/jsx/index.ts 中使用任何 PSApi 的功能
// 示例 1图层操作
export function selectLayerById(id: number) {
psApi.layer.onIdSelectLayer(id);
return JSON_EX.stringify({ success: true });
}
// 示例 2文档操作
export function getDocumentSize() {
const size = psApi.doc.getSize();
return JSON_EX.stringify({
width: size.cx,
height: size.cy
});
}
// 示例 3颜色操作
export function setFillColor(hex: string) {
const col = new SolidColor();
col.rgb.hexValue = hex;
psApi.activeLayer.shape.setFillColor(true, col);
return JSON_EX.stringify({ success: true });
}
// 示例 4字体操作
export function changeFontFamily(postScriptName: string, name: string, style: string) {
psApi.font.setFontName(postScriptName, name, style);
return JSON_EX.stringify({ success: true });
}
```
## 🚨 常见问题
### Q1: 点击按钮没有反应
**原因:** JSX 未加载或函数未暴露到全局
**解决:**
- 检查控制台是否有 `[__LDX] Success` 日志
- 确认 `testPSApi` 已添加到 `$.global`
### Q2: 报错 "PSApi is not defined"
**原因:** 构建时未包含 `api.ts`
**解决:**
- 确认 `src/jsx/index.ts` 中有 `import { PSApi } from "./function/ps/api"`
- 重新构建或检查构建配置
### Q3: 报错 "getSelectLength is not a function"
**原因:** PSApi 的方法没有正确编译
**解决:**
- 检查 `api.ts` 是否有语法错误
- 确认构建格式为 IIFE我们已修改
- 查看 `dist_core/jsx/index.js` 是否包含 PSApi 的代码
### Q4: 构建时报错 "g_logger is not exported"
**原因:** 模块打包问题(我们之前遇到的)
**解决:**
- 暂时使用简化版测试
- 或者手动修复构建流程
- 或者等待我们修复完整版构建
## 💡 下一步计划
### 如果测试通过:
1. ✅ 确认可以使用完整的 `api.ts`
2. 🔄 修复完整版的构建流程
3. 📦 重新打包包含所有功能的 Core
4. 🚀 上传到服务器供用户使用
### 如果测试失败:
1. 📝 记录详细的错误信息
2. 🔍 分析是构建问题还是代码问题
3. 🔧 逐步调试修复
---
**准备好了吗?开始测试吧!** 🚀
测试完成后,请告诉我:
- ✅ 测试成功还是失败
- 📋 控制台的完整日志
- 💬 遇到的任何问题或错误信息

View File

@@ -0,0 +1,83 @@
# Shell 架构改造任务清单 (增强版)
基于您提供的 `serveradmin``upload` 逻辑,我们引入了 **“用户分组 (Gray Release)”** 和 **“一键发布 (CI/CD)”** 机制。
## 1. 插件端 (Frontend - Shell)
Shell 端不需要太大变化,主要是版本检查接口需要携带用户信息。
- `src/launcher/utils/`
- [ ] **增强版版本检查**:
- 请求 `POST /api/check_update`
- 参数: `{ username: "当前登录用户" }`
- 逻辑: 服务端会根据用户所在的组,返回不同的版本。普通用户返回 `v1.0`,测试组用户返回 `v1.1-beta`
## 2. 服务端 (Backend - Python)
参考 `serveradmin` 逻辑进行改造。
### 2.1 数据库结构升级
需要支持不同用户获取不同版本。
- **表 `psmark_group` (用户组)**:
- `id`: int (主键)
- `name`: string (组名,如 "Stable", "Beta")
- `current_version`: string (该组当前使用的版本文件名,如 "release_v1.0.zip")
- `comment`: string
- **表 `user` (原有表增强)**:
- `group_id`: int (外键,关联 psmark_group)
### 2.2 接口逻辑增强
- `POST /api/check_update`:
1. 根据 `username``group_id`
2.`psmark_group` 表获取该组的 `current_version`
3. 返回该版本的下载地址。
- **优势**: 您可以在后台把某个测试用户切到 "DevGroup",他重启插件就会立刻下载最新的测试版,而其他用户不受影响。
---
## 3. 发布流程 (Publishing Workflow)
为了实现“上传时编译”,我们将编写一个自动化脚本。
### 3.1 自动化发布脚本 (`scripts/publish.ts`)
这个脚本将在您的开发机上运行,一键完成所有操作。
- **执行流程**:
1. **Compile**: 运行 `npm run build:core` (生成 `dist/core`)。
2. **Package**: 使用 `adm-zip``dist/core` 压缩为 `code_{timestamp}.zip`
3. **Upload**: 调用服务端接口 `POST /archives` 将 ZIP 上传。
4. **Register (可选)**: 自动调用接口将新版本注册到数据库(可选)。
- **命令**:
```bash
npm run publish # 一键发布
```
### 3.2 服务端支持
- 确保 `POST /archives` 接口能接收文件并保存到 `archives/` 目录 (参考 `server.py` 已有逻辑)。
---
## 4. 后台管理工具 (Admin Tool)
您可以直接复用或升级现有的 PyQt 工具 (`upload/admin.py`)。
- **功能需求**:
- [ ] **用户分理**: 设置用户的 `group_id`。
- [ ] **版本指派**: 设置某个 Group 使用哪个 ZIP 版本。
---
## 5. 执行路线图
1. **Day 1 (Publishing)**: 写好 `scripts/publish.ts`,确保能一键编译并上传 ZIP 到您的服务器。
2. **Day 2 (Backend)**: 改造 Python 后端,加入 Group 表,在此基础上实现“根据用户返回版本”的接口。
3. **Day 3 (Shell)**: 开发插件 Shell 的下载器,对接上述接口。

View File

@@ -0,0 +1,88 @@
# 壳架构 (Shell Architecture) 改造计划
为了实现“登录即更新”和“代码保护”,我们将把现有的 `DesignerCEP` 单体项目拆分为 **壳 (Shell)****核 (Core)** 两部分。
## 1. 架构概览
| 模块 | 名称 | 职责 | 部署位置 | 更新频率 |
| :-------- | :---------------- | :--------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------- |
| **Shell** | 启动器 (Launcher) | 1. 负责 CEP 扩展的安装与注册<br>2. 负责用户登录鉴权<br>3. **下载并解压** Core 包<br>4. 跳转加载 Core | **本地用户电脑**<br>(安装在 PS 扩展目录) | 极低<br>(仅当下载逻辑变更时更新) |
| **Core** | 业务核 (Business) | 1. 包含所有 Vue 业务页面<br>2. 包含核心 JSX 脚本<br>3. 通过 Shell 注入的变量与宿主交互 | **远程服务器**<br>(ZIP 压缩包) | 极高<br>(随时发布新功能) |
---
## 2. 目录结构改造建议
目前的 `Designer/` 目录将作为**核心业务仓库**,我们需要在其中增加一个专门构建 Shell 的入口。
```
Designer/
├── package.json
├── vite.config.ts # [Core配置] 构建业务代码 (输出 dist/core)
├── vite.shell.config.ts # [Shell配置] 构建启动器代码 (输出 dist/shell)
├── src/
│ ├── main.ts # [Core入口] 业务主入口 (现在的代码)
│ ├── App.vue # [Core] 业务主界面
│ │
│ └── launcher/ # [Shell] 启动器模块 (NEW)
│ ├── main.ts # [Shell入口]
│ ├── App.vue # [Shell] 登录与更新进度条界面
│ ├── updater.ts # [Shell] 下载/解压/校验逻辑 (Node.js)
│ └── login.ts # [Shell] 登录逻辑
└── dist/
├── shell/ # 打包结果:这是给用户安装的 ZXP
│ ├── CSXS/manifest.xml
│ ├── index.html (登录器)
│ └── node_modules (含 fs/adm-zip 等必要依赖)
└── core/ # 打包结果:这是传到服务器的 UPDATE.zip
├── index.html (业务界面)
├── assets/
└── jsx/ (加密后的 JSX)
```
## 3. 核心运行流程 (Workflow)
### 3.1 启动阶段 (Shell)
1. 用户点击 PS 菜单,加载本地的 `dist/shell/index.html`
2. **Shell App** 启动,不再加载复杂的业务组件,只显示登录框。
3. 用户输入账号密码 -> 请求服务器 `/api/login`
4. 登录成功 -> 获取 Token 和 **最新 Core 版本号** (比如 `v1.0.2`)。
### 3.2 更新阶段 (Updater)
1. 检查本地 `C:/Users/xxx/AppData/Roaming/DesignerCache/v1.0.2/` 是否存在。
2. **不存在 (有更新)**:
- 显示进度条。
- 从 CDN/服务器下载 `core-v1.0.2.zip`
- 使用 Node.js (`adm-zip`) 解压到上述目录。
3. **存在**: 直接跳过。
### 3.3 注入阶段 (Launch)
1. **加载 JSX**:
- Shell 调用 `CSInterface.evalScript`
- 读取缓存目录下的 `v1.0.2/jsx/index.jsx` 内容。
- 执行 `$.evalFile("C:/.../v1.0.2/jsx/index.jsx")`,把业务函数注入此 ExtendScript 上下文。
2. **加载 UI**:
- Shell 执行 `window.location.href = "file:///C:/.../v1.0.2/index.html"`
3. **接管**:
- 浏览器跳转,页面变成业务界面。
- 此时 `window.cep` 对象依然存在,业务界面可以继续调用刚刚注入的 JSX 函数。
## 4. 兼容性评估
**现有框架可以完全兼容,只需做以下调整**
1. **Vite 配置拆分**: 需要新增一个 `vite.shell.config.ts`,专门用来打包轻量级的 Shell。
2. **Node.js 依赖**: Shell 需要打包进 `adm-zip` (解压用) 和 `axios` (下载用)。由于 CEP 支持 Node.js这完全没问题。
3. **路由模式**: 业务代码 (Core) 读取本地文件时,路由最好使用 `Hash` 模式 (`createWebHashHistory`),避免 file 协议下的路径问题。
## 5. 下一步行动计划
1. **[Shell]** 创建 `src/launcher` 目录,编写简单的登录+下载器 Demo。
2. **[Build]** 配置 `vite.shell.config.ts`,尝试打包出一个只包含登录功能的 ZXP。
3. **[Test]** 模拟远程更新,手动把现在的业务代码打包成 ZIP 放在本地服务器,测试 Shell 能否下载并跳转。

View File

@@ -0,0 +1,89 @@
# psmark 脚本分析与功能报告
## 1. 概述
`psmark` 目录下发现的 `JSX*.py` 文件JSX2.py - JSX9.py主要承担了**Photoshop 自动化脚本容器**的角色。
这些 Python 文件本身不包含图像处理逻辑,而是作为 Adobe ExtendScript (JSX) 代码的“包装壳”。所有的核心自动化逻辑(如图层操作、裁片缩放、排版等)都由嵌入的 JSX 字符串实现。
## 2. 文件功能详细清单
| 文件名 | 核心功能 | 详细描述 |
| :----------- | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **JSX2.py** | **裁片射出宽高缩放** | - 遍历文档图层,根据图层名称(包含尺寸信息)进行精确缩放。<br>- 包含像素与毫米的自动换算。<br>- 处理 90 度/180 度 旋转逻辑。<br>- 使用 `ActionManager` 进行高效的选区和变换操作。 |
| **JSX3.py** | **设置花样组顺序居中** | - 专用于印花对位。<br>- 自动识别 "P" 开头的花样图层。<br>- 将花样层对齐到“大货裁片”组的中心位置。<br>- 包含蒙版选区获取和坐标计算逻辑。 |
| **JSX4.py** | **图像切割** | - 根据特定图层的边界对文档进行裁剪。<br>- 包含出血/边距处理(例如自动从选区扩展一定像素)。<br>- 适用于从大图中提取独立裁片。 |
| **JSX5.py** | **图像切割 (变体)** | - 功能与 JSX4 类似,侧重于特定图层组的裁剪和蒙版应用。 |
| **JSX6.py** | **裁片射出宽高缩放 (变体)** | - 与 JSX2 类似,提供裁片缩放和定位功能。<br>- 强调了“前景色修改”和“图层选择”等辅助操作。 |
| **JSX7.py** | **裁片射出缩放模板** | - **关键特性**:引入了“**缩放定位点**”图层概念。<br>- 不以图层中心为原点,而是根据“缩放定位点”图层的选区中心进行缩放变换。<br>- 包含 `烧花线添加` 功能,用于绘制剪口和标记线。 |
| **JSX8.py** | **批量化替换外链新 (UI)** | - **全自动化工作流脚本**。<br>- **内置 UI**:包含一个 ScriptUI 编写的对话框,用于选择文件夹路径。<br>- **功能**<br> 1. 批量打开文件。<br> 2. 自动替换智能对象链接(换花型)。<br> 3. `码标添加`:自动生成包含文件名的尺寸标记。<br> 4. `花样标准化`:统一花样大小并添加白底。<br> 5. 自动另存为 TIF 格式并归档。 |
| **JSX9.py** | **按中心点比例缩放** | - 结合了 JSX2 和 JSX7 的特性。<br>- 专注于按“缩放定位点”进行**等比例**或**宽高独立**缩放。<br>- 包含详细的 `ActionManager` 变换代码(`transform` 命令)。 |
| **JSX16.py** | **花样图层导出** | - 遍历图层组(特别是“填充底图”),将子图层导出为 TIF。<br>- 处理“最大白边值”图层以确定裁剪边界。<br>- 包含智能对象转换、重新链接、蒙版合并等复合操作。 |
| **JSX17.py** | **定位码批量快速换图 (UI)** | - **带 ScriptUI**。<br>- 全自动批量流程:选择模板路径、素材路径、输出路径。<br>- 遍历素材图片,打开模板,替换智能对象(`placedLayerRelinkToFile`),应用预设图案填充,合并图层并另存。<br>- 支持读取 JSON 数据来匹配图案名称。 |
| **JSX18.py** | **龙服快速换图 (UI)** | - **带 ScriptUI**(定制版本)。<br>- 读取文件夹文件列表,允许用户在 UI 中输入“数量”信息。<br>- 根据输入信息更新图层文本内容,替换裁片智能对象链接,合并并保存。 |
| **JSX19.py** | **裁片抓取与导入** | - 批量置入 PDF 文件。<br>- **自动旋转特性**:通过解析文件名(如包含 `_180`)自动判断是否需要旋转 180 度导入。<br>- 自动建立“大货裁片”组并归档。 |
| **JSX20.py** | **混合裁片导出** | - 遍历包含“大货裁片”名称的图层组。<br>- 逐个提取子图层,应用蒙版,裁切到边界,导出为 TIF 文件。<br>- 使用历史记录回退(`historyState`)来恢复状态以处理下一个图层。 |
| **JSX21.py** | **裁片排版基础** | - 基础排版功能库。<br>- `创建裁片排版文档`:按毫米创建新画布。<br>- `置入链接的智能对象`:将外部文件作为链接对象置入。<br>- `裁片排版_lay`:提供基于毫米坐标的精确位移功能。 |
| **JSX22.py** | **模特换衣功能 (UI)** | - **带 ScriptUI**。<br>- 针对模特展示图的批量替换。<br>- 遍历素材目录,替换模板中名为“替换对象”的智能对象。<br>- 支持保持原始目录结构导出支持切片导出Web 切片)。 |
| **JSX23.py** | **S/O 样自动连晒 (UI)** | - **带 ScriptUI**。<br>- 自动生成连晒(米样)效果。<br>- 将图片定义为图案,填充到指定宽高的文档中(平铺),并添加对应的文件名文字标签。<br>- 保存两份:一份纯拼贴,一份带文字标签。 |
| **JSX24.py** | **自动米样拼贴 (UI)** | - **带 ScriptUI**。<br>- 与 JSX23 类似但逻辑不同,这是真正的**拼图**Collage。<br>- 读取文件夹所有图片,按高度排序。<br>- 自动计算总画布大小,将图片紧凑排列(换行逻辑)拼贴到一个大文档中。 |
| **JSX26.py** | **模特多色换图** | - 利用**快照**Snapshot机制进行批量处理。<br>- 遍历文件夹素材,替换“贴图位置”组内的图层内容。<br>- 支持对替换后的图案进行位移Offset以制作不同花位效果。<br>- 导出 TIF 后回退到快照状态。 |
| **JSX27.py** | **新的米样缩放 (UI)** | - **带 ScriptUI**。<br>- 批量调整图片尺寸到指定的厘米数Resize Image。<br>- 随后进行画布扩展和文字标签添加,用于制作标准化的缩放样图。 |
## 3. 技术架构特点
### 3.1 Python 包装器模式
实际上是“伪 Python”代码。
```python
# 典型结构
dxf7_jscode = """
function 核心功能() {
// 实际的 ExtendScript (JavaScript) 代码
var doc = app.activeDocument;
...
}
"""
```
这种结构是为了便于 Python 后端(可能是旧的自动化系统)直接将 JS 代码发送给 Photoshop 执行。迁移时应直接提取 `"""` 内部的内容。
### 3.2 ActionManager (AM) 代码
脚本极度依赖 Photoshop 的底层 ActionManager API`executeAction`, `ActionDescriptor`)。
- **优点**:执行速度快,能实现 DOM API普通 JS 对象)无法实现的功能(如“再次变换”、“应用图层样式”、“特定算法的选区运算”)。
- **缺点**:代码可读性差,维护难度大(充斥着 `stringIDToTypeID`)。
### 3.3 命名约定
脚本逻辑强依赖于图层和组的命名规范:
- `P...`:识别为花样图层。
- `-大货裁片`:识别为标准的裁片组模板。
- `缩放定位点`:用于计算变换中心的辅助图层。
- `_` 分隔符:用于从图层名中提取尺寸参数(如 `名称_宽度_高度`)。
## 4. 迁移与集成建议 (DesignerCEP)
为了将这些资产整合到当前的 `DesignerCEP` (Vue + TypeScript + CEP) 项目中,建议采取以下步骤:
1. **代码提取 (Extract)**
- 废弃 `.py` 文件。
- 将字符串内的 JS 代码提取并在 `src/jsx/` 目录下建立对应的 `.jsx` 文件(建议按功能重命名,如 `ResizeUtils.jsx`, `BatchProcess.jsx`)。
2. **模块化重构 (Refactor)**
- 原脚本大量使用全局变量,容易造成污染。需要将其封装为独立的函数,例如 `function scaleLayerByAnchor(layerName, anchorLayerName) { ... }`
- 将通用的 `ActionManager` 辅助函数(如 `executeAction` 的封装)移动到统一的工具库中。
3. **UI 重写 (Modernize UI)**
- **重点**`JSX8.py` 中的 ScriptUI 弹窗(灰色原生界面)应完全废弃。
- 使用 **Vue + Element Plus/Arco Design** 重写批量处理界面。
- 前端收集用户输入的路径和选项,通过 `cep.ts` 桥接层传递给 JSX 执行核心逻辑。
4. **功能桥接 (Bridge)**
-`src/jsx/index.ts` 中暴露新的接口,供前端调用。
- 例如:`canvas_cut_image` (对应 JSX4), `auto_resize_layer` (对应 JSX2)。

View File

@@ -0,0 +1,86 @@
[
{
"title": "【CEP教程-17】插件的打包和发布",
"url": "https://zhuanlan.zhihu.com/p/27361054277"
},
{
"title": "【CEP教程-16】JSX的工程化",
"url": "https://zhuanlan.zhihu.com/p/22605290525"
},
{
"title": "【UXP教程-2】UXP插件开发起步",
"url": "https://zhuanlan.zhihu.com/p/20904402159"
},
{
"title": "【CEP教程-15】前端框架在插件面板中的应用",
"url": "https://zhuanlan.zhihu.com/p/683712943"
},
{
"title": "我给三年级女儿开发了一个打字网站",
"url": "https://zhuanlan.zhihu.com/p/676856628"
},
{
"title": "【CEP教程-14】数据存储相关",
"url": "https://zhuanlan.zhihu.com/p/675795467"
},
{
"title": "【CEP教程-13】nodejs在插件开发中的应用",
"url": "https://zhuanlan.zhihu.com/p/661392566"
},
{
"title": "【CEP教程-12】如何从Ps中导出图片",
"url": "https://zhuanlan.zhihu.com/p/658067352"
},
{
"title": "【CEP教程-11】生成器",
"url": "https://zhuanlan.zhihu.com/p/643541900"
},
{
"title": "【CEP教程-10】图层处理那些事",
"url": "https://zhuanlan.zhihu.com/p/617477492"
},
{
"title": "【CEP教程-10】Action Manager完全指南 - 下篇",
"url": "https://zhuanlan.zhihu.com/p/608104095"
},
{
"title": "【CEP教程-9】Action Manager完全指南 - 中篇",
"url": "https://zhuanlan.zhihu.com/p/601014597"
},
{
"title": "【Adobe UXP插件开发中文教程】- 1. 简介",
"url": "https://zhuanlan.zhihu.com/p/600569875"
},
{
"title": "【CEP教程-8】Action Manager完全指南 - 上篇",
"url": "https://zhuanlan.zhihu.com/p/600014746"
},
{
"title": "Photoshop插件开发教程 - 7JSX脚本指南 - DOM篇",
"url": "https://zhuanlan.zhihu.com/p/596166382"
},
{
"title": "Photoshop插件开发教程 - 6面板与宿主之间的交互",
"url": "https://zhuanlan.zhihu.com/p/566983957"
},
{
"title": "Photoshop插件开发教程 - 5插件面板的样式",
"url": "https://zhuanlan.zhihu.com/p/563847844"
},
{
"title": "Photoshop插件开发教程 - 4开发工具选择和调试",
"url": "https://zhuanlan.zhihu.com/p/559290141"
},
{
"title": "Photoshop插件开发教程 - 3CEP插件面板结构介绍",
"url": "https://zhuanlan.zhihu.com/p/555070606"
},
{
"title": "Photoshop插件开发教程 - 2开发环境搭建",
"url": "https://zhuanlan.zhihu.com/p/532152091"
},
{
"title": "Photoshop插件开发教程 - 1插件类型",
"url": "https://zhuanlan.zhihu.com/p/518229060"
}
]

View File

@@ -0,0 +1,82 @@
# 前端代码结构与功能说明 (DesignerCEP)
本文档详细说明了 `DesignerCEP` 项目的前端代码结构,包括新引入的 **"Shell (启动器) + Core (业务核)"** 双层架构。
---
## 1. 核心架构说明
项目被设计为两个独立的部分,分别构建:
1. **Shell (启动器)**:
- **职责**: 极轻量级,负责登录验证、检查版本、下载 ZIP、解压、动态加载 Core。
- **代码位置**: `src/launcher/`
- **构建产物**: 生成 ZXP 安装包的主体。
2. **Core (业务核)**:
- **职责**: 包含所有设计功能Vue 界面 + JSX 脚本)。
- **代码位置**: `src/` (除 `launcher` 外)
- **构建产物**: 被打包成 ZIP由 Shell 下载并运行。
---
## 2. 目录结构详解
### 2.1 根目录配置
| 文件名 | 作用 |
| :--------------------- | :------------------------------------------------------------------------------------------------------------ |
| `package.json` | 项目依赖管理。包含 `npm run build:shell` (构建启动器) 和 `npm run build:core` (构建业务核) 等脚本。 |
| `vite.config.ts` | **Core 的构建配置**。用于开发环境 (`npm run dev`) 和业务核打包。配置了 API 代理和 Vue 插件。 |
| `vite.shell.config.ts` | **Shell 的构建配置**。专门用于构建启动器,指定入口为 `src/launcher/index.html`,并打包为独立的 CEP 插件结构。 |
| `prod.cep.config.ts` | CEP 插件的生产环境配置文件 (Manifest)。定义包括插件 ID、版本、窗口大小、支持的宿主 (PS, AI 等)。 |
| `tsconfig.json` | TypeScript 配置文件。定义了别名 (`@/`, `@plugins/`) 和编译选项。 |
### 2.2 Shell 启动器 (`src/launcher/`)
这是用户安装 ZXP 后第一时间运行的代码。
| 路径/文件名 | 作用 |
| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`index.html`** | Shell 的 HTML 入口。引入 `CSInterface.js``main.ts`。 |
| **`main.ts`** | Shell 的 JS 入口。初始化 Vue 应用,挂载 `App.vue`。 |
| **`App.vue`** | Shell 的根组件。包含 `<router-view>`,用于切换登录/更新界面。 |
| **`router.ts`** | Shell 的路由配置。定义了 `/login` (登录页) 和 `/register` (注册页)。 |
| **`view/Login.vue`** | **核心登录界面**。集成了 `Updater` 类,点击登录后自动执行:**登录 -> 检查更新 -> 下载 ZIP -> 解压 -> 跳转**。 |
| **`view/Register.vue`** | 注册界面。 |
| **`utils/updater.ts`** | **核心更新逻辑**。包含 `Updater` 类。负责调用后端 API (`check_update`),使用 `fs` 写入文件,使用 `adm-zip` 解压,最后修改 `window.location.href` 加载 Core。 |
| **`jsx/index.ts`** | Shell 的最小化 JSX 脚本。CEP 插件必须包含至少一个 JSX这里仅返回版本号不包含重型业务逻辑。 |
### 2.3 Core 业务核 (`src/`)
这是实际的业务软件,被下载后动态加载。
| 路径/目录 | 作用 |
| :------------------------------ | :----------------------------------------------------------------------------------------------------- |
| **`main.ts`** | Core 的入口文件。引入 Arco Design全局样式等。 |
| **`App.vue`** | Core 的根组件。 |
| **`router/index.ts`** | Core 的路由配置。包含 `/home`, `/about` 等业务页面。(注意:已移除登录/注册路由,因为这由 Shell 接管)。 |
| **`view/Home.vue`** | 主页组件。包含主要的功能入口和测试按钮。 |
| **`jsx/`** | **Adobe ExtendScript (JSX) 脚本目录**。 |
| &emsp; `index.ts` | JSX 入口。导出所有供 Vue 调用的 Photoshop 接口函数。 |
| &emsp; `utils/ActionManager.ts` | action manager 底层封装 (如载入选区、填充、新建文档)。底层核心。 |
| &emsp; `utils/LayerUtils.ts` | 图层操作封装 (如遍历、查找、重命名)。 |
| **`utils/cep.ts`** | `CSInterface` 的 TS 封装。用于前端 Vue 调用后端 JSX (`evalScript`)。 |
| **`utils/request.ts`** | 基于 Umi-Request 或 Axios 的 HTTP 请求封装 (业务用)。 |
### 2.4 构建插件 (`plugins/`)
用于辅助 Vite 打包 CEP 环境的代码。
| 路径/文件名 | 作用 |
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`jsx/cepPlugin.ts`** | **构建核心**。Vite 插件。负责:<br>1. 监听 JSX 变化并重编译 (Dev)。<br>2. 生产环境调用 `rollup` 编译 JSX 为 `index.js`。<br>3. 复制 manifest.xml 和 index.html 到 `dist` 目录。 |
| **`buildJsx/index.ts`** | 单独的 Rollup 构建配置,专门用于将 `src/jsx/*.ts` 编译成 Photoshop 能跑的 ES3 格式 JS。 |
| **`template/`** | 存放 `manifest.xml`, `.debug` 等 CEP 配置文件的模板。 |
---
## 3. 开发与发布流程总结
1. **开发 Shell**: 修改 `src/launcher` -> `npm run build:shell` -> 生成 ZXP。
2. **开发 Core**: 修改 `src/view``src/jsx` -> `npm run build:core` -> 得到业务 ZIP 包。
3. **用户视角**: 安装 ZXP -> 启动插件 -> 看到登录框 (Shell) -> 登录并下载最新 Core -> 自动跳转进入业务界面。

View File

@@ -0,0 +1,126 @@
# 前端安全升级接入指南
为了提高安全性,后端鉴权机制已升级为 **Token + Device Binding + Session Enforcement**
**⚠️ 核心变更:所有身份验证相关的接口现在都强制要求携带 `device_id`。**
前端必须配合进行以下改动,否则接口将返回 `422 Unprocessable Entity` (参数缺失) 或 `401 Unauthorized` (设备不匹配)。
---
## 🛠️ 接口改造清单
请检查并修改以下所有接口的调用参数:
### 1. 登录接口 (Login)
* **URL**: `/api/v1/auth/login`
* **变更**: 新增必填字段 `device_id`
* **说明**: 之前的文档描述有误,登录接口**必须**传此参数,否则无法建立会话。
**修改后示例:**
```typescript
const loginData = {
username: "user1",
password: "pwd",
device_id: getDeviceId() // ✅ 必传
};
```
### 2. 注册接口 (Register)
* **URL**: `/api/v1/auth/register`
* **变更**: 新增必填字段 `device_id`
* **说明**: 注册成功后会自动登录,因此必须绑定当前设备。
**修改后示例:**
```typescript
const registerData = {
username: "user1",
password: "pwd",
confirm_password: "pwd",
device_id: getDeviceId() // ✅ 必传
};
```
### 3. 登出接口 (Logout)
* **URL**: `/api/v1/auth/logout`
* **变更**: 新增必填字段 `device_id`
* **说明**: 用于精准注销当前设备的会话,而不影响用户在其他设备上的登录状态。
**修改后示例:**
```typescript
const logoutData = {
username: "user1",
device_id: getDeviceId() // ✅ 必传
};
```
### 4. 心跳保活接口 (Heartbeat)
* **URL**: `/api/v1/auth/heartbeat`
* **变更**: 新增必填字段 `device_id`
* **说明**: 用于维持当前设备 Session 的活跃状态。
**修改后示例:**
```typescript
const heartbeatData = {
username: "user1",
device_id: getDeviceId() // ✅ 必传
};
```
### 5. 许可证验证接口 (Verify)
* **URL**: `/api/v1/auth/verify`
* **变更**: 新增必填字段 `device_id`
* **说明**: 后端会校验 Token 中的设备 ID 是否与参数中的设备 ID 一致,且 Session 是否活跃。
**修改后示例:**
```typescript
const verifyData = {
username: "user1",
timestamp: Date.now(),
device_id: getDeviceId() // ✅ 必传
};
```
---
## 🚨 统一错误处理 (Interceptor)
由于新的强校验机制,`401 Unauthorized` 可能会在任何接口出现(不仅仅是 Token 过期,也可能是被踢下线)。
前端拦截器需要统一处理 `401`,强制跳转到登录页。
**Axios 拦截器示例:**
```typescript
axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
// 1. 清除本地 Token
localStorage.removeItem('access_token');
// 2. 提示用户
const msg = error.response.data.detail || '登录已失效,请重新登录';
// Message.error(msg); // Arco Design / Element UI
console.error(msg);
// 3. 跳转登录页
// 如果是 Shell 环境,跳转到 Shell 登录
if (window.location.hash.indexOf('/login') === -1) {
window.location.href = '/shell/index.html#/login';
}
}
return Promise.reject(error);
}
);
```
---
## 💡 关于 device_id 的说明
`device_id` 是用于标识客户端设备的唯一字符串。
* **生成方式**:建议在应用启动时检查 `LocalStorage`,如果没有则生成一个 UUID 并存入;如果有则直接读取。
* **作用**:用于区分不同的客户端实例,实现单设备登录限制(互踢功能)。
* **持久化**:必须确保存储在浏览器/CEP 环境的持久化存储中(如 `localStorage`),避免刷新页面后变化。

View File

@@ -0,0 +1,160 @@
# DesignerCEP 后端 API 接口文档
本文档描述了 DesignerCEP 后端服务的 API 接口规范,包括客户端插件接口和后台管理接口。
## 1. 基础信息
- **Base URL**: `http://localhost:8000/api/v1`
- **文件下载 Base URL**: `http://localhost:8000/download/`
- **鉴权方式**:
- **Client**: Bearer Token (JWT)
- **Admin**: 简单 Token (Header `x-admin-token` 或 Form `token`) - *开发阶段*
## 2. 客户端接口 (Client)
用于 Photoshop 插件端的交互。
### 2.1 登录 (Login)
客户端登录,获取 Token、权限及过期时间。
- **URL**: `/client/login`
- **Method**: `POST`
- **Request Body**:
```json
{
"username": "user1",
"password": "password123",
"device_id": "unique-device-id"
}
```
- **Response**:
```json
{
"code": 200,
"data": {
"token": "eyJhbGciOiJIUzI1...",
"username": "user1",
"expire_date": "2025-12-31",
"permissions": ["batch_process", "export"]
},
"message": "success"
}
```
### 2.2 检查更新 (Check Update)
根据用户所在组检查是否有新版本。
- **URL**: `/client/check_update`
- **Method**: `POST`
- **Request Body**:
```json
{
"username": "user1"
}
```
- **Response**:
```json
{
"code": 200,
"data": {
"version": "v1.0",
"download_url": "/download/plugin_v1.0.zip",
"force_update": false,
"is_expired": false
},
"message": "success"
}
```
- `is_expired`: 若为 `true`,表示用户授权已过期,客户端应限制功能。
- `download_url`: 拼接 Base URL 使用。
---
## 3. 管理端接口 (Admin)
用于发布系统管理CI/CD 或管理后台)。需在 Header 中携带 `x-admin-token: admin-secret-token` (默认开发Token)。
### 3.1 上传版本文件 (Upload Version)
上传插件 ZIP 包到服务器。
- **URL**: `/admin/upload_version`
- **Method**: `POST`
- **Content-Type**: `multipart/form-data`
- **Form Data**:
- `file`: (Binary Zip File)
- `token`: "admin-secret-token" (作为 Form 字段兼容脚本)
- **Response**:
```json
{
"code": 200,
"message": "File 'plugin_v1.0.zip' uploaded successfully",
"filename": "plugin_v1.0.zip"
}
```
### 3.2 创建用户组 (Create Group)
- **URL**: `/admin/groups`
- **Method**: `POST`
- **Request Body**:
```json
{
"name": "Dev Group",
"current_version_file": "plugin_v1.0_beta.zip",
"comment": "开发测试组"
}
```
- **Response**: 返回创建的组对象。
### 3.3 更新用户组 (Update Group)
用于切换组的版本。
- **URL**: `/admin/groups/{group_id}`
- **Method**: `PUT`
- **Request Body**:
```json
{
"current_version_file": "plugin_v1.1_stable.zip"
}
```
### 3.4 获取所有组 (List Groups)
- **URL**: `/admin/groups`
- **Method**: `GET`
- **Response**: 组列表数组。
### 3.5 修改用户所属组 (Update User Group)
- **URL**: `/admin/users/{user_id}/group`
- **Method**: `PUT`
- **Query Params**:
- `group_id`: 目标组 ID
- **Response**:
```json
{
"code": 200,
"message": "User group updated"
}
```
---
## 4. 数据库模型说明
### PluginGroup (用户组)
- `id`: ID
- `name`: 组名 (Unique)
- `current_version_file`: 当前关联的 ZIP 文件名
- `comment`: 备注
### User (用户)
- `id`: ID
- `username`: 用户名
- `group_id`: 所属组 ID (Foreign Key)
- `permissions`: 权限列表 (逗号分隔字符串)
- `expire_date`: 过期时间

View File

@@ -0,0 +1,57 @@
# 后端代码与工具说明 (DesignerCEP)
本文档详细说明了 `DesignerCEP` 项目的后端服务、测试脚本以及辅助管理工具。
---
## 1. 自动化发布 (`publish.py`)
位于项目根目录 `d:\main\DesignerCEP\publish.py`
- **类型**: Python 脚本。
- **功能**: 实现 "一键发布" 流程。
1. **打包**: 自动将 `Designer` 目录压缩为 `.zip` 文件(自动排除 `.git`, `node_modules` 等无关文件)。
2. **上传**: 调用后端接口 `POST /api/v1/admin/upload_version` 将 ZIP 包上传到服务器。
3. **鉴权**: 使用 `token` 进行简单的管理员验证。
- **使用方法**:
```bash
python publish.py ./Designer --name "release_v1.0"
```
---
## 2. 后端服务 (`Server/`)
位于 `d:\main\DesignerCEP\Server\`,基于 Python FastAPI 框架。
### 核心文件
| 路径/文件名 | 作用 |
| :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`app/main.py`** | **程序入口**。初始化 FastAPI 应用,配置 CORS允许跨域挂载路由初始化数据库。 |
| **`app/api/v1/client.py`** | **客户端接口**。处理插件端的请求:<br>- `/check_update`: 检查更新(含过期校验)。<br>- `/login`: 用户登录(返回 Token 和权限)。 |
| **`app/api/v1/admin.py`** | **管理接口**。供 AdminTool 使用:<br>- `/upload_version`: 接收发布的 ZIP 包。<br>- `/groups`: 管理用户组 (Stable/Dev)。<br>- `/users`: 管理用户及其所属组。 |
| **`app/models/`** | **数据库模型 (SQLAlchemy)**。定义 `User``PluginGroup` 表结构。 |
| **`tests/test_api.py`** | **集成测试**。包含完整的业务流程测试:<br>1. 创建用户组。<br>2. 上传 ZIP 包。<br>3. 将组指向该 ZIP。<br>4. 模拟客户端检查更新,验证返回版本是否正确。<br>5. 验证过期用户是否被拦截。 |
---
## 3. 管理后台工具 (`AdminTool/`)
位于 `d:\main\DesignerCEP\AdminTool\`,基于 PyQt5 的桌面应用程序。
- **功能**: 为不熟悉命令行的管理员提供图形化界面。
- **主要能力**:
- **用户管理**: 增删改查用户,设置过期时间,分配用户组。
- **版本管理**: 切换某个组当前使用的插件版本(实现灰度发布或回滚)。
- **发布管理**: (可选) 集成文件上传功能。
---
## 总结
整个后端生态由这三部分组成闭环:
1. **Server**: 提供数据存储和 API 服务。
2. **publish.py**: 开发者用的“发货工具”。
3. **AdminTool**: 管理员用的“控制台”。

View File

@@ -0,0 +1,116 @@
# DesignerCEP 后端开发需求文档
请根据以下需求使用 Python (Flask/FastAPI) + MySQL 开发后端服务。
## 1. 核心需求概览
我们需要实现一个 **“灰度发布系统”**。
- **用户与权限**: 用户属于不同的“组” (Group),如 "Stable Group", "Dev Group"。
- **版本控制**: 不同的组对应不同的插件版本 (ZIP 包)。
- **客户端交互**: 客户端插件 (Shell) 登录时,根据用户所在的组,返回对应的 ZIP 包下载地址。
---
## 2. 数据库设计 (Database Schema)
请创建以下两张核心表(基于 MySQL
### 2.1 用户组表 (`plugin_group`)
用于管理不同的发布通道。
| 字段名 | 类型 | 描述 | 示例值 |
| :--------------------- | :------- | :----------------------- | :------------------ |
| `id` | INT (PK) | 组 ID | 1 |
| `name` | VARCHAR | 组名称 | "正式版用户组" |
| `current_version_file` | VARCHAR | **当前使用的版本文件名** | "plugin_v1.0.2.zip" |
| `comment` | TEXT | 备注 | "稳定版本通道" |
### 2.2 用户表 (`user`)
_需关联到组表_
| 字段名 | 类型 | 描述 | 示例值 |
| :------------ | :------- | :---------------------- | :------------------------- |
| `id` | INT (PK) | 用户 ID | 1001 |
| `username` | VARCHAR | 用户名 | "designer01" |
| `password` | VARCHAR | 密码 | "123456" |
| `group_id` | INT (FK) | **所属组 ID** | 1 (关联 `plugin_group.id`) |
| `permissions` | TEXT | **权限列表** (逗号分隔) | "batch_process,vip_export" |
| `expire_date` | DATETIME | **过期时间** | "2025-12-31 23:59:59" |
---
## 3. API 接口规范 (API Specification)
所有接口前缀建议为 `/api/v1`
### 3.1 检查更新 (Client 接口)
客户端插件启动时调用,查询自己应该下载哪个版本。
- **URL**: `POST /api/v1/client/check_update`
- **请求参数**: `{ "username": "..." }`
- **逻辑**:
1. 检查用户是否过期 (`expire_date` < user.expire_date). 如果过期,返回 403 或特定状态码。
2. 根据 `username` 查询用户所在的 `group_id`
3. 根据 `group_id` 查询 `plugin_group` 表,获取 `current_version_file`
4. 拼接下载链接。
- **返回示例**:
```json
{
"code": 200,
"data": {
"version": "v1.0.2",
"download_url": "http://your-server.com/download/plugin_v1.0.2.zip",
"force_update": false,
"is_expired": false // 明确告知是否过期
},
"message": "success"
}
```
### 3.2 登录验证 (Client 接口)
- **URL**: `POST /api/v1/client/login`
- **请求参数**: `{ "username": "...", "password": "..." }`
- **逻辑**: 校验密码及过期状态。
- **返回**:
```json
{
"code": 200,
"data": {
"token": "...",
"username": "...",
"expire_date": "2025-12-31",
"permissions": ["batch_process", "vip_export"] // 返回权限数组
}
}
```
### 3.3 上传新版本 (Admin/CI 接口)
用于发布脚本上传新的 ZIP 包。
- **请求参数**: `file` (multipart/form-data)
- **逻辑**:
1. 校验管理员权限(可硬编码 Token 或密码)。
2. 将上传的文件保存到服务器的 `archives/` 目录。
3. 文件名通常包含版本号或时间戳,如 `plugin_v1.0.3_20251216.zip`。
4. (可选) 自动更新相关数据库记录。
### 3.4 文件下载 (Download)
- **URL**: `GET /download/<filename>`
- **功能**: 提供静态文件下载服务(指向 `archives/` 目录)。
---
## 4. 后台管理功能 (Admin Tool 支持)
后端需要提供基础的数据增删改查接口,以支持 PyQt 或 Web 管理后台:
1. **用户管理**: 新增用户、修改用户所属组 (`group_id`)。
2. **组管理**: 修改组指向的版本 (`current_version_file`)。
- _场景_: 管理员发现 "v1.1-beta" 有 Bug可以将 "Dev Group" 的 `current_version_file` 改回 "v1.0-stable",实现快速回滚。

View File

@@ -0,0 +1,387 @@
# 后端部署 Shell 完整指南
## 🎯 目标
让服务器提供 Shell 登录页面,解决退出时无法跳转回登录页的问题。
---
## 📋 需要做的事情
### ✅ 后端代码已修改完成
`Server/app/main.py` 已经修改好了,包含:
1. 挂载 Shell 目录到 `/shell/` 路径
2. 根路径重定向到 Shell 登录页
---
## 📦 需要上传的文件
### 1. 构建 Shell在前端项目中执行
```bash
cd Designer
npm run build:shell
```
**生成位置:** `Designer/dist/`
**生成内容:**
```
Designer/dist/
├── index.html ← Shell 入口文件
├── assets/
│ ├── index-xxx.js ← Shell 的 JS
│ ├── index-xxx.css ← Shell 的 CSS
│ └── ...
├── node_modules/ ← CEP 需要的依赖
└── ...
```
---
### 2. 上传 Shell 到服务器
有两种方式:
#### 方式 1直接复制到服务器项目推荐
```bash
# 在项目根目录执行
# 把 Designer/dist/ 整个目录复制到服务器项目下
# Windows PowerShell
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
# Linux/Mac
cp -r Designer/dist Server/Designer/dist
```
最终服务器目录结构:
```
Server/
├── app/
│ ├── main.py ← 已修改
│ └── ...
├── Designer/ ← 新增!
│ └── dist/ ← Shell 文件
│ ├── index.html
│ └── ...
└── ...
```
#### 方式 2使用软链接开发环境
```bash
# Windows管理员权限
cd Server
mklink /D Designer ..\Designer
# Linux/Mac
cd Server
ln -s ../Designer Designer
```
---
## 🔧 后端代码说明
### main.py 的修改(已完成)
```python
# 1. 导入 Path
from pathlib import Path
# 2. 挂载 Shell 目录
shell_dir = Path(__file__).parent.parent / "Designer"
if shell_dir.exists():
app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
print(f"✓ Shell 已挂载: {shell_dir}")
else:
print(f"⚠️ Shell 目录不存在: {shell_dir}")
print(" 请先运行: cd Designer && npm run build:shell")
# 3. 根路径重定向到 Shell
@app.get("/")
def read_root():
from fastapi.responses import RedirectResponse
return RedirectResponse(url="/shell/index.html")
```
---
## 🚀 部署步骤
### Step 1: 构建 Shell
```bash
# 在前端项目目录
cd D:\main\DesignerCEP\Designer
npm run build:shell
```
**检查输出:**
-`dist/index.html` 文件存在
-`dist/assets/` 目录存在
---
### Step 2: 复制 Shell 到服务器
**方法 A在本地复制推荐**
```bash
# 在项目根目录
cd D:\main\DesignerCEP
# 创建目标目录
mkdir Server\Designer -Force
# 复制文件
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
```
**方法 B在服务器上从 Git 拉取(如果用了 Git**
```bash
# 在服务器上
cd /path/to/DesignerCEP
git pull
cd Designer
npm run build:shell
```
---
### Step 3: 验证文件结构
```bash
cd Server
dir Designer\dist\index.html # Windows
# 或
ls Designer/dist/index.html # Linux
```
**应该看到:**
```
D:\main\DesignerCEP\Server\Designer\dist\index.html
```
---
### Step 4: 重启后端服务器
```bash
cd Server
python -m uvicorn app.main:app --reload
```
**启动日志应该显示:**
```
✓ Shell 已挂载: D:\main\DesignerCEP\Server\Designer\dist
INFO: Uvicorn running on http://127.0.0.1:8000
```
如果显示:
```
⚠️ Shell 目录不存在: ...
请先运行: cd Designer && npm run build:shell
```
说明文件没有正确复制,回到 Step 2。
---
### Step 5: 测试访问
#### 测试 1根路径重定向
```bash
# 在浏览器访问
http://127.0.0.1:8000/
```
**预期:** 自动跳转到 `http://127.0.0.1:8000/shell/index.html` 并显示登录页
#### 测试 2直接访问 Shell
```bash
http://127.0.0.1:8000/shell/
```
**预期:** 显示登录页面
#### 测试 3登录并退出
1. 在 Shell 登录页输入账号密码
2. 登录成功 → 跳转到 Core
3. 在 Core 点击退出
4. **预期:** 跳转回 Shell 登录页 ✅
---
## 📁 完整目录结构
```
D:\main\DesignerCEP\
├── Designer/
│ ├── src/
│ │ ├── launcher/ ← Shell 源代码
│ │ │ ├── view/
│ │ │ │ └── Login.vue ← 登录页面源码
│ │ │ └── utils/
│ │ │ └── updater.ts
│ │ └── view/
│ │ └── Home.vue ← Core 页面
│ │
│ └── dist/ ← 构建后的 Shell
│ ├── index.html ← 需要复制到服务器
│ └── assets/
└── Server/
├── app/
│ └── main.py ← 已修改
├── Designer/ ← 新增目录
│ └── dist/ ← 从 Designer/dist/ 复制过来
│ ├── index.html ← Shell 登录页
│ └── assets/
└── archives/ ← Core 下载包
└── core-v1.2.4.zip
```
---
## 🔍 访问路径说明
| URL | 作用 | 文件位置 |
|-----|------|---------|
| `http://127.0.0.1:8000/` | 根路径,重定向到 Shell | - |
| `http://127.0.0.1:8000/shell/` | Shell 登录页 | `Server/Designer/dist/index.html` |
| `http://127.0.0.1:8000/core/v1.2.4/` | Core 业务页 | `C:/Users/.../DesignerCache/v1.2.4/` |
| `http://127.0.0.1:8000/download/` | 下载 Core 包 | `Server/archives/` |
---
## ⚠️ 常见问题
### 问题 1启动后显示 "Shell 目录不存在"
**原因:** `Server/Designer/dist/` 目录不存在
**解决:**
```bash
# 检查目录
cd D:\main\DesignerCEP\Server
dir Designer\dist
# 如果不存在,重新复制
cd ..
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
```
---
### 问题 2访问 `/shell/` 显示 404
**原因:** 文件没有正确挂载
**解决:**
1. 检查 `Server/Designer/dist/index.html` 是否存在
2. 检查后端启动日志是否显示 "✓ Shell 已挂载"
3. 重启后端服务器
---
### 问题 3访问 `/` 不重定向
**原因:** `main.py` 中的重定向代码没有生效
**解决:**
1. 确认 `main.py` 中有重定向代码:
```python
@app.get("/")
def read_root():
from fastapi.responses import RedirectResponse
return RedirectResponse(url="/shell/index.html")
```
2. 重启后端服务器
---
### 问题 4Shell 页面加载失败
**原因:** 资源文件路径问题
**解决:**
1. 检查 `dist/index.html` 中的资源引用是否正确
2. 确认 `assets/` 目录完整复制
3. 清除浏览器缓存后重试
---
## 🔄 更新流程
当 Shell 代码有更新时:
```bash
# 1. 重新构建 Shell
cd Designer
npm run build:shell
# 2. 复制到服务器
cd ..
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
# 3. 重启后端(如果使用 --reload 则自动重启)
# 或手动重启
```
---
## ✅ 部署检查清单
部署完成后,检查以下项:
- [ ] `Server/Designer/dist/index.html` 文件存在
- [ ] 后端启动日志显示 "✓ Shell 已挂载"
- [ ] 访问 `http://127.0.0.1:8000/` 自动跳转到 Shell
- [ ] 访问 `http://127.0.0.1:8000/shell/` 显示登录页
- [ ] Shell 页面可以正常登录
- [ ] 登录后跳转到 Core
- [ ] Core 退出后跳转回 Shell 登录页 ✅
---
## 📝 快速命令汇总
```bash
# 1. 构建 Shell
cd D:\main\DesignerCEP\Designer
npm run build:shell
# 2. 复制到服务器
cd ..
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
# 3. 重启后端
cd Server
python -m uvicorn app.main:app --reload
# 4. 测试访问
# 浏览器打开: http://127.0.0.1:8000/
```
---
## 🎉 完成
部署完成后:
- ✅ 用户登录后使用 Core
- ✅ 退出时自动跳转回 Shell 登录页
- ✅ 不再显示 `{"message":"Welcome to DesignerCEP Backend"}`
**现在退出流程完美了!** 🎊

View File

@@ -0,0 +1,150 @@
# 安全方案对比
## 方案 A前端内联 JSX当前方案
### 优点
- ✅ 离线可用
- ✅ 响应快速
- ✅ 开发简单
### 缺点
-**代码完全暴露**
-**可以被逆向破解**
-**用户可以绕过验证**
-**核心算法可被复制**
### 安全性
```
★☆☆☆☆ (1/5)
```
### 攻击方式
```javascript
// 攻击者可以直接修改前端代码:
// 1. 删除 verifyLicense() 调用
// 2. 直接调用 Layer.createLayer()
// 3. 复制 JSX 代码到自己的插件
```
---
## 方案 B服务器端 JSX推荐
### 优点
-**核心代码在服务器,无法被窃取**
-**强制在线验证**
-**可以随时更新逻辑**
-**支持按功能付费**
-**完整的用户行为追踪**
### 缺点
- ❌ 需要联网
- ❌ 有延迟(通常 <100ms
- ❌ 服务器成本
### 安全性
```
★★★★★ (5/5)
```
### 工作流程
```
客户端 服务器
------- --------
1. 点击按钮 →
2. 发送请求 → 验证许可证
验证设备绑定
检查用户等级
生成 JSX 代码
3. 接收代码 ← 返回 JSX
4. 执行代码
5. 显示结果
```
### 破解难度
- 前端只有 API 调用,没有核心逻辑
- 即使破解前端,也无法获取服务器端的 JSX 模板
- 服务器可以检测异常调用并封禁账号
---
## 方案 C混合方案平衡
### 策略
- **基础功能** → 前端内联(离线可用)
- **核心功能** → 服务器端(保护算法)
- **高级功能** → 服务器端 + 付费验证
### 示例
```typescript
// 基础功能:前端内联(免费,离线)
await Layer.createLayer('新图层');
// 核心功能:服务器端(付费,在线)
await ServerJSX.createLayerWithStyle('设计图层', 80, '#FF0000');
// 高级功能:服务器端(高级会员专属)
await ServerJSX.aiAutoDesign(params);
```
### 安全性
```
★★★☆☆ (3/5)
```
---
## 🎯 推荐配置
### 商业产品(强保护)
```
✅ 使用方案 B服务器端 JSX
✅ 所有核心功能服务器化
✅ 前端代码混淆
✅ 设备指纹 + 硬件绑定
✅ 许可证在线验证
✅ 操作日志 + 异常检测
```
### 免费/开源产品(轻保护)
```
✅ 使用方案 A前端内联 JSX
✅ 基础代码混淆
✅ 可选的在线功能
```
---
## 💡 实现建议
### 1. 短期(快速上线)
使用方案 A + 基础保护:
- 代码混淆
- 许可证验证
- 行为记录
### 2. 中期(商业化)
迁移到方案 C
- 保留基础功能在前端
- 核心算法移到服务器
- 实现付费功能
### 3. 长期(高价值产品)
完全方案 B
- 所有核心功能服务器化
- AI 功能集成
- 多端同步
- 企业级管理
---
## ⚠️ 重要提醒
**前端代码永远可以被破解!**
真正的保护只有两种:
1. **服务器端执行** - 代码不在客户端
2. **硬件加密** - 使用加密狗CEP 插件不支持)
其他所有前端保护(混淆、加密)都只是**增加破解难度**,不能完全防止。

311
tempdocs/常用命令.md Normal file
View File

@@ -0,0 +1,311 @@
# DesignerCEP 常用命令
## 🏗️ 三层架构说明
```
┌────────────────────────────────────────┐
│ 第 1 层Shell登录层
│ - 本地file:// 协议CEP 扩展) │
│ - 服务器http://127.0.0.1:8000/shell/ │
│ - 作用:登录、下载、启动 │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 第 2 层Core业务层
│ - http://127.0.0.1:8000/core/vX.X.X/ │
│ - 作用:所有业务功能 │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 第 3 层Server后端服务器
│ - http://127.0.0.1:8000/api/v1/ │
│ - 作用API、核心算法、数据库 │
└────────────────────────────────────────┘
```
---
## 📦 构建命令
### 开发模式
```bash
cd D:\main\DesignerCEP\Designer
npm run dev # 启动开发服务器Core
```
### 构建 Core业务层
```bash
cd D:\main\DesignerCEP\Designer
npm run build:core # 构建 Core 应用
```
### 构建 Shell登录层
```bash
cd D:\main\DesignerCEP\Designer
npm run build:shell # 构建 Shell登录、更新、加载 Core
# 构建后部署到服务器
cd ..
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
```
---
## 🚀 发布命令
### 自动发布 Core推荐
```bash
cd D:\main\DesignerCEP
python auto_deploy_core.py --version v1.1.2 # 只构建打包
python auto_deploy_core.py --version v1.1.2 --update-db # 构建打包 + 更新数据库
python auto_deploy_core.py --version v1.1.2 --update-db --skip-clean # 不清除缓存
```
### 手动发布步骤
```bash
# 1. 构建
cd D:\main\DesignerCEP\Designer
npm run build:core
# 2. 重命名入口文件
cd dist_core
ren index-core.html index.html
# 3. 打包 ZIP
# 把 dist_core 目录打包成 core-v1.1.2.zip
# 4. 上传到 Server/archives/
```
---
## 🗄️ 服务器命令
### 启动后端服务
```bash
cd D:\main\DesignerCEP\Server
python main.py # 启动 FastAPI 服务器
python -m uvicorn app.main:app --reload # 或使用 uvicorn推荐
```
### 更新数据库版本
```bash
cd D:\main\DesignerCEP\Server
python update_version.py
```
### 部署 Shell 到服务器
```bash
# 1. 构建 Shell
cd D:\main\DesignerCEP\Designer
npm run build:shell
# 2. 复制到服务器
cd ..
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
# 3. 重启服务器
cd Server
python -m uvicorn app.main:app --reload
```
**验证:** 访问 `http://127.0.0.1:8000/` 应该自动跳转到 Shell 登录页
---
## 🧹 清理命令
### 清除客户端缓存
```powershell
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache"
```
### 清除 CEP 扩展缓存
```powershell
Remove-Item -Recurse -Force "$env:APPDATA\Adobe\CEP\extensions\Designer"
Remove-Item -Recurse -Force "$env:APPDATA\Adobe\CEP\extensions\Designer-dev"
```
---
## 📁 重要目录
| 目录 | 说明 |
|------|------|
| `Designer/src/view/` | Core Vue 页面组件 |
| `Designer/src/launcher/` | Shell 源代码(登录、更新器)|
| `Designer/src/api/jsxApi/inline/` | 内联 JSX 函数 |
| `Designer/dist_core/` | Core 构建输出 |
| `Designer/dist/` | Shell 构建输出 |
| `Server/Designer/dist/` | Shell 服务器部署目录 ⭐ |
| `Server/archives/` | Core 版本 ZIP 包存放 |
| `%APPDATA%\DesignerCache\` | 客户端 Core 缓存目录 |
---
## 🌐 URL 访问路径
| URL | 说明 | 文件位置 |
|-----|------|---------|
| `http://127.0.0.1:8000/` | 根路径(自动跳转) | 重定向到 Shell |
| `http://127.0.0.1:8000/shell/` | Shell 登录页 | `Server/Designer/dist/` |
| `http://127.0.0.1:8000/core/v1.2.4/` | Core 业务页 | `%APPDATA%\DesignerCache\v1.2.4\` |
| `http://127.0.0.1:8000/api/v1/` | 后端 API | Server 代码 |
| `http://127.0.0.1:8000/download/` | Core 下载 | `Server/archives/` |
---
## 🔧 开发新功能
### 📝 添加纯前端 JSX 功能
#### 1. 在 layer.ts 添加函数
```typescript
// Designer/src/api/jsxApi/inline/layer.ts
export async function (): Promise<JSXResponse> {
const jsx = `
try {
// JSX 代码
return JSXUtils.stringify({ success: true });
} catch (error) {
return JSXUtils.stringify({ error: error.toString() });
}
`;
return evalInlineJSX(jsx);
}
```
#### 2. 在 Home.vue 调用
```typescript
import * as Layer from '@/api/jsxApi/inline/layer';
const handle新功能 = async () => {
const res = await Layer.();
if (res.success) {
Message.success('成功');
}
};
```
---
### 🔐 添加混合架构功能(前端 + 后端)
**适用场景:** 需要保护核心算法、AI 计算、复杂数学模型等
#### 1. 后端实现Server/app/api/v1/jsx_demo.py
```python
class YourFeatureRequest(BaseModel):
param1: str
param2: int
@router.post("/your-feature")
async def your_feature(
request: YourFeatureRequest,
x_api_key: Optional[str] = Header(None)
):
# 验证 API Key
if not validate_api_key(x_api_key):
raise HTTPException(403, "无效的 API Key")
# 🔒 核心算法(客户端看不到)
result = complex_algorithm(request.param1, request.param2)
return {"success": True, "result": result}
```
#### 2. 前端实现Designer/src/api/jsxApi/inline/your-feature.ts
```typescript
export async function yourFeature(param1: string, param2: number): Promise<JSXResponse> {
// 1. 发送到服务器计算
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/your-feature`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'demo_key_123' // 🔐 API Key
},
body: JSON.stringify({ param1, param2 })
});
const serverResult = await response.json();
// 2. 应用服务器结果到 PS
const jsx = `
// 使用 serverResult 执行 PS 操作
`;
return evalInlineJSX(jsx);
}
```
**参考文档:**
- `tempdocs/framework_guide.md` - 完整框架说明
- `tempdocs/quick_start_template.md` - 快速开发模板
---
## 🧪 测试命令
### 测试混合架构 Demo
```bash
# 1. 启动后端
cd D:\main\DesignerCEP\Server
python -m uvicorn app.main:app --reload
# 2. 在 PS 中创建图层,名称为数学表达式
# 如87-98, 100+200
# 3. 点击"智能配色"按钮
# 前端会获取图层名称 → 发送到服务器计算 → 显示结果
```
### 测试 API Key 验证
```bash
# 使用 curl 测试
curl -X POST http://127.0.0.1:8000/api/v1/jsx_demo/calculate \
-H "Content-Type: application/json" \
-H "X-API-Key: demo_key_123" \
-d '{"expression": "87-98"}'
```
---
## 🔄 完整发布流程
### 发布新版本 Core
```bash
# 1. 修改代码
# 2. 构建 + 打包 + 更新数据库(一键完成)
cd D:\main\DesignerCEP
python auto_deploy_core.py --version v1.2.5 --update-db
# 3. 清除客户端缓存(测试用)
Remove-Item -Recurse -Force "$env:APPDATA\DesignerCache"
# 4. 重新登录测试
```
### 更新 Shell
```bash
# 1. 构建 Shell
cd D:\main\DesignerCEP\Designer
npm run build:shell
# 2. 部署到服务器
cd ..
Copy-Item -Path "Designer\dist" -Destination "Server\Designer\dist" -Recurse -Force
# 3. 重启后端(如果使用 --reload 会自动重启)
```
---
## 📚 相关文档
| 文档 | 说明 |
|------|------|
| `混合架构开发框架指南.md` | 混合架构框架完整指南 |
| `混合架构快速开发模板.md` | 5 分钟快速开发模板 |
| `混合方案Demo说明.md` | 混合方案 Demo 说明 |
| `许可证验证接口文档.md` | 许可证验证接口文档 |
| `后端部署Shell指南.md` | Shell 服务器部署指南 |
| `API密钥使用指南.md` | API Key 使用指南 |

View File

@@ -0,0 +1,416 @@
# ✅ 必须修改清单(上线前)
## 🎯 修改目标
解决 5 个架构问题:
1. ✅ CORS 支持 CEP 环境(`Origin: null`
2. ✅ 生产环境 API 地址配置
3. ✅ 静态文件由 Caddy 处理(性能优化)
4. ✅ Token 通过 Header 传递(已经正确,确认即可)
5. ✅ 使用 Caddy 自动 HTTPS
---
## 📝 需要修改的文件
### 1. 后端 CORS 配置 ⭐ 重要
**文件**: `Server/app/main.py`
**修改前** (第 16-28 行):
```python
# CORS configuration
origins = [
"http://localhost:5173", # Vite default
"http://localhost:3000",
"*" # For development convenience
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
**修改后**:
```python
# ========== CORS 配置 ==========
import os
IS_DEV = os.getenv("ENV", "development") == "development"
if IS_DEV:
# 开发环境:保持宽松
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
else:
# 生产环境:严格配置
allowed_origins = os.getenv("ALLOWED_ORIGINS", "").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=allowed_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allow_headers=["*"],
)
# ✅ CEP 环境特殊处理
@app.middleware("http")
async def cep_cors_middleware(request: Request, call_next):
origin = request.headers.get("origin")
# CEP 的 Origin 是 null 或 cep://
if origin in ["null", None] or (origin and origin.startswith("cep://")):
response = await call_next(request)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "*"
return response
return await call_next(request)
```
**还需要在文件开头添加导入**:
```python
from fastapi import FastAPI, Request # ← 添加 Request
```
---
### 2. 删除 FastAPI 静态文件挂载 ⭐ 重要
**文件**: `Server/app/main.py`
**修改前** (第 36-55 行):
```python
# Mount archives directory for download
app.mount("/download", StaticFiles(directory="archives"), name="download")
# Mount Shell directory (登录页面)
shell_dir = Path(__file__).parent.parent / "Designer"
if shell_dir.exists():
app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
print(f"✓ Shell 已挂载: {shell_dir}")
else:
print(f"⚠️ Shell 目录不存在: {shell_dir}")
print(" 请先运行: cd Designer && npm run build:shell")
# Mount DesignerCache directory to serve Core application files
designer_cache = Path.home() / "AppData" / "Roaming" / "DesignerCache"
if designer_cache.exists():
app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
else:
# Create directory if it doesn't exist
designer_cache.mkdir(parents=True, exist_ok=True)
app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
```
**修改后**:
```python
# ❌ 删除所有静态文件挂载(交给 Caddy 处理)
# 生产环境不需要 FastAPI 处理静态文件
# 可以添加健康检查接口
@app.get("/health")
def health_check():
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
```
---
### 3. 后端环境变量配置
**文件**: `Server/.env`
**新建或修改**:
```bash
# 环境配置
ENV=production
# 项目配置
PROJECT_NAME=DesignerCEP
API_V1_STR=/api/v1
# 安全配置
SECRET_KEY=your-secret-key-here-change-this-in-production
# 数据库配置
DATABASE_URL=mysql://username:password@localhost:3306/designer_cep
# CORS 允许的来源(生产环境)
ALLOWED_ORIGINS=https://your-domain.com,https://www.your-domain.com
# 管理员配置
ADMIN_TOKEN=your-admin-token-here
```
**⚠️ 重要**:将 `your-domain.com` 替换为你的实际域名!
---
### 4. 前端 API 地址配置
**文件**: `Designer/src/config/index.ts`
**修改前** (第 14-16 行):
```typescript
apiServer: isDev
? 'http://127.0.0.1:8000'
: 'http://127.0.0.1:8000', // ❌ 生产环境还是 localhost
```
**修改后**:
```typescript
apiServer: isDev
? 'http://127.0.0.1:8000'
: 'https://your-domain.com', // ✅ 生产环境用线上地址
```
**或者使用环境变量** (推荐):
**新建文件**: `Designer/.env.production`
```bash
VITE_API_SERVER=https://your-domain.com
```
**修改**: `Designer/src/config/index.ts`
```typescript
const isDev = import.meta.env.DEV;
const PROD_API_SERVER = import.meta.env.VITE_API_SERVER || 'http://127.0.0.1:8000';
export const config = {
apiServer: isDev
? 'http://127.0.0.1:8000'
: PROD_API_SERVER, // ✅ 从环境变量读取
apiPrefix: '/api/v1',
shellLoginUrl: isDev
? 'http://localhost:5173/#/login'
: 'https://your-domain.com/shell/#/login', // ✅ 生产环境登录页
get apiBaseUrl() {
return `${this.apiServer}${this.apiPrefix}`;
},
getDownloadUrl(path: string): string {
if (path.startsWith('/')) {
return `${this.apiServer}${path}`;
}
return path;
},
getCoreUrl(version: string): string {
if (isDev) {
return `http://localhost:5173/`;
}
return `${this.apiServer}/core/${version}/index.html`; // ✅ 线上加载
}
};
```
---
### 5. 确认 Token 传递方式(已经正确)✅
**文件**: `Designer/src/utils/request.ts`
**检查** (第 16-18 行):
```typescript
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`; // ✅ 已经正确
}
```
**这个不需要改,已经是对的!**
---
## 🚀 部署步骤
### 步骤 1: 修改代码
```bash
# 1. 后端修改
# - Server/app/main.pyCORS + 删除静态挂载)
# - Server/.env环境变量
# 2. 前端修改
# - Designer/.env.productionAPI 地址)
# - Designer/src/config/index.ts可选
```
### 步骤 2: 本地测试
```bash
# 1. 测试后端
cd Server
ENV=development python -m uvicorn app.main:app --reload
# 访问 http://localhost:8000/health
# 2. 测试前端
cd Designer
npm run dev
# 访问 http://localhost:5173
# 登录测试,检查 Network 标签
```
### 步骤 3: 构建生产版本
```bash
cd Designer
npm run build
# 检查生成的文件
ls -la dist/Shell/
ls -la dist/Designer/
```
### 步骤 4: 部署到服务器
```bash
# 方法 A: 自动化部署(推荐)
cd AdminTool
python auto_deploy_core.py --version 1.0.6 --deploy --update-db
# 方法 B: 手动部署
scp -r dist/Shell/* user@server:/var/www/DesignerCEP/Server/static/shell/
scp -r dist/Designer/* user@server:/var/www/DesignerCEP/Server/static/core/1.0.6/
```
### 步骤 5: 配置 Caddy
参考 `Caddy部署指南.md` 的完整配置。
**关键配置**:
```caddy
your-domain.com {
# API → FastAPI
handle /api/* {
reverse_proxy localhost:8000
}
# Shell 静态文件
handle /shell/* {
root * /var/www/DesignerCEP/Server/static/shell
file_server
}
# Core 静态文件
handle /core/* {
root * /var/www/DesignerCEP/Server/static/core
file_server
}
# 下载文件
handle /downloads/* {
root * /var/www/DesignerCEP/Server/static/downloads
file_server
}
}
```
### 步骤 6: 启动服务
```bash
# 1. 启动 FastAPI
sudo systemctl restart designer-cep
# 2. 启动 Caddy
sudo systemctl restart caddy
# 3. 检查状态
sudo systemctl status designer-cep
sudo systemctl status caddy
```
### 步骤 7: 测试上线
```bash
# 1. 测试 API
curl https://your-domain.com/api/v1/health
# 2. 测试 CEP CORS
curl -X OPTIONS https://your-domain.com/api/v1/client/login \
-H "Origin: null" \
-H "Access-Control-Request-Method: POST" \
-v
# 3. 浏览器测试
# 打开 https://your-domain.com/shell/
# 登录并检查功能
```
---
## 📊 修改影响评估
| 修改项 | 风险 | 是否必须 | 回滚难度 |
|--------|------|---------|---------|
| CORS CEP 支持 | ⭐ 低 | 是 | 容易 |
| 删除静态挂载 | ⭐⭐ 中 | 是 | 中等 |
| 环境变量配置 | ⭐ 低 | 是 | 容易 |
| 前端 API 地址 | ⭐⭐ 中 | 是 | 容易 |
| Token 方式确认 | ⭐ 无 | 否(已正确) | - |
---
## 🔍 常见问题
### Q: 改完后本地开发会受影响吗?
**A**: 不会!代码中有环境判断:
- 开发环境(`ENV=development`保持原样CORS 宽松
- 生产环境(`ENV=production`):严格 CORS + CEP 支持
### Q: 如果改错了怎么办?
**A**:
1. Git 回滚:`git checkout Server/app/main.py`
2. 或保留 FastAPI 静态挂载(性能差一点,但能用)
3. 环境变量改回 `ENV=development`
### Q: 必须用 Caddy 吗?
**A**: 不必须,但强烈推荐:
- ✅ Caddy配置简单自动 HTTPS
- ⚠️ Nginx配置复杂需要手动申请证书
- 二选一即可
---
## ✅ 完成后检查
- [ ] 本地开发环境测试通过
- [ ] 生产构建无错误
- [ ] API 请求正常200
- [ ] CORS 无错误
- [ ] CEP 扩展能登录
- [ ] 浏览器能访问
- [ ] Token 在 Header 里
- [ ] 静态文件正常加载
- [ ] HTTPS 证书有效
---
## 📞 如需帮助
1. 查看日志:`sudo journalctl -u designer-cep -f`
2. 查看 Caddy 日志:`sudo journalctl -u caddy -f`
3. 测试 API`curl -v https://your-domain.com/api/v1/health`
---
**总结**:改动不大,主要是添加环境判断和 CEP CORS 支持,风险可控!

View File

@@ -0,0 +1,865 @@
# 🔧 DesignerCEP 架构问题修正方案
## 问题汇总
经过仔细检查,当前架构存在以下 **5 个严重问题**,会导致本地测试 OK 但上线后炸:
1.**CORS 配置错误**:只写 `file://`CEP 环境实际是 `Origin: null``cep://`
2.**Token 暴露在 URL**`#/home?token=xxx` 会泄露到日志、分享链接
3.**localhost:8000 的硬编码**:文档说 Core 可从 `http://localhost:8000/core/...` 加载,但没说这个服务谁提供
4.**Cloudflare 证书方案冲突**:用 Cloudflare 代理时不应该用 Certbot
5.**静态文件服务混乱**FastAPI 和 Nginx 职责不清
---
## 问题 1: CORS 配置错误
### 当前问题
```python
# Server/app/main.py (当前代码)
origins = [
"http://localhost:5173",
"http://localhost:3000",
"*" # ❌ 这个在生产环境不安全
]
# 部署架构说明.md (第 264 行)
ALLOWED_ORIGINS = [
"https://your-domain.com",
"file://", # ❌ CEP 环境不是 file://
]
```
**实际情况**
- CEP/CEF 环境的 Origin 是 `null``cep://xxx`
- 预检 OPTIONS 请求会因为 Origin 不匹配而失败
- 导致所有 API 调用都会被 CORS blocked
### ✅ 修正方案
```python
# Server/app/main.py
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
import os
app = FastAPI(title=settings.PROJECT_NAME)
# 环境判断
IS_PRODUCTION = os.getenv("ENV", "development") == "production"
# CORS 中间件 - 支持 CEP 环境
if IS_PRODUCTION:
# 生产环境:严格的 CORS
origins = [
"https://your-domain.com",
"https://www.your-domain.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["*"],
)
# 额外处理 CEP 环境的 Origin: null
@app.middleware("http")
async def cep_cors_middleware(request: Request, call_next):
origin = request.headers.get("origin")
# CEP 环境Origin 是 null 或 cep://
if origin in ["null", None] or origin.startswith("cep://"):
response = await call_next(request)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "*"
return response
return await call_next(request)
else:
# 开发环境:宽松的 CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
**关键点**
- ✅ 生产环境只允许你的域名
- ✅ 额外处理 CEP 的 `Origin: null``cep://` 情况
- ✅ OPTIONS 预检请求会正常通过
---
## 问题 2: Token 暴露在 URL
### 当前问题
```typescript
// 文档中写的跳转方式(第 178 行)
/core/1.0.0/#/home?token=xxx&username=xxx&device_id=xxx
// 问题:
// 1. Nginx access.log 会记录 URLtoken 泄露)
// 2. Cloudflare 日志也会记录
// 3. 用户截图/分享会带上 token
// 4. 浏览器历史记录会保存 token
```
**实际代码情况**
- 登录接口返回 token
- 前端保存到 localStorage
- 没有看到 URL 传 token 的代码(好消息!)
但**文档写错了**,容易误导开发。
### ✅ 修正方案
**正确的流程**(代码已经是对的,只需更新文档):
```
1. 用户登录
POST /api/v1/client/login
返回: { token, username, version, permissions }
2. 前端保存到 localStorage
localStorage.setItem('token', token)
localStorage.setItem('username', username)
localStorage.setItem('auto_login', 'true')
3. 跳转到 Core不带 token
/core/1.0.0/#/home
✅ URL 干净,没有敏感信息
4. Core 启动时从 localStorage 读取 token
const token = localStorage.getItem('token')
5. API 请求时通过 Header 传递
Authorization: Bearer ${token}
```
**更新 Axios 配置**
```typescript
// Designer/src/api/request.ts
import axios from 'axios';
import { config } from '@/config';
const request = axios.create({
baseURL: config.apiBaseUrl,
timeout: 30000,
});
// 请求拦截器 - 自动添加 token
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
// ✅ 通过 Header 传递 token不在 URL 里
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器 - 处理 401 自动跳转登录
request.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Token 过期,清除并跳转登录
localStorage.removeItem('token');
localStorage.removeItem('username');
localStorage.removeItem('auto_login');
window.location.href = config.shellLoginUrl;
}
return Promise.reject(error);
}
);
export default request;
```
---
## 问题 3: localhost:8000 的来源不明
### 当前问题
```typescript
// Designer/src/config/index.ts (第 15-16 行)
apiServer: isDev
? 'http://127.0.0.1:8000'
: 'http://127.0.0.1:8000', // ❌ 生产环境也用 localhost
// Designer/src/launcher/utils/updater.ts (第 40 行)
getCoreUrl(version: string): string {
return `${this.apiServer}/core/${version}/index.html`;
}
// 部署架构说明.md (第 65 行)
(https://your-domain.com/core/1.0.0/)
```
**问题**
- 文档说 Core 可能从 `localhost:8000` 加载
- 但没说这个本地服务**谁提供**
- 用户电脑上没有这个服务 → **直接打不开**
### ✅ 修正方案
**方案 A完全在线模式推荐**
Core 始终从服务器加载,不依赖本地服务:
```typescript
// Designer/src/config/index.ts
export const config = {
// 后端 API 服务器地址
apiServer: isDev
? 'http://127.0.0.1:8000' // 开发环境:本地后端
: 'https://your-domain.com', // ✅ 生产环境:线上服务器
// Shell 登录页面地址
shellLoginUrl: isDev
? 'http://localhost:5173/#/login'
: 'https://your-domain.com/shell/#/login', // ✅ 线上 Shell
// 获取 Core 应用加载地址
getCoreUrl(version: string): string {
if (isDev) {
return `http://localhost:5173/`; // 开发环境Vite dev server
}
return `${this.apiServer}/core/${version}/index.html`; // ✅ 线上 Core
}
};
```
**方案 BCEP 扩展离线模式(需要额外开发)**
如果要支持离线使用CEP 扩展从本地加载),需要:
1. **CEP 扩展自带本地服务器**
```typescript
// 在 CEP 扩展中启动一个轻量级 HTTP 服务器(使用 Node.js http-server
import { spawn } from 'child_process';
import path from 'path';
class LocalServer {
private server: any;
start() {
const cacheDir = path.join(os.homedir(), 'AppData', 'Roaming', 'DesignerCache');
// 启动本地 HTTP 服务器
this.server = spawn('http-server', [
cacheDir,
'-p', '8000',
'--cors',
'-c-1' // 禁用缓存
], {
cwd: __dirname,
shell: true
});
console.log('✓ 本地服务器已启动: http://localhost:8000');
}
stop() {
if (this.server) {
this.server.kill();
}
}
}
```
2. **检测端口占用并动态分配**
```typescript
import net from 'net';
async function getAvailablePort(startPort: number): Promise<number> {
return new Promise((resolve) => {
const server = net.createServer();
server.listen(startPort, () => {
const port = (server.address() as any).port;
server.close(() => resolve(port));
});
server.on('error', () => {
resolve(getAvailablePort(startPort + 1));
});
});
}
// 使用
const port = await getAvailablePort(8000);
```
**我的建议**
-**使用方案 A**(完全在线)- 简单可靠
- ⚠️ 方案 B 需要额外开发,且有端口冲突、权限等问题
- 📝 **更新文档**,明确说明 Core 从哪里加载
---
## 问题 4: Cloudflare 证书方案冲突
### 当前问题
```bash
# 部署前检查清单.md - 推荐 Certbot
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
```
**实际情况**
- 你用的是 **Cloudflare 橙云代理**
- Cloudflare 已经提供了 SSL/TLS 加密
- 继续用 Certbot 会遇到:
- Let's Encrypt 验证失败Cloudflare 代理了请求)
- 证书续期问题
- 真实 IP 暴露风险
### ✅ 修正方案
**Cloudflare 场景的正确配置**
#### 1. Cloudflare SSL/TLS 模式
在 Cloudflare 控制台设置:
```
SSL/TLS → Overview → 选择 "Full (Strict)"
```
**模式说明**
-**Flexible**: Cloudflare → 源站是 HTTP不安全
- ⚠️ **Full**: Cloudflare → 源站是 HTTPS但不验证证书
-**Full (Strict)**: Cloudflare → 源站是 HTTPS验证证书- **推荐**
#### 2. 生成 Cloudflare Origin Certificate
在 Cloudflare 控制台:
```
SSL/TLS → Origin Server → Create Certificate
选项:
- 私钥类型RSA (2048)
- 有效期15 年
- 域名:*.your-domain.com, your-domain.com
生成后会得到:
- Origin Certificate (保存为 cloudflare-origin.pem)
- Private Key (保存为 cloudflare-origin.key)
```
#### 3. 配置 Nginx
```nginx
# /etc/nginx/sites-available/designer-cep
server {
listen 443 ssl http2;
server_name your-domain.com www.your-domain.com;
# ✅ 使用 Cloudflare Origin Certificate
ssl_certificate /etc/nginx/ssl/cloudflare-origin.pem;
ssl_certificate_key /etc/nginx/ssl/cloudflare-origin.key;
# SSL 配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Cloudflare Real IP获取真实用户 IP
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
real_ip_header CF-Connecting-IP;
# 其他配置...
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name your-domain.com www.your-domain.com;
return 301 https://$server_name$request_uri;
}
```
#### 4. 安装证书
```bash
# 1. 创建 SSL 目录
sudo mkdir -p /etc/nginx/ssl
sudo chmod 700 /etc/nginx/ssl
# 2. 上传证书文件(从本地上传)
sudo nano /etc/nginx/ssl/cloudflare-origin.pem
# 粘贴 Origin Certificate
sudo nano /etc/nginx/ssl/cloudflare-origin.key
# 粘贴 Private Key
# 3. 设置权限
sudo chmod 600 /etc/nginx/ssl/*
# 4. 测试配置
sudo nginx -t
# 5. 重启 Nginx
sudo systemctl restart nginx
```
**优势**
- ✅ 证书有效期 15 年,不需要续期
- ✅ 不需要 Certbot 验证
- ✅ Cloudflare 和源站双重加密
- ✅ 自动获取真实用户 IP
---
## 问题 5: 静态文件服务混乱
### 当前问题
```python
# Server/app/main.py (第 37-55 行)
# ❌ FastAPI 直接挂载静态文件目录
app.mount("/download", StaticFiles(directory="archives"), name="download")
app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
```
```nginx
# 部署架构说明.md (第 284-289 行)
# ✅ 文档说让 Nginx 处理静态文件
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
```
**矛盾**
- FastAPI 挂载了静态文件 → API 和静态抢资源
- 文档说用 Nginx 缓存 → 但请求还是先到 FastAPI
- 大文件下载shell.zip性能差
### ✅ 修正方案
**明确职责**
- **Nginx** → 处理所有静态文件HTML/JS/CSS/ZIP
- **FastAPI** → 只处理 API 请求(/api/v1/...
#### 1. 修改 FastAPI只留 API
```python
# Server/app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware, Request
import os
from app.core.config import settings
from app.api.v1 import auth, client, admin, analytics, jsx_demo
from app.db import init_db
app = FastAPI(title=settings.PROJECT_NAME)
# 环境判断
IS_PRODUCTION = os.getenv("ENV", "development") == "production"
# ========== CORS 配置 ==========
if IS_PRODUCTION:
origins = [
"https://your-domain.com",
"https://www.your-domain.com",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["*"],
)
# 处理 CEP 环境的 Origin: null
@app.middleware("http")
async def cep_cors_middleware(request: Request, call_next):
origin = request.headers.get("origin")
if origin in ["null", None] or origin.startswith("cep://"):
response = await call_next(request)
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Credentials"] = "true"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "*"
return response
return await call_next(request)
else:
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ========== API 路由 ==========
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["authentication"])
app.include_router(client.router, prefix=f"{settings.API_V1_STR}/client", tags=["client"])
app.include_router(admin.router, prefix=f"{settings.API_V1_STR}/admin", tags=["admin"])
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
app.include_router(jsx_demo.router, prefix=f"{settings.API_V1_STR}/jsx_demo", tags=["jsx_demo"])
# ❌ 删除所有静态文件挂载
# app.mount("/download", ...) # 删除
# app.mount("/shell", ...) # 删除
# app.mount("/core", ...) # 删除
@app.get("/")
def read_root():
return {"message": "DesignerCEP API Server", "version": "1.0.0"}
@app.get("/health")
def health_check():
return {"status": "healthy"}
@app.on_event("startup")
def on_startup():
init_db()
if __name__ == "__main__":
import uvicorn
uvicorn.run("app.main:app", host="127.0.0.1", port=8000, reload=True)
```
#### 2. 配置 Nginx完整版
```nginx
# /etc/nginx/sites-available/designer-cep
# ========== 上游 FastAPI 服务器 ==========
upstream fastapi_backend {
server 127.0.0.1:8000;
}
# ========== HTTPS 服务器 ==========
server {
listen 443 ssl http2;
server_name your-domain.com www.your-domain.com;
# SSL 证书Cloudflare Origin Certificate
ssl_certificate /etc/nginx/ssl/cloudflare-origin.pem;
ssl_certificate_key /etc/nginx/ssl/cloudflare-origin.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Cloudflare Real IP
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
# 日志
access_log /var/log/nginx/designer-cep-access.log;
error_log /var/log/nginx/designer-cep-error.log;
# ========== API 请求(转发到 FastAPI==========
location /api/ {
proxy_pass http://fastapi_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 禁用缓存API 不应该缓存)
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# ========== 静态文件Shell在线登录页==========
location /shell/ {
alias /var/www/DesignerCEP/Server/static/shell/;
try_files $uri $uri/ /shell/index.html;
# HTML 文件:不缓存(保证拿到最新版本)
location ~ \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# JS/CSS长期缓存文件名带 hash
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 图片/字体:长期缓存
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# ========== 静态文件Core核心应用==========
location /core/ {
alias /var/www/DesignerCEP/Server/static/core/;
try_files $uri $uri/ =404;
# HTML 文件:不缓存
location ~ \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# JS/CSS长期缓存
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 图片/字体:长期缓存
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# ========== 下载文件Shell.zip / Core.zip ==========
location /downloads/ {
alias /var/www/DesignerCEP/Server/static/downloads/;
# 允许大文件下载
client_max_body_size 500M;
# 启用断点续传
add_header Accept-Ranges bytes;
# 缓存 ZIP 文件1 天)
expires 1d;
add_header Cache-Control "public";
}
# ========== 根路径 ==========
location / {
return 301 /shell/;
}
# ========== Gzip 压缩 ==========
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
# ========== 安全头 ==========
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
# ========== HTTP → HTTPS 重定向 ==========
server {
listen 80;
server_name your-domain.com www.your-domain.com;
return 301 https://$server_name$request_uri;
}
```
#### 3. 启用配置
```bash
# 1. 测试配置
sudo nginx -t
# 2. 重启 Nginx
sudo systemctl restart nginx
# 3. 验证
curl -I https://your-domain.com/shell/
curl -I https://your-domain.com/api/v1/health
```
---
## 📊 修正后的架构对比
### 修正前(有问题)
```
CEP 扩展
↓ Origin: file:// ❌ CORS 失败
服务器 FastAPI
静态文件 + API 混在一起 ❌ 性能差
Token 在 URL ❌ 泄露风险
```
### 修正后(正确)
```
CEP 扩展
↓ Origin: null ✅ CORS 中间件处理
Nginx
├─> /api/ → FastAPI只处理 API
├─> /shell/ → 静态文件(高性能)
├─> /core/ → 静态文件(高性能)
└─> /downloads/ → 静态文件(支持断点续传)
Token 在 Header ✅ Authorization: Bearer xxx
```
---
## ✅ 总结:修正清单
### 1. 代码修改
- [ ] **Server/app/main.py**
- [ ] 添加 CEP CORS 中间件(处理 `Origin: null`
- [ ] 删除所有静态文件挂载
- [ ] 添加环境判断(生产/开发)
- [ ] **Designer/src/config/index.ts**
- [ ] 生产环境 apiServer 改为 `https://your-domain.com`
- [ ] 生产环境 shellLoginUrl 改为 `https://your-domain.com/shell/#/login`
- [ ] **Designer/src/api/request.ts**
- [ ] 确认 token 通过 `Authorization: Bearer` 传递
- [ ] 确认 401 自动跳转登录
### 2. 服务器配置
- [ ] **Cloudflare**
- [ ] SSL/TLS 模式设为 "Full (Strict)"
- [ ] 生成 Origin Certificate
- [ ] 下载证书和私钥
- [ ] **Nginx**
- [ ] 上传 Cloudflare 证书
- [ ] 更新 Nginx 配置(使用上面的完整配置)
- [ ] 添加 Cloudflare Real IP 配置
- [ ] 配置静态文件缓存策略
- [ ] 重启 Nginx
### 3. 文档更新
- [ ] **部署架构说明.md**
- [ ] 删除 `file://` 的 CORS 说明
- [ ] 添加 `Origin: null``cep://` 的处理方式
- [ ] 删除 `localhost:8000` 的混淆说明
- [ ] 明确 Core 从服务器加载
- [ ] 删除 token 在 URL 的示例
- [ ] **部署前检查清单.md**
- [ ] 删除 Certbot 步骤
- [ ] 添加 Cloudflare Origin Certificate 步骤
### 4. 环境变量
```bash
# Server/.env
ENV=production
PROJECT_NAME=DesignerCEP
API_V1_STR=/api/v1
SECRET_KEY=your-secret-key-here
DATABASE_URL=mysql://user:password@localhost:3306/designer_cep
```
---
## 🧪 测试验证
### 1. CORS 测试
```bash
# CEP 环境测试Origin: null
curl -X OPTIONS https://your-domain.com/api/v1/client/login \
-H "Origin: null" \
-H "Access-Control-Request-Method: POST" \
-v
# 期望输出:
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
```
### 2. Token 安全测试
```bash
# 检查 Nginx 日志,不应该有 token
tail -f /var/log/nginx/designer-cep-access.log
# 期望:只有干净的 URL
# GET /core/1.0.0/#/home HTTP/2.0
# 不应该有GET /core/1.0.0/#/home?token=xxx
```
### 3. 静态文件性能测试
```bash
# 测试缓存头
curl -I https://your-domain.com/shell/assets/index-abc123.js
# 期望输出:
# Cache-Control: public, immutable
# Expires: (一年后的日期)
# 测试 HTML不应该缓存
curl -I https://your-domain.com/shell/index.html
# 期望输出:
# Cache-Control: no-cache, no-store, must-revalidate
```
---
**修正完成后,你的架构就真正可上线了!** 🎉

View File

@@ -0,0 +1,152 @@
# 混合方案 Demo 说明
## 🎯 核心思想
**本地 = 简单执行 | 服务器 = 复杂计算**
---
## 📋 Demo 示例
### 功能:创建智能配色图层
#### 流程:
```
1⃣ 用户点击"智能配色"按钮
2⃣ 前端发送请求到服务器
POST /api/v1/jsx_demo/calculate-color
{
"layer_name": "智能配色_12345",
"base_color": "#FF6B9D"
}
3⃣ 🔒 服务器执行核心算法(客户端看不到)
- 根据图层名称计算最佳颜色
- 计算最佳不透明度
- 选择最佳混合模式
→ 返回结果:
{
"r": 255,
"g": 120,
"b": 180,
"opacity": 85,
"blend_mode": "overlay"
}
4⃣ 前端接收结果,执行简单 JSX
- 创建图层
- 应用服务器计算的参数
- 填充颜色
```
---
## 🔐 安全对比
### 当前内联方式(全在前端)
**前端代码:**
```typescript
const jsx = `
var layer = doc.artLayers.add();
layer.opacity = 85; // ⚠️ 算法暴露
layer.blendMode = BlendMode.OVERLAY;
// 复杂的颜色计算逻辑
var r = 核心算法1(); // ⚠️ 可被看到
var g = 核心算法2(); // ⚠️ 可被看到
var b = 核心算法3(); // ⚠️ 可被看到
`;
```
**问题:** 攻击者可以看到所有代码,复制算法
---
### 混合方式(计算在服务器)
**前端代码:**
```typescript
const colorResult = await fetch('/api/calculate-color', {...});
const { r, g, b, opacity } = colorResult;
const jsx = `
var layer = doc.artLayers.add();
layer.opacity = ${opacity}; // ✅ 只是应用参数
color.rgb.red = ${r}; // ✅ 只是应用参数
color.rgb.green = ${g};
color.rgb.blue = ${b};
`;
```
**服务器代码(客户端看不到):**
```python
# 🔒 核心算法在这里
def calculate_color(layer_name, base_color):
# 复杂的颜色理论计算
# 机器学习模型推荐
# 你的独家算法
return {r, g, b, opacity, blend_mode}
```
**优势:** 攻击者只能看到"发送请求 → 应用结果",看不到算法
---
## 📂 新增文件(不影响现有系统)
```
✅ 保留:
Designer/src/api/jsxApi/inline/layer.ts (原有内联 JSX)
Designer/src/api/jsxApi/inline/document.ts (原有内联 JSX)
新增:
Server/app/api/v1/jsx_demo.py (服务器计算 Demo)
Designer/src/api/jsxApi/inline/hybrid-demo.ts (混合调用 Demo)
```
**两种方式并存,互不影响!**
---
## 🚀 测试 Demo
1. **启动后端**
```bash
cd Server
python main.py
```
2. **启动前端开发模式**
```bash
cd Designer
npm run dev
```
3. **点击"智能配色"按钮**
- 会调用服务器计算颜色
- 然后在本地创建图层并应用
4. **查看控制台**
- 可以看到发送的参数
- 可以看到服务器返回的结果
- **但看不到服务器端的计算逻辑!**
---
## 💡 扩展建议
你可以把**任何核心功能**改成这种方式:
| 功能 | 本地执行 | 服务器计算 |
|------|---------|-----------|
| 创建图层 | ✅ 简单创建 | ❌ |
| 智能配色 | ✅ 应用颜色 | ✅ 计算最佳颜色 |
| AI 设计 | ✅ 应用结果 | ✅ AI 模型推理 |
| 自动排版 | ✅ 移动元素 | ✅ 计算最佳位置 |
| 滤镜参数 | ✅ 应用滤镜 | ✅ 计算最佳参数 |
**核心算法都在服务器,客户端只是"执行器"**

View File

@@ -0,0 +1,282 @@
# 混合方案安全性说明
## 📋 当前实现的安全特性
### 1⃣ 核心算法保护 ✅
**问题:** 如何防止客户端看到核心算法?
**解决:**
- ✅ 核心计算逻辑在服务器端执行
- ✅ 客户端只能看到输入和输出,看不到计算过程
- ✅ 即使客户端打开开发者工具,也无法获取算法
```python
# 🔒 服务器端(客户端看不到)
def calculate_expression(request):
# 这里的算法逻辑客户端完全看不到
result = eval(expression) # 可以替换成复杂的 AI 模型
return result
```
---
### 2⃣ 输入验证 ✅
**问题:** 如何防止恶意输入?
**解决:**
- ✅ 正则表达式验证:只允许数字和基本运算符
- ✅ 拒绝危险字符和代码注入
```python
# 安全检查
if not re.match(r'^[\d\s\+\-\*\/\(\)\.]+$', expression):
return "非法输入"
```
**可接受:** `87-98`, `100+200`, `(10*5)/2`
**拒绝:** `__import__('os')`, `exec('code')`, `sys.exit()`
---
### 3⃣ 详细日志 ✅
**功能:** 监控所有请求和响应
**日志内容:**
```
============================================================
📥 收到计算请求
时间: 2024-12-16 15:30:45
表达式: 87-98
API Key: 未提供(无鉴权模式)
============================================================
🛡️ 安全检查: 验证表达式格式...
✅ 表达式格式验证通过
🔒 开始执行核心算法...
✅ 计算完成: 87-98 = -11
============================================================
📤 返回计算结果
成功: True
表达式: 87-98
结果: -11.0
消息: 计算成功: 87-98 = -11
============================================================
```
---
### 4⃣ API Key 鉴权(可选)🔐
**当前状态:** 已准备好,但默认关闭
#### 如何启用 API Key 验证?
**步骤 1在后端取消注释**
打开 `Server/app/api/v1/jsx_demo.py`,取消注释这段代码:
```python
# 取消注释下面这段
if x_api_key not in VALID_API_KEYS:
logger.warning("❌ API Key 验证失败")
raise HTTPException(status_code=403, detail="无效的 API Key")
logger.info("✅ API Key 验证通过")
```
**步骤 2在前端添加 API Key**
打开 `Designer/src/api/jsxApi/inline/hybrid-demo.ts`
```typescript
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'demo_key_123' // 添加这行
},
body: JSON.stringify({
expression: layerName
})
});
```
---
## 🔐 安全级别对比
### ❌ 纯前端方案(不安全)
```typescript
// 客户端代码(所有人都能看到)
const result = complexAlgorithm(input); // ⚠️ 算法暴露
const jsx = `var layer = doc.artLayers.add(); ...`;
```
**风险:**
- ❌ 核心算法完全暴露
- ❌ 可以轻松复制算法
- ❌ 可以绕过任何验证
---
### ✅ 混合方案(当前实现)
```typescript
// 客户端只发送请求
const result = await fetch('/calculate', { expression: '87-98' });
// 只能看到返回的结果,看不到计算过程
```
**优势:**
- ✅ 核心算法在服务器,客户端看不到
- ✅ 输入验证防止注入攻击
- ✅ 详细日志监控所有请求
---
### 🔐 混合方案 + API Key高级
```typescript
// 客户端需要提供 API Key
const result = await fetch('/calculate', {
headers: { 'X-API-Key': 'your_secret_key' },
body: { expression: '87-98' }
});
```
**优势:**
- ✅ 以上所有优势
- ✅ 只有授权客户端可以调用
- ✅ 可以限制每个 Key 的调用次数
- ✅ 可以追踪谁在使用 API
---
## 💡 进一步加强建议
### 1. 加密传输HTTPS
```bash
# 使用 SSL 证书
uvicorn app.main:app --ssl-keyfile=key.pem --ssl-certfile=cert.pem
```
### 2. 限流Rate Limiting
```python
from slowapi import Limiter
limiter = Limiter(key_func=get_remote_address)
@limiter.limit("10/minute") # 每分钟最多 10 次请求
@router.post("/calculate")
async def calculate_expression(...):
...
```
### 3. IP 白名单
```python
ALLOWED_IPS = {"127.0.0.1", "192.168.1.100"}
@router.post("/calculate")
async def calculate_expression(request: Request):
if request.client.host not in ALLOWED_IPS:
raise HTTPException(403, "IP 未授权")
```
### 4. 结果缓存
```python
from functools import lru_cache
@lru_cache(maxsize=1000)
def calculate(expression: str):
# 相同的表达式不重复计算
return eval(expression)
```
---
## 📊 总结
| 特性 | 状态 | 说明 |
|------|------|------|
| 核心算法保护 | ✅ 已实现 | 算法在服务器端,客户端看不到 |
| 输入验证 | ✅ 已实现 | 正则表达式过滤危险输入 |
| 详细日志 | ✅ 已实现 | 记录所有请求和响应 |
| API Key 鉴权 | 🔧 可选 | 默认关闭,取消注释即可启用 |
| HTTPS 加密 | ⚠️ 推荐 | 生产环境必须启用 |
| 限流保护 | ⚠️ 推荐 | 防止恶意刷接口 |
---
## 🚀 测试安全性
### 测试 1查看网络请求
1. 打开浏览器开发者工具F12
2. 切换到 Network 标签
3. 点击"智能配色"按钮
4. 查看请求详情
**你只能看到:**
- ✅ 请求 URL`/api/v1/jsx_demo/calculate`
- ✅ 请求参数:`{"expression": "87-98"}`
- ✅ 响应结果:`{"success": true, "result": -11}`
**你看不到:**
- ❌ 服务器端的计算逻辑
- ❌ 核心算法代码
- ❌ 其他用户的请求
---
### 测试 2尝试注入攻击
尝试创建一个图层,名称为:`__import__('os').system('rm -rf /')`
**预期结果:**
- ❌ 被拒绝
- 📝 日志显示:`表达式包含非法字符`
- 🛡️ 系统安全
---
### 测试 3查看服务器日志
运行 Demo 后,查看后端控制台输出,应该看到详细的日志:
```bash
cd Server
python -m uvicorn app.main:app --reload
```
**日志示例:**
```
2024-12-16 15:30:45 [INFO] ============================================================
2024-12-16 15:30:45 [INFO] 📥 收到计算请求
2024-12-16 15:30:45 [INFO] 时间: 2024-12-16 15:30:45
2024-12-16 15:30:45 [INFO] 表达式: 87-98
2024-12-16 15:30:45 [INFO] API Key: 未提供(无鉴权模式)
...
```
---
## 🎯 最佳实践建议
### 开发阶段(当前)
- ✅ 无 API Key方便测试
- ✅ 详细日志
- ✅ HTTP 即可
### 生产环境
- 🔐 启用 API Key
- 🔒 启用 HTTPS
- 📊 启用限流
- 📝 日志写入文件
- 🛡️ IP 白名单(可选)
---
**安全性总结:**
当前方案已经实现了**核心算法保护**,客户端无法看到服务器端的计算逻辑。如需进一步加强,可以启用 API Key 验证和其他安全措施。

View File

@@ -0,0 +1,442 @@
# DesignerCEP 混合架构开发框架指南
## 🎯 框架概述
这是一个**三层混合架构框架**,专为 Adobe CEP 插件设计,实现了:
- **核心算法保护**(服务器端计算)
- **动态更新**Shell + Core 分离)
- **安全鉴权**API Key + 详细日志)
---
## 🏗️ 架构层次
```
┌──────────────────────────────────────────────────┐
│ Layer 1: Shell壳/启动器) │
│ 职责: 登录、更新、加载 Core │
│ 更新频率: 极低 │
│ 位置: 本地安装PS 扩展目录) │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Layer 2: Core业务核
│ 职责: UI 界面、简单 JSX 执行 │
│ 更新频率: 高(随时发布新功能) │
│ 位置: 远程服务器 → 下载到本地缓存 │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Layer 3: Server后端服务器
│ 职责: 核心算法、数据处理、鉴权 │
│ 更新频率: 随时 │
│ 位置: 远程服务器 │
└──────────────────────────────────────────────────┘
```
---
## 📝 如何添加新功能
### 示例:添加一个"智能滤镜"功能
#### Step 1: 设计功能流程
```
用户点击按钮
→ Core 获取图层信息
→ Core 发送到 Server带 API Key
→ Server 计算最佳滤镜参数(🔒核心算法)
→ Server 返回参数
→ Core 应用滤镜到图层
```
---
#### Step 2: 后端实现Server 层)
**文件:** `Server/app/api/v1/jsx_demo.py`
```python
class FilterRequest(BaseModel):
"""滤镜计算请求"""
layer_id: str
image_data: str # Base64 编码的图层预览
class FilterResult(BaseModel):
"""滤镜计算结果"""
success: bool
blur_radius: float
sharpen_amount: float
saturation: float
message: str
@router.post("/calculate-filter", response_model=FilterResult)
async def calculate_filter(
request: FilterRequest,
x_api_key: Optional[str] = Header(None)
):
"""
🔒 服务器端智能滤镜计算
客户端只能拿到参数,看不到算法
"""
# 日志
logger.info("="*60)
logger.info("📥 收到滤镜计算请求")
logger.info(f" 图层ID: {request.layer_id}")
logger.info(f" API Key: {x_api_key}")
logger.info("="*60)
# API Key 验证
if not validate_api_key(x_api_key):
logger.warning(f"❌ API Key 验证失败")
raise HTTPException(status_code=403, detail="无效的 API Key")
logger.info("✅ API Key 验证通过")
try:
# 🔒 核心算法在这里(客户端看不到)
# 可以是 AI 模型、图像分析等
# 示例:根据图层 ID 计算参数
layer_hash = sum(ord(c) for c in request.layer_id)
blur_radius = 2.0 + (layer_hash % 10) / 10
sharpen_amount = 0.5 + (layer_hash % 5) / 10
saturation = 1.0 + (layer_hash % 3) / 10
logger.info(f"✅ 计算完成: blur={blur_radius}, sharpen={sharpen_amount}")
return FilterResult(
success=True,
blur_radius=blur_radius,
sharpen_amount=sharpen_amount,
saturation=saturation,
message="滤镜参数计算成功"
)
except Exception as e:
logger.error(f"❌ 计算失败: {str(e)}")
return FilterResult(
success=False,
blur_radius=0,
sharpen_amount=0,
saturation=1.0,
message=f"计算失败: {str(e)}"
)
```
---
#### Step 3: 前端实现Core 层)
**文件:** `Designer/src/api/jsxApi/inline/smart-filter.ts`
```typescript
import { evalInlineJSX, JSXResponse } from './utils';
import { config } from '@/config';
/**
* 智能滤镜:混合方案
* 1. 本地获取图层信息
* 2. 服务器计算最佳参数
* 3. 本地应用滤镜
*/
export async function applySmartFilter(): Promise<JSXResponse> {
try {
// 1. 💻 本地获取图层信息
const jsx = `
try {
if (!$.global.JSXUtils.hasDocument()) {
return $.global.JSXUtils.stringify({ error: '没有打开的文档' });
}
var doc = $.global.JSXUtils.getDocument();
var layer = doc.activeLayer;
return $.global.JSXUtils.stringify({
success: true,
layerId: layer.id,
layerName: layer.name
});
} catch (error) {
return $.global.JSXUtils.stringify({ error: error.toString() });
}
`;
const layerResult = await evalInlineJSX(jsx);
if (layerResult.error || !layerResult.success) {
return layerResult;
}
// 2. 🌐 发送到服务器计算(核心算法)
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/calculate-filter`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'demo_key_123' // 🔐 API Key
},
body: JSON.stringify({
layer_id: layerResult.layerId,
image_data: '' // 可选:发送图层预览
})
});
if (!response.ok) {
return { error: '服务器错误' };
}
const filterResult = await response.json();
if (!filterResult.success) {
return { error: filterResult.message };
}
// 3. 💻 本地应用滤镜参数
const { blur_radius, sharpen_amount, saturation } = filterResult;
const applyJsx = `
try {
var doc = $.global.JSXUtils.getDocument();
var layer = doc.activeLayer;
// 应用服务器计算的滤镜参数
// 高斯模糊
layer.applyGaussianBlur(${blur_radius});
// 锐化
layer.applySharpen();
return $.global.JSXUtils.stringify({
success: true,
message: '智能滤镜应用成功'
});
} catch (error) {
return $.global.JSXUtils.stringify({ error: error.toString() });
}
`;
return evalInlineJSX(applyJsx);
} catch (error) {
return { error: String(error) };
}
}
```
---
#### Step 4: UI 界面Core 层)
**文件:** `Designer/src/view/Home.vue`
```vue
<template>
<a-button @click="handleSmartFilter">智能滤镜</a-button>
</template>
<script setup lang="ts">
import { applySmartFilter } from '@/api/jsxApi/inline/smart-filter';
import { Message } from '@arco-design/web-vue';
const handleSmartFilter = async () => {
try {
Message.loading('正在计算最佳滤镜参数...');
const res = await applySmartFilter();
if (res && res.success) {
Message.success(res.message);
} else {
Message.error(res?.error || '执行失败');
}
} catch (e: any) {
Message.error('调用失败: ' + e.message);
}
};
</script>
```
---
#### Step 5: 部署新功能
```bash
# 1. 构建 Core
cd Designer
npm run build:core
# 2. 发布新版本(使用自动部署脚本)
cd ..
python auto_deploy_core.py
# 3. 用户登录后会自动下载新版本
```
---
## 🔐 API Key 管理
### 添加新的 API Key
**文件:** `Server/app/core/api_keys.py`
```python
VALID_KEYS: Dict[str, dict] = {
"demo_key_123": {
"name": "测试密钥",
"permissions": ["calculate", "filter"], # 添加权限
"rate_limit": 100
},
"customer_abc_456": { # 新客户
"name": "客户 A",
"permissions": ["calculate"],
"rate_limit": 200
}
}
```
### 权限检查
```python
@router.post("/calculate-filter")
async def calculate_filter(request, x_api_key: Optional[str] = Header(None)):
# 验证 Key
if not validate_api_key(x_api_key):
raise HTTPException(403, "无效的 API Key")
# 检查权限
if not APIKeyManager.check_permission(x_api_key, "filter"):
raise HTTPException(403, "没有滤镜功能权限")
# 继续处理...
```
---
## 📊 日志监控
所有请求都会自动记录:
```
============================================================
📥 收到滤镜计算请求
图层ID: Layer_123
API Key: demo_key_123
============================================================
✅ API Key 验证通过 | 名称: 测试密钥 | 权限: ['calculate', 'filter']
🔒 开始执行核心算法...
✅ 计算完成: blur=2.3, sharpen=0.7
============================================================
```
---
## 🚀 扩展方向
### 1. AI 功能
```python
# Server 端
@router.post("/ai-enhance")
async def ai_enhance(image: str, x_api_key: str):
# 调用 TensorFlow/PyTorch 模型
result = ai_model.predict(image)
return result
```
### 2. 批量处理
```python
@router.post("/batch-process")
async def batch_process(layers: List[str], x_api_key: str):
results = []
for layer_id in layers:
result = process_layer(layer_id)
results.append(result)
return results
```
### 3. 数据分析
```python
@router.post("/analyze-design")
async def analyze_design(doc_info: dict, x_api_key: str):
# 分析设计质量、配色方案等
analysis = analyze_composition(doc_info)
return analysis
```
### 4. 实时协作
```python
@router.websocket("/ws/collaborate")
async def collaborate(websocket: WebSocket, x_api_key: str):
# WebSocket 实时同步
await websocket.accept()
# 多用户协作编辑
```
---
## 🛡️ 安全最佳实践
### ✅ 已实现
- API Key 鉴权
- 输入验证
- 详细日志
- 核心算法保护
### ⚠️ 生产环境建议
- 启用 HTTPS
- 添加限流Rate Limiting
- IP 白名单
- 定期更换 API Key
- 数据库存储 Key而非配置文件
---
## 📚 开发流程总结
```mermaid
graph TD
A[设计新功能] --> B[后端实现核心算法]
B --> C[前端调用 API]
C --> D[UI 界面集成]
D --> E[测试]
E --> F[构建 Core]
F --> G[自动部署]
G --> H[用户自动更新]
```
---
## 🎯 框架优势
| 特性 | 传统方案 | 本框架 |
|------|---------|--------|
| 核心算法保护 | ❌ 暴露在前端 | ✅ 服务器端执行 |
| 动态更新 | ❌ 需重装插件 | ✅ 自动下载更新 |
| 安全鉴权 | ❌ 无验证 | ✅ API Key + 日志 |
| 可扩展性 | ⚠️ 受限 | ✅ 易于添加功能 |
| 开发效率 | ⚠️ 中等 | ✅ 模板化开发 |
---
## 🎉 总结
这个框架提供了:
1. **完整的三层架构**Shell → Core → Server
2. **安全的算法保护**(服务器端计算)
3. **灵活的更新机制**(动态加载 Core
4. **标准的开发模式**(模板化添加功能)
5. **完善的监控日志**(追踪所有请求)
**适用场景:**
- Adobe CEP 插件开发
- 需要保护核心算法的应用
- 需要频繁更新的软件
- 需要鉴权和监控的服务
---
**现在你可以基于这个框架快速开发新功能了!** 🚀

View File

@@ -0,0 +1,355 @@
# 混合架构快速开发模板
## 🚀 5 分钟添加新功能
### 模板代码
复制下面的模板,替换 `YOUR_FEATURE` 为你的功能名称。
---
## 📝 Step 1: 后端 API3 层)
**文件:** `Server/app/api/v1/jsx_demo.py`
```python
# ==================== 请求/响应模型 ====================
class YourFeatureRequest(BaseModel):
"""你的功能请求"""
param1: str
param2: int
class YourFeatureResult(BaseModel):
"""你的功能结果"""
success: bool
result_data: dict
message: str
# ==================== API 端点 ====================
@router.post("/your-feature", response_model=YourFeatureResult)
async def your_feature_endpoint(
request: YourFeatureRequest,
x_api_key: Optional[str] = Header(None)
):
"""
🔒 服务器端核心计算
客户端只能拿到结果,看不到算法
"""
# 📝 日志:记录请求
logger.info("="*60)
logger.info("📥 收到请求: YOUR_FEATURE")
logger.info(f" 参数1: {request.param1}")
logger.info(f" 参数2: {request.param2}")
logger.info(f" API Key: {x_api_key}")
logger.info("="*60)
# 🔐 API Key 验证
if not validate_api_key(x_api_key):
logger.warning(f"❌ API Key 验证失败")
raise HTTPException(status_code=403, detail="无效的 API Key")
key_info = get_key_info(x_api_key)
logger.info(f"✅ API Key 验证通过 | 名称: {key_info['name']}")
try:
# 🛡️ 输入验证
logger.info("🛡️ 验证输入参数...")
if not request.param1 or request.param2 < 0:
logger.warning("❌ 参数验证失败")
return YourFeatureResult(
success=False,
result_data={},
message="参数无效"
)
logger.info("✅ 参数验证通过")
# 🔒 核心算法(客户端看不到)
logger.info("🔒 开始执行核心算法...")
# ===== 在这里写你的核心逻辑 =====
result = {
"output1": f"处理结果: {request.param1}",
"output2": request.param2 * 2
}
# ================================
logger.info(f"✅ 计算完成: {result}")
# 📤 返回结果
logger.info("="*60)
return YourFeatureResult(
success=True,
result_data=result,
message="处理成功"
)
except Exception as e:
logger.error(f"❌ 处理失败: {str(e)}")
return YourFeatureResult(
success=False,
result_data={},
message=f"处理失败: {str(e)}"
)
```
---
## 📝 Step 2: 前端 API2 层)
**文件:** `Designer/src/api/jsxApi/inline/your-feature.ts`
```typescript
/**
* YOUR_FEATURE 功能
* 混合方案:本地执行 + 服务器计算
*/
import { evalInlineJSX, JSXResponse } from './utils';
import { config } from '@/config';
export async function yourFeatureFunction(
param1: string,
param2: number
): Promise<JSXResponse> {
try {
// 1. 💻 【可选】本地获取 PS 数据
const getDataJsx = `
try {
if (!$.global.JSXUtils.hasDocument()) {
return $.global.JSXUtils.stringify({ error: '没有打开的文档' });
}
var doc = $.global.JSXUtils.getDocument();
// 获取你需要的数据
var layerName = doc.activeLayer ? doc.activeLayer.name : '';
return $.global.JSXUtils.stringify({
success: true,
layerName: layerName
});
} catch (error) {
return $.global.JSXUtils.stringify({ error: error.toString() });
}
`;
const psData = await evalInlineJSX(getDataJsx);
if (psData.error) {
return psData;
}
// 2. 🌐 发送到服务器计算(核心算法)
const response = await fetch(`${config.apiBaseUrl}/jsx_demo/your-feature`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'demo_key_123' // 🔐 API Key
},
body: JSON.stringify({
param1: param1,
param2: param2
})
});
if (!response.ok) {
return { error: '服务器错误' };
}
const serverResult = await response.json();
if (!serverResult.success) {
return { error: serverResult.message };
}
// 3. 💻 【可选】使用服务器结果执行 PS 操作
const { output1, output2 } = serverResult.result_data;
const applyJsx = `
try {
var doc = $.global.JSXUtils.getDocument();
// 使用服务器计算的结果
// 示例:创建文本图层显示结果
var textLayer = doc.artLayers.add();
textLayer.kind = LayerKind.TEXT;
textLayer.name = "${output1}";
return $.global.JSXUtils.stringify({
success: true,
message: '操作完成'
});
} catch (error) {
return $.global.JSXUtils.stringify({ error: error.toString() });
}
`;
return evalInlineJSX(applyJsx);
} catch (error) {
return { error: String(error) };
}
}
```
---
## 📝 Step 3: UI 界面2 层)
**文件:** `Designer/src/view/Home.vue`
```vue
<template>
<a-button type="primary" @click="handleYourFeature">
你的功能
</a-button>
</template>
<script setup lang="ts">
import { yourFeatureFunction } from '@/api/jsxApi/inline/your-feature';
import { Message } from '@arco-design/web-vue';
const handleYourFeature = async () => {
try {
Message.loading('处理中...');
// 调用混合 API
const res = await yourFeatureFunction('测试参数', 100);
if (res && res.success) {
Message.success(res.message || '操作成功');
} else {
Message.error(res?.error || '执行失败');
}
} catch (e: any) {
Message.error('调用失败: ' + e.message);
}
};
</script>
```
---
## 🔄 开发流程
### 本地测试
```bash
# Terminal 1: 启动后端
cd Server
python -m uvicorn app.main:app --reload
# Terminal 2: 启动前端
cd Designer
npm run dev
```
### 发布新版本
```bash
# 自动构建、打包、发布
python auto_deploy_core.py
```
---
## 📋 检查清单
开发新功能时,确保:
- [ ] 后端添加了 API Key 验证
- [ ] 后端添加了详细日志
- [ ] 后端添加了输入验证
- [ ] 前端使用了正确的 API Key
- [ ] 前端添加了错误处理
- [ ] UI 有加载提示和错误提示
- [ ] 测试了成功和失败的情况
- [ ] 更新了版本号
---
## 🎯 三种常见模式
### 模式 1纯服务器计算
```
前端输入 → 服务器计算 → 前端显示结果
(不涉及 PS 操作)
```
### 模式 2服务器计算 + PS 应用
```
前端获取 PS 数据 → 服务器计算 → 前端应用到 PS
(当前示例)
```
### 模式 3本地执行 + 服务器验证
```
前端执行操作 → 服务器验证权限 → 前端继续
(需要权限控制的操作)
```
---
## 💡 快速参考
### 后端日志模板
```python
logger.info("="*60)
logger.info("📥 收到请求")
logger.info("✅ 验证通过")
logger.info("🔒 开始计算")
logger.info("✅ 计算完成")
logger.info("="*60)
```
### 前端错误处理模板
```typescript
try {
Message.loading('处理中...');
const res = await yourFunction();
if (res?.success) {
Message.success(res.message);
} else {
Message.error(res?.error || '失败');
}
} catch (e: any) {
Message.error('调用失败: ' + e.message);
}
```
### JSX 模板
```typescript
const jsx = `
try {
if (!$.global.JSXUtils.hasDocument()) {
return $.global.JSXUtils.stringify({ error: '没有打开的文档' });
}
var doc = $.global.JSXUtils.getDocument();
// 你的 PS 操作代码
return $.global.JSXUtils.stringify({
success: true,
message: '成功'
});
} catch (error) {
return $.global.JSXUtils.stringify({ error: error.toString() });
}
`;
return evalInlineJSX(jsx);
```
---
## 🎉 完成!
现在你可以:
1. 复制模板代码
2. 替换功能名称
3. 填写核心逻辑
4. 测试
5. 发布
**预计开发时间5-15 分钟/功能**

View File

@@ -0,0 +1,353 @@
# 🚀 DesignerCEP 生产环境部署实操指南
本文档为您提供从零开始部署 DesignerCEP 到 Linux 服务器Ubuntu/Debian/CentOS的完整操作步骤。
---
## 📋 1. 准备工作
### 1.1 服务器与域名
- **服务器**: 建议 Ubuntu 20.04+ (已购买)
- **域名**: `your-domain.com` (已购买)
- **DNS 解析**: 请将域名 A 记录解析到服务器公网 IP。
### 1.2 本地文件准备
在您的电脑上创建一个 `deployment` 文件夹,用于存放准备上传的文件。
#### A. 后端代码
复制 `Server` 目录下的以下文件/文件夹到 `deployment/Server`
- `app/` (文件夹)
- `requirements.txt`
- `.env` (稍后修改)
- `main.py` (如果有入口文件在根目录,或者确认入口是 `app.main`)
#### B. 前端构建
1. **配置生产环境地址**
修改 `Designer/.env.production`
```env
VITE_API_SERVER=https://your-domain.com
```
2. **构建项目**
在 `Designer` 目录下运行:
```bash
npm run build
```
这将生成 `Designer/dist` 目录,包含 `Shell` 和 `Designer` (Core) 两个文件夹。
3. **打包 Shell**
将 `Designer/dist/Shell` 文件夹压缩为 `shell-1.0.1.zip` (版本号请参考 package.json)。
#### C. 最终上传清单
您的 `deployment` 文件夹结构应如下:
```
deployment/
├── Server/ # 后端代码
│ ├── app/
│ └── requirements.txt
├── Shell/ # 前端 Shell (来自 dist/Shell)
├── Core/ # 前端 Core (来自 dist/Designer)
└── shell-1.0.1.zip # 压缩包 (来自 dist/Shell)
```
---
## 🖥️ 2. 服务器环境安装
使用 SSH 登录您的服务器:
```bash
ssh root@your-server-ip
```
### 2.1 安装基础软件
```bash
# Ubuntu/Debian
sudo apt update
sudo apt install -y python3 python3-pip python3-venv unzip curl
# 安装 Caddy (Web 服务器)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
```
---
## 📂 3. 上传与部署文件
### 3.1 创建目录结构
```bash
# 创建应用根目录
sudo mkdir -p /var/www/DesignerCEP/Server
# 创建静态文件目录
sudo mkdir -p /var/www/DesignerCEP/Server/static/shell
sudo mkdir -p /var/www/DesignerCEP/Server/static/core/1.0.1
sudo mkdir -p /var/www/DesignerCEP/Server/static/downloads
# 设置权限
sudo chown -R www-data:www-data /var/www/DesignerCEP
sudo chmod -R 755 /var/www/DesignerCEP
```
### 3.2 上传文件 (在本地执行)
打开新的终端窗口(本地),执行上传命令:
```bash
# 假设您在 deployment 目录的上级目录
cd path/to/deployment
# 1. 上传后端
scp -r Server/* root@your-server-ip:/var/www/DesignerCEP/Server/
# 2. 上传 Shell 静态文件
scp -r Shell/* root@your-server-ip:/var/www/DesignerCEP/Server/static/shell/
# 3. 上传 Core 静态文件 (注意:这里是上传特定版本)
# 如果您有多个版本,请重复此步骤,例如 1.0.0, 1.0.1 等
scp -r Core/* root@your-server-ip:/var/www/DesignerCEP/Server/static/core/1.0.1/
# 4. 上传 Shell 压缩包
scp shell-1.0.1.zip root@your-server-ip:/var/www/DesignerCEP/Server/static/downloads/
# 5. 上传历史版本 (如果您有 archives 文件夹)
# 假设您本地有一个 archives 文件夹包含 core-v1.0.0.zip 等
# scp -r archives/* root@your-server-ip:/var/www/DesignerCEP/Server/archives/
# 并在服务器上解压到 /var/www/DesignerCEP/Server/static/core/
```
### 3.3 批量上传历史版本(可选)
如果您需要一次性部署多个历史版本,可以按照以下步骤操作:
1. **在本地准备版本**
将所有需要部署的 `core-vX.X.X.zip` 文件放在一个文件夹中,例如 `all_versions`。
2. **上传到服务器**
```bash
scp -r all_versions/*.zip root@your-server-ip:/var/www/DesignerCEP/Server/archives/
```
3. **在服务器上解压**
登录服务器,运行以下命令批量解压:
```bash
cd /var/www/DesignerCEP/Server/archives
# 安装 unzip (如果未安装)
sudo apt install unzip
# 遍历解压所有 core zip 文件到 static/core
for f in core-v*.zip; do
# 提取版本号 (假设文件名格式为 core-v1.0.0.zip)
version=$(echo "$f" | sed 's/core-v//' | sed 's/.zip//')
echo "Deploying version $version..."
sudo mkdir -p "/var/www/DesignerCEP/Server/static/core/$version"
sudo unzip -o "$f" -d "/var/www/DesignerCEP/Server/static/core/$version"
done
# 修正权限
sudo chown -R www-data:www-data /var/www/DesignerCEP/Server/static/core
```
### 3.4 修正权限 (服务器端)
```bash
sudo chown -R www-data:www-data /var/www/DesignerCEP
```
---
## ⚙️ 4. 后端服务配置
### 4.1 安装 Python 依赖
```bash
cd /var/www/DesignerCEP/Server
# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate
# 安装依赖
pip install -r requirements.txt
pip install gunicorn uvicorn[standard]
```
### 4.2 配置环境变量
创建 `.env` 文件:
```bash
nano .env
```
粘贴以下内容(**已为您预填好域名**
```env
ENV=production
PROJECT_NAME=DesignerCEP
API_V1_STR=/api/v1
SECRET_KEY=generate-a-secure-random-key-here
ACCESS_TOKEN_EXPIRE_MINUTES=43200
DATABASE_URL=sqlite:///./designercep.db
# 关键:允许 CORS 的域名
ALLOWED_ORIGINS=https://aidg168.uk,https://www.aidg168.uk,https://backend.aidg168.uk
# 邮箱配置 (可选,如果您需要发送邮件)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
EMAILS_FROM_EMAIL=your-email@gmail.com
EMAILS_FROM_NAME=DesignerCEP
```
### 4.3 配置 Systemd 服务
创建服务文件:
```bash
sudo nano /etc/systemd/system/designer-cep.service
```
粘贴内容:
```ini
[Unit]
Description=DesignerCEP Backend
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/DesignerCEP/Server
Environment="PATH=/var/www/DesignerCEP/Server/venv/bin"
ExecStart=/var/www/DesignerCEP/Server/venv/bin/gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8000
Restart=always
[Install]
WantedBy=multi-user.target
```
启动服务:
```bash
sudo systemctl daemon-reload
sudo systemctl enable designer-cep
sudo systemctl start designer-cep
sudo systemctl status designer-cep # 检查是否显示 Active: active (running)
```
---
## 🌐 5. Caddy 配置 (HTTPS & 反向代理)
**⚠️ 关键提示Cloudflare SSL 模式必须设置为 Full (Strict)**
编辑 Caddy 配置文件:
```bash
sudo nano /etc/caddy/Caddyfile
```
**清空原有内容,粘贴以下内容**
```caddy
# ========== 主域名 (Shell & Core & Downloads) ==========
aidg168.uk, www.aidg168.uk {
# 1. Shell 静态页面
handle /shell/* {
root * /var/www/DesignerCEP/Server/static/shell
try_files {path} {path}/ /shell/index.html
file_server
}
# 2. Core 核心应用
handle /core/* {
root * /var/www/DesignerCEP/Server/static/core
file_server
}
# 3. 下载文件
handle /downloads/* {
root * /var/www/DesignerCEP/Server/static/downloads
file_server
}
# 4. 根路径跳转到 Shell
handle / {
redir /shell/ permanent
}
# 5. 压缩与日志
encode gzip
log {
output file /var/log/caddy/designer-cep.log
}
}
# ========== API 专用域名 (FastAPI) ==========
backend.aidg168.uk {
# 1. API 代理到后端 (FastAPI)
reverse_proxy 127.0.0.1:8000
# 2. 压缩与日志
encode gzip
log {
output file /var/log/caddy/designer-cep-api.log
}
}
```
重启 Caddy
```bash
sudo systemctl restart caddy
sudo systemctl status caddy
```
---
## 🔒 6. 安全与网络检查 (必做!)
**1. 确认 Cloudflare SSL 模式**
- 登录 Cloudflare 控制台 -> SSL/TLS -> Overview
- **必须选择**: `Full (Strict)`
- *原因*: Caddy 会自动申请 Let's Encrypt 证书Cloudflare 需要信任这个有效证书。
**2. 确认防火墙端口**
在服务器上执行:
```bash
sudo ufw allow 80
sudo ufw allow 443
sudo ufw reload
```
**3. 确认 Caddy 监听状态**
在服务器上执行:
```bash
sudo ss -lntp | grep 443
```
*预期输出*: 应该看到 `caddy` 进程正在监听 `*:443`。
**4. 快速自检 (在服务器上执行)**
```bash
# 1. 检查 DNS (应返回 Cloudflare IP)
dig backend.aidg168.uk +short
# 2. 本机测试 API (绕过 Cloudflare)
curl -v http://127.0.0.1:8000/health
# 3. 域名测试 (走公网)
curl -I https://backend.aidg168.uk/health
```
---
## ✅ 7. 验证部署
打开浏览器访问:
1. **API 健康检查**: `https://backend.aidg168.uk/health` (应返回 `{"status": "healthy"}`)
2. **Shell 页面**: `https://aidg168.uk/shell/` (应显示登录页)
3. **Core 版本**: `https://aidg168.uk/core/1.0.1/index.html` (确认版本存在)
---
## 🔄 更新维护
### 更新前端
1. 本地 `npm run build`
2. 上传新文件到对应目录
3. 不需要重启服务
### 更新后端
1. 上传新代码到 `/var/www/DesignerCEP/Server`
2. 重启服务:`sudo systemctl restart designer-cep`

View File

@@ -0,0 +1,56 @@
# EmptyKid 知乎专栏文章汇总与摘要
由于网络请求限制,以下整理了**EmptyKid**专栏的最新文章目录,并对部分能够获取内容的核心文章进行了摘要。
## 📂 文章索引 (按照最新发布排序)
### PS 插件与 CEP 开发教程系列
1. **[【CEP教程-17】插件的打包和发布](https://zhuanlan.zhihu.com/p/27361054277)**
2. [【CEP教程-16】JSX的工程化](https://zhuanlan.zhihu.com/p/22605290525)
3. [【CEP教程-15】前端框架在插件面板中的应用](https://zhuanlan.zhihu.com/p/683712943)
4. [【CEP教程-14】数据存储相关](https://zhuanlan.zhihu.com/p/675795467)
5. [【CEP教程-13】nodejs在插件开发中的应用](https://zhuanlan.zhihu.com/p/661392566)
6. [【CEP教程-12】如何从Ps中导出图片](https://zhuanlan.zhihu.com/p/658067352)
7. [【CEP教程-11】生成器](https://zhuanlan.zhihu.com/p/643541900)
8. [【CEP教程-10】图层处理那些事](https://zhuanlan.zhihu.com/p/617477492)
9. [【CEP教程-10】Action Manager完全指南 - 下篇](https://zhuanlan.zhihu.com/p/608104095)
10. [【CEP教程-9】Action Manager完全指南 - 中篇](https://zhuanlan.zhihu.com/p/601014597)
11. [【CEP教程-8】Action Manager完全指南 - 上篇](https://zhuanlan.zhihu.com/p/600014746)
12. [Photoshop插件开发教程 - 7JSX脚本指南 - DOM篇](https://zhuanlan.zhihu.com/p/596166382)
13. [Photoshop插件开发教程 - 6面板与宿主之间的交互](https://zhuanlan.zhihu.com/p/566983957)
14. [Photoshop插件开发教程 - 5插件面板的样式](https://zhuanlan.zhihu.com/p/563847844)
15. [Photoshop插件开发教程 - 4开发工具选择和调试](https://zhuanlan.zhihu.com/p/559290141)
16. [Photoshop插件开发教程 - 3CEP插件面板结构介绍](https://zhuanlan.zhihu.com/p/555070606)
17. [Photoshop插件开发教程 - 2开发环境搭建](https://zhuanlan.zhihu.com/p/532152091)
18. [Photoshop插件开发教程 - 1插件类型](https://zhuanlan.zhihu.com/p/518229060)
### UXP 开发系列
1. [【UXP教程-2】UXP插件开发起步](https://zhuanlan.zhihu.com/p/20904402159)
2. [【Adobe UXP插件开发中文教程】- 1. 简介](https://zhuanlan.zhihu.com/p/600569875)
### 其他分享
1. [我给三年级女儿开发了一个打字网站](https://zhuanlan.zhihu.com/p/676856628)
---
## 📝 精选摘要
### 1. 插件的打包和发布 (CEP教程-17)
**核心内容要点:**
* **格式**: CEP 插件最终需要打包为 `.zxp` 格式才能分发。
* **工具**: 使用 Adobe 官方提供的 `ZXPSignCmd` 命令行工具。
* **流程**:
1. 准备好完整的插件目录(包含 `CSXS/manifest.xml`)。
2. 需要一个数字证书(`.p12`),可以使用 `ZXPSignCmd` 自行生成自签名证书。
3. 运行打包命令,将源码目录和证书合并生成 `.zxp` 文件。
* **安装**: 用户可以使用第三方工具(如 ZXPInstaller或专门的 Extension Manager 来安装 `.zxp` 文件。
### 2. 我给三年级女儿开发了一个打字网站
**背景**: 作者因为目前国内访问国外的 TypingClub 困难,且学校要求练习打字,于是为还在上三年级的女儿开发了一个免费的打字练习网站。
**特点**:
* 针对小学生优化。
* 解决了访问速度和稳定性问题。
* (推测) 包含基础的指法练习和进度记录。
---
> **注意**: 由于知乎的访问安全策略,目前只能提取到文章列表。如需其他文章的详细内容,建议在该浏览器中保持登录状态并手动复制文本给我。

164
tempdocs/紧急诊断.md Normal file
View File

@@ -0,0 +1,164 @@
# 🚨 紧急诊断:所有 JSX 方法都失败
## 😰 当前状况
**所有测试方法都失败了**,这说明问题很严重。让我们逐步排查。
---
## 🔍 诊断步骤
### 步骤 1确认 JSX 文件是否被加载
**在浏览器控制台查看:**
1. 是否有 `[__LDX] Success` 日志?
2. 是否有 `Ultra Simple JSX Loaded` 日志?
**如果没有这些日志:**
- ❌ JSX 文件根本没被加载
- 原因:路径不对或文件不存在
**如果有这些日志:**
- ✅ JSX 文件已加载
- 但函数调用失败
---
### 步骤 2测试最简单的函数
**在浏览器控制台执行:**
```javascript
// 测试1最简单的字符串返回
cep.evalScript("test1()").then(r => console.log('Test1:', r))
// 测试2简单计算
cep.evalScript("test2()").then(r => console.log('Test2:', r))
// 测试3获取 PS 名称
cep.evalScript("test3()").then(r => console.log('Test3:', r))
// 测试4检查文档
cep.evalScript("test4()").then(r => console.log('Test4:', r))
// 测试5创建图层
cep.evalScript("test5()").then(r => console.log('Test5:', r))
```
**预期结果:**
```
Test1: "test1_success"
Test2: "1+1=2"
Test3: "app_name=Adobe Photoshop"
Test4: "has_document" 或 "no_document"
Test5: "layer_created" 或 "error=..."
```
---
### 步骤 3如果还是全部失败
**可能的原因:**
#### 原因 1路径错误
控制台显示的路径是:
```
C:/Users/35780/AppData/Roaming/DesignerCache/v1.0.7/jsx/index.js
```
**检查这个文件是否存在:**
```powershell
# 在 PowerShell 中运行
Get-Content "$env:APPDATA\DesignerCache\v1.0.7\jsx\index.js" -Head 5
```
**应该看到:**
```javascript
/**
* 超级简化版 JSX 测试
*/
```
**如果看不到或报错:**
- 文件不存在或内容不对
- 需要重新复制
#### 原因 2CEP 版本问题
**检查 CEP 版本:**
```javascript
// 在控制台运行
console.log('CEP Version:', (window as any).__adobe_cep__?.getCurrentImsUserId ? 'CEP 7+' : 'Unknown')
```
#### 原因 3evalScript 本身有问题
**测试 evalScript 是否工作:**
```javascript
// 最简单的测试
cep.evalScript("1+1").then(r => console.log('Basic calc:', r))
// 测试 app 对象
cep.evalScript("app.name").then(r => console.log('App name:', r))
// 测试返回字符串
cep.evalScript("'hello world'").then(r => console.log('String:', r))
```
---
## 🎯 请告诉我
测试完成后,请告诉我:
### 1. JSX 是否被加载?
- [ ] 看到了 `[__LDX] Success` 日志
- [ ] 看到了 `Ultra Simple JSX Loaded` 日志
- [ ] 都没看到
### 2. 简单测试的结果
```
test1(): ______________
test2(): ______________
test3(): ______________
test4(): ______________
test5(): ______________
```
### 3. evalScript 基本测试
```
1+1: ______________
app.name: ______________
'hello world': ______________
```
### 4. 文件是否存在?
```powershell
Get-Content "$env:APPDATA\DesignerCache\v1.0.7\jsx\index.js" -Head 5
```
结果: ______________
---
## 💡 可能的解决方案
根据上面的测试结果,我们可以确定:
### 如果 JSX 没被加载
→ 路径问题,需要找到正确的路径
### 如果 JSX 被加载但函数失败
→ evalScript 调用方式问题
### 如果 evalScript 基本测试也失败
→ CEP 环境本身有问题
---
**现在请按照步骤 1-4 测试,并告诉我结果!** 🔍

View File

@@ -0,0 +1,634 @@
# DesignerCEP 线上部署指南
## 📋 部署架构概览
本项目包含三个主要部分:
1. **Shell登录壳**: 本地 CEP 扩展,负责登录和版本管理
2. **Core核心应用**: 通过后端服务器提供的 Web 应用
3. **Backend后端服务**: FastAPI 服务器,提供 API 和静态文件服务
---
## 🚀 一、前端部署
### 1.1 构建前准备
#### ✅ 关闭生产环境日志
`main.ts` 或应用入口添加:
```typescript
import { logger } from '@/utils/logger';
// 生产环境关闭日志
if (import.meta.env.PROD) {
logger.disable();
} else {
logger.enable();
}
```
或者在 `logger.ts` 中修改默认状态:
```typescript
class Logger {
// 生产环境默认关闭,开发环境默认开启
private _enabled: boolean = import.meta.env.DEV;
// ...
}
```
#### ✅ 配置生产环境 API 地址
创建 `.env.production` 文件:
```env
# 生产环境配置
VITE_API_BASE_URL=https://your-domain.com/api/v1
VITE_API_SERVER=https://your-domain.com
```
修改 `config.ts`
```typescript
export const config = {
// 从环境变量读取
apiBaseUrl: import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:8000/api/v1',
apiServer: import.meta.env.VITE_API_SERVER || 'http://127.0.0.1:8000',
// ...其他配置
};
```
### 1.2 构建命令
```bash
# 进入 Designer 目录
cd Designer
# 安装依赖(如果还没安装)
npm install
# 构建生产版本
npm run build
# 构建完成后,产物在 dist/ 目录
```
### 1.3 构建产物说明
```
Designer/dist/
├── Shell/ # Shell 登录壳(本地 CEP 扩展使用)
│ ├── index.html
│ ├── assets/
│ └── ...
└── Designer/ # Core 核心应用(上传到服务器)
├── index.html
├── assets/
└── ...
```
**重要说明:**
- `dist/Shell/` → 需要部署到服务器的两个地方:
- 打包为 `.zip` 供 CEP 扩展下载:`static/shell/shell-{version}.zip`
- 部署为在线登录页:`static/shell/` (直接部署文件)
- `dist/Designer/` → 上传到服务器的 `static/core/{version}/` 目录
---
## 🖥️ 二、后端部署
### 2.1 服务器要求
- **操作系统**: Linux (推荐 Ubuntu 20.04+) 或 Windows Server
- **Python 版本**: Python 3.9+
- **内存**: 最低 1GB推荐 2GB+
- **磁盘**: 至少 10GB 可用空间(用于存储版本文件)
- **域名**: 需要一个域名并配置 DNS
- **SSL 证书**: 建议使用 Let's Encrypt 免费证书
### 2.2 部署步骤
#### 步骤 1: 上传代码
```bash
# 方式 1: Git 克隆(推荐)
ssh user@your-server
cd /var/www
git clone https://your-repo-url.git DesignerCEP
cd DesignerCEP/Server
# 方式 2: SCP 上传
# 本地执行
scp -r Server/ user@your-server:/var/www/DesignerCEP/
```
#### 步骤 2: 创建虚拟环境
```bash
cd /var/www/DesignerCEP/Server
# 创建虚拟环境
python3 -m venv venv
# 激活虚拟环境
source venv/bin/activate # Linux
# 或
venv\Scripts\activate # Windows
# 安装依赖
pip install -r requirements.txt
```
#### 步骤 3: 配置环境变量
创建 `.env` 文件:
```bash
# /var/www/DesignerCEP/Server/.env
# 基础配置
PROJECT_NAME=DesignerCEP
VERSION=1.0.0
DEBUG=False
# 安全配置
SECRET_KEY=your-super-secret-key-change-this-in-production
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=43200
# 数据库配置(如果使用)
DATABASE_URL=sqlite:///./designer.db
# 或使用 PostgreSQL/MySQL
# DATABASE_URL=postgresql://user:password@localhost/dbname
# CORS 配置
ALLOWED_ORIGINS=https://your-domain.com,https://www.your-domain.com
# 静态文件路径
STATIC_DIR=/var/www/DesignerCEP/Server/static
SHELL_DIR=/var/www/DesignerCEP/Server/static/shell
CORE_DIR=/var/www/DesignerCEP/Server/static/core
```
#### 步骤 4: 准备静态文件目录
```bash
# 创建目录结构
mkdir -p static/shell
mkdir -p static/core/1.0.0
# 1. 上传 Shell 在线登录页(用于网页访问)
scp -r Designer/dist/Shell/* user@your-server:/var/www/DesignerCEP/Server/static/shell/
# 2. 打包 Shell 供 CEP 扩展下载
cd Designer/dist
zip -r shell-1.0.0.zip Shell/
scp shell-1.0.0.zip user@your-server:/var/www/DesignerCEP/Server/static/downloads/
cd ../..
# 3. 上传 Core 构建产物
scp -r Designer/dist/Designer/* user@your-server:/var/www/DesignerCEP/Server/static/core/1.0.0/
```
目录结构应该是:
```
Server/
├── app/
├── static/
│ ├── shell/ # Shell 在线登录页
│ │ ├── index.html
│ │ ├── assets/
│ │ └── ...
│ ├── downloads/ # 下载文件(供 CEP 扩展)
│ │ └── shell-1.0.0.zip
│ └── core/
│ └── 1.0.0/ # Core 核心应用
│ ├── index.html
│ ├── assets/
│ └── ...
├── .env
├── requirements.txt
└── main.py
```
**说明:**
- `static/shell/` - Shell 在线登录页,用户退出登录后访问这里
- `static/downloads/shell-1.0.0.zip` - 供 CEP 扩展首次下载的 Shell 安装包
- `static/core/1.0.0/` - Core 核心应用,用户登录后使用
#### 步骤 5: 使用 Gunicorn 运行(生产环境)
安装 Gunicorn
```bash
pip install gunicorn uvicorn[standard]
```
创建启动脚本 `start.sh`:
```bash
#!/bin/bash
cd /var/www/DesignerCEP/Server
source venv/bin/activate
gunicorn app.main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--access-logfile logs/access.log \
--error-logfile logs/error.log \
--daemon
```
```bash
chmod +x start.sh
./start.sh
```
#### 步骤 6: 使用 Systemd 管理服务(推荐)
创建服务文件 `/etc/systemd/system/designer-cep.service`:
```ini
[Unit]
Description=DesignerCEP Backend Service
After=network.target
[Service]
Type=notify
User=www-data
Group=www-data
WorkingDirectory=/var/www/DesignerCEP/Server
Environment="PATH=/var/www/DesignerCEP/Server/venv/bin"
ExecStart=/var/www/DesignerCEP/Server/venv/bin/gunicorn \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
app.main:app
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
```
启动服务:
```bash
# 重载 systemd
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start designer-cep
# 设置开机自启
sudo systemctl enable designer-cep
# 查看状态
sudo systemctl status designer-cep
# 查看日志
sudo journalctl -u designer-cep -f
```
---
## 🔧 三、Nginx 反向代理配置
### 3.1 安装 Nginx
```bash
sudo apt update
sudo apt install nginx
```
### 3.2 配置 Nginx
创建配置文件 `/etc/nginx/sites-available/designer-cep`:
```nginx
# HTTP 重定向到 HTTPS
server {
listen 80;
listen [::]:80;
server_name your-domain.com www.your-domain.com;
# Let's Encrypt 验证
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 重定向到 HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS 配置
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your-domain.com www.your-domain.com;
# SSL 证书配置
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 日志
access_log /var/log/nginx/designer-cep-access.log;
error_log /var/log/nginx/designer-cep-error.log;
# 客户端最大上传大小(用于大文件上传)
client_max_body_size 100M;
# API 代理
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持(如果需要)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Shell 在线登录页
location /shell/ {
alias /var/www/DesignerCEP/Server/static/shell/;
try_files $uri $uri/ /shell/index.html;
# HTML 不缓存,资源文件缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 7d;
add_header Cache-Control "public, immutable";
}
}
# 下载文件(供 CEP 扩展下载)
location /downloads/ {
alias /var/www/DesignerCEP/Server/static/downloads/;
autoindex off;
# 强制下载
add_header Content-Disposition "attachment";
# 缓存配置
expires 7d;
add_header Cache-Control "public, immutable";
}
# Core 静态文件
location /core/ {
alias /var/www/DesignerCEP/Server/static/core/;
try_files $uri $uri/ =404;
# SPA 路由支持(如果需要)
# try_files $uri $uri/ /core/$1/index.html;
# 缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}
# 健康检查
location /health {
proxy_pass http://127.0.0.1:8000/health;
access_log off;
}
}
```
启用配置:
```bash
# 创建软链接
sudo ln -s /etc/nginx/sites-available/designer-cep /etc/nginx/sites-enabled/
# 测试配置
sudo nginx -t
# 重载 Nginx
sudo systemctl reload nginx
```
### 3.3 配置 SSL 证书Let's Encrypt
```bash
# 安装 Certbot
sudo apt install certbot python3-certbot-nginx
# 获取证书
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
# 自动续期(已自动配置 cron
sudo certbot renew --dry-run
```
---
## 🔒 四、安全配置
### 4.1 防火墙配置
```bash
# 允许 SSH
sudo ufw allow 22/tcp
# 允许 HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 启用防火墙
sudo ufw enable
# 查看状态
sudo ufw status
```
### 4.2 生产环境安全检查清单
- [ ] 修改 `SECRET_KEY` 为随机强密钥
- [ ] 设置 `DEBUG=False`
- [ ] 配置正确的 `ALLOWED_ORIGINS`
- [ ] 使用 HTTPSSSL 证书)
- [ ] 配置数据库(不使用 SQLite
- [ ] 设置文件上传大小限制
- [ ] 配置日志轮转
- [ ] 关闭前端日志输出
- [ ] 定期备份数据库和用户数据
- [ ] 配置监控和告警
---
## 📦 五、版本更新流程
### 5.1 发布新版本
```bash
# 1. 本地构建新版本
cd Designer
npm run build
# 2. 上传到服务器
scp -r dist/Designer/* user@your-server:/var/www/DesignerCEP/Server/static/core/1.0.1/
# 3. 更新后端版本配置
# 在后端 API 中更新 current_version 为 1.0.1
```
### 5.2 版本管理建议
```
static/core/
├── 1.0.0/ # 旧版本保留
├── 1.0.1/ # 当前版本
└── 1.0.2/ # 新版本
```
后端 API 返回最新版本号,客户端自动下载更新。
---
## 🔍 六、监控与日志
### 6.1 日志管理
创建日志轮转配置 `/etc/logrotate.d/designer-cep`:
```
/var/www/DesignerCEP/Server/logs/*.log {
daily
rotate 30
compress
delaycompress
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload designer-cep > /dev/null 2>&1 || true
endscript
}
```
### 6.2 性能监控
推荐使用:
- **PM2**: 进程管理和监控
- **Prometheus + Grafana**: 指标监控
- **Sentry**: 错误追踪
- **ELK Stack**: 日志分析
---
## 🚀 七、快速部署脚本
创建 `deploy.sh` 一键部署脚本:
```bash
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} DesignerCEP 自动部署脚本${NC}"
echo -e "${GREEN}========================================${NC}"
# 1. 拉取最新代码
echo -e "\n${YELLOW}[1/6] 拉取最新代码...${NC}"
git pull origin main
# 2. 构建前端
echo -e "\n${YELLOW}[2/6] 构建前端...${NC}"
cd Designer
npm install
npm run build
cd ..
# 3. 上传 Core 到服务器
echo -e "\n${YELLOW}[3/6] 上传 Core 到服务器...${NC}"
VERSION=$(node -p "require('./Designer/package.json').version")
ssh user@your-server "mkdir -p /var/www/DesignerCEP/Server/static/core/$VERSION"
scp -r Designer/dist/Designer/* user@your-server:/var/www/DesignerCEP/Server/static/core/$VERSION/
# 4. 更新后端代码
echo -e "\n${YELLOW}[4/6] 更新后端代码...${NC}"
ssh user@your-server "cd /var/www/DesignerCEP/Server && git pull origin main"
# 5. 重启后端服务
echo -e "\n${YELLOW}[5/6] 重启后端服务...${NC}"
ssh user@your-server "sudo systemctl restart designer-cep"
# 6. 检查服务状态
echo -e "\n${YELLOW}[6/6] 检查服务状态...${NC}"
ssh user@your-server "sudo systemctl status designer-cep --no-pager"
echo -e "\n${GREEN}========================================${NC}"
echo -e "${GREEN} 部署完成!${NC}"
echo -e "${GREEN} 版本: $VERSION${NC}"
echo -e "${GREEN}========================================${NC}"
```
使用方法:
```bash
chmod +x deploy.sh
./deploy.sh
```
---
## ❓ 八、常见问题
### Q1: CORS 跨域问题
**A**: 在后端 `.env` 中正确配置 `ALLOWED_ORIGINS`
### Q2: 静态文件 404
**A**: 检查 Nginx 配置中的 `alias` 路径是否正确。
### Q3: API 请求超时
**A**: 调整 Nginx `proxy_read_timeout` 和 Gunicorn `timeout` 参数。
### Q4: 前端日志仍然输出
**A**: 确保在生产环境中调用了 `logger.disable()`
### Q5: 版本更新后客户端没反应
**A**: 检查后端 API 返回的版本号是否正确,清除客户端缓存。
---
## 📞 技术支持
如遇到部署问题,请检查:
1. 后端日志:`sudo journalctl -u designer-cep -f`
2. Nginx 日志:`sudo tail -f /var/log/nginx/designer-cep-error.log`
3. 系统资源:`htop``top`
---
**最后更新**: 2024-12-17

View File

@@ -0,0 +1,97 @@
# 认证接口文档
## 概述
- 基础地址:`http://localhost:8000`
- 版本前缀:`/api/v1`
- 认证方式:`Bearer JWT`(登录或注册成功后返回的 `access_token`
- 单设备限制:同一账号仅允许一个设备同时在线(依据 `device_id`
## 注册
- 方法与路径:`POST /api/v1/auth/register`
- 请求体:
```json
{
"username": "alice",
"password": "secret123",
"confirm_password": "secret123"
}
```
- 成功响应:
```json
{
"access_token": "<JWT>",
"token_type": "bearer",
"username": "alice"
}
```
- 失败响应示例:
- 400`Passwords do not match`
- 400`Username already registered`
- 调用示例curl
```bash
curl -X POST http://localhost:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"secret123","confirm_password":"secret123"}'
```
## 登录
- 方法与路径:`POST /api/v1/auth/login`
- 请求体:
```json
{
"username": "alice",
"password": "secret123",
"device_id": "devA"
}
```
- 成功响应:
```json
{
"access_token": "<JWT>",
"token_type": "bearer",
"username": "alice"
}
```
- 失败响应示例:
- 401`用户名或密码错误`
- 403`该账号已在其他设备在线`
- 调用示例curl
```bash
curl -X POST http://localhost:8000/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"secret123","device_id":"devA"}'
```
## 登出
- 方法与路径:`POST /api/v1/auth/logout`
- 请求体:
```json
{
"username": "alice",
"device_id": "devA"
}
```
- 成功响应:
```json
{ "detail": "已退出登录" }
```
- 用途:释放当前设备的会话,便于其他设备登录同账号
## 在线时长统计
- 方法与路径:`GET /api/v1/auth/online-time/{username}`
- 返回体:
```json
{
"username": "alice",
"total_seconds": 1234, // 已登出会话累计时长(秒)
"active_seconds": 56 // 当前活跃会话的实时在线时长(秒)
}
```
- 说明:
- 登录时会记录 `login_at`,登出时写入 `logout_at` 并计算 `duration_seconds`
- `total_seconds` 为历史累计;`active_seconds` 为当前会话实时值
## 前端对接建议
- 前端拿到 `access_token` 后,将其置于请求头:`Authorization: Bearer <token>`
- 后续接口可以基于该令牌进行身份识别和鉴权
- 登录时必须传入稳定的 `device_id`,建议由前端根据系统信息生成并持久化(例如用户目录内文件或硬件指纹)

View File

@@ -0,0 +1,371 @@
# 许可证验证接口文档
## 📋 接口概述
**接口路径:** `POST /api/v1/auth/verify`
**功能:** 验证用户的登录 token 是否有效(用于心跳检测)
**调用频率:** 每 60 秒调用一次
---
## 📥 请求参数
### Headers
```json
{
"Content-Type": "application/json",
"Authorization": "Bearer {token}"
}
```
### Body (JSON)
```json
{
"username": "string", // 用户名
"device_id": "string", // 设备 ID
"timestamp": 1234567890 // 客户端时间戳(毫秒)
}
```
### 示例请求
```http
POST /api/v1/auth/verify HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
---
## 📤 响应格式
### 成功响应200 OK
```json
{
"valid": true,
"username": "zuowei",
"expire_date": "2025-12-31T23:59:59Z" // 可选:账户过期时间
}
```
### 失败响应 1Token 无效401 Unauthorized
```json
{
"detail": "Token 无效或已过期"
}
```
### 失败响应 2账户已过期403 Forbidden
```json
{
"valid": false,
"message": "账户已过期"
}
```
### 失败响应 3会话不存在404 Not Found
```json
{
"detail": "会话不存在或已登出"
}
```
---
## 🔧 后端实现示例FastAPI
### 1. 在 `Server/app/api/v1/auth.py` 添加路由
```python
from fastapi import Depends, HTTPException, status
from pydantic import BaseModel
from app.core.security import get_current_user
from app.db import get_db
from sqlalchemy.orm import Session
from app.models.user import User, UserSession
from datetime import datetime, timezone
class VerifyRequest(BaseModel):
"""验证请求"""
username: str
device_id: str
timestamp: int
class VerifyResponse(BaseModel):
"""验证响应"""
valid: bool
username: str = None
expire_date: str = None
@router.post("/verify", response_model=VerifyResponse)
async def verify_license(
request: VerifyRequest,
current_user: dict = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
验证许可证(心跳检测)
检查:
1. Token 是否有效(通过 Depends(get_current_user) 自动验证)
2. 用户是否存在
3. 会话是否活跃
4. 账户是否过期
"""
# 1. 查询用户
user = db.query(User).filter(User.username == request.username).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="用户不存在"
)
# 2. 检查账户是否过期
if user.expire_date:
expire_dt = user.expire_date
if expire_dt.tzinfo is None:
expire_dt = expire_dt.replace(tzinfo=timezone.utc)
if datetime.now(timezone.utc) > expire_dt:
return VerifyResponse(
valid=False,
username=request.username,
expire_date=user.expire_date.isoformat() if user.expire_date else None
)
# 3. 检查会话是否活跃
session = db.query(UserSession).filter(
UserSession.user_id == user.id,
UserSession.device_id == request.device_id,
UserSession.active == True
).first()
if not session:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="会话不存在或已登出"
)
# 4. 更新最后活跃时间
session.last_seen_at = datetime.now(timezone.utc)
db.commit()
# 5. 返回验证成功
return VerifyResponse(
valid=True,
username=request.username,
expire_date=user.expire_date.isoformat() if user.expire_date else None
)
```
---
### 2. 更新 `Server/app/schemas/auth.py`
添加验证相关的 Schema
```python
class VerifyRequest(BaseModel):
"""验证请求模型"""
username: str
device_id: str
timestamp: int
class VerifyResponse(BaseModel):
"""验证响应模型"""
valid: bool
username: Optional[str] = None
expire_date: Optional[str] = None
```
---
## 🧪 测试验证接口
### 使用 curl 测试
```bash
curl -X POST http://127.0.0.1:8000/api/v1/auth/verify \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
-d '{
"username": "zuowei",
"device_id": "test-device-123",
"timestamp": 1702828800000
}'
```
### 预期响应
**成功:**
```json
{
"valid": true,
"username": "zuowei",
"expire_date": null
}
```
**Token 无效:**
```json
{
"detail": "Could not validate credentials"
}
```
**会话不存在:**
```json
{
"detail": "会话不存在或已登出"
}
```
---
## 📊 数据库表设计(参考)
确保数据库有以下表和字段:
### users 表
```sql
CREATE TABLE users (
id INTEGER PRIMARY KEY,
username VARCHAR UNIQUE NOT NULL,
expire_date DATETIME, -- 账户过期时间NULL = 永久)
...
);
```
### user_sessions 表
```sql
CREATE TABLE user_sessions (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
device_id VARCHAR NOT NULL,
active BOOLEAN DEFAULT TRUE,
last_seen_at DATETIME, -- 最后活跃时间
...
FOREIGN KEY (user_id) REFERENCES users(id)
);
```
---
## 🔐 安全注意事项
1. **Token 验证**
- 使用 `Depends(get_current_user)` 确保 Token 有效
- Token 过期自动返回 401
2. **会话管理**
- 验证成功时更新 `last_seen_at`
- 可用于统计在线时长
3. **过期检查**
- 支持账户过期时间
- 过期返回 `valid: false`
4. **频率控制**
- 前端已做 30 秒缓存,减少请求频率
- 后端可添加 Rate Limiting
---
## 🚀 部署检查
添加接口后,确认:
1. ✅ 路由已注册
```python
# Server/app/main.py
app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth")
```
2. ✅ 数据库表存在
```bash
# 检查 users 和 user_sessions 表
```
3. ✅ Token 验证正常
```bash
# 测试登录获取 Token
# 测试 verify 接口
```
4. ✅ 前端心跳正常
```
# 前端每 60 秒调用一次
# 30 秒内不重复验证(有缓存)
```
---
## 📝 完整实现清单
- [ ] 添加 `VerifyRequest` 和 `VerifyResponse` Schema
- [ ] 在 `auth.py` 添加 `/verify` 路由
- [ ] 实现 Token 验证逻辑
- [ ] 实现账户过期检查
- [ ] 实现会话活跃检查
- [ ] 更新最后活跃时间
- [ ] 测试接口(成功、失败、过期等场景)
- [ ] 部署到服务器
---
## 💡 可选增强
### 1. 添加限流Rate Limiting
```python
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
@router.post("/verify")
@limiter.limit("120/minute") # 每分钟最多 120 次
async def verify_license(...):
...
```
### 2. 添加详细日志
```python
import logging
logger = logging.getLogger(__name__)
@router.post("/verify")
async def verify_license(...):
logger.info(f"[Verify] {request.username} from {request.device_id}")
...
```
### 3. 返回更多信息
```python
class VerifyResponse(BaseModel):
valid: bool
username: str = None
expire_date: str = None
permissions: list = [] # 用户权限列表
online_time: int = 0 # 在线时长(秒)
```
---
**实现这个接口后,前端的心跳检测就能正常工作了!**
---
**实现这个接口后,前端的心跳检测就能正常工作了!** ✅

View File

@@ -0,0 +1,106 @@
# 设备与在线时长接口
## 概述
- 基础地址:`http://localhost:8000`
- 版本前缀:`/api/v1`
- 说明:同一账号仅允许一个设备同时在线(依据 `device_id`);后端会记录登录登出并可统计在线时长。
## 登录(含单设备限制)
- 方法与路径:`POST /api/v1/auth/login`
- 请求体:
```json
{
"username": "alice",
"password": "secret123",
"device_id": "devA"
}
```
- 成功响应:
```json
{
"access_token": "<JWT>",
"token_type": "bearer",
"username": "alice"
}
```
- 失败响应:
- 401`用户名或密码错误`
- 403`该账号已在其他设备在线`
- 说明:
- 必须传入稳定的 `device_id`(前端生成并持久化),用于限制并发登录与统计时长
- 登录成功会记录会话的 `login_at`,并将设备会话标记为活跃
## 登出(记录在线时长)
- 方法与路径:`POST /api/v1/auth/logout`
- 请求体:
```json
{
"username": "alice",
"device_id": "devA"
}
```
- 成功响应:
```json
{ "detail": "已退出登录" }
```
- 行为:
- 将对应设备的会话置为非活跃,并记录 `logout_at`
- 若存在 `login_at`,会计算本次会话的在线时长 `duration_seconds`(单位:秒)
## 在线时长统计
- 方法与路径:`GET /api/v1/auth/online-time/{username}`
- 成功响应:
```json
{
"username": "alice",
"total_seconds": 1234,
"active_seconds": 56
}
```
- 字段说明:
- `total_seconds`:历史所有已登出会话的累计在线时长(秒)
- `active_seconds`:当前活跃会话的实时在线时长(秒),若无活跃会话则为 0实时时长基于最近心跳时间 `last_seen_at``login_at` 的差值
- 统计逻辑:
- 登录时写入 `login_at`UTC
- 登出时写入 `logout_at` 并计算 `duration_seconds`
- 查询时将已登出会话的 `duration_seconds` 累加为 `total_seconds`,并以当前时间与活跃会话的 `login_at` 计算 `active_seconds`
## 心跳接口(保持在线时长统计)
- 方法与路径:`POST /api/v1/auth/heartbeat`
- 请求体:
```json
{
"username": "alice",
"device_id": "devA"
}
```
- 成功响应:
```json
{ "detail": "心跳已更新" }
```
- 说明:
- 前端应在用户在线期间定期调用心跳(例如每 3060 秒),以更新会话的 `last_seen_at`
- 若未调用登出而直接关闭应用,在线时长将以最近一次心跳为准,不会无限累计
## 错误信息(统一中文)
- 401`用户名或密码错误`
- 403`该账号已在其他设备在线`
- 404`用户不存在`
## 前端调用示例Axios
```ts
import axios from 'axios';
const API = 'http://localhost:8000/api/v1/auth';
export async function login(username: string, password: string, deviceId: string) {
return axios.post(`${API}/login`, { username, password, device_id: deviceId });
}
export async function logout(username: string, deviceId: string) {
return axios.post(`${API}/logout`, { username, device_id: deviceId });
}
export async function getOnlineTime(username: string) {
return axios.get(`${API}/online-time/${encodeURIComponent(username)}`);
}
```

View File

@@ -0,0 +1,137 @@
# DesignerCEP 邮箱验证与密码重置接口文档
本文档描述了最新的注册验证流程和密码重置流程。
## 1. 注册流程 (单表单模式)
新版注册流程改为在同一个表单中完成:输入邮箱 -> 发送验证码 -> 填写验证码及密码 -> 提交注册。
### 1.1 发送注册验证码
用户输入邮箱后,点击“发送验证码”按钮调用此接口。
- **接口地址**: `/api/v1/auth/send-verification-code`
- **请求方式**: `POST`
- **Content-Type**: `application/json`
**请求参数**:
```json
{
"email": "user@example.com"
}
```
**响应示例 (成功)**:
```json
{
"detail": "验证码已发送"
}
```
**响应示例 (失败)**:
- 400 Bad Request: "该邮箱已被注册"
- 500 Internal Server Error: "邮件发送失败: ..."
---
### 1.2 提交注册
用户填写收到的 6 位数字验证码、用户名、密码后,调用此接口完成注册。
- **接口地址**: `/api/v1/auth/register`
- **请求方式**: `POST`
- **Content-Type**: `application/json`
**请求参数**:
```json
{
"username": "myusername",
"email": "user@example.com",
"password": "mypassword123",
"confirm_password": "mypassword123",
"code": "123456",
"device_id": "device_unique_id"
}
```
*注:`device_id` 为可选,若不传默认为 "unknown_device"*
**响应示例 (成功)**:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer",
"username": "myusername"
}
```
**响应示例 (失败)**:
- 400 Bad Request: "请先发送验证码" (如果邮箱未先调用发送接口)
- 400 Bad Request: "验证码错误"
- 400 Bad Request: "验证码已过期"
- 400 Bad Request: "用户名已存在"
---
## 2. 找回/重置密码流程
密码重置流程改为使用 6 位数字验证码,而非之前的长链接。
### 2.1 发送重置验证码
用户在“忘记密码”页面输入邮箱,点击发送。
- **接口地址**: `/api/v1/auth/forgot-password`
- **请求方式**: `POST`
- **Content-Type**: `application/json`
**请求参数**:
```json
{
"email": "user@example.com"
}
```
**响应示例**:
```json
{
"detail": "如果邮箱存在,重置邮件已发送"
}
```
---
### 2.2 重置密码
用户输入收到的 6 位验证码和新密码进行重置。
- **接口地址**: `/api/v1/auth/reset-password`
- **请求方式**: `POST`
- **Content-Type**: `application/json`
**请求参数**:
```json
{
"email": "user@example.com",
"token": "123456",
"new_password": "newpassword123",
"confirm_password": "newpassword123"
}
```
*注:`token` 字段即为邮件中收到的 6 位数字验证码*
**响应示例 (成功)**:
```json
{
"detail": "密码重置成功"
}
```
**响应示例 (失败)**:
- 400 Bad Request: "验证码错误"
- 400 Bad Request: "验证码已过期"
- 400 Bad Request: "两次输入的密码不一致"
- 404 Not Found: "用户不存在"

View File

@@ -0,0 +1,460 @@
# DesignerCEP 部署前检查清单
## ✅ 部署前准备(本地)
### 前端配置
- [ ] 创建 `Designer/.env.production` 文件
```env
VITE_API_BASE_URL=https://your-domain.com/api/v1
VITE_API_SERVER=https://your-domain.com
```
- [ ] 修改 `Designer/src/main.ts` 添加日志控制
```typescript
import { logger } from '@/utils/logger';
if (import.meta.env.PROD) {
logger.disable(); // 生产环境关闭日志
} else {
logger.enable(); // 开发环境开启日志
}
```
- [ ] 检查 `Designer/src/config.ts` 是否使用环境变量
```typescript
apiBaseUrl: import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:8000/api/v1'
```
- [ ] 测试构建命令
```bash
cd Designer
npm run build
```
- [ ] 检查构建产物(应该在 `Designer/dist/` 目录)
- `dist/Shell/` - Shell 登录壳
- `dist/Designer/` - Core 核心应用
### 后端配置
- [ ] 创建生产环境配置文件(用于上传到服务器)
```bash
cp Server/.env.example Server/.env.production
```
- [ ] 修改 `Server/.env.production`
- [ ] 修改 `SECRET_KEY` 为强随机密钥
- [ ] 设置 `DEBUG=False`
- [ ] 配置 `ALLOWED_ORIGINS`(你的域名)
- [ ] 配置数据库连接(如使用 PostgreSQL/MySQL
- [ ] 检查 `Server/requirements.txt` 包含所有依赖
- [ ] 打包 Shell供用户下载
```bash
cd Designer/dist
zip -r shell-1.0.0.zip Shell/
# 将 shell-1.0.0.zip 准备上传到服务器
```
---
## 🖥️ 服务器准备
### 基础环境
- [ ] 服务器已购买推荐阿里云、腾讯云、AWS
- [ ] 操作系统Ubuntu 20.04+ 或 CentOS 7+
- [ ] 配置:最低 1核2G推荐 2核4G
- [ ] 已安装 Python 3.9+
```bash
python3 --version
```
- [ ] 已安装 Nginx
```bash
nginx -v
```
- [ ] 已安装 Git
```bash
git --version
```
### 域名和 SSL
- [ ] 域名已购买
- [ ] DNS 已解析到服务器 IP
```bash
# 验证 DNS 解析
ping your-domain.com
```
- [ ] 准备申请 SSL 证书Let's Encrypt 免费)
### 网络配置
- [ ] 服务器防火墙已开放端口:
- [ ] 22 (SSH)
- [ ] 80 (HTTP)
- [ ] 443 (HTTPS)
- [ ] 安全组规则已配置(云服务器)
---
## 📤 文件上传
### 上传后端代码
- [ ] 方式 1: 使用 Git推荐
```bash
ssh user@your-server
cd /var/www
git clone https://your-repo-url.git DesignerCEP
```
- [ ] 方式 2: 使用 SCP
```bash
scp -r Server/ user@your-server:/var/www/DesignerCEP/
```
### 上传前端构建产物
- [ ] 上传 Shell 在线登录页
```bash
scp -r Designer/dist/Shell/* \
user@your-server:/var/www/DesignerCEP/Server/static/shell/
```
- [ ] 上传 Core核心应用
```bash
scp -r Designer/dist/Designer/* \
user@your-server:/var/www/DesignerCEP/Server/static/core/1.0.0/
```
- [ ] 打包并上传 Shell供 CEP 扩展下载)
```bash
# 本地打包
cd Designer/dist
zip -r shell-1.0.0.zip Shell/
# 上传到服务器
scp shell-1.0.0.zip \
user@your-server:/var/www/DesignerCEP/Server/static/downloads/
```
---
## 🔧 服务器配置
### Python 环境
- [ ] 创建虚拟环境
```bash
cd /var/www/DesignerCEP/Server
python3 -m venv venv
source venv/bin/activate
```
- [ ] 安装依赖
```bash
pip install -r requirements.txt
pip install gunicorn uvicorn[standard]
```
### 环境变量
- [ ] 上传 `.env.production` 并重命名为 `.env`
```bash
scp Server/.env.production user@your-server:/var/www/DesignerCEP/Server/.env
```
- [ ] 验证配置
```bash
cat /var/www/DesignerCEP/Server/.env
```
### 目录结构
- [ ] 验证目录结构正确
```
/var/www/DesignerCEP/Server/
├── app/
├── static/
│ ├── shell/ # Shell 在线登录页
│ │ ├── index.html
│ │ └── assets/
│ ├── downloads/ # 下载文件
│ │ └── shell-1.0.0.zip
│ └── core/
│ └── 1.0.0/
│ ├── index.html
│ └── assets/
├── .env
├── requirements.txt
└── venv/
```
- [ ] 创建日志目录
```bash
mkdir -p /var/www/DesignerCEP/Server/logs
```
### Systemd 服务
- [ ] 创建服务文件
```bash
sudo nano /etc/systemd/system/designer-cep.service
```
- [ ] 启动服务
```bash
sudo systemctl daemon-reload
sudo systemctl start designer-cep
sudo systemctl enable designer-cep
```
- [ ] 验证服务状态
```bash
sudo systemctl status designer-cep
```
### Nginx 配置
- [ ] 创建 Nginx 配置文件
```bash
sudo nano /etc/nginx/sites-available/designer-cep
```
- [ ] 启用配置
```bash
sudo ln -s /etc/nginx/sites-available/designer-cep /etc/nginx/sites-enabled/
```
- [ ] 测试 Nginx 配置
```bash
sudo nginx -t
```
- [ ] 重启 Nginx
```bash
sudo systemctl restart nginx
```
### SSL 证书
- [ ] 安装 Certbot
```bash
sudo apt install certbot python3-certbot-nginx
```
- [ ] 申请证书
```bash
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
```
- [ ] 验证自动续期
```bash
sudo certbot renew --dry-run
```
---
## 🧪 测试验证
### 后端 API 测试
- [ ] 健康检查
```bash
curl https://your-domain.com/api/v1/health
```
- [ ] 登录接口
```bash
curl -X POST https://your-domain.com/api/v1/client/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"test123","device_id":"test-device"}'
```
### 前端访问测试
- [ ] 访问 Shell 在线登录页
```
https://your-domain.com/shell/
https://your-domain.com/shell/index.html
```
- [ ] 访问 Core 首页
```
https://your-domain.com/core/1.0.0/index.html
```
- [ ] 检查静态资源加载F12 查看 Network
- [ ] Shell 下载链接(供 CEP 扩展下载)
```
https://your-domain.com/downloads/shell-1.0.0.zip
```
### 功能测试
- [ ] 用户注册功能
- [ ] 用户登录功能
- [ ] 自动登录功能
- [ ] 版本检查和更新
- [ ] Photoshop 插件功能(创建图层等)
- [ ] 退出登录功能
### 性能测试
- [ ] 页面加载速度
```bash
curl -w "@curl-format.txt" -o /dev/null -s https://your-domain.com/core/1.0.0/
```
- [ ] API 响应时间
- [ ] 并发测试(可选)
```bash
ab -n 1000 -c 10 https://your-domain.com/api/v1/health
```
---
## 🔒 安全检查
- [ ] 防火墙已启用
```bash
sudo ufw status
```
- [ ] 仅开放必要端口22, 80, 443
- [ ] `SECRET_KEY` 已修改为强随机值
- [ ] `DEBUG=False` 已设置
- [ ] CORS 配置正确(`ALLOWED_ORIGINS`
- [ ] 数据库密码强度检查(如使用数据库)
- [ ] 文件权限检查
```bash
# .env 文件应该仅所有者可读
chmod 600 /var/www/DesignerCEP/Server/.env
```
- [ ] 定期更新系统和依赖
```bash
sudo apt update && sudo apt upgrade
```
---
## 📊 监控配置(可选)
- [ ] 配置日志轮转
```bash
sudo nano /etc/logrotate.d/designer-cep
```
- [ ] 设置系统监控(推荐工具)
- [ ] Prometheus + Grafana
- [ ] Zabbix
- [ ] Datadog
- [ ] 配置错误追踪(推荐)
- [ ] Sentry
- [ ] Bugsnag
- [ ] 配置服务告警(推荐)
- [ ] 邮件告警
- [ ] 短信告警
- [ ] 企业微信/钉钉告警
---
## 📝 文档和备份
- [ ] 记录服务器信息
- 服务器 IP: __________________
- SSH 用户名: __________________
- SSH 密钥位置: __________________
- 域名: __________________
- [ ] 记录部署信息
- 部署日期: __________________
- 当前版本: __________________
- 数据库位置: __________________
- [ ] 设置定期备份
- [ ] 数据库备份(每天)
- [ ] 配置文件备份
- [ ] 用户数据备份
- [ ] 编写故障恢复文档
---
## 🎉 上线后操作
- [ ] 通知用户新版本上线
- [ ] 更新 CEP 插件配置(如需要)
- 修改 API 地址指向生产环境
- [ ] 监控服务器资源使用情况
```bash
htop
df -h
```
- [ ] 检查错误日志
```bash
sudo journalctl -u designer-cep -f
sudo tail -f /var/log/nginx/designer-cep-error.log
```
- [ ] 第一周密切关注系统运行状况
---
## 🆘 应急预案
### 服务无法访问
1. 检查服务状态
```bash
sudo systemctl status designer-cep
sudo systemctl status nginx
```
2. 查看错误日志
```bash
sudo journalctl -u designer-cep -n 100 --no-pager
```
3. 重启服务
```bash
sudo systemctl restart designer-cep
sudo systemctl restart nginx
```
### 回滚到上一版本
1. 切换 Core 版本(修改后端 API 返回的版本号)
2. 或直接替换静态文件
```bash
rm -rf /var/www/DesignerCEP/Server/static/core/1.0.1
# 恢复旧版本
```
3. 重启服务
---
**检查完成日期**: _______________
**检查人**: _______________
**备注**: _______________

View File

@@ -0,0 +1,333 @@
# DesignerCEP 部署架构说明
## 📐 整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ 用户设备 │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Adobe Photoshop + CEP 扩展 │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ 1. Shell (本地 CEP) │ │ │
│ │ │ - 首次从服务器下载 shell.zip │ │ │
│ │ │ - 离线登录页 (file://) │ │ │
│ │ │ - 版本管理和更新 │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ 2. Core (在线应用) │ │ │
│ │ │ - 从服务器加载 (https://) │ │ │
│ │ │ - 核心功能和业务逻辑 │ │ │
│ │ │ - 与 Photoshop 交互 │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
▼ HTTP/HTTPS
┌─────────────────────────────────────────────────────────────┐
│ 服务器 (Linux) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Nginx (反向代理) │ │
│ │ ┌──────────────┬──────────────┬──────────────────┐ │ │
│ │ │ /shell/ │ /core/ │ /api/ │ │ │
│ │ │ Shell 在线页 │ Core 应用 │ API 接口 │ │ │
│ │ └──────┬───────┴──────┬───────┴────────┬─────────┘ │ │
│ └─────────┼──────────────┼────────────────┼─────────────┘ │
│ │ │ │ │
│ ┌─────────▼──────────────▼────────────────▼─────────────┐ │
│ │ 静态文件服务 + FastAPI │ │
│ │ ┌────────────┐ ┌────────────┐ ┌─────────────┐ │ │
│ │ │ Shell │ │ Core │ │ API Routes │ │ │
│ │ │ (在线登录) │ │ (核心应用) │ │ (业务逻辑) │ │ │
│ │ └────────────┘ └────────────┘ └─────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## 🔄 用户流程
### 场景 1: 首次使用CEP 扩展)
```
1. 用户安装 CEP 扩展
└─> 扩展启动,检查本地是否有 Shell
2. 如果没有 Shell从服务器下载
└─> 下载 /downloads/shell-1.0.0.zip
└─> 解压到本地缓存目录
3. 加载本地 Shell 登录页 (file://)
└─> 用户输入用户名密码
4. 登录成功后
└─> 检查版本更新
└─> 下载 Core 应用到本地缓存
└─> 加载 Core 应用 (http://localhost:8000/core/1.0.0/)
或直接从服务器加载 (https://your-domain.com/core/1.0.0/)
```
### 场景 2: 在线使用(浏览器访问)
```
1. 用户访问在线登录页
└─> https://your-domain.com/shell/
2. 输入用户名密码登录
3. 登录成功后跳转到 Core
└─> https://your-domain.com/core/1.0.0/
4. 使用核心功能
└─> API 调用 → /api/v1/*
5. 退出登录
└─> 清除本地 token
└─> 跳转回 Shell 登录页
https://your-domain.com/shell/#/login
```
## 📁 服务器目录结构详解
```
/var/www/DesignerCEP/Server/
├── app/ # 后端应用代码
│ ├── api/ # API 路由
│ │ └── v1/
│ │ ├── auth.py # 认证相关
│ │ ├── client.py # 客户端相关
│ │ └── ...
│ ├── core/ # 核心配置
│ ├── services/ # 业务逻辑
│ └── main.py # 应用入口
├── static/ # 静态文件(重要!)
│ │
│ ├── shell/ # Shell 在线登录页
│ │ ├── index.html # 登录页入口
│ │ ├── assets/ # JS/CSS/图片等
│ │ │ ├── index-abc123.js
│ │ │ ├── index-xyz456.css
│ │ │ └── logo.png
│ │ └── ...
│ │
│ ├── downloads/ # 下载文件(供 CEP 扩展)
│ │ ├── shell-1.0.0.zip # Shell 安装包
│ │ ├── shell-1.0.1.zip
│ │ └── ...
│ │
│ └── core/ # Core 核心应用
│ ├── 1.0.0/ # 版本 1.0.0
│ │ ├── index.html
│ │ ├── assets/
│ │ └── ...
│ ├── 1.0.1/ # 版本 1.0.1
│ │ ├── index.html
│ │ ├── assets/
│ │ └── ...
│ └── ...
├── logs/ # 日志文件
│ ├── access.log
│ └── error.log
├── .env # 环境变量配置
├── requirements.txt # Python 依赖
└── venv/ # Python 虚拟环境
```
## 🌐 URL 路由映射
| URL 路径 | 本地文件路径 | 说明 | 访问方式 |
|---------|------------|------|---------|
| `/shell/` | `static/shell/index.html` | Shell 在线登录页 | 浏览器/CEP |
| `/shell/assets/xxx.js` | `static/shell/assets/xxx.js` | Shell 静态资源 | 自动加载 |
| `/downloads/shell-1.0.0.zip` | `static/downloads/shell-1.0.0.zip` | Shell 安装包 | CEP 下载 |
| `/core/1.0.0/` | `static/core/1.0.0/index.html` | Core 应用(版本 1.0.0 | 浏览器/CEP |
| `/core/1.0.0/assets/xxx.js` | `static/core/1.0.0/assets/xxx.js` | Core 静态资源 | 自动加载 |
| `/api/v1/client/login` | FastAPI 路由 | 登录接口 | API 调用 |
| `/api/v1/client/check_update` | FastAPI 路由 | 检查更新 | API 调用 |
## 🔐 认证流程详解
### 1. 用户登录
```
用户 → Shell 登录页
输入用户名/密码
POST /api/v1/client/login
┌─────────────────────┐
│ 后端验证 │
│ - 检查用户名密码 │
│ - 检查设备限制 │
│ - 生成 JWT Token │
└─────────────────────┘
返回 token + 用户信息
保存到 localStorage:
- token
- username
- device_id
- auto_login
跳转到 Core 应用
(/core/1.0.0/#/home?token=xxx&username=xxx&device_id=xxx)
```
### 2. Core 应用初始化
```
Core 启动
检查 URL 参数或 localStorage 中的 token
如果有 token:
├─> 保存到 localStorage
├─> 启动心跳检测 (每 60 秒验证一次)
└─> 加载用户数据
如果没有 token:
└─> 跳转到 Shell 登录页
```
### 3. 退出登录
```
用户点击退出
POST /api/v1/auth/logout
清除 localStorage:
- token
- username
- device_id
- auto_login
判断环境:
- CEP 环境 → 跳转到本地 Shell (file://)
- 浏览器环境 → 跳转到在线 Shell (https://)
```
## 🔄 版本更新流程
### 方案 1: 服务器端更新(推荐)
```
1. 构建新版本
cd Designer
npm run build
2. 上传到服务器
scp -r dist/Designer/* \
user@server:/var/www/DesignerCEP/Server/static/core/1.0.1/
3. 更新后端配置
修改 current_version = "1.0.1"
4. 用户下次登录时自动获取新版本
```
### 方案 2: CEP 扩展自动更新
```
1. 用户启动 CEP 扩展
2. Shell 调用 /api/v1/client/check_update
返回: { version: "1.0.1", download_url: "/core/1.0.1.zip" }
3. 如果版本号不同,自动下载新版本
4. 下载并解压到本地缓存
5. 启动新版本 Core
```
## 🔒 安全考虑
### 1. 数据传输
- ✅ 必须使用 HTTPSSSL 证书)
- ✅ API 请求必须携带 JWT Token
- ✅ Token 过期时间设置合理(默认 30 天)
### 2. 跨域处理
```python
# 后端配置 CORS
ALLOWED_ORIGINS = [
"https://your-domain.com",
"https://www.your-domain.com",
"file://", # 允许 CEP 扩展访问
]
```
### 3. 文件访问权限
```bash
# 静态文件目录权限
chmod 755 static/
chmod 644 static/shell/*
chmod 644 static/core/*
# 配置文件权限
chmod 600 .env
```
## 📊 性能优化
### 1. 静态资源缓存
```nginx
# Nginx 配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache";
}
```
### 2. 资源压缩
```nginx
# 启用 Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
```
### 3. CDN 加速(可选)
将静态资源部署到 CDN加快全球访问速度。
## 🔍 监控和日志
### 1. 应用日志
- 后端日志: `/var/www/DesignerCEP/Server/logs/`
- Nginx 日志: `/var/log/nginx/`
- Systemd 日志: `journalctl -u designer-cep`
### 2. 监控指标
- 服务器 CPU/内存使用率
- API 响应时间
- 在线用户数
- 错误率和异常追踪
---
**总结**:
这个架构支持两种使用方式:
1. **CEP 扩展模式**Shell 在本地Core 可在本地或服务器
2. **纯在线模式**Shell 和 Core 都在服务器,通过浏览器访问
无论哪种方式,用户退出登录后都能回到 Shell 登录页重新登录。

View File

@@ -0,0 +1,77 @@
# DesignerCEP 项目结构文档
本文档详细说明了 `DesignerCEP/Designer` 项目的目录结构及各核心文件的作用,帮助开发者快速理解项目架构。
## 1. 核心目录结构 (src)
`src` 是项目的源代码目录,主要分为 **前端 UI (Vue)****后端逻辑 (JSX/ExtendScript)** 两部分。
### 📂 src/
| 文件/目录 | 说明 |
| :---------------- | :-------------------------------------------------------------------- |
| **`App.vue`** | Vue 应用的根组件,定义了全局的布局结构。 |
| **`main.ts`** | Vue 应用的入口文件,负责初始化 Vue 实例、挂载路由和插件。 |
| **`api/`** | 存放前端与后端接口交互的代码 (虽然是 CEP但也可能涉及部分网络请求)。 |
| **`assets/`** | 静态资源目录,存放图片、字体等。 |
| **`components/`** | Vue 通用组件库,存放可在多个页面复用的 UI 组件。 |
| **`view/`** | Vue 页面目录,存放具体的业务页面 (如 Login.vue, Home.vue)。 |
| **`router/`** | 路由配置目录,定义了页面跳转规则。 |
| **`utils/`** | 前端通用工具函数库。 |
| **`jsx/`** | **核心 Photoshop 交互逻辑代码** (见下文详细说明)。 |
---
## 2. Photoshop 交互核心 (src/jsx)
`src/jsx` 目录包含了所有在 Photoshop 内部执行的 ExtendScript 代码。这部分代码会被编译为 `index.js` 供 CEP 调用。
### 📂 src/jsx/
| 文件/目录 | 说明 |
| :------------------ | :------------------------------------------------------------------------------------------ |
| **`index.ts`** | **JSX 入口文件**。导出了供前端调用的所有高级函数。可以理解为 CEP 插件的 "后端 API 控制器"。 |
| **`tsconfig.json`** | JSX 部分的 TypeScript 配置文件,确保 ExtendScript 代码被正确编译(编译为 ES3 兼容代码)。 |
| **`types/`** | 存放 `.d.ts` 类型定义文件,用于支持 TypeScript 开发 Photoshop 脚本。 |
### 📂 src/jsx/utils/ (通用工具库)
这是我们本次重构新建的目录,用于存放可复用的底层工具函数。
| 文件 | 作用详解 |
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`ActionManager.ts`** | **ActionManager (AM) 低级操作库**。<br>封装了 `loadSelection` (载入选区), `fill` (填充), `newDocument` (新建文档) 等通过 ActionDescriptor 调用 Photoshop 底层功能的函数。 |
| **`LayerUtils.ts`** | **图层操作工具库**。<br>包含了 `getAllLayers` (获取所有图层), `findLayerByName` (按名查找), `selectLayerByName` (选中图层) 等 DOM 层面的图层辅助函数。 |
### 📂 src/jsx/function/ (业务逻辑封装)
这里存放封装好的高级业务类。
| 文件/目录 | 说明 |
| :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`abstract.ts`** | 定义了一些抽象基类或接口 (目前看来可能是作为一个基类模板)。 |
| **`index.ts`** | 导出 `g_func` 等全局对象,汇集了所有功能模块。 |
| **`ps/api.ts`** | **核心 API 实现类**。包含了 `PSApi`, `MyLayer`, `MyDocument` 等类。<br>- 这是一个庞大的文件,封装了大量对 Photoshop 的操作。<br>- 很多功能与我们的 `ActionManager.ts` 类似,但以类的形式组织。<br>- **注意**: 该文件定义了全局的 `executeAction` 类型,我们在写新代码时要注意兼容。 |
---
## 3. 前端工具库 (src/utils)
| 文件 | 说明 |
| :--------------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
| **`cep.ts`** | **CEP 桥接工具**。封装了 `CSInterface`,提供了前端调用 JSX`evalScript`)、监听事件、获取路径等功能。这是 Vue 与 Photoshop 对话的桥梁。 |
| **`request.ts`** | 封装了 axios 请求,用于网络通信 (如登录、数据同步)。 |
| **`common.ts`** | 通用的 JS 辅助函数 (如日期格式化等)。 |
## 4. 总结:我们要关注的重点
在接下来的重构工作中,我们主要关注以下流程:
1. **Vue (前端)**: 在 `src/view` 中编写 UI 界面。
2. **调用**: 使用 `src/utils/cep.ts` 中的 `evalFile``evalScript` 调用 JSX。
3. **JSX (后端)**:
-`src/jsx/index.ts` 中注册导出的函数。
- 复杂的业务逻辑写在 `src/jsx/function` 中。
- 通用的底层操作 (AM/图层) 优先调用 `src/jsx/utils/ActionManager.ts``LayerUtils.ts`
这样分层清晰,便于我们逐步迁移 `psmark` 的旧脚本。