├── .env
├── .env.development.example
├── .env.production.example
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── .run
└── Comfy-server.run.xml
├── Dockerfile
├── README.md
├── docker-compose.yml.example
├── img.png
├── init.sql
├── nest-cli.json
├── package-lock.json
├── package.json
├── pm2.config.js
├── src
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── auth
│ ├── auth.controller.ts
│ ├── auth.guard.ts
│ ├── auth.module.ts
│ ├── auth.service.ts
│ └── constants.ts
├── cache
│ ├── cache.controller.ts
│ ├── cache.module.ts
│ └── cache.service.ts
├── draw
│ ├── DrawCosumer.ts
│ ├── data
│ │ ├── AImodel_api.json
│ │ ├── DrawConfig.ts
│ │ ├── aistudio_api.json
│ │ ├── api_matting.json
│ │ ├── api_removebg.json
│ │ ├── api_upscale.json
│ │ ├── api_upscale_detailfix.json
│ │ ├── api_wechat_img2imgi.json
│ │ ├── impaiting_api.json
│ │ ├── objectInfo.json
│ │ ├── test.json
│ │ ├── text2imgapi.json
│ │ ├── workflow_api_faceswap.ts
│ │ ├── workflow_api_hdfix_4.ts
│ │ ├── workflow_api_image2img.json
│ │ ├── workflow_api_image2img.ts
│ │ ├── workflow_api_img2video.json
│ │ ├── workflow_api_img2video.ts
│ │ ├── workflow_api_inpainting.json
│ │ ├── workflow_api_inpainting.ts
│ │ ├── workflow_api_matting.json
│ │ ├── workflow_api_matting.ts
│ │ ├── workflow_api_model.ts
│ │ ├── workflow_api_removebg.json
│ │ ├── workflow_api_removebg.ts
│ │ ├── workflow_api_tagger.ts
│ │ ├── workflow_api_text2img.json
│ │ └── workflow_api_text2img.ts
│ ├── draw.controller.ts
│ ├── draw.module.ts
│ └── draw.service.ts
├── file
│ ├── file.controller.ts
│ ├── file.module.ts
│ └── file.service.ts
├── main.ts
├── middleware
│ └── XML.middleware.ts
├── oneapi
│ ├── tokens
│ │ ├── dto
│ │ │ ├── create-token.dto.ts
│ │ │ └── update-token.dto.ts
│ │ ├── entities
│ │ │ └── token.entity.ts
│ │ ├── tokens.controller.ts
│ │ ├── tokens.module.ts
│ │ └── tokens.service.ts
│ └── users
│ │ ├── dto
│ │ ├── create-user.dto.ts
│ │ └── update-user.dto.ts
│ │ ├── entities
│ │ └── user.entity.ts
│ │ ├── users.controller.ts
│ │ ├── users.module.ts
│ │ └── users.service.ts
├── tweet
│ ├── dto
│ │ ├── create-tweet.dto.ts
│ │ └── update-tweet.dto.ts
│ ├── entities
│ │ └── tweet.schema.ts
│ ├── tweet.controller.ts
│ ├── tweet.module.ts
│ └── tweet.service.ts
├── users
│ ├── dto
│ │ ├── create-user.dto.ts
│ │ └── update-user.dto.ts
│ ├── schema
│ │ └── user.schema.ts
│ ├── users.controller.ts
│ ├── users.module.ts
│ └── users.service.ts
├── wechat-auth
│ ├── wechat-auth.controller.ts
│ ├── wechat-auth.module.ts
│ └── wechat-auth.service.ts
└── ws
│ └── ws.gateway.ts
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
/.env:
--------------------------------------------------------------------------------
1 | #端口,socket端口默认3002需要在ws/ws.gateway.ts中修改
2 | HTTP_PORT=3001
3 | #ComfyUI的服务器和websocket链接设置
4 | CONFIG_COMFYUI_WS_SERVER_URL=ws://127.0.0.1:8188
5 | CONFIG_COMFYUI_HTTP_SERVER_URL=http://127.0.0.1:8188
6 | #队列管理redis的密码
7 | CONFIG_COMFYUI_QUENE_REDIS_PASSWORD=
8 | #公众号配置
9 | CONFIG_OFFICIAL_WECHAT_APPID=
10 | CONFIG_OFFICIAL_WECHAT_SECRET=
--------------------------------------------------------------------------------
/.env.development.example:
--------------------------------------------------------------------------------
1 | #默认的HTTP启动端口
2 | CONFIG_BASE_POST=3001
3 | #websocket端口默认为3002在ws/ws.gateway.ts中修改
4 |
5 | #ComfyUI服务端地址,需要增加协议名称,默认添加ws和http协议
6 | CONFIG_COMFYUI_SERVER_URL=http://127.0.0.1:8188
7 | #CONFIG_COMFYUI_SERVER_URL=http://region-42.seetacloud.com:36432
8 | #CONFIG_COMFYUI_SERVER_URL=https://u231197-80dd-883d166e.bjb1.seetacloud.com:8443
9 | #CONFIG_COMFYUI_SERVER_URL=http://region-42.seetacloud.com:36432
10 |
11 | #部署ComfyUI服务端比较困难可以直接开启如下配置
12 | #CONFIG_COMFYUI_SERVER_REMOTE_URL=http://47.116.212.183:3001
13 | CONFIG_COMFUI_SERVER_REMOTE_AUTH_USERNAME=
14 | CONFIG_COMFUI_SERVER_REMOTE_AUTH_PASSWORD=
15 |
16 | #OSS配置
17 | OSS_REGION=
18 | OSS_ACCESSKEYID=
19 | OSS_ACCESSKEYSECRET=
20 | OSS_BUCKET=
21 |
22 | #SD3,使用SDE的API需要配置这里的key,并且文生图和图生图传入API的options参数里传入apisource:sd3
23 | CONFIG_SD3_APIKEY=
24 |
25 | #mongo数据库
26 | CONFIG_DB_MONGO_URI=mongodb://127.0.0.1:27017
27 | CONFIG_DB_MONGO_USERNAME=username
28 | CONFIG_DB_MONGO_PASSWORD=password
29 | #REDIS
30 | #CONFIG_COMFYUI_QUENE_REDIS_HOST=47.116.212.183
31 | CONFIG_COMFYUI_QUENE_REDIS_HOST=127.0.0.1
32 | CONFIG_COMFYUI_QUENE_REDIS_PORT=6379
33 | CONFIG_COMFYUI_QUENE_REDIS_PASSWORD=
34 | #ONEAPI数据库配置
35 | ONEAPI_MYSQL_HOST=127.0.0.1
36 | ONEAPI_MYSQL_PORT=3306
37 | ONEAPI_MYSQL_USERNAME=root
38 | ONEAPI_MYSQL_PASSWORD=test123
39 | ONEAPI_MYSQL_DATABASE=oneapi
40 |
41 | #微信开放平台,微信登录功能
42 | CONFIG_AUTH_WECHAT_APPID=
43 | CONFIG_AUTH_WECHAT_SECRET=
44 |
45 | #公众号配置
46 | CONFIG_OFFICIAL_WECHAT_APPID=
47 | CONFIG_OFFICIAL_WECHAT_SECRET=
--------------------------------------------------------------------------------
/.env.production.example:
--------------------------------------------------------------------------------
1 | #默认的HTTP启动端口
2 | CONFIG_BASE_POST=3001
3 | #websocket端口默认为3002在ws/ws.gateway.ts中修改
4 |
5 | #ComfyUI服务端地址,需要增加协议名称,默认添加ws和http协议
6 | CONFIG_COMFYUI_SERVER_URL=http://127.0.0.1:8188
7 | #CONFIG_COMFYUI_SERVER_URL=http://region-42.seetacloud.com:36432
8 | #CONFIG_COMFYUI_SERVER_URL=https://u231197-80dd-883d166e.bjb1.seetacloud.com:8443
9 | #CONFIG_COMFYUI_SERVER_URL=http://region-42.seetacloud.com:36432
10 |
11 | #部署ComfyUI服务端比较困难可以直接开启如下配置
12 | #CONFIG_COMFYUI_SERVER_REMOTE_URL=http://47.116.212.183:3001
13 | CONFIG_COMFUI_SERVER_REMOTE_AUTH_USERNAME=
14 | CONFIG_COMFUI_SERVER_REMOTE_AUTH_PASSWORD=
15 |
16 | #OSS配置
17 | OSS_REGION=
18 | OSS_ACCESSKEYID=
19 | OSS_ACCESSKEYSECRET=
20 | OSS_BUCKET=
21 |
22 | #SD3,使用SDE的API需要配置这里的key,并且文生图和图生图传入API的options参数里传入apisource:sd3
23 | CONFIG_SD3_APIKEY=
24 |
25 | #mongo数据库
26 | CONFIG_DB_MONGO_URI=mongodb://127.0.0.1:27017
27 | CONFIG_DB_MONGO_USERNAME=username
28 | CONFIG_DB_MONGO_PASSWORD=password
29 | #REDIS
30 | #CONFIG_COMFYUI_QUENE_REDIS_HOST=47.116.212.183
31 | CONFIG_COMFYUI_QUENE_REDIS_HOST=127.0.0.1
32 | CONFIG_COMFYUI_QUENE_REDIS_PORT=6379
33 | CONFIG_COMFYUI_QUENE_REDIS_PASSWORD=
34 | #ONEAPI数据库配置
35 | ONEAPI_MYSQL_HOST=127.0.0.1
36 | ONEAPI_MYSQL_PORT=3306
37 | ONEAPI_MYSQL_USERNAME=root
38 | ONEAPI_MYSQL_PASSWORD=test123
39 | ONEAPI_MYSQL_DATABASE=oneapi
40 |
41 | #微信开放平台,微信登录功能
42 | CONFIG_AUTH_WECHAT_APPID=
43 | CONFIG_AUTH_WECHAT_SECRET=
44 |
45 | #公众号配置
46 | CONFIG_OFFICIAL_WECHAT_APPID=
47 | CONFIG_OFFICIAL_WECHAT_SECRET=
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | tsconfigRootDir: __dirname,
6 | sourceType: 'module',
7 | },
8 | plugins: ['@typescript-eslint/eslint-plugin'],
9 | extends: [
10 | 'plugin:@typescript-eslint/recommended',
11 | 'plugin:prettier/recommended',
12 | ],
13 | root: true,
14 | env: {
15 | node: true,
16 | jest: true,
17 | },
18 | ignorePatterns: ['.eslintrc.js'],
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/explicit-module-boundary-types': 'off',
23 | '@typescript-eslint/no-explicit-any': 'off',
24 | "prettier/prettier": ["error", { "endOfLine": "auto" }]
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | pnpm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # OS
15 | .DS_Store
16 |
17 | # Tests
18 | /coverage
19 | /.nyc_output
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
36 | #配置文件
37 | docker-compose.yml
38 | .env.development
39 | .env.production
40 |
41 | temp.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "endOfline":"auto"
5 | }
--------------------------------------------------------------------------------
/.run/Comfy-server.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 |
2 | # 第二阶段
3 | FROM node:20.12.1 AS runtime-stage
4 | #2、作者
5 | MAINTAINER bobovinch
6 | # 创建工作目录
7 | RUN mkdir -p /app
8 | WORKDIR /app
9 |
10 | # 复制构建阶段生成的输出到运行时阶段
11 | COPY ./dist /app/dist
12 | COPY ./dist /app/dist
13 | #COPY ./node_modules /app/node_modules
14 | COPY ./package.json /app/
15 | COPY ./.env.* /app/
16 | RUN npm install --force
17 |
18 | # 设置环境变量
19 | ENV NODE_ENV=production
20 | ENV CONFIG_COMFYUI_QUENE_REDIS_HOST=172.17.0.4
21 | ENV CONFIG_COMFYUI_QUENE_REDIS_PORT=6379
22 | ENV CONFIG_COMFYUI_SERVER_URL=http://127.0.0.1:8188
23 |
24 |
25 | # 暴露端口
26 | EXPOSE 3001
27 | EXPOSE 3002
28 |
29 | # 设置入口点为启动脚本
30 | ENTRYPOINT ["npm", "run", "start:prod"]
31 |
32 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
6 | [circleci-url]: https://circleci.com/gh/nestjs/nest
7 |
8 | A progressive Node.js framework for building efficient and scalable server-side applications.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 | ## Description
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## 开始使用前必读!!!!
37 | 开始使用前请先安装redis,并配置.env .end.development .env.production环境变量中密码,如果有的话,没有密码则将密码配置删除
38 | 然后检查环境变量中ComfyUI的http路径和websocket路径
39 | ## 功能介绍
40 | 0502更新,修复Redis相关问题,增加容器一键部署方式
41 | 最新0411更新,重大更新,增加大量商业化能力,运营AI绘画商业网站必备后台服务器
42 | 核心功能1:ComfyUI的绘画API服务和websocket转发,客户端必须使用socketIO链接,WS无法连接,注意版本
43 | 核心功能2:方便将任意comfyui工作转换为在线API,向外提供AI能力
44 | ComfyUI server之间可以共享AI绘画能力
45 | 天然支持利用nginx直接实现负载均衡
46 | 增加注册,登录,微信登录,鉴权,黑名单等常用运营功能
47 | 支持任务队列,支持API提交任务的时候指定队列
48 | 支持黑名单管理
49 | 一键接入微信公众号,并且支持利用别人的API接入微信绘画,支持多轮指令记忆,能够区分绘画指令和提示词
50 |
51 | ## 如何使用
52 | ☆推荐使用docker-compose一键部署
53 | 1.下载docker-compose.yml.example和init.sql两个文件到一个文件夹中
54 | 2.取消.example后缀,修改docker-compose.yml中环境变量,主要修改ComfyUI的服务器地址和端口
55 | 3.运行:docker-compose pull拉取镜像
56 | 4.拉取完成后,运行docker-compose up -d启动容器
57 | 5.打开127.0.0.3001/api-docs可以访问API地址和进行在线API调试
58 |
59 | 开发部署方式
60 | 1.先按照如下如下方式启动服务器
61 | 2.客户端通过socketIO链接服务器,默认为3002端口,如果冲突在src/ws/ws.gateway.ts中修改
62 | 3.以websocket消息形式提交,提交绘画任务,事件名称为draw,消息格式:{client_id:"userid", prompt:"comfyui API", api:"define a API name" }
63 | 4、使用微信公众号绘画功能需要配置APPID和Secret
64 | 教程地址:https://www.bilibili.com/video/BV1AE42137Gn?t=40.6
65 | ## Installation
66 |
67 | ```bash
68 | $ npm install
69 | ```
70 |
71 | ## Running the app
72 |
73 | ```bash
74 | # development
75 | $ npm run start
76 |
77 | # watch mode
78 | $ npm run start:dev
79 |
80 | # production mode
81 | $ npm run start:prod
82 | ```
83 |
84 | ## Test
85 |
86 | ```bash
87 | # unit tests
88 | $ npm run test
89 |
90 | # e2e tests
91 | $ npm run test:e2e
92 |
93 | # test coverage
94 | $ npm run test:cov
95 | ```
96 |
97 | ## Support
98 |
99 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
100 |
101 | ## Stay in touch
102 |
103 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
104 | - Website - [https://nestjs.com](https://nestjs.com/)
105 | - Twitter - [@nestframework](https://twitter.com/nestframework)
106 |
107 | ## License
108 |
109 | Nest is [MIT licensed](LICENSE).
110 |
--------------------------------------------------------------------------------
/docker-compose.yml.example:
--------------------------------------------------------------------------------
1 | # 非 host 版本, 不使用本机代理
2 | # (不懂 Docker 的,只需要关心 OPENAI_BASE_URL 和 CHAT_API_KEY 即可!)
3 | version: '3'
4 | services:
5 | comfy-server:
6 | container_name: comfy-server
7 | #image: ghcr.io/labring/fastgpt:latest # git
8 | image: registry.cn-shanghai.aliyuncs.com/comfy-ai/comfy-server:latest # 阿里云
9 | ports:
10 | - "3001:3001" #http端口
11 | - "3002:3002" #websocket端口
12 | networks:
13 | comfyai:
14 | ipv4_address: 172.20.0.6
15 | depends_on:
16 | - mysql
17 | - mongo
18 | - redis
19 | restart: always
20 | environment:
21 | # 必须!!重要配置!!!- Comfy UI服务器地址,注意不能是127.0.0.1,容器内无法访问,查看网络获取
22 | - CONFIG_COMFYUI_SERVER_URL=http://192.168.0.107:8188
23 | # 必须配置,MongoDB配置,用户管理
24 | - CONFIG_DB_MONGO_URI=mongodb://172.20.0.3:27017
25 | - CONFIG_DB_MONGO_USERNAME=username
26 | - CONFIG_DB_MONGO_PASSWORD=password
27 | # 必须配置,Redis配置,队列缓存
28 | - CONFIG_COMFYUI_QUENE_REDIS_HOST=172.20.0.4
29 | - CONFIG_COMFYUI_QUENE_REDIS_PORT=6379
30 | - CONFIG_COMFYUI_QUENE_REDIS_PASSWORD=
31 | #必须配置,大模型ONEAPI数据库配置
32 | - ONEAPI_MYSQL_HOST=172.20.0.2
33 | - ONEAPI_MYSQL_USERNAME=root
34 | - ONEAPI_MYSQL_PASSWORD=test123
35 | - ONEAPI_MYSQL_DATABASE=oneapi
36 | #可选配置,微信开放平台,微信登录功能
37 | - CONFIG_AUTH_WECHAT_APPID=
38 | - CONFIG_AUTH_WECHAT_SECRET=
39 | #可选配置,公众号配置,实现公众号绘图配置这里
40 | - CONFIG_OFFICIAL_WECHAT_APPID=
41 | - CONFIG_OFFICIAL_WECHAT_SECRET=
42 | #可选配置,使用阿里云OSS存储绘画结果,OSS配置,如果需要将绘画的图片上传到阿里云oss,则配置这里
43 | - OSS_REGION=
44 | - OSS_ACCESSKEYID=
45 | - OSS_ACCESSKEYSECRET=
46 | - OSS_BUCKET=
47 | #SD3 key,使用SD3 需要配置
48 | - CONFIG_SD3_APIKEY=
49 | #可选配置,接别人的Comfy-server API
50 | - CONFIG_COMFYUI_SERVER_REMOTE_URL=
51 | - CONFIG_COMFUI_SERVER_REMOTE_AUTH_USERNAME=
52 | - CONFIG_COMFUI_SERVER_REMOTE_AUTH_PASSWORD=
53 | # 使用大预言模型的后端接口
54 | oneapi:
55 | image: justsong/one-api
56 | container_name: oneapi-mysql
57 | restart: always
58 | ports:
59 | - "3000:3000"
60 | networks:
61 | comfyai:
62 | ipv4_address: 172.20.0.5
63 | depends_on:
64 | - mysql
65 | environment:
66 | - SQL_DSN=root:test123@tcp(172.20.0.2:3306)/oneapi
67 | - TZ=Asia/Shanghai
68 | mongo:
69 | image: registry.cn-shanghai.aliyuncs.com/comfy-ai/mongo-aliyun:latest
70 | container_name: mongo
71 | restart: always
72 | # 生产环境建议不要暴露
73 | ports:
74 | - "27017:27017"
75 | networks:
76 | comfyai:
77 | ipv4_address: 172.20.0.3
78 | environment:
79 | # 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
80 | - MONGO_INITDB_ROOT_USERNAME=username
81 | - MONGO_INITDB_ROOT_PASSWORD=password
82 | volumes:
83 | - /usr/mongo/data:/data/db
84 | # 大预言模型数据库
85 | mysql:
86 | image: registry.cn-shanghai.aliyuncs.com/comfy-ai/mysql-aliyun:latest
87 | container_name: mysql
88 | restart: always
89 | ports:
90 | - "3306:3306"
91 | networks:
92 | comfyai:
93 | ipv4_address: 172.20.0.2
94 | environment:
95 | - MYSQL_ROOT_PASSWORD=test123
96 | volumes:
97 | - ./init.sql:/docker-entrypoint-initdb.d/init.sql
98 | - /usr/mysql/log:/var/log/mysql
99 | - /usr/mysql/data:/var/lib/mysql
100 | - /usr/mysql/conf:/etc/mysql/conf.d
101 | # command: bash -c "mysqld --init-file=/docker-entrypoint-initdb.d/init.sql"
102 | redis:
103 | image: redis:latest
104 | container_name: redis
105 | restart: always
106 | ports:
107 | - "6379:6379"
108 | networks:
109 | comfyai:
110 | ipv4_address: 172.20.0.4
111 | networks:
112 | comfyai:
113 | driver: bridge
114 | ipam:
115 | config:
116 | - subnet: 172.20.0.0/16
117 | gateway: 172.20.0.1
118 |
119 |
120 |
--------------------------------------------------------------------------------
/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bobvinch/comfy-server/137cfdd59a167ef499418b894ae2706aac839f21/img.png
--------------------------------------------------------------------------------
/init.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE oneapi;
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/nest-cli",
3 | "collection": "@nestjs/schematics",
4 | "sourceRoot": "src",
5 | "compilerOptions": {
6 | "deleteOutDir": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "myserver",
3 | "version": "0.0.1",
4 | "description": "\r
\r
",
5 | "author": "",
6 | "private": true,
7 | "license": "UNLICENSED",
8 | "scripts": {
9 | "build": "cross-env NODE_ENV=production nest build",
10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
11 | "start": "cross-env NODE_ENV=production nest start",
12 | "start:dev": "cross-env NODE_ENV=development nest start --watch",
13 | "start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
14 | "start:prod": "node dist/main",
15 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
16 | "test": "jest",
17 | "test:watch": "jest --watch",
18 | "test:cov": "jest --coverage",
19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
20 | "test:e2e": "jest --config ./test/jest-e2e.json"
21 | },
22 | "dependencies": {
23 | "@nestjs-modules/ioredis": "^2.0.2",
24 | "@nestjs/bull": "^10.0.1",
25 | "@nestjs/common": "^10.3.6",
26 | "@nestjs/config": "^3.2.0",
27 | "@nestjs/core": "^10.3.6",
28 | "@nestjs/jwt": "^10.2.0",
29 | "@nestjs/mapped-types": "*",
30 | "@nestjs/mongoose": "^10.0.4",
31 | "@nestjs/platform-express": "^10.0.0",
32 | "@nestjs/platform-socket.io": "^10.3.1",
33 | "@nestjs/schedule": "^4.0.1",
34 | "@nestjs/swagger": "^7.3.0",
35 | "@nestjs/typeorm": "^10.0.2",
36 | "@nestjs/websockets": "^10.3.1",
37 | "@types/ali-oss": "^6.16.11",
38 | "@types/socket.io": "^3.0.2",
39 | "@types/socket.io-client": "^3.0.0",
40 | "ali-oss": "^6.20.0",
41 | "axios": "^1.6.7",
42 | "bcrypt": "^5.1.1",
43 | "bull": "^4.12.2",
44 | "cross-env": "^7.0.3",
45 | "fast-xml-parser": "^4.3.6",
46 | "ioredis": "^5.4.1",
47 | "mongoose": "^8.1.3",
48 | "mysql2": "^3.9.1",
49 | "nanoid": "^3.0.0",
50 | "reflect-metadata": "^0.1.13",
51 | "rxjs": "^7.8.1",
52 | "socket.io": "^4.7.4",
53 | "socket.io-client": "^4.7.4",
54 | "typeorm": "^0.3.20",
55 | "ws": "^8.16.0",
56 | "xml2js": "^0.6.2",
57 | "xmlbuilder2": "^3.1.1"
58 | },
59 | "devDependencies": {
60 | "@nestjs/cli": "^10.0.0",
61 | "@nestjs/schematics": "^10.0.0",
62 | "@nestjs/testing": "^10.0.0",
63 | "@types/bcrypt": "^5.0.2",
64 | "@types/bull": "^4.10.0",
65 | "@types/express": "^4.17.21",
66 | "@types/jest": "^29.5.2",
67 | "@types/node": "^20.3.1",
68 | "@types/supertest": "^6.0.0",
69 | "@types/ws": "^8.5.10",
70 | "@typescript-eslint/eslint-plugin": "^6.0.0",
71 | "@typescript-eslint/parser": "^6.0.0",
72 | "eslint": "^8.42.0",
73 | "eslint-config-prettier": "^9.0.0",
74 | "eslint-plugin-prettier": "^5.0.0",
75 | "jest": "^29.5.0",
76 | "prettier": "^3.0.0",
77 | "source-map-support": "^0.5.21",
78 | "supertest": "^6.3.3",
79 | "ts-jest": "^29.1.0",
80 | "ts-loader": "^9.4.3",
81 | "ts-node": "^10.9.1",
82 | "tsconfig-paths": "^4.2.0",
83 | "typescript": "^5.1.3"
84 | },
85 | "jest": {
86 | "moduleFileExtensions": [
87 | "js",
88 | "json",
89 | "ts"
90 | ],
91 | "rootDir": "src",
92 | "testRegex": ".*\\.spec\\.ts$",
93 | "transform": {
94 | "^.+\\.(t|j)s$": "ts-jest"
95 | },
96 | "collectCoverageFrom": [
97 | "**/*.(t|j)s"
98 | ],
99 | "coverageDirectory": "../coverage",
100 | "testEnvironment": "node"
101 | },
102 | "main": ".eslintrc.js"
103 | }
104 |
--------------------------------------------------------------------------------
/pm2.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * this is a pm2 starter config file
3 | * @type {{apps: [{watch: boolean, name: string, env: {NODE_ENV: string}, script: string}]}}
4 | */
5 | module.exports = {
6 | apps: [
7 | {
8 | name: 'comfy-server',
9 | script: './dist/main.js',
10 | watch: true,
11 | env: {
12 | NODE_ENV: 'production',
13 | },
14 | },
15 | ],
16 | };
17 |
--------------------------------------------------------------------------------
/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Inject } from '@nestjs/common';
2 | import { AppService } from './app.service';
3 | import { DrawService } from './draw/draw.service';
4 |
5 | @Controller()
6 | export class AppController {
7 | constructor(
8 | private readonly appService: AppService,
9 | private readonly drawService: DrawService,
10 | ) {}
11 |
12 | @Get()
13 | getHello(): string {
14 | return this.appService.getHello();
15 | }
16 | @Get('test')
17 | test(): string {
18 | return process.env.REDIS_HOST;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MiddlewareConsumer,
3 | Module,
4 | NestModule,
5 | RequestMethod,
6 | } from '@nestjs/common';
7 | import { AppController } from './app.controller';
8 | import { AppService } from './app.service';
9 | import { WsGateway } from './ws/ws.gateway';
10 | import { DrawConsumer } from './draw/DrawCosumer';
11 | import { ConfigModule } from '@nestjs/config';
12 |
13 | //链接mongodb
14 | import { MongooseModule } from '@nestjs/mongoose';
15 |
16 | //链接mysql
17 | import { TypeOrmModule } from '@nestjs/typeorm';
18 | import { OneAPIUsersModule } from 'src/oneapi/users/users.module';
19 | import { TokensModule } from './oneapi/tokens/tokens.module';
20 | import { ConfigService } from '@nestjs/config/dist';
21 |
22 | //定时任务
23 |
24 | import { WechatAuthModule } from './wechat-auth/wechat-auth.module';
25 | import { XMLMiddleware } from './middleware/XML.middleware';
26 | import { DrawModule } from './draw/draw.module';
27 | import { UsersModule } from './users/users.module';
28 | import { AuthModule } from './auth/auth.module';
29 | import { CacheModule } from './cache/cache.module';
30 | import { FileModule } from './file/file.module';
31 | import { TweetModule } from './tweet/tweet.module';
32 |
33 | //'mongodb://username:password@localhost:27017/nest'
34 | @Module({
35 | imports: [
36 | ConfigModule.forRoot({
37 | isGlobal: true,
38 | envFilePath: [`.env.${process.env.NODE_ENV}`],
39 | }),
40 | //OneAPI数据库,如果不启动ONEAPI的大模型,删除此处的模块引入和oneapi模块
41 | TypeOrmModule.forRootAsync({
42 | useFactory: (config: ConfigService) => ({
43 | type: 'mysql',
44 | host: config.get('ONEAPI_MYSQL_HOST'),
45 | port: config.get('ONEAPI_MYSQL_PORT'),
46 | username: config.get('ONEAPI_MYSQL_USERNAME'),
47 | password: config.get('ONEAPI_MYSQL_PASSWORD'),
48 | database: config.get('ONEAPI_MYSQL_DATABASE'),
49 | // entities: [User],
50 | autoLoadEntities: true,
51 | synchronize: false, //是否同步,正式环境设置为false
52 | }),
53 | inject: [ConfigService],
54 | }),
55 | MongooseModule.forRootAsync({
56 | useFactory: (config: ConfigService) => ({
57 | uri: config.get('CONFIG_DB_MONGO_URI'),
58 | user: config.get('CONFIG_DB_MONGO_USERNAME'),
59 | pass: config.get('CONFIG_DB_MONGO_PASSWORD'),
60 | dbName: 'aidraw',
61 | }),
62 | inject: [ConfigService],
63 | }),
64 | WechatAuthModule,
65 | OneAPIUsersModule,
66 | TokensModule,
67 | DrawModule,
68 | ConfigModule,
69 | UsersModule,
70 | AuthModule,
71 | CacheModule,
72 | FileModule,
73 | TweetModule,
74 | ],
75 | controllers: [AppController],
76 | providers: [AppService, WsGateway, DrawConsumer],
77 | })
78 | /**
79 | * 注册中间件,微信消息处理中间件
80 | */
81 | export class AppModule implements NestModule {
82 | configure(consumer: MiddlewareConsumer) {
83 | consumer.apply(XMLMiddleware).forRoutes({
84 | path: 'wechatauth/handleMessage',
85 | method: RequestMethod.POST,
86 | });
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/app.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class AppService {
5 | // 是否启用保存绘画历史记录
6 | public Draw_SaveHistory = false;
7 | getHello(): string {
8 | return 'Welcome to my comfyui server'.toUpperCase();
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/auth/auth.controller.ts:
--------------------------------------------------------------------------------
1 | import { AuthService } from './auth.service';
2 | import {
3 | Get,
4 | Query,
5 | Body,
6 | Controller,
7 | Post,
8 | HttpCode,
9 | HttpStatus,
10 | } from '@nestjs/common';
11 | import { ApiOperation } from '@nestjs/swagger';
12 |
13 | @Controller('auth')
14 | export class AuthController {
15 | constructor(private authService: AuthService) {}
16 | @ApiOperation({
17 | summary: '鉴权',
18 | description: 'Sign in with username and password,returning a token',
19 | parameters: [
20 | {
21 | name: 'username',
22 | in: 'query',
23 | required: true,
24 | description: 'username',
25 | schema: {
26 | type: 'string',
27 | },
28 | },
29 | {
30 | name: 'password',
31 | in: 'query',
32 | required: true,
33 | description: 'password',
34 | schema: {
35 | type: 'string',
36 | },
37 | },
38 | ],
39 | })
40 | @HttpCode(HttpStatus.OK)
41 | @Get('signin')
42 | signIn(
43 | @Query('username') username: string,
44 | @Query('password') password: string,
45 | ) {
46 | return this.authService.authToken(username, password);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/auth/auth.guard.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CanActivate,
3 | ExecutionContext,
4 | Injectable,
5 | UnauthorizedException,
6 | } from '@nestjs/common';
7 | import { JwtService } from '@nestjs/jwt';
8 | import { jwtConstants } from './constants';
9 | import { Request } from 'express';
10 |
11 | /**
12 | * 🔒 This guard is used to protect routes that require authentication.
13 | * It will check if the user is authenticated and if so, it will attach the user's payload to the request object.
14 | * If the user is not authenticated, it will throw an UnauthorizedException.
15 | *
16 | */
17 | @Injectable()
18 | export class AuthGuard implements CanActivate {
19 | constructor(private jwtService: JwtService) {}
20 |
21 | async canActivate(context: ExecutionContext): Promise {
22 | const request = context.switchToHttp().getRequest();
23 | const token = this.extractTokenFromHeader(request);
24 | if (!token) {
25 | throw new UnauthorizedException();
26 | }
27 | try {
28 | // 💡 We're assigning the payload to the request object here
29 | // so that we can access it in our route handlers
30 | request['user'] = await this.jwtService.verifyAsync(token, {
31 | secret: jwtConstants.secret,
32 | });
33 | } catch {
34 | throw new UnauthorizedException();
35 | }
36 | return true;
37 | }
38 |
39 | private extractTokenFromHeader(request: Request): string | undefined {
40 | const [type, token] = request.headers.authorization?.split(' ') ?? [];
41 | return type === 'Bearer' ? token : undefined;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { AuthController } from './auth.controller';
3 | import { AuthService } from './auth.service';
4 | import { UsersModule } from '../users/users.module';
5 | import { JwtModule } from '@nestjs/jwt';
6 | import { jwtConstants } from './constants';
7 |
8 | @Module({
9 | imports: [
10 | UsersModule,
11 | JwtModule.register({
12 | global: true,
13 | secret: jwtConstants.secret,
14 | signOptions: { expiresIn: '300s' },
15 | }),
16 | ],
17 | controllers: [AuthController],
18 | providers: [AuthService],
19 | exports: [AuthService],
20 | })
21 | export class AuthModule {}
22 |
--------------------------------------------------------------------------------
/src/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, UnauthorizedException } from '@nestjs/common';
2 | import { UsersService } from '../users/users.service';
3 | import { JwtService } from '@nestjs/jwt';
4 |
5 | @Injectable()
6 | export class AuthService {
7 | constructor(
8 | private usersService: UsersService,
9 | private jwtService: JwtService,
10 | ) {}
11 |
12 | async authToken(username: string, pass: string): Promise {
13 | const user = await this.usersService.findByUsername(username);
14 | if (
15 | !user ||
16 | !(await this.usersService.comparePassword(pass, user?.password))
17 | ) {
18 | throw new UnauthorizedException();
19 | }
20 | return {
21 | access_token: await this.usersService.createToken(
22 | user._id + '',
23 | user.username,
24 | ),
25 | };
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/auth/constants.ts:
--------------------------------------------------------------------------------
1 | export const jwtConstants = {
2 | secret: 'this is a very secret secret',
3 | };
4 |
--------------------------------------------------------------------------------
/src/cache/cache.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller } from '@nestjs/common';
2 | import { CacheService } from './cache.service';
3 |
4 | @Controller('cache')
5 | export class CacheController {
6 | constructor(private readonly cacheService: CacheService) {}
7 | }
8 |
--------------------------------------------------------------------------------
/src/cache/cache.module.ts:
--------------------------------------------------------------------------------
1 | import { Module, Global } from '@nestjs/common';
2 | import { CacheService } from './cache.service';
3 | import { CacheController } from './cache.controller';
4 | import { ConfigService } from '@nestjs/config';
5 | import { RedisModule } from '@nestjs-modules/ioredis';
6 | @Global()
7 | @Module({
8 | imports: [
9 | RedisModule.forRootAsync({
10 | useFactory: (config: ConfigService) => ({
11 | type: 'single',
12 | url: `redis://${config.get('CONFIG_COMFYUI_QUENE_REDIS_HOST')}:${config.get('CONFIG_COMFYUI_QUENE_REDIS_PORT')}`,
13 | options: {
14 | password: config.get('CONFIG_COMFYUI_QUENE_REDIS_PASSWORD'),
15 | },
16 | }),
17 | inject: [ConfigService],
18 | }),
19 | ],
20 | providers: [CacheService],
21 | exports: [CacheService],
22 | })
23 | export class CacheModule {}
24 |
--------------------------------------------------------------------------------
/src/cache/cache.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import Redis from 'ioredis';
3 | import { ConfigService } from '@nestjs/config';
4 | import { InjectRedis } from '@nestjs-modules/ioredis';
5 | @Injectable()
6 | export class CacheService {
7 | // private redisClient: Redis;
8 | constructor(
9 | private readonly configService: ConfigService,
10 | @InjectRedis() private readonly redis: Redis,
11 | // private readonly redisService: RedisService,
12 | ) {
13 | console.log('环境变量', this.configService);
14 | // this.redisClient = this.redisService.getClient();
15 | }
16 | async root(): Promise {
17 | // this.redisClient = await this.redisService.getClient();
18 | return true;
19 | }
20 | //获取值
21 | async get(key: string): Promise {
22 | let value = await this.redis.get(key);
23 | try {
24 | value = JSON.parse(value);
25 | } catch (error) {}
26 | return value;
27 | }
28 | /**
29 | * 设置值
30 | * @param key {string} key
31 | * @param value 值
32 | * @param second 过期时间 秒
33 | * @returns Promise
34 | */
35 | async set(key: string, value: any, second?: number) {
36 | value = JSON.stringify(value);
37 | return this.redis.set(key, value);
38 | }
39 | //删除值
40 | async del(key: string) {
41 | return this.redis.del(key);
42 | }
43 | //清除缓存
44 | async flushall() {
45 | return this.redis.flushall();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/draw/DrawCosumer.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Process,
3 | Processor,
4 | OnQueueActive,
5 | OnQueueCompleted,
6 | } from '@nestjs/bull';
7 | import { Job } from 'bull';
8 | import { WsGateway } from 'src/ws/ws.gateway';
9 | import { Logger } from '@nestjs/common';
10 | import { DrawService } from './draw.service';
11 | import { WechatAuthService } from '../wechat-auth/wechat-auth.service';
12 | import { AppService } from '../app.service';
13 | import Websocket = require('ws');
14 |
15 | @Processor('draw')
16 | export class DrawConsumer {
17 | constructor(
18 | private readonly drawService: DrawService,
19 | private readonly wechatauthService: WechatAuthService,
20 | private readonly appSevice: AppService,
21 | ) {}
22 |
23 | private readonly logger = new Logger(DrawConsumer.name);
24 | private readonly clientId = 'admin9527'; //id可以随意
25 | public ws_client: Websocket;
26 |
27 | @Process('drawtask')
28 | async drawtask(job: Job): Promise {
29 | // const output = await this.testTask(); //测试
30 | // 绘画任务
31 | const output = await this.drawTaskExcu(job, this.drawService.local_comfyui);
32 | // this.logger.debug(`任务完成,${job.id}`);
33 | //广播给所有人排队情况
34 | const message = {
35 | type: 'receive',
36 | queue_remaining: await this.drawService.getQueueLength(),
37 | };
38 | WsGateway.server.emit('message', JSON.stringify(message));
39 | return output + '';
40 | }
41 |
42 | /**
43 | *
44 | * @param job
45 | * @param server_url
46 | */
47 | async drawTaskExcu(job: Job, server_url: string) {
48 | return new Promise(async (resolve, reject) => {
49 | //client_id为用户id
50 | // this.logger.debug(
51 | // `初始化websocket链接的结构${await this.websocketInit()}`,
52 | // );
53 | if (!(await this.websocketInit(server_url))) {
54 | reject({ status: 'error', data: job.data });
55 | return;
56 | }
57 | const {
58 | data: { source, client_id, prompt, socket_id, api },
59 | } = job;
60 | const params = {
61 | client_id: this.clientId, //固定值
62 | prompt: prompt,
63 | };
64 | const response = await this.drawService.sendTackprompt(
65 | server_url,
66 | params,
67 | );
68 | this.logger.debug(`发送绘画任务后的响应${response}`);
69 | if (response) {
70 | //将获取到的任务id保存到data中 方便后续在websocket失效的情况下也能取回绘画结果
71 | job.data.prompt_id = response;
72 | //将数据重新存给job
73 | await job.update(job.data);
74 | //监听服务器消息
75 | this.logger.debug(`发送绘画任务成功`);
76 | this.ws_client.onmessage = async (event: any) => {
77 | //转发
78 | this.logger.debug(event.data);
79 | //如果存在并且socket处于连接状态
80 | const target_socket =
81 | WsGateway.server.sockets?.sockets?.get(socket_id);
82 | if (target_socket) {
83 | this.logger.debug(`发送给${socket_id},${event.data}`);
84 | target_socket.emit('message', event.data);
85 | }
86 | try {
87 | const { type, data } = JSON.parse(event.data + '');
88 | if (type === 'execution_error') {
89 | reject({ status: 'error', data: job.data });
90 | }
91 | if (type === 'executed') {
92 | //如果API是反推提示词输出文本结果
93 | if (api === '图片反推提示词') {
94 | const {
95 | output: { tags },
96 | } = data;
97 | resolve(tags[0]);
98 | }
99 | // 解析视频
100 | const {
101 | data: {
102 | output: { gifs },
103 | },
104 | } = JSON.parse(event.data + '');
105 | if (gifs && gifs[0]?.filename.includes('final')) {
106 | let videoUrl = '';
107 | //如果是微信消息
108 | const { filename, subfolder, type } = gifs[0];
109 | if (subfolder) {
110 | videoUrl = `${server_url}/view?subfolder=${subfolder}&filename=${filename}&type=${type}`;
111 | } else {
112 | videoUrl = `${server_url}/view?filename=${filename}&type=${type}`;
113 | }
114 | // 微信公众号回复
115 | if (source === 'wechat') {
116 | const mediaId =
117 | await this.wechatauthService.getMediaId(videoUrl);
118 | await this.wechatauthService.sendServiceImageMessge(
119 | mediaId,
120 | client_id,
121 | );
122 | }
123 | resolve(videoUrl);
124 | }
125 |
126 | // 解析图片
127 | const {
128 | data: {
129 | output: { images },
130 | },
131 | } = JSON.parse(event.data + '');
132 | if (images && images[0]?.filename.includes('final')) {
133 | let imageUrl = '';
134 | //如果是微信消息
135 | const { filename, subfolder, type } = images[0];
136 |
137 | if (subfolder) {
138 | imageUrl = `${server_url}/view?subfolder=${subfolder}&filename=${filename}&type=${type}`;
139 | } else {
140 | imageUrl = `${server_url}/view?filename=${filename}&type=${type}`;
141 | }
142 | // 微信公众号回复
143 | // if (source === 'wechat') {
144 | // const mediaId =
145 | // await this.wechatauthService.getMediaId(imageUrl);
146 | // await this.wechatauthService.sendServiceImageMessge(
147 | // mediaId,
148 | // client_id,
149 | // );
150 | // }
151 | resolve(imageUrl);
152 | }
153 | //保存到数据库
154 | if (this.appSevice.Draw_SaveHistory) {
155 | // todo 保存到数据库
156 | }
157 | }
158 | } catch (e) {
159 | this.logger.error(e);
160 | }
161 | };
162 | } else {
163 | reject({ status: 'error', data: job.data });
164 | }
165 | });
166 | }
167 |
168 | /**
169 | * 初始化与绘画服务端的链接
170 | */
171 | async websocketInit(server_url: string): Promise {
172 | return new Promise(async (resolve) => {
173 | if (!this.validateWsconnect()) {
174 | const url = server_url.split('//')[1];
175 | this.ws_client = new Websocket(
176 | `${server_url.includes('https') ? 'wss' : 'ws'}://${url}/ws?clientId=${this.clientId}`,
177 | );
178 | this.ws_client.onopen = () => {
179 | this.logger.debug(
180 | '链接绘画服务器成功,链接状态:',
181 | this.ws_client.readyState,
182 | );
183 | resolve(true);
184 | };
185 | this.ws_client.onerror = () => {
186 | this.logger.error('链接绘画服务器失败');
187 | resolve(false);
188 | };
189 | setTimeout(() => {
190 | if (this.ws_client.readyState !== 1) {
191 | this.logger.error('链接绘画服务器超时');
192 | resolve(false);
193 | }
194 | }, 500);
195 | } else {
196 | resolve(true);
197 | }
198 | });
199 | }
200 |
201 | /**
202 | * 验证链接状态
203 | */
204 | validateWsconnect() {
205 | if (this.ws_client === undefined || this.ws_client.readyState !== 1) {
206 | this.logger.error('链接不存在,尝试重新连接');
207 | return false;
208 | } else {
209 | return true;
210 | }
211 | }
212 |
213 | @OnQueueActive()
214 | async onActive(job: Job) {
215 | const remain = await this.drawService.getQueueLength();
216 | //广播队列任务信息
217 | WsGateway.server.sockets?.emit('remain', {
218 | remain,
219 | });
220 | this.logger.debug(
221 | `onActive job ${job.id} of type ${job.name} with data ${job.data}...队列::`,
222 | remain,
223 | );
224 | }
225 |
226 | @OnQueueCompleted()
227 | OnQueueCompleted(job: Job) {
228 | console.log(
229 | `Processing job ${job.id} of type ${job.name} with data ${job.returnvalue}...finished`,
230 | );
231 | }
232 |
233 | private async testTask() {
234 | return new Promise((resolve) => {
235 | setTimeout(() => {
236 | resolve('ok');
237 | }, 10000);
238 | });
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/draw/data/DrawConfig.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 绘画模块配置参数
3 | */
4 |
5 | export const drawConfig={
6 | defaultTimeOut: 60, //默认任务超时时间
7 | };
8 |
9 | export const ApiTimeOut = [
10 | {
11 | type: '文生图',
12 | timeout: 60,
13 | },
14 | {
15 | type: '图生图',
16 | timeout: 60,
17 | },
18 | {
19 | type: '图生视频',
20 | timeout: 120,
21 | },
22 | {
23 | type: 'AI模特',
24 | timeout: 120,
25 | },
26 | {
27 | type: 'AI写真',
28 | timeout: 240,
29 | },
30 | {
31 | type: '放大1',
32 | timeout: 120,
33 | },
34 | {
35 | type: '放大2',
36 | timeout: 180,
37 | },
38 | {
39 | type: '图片反推提示词',
40 | timeout: 20,
41 | },
42 | ] as ComfyAPIType[];
43 |
44 | export interface ComfyAPIType {
45 | type:
46 | | '文生图'
47 | | '图生图'
48 | | '图生视频'
49 | | 'AI模特'
50 | | 'AI写真'
51 | | '放大1'
52 | | '放大2'
53 | | 'AI推文'
54 | | '换脸'
55 | | '图片反推提示词';
56 | timeout: number;
57 | }
58 |
59 | export interface DrawTask {
60 | source: 'wechat' | 'web';
61 | client_id: string;
62 | prompt: any;
63 | api: string;
64 | socket_id?: string;
65 | lifo?: boolean;
66 | prompt_id?: string;
67 | }
68 |
--------------------------------------------------------------------------------
/src/draw/data/api_matting.json:
--------------------------------------------------------------------------------
1 | {
2 | "5": {
3 | "inputs": {
4 | "prompt": [
5 | "19",
6 | 0
7 | ],
8 | "threshold": 0.31,
9 | "sam_model": [
10 | "6",
11 | 0
12 | ],
13 | "grounding_dino_model": [
14 | "7",
15 | 0
16 | ],
17 | "image": [
18 | "62",
19 | 0
20 | ]
21 | },
22 | "class_type": "GroundingDinoSAMSegment (segment anything)"
23 | },
24 | "6": {
25 | "inputs": {
26 | "model_name": "sam_vit_h_4b8939.pth",
27 | "device_mode": "Prefer GPU"
28 | },
29 | "class_type": "SAMLoader"
30 | },
31 | "7": {
32 | "inputs": {
33 | "model_name": "GroundingDINO_SwinB (938MB)"
34 | },
35 | "class_type": "GroundingDinoModelLoader (segment anything)"
36 | },
37 | "19": {
38 | "inputs": {
39 | "from_translate": "auto",
40 | "to_translate": "english",
41 | "add_proxies": "disable",
42 | "proxies": "",
43 | "auth_data": "",
44 | "service": "GoogleTranslator",
45 | "text": "人",
46 | "Show proxy": "proxy_hide",
47 | "Show authorization": "authorization_hide"
48 | },
49 | "class_type": "DeepTranslatorTextNode"
50 | },
51 | "44": {
52 | "inputs": {
53 | "mask": [
54 | "5",
55 | 1
56 | ]
57 | },
58 | "class_type": "InvertMask"
59 | },
60 | "59": {
61 | "inputs": {
62 | "image": [
63 | "5",
64 | 0
65 | ],
66 | "alpha": [
67 | "44",
68 | 0
69 | ]
70 | },
71 | "class_type": "JoinImageWithAlpha"
72 | },
73 | "62": {
74 | "inputs": {
75 | "image_path": "C:\\Users\\wangb\\Downloads\\image1 - 2024-03-19T221914.324.png",
76 | "RGBA": "false",
77 | "filename_text_extension": "true"
78 | },
79 | "class_type": "Image Load"
80 | },
81 | "63": {
82 | "inputs": {
83 | "output_path": "[time(%Y-%m-%d)]",
84 | "filename_prefix": [
85 | "66",
86 | 0
87 | ],
88 | "filename_delimiter": "_",
89 | "filename_number_padding": 4,
90 | "filename_number_start": "false",
91 | "extension": "png",
92 | "quality": 100,
93 | "lossless_webp": "false",
94 | "overwrite_mode": "false",
95 | "show_history": "false",
96 | "show_history_by_prefix": "true",
97 | "embed_workflow": "true",
98 | "show_previews": "true",
99 | "images": [
100 | "59",
101 | 0
102 | ]
103 | },
104 | "class_type": "Image Save"
105 | },
106 | "66": {
107 | "inputs": {
108 | "delimiter": "-",
109 | "clean_whitespace": "true",
110 | "text_a": [
111 | "19",
112 | 0
113 | ],
114 | "text_b": "output_matting_final_"
115 | },
116 | "class_type": "Text Concatenate"
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/draw/data/api_removebg.json:
--------------------------------------------------------------------------------
1 | {
2 | "62": {
3 | "inputs": {
4 | "image_path": "E:\\ComfyUI_windows_portable\\ComfyUI\\input\\canvasimage (35).png",
5 | "RGBA": "false",
6 | "filename_text_extension": "true"
7 | },
8 | "class_type": "Image Load"
9 | },
10 | "63": {
11 | "inputs": {
12 | "output_path": "[time(%Y-%m-%d)]",
13 | "filename_prefix": [
14 | "66",
15 | 0
16 | ],
17 | "filename_delimiter": "_",
18 | "filename_number_padding": 4,
19 | "filename_number_start": "false",
20 | "extension": "png",
21 | "quality": 100,
22 | "lossless_webp": "false",
23 | "overwrite_mode": "false",
24 | "show_history": "false",
25 | "show_history_by_prefix": "true",
26 | "embed_workflow": "true",
27 | "show_previews": "true",
28 | "images": [
29 | "72",
30 | 0
31 | ]
32 | },
33 | "class_type": "Image Save"
34 | },
35 | "66": {
36 | "inputs": {
37 | "delimiter": "-",
38 | "clean_whitespace": "true",
39 | "text_a": [
40 | "62",
41 | 2
42 | ],
43 | "text_b": "output_rmbg_final_"
44 | },
45 | "class_type": "Text Concatenate"
46 | },
47 | "72": {
48 | "inputs": {
49 | "transparency": true,
50 | "model": "u2net",
51 | "post_processing": false,
52 | "only_mask": false,
53 | "alpha_matting": false,
54 | "alpha_matting_foreground_threshold": 240,
55 | "alpha_matting_background_threshold": 10,
56 | "alpha_matting_erode_size": 10,
57 | "background_color": "none",
58 | "images": [
59 | "62",
60 | 0
61 | ]
62 | },
63 | "class_type": "Image Rembg (Remove Background)"
64 | }
65 | }
--------------------------------------------------------------------------------
/src/draw/data/api_upscale.json:
--------------------------------------------------------------------------------
1 | {
2 | "5": {
3 | "inputs": {
4 | "output_path": "[time(%Y-%m-%d)]",
5 | "filename_prefix": [
6 | "16",
7 | 0
8 | ],
9 | "filename_delimiter": "_",
10 | "filename_number_padding": 4,
11 | "filename_number_start": "false",
12 | "extension": "png",
13 | "quality": 100,
14 | "lossless_webp": "false",
15 | "overwrite_mode": "false",
16 | "show_history": "false",
17 | "show_history_by_prefix": "true",
18 | "embed_workflow": "true",
19 | "show_previews": "true",
20 | "images": [
21 | "11",
22 | 0
23 | ]
24 | },
25 | "class_type": "Image Save"
26 | },
27 | "11": {
28 | "inputs": {
29 | "upscale_model": [
30 | "12",
31 | 0
32 | ],
33 | "image": [
34 | "15",
35 | 0
36 | ]
37 | },
38 | "class_type": "ImageUpscaleWithModel"
39 | },
40 | "12": {
41 | "inputs": {
42 | "model_name": "4x-UltraSharp.pth"
43 | },
44 | "class_type": "UpscaleModelLoader"
45 | },
46 | "15": {
47 | "inputs": {
48 | "image_path": "E:\\ComfyUI_windows_portable\\ComfyUI\\output\\ComfyUI_01494_.png",
49 | "RGBA": "false",
50 | "filename_text_extension": "false"
51 | },
52 | "class_type": "Image Load"
53 | },
54 | "16": {
55 | "inputs": {
56 | "delimiter": "_",
57 | "clean_whitespace": "true",
58 | "text_a": [
59 | "15",
60 | 2
61 | ],
62 | "text_b": "output_upscale_final_"
63 | },
64 | "class_type": "Text Concatenate"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/draw/data/api_upscale_detailfix.json:
--------------------------------------------------------------------------------
1 | {
2 | "5": {
3 | "inputs": {
4 | "output_path": "[time(%Y-%m-%d)]",
5 | "filename_prefix": [
6 | "16",
7 | 0
8 | ],
9 | "filename_delimiter": "_",
10 | "filename_number_padding": 4,
11 | "filename_number_start": "false",
12 | "extension": "png",
13 | "quality": 100,
14 | "lossless_webp": "false",
15 | "overwrite_mode": "false",
16 | "show_history": "false",
17 | "show_history_by_prefix": "true",
18 | "embed_workflow": "true",
19 | "show_previews": "true",
20 | "images": [
21 | "28",
22 | 0
23 | ]
24 | },
25 | "class_type": "Image Save"
26 | },
27 | "15": {
28 | "inputs": {
29 | "image_path": "E:\\ComfyUI_windows_portable\\ComfyUI\\output\\ComfyUI_01494_.png",
30 | "RGBA": "false",
31 | "filename_text_extension": "false"
32 | },
33 | "class_type": "Image Load"
34 | },
35 | "16": {
36 | "inputs": {
37 | "delimiter": "_",
38 | "clean_whitespace": "true",
39 | "text_a": [
40 | "15",
41 | 2
42 | ],
43 | "text_b": "upscale_detailfix_final_"
44 | },
45 | "class_type": "Text Concatenate"
46 | },
47 | "23": {
48 | "inputs": {
49 | "ckpt_name": "极氪写实MAX-极氪白系列模型_V6.safetensors",
50 | "vae_name": "vae-ft-mse-840000-ema-pruned.safetensors",
51 | "clip_skip": -2,
52 | "lora_name": "None",
53 | "lora_model_strength": 1,
54 | "lora_clip_strength": 1,
55 | "positive": [
56 | "24",
57 | 0
58 | ],
59 | "negative": "CLIP_NEGATIVE",
60 | "token_normalization": "none",
61 | "weight_interpretation": "comfy",
62 | "empty_latent_width": 512,
63 | "empty_latent_height": 512,
64 | "batch_size": 1
65 | },
66 | "class_type": "Efficient Loader"
67 | },
68 | "24": {
69 | "inputs": {
70 | "model": "wd-v1-4-moat-tagger-v2",
71 | "threshold": 0.35,
72 | "character_threshold": 0.85,
73 | "exclude_tags": "",
74 | "tags": "1girl, solo, long_hair, looking_at_viewer, brown_hair, black_hair, bare_shoulders, brown_eyes, jewelry, upper_body, earrings, lips, eyelashes, red_background, realistic, nose",
75 | "image": [
76 | "15",
77 | 0
78 | ]
79 | },
80 | "class_type": "WD14Tagger|pysssss"
81 | },
82 | "28": {
83 | "inputs": {
84 | "upscale_by": 2,
85 | "seed": 54765465571794,
86 | "steps": 20,
87 | "cfg": 8,
88 | "sampler_name": "dpmpp_2m",
89 | "scheduler": "karras",
90 | "denoise": 0.25,
91 | "mode_type": "Linear",
92 | "tile_width": 512,
93 | "tile_height": 512,
94 | "mask_blur": 8,
95 | "tile_padding": 32,
96 | "seam_fix_mode": "None",
97 | "seam_fix_denoise": 1,
98 | "seam_fix_width": 64,
99 | "seam_fix_mask_blur": 8,
100 | "seam_fix_padding": 16,
101 | "force_uniform_tiles": "enable",
102 | "image": [
103 | "15",
104 | 0
105 | ],
106 | "model": [
107 | "23",
108 | 0
109 | ],
110 | "positive": [
111 | "23",
112 | 1
113 | ],
114 | "negative": [
115 | "23",
116 | 2
117 | ],
118 | "vae": [
119 | "23",
120 | 4
121 | ],
122 | "upscale_model": [
123 | "29",
124 | 0
125 | ]
126 | },
127 | "class_type": "UltimateSDUpscale"
128 | },
129 | "29": {
130 | "inputs": {
131 | "model_name": "4x-UltraSharp.pth"
132 | },
133 | "class_type": "UpscaleModelLoader"
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/draw/data/api_wechat_img2imgi.json:
--------------------------------------------------------------------------------
1 | {
2 | "18": {
3 | "inputs": {
4 | "text": "paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,"
5 | },
6 | "class_type": "ttN text"
7 | },
8 | "19": {
9 | "inputs": {
10 | "text": "(8k, best quality, masterpiece:1.2)"
11 | },
12 | "class_type": "ttN text"
13 | },
14 | "23": {
15 | "inputs": {
16 | "from_translate": "auto",
17 | "to_translate": "english",
18 | "add_proxies": "disable",
19 | "proxies": "",
20 | "auth_data": "",
21 | "service": "GoogleTranslator",
22 | "text": "",
23 | "Show proxy": "proxy_hide",
24 | "Show authorization": "authorization_hide"
25 | },
26 | "class_type": "DeepTranslatorTextNode"
27 | },
28 | "25": {
29 | "inputs": {
30 | "delimiter": ",",
31 | "clean_whitespace": "true",
32 | "text_a": [
33 | "87",
34 | 0
35 | ],
36 | "text_b": [
37 | "19",
38 | 0
39 | ]
40 | },
41 | "class_type": "Text Concatenate"
42 | },
43 | "26": {
44 | "inputs": {
45 | "delimiter": "",
46 | "clean_whitespace": "true",
47 | "text_a": [
48 | "23",
49 | 0
50 | ],
51 | "text_b": [
52 | "18",
53 | 0
54 | ]
55 | },
56 | "class_type": "Text Concatenate"
57 | },
58 | "44": {
59 | "inputs": {
60 | "upscale_type": "latent",
61 | "hires_ckpt_name": "(use same)",
62 | "latent_upscaler": "nearest-exact",
63 | "pixel_upscaler": "4x-UltraSharp.pth",
64 | "upscale_by": 1.5,
65 | "use_same_seed": true,
66 | "seed": -1,
67 | "hires_steps": 12,
68 | "denoise": 0.3,
69 | "iterations": 1,
70 | "use_controlnet": false,
71 | "control_net_name": "control_sd15_random_color.pth",
72 | "strength": 1,
73 | "preprocessor": "CannyEdgePreprocessor",
74 | "preprocessor_imgs": false
75 | },
76 | "class_type": "HighRes-Fix Script"
77 | },
78 | "45": {
79 | "inputs": {
80 | "input_mode": "simple",
81 | "lora_count": 7,
82 | "lora_name_1": "lcm\\lcm_sd1.5_pytorch_lora_weights.safetensors",
83 | "lora_wt_1": 1,
84 | "model_str_1": 1,
85 | "clip_str_1": 1,
86 | "lora_name_2": "None",
87 | "lora_wt_2": 0.5,
88 | "model_str_2": 1,
89 | "clip_str_2": 1,
90 | "lora_name_3": "None",
91 | "lora_wt_3": 0.5,
92 | "model_str_3": 1,
93 | "clip_str_3": 1,
94 | "lora_name_4": "None",
95 | "lora_wt_4": 1,
96 | "model_str_4": 1,
97 | "clip_str_4": 1,
98 | "lora_name_5": "None",
99 | "lora_wt_5": 1,
100 | "model_str_5": 1,
101 | "clip_str_5": 1,
102 | "lora_name_6": "None",
103 | "lora_wt_6": 1,
104 | "model_str_6": 1,
105 | "clip_str_6": 1,
106 | "lora_name_7": "None",
107 | "lora_wt_7": 1,
108 | "model_str_7": 1,
109 | "clip_str_7": 1,
110 | "lora_name_8": "None",
111 | "lora_wt_8": 1,
112 | "model_str_8": 1,
113 | "clip_str_8": 1,
114 | "lora_name_9": "None",
115 | "lora_wt_9": 1,
116 | "model_str_9": 1,
117 | "clip_str_9": 1,
118 | "lora_name_10": "None",
119 | "lora_wt_10": 1,
120 | "model_str_10": 1,
121 | "clip_str_10": 1,
122 | "lora_name_11": "None",
123 | "lora_wt_11": 1,
124 | "model_str_11": 1,
125 | "clip_str_11": 1,
126 | "lora_name_12": "None",
127 | "lora_wt_12": 1,
128 | "model_str_12": 1,
129 | "clip_str_12": 1,
130 | "lora_name_13": "None",
131 | "lora_wt_13": 1,
132 | "model_str_13": 1,
133 | "clip_str_13": 1,
134 | "lora_name_14": "None",
135 | "lora_wt_14": 1,
136 | "model_str_14": 1,
137 | "clip_str_14": 1,
138 | "lora_name_15": "None",
139 | "lora_wt_15": 1,
140 | "model_str_15": 1,
141 | "clip_str_15": 1,
142 | "lora_name_16": "None",
143 | "lora_wt_16": 1,
144 | "model_str_16": 1,
145 | "clip_str_16": 1,
146 | "lora_name_17": "None",
147 | "lora_wt_17": 1,
148 | "model_str_17": 1,
149 | "clip_str_17": 1,
150 | "lora_name_18": "None",
151 | "lora_wt_18": 1,
152 | "model_str_18": 1,
153 | "clip_str_18": 1,
154 | "lora_name_19": "None",
155 | "lora_wt_19": 1,
156 | "model_str_19": 1,
157 | "clip_str_19": 1,
158 | "lora_name_20": "None",
159 | "lora_wt_20": 1,
160 | "model_str_20": 1,
161 | "clip_str_20": 1,
162 | "lora_name_21": "None",
163 | "lora_wt_21": 1,
164 | "model_str_21": 1,
165 | "clip_str_21": 1,
166 | "lora_name_22": "None",
167 | "lora_wt_22": 1,
168 | "model_str_22": 1,
169 | "clip_str_22": 1,
170 | "lora_name_23": "None",
171 | "lora_wt_23": 1,
172 | "model_str_23": 1,
173 | "clip_str_23": 1,
174 | "lora_name_24": "None",
175 | "lora_wt_24": 1,
176 | "model_str_24": 1,
177 | "clip_str_24": 1,
178 | "lora_name_25": "None",
179 | "lora_wt_25": 1,
180 | "model_str_25": 1,
181 | "clip_str_25": 1,
182 | "lora_name_26": "None",
183 | "lora_wt_26": 1,
184 | "model_str_26": 1,
185 | "clip_str_26": 1,
186 | "lora_name_27": "None",
187 | "lora_wt_27": 1,
188 | "model_str_27": 1,
189 | "clip_str_27": 1,
190 | "lora_name_28": "None",
191 | "lora_wt_28": 1,
192 | "model_str_28": 1,
193 | "clip_str_28": 1,
194 | "lora_name_29": "None",
195 | "lora_wt_29": 1,
196 | "model_str_29": 1,
197 | "clip_str_29": 1,
198 | "lora_name_30": "None",
199 | "lora_wt_30": 1,
200 | "model_str_30": 1,
201 | "clip_str_30": 1,
202 | "lora_name_31": "None",
203 | "lora_wt_31": 1,
204 | "model_str_31": 1,
205 | "clip_str_31": 1,
206 | "lora_name_32": "None",
207 | "lora_wt_32": 1,
208 | "model_str_32": 1,
209 | "clip_str_32": 1,
210 | "lora_name_33": "None",
211 | "lora_wt_33": 1,
212 | "model_str_33": 1,
213 | "clip_str_33": 1,
214 | "lora_name_34": "None",
215 | "lora_wt_34": 1,
216 | "model_str_34": 1,
217 | "clip_str_34": 1,
218 | "lora_name_35": "None",
219 | "lora_wt_35": 1,
220 | "model_str_35": 1,
221 | "clip_str_35": 1,
222 | "lora_name_36": "None",
223 | "lora_wt_36": 1,
224 | "model_str_36": 1,
225 | "clip_str_36": 1,
226 | "lora_name_37": "None",
227 | "lora_wt_37": 1,
228 | "model_str_37": 1,
229 | "clip_str_37": 1,
230 | "lora_name_38": "None",
231 | "lora_wt_38": 1,
232 | "model_str_38": 1,
233 | "clip_str_38": 1,
234 | "lora_name_39": "None",
235 | "lora_wt_39": 1,
236 | "model_str_39": 1,
237 | "clip_str_39": 1,
238 | "lora_name_40": "None",
239 | "lora_wt_40": 1,
240 | "model_str_40": 1,
241 | "clip_str_40": 1,
242 | "lora_name_41": "None",
243 | "lora_wt_41": 1,
244 | "model_str_41": 1,
245 | "clip_str_41": 1,
246 | "lora_name_42": "None",
247 | "lora_wt_42": 1,
248 | "model_str_42": 1,
249 | "clip_str_42": 1,
250 | "lora_name_43": "None",
251 | "lora_wt_43": 1,
252 | "model_str_43": 1,
253 | "clip_str_43": 1,
254 | "lora_name_44": "None",
255 | "lora_wt_44": 1,
256 | "model_str_44": 1,
257 | "clip_str_44": 1,
258 | "lora_name_45": "None",
259 | "lora_wt_45": 1,
260 | "model_str_45": 1,
261 | "clip_str_45": 1,
262 | "lora_name_46": "None",
263 | "lora_wt_46": 1,
264 | "model_str_46": 1,
265 | "clip_str_46": 1,
266 | "lora_name_47": "None",
267 | "lora_wt_47": 1,
268 | "model_str_47": 1,
269 | "clip_str_47": 1,
270 | "lora_name_48": "None",
271 | "lora_wt_48": 1,
272 | "model_str_48": 1,
273 | "clip_str_48": 1,
274 | "lora_name_49": "None",
275 | "lora_wt_49": 1,
276 | "model_str_49": 1,
277 | "clip_str_49": 1
278 | },
279 | "class_type": "LoRA Stacker"
280 | },
281 | "47": {
282 | "inputs": {
283 | "ckpt_name": "极氪写实MAX-极氪白系列模型_V6.safetensors",
284 | "vae_name": "Baked VAE",
285 | "clip_skip": -2,
286 | "lora_name": "None",
287 | "lora_model_strength": 1,
288 | "lora_clip_strength": 1,
289 | "positive": [
290 | "25",
291 | 0
292 | ],
293 | "negative": [
294 | "26",
295 | 0
296 | ],
297 | "token_normalization": "none",
298 | "weight_interpretation": "A1111",
299 | "empty_latent_width": 512,
300 | "empty_latent_height": 768,
301 | "batch_size": 1,
302 | "lora_stack": [
303 | "45",
304 | 0
305 | ]
306 | },
307 | "class_type": "Efficient Loader"
308 | },
309 | "51": {
310 | "inputs": {
311 | "output_path": "[time(%Y-%m-%d)]",
312 | "filename_prefix": "img2img_output_final_",
313 | "filename_delimiter": "_",
314 | "filename_number_padding": 4,
315 | "filename_number_start": "false",
316 | "extension": "png",
317 | "quality": 100,
318 | "lossless_webp": "false",
319 | "overwrite_mode": "false",
320 | "show_history": "false",
321 | "show_history_by_prefix": "true",
322 | "embed_workflow": "true",
323 | "show_previews": "true",
324 | "images": [
325 | "85",
326 | 5
327 | ]
328 | },
329 | "class_type": "Image Save"
330 | },
331 | "70": {
332 | "inputs": {
333 | "image_path": "C:\\Users\\wangb\\Pictures\\342585.png",
334 | "RGBA": "false",
335 | "filename_text_extension": "false"
336 | },
337 | "class_type": "Image Load"
338 | },
339 | "71": {
340 | "inputs": {
341 | "image": [
342 | "70",
343 | 0
344 | ]
345 | },
346 | "class_type": "Image Size to Number"
347 | },
348 | "72": {
349 | "inputs": {
350 | "number_type": "integer",
351 | "number": 512
352 | },
353 | "class_type": "Constant Number"
354 | },
355 | "73": {
356 | "inputs": {
357 | "operation": "division",
358 | "number_a": [
359 | "72",
360 | 0
361 | ],
362 | "number_b": [
363 | "71",
364 | 0
365 | ]
366 | },
367 | "class_type": "Number Operation"
368 | },
369 | "74": {
370 | "inputs": {
371 | "upscale_method": "nearest-exact",
372 | "scale_by": [
373 | "73",
374 | 1
375 | ],
376 | "image": [
377 | "70",
378 | 0
379 | ]
380 | },
381 | "class_type": "ImageScaleBy"
382 | },
383 | "85": {
384 | "inputs": {
385 | "seed": 579488174679770,
386 | "steps": 5,
387 | "cfg": 1.5,
388 | "sampler_name": "lcm",
389 | "scheduler": "sgm_uniform",
390 | "denoise": 0.5,
391 | "preview_method": "none",
392 | "vae_decode": "true",
393 | "model": [
394 | "47",
395 | 0
396 | ],
397 | "positive": [
398 | "47",
399 | 1
400 | ],
401 | "negative": [
402 | "47",
403 | 2
404 | ],
405 | "latent_image": [
406 | "86",
407 | 0
408 | ],
409 | "optional_vae": [
410 | "47",
411 | 4
412 | ],
413 | "script": [
414 | "44",
415 | 0
416 | ]
417 | },
418 | "class_type": "KSampler (Efficient)"
419 | },
420 | "86": {
421 | "inputs": {
422 | "pixels": [
423 | "74",
424 | 0
425 | ],
426 | "vae": [
427 | "47",
428 | 4
429 | ]
430 | },
431 | "class_type": "VAEEncode"
432 | },
433 | "87": {
434 | "inputs": {
435 | "model": "wd-v1-4-moat-tagger-v2",
436 | "threshold": 0.35,
437 | "character_threshold": 0.85,
438 | "exclude_tags": "",
439 | "tags": "1girl, solo, long_hair, breasts, blue_eyes, skirt, jewelry, braid, white_hair, necklace, cape, armor, lips, snow, realistic, dragon, crown_braid",
440 | "image": [
441 | "70",
442 | 0
443 | ]
444 | },
445 | "class_type": "WD14Tagger|pysssss"
446 | }
447 | }
--------------------------------------------------------------------------------
/src/draw/data/test.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": "web",
3 | "prompt": {
4 | "18": {
5 | "inputs": {
6 | "text": "paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,"
7 | },
8 | "class_type": "ttN text",
9 | "_meta": {
10 | "title": "TTN文本"
11 | }
12 | },
13 | "19": {
14 | "inputs": {
15 | "text": "(8k, best quality, masterpiece:1.2)"
16 | },
17 | "class_type": "ttN text",
18 | "_meta": {
19 | "title": "TTN文本"
20 | }
21 | },
22 | "25": {
23 | "inputs": {
24 | "delimiter": ",",
25 | "clean_whitespace": "true",
26 | "text_a": [
27 | "55",
28 | 0
29 | ],
30 | "text_b": [
31 | "19",
32 | 0
33 | ]
34 | },
35 | "class_type": "Text Concatenate",
36 | "_meta": {
37 | "title": "文本连锁"
38 | }
39 | },
40 | "26": {
41 | "inputs": {
42 | "delimiter": "",
43 | "clean_whitespace": "true",
44 | "text_a": [
45 | "58",
46 | 0
47 | ],
48 | "text_b": [
49 | "18",
50 | 0
51 | ]
52 | },
53 | "class_type": "Text Concatenate",
54 | "_meta": {
55 | "title": "文本连锁"
56 | }
57 | },
58 | "44": {
59 | "inputs": {
60 | "upscale_type": "latent",
61 | "hires_ckpt_name": "(use same)",
62 | "latent_upscaler": "nearest-exact",
63 | "pixel_upscaler": "4x-UltraSharp.pth",
64 | "upscale_by": 1,
65 | "use_same_seed": true,
66 | "seed": -1,
67 | "hires_steps": 12,
68 | "denoise": 0.3,
69 | "iterations": 1,
70 | "use_controlnet": false,
71 | "control_net_name": "control_sd15_random_color.pth",
72 | "strength": 1,
73 | "preprocessor": "CannyEdgePreprocessor",
74 | "preprocessor_imgs": false
75 | },
76 | "class_type": "HighRes-Fix Script",
77 | "_meta": {
78 | "title": "高清修复"
79 | }
80 | },
81 | "45": {
82 | "inputs": {
83 | "input_mode": "simple",
84 | "lora_count": 5,
85 | "lora_name_1": "lcm\\lcm_sd1.5_pytorch_lora_weights.safetensors",
86 | "lora_wt_1": 1,
87 | "model_str_1": 1,
88 | "clip_str_1": 1,
89 | "lora_name_2": "None",
90 | "lora_wt_2": 0.5,
91 | "model_str_2": 1,
92 | "clip_str_2": 1,
93 | "lora_name_3": "None",
94 | "lora_wt_3": 0.5,
95 | "model_str_3": 1,
96 | "clip_str_3": 1,
97 | "lora_name_4": "None",
98 | "lora_wt_4": 1,
99 | "model_str_4": 1,
100 | "clip_str_4": 1,
101 | "lora_name_5": "None",
102 | "lora_wt_5": 1,
103 | "model_str_5": 1,
104 | "clip_str_5": 1,
105 | "lora_name_6": "None",
106 | "lora_wt_6": 1,
107 | "model_str_6": 1,
108 | "clip_str_6": 1,
109 | "lora_name_7": "None",
110 | "lora_wt_7": 1,
111 | "model_str_7": 1,
112 | "clip_str_7": 1,
113 | "lora_name_8": "None",
114 | "lora_wt_8": 1,
115 | "model_str_8": 1,
116 | "clip_str_8": 1,
117 | "lora_name_9": "None",
118 | "lora_wt_9": 1,
119 | "model_str_9": 1,
120 | "clip_str_9": 1,
121 | "lora_name_10": "None",
122 | "lora_wt_10": 1,
123 | "model_str_10": 1,
124 | "clip_str_10": 1,
125 | "lora_name_11": "None",
126 | "lora_wt_11": 1,
127 | "model_str_11": 1,
128 | "clip_str_11": 1,
129 | "lora_name_12": "None",
130 | "lora_wt_12": 1,
131 | "model_str_12": 1,
132 | "clip_str_12": 1,
133 | "lora_name_13": "None",
134 | "lora_wt_13": 1,
135 | "model_str_13": 1,
136 | "clip_str_13": 1,
137 | "lora_name_14": "None",
138 | "lora_wt_14": 1,
139 | "model_str_14": 1,
140 | "clip_str_14": 1,
141 | "lora_name_15": "None",
142 | "lora_wt_15": 1,
143 | "model_str_15": 1,
144 | "clip_str_15": 1,
145 | "lora_name_16": "None",
146 | "lora_wt_16": 1,
147 | "model_str_16": 1,
148 | "clip_str_16": 1,
149 | "lora_name_17": "None",
150 | "lora_wt_17": 1,
151 | "model_str_17": 1,
152 | "clip_str_17": 1,
153 | "lora_name_18": "None",
154 | "lora_wt_18": 1,
155 | "model_str_18": 1,
156 | "clip_str_18": 1,
157 | "lora_name_19": "None",
158 | "lora_wt_19": 1,
159 | "model_str_19": 1,
160 | "clip_str_19": 1,
161 | "lora_name_20": "None",
162 | "lora_wt_20": 1,
163 | "model_str_20": 1,
164 | "clip_str_20": 1,
165 | "lora_name_21": "None",
166 | "lora_wt_21": 1,
167 | "model_str_21": 1,
168 | "clip_str_21": 1,
169 | "lora_name_22": "None",
170 | "lora_wt_22": 1,
171 | "model_str_22": 1,
172 | "clip_str_22": 1,
173 | "lora_name_23": "None",
174 | "lora_wt_23": 1,
175 | "model_str_23": 1,
176 | "clip_str_23": 1,
177 | "lora_name_24": "None",
178 | "lora_wt_24": 1,
179 | "model_str_24": 1,
180 | "clip_str_24": 1,
181 | "lora_name_25": "None",
182 | "lora_wt_25": 1,
183 | "model_str_25": 1,
184 | "clip_str_25": 1,
185 | "lora_name_26": "None",
186 | "lora_wt_26": 1,
187 | "model_str_26": 1,
188 | "clip_str_26": 1,
189 | "lora_name_27": "None",
190 | "lora_wt_27": 1,
191 | "model_str_27": 1,
192 | "clip_str_27": 1,
193 | "lora_name_28": "None",
194 | "lora_wt_28": 1,
195 | "model_str_28": 1,
196 | "clip_str_28": 1,
197 | "lora_name_29": "None",
198 | "lora_wt_29": 1,
199 | "model_str_29": 1,
200 | "clip_str_29": 1,
201 | "lora_name_30": "None",
202 | "lora_wt_30": 1,
203 | "model_str_30": 1,
204 | "clip_str_30": 1,
205 | "lora_name_31": "None",
206 | "lora_wt_31": 1,
207 | "model_str_31": 1,
208 | "clip_str_31": 1,
209 | "lora_name_32": "None",
210 | "lora_wt_32": 1,
211 | "model_str_32": 1,
212 | "clip_str_32": 1,
213 | "lora_name_33": "None",
214 | "lora_wt_33": 1,
215 | "model_str_33": 1,
216 | "clip_str_33": 1,
217 | "lora_name_34": "None",
218 | "lora_wt_34": 1,
219 | "model_str_34": 1,
220 | "clip_str_34": 1,
221 | "lora_name_35": "None",
222 | "lora_wt_35": 1,
223 | "model_str_35": 1,
224 | "clip_str_35": 1,
225 | "lora_name_36": "None",
226 | "lora_wt_36": 1,
227 | "model_str_36": 1,
228 | "clip_str_36": 1,
229 | "lora_name_37": "None",
230 | "lora_wt_37": 1,
231 | "model_str_37": 1,
232 | "clip_str_37": 1,
233 | "lora_name_38": "None",
234 | "lora_wt_38": 1,
235 | "model_str_38": 1,
236 | "clip_str_38": 1,
237 | "lora_name_39": "None",
238 | "lora_wt_39": 1,
239 | "model_str_39": 1,
240 | "clip_str_39": 1,
241 | "lora_name_40": "None",
242 | "lora_wt_40": 1,
243 | "model_str_40": 1,
244 | "clip_str_40": 1,
245 | "lora_name_41": "None",
246 | "lora_wt_41": 1,
247 | "model_str_41": 1,
248 | "clip_str_41": 1,
249 | "lora_name_42": "None",
250 | "lora_wt_42": 1,
251 | "model_str_42": 1,
252 | "clip_str_42": 1,
253 | "lora_name_43": "None",
254 | "lora_wt_43": 1,
255 | "model_str_43": 1,
256 | "clip_str_43": 1,
257 | "lora_name_44": "None",
258 | "lora_wt_44": 1,
259 | "model_str_44": 1,
260 | "clip_str_44": 1,
261 | "lora_name_45": "None",
262 | "lora_wt_45": 1,
263 | "model_str_45": 1,
264 | "clip_str_45": 1,
265 | "lora_name_46": "None",
266 | "lora_wt_46": 1,
267 | "model_str_46": 1,
268 | "clip_str_46": 1,
269 | "lora_name_47": "None",
270 | "lora_wt_47": 1,
271 | "model_str_47": 1,
272 | "clip_str_47": 1,
273 | "lora_name_48": "None",
274 | "lora_wt_48": 1,
275 | "model_str_48": 1,
276 | "clip_str_48": 1,
277 | "lora_name_49": "None",
278 | "lora_wt_49": 1,
279 | "model_str_49": 1,
280 | "clip_str_49": 1
281 | },
282 | "class_type": "LoRA Stacker",
283 | "_meta": {
284 | "title": "LoRA堆"
285 | }
286 | },
287 | "51": {
288 | "inputs": {
289 | "output_path": "[time(%Y-%m-%d)]",
290 | "filename_prefix": "undefined_text2img_output_final_",
291 | "filename_delimiter": "_",
292 | "filename_number_padding": 4,
293 | "filename_number_start": "false",
294 | "extension": "png",
295 | "quality": 100,
296 | "lossless_webp": "false",
297 | "overwrite_mode": "false",
298 | "show_history": "false",
299 | "show_history_by_prefix": "true",
300 | "embed_workflow": "true",
301 | "show_previews": "true",
302 | "images": [
303 | "54",
304 | 5
305 | ]
306 | },
307 | "class_type": "Image Save",
308 | "_meta": {
309 | "title": "图像保存"
310 | }
311 | },
312 | "52": {
313 | "inputs": {
314 | "vae_name": "Baked VAE",
315 | "clip_skip": -2,
316 | "lora_name": "None",
317 | "lora_model_strength": 1,
318 | "lora_clip_strength": 1,
319 | "positive": [
320 | "25",
321 | 0
322 | ],
323 | "negative": [
324 | "26",
325 | 0
326 | ],
327 | "token_normalization": "none",
328 | "weight_interpretation": "A1111",
329 | "empty_latent_width": 512,
330 | "empty_latent_height": 768,
331 | "batch_size": 1
332 | },
333 | "class_type": "Efficient Loader",
334 | "_meta": {
335 | "title": "效率加载器"
336 | }
337 | },
338 | "54": {
339 | "inputs": {
340 | "seed": 517745000437515,
341 | "steps": 20,
342 | "cfg": 7,
343 | "sampler_name": "dpmpp_2m",
344 | "scheduler": "karras",
345 | "denoise": 1,
346 | "preview_method": "none",
347 | "vae_decode": "true",
348 | "model": [
349 | "52",
350 | 0
351 | ],
352 | "positive": [
353 | "52",
354 | 1
355 | ],
356 | "negative": [
357 | "52",
358 | 2
359 | ],
360 | "latent_image": [
361 | "52",
362 | 3
363 | ],
364 | "optional_vae": [
365 | "52",
366 | 4
367 | ]
368 | },
369 | "class_type": "KSampler (Efficient)",
370 | "_meta": {
371 | "title": "K采样器(效率)"
372 | }
373 | },
374 | "55": {
375 | "inputs": {
376 | "text": "个绝美的职业装女孩",
377 | "platform": "alibaba",
378 | "source": "auto",
379 | "target": "en"
380 | },
381 | "class_type": "ZFTextTranslation",
382 | "_meta": {
383 | "title": "文本翻译"
384 | }
385 | },
386 | "58": {
387 | "inputs": {
388 | "text": "",
389 | "platform": "alibaba",
390 | "source": "auto",
391 | "target": "en"
392 | },
393 | "class_type": "ZFTextTranslation",
394 | "_meta": {
395 | "title": "文本翻译"
396 | }
397 | }
398 | },
399 | "api": "文生图",
400 | "lifo": false
401 | }
--------------------------------------------------------------------------------
/src/draw/data/text2imgapi.json:
--------------------------------------------------------------------------------
1 | {
2 | "18": {
3 | "inputs": {
4 | "text": "paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,"
5 | },
6 | "class_type": "ttN text"
7 | },
8 | "19": {
9 | "inputs": {
10 | "text": "(8k, best quality, masterpiece:1.2)"
11 | },
12 | "class_type": "ttN text"
13 | },
14 | "23": {
15 | "inputs": {
16 | "from_translate": "auto",
17 | "to_translate": "english",
18 | "add_proxies": "disable",
19 | "proxies": "",
20 | "auth_data": "",
21 | "service": "GoogleTranslator",
22 | "text": "",
23 | "Show proxy": "proxy_hide",
24 | "Show authorization": "authorization_hide"
25 | },
26 | "class_type": "DeepTranslatorTextNode"
27 | },
28 | "24": {
29 | "inputs": {
30 | "from_translate": "auto",
31 | "to_translate": "english",
32 | "add_proxies": "disable",
33 | "proxies": "",
34 | "auth_data": "",
35 | "service": "GoogleTranslator",
36 | "text": "aichang,sdmai,fanhua,1girl\\(Eyes\\(Deep amber,crystal clear,long and delicate eyelashes\\),Nose\\(Elevated,with a slightly upturned nose tip\\),Lips\\(Rosy color, with a defined lip line\\),Hairstyle\\(Black hair,smooth and shiny,slightly wavy at the ends\\),Skin\\(Fair,blemish-free,as delicate as porcelain\\),Clothing\\(chinese traditional clothing,Fabric\\(Lightweight silk,smooth and shiny\\),Posture\\(Straight back,walking confidently and gracefully,light and graceful gait\\),cyberpunk,Chinese New Year,dragon dance,(look at viewer,floating hair,outdoor,upper body):1.5\\),\nBackground\\(snow,mountains,sunshine,cloud,grassland,sky,forest,lake\\),\nmasterpiece,best quality,unreal engine 5 rendering,movie light,movie lens,movie special effects,detailed details,HDR,UHD,8K,CG wallpaper",
37 | "Show proxy": "proxy_hide",
38 | "Show authorization": "authorization_hide"
39 | },
40 | "class_type": "DeepTranslatorTextNode"
41 | },
42 | "25": {
43 | "inputs": {
44 | "delimiter": ",",
45 | "clean_whitespace": "true",
46 | "text_a": [
47 | "24",
48 | 0
49 | ],
50 | "text_b": [
51 | "19",
52 | 0
53 | ]
54 | },
55 | "class_type": "Text Concatenate"
56 | },
57 | "26": {
58 | "inputs": {
59 | "delimiter": "",
60 | "clean_whitespace": "true",
61 | "text_a": [
62 | "23",
63 | 0
64 | ],
65 | "text_b": [
66 | "18",
67 | 0
68 | ]
69 | },
70 | "class_type": "Text Concatenate"
71 | },
72 | "32": {
73 | "inputs": {
74 | "add_noise": "enable",
75 | "noise_seed": 830784415360923,
76 | "steps": 5,
77 | "cfg": 1.5,
78 | "sampler_name": "lcm",
79 | "scheduler": "normal",
80 | "start_at_step": 0,
81 | "end_at_step": 10000,
82 | "return_with_leftover_noise": "disable",
83 | "preview_method": "none",
84 | "vae_decode": "true",
85 | "model": [
86 | "47",
87 | 0
88 | ],
89 | "positive": [
90 | "47",
91 | 1
92 | ],
93 | "negative": [
94 | "47",
95 | 2
96 | ],
97 | "latent_image": [
98 | "47",
99 | 3
100 | ],
101 | "optional_vae": [
102 | "47",
103 | 4
104 | ],
105 | "script": [
106 | "44",
107 | 0
108 | ]
109 | },
110 | "class_type": "KSampler Adv. (Efficient)"
111 | },
112 | "44": {
113 | "inputs": {
114 | "upscale_type": "latent",
115 | "hires_ckpt_name": "(use same)",
116 | "latent_upscaler": "nearest-exact",
117 | "pixel_upscaler": "4x-UltraSharp.pth",
118 | "upscale_by": 1.5,
119 | "use_same_seed": true,
120 | "seed": -1,
121 | "hires_steps": 12,
122 | "denoise": 0.3,
123 | "iterations": 1,
124 | "use_controlnet": false,
125 | "control_net_name": "control_sd15_random_color.pth",
126 | "strength": 1,
127 | "preprocessor": "CannyEdgePreprocessor",
128 | "preprocessor_imgs": false
129 | },
130 | "class_type": "HighRes-Fix Script"
131 | },
132 | "45": {
133 | "inputs": {
134 | "input_mode": "simple",
135 | "lora_count": 5,
136 | "lora_name_1": "lcm\\lcm_sd1.5_pytorch_lora_weights.safetensors",
137 | "lora_wt_1": 1,
138 | "model_str_1": 1,
139 | "clip_str_1": 1,
140 | "lora_name_2": "None",
141 | "lora_wt_2": 0.5,
142 | "model_str_2": 1,
143 | "clip_str_2": 1,
144 | "lora_name_3": "None",
145 | "lora_wt_3": 0.5,
146 | "model_str_3": 1,
147 | "clip_str_3": 1,
148 | "lora_name_4": "None",
149 | "lora_wt_4": 1,
150 | "model_str_4": 1,
151 | "clip_str_4": 1,
152 | "lora_name_5": "None",
153 | "lora_wt_5": 1,
154 | "model_str_5": 1,
155 | "clip_str_5": 1,
156 | "lora_name_6": "None",
157 | "lora_wt_6": 1,
158 | "model_str_6": 1,
159 | "clip_str_6": 1,
160 | "lora_name_7": "None",
161 | "lora_wt_7": 1,
162 | "model_str_7": 1,
163 | "clip_str_7": 1,
164 | "lora_name_8": "None",
165 | "lora_wt_8": 1,
166 | "model_str_8": 1,
167 | "clip_str_8": 1,
168 | "lora_name_9": "None",
169 | "lora_wt_9": 1,
170 | "model_str_9": 1,
171 | "clip_str_9": 1,
172 | "lora_name_10": "None",
173 | "lora_wt_10": 1,
174 | "model_str_10": 1,
175 | "clip_str_10": 1,
176 | "lora_name_11": "None",
177 | "lora_wt_11": 1,
178 | "model_str_11": 1,
179 | "clip_str_11": 1,
180 | "lora_name_12": "None",
181 | "lora_wt_12": 1,
182 | "model_str_12": 1,
183 | "clip_str_12": 1,
184 | "lora_name_13": "None",
185 | "lora_wt_13": 1,
186 | "model_str_13": 1,
187 | "clip_str_13": 1,
188 | "lora_name_14": "None",
189 | "lora_wt_14": 1,
190 | "model_str_14": 1,
191 | "clip_str_14": 1,
192 | "lora_name_15": "None",
193 | "lora_wt_15": 1,
194 | "model_str_15": 1,
195 | "clip_str_15": 1,
196 | "lora_name_16": "None",
197 | "lora_wt_16": 1,
198 | "model_str_16": 1,
199 | "clip_str_16": 1,
200 | "lora_name_17": "None",
201 | "lora_wt_17": 1,
202 | "model_str_17": 1,
203 | "clip_str_17": 1,
204 | "lora_name_18": "None",
205 | "lora_wt_18": 1,
206 | "model_str_18": 1,
207 | "clip_str_18": 1,
208 | "lora_name_19": "None",
209 | "lora_wt_19": 1,
210 | "model_str_19": 1,
211 | "clip_str_19": 1,
212 | "lora_name_20": "None",
213 | "lora_wt_20": 1,
214 | "model_str_20": 1,
215 | "clip_str_20": 1,
216 | "lora_name_21": "None",
217 | "lora_wt_21": 1,
218 | "model_str_21": 1,
219 | "clip_str_21": 1,
220 | "lora_name_22": "None",
221 | "lora_wt_22": 1,
222 | "model_str_22": 1,
223 | "clip_str_22": 1,
224 | "lora_name_23": "None",
225 | "lora_wt_23": 1,
226 | "model_str_23": 1,
227 | "clip_str_23": 1,
228 | "lora_name_24": "None",
229 | "lora_wt_24": 1,
230 | "model_str_24": 1,
231 | "clip_str_24": 1,
232 | "lora_name_25": "None",
233 | "lora_wt_25": 1,
234 | "model_str_25": 1,
235 | "clip_str_25": 1,
236 | "lora_name_26": "None",
237 | "lora_wt_26": 1,
238 | "model_str_26": 1,
239 | "clip_str_26": 1,
240 | "lora_name_27": "None",
241 | "lora_wt_27": 1,
242 | "model_str_27": 1,
243 | "clip_str_27": 1,
244 | "lora_name_28": "None",
245 | "lora_wt_28": 1,
246 | "model_str_28": 1,
247 | "clip_str_28": 1,
248 | "lora_name_29": "None",
249 | "lora_wt_29": 1,
250 | "model_str_29": 1,
251 | "clip_str_29": 1,
252 | "lora_name_30": "None",
253 | "lora_wt_30": 1,
254 | "model_str_30": 1,
255 | "clip_str_30": 1,
256 | "lora_name_31": "None",
257 | "lora_wt_31": 1,
258 | "model_str_31": 1,
259 | "clip_str_31": 1,
260 | "lora_name_32": "None",
261 | "lora_wt_32": 1,
262 | "model_str_32": 1,
263 | "clip_str_32": 1,
264 | "lora_name_33": "None",
265 | "lora_wt_33": 1,
266 | "model_str_33": 1,
267 | "clip_str_33": 1,
268 | "lora_name_34": "None",
269 | "lora_wt_34": 1,
270 | "model_str_34": 1,
271 | "clip_str_34": 1,
272 | "lora_name_35": "None",
273 | "lora_wt_35": 1,
274 | "model_str_35": 1,
275 | "clip_str_35": 1,
276 | "lora_name_36": "None",
277 | "lora_wt_36": 1,
278 | "model_str_36": 1,
279 | "clip_str_36": 1,
280 | "lora_name_37": "None",
281 | "lora_wt_37": 1,
282 | "model_str_37": 1,
283 | "clip_str_37": 1,
284 | "lora_name_38": "None",
285 | "lora_wt_38": 1,
286 | "model_str_38": 1,
287 | "clip_str_38": 1,
288 | "lora_name_39": "None",
289 | "lora_wt_39": 1,
290 | "model_str_39": 1,
291 | "clip_str_39": 1,
292 | "lora_name_40": "None",
293 | "lora_wt_40": 1,
294 | "model_str_40": 1,
295 | "clip_str_40": 1,
296 | "lora_name_41": "None",
297 | "lora_wt_41": 1,
298 | "model_str_41": 1,
299 | "clip_str_41": 1,
300 | "lora_name_42": "None",
301 | "lora_wt_42": 1,
302 | "model_str_42": 1,
303 | "clip_str_42": 1,
304 | "lora_name_43": "None",
305 | "lora_wt_43": 1,
306 | "model_str_43": 1,
307 | "clip_str_43": 1,
308 | "lora_name_44": "None",
309 | "lora_wt_44": 1,
310 | "model_str_44": 1,
311 | "clip_str_44": 1,
312 | "lora_name_45": "None",
313 | "lora_wt_45": 1,
314 | "model_str_45": 1,
315 | "clip_str_45": 1,
316 | "lora_name_46": "None",
317 | "lora_wt_46": 1,
318 | "model_str_46": 1,
319 | "clip_str_46": 1,
320 | "lora_name_47": "None",
321 | "lora_wt_47": 1,
322 | "model_str_47": 1,
323 | "clip_str_47": 1,
324 | "lora_name_48": "None",
325 | "lora_wt_48": 1,
326 | "model_str_48": 1,
327 | "clip_str_48": 1,
328 | "lora_name_49": "None",
329 | "lora_wt_49": 1,
330 | "model_str_49": 1,
331 | "clip_str_49": 1
332 | },
333 | "class_type": "LoRA Stacker"
334 | },
335 | "47": {
336 | "inputs": {
337 | "ckpt_name": "极氪写实MAX-极氪白系列模型_V6.safetensors",
338 | "vae_name": "Baked VAE",
339 | "clip_skip": -2,
340 | "lora_name": "None",
341 | "lora_model_strength": 1,
342 | "lora_clip_strength": 1,
343 | "positive": [
344 | "25",
345 | 0
346 | ],
347 | "negative": [
348 | "26",
349 | 0
350 | ],
351 | "token_normalization": "none",
352 | "weight_interpretation": "A1111",
353 | "empty_latent_width": 512,
354 | "empty_latent_height": 768,
355 | "batch_size": 1,
356 | "lora_stack": [
357 | "45",
358 | 0
359 | ]
360 | },
361 | "class_type": "Efficient Loader"
362 | },
363 | "51": {
364 | "inputs": {
365 | "output_path": "[time(%Y-%m-%d)]",
366 | "filename_prefix": "text2img_output_final_",
367 | "filename_delimiter": "_",
368 | "filename_number_padding": 4,
369 | "filename_number_start": "false",
370 | "extension": "png",
371 | "quality": 100,
372 | "lossless_webp": "false",
373 | "overwrite_mode": "false",
374 | "show_history": "false",
375 | "show_history_by_prefix": "true",
376 | "embed_workflow": "true",
377 | "show_previews": "true",
378 | "images": [
379 | "32",
380 | 5
381 | ]
382 | },
383 | "class_type": "Image Save"
384 | }
385 | }
386 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_faceswap.ts:
--------------------------------------------------------------------------------
1 | export const FaceSwap = {
2 | '63': {
3 | inputs: {
4 | enabled: true,
5 | swap_model: 'inswapper_128.onnx',
6 | facedetection: 'retinaface_resnet50',
7 | face_restore_model: 'GFPGANv1.4.pth',
8 | face_restore_visibility: 1,
9 | codeformer_weight: 0.5,
10 | detect_gender_input: 'no',
11 | detect_gender_source: 'no',
12 | input_faces_index: '0',
13 | source_faces_index: '0',
14 | console_log_level: 1,
15 | input_image: ['91', 0],
16 | source_image: ['92', 0],
17 | },
18 | class_type: 'ReActorFaceSwap',
19 | _meta: {
20 | title: 'ReActor换脸',
21 | },
22 | },
23 | '91': {
24 | inputs: {
25 | image_path:
26 | 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/t2i_1.jpg',
27 | RGBA: 'false',
28 | filename_text_extension: 'true',
29 | },
30 | class_type: 'Image Load',
31 | _meta: {
32 | title: '图像加载',
33 | },
34 | },
35 | '92': {
36 | inputs: {
37 | image_path:
38 | 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/R-C.jpg',
39 | RGBA: 'false',
40 | filename_text_extension: 'true',
41 | },
42 | class_type: 'Image Load',
43 | _meta: {
44 | title: '图像加载',
45 | },
46 | },
47 | '93': {
48 | inputs: {
49 | output_path: '[time(%Y-%m-%d)]',
50 | filename_prefix: 'aistudio_output_final_',
51 | filename_delimiter: '_',
52 | filename_number_padding: 4,
53 | filename_number_start: 'false',
54 | extension: 'png',
55 | quality: 100,
56 | lossless_webp: 'false',
57 | overwrite_mode: 'false',
58 | show_history: 'false',
59 | show_history_by_prefix: 'true',
60 | embed_workflow: 'true',
61 | show_previews: 'true',
62 | },
63 | class_type: 'Image Save',
64 | _meta: {
65 | title: '图像保存',
66 | },
67 | },
68 | '94': {
69 | inputs: {
70 | output_path: '[time(%Y-%m-%d)]',
71 | filename_prefix: 'aistudio_output_2_',
72 | filename_delimiter: '_',
73 | filename_number_padding: 4,
74 | filename_number_start: 'false',
75 | extension: 'png',
76 | quality: 100,
77 | lossless_webp: 'false',
78 | overwrite_mode: 'false',
79 | show_history: 'false',
80 | show_history_by_prefix: 'true',
81 | embed_workflow: 'true',
82 | show_previews: 'true',
83 | },
84 | class_type: 'Image Save',
85 | _meta: {
86 | title: '图像保存',
87 | },
88 | },
89 | '95': {
90 | inputs: {
91 | output_path: '[time(%Y-%m-%d)]',
92 | filename_prefix: 'faceswap_output_final_',
93 | filename_delimiter: '_',
94 | filename_number_padding: 4,
95 | filename_number_start: 'false',
96 | extension: 'png',
97 | quality: 100,
98 | lossless_webp: 'false',
99 | overwrite_mode: 'false',
100 | show_history: 'false',
101 | show_history_by_prefix: 'true',
102 | embed_workflow: 'true',
103 | show_previews: 'true',
104 | images: ['63', 0],
105 | },
106 | class_type: 'Image Save',
107 | _meta: {
108 | title: '图像保存',
109 | },
110 | },
111 | };
112 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_hdfix_4.ts:
--------------------------------------------------------------------------------
1 | export const workflowApiHdfix4 = {
2 | '5': {
3 | inputs: {
4 | output_path: '[time(%Y-%m-%d)]',
5 | filename_prefix: ['16', 0],
6 | filename_delimiter: '_',
7 | filename_number_padding: 4,
8 | filename_number_start: 'false',
9 | extension: 'png',
10 | quality: 100,
11 | lossless_webp: 'false',
12 | overwrite_mode: 'false',
13 | show_history: 'false',
14 | show_history_by_prefix: 'true',
15 | embed_workflow: 'true',
16 | show_previews: 'true',
17 | images: ['11', 0],
18 | },
19 | class_type: 'Image Save',
20 | _meta: {
21 | title: '图像保存',
22 | },
23 | },
24 | '11': {
25 | inputs: {
26 | upscale_model: ['12', 0],
27 | image: ['15', 0],
28 | },
29 | class_type: 'ImageUpscaleWithModel',
30 | _meta: {
31 | title: '图像通过模型放大',
32 | },
33 | },
34 | '12': {
35 | inputs: {
36 | model_name: '4x-UltraSharp.pth',
37 | },
38 | class_type: 'UpscaleModelLoader',
39 | _meta: {
40 | title: '放大模型加载器',
41 | },
42 | },
43 | '15': {
44 | inputs: {
45 | image_path: 'E:\\ComfyUI-aki-v1.2\\output\\ComfyUI_00001_.png',
46 | RGBA: 'false',
47 | filename_text_extension: 'false',
48 | },
49 | class_type: 'Image Load',
50 | _meta: {
51 | title: '图像加载',
52 | },
53 | },
54 | '16': {
55 | inputs: {
56 | delimiter: '_',
57 | clean_whitespace: 'true',
58 | text_a: ['15', 2],
59 | text_b: ['18', 0],
60 | },
61 | class_type: 'Text Concatenate',
62 | _meta: {
63 | title: '文本连锁',
64 | },
65 | },
66 | '18': {
67 | inputs: {
68 | Text: 'hdifx_output_final_',
69 | },
70 | class_type: 'Text box',
71 | _meta: {
72 | title: '文本框',
73 | },
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_image2img.ts:
--------------------------------------------------------------------------------
1 | export const image2img = {
2 | '18': {
3 | inputs: {
4 | text: 'paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,',
5 | },
6 | class_type: 'ttN text',
7 | _meta: {
8 | title: 'TTN文本',
9 | },
10 | },
11 | '19': {
12 | inputs: {
13 | text: '(8k, best quality, masterpiece:1.2)',
14 | },
15 | class_type: 'ttN text',
16 | _meta: {
17 | title: 'TTN文本',
18 | },
19 | },
20 | '25': {
21 | inputs: {
22 | delimiter: ',',
23 | clean_whitespace: 'true',
24 | text_a: ['98', 0],
25 | text_b: ['19', 0],
26 | },
27 | class_type: 'Text Concatenate',
28 | _meta: {
29 | title: '文本连锁',
30 | },
31 | },
32 | '26': {
33 | inputs: {
34 | delimiter: '',
35 | clean_whitespace: 'true',
36 | text_b: ['18', 0],
37 | },
38 | class_type: 'Text Concatenate',
39 | _meta: {
40 | title: '文本连锁',
41 | },
42 | },
43 | '51': {
44 | inputs: {
45 | output_path: '[time(%Y-%m-%d)]',
46 | filename_prefix: 'img2img_output_final_',
47 | filename_delimiter: '_',
48 | filename_number_padding: 4,
49 | filename_number_start: 'false',
50 | extension: 'png',
51 | quality: 100,
52 | lossless_webp: 'false',
53 | overwrite_mode: 'false',
54 | show_history: 'false',
55 | show_history_by_prefix: 'true',
56 | embed_workflow: 'true',
57 | show_previews: 'true',
58 | images: ['85', 5],
59 | },
60 | class_type: 'Image Save',
61 | _meta: {
62 | title: '图像保存',
63 | },
64 | },
65 | '70': {
66 | inputs: {
67 | image_path: './ComfyUI/input/example.png',
68 | RGBA: 'false',
69 | filename_text_extension: 'false',
70 | },
71 | class_type: 'Image Load',
72 | _meta: {
73 | title: '图像加载',
74 | },
75 | },
76 | '71': {
77 | inputs: {
78 | image: ['70', 0],
79 | },
80 | class_type: 'Image Size to Number',
81 | _meta: {
82 | title: '图像尺寸到数字',
83 | },
84 | },
85 | '72': {
86 | inputs: {
87 | number_type: 'integer',
88 | number: 512,
89 | },
90 | class_type: 'Constant Number',
91 | _meta: {
92 | title: '常数',
93 | },
94 | },
95 | '73': {
96 | inputs: {
97 | operation: 'division',
98 | number_a: ['72', 0],
99 | number_b: ['71', 0],
100 | },
101 | class_type: 'Number Operation',
102 | _meta: {
103 | title: '数字运算',
104 | },
105 | },
106 | '74': {
107 | inputs: {
108 | upscale_method: 'nearest-exact',
109 | scale_by: ['73', 1],
110 | image: ['70', 0],
111 | },
112 | class_type: 'ImageScaleBy',
113 | _meta: {
114 | title: '图像按系数缩放',
115 | },
116 | },
117 | '85': {
118 | inputs: {
119 | seed: 116235324919319,
120 | steps: 20,
121 | cfg: 7,
122 | sampler_name: 'dpmpp_2m',
123 | scheduler: 'karras',
124 | denoise: 1,
125 | preview_method: 'none',
126 | vae_decode: 'true',
127 | model: ['101', 0],
128 | positive: ['101', 1],
129 | negative: ['101', 2],
130 | latent_image: ['86', 0],
131 | optional_vae: ['101', 4],
132 | },
133 | class_type: 'KSampler (Efficient)',
134 | _meta: {
135 | title: 'K采样器(效率)',
136 | },
137 | },
138 | '86': {
139 | inputs: {
140 | pixels: ['74', 0],
141 | vae: ['101', 4],
142 | },
143 | class_type: 'VAEEncode',
144 | _meta: {
145 | title: 'VAE编码',
146 | },
147 | },
148 | '89': {
149 | inputs: {
150 | image: 'ComfyUI_00001_.png',
151 | upload: 'image',
152 | },
153 | class_type: 'LoadImage',
154 | _meta: {
155 | title: '加载图像',
156 | },
157 | },
158 | '98': {
159 | inputs: {
160 | model: 'wd-v1-4-moat-tagger-v2',
161 | threshold: 0.35,
162 | character_threshold: 0.85,
163 | replace_underscore: false,
164 | trailing_comma: false,
165 | exclude_tags: '',
166 | tags: 'flower, outdoors, sky, cloud, water, no_humans, night, star_\\(sky\\), night_sky, scenery, starry_sky, reflection, sunset, mountain, horizon, landscape, mountainous_horizon',
167 | image: ['70', 0],
168 | },
169 | class_type: 'WD14Tagger|pysssss',
170 | _meta: {
171 | title: 'WD14反推提示词',
172 | },
173 | },
174 | '101': {
175 | inputs: {
176 | ckpt_name: 'anything-v5-PrtRE.safetensors',
177 | vae_name: 'Baked VAE',
178 | clip_skip: -1,
179 | lora_name: 'None',
180 | lora_model_strength: 1,
181 | lora_clip_strength: 1,
182 | positive: ['25', 0],
183 | negative: ['26', 0],
184 | token_normalization: 'none',
185 | weight_interpretation: 'A1111',
186 | empty_latent_width: 512,
187 | empty_latent_height: 512,
188 | batch_size: 1,
189 | },
190 | class_type: 'Efficient Loader',
191 | _meta: {
192 | title: '效率加载器',
193 | },
194 | },
195 | };
196 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_img2video.json:
--------------------------------------------------------------------------------
1 | {
2 | "8": {
3 | "inputs": {
4 | "width": [
5 | "36",
6 | 2
7 | ],
8 | "height": [
9 | "36",
10 | 1
11 | ],
12 | "video_frames": 14,
13 | "motion_bucket_id": 127,
14 | "fps": 6,
15 | "augmentation_level": 0,
16 | "clip_vision": [
17 | "35",
18 | 3
19 | ],
20 | "init_image": [
21 | "37",
22 | 0
23 | ],
24 | "vae": [
25 | "35",
26 | 2
27 | ]
28 | },
29 | "class_type": "SVD_img2vid_Conditioning",
30 | "_meta": {
31 | "title": "SVD_图像到视频_条件"
32 | }
33 | },
34 | "10": {
35 | "inputs": {
36 | "image": "u=1090338134,2696420997&fm=253&fmt=auto&app=138&f=JPEG (3).webp",
37 | "upload": "image"
38 | },
39 | "class_type": "LoadImage",
40 | "_meta": {
41 | "title": "加载图像"
42 | }
43 | },
44 | "18": {
45 | "inputs": {
46 | "min_cfg": 1,
47 | "model": [
48 | "35",
49 | 0
50 | ]
51 | },
52 | "class_type": "VideoLinearCFGGuidance",
53 | "_meta": {
54 | "title": "线性CFG引导"
55 | }
56 | },
57 | "19": {
58 | "inputs": {
59 | "seed": 526114517786088,
60 | "steps": 20,
61 | "cfg": 3,
62 | "sampler_name": "dpmpp_2m",
63 | "scheduler": "karras",
64 | "denoise": 1,
65 | "model": [
66 | "18",
67 | 0
68 | ],
69 | "positive": [
70 | "8",
71 | 0
72 | ],
73 | "negative": [
74 | "8",
75 | 1
76 | ],
77 | "latent_image": [
78 | "8",
79 | 2
80 | ]
81 | },
82 | "class_type": "KSampler",
83 | "_meta": {
84 | "title": "K采样器"
85 | }
86 | },
87 | "20": {
88 | "inputs": {
89 | "samples": [
90 | "19",
91 | 0
92 | ],
93 | "vae": [
94 | "35",
95 | 2
96 | ]
97 | },
98 | "class_type": "VAEDecode",
99 | "_meta": {
100 | "title": "VAE解码"
101 | }
102 | },
103 | "23": {
104 | "inputs": {
105 | "frame_rate": 8,
106 | "loop_count": 0,
107 | "filename_prefix": "img2video_output_final_",
108 | "format": "video/h264-mp4",
109 | "pix_fmt": "yuv420p",
110 | "crf": 19,
111 | "save_metadata": true,
112 | "pingpong": false,
113 | "save_output": true,
114 | "images": [
115 | "24",
116 | 0
117 | ]
118 | },
119 | "class_type": "VHS_VideoCombine",
120 | "_meta": {
121 | "title": "合并为视频"
122 | }
123 | },
124 | "24": {
125 | "inputs": {
126 | "ckpt_name": "rife47.pth",
127 | "clear_cache_after_n_frames": 10,
128 | "multiplier": 2,
129 | "fast_mode": true,
130 | "ensemble": true,
131 | "scale_factor": 1,
132 | "frames": [
133 | "20",
134 | 0
135 | ]
136 | },
137 | "class_type": "RIFE VFI",
138 | "_meta": {
139 | "title": "RIFE VFI"
140 | }
141 | },
142 | "35": {
143 | "inputs": {
144 | "ckpt_name": "svd-fp16.safetensors"
145 | },
146 | "class_type": "unCLIPCheckpointLoader",
147 | "_meta": {
148 | "title": "unCLIPCheckpoint加载器"
149 | }
150 | },
151 | "36": {
152 | "inputs": {
153 | "value": [
154 | "37",
155 | 0
156 | ]
157 | },
158 | "class_type": "ImpactImageInfo",
159 | "_meta": {
160 | "title": "图像信息"
161 | }
162 | },
163 | "37": {
164 | "inputs": {
165 | "image_path": "./ComfyUI/input/example.png",
166 | "RGBA": "false",
167 | "filename_text_extension": "true"
168 | },
169 | "class_type": "Image Load",
170 | "_meta": {
171 | "title": "图像加载"
172 | }
173 | }
174 | }
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_img2video.ts:
--------------------------------------------------------------------------------
1 | export const img2video = {
2 | '8': {
3 | inputs: {
4 | width: ['36', 2],
5 | height: ['36', 1],
6 | video_frames: 14,
7 | motion_bucket_id: 127,
8 | fps: 6,
9 | augmentation_level: 0,
10 | clip_vision: ['35', 3],
11 | init_image: ['37', 0],
12 | vae: ['35', 2],
13 | },
14 | class_type: 'SVD_img2vid_Conditioning',
15 | _meta: {
16 | title: 'SVD_图像到视频_条件',
17 | },
18 | },
19 | '10': {
20 | inputs: {
21 | image: 'u=1090338134,2696420997&fm=253&fmt=auto&app=138&f=JPEG (3).webp',
22 | upload: 'image',
23 | },
24 | class_type: 'LoadImage',
25 | _meta: {
26 | title: '加载图像',
27 | },
28 | },
29 | '18': {
30 | inputs: {
31 | min_cfg: 1,
32 | model: ['35', 0],
33 | },
34 | class_type: 'VideoLinearCFGGuidance',
35 | _meta: {
36 | title: '线性CFG引导',
37 | },
38 | },
39 | '19': {
40 | inputs: {
41 | seed: 526114517786088,
42 | steps: 20,
43 | cfg: 3,
44 | sampler_name: 'dpmpp_2m',
45 | scheduler: 'karras',
46 | denoise: 1,
47 | model: ['18', 0],
48 | positive: ['8', 0],
49 | negative: ['8', 1],
50 | latent_image: ['8', 2],
51 | },
52 | class_type: 'KSampler',
53 | _meta: {
54 | title: 'K采样器',
55 | },
56 | },
57 | '20': {
58 | inputs: {
59 | samples: ['19', 0],
60 | vae: ['35', 2],
61 | },
62 | class_type: 'VAEDecode',
63 | _meta: {
64 | title: 'VAE解码',
65 | },
66 | },
67 | '23': {
68 | inputs: {
69 | frame_rate: 8,
70 | loop_count: 0,
71 | filename_prefix: 'img2video_output_final_',
72 | format: 'video/h264-mp4',
73 | pix_fmt: 'yuv420p',
74 | crf: 19,
75 | save_metadata: true,
76 | pingpong: false,
77 | save_output: true,
78 | images: ['24', 0],
79 | },
80 | class_type: 'VHS_VideoCombine',
81 | _meta: {
82 | title: '合并为视频',
83 | },
84 | },
85 | '24': {
86 | inputs: {
87 | ckpt_name: 'rife47.pth',
88 | clear_cache_after_n_frames: 10,
89 | multiplier: 2,
90 | fast_mode: true,
91 | ensemble: true,
92 | scale_factor: 1,
93 | frames: ['20', 0],
94 | },
95 | class_type: 'RIFE VFI',
96 | _meta: {
97 | title: 'RIFE VFI',
98 | },
99 | },
100 | '35': {
101 | inputs: {
102 | ckpt_name: 'svd-fp16.safetensors',
103 | },
104 | class_type: 'unCLIPCheckpointLoader',
105 | _meta: {
106 | title: 'unCLIPCheckpoint加载器',
107 | },
108 | },
109 | '36': {
110 | inputs: {
111 | value: ['37', 0],
112 | },
113 | class_type: 'ImpactImageInfo',
114 | _meta: {
115 | title: '图像信息',
116 | },
117 | },
118 | '37': {
119 | inputs: {
120 | image_path: './ComfyUI/input/example.png',
121 | RGBA: 'false',
122 | filename_text_extension: 'true',
123 | },
124 | class_type: 'Image Load',
125 | _meta: {
126 | title: '图像加载',
127 | },
128 | },
129 | };
130 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_inpainting.json:
--------------------------------------------------------------------------------
1 | {
2 | "18": {
3 | "inputs": {
4 | "text": "paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,"
5 | },
6 | "class_type": "ttN text",
7 | "_meta": {
8 | "title": "TTN文本"
9 | }
10 | },
11 | "19": {
12 | "inputs": {
13 | "text": "(8k, best quality, masterpiece:1.2)"
14 | },
15 | "class_type": "ttN text",
16 | "_meta": {
17 | "title": "TTN文本"
18 | }
19 | },
20 | "25": {
21 | "inputs": {
22 | "delimiter": ",",
23 | "clean_whitespace": "true",
24 | "text_a": [
25 | "95",
26 | 0
27 | ],
28 | "text_b": [
29 | "19",
30 | 0
31 | ]
32 | },
33 | "class_type": "Text Concatenate",
34 | "_meta": {
35 | "title": "文本连锁"
36 | }
37 | },
38 | "26": {
39 | "inputs": {
40 | "delimiter": "",
41 | "clean_whitespace": "true",
42 | "text_a": [
43 | "94",
44 | 0
45 | ],
46 | "text_b": [
47 | "18",
48 | 0
49 | ]
50 | },
51 | "class_type": "Text Concatenate",
52 | "_meta": {
53 | "title": "文本连锁"
54 | }
55 | },
56 | "51": {
57 | "inputs": {
58 | "output_path": "[time(%Y-%m-%d)]",
59 | "filename_prefix": [
60 | "93",
61 | 0
62 | ],
63 | "filename_delimiter": "_",
64 | "filename_number_padding": 4,
65 | "filename_number_start": "false",
66 | "extension": "png",
67 | "quality": 100,
68 | "lossless_webp": "false",
69 | "overwrite_mode": "false",
70 | "show_history": "false",
71 | "show_history_by_prefix": "true",
72 | "embed_workflow": "true",
73 | "show_previews": "true",
74 | "images": [
75 | "85",
76 | 5
77 | ]
78 | },
79 | "class_type": "Image Save",
80 | "_meta": {
81 | "title": "图像保存"
82 | }
83 | },
84 | "70": {
85 | "inputs": {
86 | "image_path": "C:\\Users\\wangb\\Downloads\\image1 - 2024-03-20T180727.831.png",
87 | "RGBA": "false",
88 | "filename_text_extension": "false"
89 | },
90 | "class_type": "Image Load",
91 | "_meta": {
92 | "title": "图像加载"
93 | }
94 | },
95 | "85": {
96 | "inputs": {
97 | "seed": 205149528245215,
98 | "steps": 20,
99 | "cfg": 7,
100 | "sampler_name": "dpmpp_2m",
101 | "scheduler": "karras",
102 | "denoise": 0.5,
103 | "preview_method": "none",
104 | "vae_decode": "true",
105 | "model": [
106 | "98",
107 | 0
108 | ],
109 | "positive": [
110 | "98",
111 | 1
112 | ],
113 | "negative": [
114 | "98",
115 | 2
116 | ],
117 | "latent_image": [
118 | "88",
119 | 0
120 | ],
121 | "optional_vae": [
122 | "98",
123 | 4
124 | ]
125 | },
126 | "class_type": "KSampler (Efficient)",
127 | "_meta": {
128 | "title": "K采样器(效率)"
129 | }
130 | },
131 | "86": {
132 | "inputs": {
133 | "pixels": [
134 | "70",
135 | 0
136 | ],
137 | "vae": [
138 | "98",
139 | 4
140 | ]
141 | },
142 | "class_type": "VAEEncode",
143 | "_meta": {
144 | "title": "VAE编码"
145 | }
146 | },
147 | "87": {
148 | "inputs": {
149 | "image_path": "C:\\Users\\wangb\\Downloads\\mask (21).png",
150 | "RGBA": "false",
151 | "filename_text_extension": "false"
152 | },
153 | "class_type": "Image Load",
154 | "_meta": {
155 | "title": "图像加载"
156 | }
157 | },
158 | "88": {
159 | "inputs": {
160 | "samples": [
161 | "86",
162 | 0
163 | ],
164 | "mask": [
165 | "87",
166 | 1
167 | ]
168 | },
169 | "class_type": "SetLatentNoiseMask",
170 | "_meta": {
171 | "title": "设置Latent噪波遮罩"
172 | }
173 | },
174 | "93": {
175 | "inputs": {
176 | "delimiter": "_",
177 | "clean_whitespace": "true",
178 | "text_a": [
179 | "87",
180 | 2
181 | ],
182 | "text_b": [
183 | "100",
184 | 0
185 | ]
186 | },
187 | "class_type": "Text Concatenate",
188 | "_meta": {
189 | "title": "文本连锁"
190 | }
191 | },
192 | "94": {
193 | "inputs": {
194 | "text": "",
195 | "platform": "alibaba",
196 | "source": "auto",
197 | "target": "en"
198 | },
199 | "class_type": "ZFTextTranslation",
200 | "_meta": {
201 | "title": "文本翻译"
202 | }
203 | },
204 | "95": {
205 | "inputs": {
206 | "text": "",
207 | "platform": "alibaba",
208 | "source": "auto",
209 | "target": "en"
210 | },
211 | "class_type": "ZFTextTranslation",
212 | "_meta": {
213 | "title": "文本翻译"
214 | }
215 | },
216 | "98": {
217 | "inputs": {
218 | "ckpt_name": "majicmixRealistic_v7.safetensors",
219 | "vae_name": "Baked VAE",
220 | "clip_skip": -2,
221 | "lora_name": "None",
222 | "lora_model_strength": 1,
223 | "lora_clip_strength": 1,
224 | "positive": [
225 | "25",
226 | 0
227 | ],
228 | "negative": [
229 | "26",
230 | 0
231 | ],
232 | "token_normalization": "none",
233 | "weight_interpretation": "A1111",
234 | "empty_latent_width": 512,
235 | "empty_latent_height": 512,
236 | "batch_size": 1
237 | },
238 | "class_type": "Efficient Loader",
239 | "_meta": {
240 | "title": "效率加载器"
241 | }
242 | },
243 | "100": {
244 | "inputs": {
245 | "text": "inpaiting_output_final_"
246 | },
247 | "class_type": "ttN text",
248 | "_meta": {
249 | "title": "TTN文本"
250 | }
251 | }
252 | }
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_inpainting.ts:
--------------------------------------------------------------------------------
1 | export const inpainting = {
2 | '18': {
3 | inputs: {
4 | text: 'paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,',
5 | },
6 | class_type: 'ttN text',
7 | _meta: {
8 | title: 'TTN文本',
9 | },
10 | },
11 | '19': {
12 | inputs: {
13 | text: '(8k, best quality, masterpiece:1.2)',
14 | },
15 | class_type: 'ttN text',
16 | _meta: {
17 | title: 'TTN文本',
18 | },
19 | },
20 | '25': {
21 | inputs: {
22 | delimiter: ',',
23 | clean_whitespace: 'true',
24 | text_a: ['95', 0],
25 | text_b: ['19', 0],
26 | },
27 | class_type: 'Text Concatenate',
28 | _meta: {
29 | title: '文本连锁',
30 | },
31 | },
32 | '26': {
33 | inputs: {
34 | delimiter: '',
35 | clean_whitespace: 'true',
36 | text_a: ['94', 0],
37 | text_b: ['18', 0],
38 | },
39 | class_type: 'Text Concatenate',
40 | _meta: {
41 | title: '文本连锁',
42 | },
43 | },
44 | '51': {
45 | inputs: {
46 | output_path: '[time(%Y-%m-%d)]',
47 | filename_prefix: ['93', 0],
48 | filename_delimiter: '_',
49 | filename_number_padding: 4,
50 | filename_number_start: 'false',
51 | extension: 'png',
52 | quality: 100,
53 | lossless_webp: 'false',
54 | overwrite_mode: 'false',
55 | show_history: 'false',
56 | show_history_by_prefix: 'true',
57 | embed_workflow: 'true',
58 | show_previews: 'true',
59 | images: ['85', 5],
60 | },
61 | class_type: 'Image Save',
62 | _meta: {
63 | title: '图像保存',
64 | },
65 | },
66 | '70': {
67 | inputs: {
68 | image_path:
69 | 'C:\\Users\\wangb\\Downloads\\image1 - 2024-03-20T180727.831.png',
70 | RGBA: 'false',
71 | filename_text_extension: 'false',
72 | },
73 | class_type: 'Image Load',
74 | _meta: {
75 | title: '图像加载',
76 | },
77 | },
78 | '85': {
79 | inputs: {
80 | seed: 205149528245215,
81 | steps: 20,
82 | cfg: 7,
83 | sampler_name: 'dpmpp_2m',
84 | scheduler: 'karras',
85 | denoise: 0.5,
86 | preview_method: 'none',
87 | vae_decode: 'true',
88 | model: ['98', 0],
89 | positive: ['98', 1],
90 | negative: ['98', 2],
91 | latent_image: ['88', 0],
92 | optional_vae: ['98', 4],
93 | },
94 | class_type: 'KSampler (Efficient)',
95 | _meta: {
96 | title: 'K采样器(效率)',
97 | },
98 | },
99 | '86': {
100 | inputs: {
101 | pixels: ['70', 0],
102 | vae: ['98', 4],
103 | },
104 | class_type: 'VAEEncode',
105 | _meta: {
106 | title: 'VAE编码',
107 | },
108 | },
109 | '87': {
110 | inputs: {
111 | image_path: 'C:\\Users\\wangb\\Downloads\\mask (21).png',
112 | RGBA: 'false',
113 | filename_text_extension: 'false',
114 | },
115 | class_type: 'Image Load',
116 | _meta: {
117 | title: '图像加载',
118 | },
119 | },
120 | '88': {
121 | inputs: {
122 | samples: ['86', 0],
123 | mask: ['87', 1],
124 | },
125 | class_type: 'SetLatentNoiseMask',
126 | _meta: {
127 | title: '设置Latent噪波遮罩',
128 | },
129 | },
130 | '93': {
131 | inputs: {
132 | delimiter: '_',
133 | clean_whitespace: 'true',
134 | text_a: ['87', 2],
135 | text_b: ['100', 0],
136 | },
137 | class_type: 'Text Concatenate',
138 | _meta: {
139 | title: '文本连锁',
140 | },
141 | },
142 | '94': {
143 | inputs: {
144 | text: '',
145 | platform: 'alibaba',
146 | source: 'auto',
147 | target: 'en',
148 | },
149 | class_type: 'ZFTextTranslation',
150 | _meta: {
151 | title: '文本翻译',
152 | },
153 | },
154 | '95': {
155 | inputs: {
156 | text: '',
157 | platform: 'alibaba',
158 | source: 'auto',
159 | target: 'en',
160 | },
161 | class_type: 'ZFTextTranslation',
162 | _meta: {
163 | title: '文本翻译',
164 | },
165 | },
166 | '98': {
167 | inputs: {
168 | ckpt_name: 'majicmixRealistic_v7.safetensors',
169 | vae_name: 'Baked VAE',
170 | clip_skip: -2,
171 | lora_name: 'None',
172 | lora_model_strength: 1,
173 | lora_clip_strength: 1,
174 | positive: ['25', 0],
175 | negative: ['26', 0],
176 | token_normalization: 'none',
177 | weight_interpretation: 'A1111',
178 | empty_latent_width: 512,
179 | empty_latent_height: 512,
180 | batch_size: 1,
181 | },
182 | class_type: 'Efficient Loader',
183 | _meta: {
184 | title: '效率加载器',
185 | },
186 | },
187 | '100': {
188 | inputs: {
189 | text: 'inpaiting_output_final_',
190 | },
191 | class_type: 'ttN text',
192 | _meta: {
193 | title: 'TTN文本',
194 | },
195 | },
196 | };
197 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_matting.json:
--------------------------------------------------------------------------------
1 | {
2 | "5": {
3 | "inputs": {
4 | "prompt": [
5 | "67",
6 | 0
7 | ],
8 | "threshold": 0.3,
9 | "sam_model": [
10 | "6",
11 | 0
12 | ],
13 | "grounding_dino_model": [
14 | "7",
15 | 0
16 | ],
17 | "image": [
18 | "62",
19 | 0
20 | ]
21 | },
22 | "class_type": "GroundingDinoSAMSegment (segment anything)",
23 | "_meta": {
24 | "title": "G-DinoSAM语义分割"
25 | }
26 | },
27 | "6": {
28 | "inputs": {
29 | "model_name": "sam_vit_h_4b8939.pth",
30 | "device_mode": "Prefer GPU"
31 | },
32 | "class_type": "SAMLoader",
33 | "_meta": {
34 | "title": "SAM加载器"
35 | }
36 | },
37 | "7": {
38 | "inputs": {
39 | "model_name": "GroundingDINO_SwinB (938MB)"
40 | },
41 | "class_type": "GroundingDinoModelLoader (segment anything)",
42 | "_meta": {
43 | "title": "G-Dino模型加载器"
44 | }
45 | },
46 | "44": {
47 | "inputs": {
48 | "mask": [
49 | "5",
50 | 1
51 | ]
52 | },
53 | "class_type": "InvertMask",
54 | "_meta": {
55 | "title": "遮罩反转"
56 | }
57 | },
58 | "59": {
59 | "inputs": {
60 | "image": [
61 | "5",
62 | 0
63 | ],
64 | "alpha": [
65 | "44",
66 | 0
67 | ]
68 | },
69 | "class_type": "JoinImageWithAlpha",
70 | "_meta": {
71 | "title": "合并图像Alpha"
72 | }
73 | },
74 | "62": {
75 | "inputs": {
76 | "image_path": "https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/4c226c64d37f410c857f98ebb3ecb5ef.jpeg",
77 | "RGBA": "false",
78 | "filename_text_extension": "false"
79 | },
80 | "class_type": "Image Load",
81 | "_meta": {
82 | "title": "图像加载"
83 | }
84 | },
85 | "63": {
86 | "inputs": {
87 | "output_path": "[time(%Y-%m-%d)]",
88 | "filename_prefix": [
89 | "69",
90 | 0
91 | ],
92 | "filename_delimiter": "_",
93 | "filename_number_padding": 4,
94 | "filename_number_start": "false",
95 | "extension": "png",
96 | "quality": 100,
97 | "lossless_webp": "false",
98 | "overwrite_mode": "false",
99 | "show_history": "false",
100 | "show_history_by_prefix": "true",
101 | "embed_workflow": "true",
102 | "show_previews": "true",
103 | "images": [
104 | "59",
105 | 0
106 | ]
107 | },
108 | "class_type": "Image Save",
109 | "_meta": {
110 | "title": "图像保存"
111 | }
112 | },
113 | "67": {
114 | "inputs": {
115 | "text": "西瓜",
116 | "platform": "alibaba",
117 | "source": "auto",
118 | "target": "en"
119 | },
120 | "class_type": "ZFTextTranslation",
121 | "_meta": {
122 | "title": "文本翻译"
123 | }
124 | },
125 | "69": {
126 | "inputs": {
127 | "delimiter": "_",
128 | "clean_whitespace": "true",
129 | "text_a": [
130 | "62",
131 | 2
132 | ],
133 | "text_b": [
134 | "71",
135 | 0
136 | ]
137 | },
138 | "class_type": "Text Concatenate",
139 | "_meta": {
140 | "title": "文本连锁"
141 | }
142 | },
143 | "71": {
144 | "inputs": {
145 | "text": "_segment_output_final_"
146 | },
147 | "class_type": "Text Multiline",
148 | "_meta": {
149 | "title": "多行文本"
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_matting.ts:
--------------------------------------------------------------------------------
1 | export const matting = {
2 | '5': {
3 | inputs: {
4 | prompt: ['67', 0],
5 | threshold: 0.3,
6 | sam_model: ['6', 0],
7 | grounding_dino_model: ['7', 0],
8 | image: ['62', 0],
9 | },
10 | class_type: 'GroundingDinoSAMSegment (segment anything)',
11 | _meta: {
12 | title: 'G-DinoSAM语义分割',
13 | },
14 | },
15 | '6': {
16 | inputs: {
17 | model_name: 'sam_vit_h_4b8939.pth',
18 | device_mode: 'Prefer GPU',
19 | },
20 | class_type: 'SAMLoader',
21 | _meta: {
22 | title: 'SAM加载器',
23 | },
24 | },
25 | '7': {
26 | inputs: {
27 | model_name: 'GroundingDINO_SwinB (938MB)',
28 | },
29 | class_type: 'GroundingDinoModelLoader (segment anything)',
30 | _meta: {
31 | title: 'G-Dino模型加载器',
32 | },
33 | },
34 | '44': {
35 | inputs: {
36 | mask: ['5', 1],
37 | },
38 | class_type: 'InvertMask',
39 | _meta: {
40 | title: '遮罩反转',
41 | },
42 | },
43 | '59': {
44 | inputs: {
45 | image: ['5', 0],
46 | alpha: ['44', 0],
47 | },
48 | class_type: 'JoinImageWithAlpha',
49 | _meta: {
50 | title: '合并图像Alpha',
51 | },
52 | },
53 | '62': {
54 | inputs: {
55 | image_path:
56 | 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/4c226c64d37f410c857f98ebb3ecb5ef.jpeg',
57 | RGBA: 'false',
58 | filename_text_extension: 'false',
59 | },
60 | class_type: 'Image Load',
61 | _meta: {
62 | title: '图像加载',
63 | },
64 | },
65 | '63': {
66 | inputs: {
67 | output_path: '[time(%Y-%m-%d)]',
68 | filename_prefix: ['69', 0],
69 | filename_delimiter: '_',
70 | filename_number_padding: 4,
71 | filename_number_start: 'false',
72 | extension: 'png',
73 | quality: 100,
74 | lossless_webp: 'false',
75 | overwrite_mode: 'false',
76 | show_history: 'false',
77 | show_history_by_prefix: 'true',
78 | embed_workflow: 'true',
79 | show_previews: 'true',
80 | images: ['59', 0],
81 | },
82 | class_type: 'Image Save',
83 | _meta: {
84 | title: '图像保存',
85 | },
86 | },
87 | '67': {
88 | inputs: {
89 | text: '西瓜',
90 | platform: 'alibaba',
91 | source: 'auto',
92 | target: 'en',
93 | },
94 | class_type: 'ZFTextTranslation',
95 | _meta: {
96 | title: '文本翻译',
97 | },
98 | },
99 | '69': {
100 | inputs: {
101 | delimiter: '_',
102 | clean_whitespace: 'true',
103 | text_a: ['62', 2],
104 | text_b: ['71', 0],
105 | },
106 | class_type: 'Text Concatenate',
107 | _meta: {
108 | title: '文本连锁',
109 | },
110 | },
111 | '71': {
112 | inputs: {
113 | text: '_segment_output_final_',
114 | },
115 | class_type: 'Text Multiline',
116 | _meta: {
117 | title: '多行文本',
118 | },
119 | },
120 | };
121 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_removebg.json:
--------------------------------------------------------------------------------
1 | {
2 | "62": {
3 | "inputs": {
4 | "image_path": "http://region-8.autodl.pro:23781/view?subfolder=2024-04-06&filename=R-C__segment_output_final__0001_inpaiting_output_final__0003.png&type=output",
5 | "RGBA": "false",
6 | "filename_text_extension": "true"
7 | },
8 | "class_type": "Image Load",
9 | "_meta": {
10 | "title": "图像加载"
11 | }
12 | },
13 | "63": {
14 | "inputs": {
15 | "output_path": "[time(%Y-%m-%d)]",
16 | "filename_prefix": [
17 | "66",
18 | 0
19 | ],
20 | "filename_delimiter": "_",
21 | "filename_number_padding": 4,
22 | "filename_number_start": "false",
23 | "extension": "png",
24 | "quality": 100,
25 | "lossless_webp": "false",
26 | "overwrite_mode": "false",
27 | "show_history": "false",
28 | "show_history_by_prefix": "true",
29 | "embed_workflow": "true",
30 | "show_previews": "true",
31 | "images": [
32 | "72",
33 | 0
34 | ]
35 | },
36 | "class_type": "Image Save",
37 | "_meta": {
38 | "title": "图像保存"
39 | }
40 | },
41 | "66": {
42 | "inputs": {
43 | "delimiter": "-",
44 | "clean_whitespace": "true",
45 | "text_a": [
46 | "62",
47 | 2
48 | ],
49 | "text_b": [
50 | "73",
51 | 0
52 | ]
53 | },
54 | "class_type": "Text Concatenate",
55 | "_meta": {
56 | "title": "文本连锁"
57 | }
58 | },
59 | "72": {
60 | "inputs": {
61 | "transparency": true,
62 | "model": "u2net",
63 | "post_processing": false,
64 | "only_mask": false,
65 | "alpha_matting": false,
66 | "alpha_matting_foreground_threshold": 240,
67 | "alpha_matting_background_threshold": 10,
68 | "alpha_matting_erode_size": 10,
69 | "background_color": "none",
70 | "images": [
71 | "62",
72 | 0
73 | ]
74 | },
75 | "class_type": "Image Rembg (Remove Background)",
76 | "_meta": {
77 | "title": "移除背景"
78 | }
79 | },
80 | "73": {
81 | "inputs": {
82 | "text": "removebg_output_final_"
83 | },
84 | "class_type": "Text Multiline",
85 | "_meta": {
86 | "title": "多行文本"
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_removebg.ts:
--------------------------------------------------------------------------------
1 | export const removebg = {
2 | '62': {
3 | inputs: {
4 | image_path:
5 | 'http://region-8.autodl.pro:23781/view?subfolder=2024-04-06&filename=R-C__segment_output_final__0001_inpaiting_output_final__0003.png&type=output',
6 | RGBA: 'false',
7 | filename_text_extension: 'true',
8 | },
9 | class_type: 'Image Load',
10 | _meta: {
11 | title: '图像加载',
12 | },
13 | },
14 | '63': {
15 | inputs: {
16 | output_path: '[time(%Y-%m-%d)]',
17 | filename_prefix: ['66', 0],
18 | filename_delimiter: '_',
19 | filename_number_padding: 4,
20 | filename_number_start: 'false',
21 | extension: 'png',
22 | quality: 100,
23 | lossless_webp: 'false',
24 | overwrite_mode: 'false',
25 | show_history: 'false',
26 | show_history_by_prefix: 'true',
27 | embed_workflow: 'true',
28 | show_previews: 'true',
29 | images: ['72', 0],
30 | },
31 | class_type: 'Image Save',
32 | _meta: {
33 | title: '图像保存',
34 | },
35 | },
36 | '66': {
37 | inputs: {
38 | delimiter: '-',
39 | clean_whitespace: 'true',
40 | text_a: ['62', 2],
41 | text_b: ['73', 0],
42 | },
43 | class_type: 'Text Concatenate',
44 | _meta: {
45 | title: '文本连锁',
46 | },
47 | },
48 | '72': {
49 | inputs: {
50 | transparency: true,
51 | model: 'u2net',
52 | post_processing: false,
53 | only_mask: false,
54 | alpha_matting: false,
55 | alpha_matting_foreground_threshold: 240,
56 | alpha_matting_background_threshold: 10,
57 | alpha_matting_erode_size: 10,
58 | background_color: 'none',
59 | images: ['62', 0],
60 | },
61 | class_type: 'Image Rembg (Remove Background)',
62 | _meta: {
63 | title: '移除背景',
64 | },
65 | },
66 | '73': {
67 | inputs: {
68 | text: 'removebg_output_final_',
69 | },
70 | class_type: 'Text Multiline',
71 | _meta: {
72 | title: '多行文本',
73 | },
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_tagger.ts:
--------------------------------------------------------------------------------
1 | export const workflowApiTagger = {
2 | '10': {
3 | inputs: {
4 | image_path: './ComfyUI/input/example.png',
5 | RGBA: 'false',
6 | filename_text_extension: 'true',
7 | },
8 | class_type: 'Image Load',
9 | _meta: {
10 | title: '图像加载',
11 | },
12 | },
13 | '11': {
14 | inputs: {
15 | model: 'wd-v1-4-moat-tagger-v2',
16 | threshold: 0.35,
17 | character_threshold: 0.85,
18 | replace_underscore: false,
19 | trailing_comma: false,
20 | exclude_tags: '',
21 | tags: 'simple_background, monochrome, comic, greyscale, no_humans, black_background, negative_space',
22 | image: ['10', 0],
23 | },
24 | class_type: 'WD14Tagger|pysssss',
25 | _meta: {
26 | title: 'WD14反推提示词',
27 | },
28 | },
29 | '14': {
30 | inputs: {
31 | text: ['11', 0],
32 | label: 'Text Output',
33 | },
34 | class_type: 'Text to Console',
35 | _meta: {
36 | title: '输出文本到控制台',
37 | },
38 | },
39 | };
40 |
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_text2img.json:
--------------------------------------------------------------------------------
1 | {
2 | "18": {
3 | "inputs": {
4 | "text": "paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,"
5 | },
6 | "class_type": "ttN text",
7 | "_meta": {
8 | "title": "TTN文本"
9 | }
10 | },
11 | "19": {
12 | "inputs": {
13 | "text": "(8k, best quality, masterpiece:1.2)"
14 | },
15 | "class_type": "ttN text",
16 | "_meta": {
17 | "title": "TTN文本"
18 | }
19 | },
20 | "25": {
21 | "inputs": {
22 | "delimiter": ",",
23 | "clean_whitespace": "true",
24 | "text_a": [
25 | "55",
26 | 0
27 | ],
28 | "text_b": [
29 | "19",
30 | 0
31 | ]
32 | },
33 | "class_type": "Text Concatenate",
34 | "_meta": {
35 | "title": "文本连锁"
36 | }
37 | },
38 | "26": {
39 | "inputs": {
40 | "delimiter": "",
41 | "clean_whitespace": "true",
42 | "text_a": [
43 | "58",
44 | 0
45 | ],
46 | "text_b": [
47 | "18",
48 | 0
49 | ]
50 | },
51 | "class_type": "Text Concatenate",
52 | "_meta": {
53 | "title": "文本连锁"
54 | }
55 | },
56 | "44": {
57 | "inputs": {
58 | "upscale_type": "latent",
59 | "hires_ckpt_name": "(use same)",
60 | "latent_upscaler": "nearest-exact",
61 | "pixel_upscaler": "4x-UltraSharp.pth",
62 | "upscale_by": 1.5,
63 | "use_same_seed": true,
64 | "seed": -1,
65 | "hires_steps": 12,
66 | "denoise": 0.3,
67 | "iterations": 1,
68 | "use_controlnet": false,
69 | "control_net_name": "control_sd15_random_color.pth",
70 | "strength": 1,
71 | "preprocessor": "CannyEdgePreprocessor",
72 | "preprocessor_imgs": false
73 | },
74 | "class_type": "HighRes-Fix Script",
75 | "_meta": {
76 | "title": "高清修复"
77 | }
78 | },
79 | "45": {
80 | "inputs": {
81 | "input_mode": "simple",
82 | "lora_count": 5,
83 | "lora_name_1": "lcm\\lcm_sd1.5_pytorch_lora_weights.safetensors",
84 | "lora_wt_1": 1,
85 | "model_str_1": 1,
86 | "clip_str_1": 1,
87 | "lora_name_2": "None",
88 | "lora_wt_2": 0.5,
89 | "model_str_2": 1,
90 | "clip_str_2": 1,
91 | "lora_name_3": "None",
92 | "lora_wt_3": 0.5,
93 | "model_str_3": 1,
94 | "clip_str_3": 1,
95 | "lora_name_4": "None",
96 | "lora_wt_4": 1,
97 | "model_str_4": 1,
98 | "clip_str_4": 1,
99 | "lora_name_5": "None",
100 | "lora_wt_5": 1,
101 | "model_str_5": 1,
102 | "clip_str_5": 1,
103 | "lora_name_6": "None",
104 | "lora_wt_6": 1,
105 | "model_str_6": 1,
106 | "clip_str_6": 1,
107 | "lora_name_7": "None",
108 | "lora_wt_7": 1,
109 | "model_str_7": 1,
110 | "clip_str_7": 1,
111 | "lora_name_8": "None",
112 | "lora_wt_8": 1,
113 | "model_str_8": 1,
114 | "clip_str_8": 1,
115 | "lora_name_9": "None",
116 | "lora_wt_9": 1,
117 | "model_str_9": 1,
118 | "clip_str_9": 1,
119 | "lora_name_10": "None",
120 | "lora_wt_10": 1,
121 | "model_str_10": 1,
122 | "clip_str_10": 1,
123 | "lora_name_11": "None",
124 | "lora_wt_11": 1,
125 | "model_str_11": 1,
126 | "clip_str_11": 1,
127 | "lora_name_12": "None",
128 | "lora_wt_12": 1,
129 | "model_str_12": 1,
130 | "clip_str_12": 1,
131 | "lora_name_13": "None",
132 | "lora_wt_13": 1,
133 | "model_str_13": 1,
134 | "clip_str_13": 1,
135 | "lora_name_14": "None",
136 | "lora_wt_14": 1,
137 | "model_str_14": 1,
138 | "clip_str_14": 1,
139 | "lora_name_15": "None",
140 | "lora_wt_15": 1,
141 | "model_str_15": 1,
142 | "clip_str_15": 1,
143 | "lora_name_16": "None",
144 | "lora_wt_16": 1,
145 | "model_str_16": 1,
146 | "clip_str_16": 1,
147 | "lora_name_17": "None",
148 | "lora_wt_17": 1,
149 | "model_str_17": 1,
150 | "clip_str_17": 1,
151 | "lora_name_18": "None",
152 | "lora_wt_18": 1,
153 | "model_str_18": 1,
154 | "clip_str_18": 1,
155 | "lora_name_19": "None",
156 | "lora_wt_19": 1,
157 | "model_str_19": 1,
158 | "clip_str_19": 1,
159 | "lora_name_20": "None",
160 | "lora_wt_20": 1,
161 | "model_str_20": 1,
162 | "clip_str_20": 1,
163 | "lora_name_21": "None",
164 | "lora_wt_21": 1,
165 | "model_str_21": 1,
166 | "clip_str_21": 1,
167 | "lora_name_22": "None",
168 | "lora_wt_22": 1,
169 | "model_str_22": 1,
170 | "clip_str_22": 1,
171 | "lora_name_23": "None",
172 | "lora_wt_23": 1,
173 | "model_str_23": 1,
174 | "clip_str_23": 1,
175 | "lora_name_24": "None",
176 | "lora_wt_24": 1,
177 | "model_str_24": 1,
178 | "clip_str_24": 1,
179 | "lora_name_25": "None",
180 | "lora_wt_25": 1,
181 | "model_str_25": 1,
182 | "clip_str_25": 1,
183 | "lora_name_26": "None",
184 | "lora_wt_26": 1,
185 | "model_str_26": 1,
186 | "clip_str_26": 1,
187 | "lora_name_27": "None",
188 | "lora_wt_27": 1,
189 | "model_str_27": 1,
190 | "clip_str_27": 1,
191 | "lora_name_28": "None",
192 | "lora_wt_28": 1,
193 | "model_str_28": 1,
194 | "clip_str_28": 1,
195 | "lora_name_29": "None",
196 | "lora_wt_29": 1,
197 | "model_str_29": 1,
198 | "clip_str_29": 1,
199 | "lora_name_30": "None",
200 | "lora_wt_30": 1,
201 | "model_str_30": 1,
202 | "clip_str_30": 1,
203 | "lora_name_31": "None",
204 | "lora_wt_31": 1,
205 | "model_str_31": 1,
206 | "clip_str_31": 1,
207 | "lora_name_32": "None",
208 | "lora_wt_32": 1,
209 | "model_str_32": 1,
210 | "clip_str_32": 1,
211 | "lora_name_33": "None",
212 | "lora_wt_33": 1,
213 | "model_str_33": 1,
214 | "clip_str_33": 1,
215 | "lora_name_34": "None",
216 | "lora_wt_34": 1,
217 | "model_str_34": 1,
218 | "clip_str_34": 1,
219 | "lora_name_35": "None",
220 | "lora_wt_35": 1,
221 | "model_str_35": 1,
222 | "clip_str_35": 1,
223 | "lora_name_36": "None",
224 | "lora_wt_36": 1,
225 | "model_str_36": 1,
226 | "clip_str_36": 1,
227 | "lora_name_37": "None",
228 | "lora_wt_37": 1,
229 | "model_str_37": 1,
230 | "clip_str_37": 1,
231 | "lora_name_38": "None",
232 | "lora_wt_38": 1,
233 | "model_str_38": 1,
234 | "clip_str_38": 1,
235 | "lora_name_39": "None",
236 | "lora_wt_39": 1,
237 | "model_str_39": 1,
238 | "clip_str_39": 1,
239 | "lora_name_40": "None",
240 | "lora_wt_40": 1,
241 | "model_str_40": 1,
242 | "clip_str_40": 1,
243 | "lora_name_41": "None",
244 | "lora_wt_41": 1,
245 | "model_str_41": 1,
246 | "clip_str_41": 1,
247 | "lora_name_42": "None",
248 | "lora_wt_42": 1,
249 | "model_str_42": 1,
250 | "clip_str_42": 1,
251 | "lora_name_43": "None",
252 | "lora_wt_43": 1,
253 | "model_str_43": 1,
254 | "clip_str_43": 1,
255 | "lora_name_44": "None",
256 | "lora_wt_44": 1,
257 | "model_str_44": 1,
258 | "clip_str_44": 1,
259 | "lora_name_45": "None",
260 | "lora_wt_45": 1,
261 | "model_str_45": 1,
262 | "clip_str_45": 1,
263 | "lora_name_46": "None",
264 | "lora_wt_46": 1,
265 | "model_str_46": 1,
266 | "clip_str_46": 1,
267 | "lora_name_47": "None",
268 | "lora_wt_47": 1,
269 | "model_str_47": 1,
270 | "clip_str_47": 1,
271 | "lora_name_48": "None",
272 | "lora_wt_48": 1,
273 | "model_str_48": 1,
274 | "clip_str_48": 1,
275 | "lora_name_49": "None",
276 | "lora_wt_49": 1,
277 | "model_str_49": 1,
278 | "clip_str_49": 1
279 | },
280 | "class_type": "LoRA Stacker",
281 | "_meta": {
282 | "title": "LoRA堆"
283 | }
284 | },
285 | "51": {
286 | "inputs": {
287 | "output_path": "[time(%Y-%m-%d)]",
288 | "filename_prefix": "text2img_output_final_",
289 | "filename_delimiter": "_",
290 | "filename_number_padding": 4,
291 | "filename_number_start": "false",
292 | "extension": "png",
293 | "quality": 100,
294 | "lossless_webp": "false",
295 | "overwrite_mode": "false",
296 | "show_history": "false",
297 | "show_history_by_prefix": "true",
298 | "embed_workflow": "true",
299 | "show_previews": "true",
300 | "images": [
301 | "54",
302 | 5
303 | ]
304 | },
305 | "class_type": "Image Save",
306 | "_meta": {
307 | "title": "图像保存"
308 | }
309 | },
310 | "52": {
311 | "inputs": {
312 | "ckpt_name": "majicmixRealistic_v7.safetensors",
313 | "vae_name": "Baked VAE",
314 | "clip_skip": -2,
315 | "lora_name": "None",
316 | "lora_model_strength": 1,
317 | "lora_clip_strength": 1,
318 | "positive": [
319 | "25",
320 | 0
321 | ],
322 | "negative": [
323 | "26",
324 | 0
325 | ],
326 | "token_normalization": "none",
327 | "weight_interpretation": "A1111",
328 | "empty_latent_width": 512,
329 | "empty_latent_height": 512,
330 | "batch_size": 1
331 | },
332 | "class_type": "Efficient Loader",
333 | "_meta": {
334 | "title": "效率加载器"
335 | }
336 | },
337 | "54": {
338 | "inputs": {
339 | "seed": 127132725962822,
340 | "steps": 20,
341 | "cfg": 7,
342 | "sampler_name": "dpmpp_2m",
343 | "scheduler": "karras",
344 | "denoise": 1,
345 | "preview_method": "none",
346 | "vae_decode": "true",
347 | "model": [
348 | "52",
349 | 0
350 | ],
351 | "positive": [
352 | "52",
353 | 1
354 | ],
355 | "negative": [
356 | "52",
357 | 2
358 | ],
359 | "latent_image": [
360 | "52",
361 | 3
362 | ],
363 | "optional_vae": [
364 | "52",
365 | 4
366 | ]
367 | },
368 | "class_type": "KSampler (Efficient)",
369 | "_meta": {
370 | "title": "K采样器(效率)"
371 | }
372 | },
373 | "55": {
374 | "inputs": {
375 | "text": "一个女孩",
376 | "platform": "alibaba",
377 | "source": "auto",
378 | "target": "en"
379 | },
380 | "class_type": "ZFTextTranslation",
381 | "_meta": {
382 | "title": "文本翻译"
383 | }
384 | },
385 | "58": {
386 | "inputs": {
387 | "text": "",
388 | "platform": "alibaba",
389 | "source": "auto",
390 | "target": "en"
391 | },
392 | "class_type": "ZFTextTranslation",
393 | "_meta": {
394 | "title": "文本翻译"
395 | }
396 | }
397 | }
--------------------------------------------------------------------------------
/src/draw/data/workflow_api_text2img.ts:
--------------------------------------------------------------------------------
1 | export const text2img = {
2 | '18': {
3 | inputs: {
4 | text: 'paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, manboobs,(ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, (bad anatomy:1.21), (bad proportions:1.331), extra limbs, (disfigured:1.331), (more than 2 nipples:1.331), (missing arms:1.331), (extra legs:1.331), (fused fingers:1.61051), (too many fingers:1.61051), (unclear eyes:1.331), bad hands, missing fingers, extra digit, (futa:1.1), bad body, NG_DeepNegative_V1_75T,',
5 | },
6 | class_type: 'ttN text',
7 | _meta: {
8 | title: 'TTN文本',
9 | },
10 | },
11 | '19': {
12 | inputs: {
13 | text: '(8k, best quality, masterpiece:1.2)',
14 | },
15 | class_type: 'ttN text',
16 | _meta: {
17 | title: 'TTN文本',
18 | },
19 | },
20 | '25': {
21 | inputs: {
22 | delimiter: ',',
23 | clean_whitespace: 'true',
24 | text_a: ['55', 0],
25 | text_b: ['19', 0],
26 | },
27 | class_type: 'Text Concatenate',
28 | _meta: {
29 | title: '文本连锁',
30 | },
31 | },
32 | '26': {
33 | inputs: {
34 | delimiter: '',
35 | clean_whitespace: 'true',
36 | text_a: ['58', 0],
37 | text_b: ['18', 0],
38 | },
39 | class_type: 'Text Concatenate',
40 | _meta: {
41 | title: '文本连锁',
42 | },
43 | },
44 | '44': {
45 | inputs: {
46 | upscale_type: 'latent',
47 | hires_ckpt_name: '(use same)',
48 | latent_upscaler: 'nearest-exact',
49 | pixel_upscaler: '4x-UltraSharp.pth',
50 | upscale_by: 1.5,
51 | use_same_seed: true,
52 | seed: -1,
53 | hires_steps: 12,
54 | denoise: 0.3,
55 | iterations: 1,
56 | use_controlnet: false,
57 | control_net_name: 'control_sd15_random_color.pth',
58 | strength: 1,
59 | preprocessor: 'CannyEdgePreprocessor',
60 | preprocessor_imgs: false,
61 | },
62 | class_type: 'HighRes-Fix Script',
63 | _meta: {
64 | title: '高清修复',
65 | },
66 | },
67 | '45': {
68 | inputs: {
69 | input_mode: 'simple',
70 | lora_count: 5,
71 | lora_name_1: 'lcm\\lcm_sd1.5_pytorch_lora_weights.safetensors',
72 | lora_wt_1: 1,
73 | model_str_1: 1,
74 | clip_str_1: 1,
75 | lora_name_2: 'None',
76 | lora_wt_2: 0.5,
77 | model_str_2: 1,
78 | clip_str_2: 1,
79 | lora_name_3: 'None',
80 | lora_wt_3: 0.5,
81 | model_str_3: 1,
82 | clip_str_3: 1,
83 | lora_name_4: 'None',
84 | lora_wt_4: 1,
85 | model_str_4: 1,
86 | clip_str_4: 1,
87 | lora_name_5: 'None',
88 | lora_wt_5: 1,
89 | model_str_5: 1,
90 | clip_str_5: 1,
91 | lora_name_6: 'None',
92 | lora_wt_6: 1,
93 | model_str_6: 1,
94 | clip_str_6: 1,
95 | lora_name_7: 'None',
96 | lora_wt_7: 1,
97 | model_str_7: 1,
98 | clip_str_7: 1,
99 | lora_name_8: 'None',
100 | lora_wt_8: 1,
101 | model_str_8: 1,
102 | clip_str_8: 1,
103 | lora_name_9: 'None',
104 | lora_wt_9: 1,
105 | model_str_9: 1,
106 | clip_str_9: 1,
107 | lora_name_10: 'None',
108 | lora_wt_10: 1,
109 | model_str_10: 1,
110 | clip_str_10: 1,
111 | lora_name_11: 'None',
112 | lora_wt_11: 1,
113 | model_str_11: 1,
114 | clip_str_11: 1,
115 | lora_name_12: 'None',
116 | lora_wt_12: 1,
117 | model_str_12: 1,
118 | clip_str_12: 1,
119 | lora_name_13: 'None',
120 | lora_wt_13: 1,
121 | model_str_13: 1,
122 | clip_str_13: 1,
123 | lora_name_14: 'None',
124 | lora_wt_14: 1,
125 | model_str_14: 1,
126 | clip_str_14: 1,
127 | lora_name_15: 'None',
128 | lora_wt_15: 1,
129 | model_str_15: 1,
130 | clip_str_15: 1,
131 | lora_name_16: 'None',
132 | lora_wt_16: 1,
133 | model_str_16: 1,
134 | clip_str_16: 1,
135 | lora_name_17: 'None',
136 | lora_wt_17: 1,
137 | model_str_17: 1,
138 | clip_str_17: 1,
139 | lora_name_18: 'None',
140 | lora_wt_18: 1,
141 | model_str_18: 1,
142 | clip_str_18: 1,
143 | lora_name_19: 'None',
144 | lora_wt_19: 1,
145 | model_str_19: 1,
146 | clip_str_19: 1,
147 | lora_name_20: 'None',
148 | lora_wt_20: 1,
149 | model_str_20: 1,
150 | clip_str_20: 1,
151 | lora_name_21: 'None',
152 | lora_wt_21: 1,
153 | model_str_21: 1,
154 | clip_str_21: 1,
155 | lora_name_22: 'None',
156 | lora_wt_22: 1,
157 | model_str_22: 1,
158 | clip_str_22: 1,
159 | lora_name_23: 'None',
160 | lora_wt_23: 1,
161 | model_str_23: 1,
162 | clip_str_23: 1,
163 | lora_name_24: 'None',
164 | lora_wt_24: 1,
165 | model_str_24: 1,
166 | clip_str_24: 1,
167 | lora_name_25: 'None',
168 | lora_wt_25: 1,
169 | model_str_25: 1,
170 | clip_str_25: 1,
171 | lora_name_26: 'None',
172 | lora_wt_26: 1,
173 | model_str_26: 1,
174 | clip_str_26: 1,
175 | lora_name_27: 'None',
176 | lora_wt_27: 1,
177 | model_str_27: 1,
178 | clip_str_27: 1,
179 | lora_name_28: 'None',
180 | lora_wt_28: 1,
181 | model_str_28: 1,
182 | clip_str_28: 1,
183 | lora_name_29: 'None',
184 | lora_wt_29: 1,
185 | model_str_29: 1,
186 | clip_str_29: 1,
187 | lora_name_30: 'None',
188 | lora_wt_30: 1,
189 | model_str_30: 1,
190 | clip_str_30: 1,
191 | lora_name_31: 'None',
192 | lora_wt_31: 1,
193 | model_str_31: 1,
194 | clip_str_31: 1,
195 | lora_name_32: 'None',
196 | lora_wt_32: 1,
197 | model_str_32: 1,
198 | clip_str_32: 1,
199 | lora_name_33: 'None',
200 | lora_wt_33: 1,
201 | model_str_33: 1,
202 | clip_str_33: 1,
203 | lora_name_34: 'None',
204 | lora_wt_34: 1,
205 | model_str_34: 1,
206 | clip_str_34: 1,
207 | lora_name_35: 'None',
208 | lora_wt_35: 1,
209 | model_str_35: 1,
210 | clip_str_35: 1,
211 | lora_name_36: 'None',
212 | lora_wt_36: 1,
213 | model_str_36: 1,
214 | clip_str_36: 1,
215 | lora_name_37: 'None',
216 | lora_wt_37: 1,
217 | model_str_37: 1,
218 | clip_str_37: 1,
219 | lora_name_38: 'None',
220 | lora_wt_38: 1,
221 | model_str_38: 1,
222 | clip_str_38: 1,
223 | lora_name_39: 'None',
224 | lora_wt_39: 1,
225 | model_str_39: 1,
226 | clip_str_39: 1,
227 | lora_name_40: 'None',
228 | lora_wt_40: 1,
229 | model_str_40: 1,
230 | clip_str_40: 1,
231 | lora_name_41: 'None',
232 | lora_wt_41: 1,
233 | model_str_41: 1,
234 | clip_str_41: 1,
235 | lora_name_42: 'None',
236 | lora_wt_42: 1,
237 | model_str_42: 1,
238 | clip_str_42: 1,
239 | lora_name_43: 'None',
240 | lora_wt_43: 1,
241 | model_str_43: 1,
242 | clip_str_43: 1,
243 | lora_name_44: 'None',
244 | lora_wt_44: 1,
245 | model_str_44: 1,
246 | clip_str_44: 1,
247 | lora_name_45: 'None',
248 | lora_wt_45: 1,
249 | model_str_45: 1,
250 | clip_str_45: 1,
251 | lora_name_46: 'None',
252 | lora_wt_46: 1,
253 | model_str_46: 1,
254 | clip_str_46: 1,
255 | lora_name_47: 'None',
256 | lora_wt_47: 1,
257 | model_str_47: 1,
258 | clip_str_47: 1,
259 | lora_name_48: 'None',
260 | lora_wt_48: 1,
261 | model_str_48: 1,
262 | clip_str_48: 1,
263 | lora_name_49: 'None',
264 | lora_wt_49: 1,
265 | model_str_49: 1,
266 | clip_str_49: 1,
267 | },
268 | class_type: 'LoRA Stacker',
269 | _meta: {
270 | title: 'LoRA堆',
271 | },
272 | },
273 | '51': {
274 | inputs: {
275 | output_path: '[time(%Y-%m-%d)]',
276 | filename_prefix: 'text2img_output_final_',
277 | filename_delimiter: '_',
278 | filename_number_padding: 4,
279 | filename_number_start: 'false',
280 | extension: 'png',
281 | quality: 100,
282 | lossless_webp: 'false',
283 | overwrite_mode: 'false',
284 | show_history: 'false',
285 | show_history_by_prefix: 'true',
286 | embed_workflow: 'true',
287 | show_previews: 'true',
288 | images: ['54', 5],
289 | },
290 | class_type: 'Image Save',
291 | _meta: {
292 | title: '图像保存',
293 | },
294 | },
295 | '52': {
296 | inputs: {
297 | ckpt_name: 'majicmixRealistic_v7.safetensors',
298 | vae_name: 'Baked VAE',
299 | clip_skip: -2,
300 | lora_name: 'None',
301 | lora_model_strength: 1,
302 | lora_clip_strength: 1,
303 | positive: ['25', 0],
304 | negative: ['26', 0],
305 | token_normalization: 'none',
306 | weight_interpretation: 'A1111',
307 | empty_latent_width: 512,
308 | empty_latent_height: 512,
309 | batch_size: 1,
310 | },
311 | class_type: 'Efficient Loader',
312 | _meta: {
313 | title: '效率加载器',
314 | },
315 | },
316 | '54': {
317 | inputs: {
318 | seed: 127132725962822,
319 | steps: 20,
320 | cfg: 7,
321 | sampler_name: 'dpmpp_2m',
322 | scheduler: 'karras',
323 | denoise: 1,
324 | preview_method: 'none',
325 | vae_decode: 'true',
326 | model: ['52', 0],
327 | positive: ['52', 1],
328 | negative: ['52', 2],
329 | latent_image: ['52', 3],
330 | optional_vae: ['52', 4],
331 | },
332 | class_type: 'KSampler (Efficient)',
333 | _meta: {
334 | title: 'K采样器(效率)',
335 | },
336 | },
337 | '55': {
338 | inputs: {
339 | text: '一个女孩',
340 | platform: 'alibaba',
341 | source: 'auto',
342 | target: 'en',
343 | },
344 | class_type: 'ZFTextTranslation',
345 | _meta: {
346 | title: '文本翻译',
347 | },
348 | },
349 | '58': {
350 | inputs: {
351 | text: '',
352 | platform: 'alibaba',
353 | source: 'auto',
354 | target: 'en',
355 | },
356 | class_type: 'ZFTextTranslation',
357 | _meta: {
358 | title: '文本翻译',
359 | },
360 | },
361 | };
362 |
--------------------------------------------------------------------------------
/src/draw/draw.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { DrawService } from './draw.service';
3 | import { DrawController } from './draw.controller';
4 | import { BullModule } from '@nestjs/bull';
5 | import { ConfigService } from '@nestjs/config/dist';
6 | import { CacheModule } from '../cache/cache.module';
7 | import { WsGateway } from '../ws/ws.gateway';
8 | import { FileModule } from '../file/file.module';
9 | import * as process from 'node:process';
10 |
11 | @Module({
12 | controllers: [DrawController],
13 | exports: [DrawService, BullModule],
14 | imports: [
15 | BullModule.forRootAsync({
16 | useFactory: (config: ConfigService) => ({
17 | redis: {
18 | host: config.get('CONFIG_COMFYUI_QUENE_REDIS_HOST'),
19 | port: config.get('CONFIG_COMFYUI_QUENE_REDIS_PORT'),
20 | password: config.get('CONFIG_COMFYUI_QUENE_REDIS_PASSWORD'),
21 | },
22 | }),
23 | inject: [ConfigService],
24 | }),
25 | BullModule.registerQueue({
26 | name: 'draw',
27 | }),
28 | CacheModule,
29 | FileModule,
30 | ],
31 | providers: [DrawService],
32 | })
33 | export class DrawModule {}
34 |
--------------------------------------------------------------------------------
/src/file/file.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get } from '@nestjs/common';
2 | import { FileService } from './file.service';
3 |
4 | @Controller('file')
5 | export class FileController {
6 | constructor(private readonly fileService: FileService) {}
7 | @Get('test')
8 | async upload() {
9 | const testurl= "https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/%E6%9D%8E%E4%BA%91%E6%80%9D1130-01.mp4";
10 | // const testurl= "https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/audio/temps/tts_undefined_%0A%0A%E4%B8%8D%E8%A6%81%E5%BF%98%E8%AE%B0%EF%BC%8C%E9%A3%8E%E9%9B%A8%E8%BF%87%E5%90%8E%EF%BC%8C%E5%BD%A9%E8%99%B9%E4%BC%9A%E5%8D%87%E8%B5%B7.wav";
11 |
12 |
13 | const _file = await this.fileService.urlToFile(
14 | testurl,
15 | 'test.wav',
16 | 'audio/wav',
17 | );
18 | return await this.fileService.uploadFileToOSS(testurl);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/file/file.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { FileService } from './file.service';
3 | import { FileController } from './file.controller';
4 |
5 | @Module({
6 | controllers: [FileController],
7 | providers: [FileService],
8 | exports: [FileService],
9 | })
10 | export class FileModule {}
11 |
--------------------------------------------------------------------------------
/src/file/file.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { ConfigService } from '@nestjs/config';
3 | import { nanoid } from 'nanoid';
4 | // eslint-disable-next-line @typescript-eslint/no-var-requires
5 | const OSS = require('ali-oss');
6 | // const fs = require('fs');
7 | @Injectable()
8 | export class FileService {
9 | constructor(
10 | private readonly configService: ConfigService,
11 | ) {
12 | }
13 | /**
14 | * 将url转换成file
15 | * @param url 图片链接
16 | * @param fileName 文件名
17 | * @param mimeType 文件类型
18 | * @private
19 | */
20 | async urlToFile(url, fileName, mimeType): Promise {
21 | return fetch(url)
22 | .then((res) => res.arrayBuffer())
23 | .then((buffer) => {
24 | return new File([buffer], fileName, { type: mimeType });
25 | });
26 | }
27 |
28 | /**
29 | * 将url转换成ArrayBuffer
30 | *
31 | * @param url
32 | */
33 | async urlToArrayBuffer(url: string): Promise {
34 | return fetch(url)
35 | .then((res) => res.arrayBuffer())
36 | .then((buffer) => {
37 | return buffer;
38 | });
39 | }
40 | /**
41 | * 上传文件到OSS,返回上传成功的文件名
42 | *
43 | * @param file
44 | * @param type
45 | */
46 | async uploadFileToOSS(
47 | file: string,
48 | type?: 'image' | 'audio' | 'video',
49 | ): Promise {
50 | // 创建OSS客户端实例
51 | const client = new OSS({
52 | region: this.configService.get('OSS_REGION'), // 替换为你的OSS区域
53 | accessKeyId: this.configService.get('OSS_ACCESSKEYID'), // 替换为你的AccessKeyId
54 | accessKeySecret: this.configService.get('OSS_ACCESSKEYSECRET'), // 替换为你的AccessKeySecret
55 | bucket: this.configService.get('OSS_BUCKET'), // 替换为你的Bucket名称
56 | });
57 | let _f = '';
58 | let _r = '';
59 | const filename = nanoid(15);
60 | // 根据文件类型转换成对应的文件
61 | switch (type) {
62 | case 'image':
63 | _f = filename + '.png';
64 | _r = 'aidraw/image/temps/';
65 | break;
66 | case 'audio':
67 | _f = filename + '.wav';
68 | _r = 'aidraw/audio/temps/';
69 | break;
70 | case 'video':
71 | _f = filename + '.mp4';
72 | _r = 'aidraw/video/temps/';
73 | break;
74 | default:
75 | _f = filename + '.png';
76 | _r = 'aidraw/others/temps/';
77 | break;
78 | }
79 | //将文件保存到服务器
80 | const targetPath = `${_r}${_f}`;
81 |
82 | try {
83 | const _f = await this.urlToArrayBuffer(file);
84 | //ArrayBuffer转换成Buffer
85 | const buffer = Buffer.from(_f);
86 | const { url } = await client.put(targetPath, buffer);
87 | return url;
88 | } catch (e) {
89 | console.error('Error uploading file to OSS:', e);
90 | throw e;
91 | }
92 | }
93 | //将Buffer上传到OSS
94 | async uploadBufferToOSS(
95 | buffer: Buffer,
96 | type?: 'image' | 'audio' | 'video',
97 | ): Promise {
98 | // 创建OSS客户端实例
99 | const client = new OSS({
100 | region: this.configService.get('OSS_REGION'), // 替换为你的OSS区域
101 | accessKeyId: this.configService.get('OSS_ACCESSKEYID'), // 替换为你的AccessKeyId
102 | accessKeySecret: this.configService.get('OSS_ACCESSKEYSECRET'), // 替换为你的AccessKeySecret
103 | bucket: this.configService.get('OSS_BUCKET'), // 替换为你的Bucket名称
104 | });
105 | let _f = '';
106 | let _r = '';
107 | const filename = nanoid(15);
108 | // 根据文件类型转换成对应的文件
109 | switch (type) {
110 | case 'image':
111 | _f = filename + '.png';
112 | _r = 'aidraw/image/temps/';
113 | break;
114 | case 'audio':
115 | _f = filename + '.wav';
116 | _r = 'aidraw/audio/temps/';
117 | break;
118 | case 'video':
119 | _f = filename + '.mp4';
120 | _r = 'aidraw/video/temps/';
121 | break;
122 | default:
123 | _f = filename + '.png';
124 | _r = 'aidraw/others/temps/';
125 | break;
126 | }
127 | //将文件保存到服务器
128 | const targetPath = `${_r}${_f}`;
129 | try {
130 | const { url } = await client.put(targetPath, buffer);
131 | return url;
132 | } catch (e) {
133 | console.error('Error uploading file to OSS:', e);
134 | throw e;
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
4 | import { ConfigService } from '@nestjs/config';
5 |
6 | async function bootstrap() {
7 | const app = await NestFactory.create(AppModule,{
8 | cors: true,
9 | });
10 | app.enableCors({
11 | origin: '*',
12 | methods: '*',
13 | allowedHeaders: '*',
14 | credentials: true,
15 | });
16 | const config = new DocumentBuilder()
17 | .setTitle('ComfyUI server API文档')
18 | .setDescription(
19 | '使用ComfyUI的工作流就像使用API一样简单!comfy-server是ComfyUI原生接口的服务端增强版本,可以将comfyUI强大的工作流转换为API供前端调用',
20 | )
21 | .setVersion('1.0')
22 | .addBearerAuth({
23 | type: 'http',
24 | scheme: 'bearer',
25 | bearerFormat: 'JWT',
26 | name: 'JWT',
27 | description: 'Enter JWT token',
28 | in: 'header',
29 | })
30 | .build();
31 | const document = SwaggerModule.createDocument(app, config);
32 | SwaggerModule.setup('api-docs', app, document);
33 | await app.listen(new ConfigService().get('CONFIG_BASE_POST'));
34 | }
35 |
36 | bootstrap();
37 |
--------------------------------------------------------------------------------
/src/middleware/XML.middleware.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, NestMiddleware } from '@nestjs/common';
2 | import { NextFunction, Request, Response } from 'express';
3 | import { parseString } from 'xml2js';
4 |
5 | /**
6 | * XML 中间件,微信消息前置处理为json
7 | *
8 | */
9 | @Injectable()
10 | export class XMLMiddleware implements NestMiddleware {
11 | async use(req: Request, res: Response, next: NextFunction) {
12 | console.log('XML middlewarem excting');
13 | const buffer: any[] = []; // 创建一个空数组,用于存储请求的数据
14 | // 监听 req 的 data 事件,每当有数据到达时,就将数据推入 buffer 数组中
15 | req.on('data', (chunk) => {
16 | buffer.push(chunk);
17 | });
18 | // 监听 req 的 end 事件,表示请求数据已经接收完毕时,执行以下操作:
19 | req.on('end', () => {
20 | // 将 buffer 数组中的数据拼接成一个字符串,并以 utf-8 编码转换为 msgXml 变量
21 | const msgXml = Buffer.concat(buffer).toString('utf-8');
22 |
23 | // 调用 parseString 函数,将 msgXml 变量中的 xml 格式的数据解析为 JavaScript 对象,并赋值给 result 变量。
24 | // 如果解析过程出现错误,则抛出异常并拒绝 promise。
25 | parseString(msgXml, (err, result) => {
26 | if (err) {
27 | throw err;
28 | }
29 |
30 | // 将 result 变量赋值给 req.body 属性,表示将请求体转换为 JavaScript 对象。
31 | req.body = result;
32 |
33 | // 调用 next 函数,表示继续执行下一个中间件函数。
34 | next();
35 | });
36 | });
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/oneapi/tokens/dto/create-token.dto.ts:
--------------------------------------------------------------------------------
1 | import { Token } from '../entities/token.entity';
2 |
3 | export class CreateTokenDto extends Token {}
4 |
--------------------------------------------------------------------------------
/src/oneapi/tokens/dto/update-token.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/mapped-types';
2 | import { CreateTokenDto } from './create-token.dto';
3 |
4 | export class UpdateTokenDto extends PartialType(CreateTokenDto) {}
5 |
--------------------------------------------------------------------------------
/src/oneapi/tokens/entities/token.entity.ts:
--------------------------------------------------------------------------------
1 | import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
2 |
3 | @Entity({ name: 'tokens' })
4 | export class Token {
5 | @PrimaryGeneratedColumn()
6 | id: number;
7 | @Column({ nullable: true })
8 | user_id: number;
9 | @Column({ nullable: true })
10 | key: string;
11 | @Column({ nullable: true })
12 | status: number;
13 | @Column({ nullable: true })
14 | name: string;
15 | @Column({ nullable: true })
16 | created_time: number;
17 | @Column({ nullable: true })
18 | accessed_time: number;
19 | @Column({ nullable: true })
20 | expired_time: number;
21 | @Column({ nullable: true })
22 | remain_quota: number;
23 | @Column({ nullable: true })
24 | unlimited_quota: boolean;
25 | @Column({ nullable: true })
26 | used_quota: number;
27 | }
28 |
--------------------------------------------------------------------------------
/src/oneapi/tokens/tokens.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
2 | import { TokensService } from './tokens.service';
3 | import { CreateTokenDto } from './dto/create-token.dto';
4 | import { UpdateTokenDto } from './dto/update-token.dto';
5 | import { ApiTags } from '@nestjs/swagger';
6 | @ApiTags('OneAPI')
7 | @Controller('oneapi/tokens')
8 | export class TokensController {
9 | constructor(private readonly tokensService: TokensService) {}
10 |
11 | @Post()
12 | create(@Body() createTokenDto: CreateTokenDto) {
13 | return this.tokensService.create(createTokenDto);
14 | }
15 |
16 | @Get()
17 | findAll() {
18 | return this.tokensService.findAll();
19 | }
20 |
21 | @Get(':id')
22 | findOne(@Param('id') id: string) {
23 | return this.tokensService.findOne(+id);
24 | }
25 |
26 | @Patch(':id')
27 | update(@Param('id') id: string, @Body() updateTokenDto: UpdateTokenDto) {
28 | return this.tokensService.update(+id, updateTokenDto);
29 | }
30 |
31 | @Delete(':id')
32 | remove(@Param('id') id: string) {
33 | return this.tokensService.remove(+id);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/oneapi/tokens/tokens.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TokensService } from './tokens.service';
3 | import { TokensController } from './tokens.controller';
4 | import { Token } from './entities/token.entity';
5 | import { TypeOrmModule } from '@nestjs/typeorm';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([Token])],
9 | controllers: [TokensController],
10 | providers: [TokensService],
11 | })
12 | export class TokensModule {}
13 |
--------------------------------------------------------------------------------
/src/oneapi/tokens/tokens.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { CreateTokenDto } from './dto/create-token.dto';
3 | import { UpdateTokenDto } from './dto/update-token.dto';
4 | import { InjectRepository } from '@nestjs/typeorm';
5 | import { Token } from './entities/token.entity';
6 | import { Repository } from 'typeorm';
7 |
8 | @Injectable()
9 | export class TokensService {
10 | constructor(
11 | @InjectRepository(Token)
12 | private readonly usersRepository: Repository,
13 | ) {}
14 | async create(createTokenDto: CreateTokenDto) {
15 | const res = await this.findOne(createTokenDto.user_id);
16 | console.log(res);
17 | if (res) {
18 | return res;
19 | } else {
20 | return await this.usersRepository.save(createTokenDto);
21 | }
22 |
23 | // return 'This action adds a new token';
24 | }
25 |
26 | findAll() {
27 | return `This action returns all tokens`;
28 | }
29 |
30 | async findOne(id: number) {
31 | return await this.usersRepository.findOneBy({ user_id: id });
32 | }
33 |
34 | update(id: number, updateTokenDto: UpdateTokenDto) {
35 | return `This action updates a #${id} token`;
36 | }
37 |
38 | remove(id: number) {
39 | return `This action removes a #${id} token`;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/oneapi/users/dto/create-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { OneAPIUser } from '../entities/user.entity';
2 |
3 | export class CreateUserDto extends OneAPIUser {}
4 |
--------------------------------------------------------------------------------
/src/oneapi/users/dto/update-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateUserDto } from './create-user.dto';
3 |
4 | export class UpdateUserDto extends PartialType(CreateUserDto) {}
5 |
--------------------------------------------------------------------------------
/src/oneapi/users/entities/user.entity.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | PrimaryGeneratedColumn,
5 | BaseEntity,
6 | } from 'typeorm';
7 | import { ApiProperty } from '@nestjs/swagger';
8 |
9 | @Entity({ name: 'users' })
10 | export class OneAPIUser extends BaseEntity {
11 | @PrimaryGeneratedColumn()
12 | id: number;
13 | @ApiProperty({
14 | description: 'username',
15 | example: 'zhangsan',
16 | })
17 | @Column()
18 | username: string;
19 | @ApiProperty({
20 | description: 'password',
21 | example: '123456',
22 | })
23 | @Column()
24 | password: string;
25 | @ApiProperty({
26 | description: '余额',
27 | example: '123456',
28 | })
29 | @Column()
30 | quota: number;
31 | @ApiProperty({
32 | description: '显示名称',
33 | example: 'zhangsan',
34 | })
35 | @Column({ nullable: true })
36 | display_name: string;
37 | @ApiProperty({
38 | description: '角色编号',
39 | example: '1',
40 | })
41 | @Column({ nullable: true })
42 | role: number;
43 | @ApiProperty({
44 | description: '状态',
45 | example: '1',
46 | })
47 | @Column({ nullable: true })
48 | status: number;
49 | @ApiProperty({
50 | description: '邮箱',
51 | example: 'nnheo@example.com',
52 | })
53 | @Column({ nullable: true })
54 | email: string;
55 | @ApiProperty({
56 | description: 'unionid',
57 | example: 'wxc1234567890',
58 | })
59 | @Column({ nullable: true })
60 | github_id: string; //用户唯一标识,微信uniid
61 | @ApiProperty({
62 | description: 'openid',
63 | example: 'wxopenidc1234567890',
64 | })
65 | @Column({ nullable: true })
66 | wechat_id: string;
67 | @ApiProperty({
68 | description: 'access_token',
69 | example: 'toen1234567890',
70 | })
71 | @Column({ nullable: true })
72 | access_token: string;
73 | @ApiProperty({
74 | description: '已使用流量',
75 | example: 100,
76 | })
77 | @Column({ nullable: true })
78 | used_quota: number;
79 | @ApiProperty({
80 | description: '请求次数',
81 | example: 100,
82 | })
83 | @Column({ nullable: true })
84 | request_count: number;
85 | @ApiProperty({
86 | description: '分组',
87 | example: '100',
88 | })
89 | @Column({ nullable: true })
90 | group: string;
91 | @Column({ nullable: true })
92 | aff_code: string;
93 | @Column({ nullable: true })
94 | inviter_id: number;
95 | }
96 |
--------------------------------------------------------------------------------
/src/oneapi/users/users.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Controller,
3 | Get,
4 | Post,
5 | Body,
6 | Patch,
7 | Param,
8 | Delete,
9 | } from '@nestjs/common';
10 | import { OneAPIUsersService } from './users.service';
11 | import { ApiOperation, ApiTags } from '@nestjs/swagger';
12 | import { OneAPIUser } from './entities/user.entity';
13 |
14 | @ApiTags('OneAPI')
15 | @Controller('oneapi/users')
16 | export class UsersController {
17 | constructor(private readonly oneAPIUsersService: OneAPIUsersService) {}
18 |
19 | @Post()
20 | create(@Body() user: OneAPIUser) {
21 | return this.oneAPIUsersService.create(user);
22 | }
23 |
24 | @ApiOperation({
25 | summary: '创建ONEAPI用户',
26 | description: '根据微信uniId传建用户,如果存在则直接返回,没有就重新创建',
27 | })
28 | @Post('creatbyuniId')
29 | async CreateByUniId(@Body() user: OneAPIUser) {
30 | return await this.oneAPIUsersService.CreateByUniId(user);
31 | }
32 |
33 | @ApiOperation({
34 | summary: '获取所有用户',
35 | description: 'Get all users',
36 | })
37 | @Get()
38 | findAll() {
39 | return this.oneAPIUsersService.findAll();
40 | }
41 |
42 | @ApiOperation({
43 | summary: '根据id获取用户',
44 | description: 'Get user by id',
45 | })
46 | @Get(':id')
47 | findOne(@Param('id') id: string) {
48 | return this.oneAPIUsersService.findOne(+id);
49 | }
50 |
51 | @ApiOperation({
52 | summary: '更新用户',
53 | description: 'Update user by id',
54 | })
55 | @Patch(':id')
56 | update(@Param('id') id: string, @Body() user: OneAPIUser) {
57 | return this.oneAPIUsersService.update(+id, user);
58 | }
59 |
60 | @ApiOperation({
61 | summary: '删除用户',
62 | description: 'Delete user by id',
63 | })
64 | @Delete(':id')
65 | remove(@Param('id') id: string) {
66 | return this.oneAPIUsersService.remove(+id);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/oneapi/users/users.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { OneAPIUsersService } from './users.service';
3 | import { UsersController } from './users.controller';
4 | import { TypeOrmModule } from '@nestjs/typeorm';
5 | import { OneAPIUser } from './entities/user.entity';
6 |
7 | @Module({
8 | imports: [TypeOrmModule.forFeature([OneAPIUser])],
9 | controllers: [UsersController],
10 | providers: [OneAPIUsersService],
11 | exports: [OneAPIUsersService, TypeOrmModule],
12 | })
13 | export class OneAPIUsersModule {}
14 |
--------------------------------------------------------------------------------
/src/oneapi/users/users.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Logger } from '@nestjs/common';
2 | import { OneAPIUser } from './entities/user.entity';
3 |
4 | import { InjectRepository } from '@nestjs/typeorm';
5 | import { Repository } from 'typeorm';
6 |
7 | @Injectable()
8 | export class OneAPIUsersService {
9 | private readonly logger = new Logger(OneAPIUsersService.name);
10 | constructor(
11 | @InjectRepository(OneAPIUser)
12 | private readonly usersRepository: Repository,
13 | ) {}
14 | async create(user: OneAPIUser) {
15 | return await this.usersRepository.save(user);
16 | }
17 |
18 | findAll() {
19 | return this.usersRepository.find();
20 | }
21 |
22 | findOne(id: number) {
23 | return this.usersRepository.findOneBy({ id });
24 | }
25 | async findOneByUniId(github_id: string) {
26 | return await this.usersRepository.findOneBy({ github_id });
27 | }
28 |
29 | /**
30 | * 根据uniid创建用户,如果存在存在返回,没有就创建
31 | * @param user
32 | * @constructor
33 | */
34 | async CreateByUniId(user: OneAPIUser) {
35 | //根据id或者用户名查找
36 | const res =
37 | (await this.findOneByUniId(user.github_id)) ||
38 | (await this.usersRepository.findOneBy({
39 | username: user.username,
40 | }));
41 | console.log(res);
42 | if (res) {
43 | this.logger.log('用户已存在');
44 | return res;
45 | } else {
46 | this.logger.log('用户不存在,创建用户');
47 | return await this.create(user);
48 | }
49 | // return this.usersRepository.findOneBy({ id });
50 | }
51 |
52 | update(id: number, user: OneAPIUser) {
53 | this.usersRepository.update(id, user);
54 | return `This action updates a #${id} user`;
55 | }
56 |
57 | remove(id: number) {
58 | this.usersRepository.delete(id);
59 | return `This action removes a #${id} user`;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/tweet/dto/create-tweet.dto.ts:
--------------------------------------------------------------------------------
1 | import { Tweet } from '../entities/tweet.schema';
2 |
3 | export class CreateTweetDto extends Tweet{}
4 |
--------------------------------------------------------------------------------
/src/tweet/dto/update-tweet.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateTweetDto } from './create-tweet.dto';
3 |
4 | export class UpdateTweetDto extends PartialType(CreateTweetDto) {}
5 |
--------------------------------------------------------------------------------
/src/tweet/entities/tweet.schema.ts:
--------------------------------------------------------------------------------
1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
2 | import { HydratedDocument } from 'mongoose';
3 | import { ApiProperty } from '@nestjs/swagger';
4 |
5 | export type UserDocument = HydratedDocument;
6 | @Schema()
7 | export class Tweet {
8 | @Prop()
9 | @ApiProperty({
10 | description: 'user_id',
11 | example: '1234567890',
12 | })
13 | user_id: string;
14 | @Prop()
15 | models: object[];
16 | @Prop()
17 | transitions: object[];
18 | @Prop()
19 | effects: object[];
20 | @Prop()
21 | animations: object[];
22 | @Prop()
23 | @ApiProperty({
24 | description: '推文项目',
25 | example: [
26 | {
27 | projectName: 'projectName',
28 | Resolution_id: 0,
29 | cover_url: 'cover_url',
30 | full_contents: 'full_contents',
31 | useVideo: true,
32 | backgroundMusic_id: 1,
33 | contents: [
34 | {
35 | id: 1,
36 | content: 'content',
37 | prompts: 'prompts',
38 | images: ['images'],
39 | choose_image_index: 1,
40 | audio: ['audio'],
41 | isTalk: true,
42 | role: 'role',
43 | scene_id: 1,
44 | video: 'video',
45 | transition: 'transition',
46 | video_effect: 'video_effect',
47 | animation: 'animation',
48 | },
49 | ],
50 | },
51 | ],
52 | })
53 | projects: TweetProject[];
54 | @Prop()
55 | roles: object[];
56 | @Prop()
57 | scenes: object[];
58 | }
59 |
60 | export interface TweetProject {
61 | projectName: string;
62 | Resolution_id: number;
63 | cover_url: string;
64 | full_contents: string;
65 | useVideo: boolean;
66 | backgroundMusic_id: number;
67 | contents: TweetData[];
68 | }
69 |
70 | // 工作空间的数据接口
71 | export interface TweetData {
72 | id?: number;
73 | content?: string;
74 | prompts?: string;
75 | images: string[];
76 | choose_image_index?: number;
77 | audio: string[];
78 | isTalk?: boolean; //是否是对话
79 | role?: string; //人物名称
80 | scene_id?: number; //场景id
81 | video: string;
82 | transition?: string;
83 | video_effect?: string;
84 | animation?: string;
85 | }
86 | export const TweetSchema = SchemaFactory.createForClass(Tweet);
87 |
--------------------------------------------------------------------------------
/src/tweet/tweet.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
2 | import { TweetService } from './tweet.service';
3 | import { CreateTweetDto } from './dto/create-tweet.dto';
4 | import { UpdateTweetDto } from './dto/update-tweet.dto';
5 | import { TweetProject } from './entities/tweet.schema';
6 | import { ApiOperation } from '@nestjs/swagger';
7 |
8 | @Controller('tweet')
9 | export class TweetController {
10 | constructor(private readonly tweetService: TweetService) {}
11 | @ApiOperation({
12 | summary: '保存项目',
13 | description: '保存项目',
14 | })
15 | @Post('savePrject')
16 | savePrject(@Body() createTweetDto: CreateTweetDto) {
17 | return this.tweetService.savePrject(
18 | createTweetDto.user_id,
19 | createTweetDto.projects[0],
20 | );
21 | }
22 | @Get()
23 | findAll() {
24 | return this.tweetService.findAll();
25 | }
26 |
27 | @Get(':id')
28 | findOne(@Param('id') id: string) {
29 | return this.tweetService.findOne(id);
30 | }
31 |
32 | @Patch(':id')
33 | update(@Param('id') id: string, @Body() updateTweetDto: UpdateTweetDto) {
34 | return this.tweetService.update(+id, updateTweetDto);
35 | }
36 |
37 | @Delete(':id')
38 | remove(@Param('id') id: string) {
39 | return this.tweetService.remove(+id);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/tweet/tweet.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { TweetService } from './tweet.service';
3 | import { TweetController } from './tweet.controller';
4 | import { MongooseModule } from '@nestjs/mongoose';
5 | import { Tweet, TweetSchema } from './entities/tweet.schema';
6 |
7 | @Module({
8 | imports: [
9 | MongooseModule.forFeature([{ name: Tweet.name, schema: TweetSchema }]),
10 | ],
11 | controllers: [TweetController],
12 | providers: [TweetService],
13 | })
14 | export class TweetModule {}
15 |
--------------------------------------------------------------------------------
/src/tweet/tweet.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { CreateTweetDto } from './dto/create-tweet.dto';
3 | import { UpdateTweetDto } from './dto/update-tweet.dto';
4 | import { InjectModel } from '@nestjs/mongoose';
5 | import { Model } from 'mongoose';
6 | import { Tweet, TweetProject } from './entities/tweet.schema';
7 |
8 | @Injectable()
9 | export class TweetService {
10 | constructor(@InjectModel(Tweet.name) private tweetModel: Model) {}
11 | create(createTweetDto: CreateTweetDto) {
12 | const newTweet = new this.tweetModel(createTweetDto);
13 | return newTweet.save();
14 | }
15 |
16 | /**
17 | * 保存项目
18 | * @param user_id
19 | * @param project
20 | */
21 | async savePrject(user_id: string, project: TweetProject) {
22 | //首先看是否有记录存在
23 | const _tweet = await this.findOne(user_id);
24 | if (_tweet) {
25 | //有记录存在,则更新
26 | //根据项目名称判断,当前项目是否存在
27 | const _project = _tweet.projects.find(
28 | (item) => item.projectName === project.projectName,
29 | );
30 | if (_project) {
31 | //存在,则更新
32 | return this.tweetModel.updateOne(
33 | { user_id, 'projects.projectName': _project.projectName },
34 | {
35 | $set: {
36 | 'projects.$': project,
37 | },
38 | },
39 | );
40 | } else {
41 | //如果项目名称不存在,则添加
42 | return this.tweetModel.updateOne(
43 | { user_id },
44 | {
45 | $push: {
46 | projects: project,
47 | },
48 | },
49 | );
50 | }
51 | }
52 | return this.tweetModel.create({ user_id, projects: [project] });
53 | }
54 |
55 | /**
56 | * 根据user_id获取项目所有的历史项目
57 | */
58 | async getData(user_id: string) {
59 | const _tweet = await this.findOne(user_id);
60 | if (_tweet) {
61 | return _tweet;
62 | }
63 | return null;
64 | }
65 |
66 | findAll() {
67 | return this.tweetModel.find();
68 | }
69 |
70 | async findOne(id: string) {
71 | return await this.tweetModel.findOne({ user_id: id }).exec();
72 | }
73 |
74 | update(id: number, updateTweetDto: UpdateTweetDto) {
75 | return `This action updates a #${id} tweet`;
76 | }
77 |
78 | remove(id: number) {
79 | return `This action removes a #${id} tweet`;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/users/dto/create-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { User } from '../schema/user.schema';
2 |
3 | export class CreateUserDto extends User{}
4 |
--------------------------------------------------------------------------------
/src/users/dto/update-user.dto.ts:
--------------------------------------------------------------------------------
1 | import { PartialType } from '@nestjs/swagger';
2 | import { CreateUserDto } from './create-user.dto';
3 |
4 | export class UpdateUserDto extends PartialType(CreateUserDto) {}
5 |
--------------------------------------------------------------------------------
/src/users/schema/user.schema.ts:
--------------------------------------------------------------------------------
1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
2 | import { HydratedDocument } from 'mongoose';
3 | import { ApiProperty } from '@nestjs/swagger';
4 |
5 | export type UserDocument = HydratedDocument;
6 |
7 | @Schema()
8 | export class User {
9 | @Prop()
10 | @ApiProperty({
11 | description: '昵称',
12 | example: '张三',
13 | })
14 | nickname: string;
15 | @ApiProperty({
16 | description: '密码',
17 | example: '123456',
18 | })
19 | @Prop()
20 | password: string;
21 | @ApiProperty({
22 | description: '用户名',
23 | example: 'wechat_abdfkdfjdkfdkfjjkdjf',
24 | })
25 | @Prop()
26 | username: string;
27 | @ApiProperty({
28 | description: '微信openid',
29 | example: 'abdfkdfjdkfdkfjjkdjf',
30 | })
31 | @Prop()
32 | wx_openid: string;
33 | @ApiProperty({
34 | description: '微信unionid',
35 | example: 'abdfkdfjdkfdkfjjkdjf',
36 | })
37 | @Prop()
38 | wx_unionid: string;
39 | @ApiProperty({
40 | description: '微信头像',
41 | example: 'abdfkdfjdkfdkfjjkdjf',
42 | })
43 | @Prop()
44 | avatar_url: string; //头像
45 | @Prop()
46 | @ApiProperty({
47 | description: '邮箱',
48 | example: 'efpyi@example.com',
49 | })
50 | email: string;
51 | @Prop()
52 | @ApiProperty({
53 | description: '邀请人id',
54 | example: 'UVGTBDS',
55 | })
56 | inviter_uid: string;
57 | @Prop()
58 | @ApiProperty({
59 | description: '最后登录时间',
60 | example: '121212133900',
61 | })
62 | last_login_date: number;
63 | @Prop()
64 | @ApiProperty({
65 | description: '注册时间',
66 | example: '121212133900',
67 | })
68 | register_date: number;
69 | @Prop()
70 | @ApiProperty({
71 | description: '最后登录ip',
72 | example: '127.0.0.1',
73 | })
74 | last_login_ip: string;
75 | @Prop()
76 | @ApiProperty({
77 | description: '手机号',
78 | example: '13888888888',
79 | })
80 | mobile: string;
81 | @Prop()
82 | @ApiProperty({
83 | description: '邀请码',
84 | example: '123456',
85 | })
86 | my_invite_code: string;
87 | @Prop()
88 | @ApiProperty({
89 | description: '角色',
90 | example: ['admin', 'user'],
91 | })
92 | role: Array;
93 | @Prop()
94 | @ApiProperty({
95 | description: 'token',
96 | example: '2121213424gfgdfdfdfsdfsdfdffg',
97 | })
98 | token: string;
99 | }
100 |
101 | export const UserSchema = SchemaFactory.createForClass(User);
102 |
--------------------------------------------------------------------------------
/src/users/users.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Query,
3 | Controller,
4 | Get,
5 | Post,
6 | Body,
7 | Patch,
8 | Param,
9 | Delete,
10 | } from '@nestjs/common';
11 | import { UsersService } from './users.service';
12 | import { UpdateUserDto } from './dto/update-user.dto';
13 | import { CreateUserDto } from './dto/create-user.dto';
14 | import { ApiTags, ApiOperation } from '@nestjs/swagger';
15 | import { AuthGuard } from '../auth/auth.guard';
16 | import { ApiBearerAuth } from '@nestjs/swagger';
17 |
18 | @ApiTags('用户管理')
19 | @Controller('users')
20 | export class UsersController {
21 | constructor(
22 | private readonly usersService: UsersService,
23 | ) {
24 | }
25 | @Post()
26 | createUser(@Body() createUserDto: CreateUserDto) {
27 | return this.usersService.create(createUserDto);
28 | }
29 | @ApiOperation({
30 | summary: '微信登录',
31 | description: '微信登录',
32 | })
33 | @Post('loginBywechat')
34 | loginBywechat(@Body() createUserDto: CreateUserDto) {
35 | return this.usersService.loginBywechat(createUserDto);
36 | }
37 |
38 | @ApiOperation({
39 | summary: '用户名注册',
40 | description: '根据用户名注册',
41 | })
42 | @Post('registerByUsername')
43 | registerByUsername(@Body() createUserDto: CreateUserDto) {
44 | return this.usersService.registerByUsername(createUserDto);
45 | }
46 |
47 | @ApiOperation({
48 | summary: '用户名登录',
49 | description: '根据用户名登录',
50 | })
51 | @Post('loginByUsername')
52 | loginByUsername(@Body() createUserDto: CreateUserDto) {
53 | return this.usersService.loginByUsername(createUserDto);
54 | }
55 |
56 | @Get()
57 | findAll() {
58 | return this.usersService.findAll();
59 | }
60 |
61 | @Get(':id')
62 | findOne(@Param('id') id: string) {
63 | return this.usersService.findOne(id);
64 | }
65 |
66 | @Patch(':id')
67 | update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
68 | return this.usersService.update(id, updateUserDto);
69 | }
70 |
71 | @Delete(':id')
72 | remove(@Param('id') id: string) {
73 | return this.usersService.remove(id);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/users/users.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { UsersService } from './users.service';
3 | import { UsersController } from './users.controller';
4 | import { MongooseModule } from '@nestjs/mongoose';
5 | import { User, UserSchema } from './schema/user.schema';
6 | import { CacheModule } from '../cache/cache.module';
7 |
8 | @Module({
9 | imports: [
10 | MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
11 | ],
12 | controllers: [UsersController],
13 | providers: [UsersService],
14 | exports: [UsersService, MongooseModule],
15 | })
16 | export class UsersModule {}
17 |
--------------------------------------------------------------------------------
/src/users/users.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { CreateUserDto } from './dto/create-user.dto';
3 | import { InjectModel } from '@nestjs/mongoose';
4 | import { Model } from 'mongoose';
5 | import { User } from './schema/user.schema';
6 | import { UpdateUserDto } from './dto/update-user.dto';
7 | import * as bcrypt from 'bcrypt';
8 | import { JwtService } from '@nestjs/jwt';
9 |
10 | /**
11 | * 注册结果消息接口
12 | */
13 | export type RegistRes = {
14 | status: number;
15 | msg: string;
16 | data: User;
17 | };
18 |
19 | @Injectable()
20 | export class UsersService {
21 | constructor(
22 | @InjectModel(User.name) private userModel: Model,
23 | private readonly jwtService: JwtService,
24 | ) {}
25 |
26 | /**
27 | * 注意,账号创建不做用户验证
28 | * @param createUserDto
29 | */
30 | async create(createUserDto: CreateUserDto) {
31 | //根据username查找用户是否存在
32 | // let user;
33 | // // 用户存在
34 | // if (createUserDto.username) {
35 | // user = await this.findByUsername(createUserDto.username);
36 | // }
37 | // // 微信ID存在
38 | // if (createUserDto.wx_unionid) {
39 | // user = await this.findByWxUnionid(createUserDto.wx_unionid);
40 | // }
41 | // // 邮箱存在
42 | // if (createUserDto.email) {
43 | // user = await this.findByEmail(createUserDto.email);
44 | // }
45 | // if (user) {
46 | // console.log('user:', user);
47 | // return user;
48 | // }
49 | //根据email查找用户是否存在
50 | // const emailUser = await this.findByEmail(createUserDto.email);
51 | // if (emailUser) {
52 | // console.log('emailUser:', emailUser);
53 | // return emailUser;
54 | // }
55 | createUserDto.password = await this.hashPassword(createUserDto.password);
56 | const createdUser = new this.userModel(createUserDto);
57 | return createdUser.save();
58 | }
59 |
60 | /**
61 | * 注册
62 | */
63 | async registerByUsername(createUserDto: CreateUserDto) {
64 | const { username } = createUserDto;
65 | console.log(username);
66 | if (username) {
67 | const user = await this.findByUsername(username);
68 | console.log(user);
69 | if (user) {
70 | return {
71 | status: 1,
72 | msg: '用户名已存在',
73 | data: null,
74 | } as RegistRes;
75 | }
76 | const createdUser = await this.create(createUserDto);
77 | //更新token,并返回User
78 | await this.updateToken(createdUser._id, createdUser.username);
79 | return {
80 | status: 0,
81 | msg: '注册成功',
82 | data: await this.findOne(createdUser._id + ''),
83 | } as RegistRes;
84 | }
85 | }
86 |
87 | /**
88 | * 根据unionid实现微信登录,如果不存在就创建账号
89 | * @param createUserDto
90 | */
91 | async loginBywechat(createUserDto: CreateUserDto) {
92 | const res = await this.findByWxUnionid(createUserDto.wx_unionid);
93 | return res ? res : await this.create(createUserDto);
94 | }
95 |
96 | /**
97 | * login,通过账号密码登录
98 | */
99 | async loginByUsername(createUserDto: CreateUserDto) {
100 | const { username, password } = createUserDto;
101 | const user = await this.findByUsername(username);
102 | if (!user) {
103 | return {
104 | status: 1,
105 | msg: '用户名不存在',
106 | data: null,
107 | } as RegistRes;
108 | }
109 | const isMatch = await this.comparePassword(password, user.password);
110 | if (!isMatch) {
111 | return {
112 | status: 1,
113 | msg: '密码错误',
114 | data: null,
115 | } as RegistRes;
116 | }
117 | return {
118 | status: 0,
119 | msg: '登录成功',
120 | data: await this.updateToken(user._id, user.username),
121 | } as RegistRes;
122 | }
123 |
124 | /**
125 | * 根据加密的密码和实现自动登录
126 | */
127 | async autoLogin(user: User) {
128 | const { password } = user;
129 | }
130 |
131 | findAll() {
132 | return this.userModel.find().exec();
133 | }
134 |
135 | findOne(id: string) {
136 | return this.userModel.findById(id).exec();
137 | }
138 |
139 | async findByUsername(username: string) {
140 | return await this.userModel.findOne({ username }).exec();
141 | }
142 |
143 | async findByEmail(email: string) {
144 | return await this.userModel.findOne({ email }).exec();
145 | }
146 |
147 | async findByWxUnionid(wx_unionid: string) {
148 | return await this.userModel.findOne({ wx_unionid }).exec();
149 | }
150 |
151 | async update(id: string, updateUserDto: UpdateUserDto) {
152 | // const updateUser = new this.userModel(updateUserDto);
153 | return await this.userModel.findByIdAndUpdate(id, updateUserDto);
154 | }
155 |
156 | async remove(id: string) {
157 | return await this.userModel.findByIdAndDelete(id);
158 | }
159 |
160 | private iv = null as Buffer;
161 | private key = null as Buffer;
162 |
163 | async hashPassword(password: string) {
164 | const saltOrRounds = 10;
165 | return await bcrypt.hash(password, saltOrRounds);
166 | }
167 |
168 | async comparePassword(password: string, hash: string) {
169 | try {
170 | return await bcrypt.compare(password, hash);
171 | } catch (e) {
172 | throw new Error(e);
173 | }
174 | }
175 |
176 | /**
177 | * 用户id,用户名生成token
178 | * @param _id
179 | * @param username
180 | */
181 | async createToken(_id: string, username: string) {
182 | const payload = {
183 | sub: _id,
184 | username,
185 | };
186 | return await this.jwtService.signAsync(payload);
187 | }
188 |
189 | /**
190 | * 更新token
191 | * @param _id
192 | * @param username
193 | */
194 | async updateToken(_id, username) {
195 | return this.userModel.findByIdAndUpdate(_id, {
196 | token: await this.createToken(_id, username),
197 | });
198 | }
199 |
200 | /**
201 | * 根据token获取用户信息,可以用validateToken替代这个函数功能,也能返回最终的User
202 | * @param token
203 | */
204 | private async getUserInfoByToken(token: string) {
205 | const payload = await this.jwtService.decode(token);
206 | return await this.userModel.findById(payload['sub']);
207 | }
208 |
209 | /**
210 | * 验证token有效性
211 | */
212 | async validateToken(token: string): Promise {
213 | const { sub, exp } = await this.jwtService.decode(token);
214 | if (exp < Math.floor(Date.now() / 1000)) {
215 | return false;
216 | }
217 | if (!sub) {
218 | return false;
219 | }
220 | return await this.userModel.findById(sub);
221 | }
222 |
223 | }
224 |
--------------------------------------------------------------------------------
/src/wechat-auth/wechat-auth.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Body,
3 | Controller,
4 | Get,
5 | HttpCode,
6 | Logger,
7 | Post,
8 | Query,
9 | Req,
10 | Res,
11 | } from '@nestjs/common';
12 | import e, { Request, Response } from 'express';
13 | import { WechatAuthService } from './wechat-auth.service';
14 | import { ApiOperation, ApiTags } from '@nestjs/swagger';
15 | import { create } from 'xmlbuilder2';
16 | import { DrawService, WeChatDrawModel } from '../draw/draw.service';
17 | import { UsersService } from '../users/users.service';
18 |
19 | @ApiTags('微信相关')
20 | @Controller('wechatauth')
21 | export class WechatAuthController {
22 | constructor(
23 | private readonly wechatAuthService: WechatAuthService,
24 | private readonly drawService: DrawService,
25 | private readonly usersService: UsersService,
26 | ) {}
27 |
28 | private readonly logger = new Logger(WechatAuthController.name);
29 |
30 | @ApiOperation({
31 | summary: '获取微信用户信息',
32 | description: '根据服务返回的code码,获取微信用户的信息',
33 | operationId: 'getUserInfo',
34 | tags: ['wechat'],
35 | externalDocs: {
36 | description: 'wechat',
37 | url: 'https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html',
38 | },
39 | })
40 | @Get('getuserinfo')
41 | async getUserInfo(@Query('code') code: string) {
42 | this.logger.log(code);
43 | return await this.wechatAuthService.getUserinfo(code);
44 | }
45 |
46 | @ApiOperation({
47 | summary: '微信登录',
48 | description: '根据微信的openid unionid headimgurl nickname自动登录',
49 | operationId: 'loginByOpenid',
50 | tags: ['wechat'],
51 | externalDocs: {
52 | description: 'wechat',
53 | url: 'https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html',
54 | },
55 | requestBody: {
56 | description: 'openid unionid headimgurl nickname',
57 | content: {
58 | 'application/json': {
59 | schema: {
60 | type: 'object',
61 | properties: {
62 | openid: {
63 | type: 'string',
64 | description: 'openid',
65 | example: 'o6_bmjrPTlm6_2sgVt7hMZOPfL2M',
66 | },
67 | unionid: {
68 | type: 'string',
69 | description: 'unionid',
70 | example: 'o6_bjrPTlm6_2sgVt7hMZOPfL2M',
71 | },
72 | headimgurl: {
73 | type: 'string',
74 | description: '头像',
75 | example:
76 | 'http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46',
77 | },
78 | nickname: {
79 | type: 'string',
80 | description: '昵称',
81 | example: 'Band',
82 | },
83 | },
84 | },
85 | },
86 | },
87 | },
88 | })
89 | @Post('loginByOpenid')
90 | async loginByOpenid(
91 | @Body('openid') openid: string,
92 | @Body('unionid') unionid: string,
93 | @Body('headimgurl') headimgurl: string,
94 | @Body('nickname') nickname: string,
95 | ) {
96 | console.log('openid unionid' + openid + unionid);
97 | const user = await this.usersService.loginBywechat({
98 | wx_openid: openid,
99 | wx_unionid: unionid,
100 | nickname: nickname,
101 | avatar_url: headimgurl,
102 | email: '',
103 | inviter_uid: '',
104 | last_login_date: Date.now(),
105 | last_login_ip: '',
106 | mobile: '',
107 | my_invite_code: '',
108 | password: await this.usersService.hashPassword('123456'),
109 | register_date: 0,
110 | role: undefined,
111 | token: '',
112 | username: `wechat_${openid}`,
113 | });
114 | const { _id, username } = user;
115 | //更新toekn
116 | await this.usersService.updateToken(_id, username);
117 | return await this.usersService.findOne(_id + '');
118 | }
119 |
120 | /**
121 | * 处理微信公众号号消息
122 | * @param xml
123 | * @param req
124 | * @param res
125 | */
126 | @ApiOperation({
127 | summary: '处理微信公众号号消息',
128 | description: 'xml',
129 | operationId: 'hangleMessage',
130 | tags: ['wechat'],
131 | externalDocs: {
132 | description: 'wechat',
133 | url: 'https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html',
134 | },
135 | requestBody: {
136 | description: 'xml',
137 | content: {
138 | 'application/xml': {
139 | schema: {
140 | type: 'object',
141 | properties: {
142 | xml: {
143 | type: 'string',
144 | description: 'xml',
145 | example:
146 | '15824045432250878104354478081',
147 | },
148 | },
149 | },
150 | },
151 | },
152 | },
153 | })
154 | @HttpCode(200)
155 | @Post('handleMessage')
156 | async hangleMessage(
157 | @Body() xml: any,
158 | @Req() req: Request,
159 | @Res() res: Response,
160 | ) {
161 | this.logger.log('收到微信服务器转发的消息')
162 | const {
163 | xml: {
164 | ToUserName: [to],
165 | FromUserName: [from],
166 | MsgType: [type],
167 | },
168 | } = xml;
169 | //处理消息参数
170 |
171 | let drawmodel = 'text2img' as WeChatDrawModel;
172 | const params = {
173 | positive: 'a girl',
174 | ckpt_name_id: 0, //使用的大模型id,调用参数参考API文档
175 | image_path: '',
176 | // 其他参数可以加载后面
177 | };
178 | // 根据消息类型,调用不同的绘画接口
179 | if (type === 'text') {
180 | //文生图
181 | const {
182 | xml: {
183 | // ToUserName: [to],
184 | FromUserName: [from],
185 | // MsgType: [type],
186 | Content: [content],
187 | },
188 | } = xml;
189 | if (content.includes('图生图') || content.includes('图生视频')) {
190 | //将用户的指令先暂存起来,保存到redis
191 | await this.wechatAuthService.saveCommand(content, from);
192 | // 回复客户端响应消息
193 | const resResult = create({
194 | xml: {
195 | ToUserName: from, // 接收方帐号(收到的OpenID)
196 | FromUserName: to, // 开发者微信号
197 | CreateTime: new Date().getTime(), // 消息创建时间 (整型)
198 | MsgType: 'text',
199 | Content:
200 | '已收到主人的绘画指令,请传入一张图片,我可以帮你完成' + content,
201 | },
202 | }).end({ prettyPrint: true });
203 |
204 | res.type('application/xml');
205 | res.send(resResult);
206 | return;
207 | } else {
208 | // 回复客户端响应消息
209 | drawmodel = 'text2img';
210 | const resResult = create({
211 | xml: {
212 | ToUserName: from, // 接收方帐号(收到的OpenID)
213 | FromUserName: to, // 开发者微信号
214 | CreateTime: new Date().getTime(), // 消息创建时间 (整型)
215 | MsgType: 'text',
216 | Content: 'AI绘图中,请稍后',
217 | },
218 | }).end({ prettyPrint: true });
219 |
220 | res.type('application/xml');
221 | res.send(resResult);
222 | }
223 | params.positive = content;
224 | }
225 | if (type === 'image') {
226 | // 读取用户的指令
227 | const {
228 | xml: {
229 | // ToUserName: [to],
230 | FromUserName: [from],
231 | PicUrl: [imageurl],
232 | },
233 | } = xml;
234 | // 回复客户端响应消息
235 | const resResult = create({
236 | xml: {
237 | ToUserName: from, // 接收方帐号(收到的OpenID)
238 | FromUserName: to, // 开发者微信号
239 | CreateTime: new Date().getTime(), // 消息创建时间 (整型)
240 | MsgType: 'text',
241 | Content: 'AI绘图中,请稍后',
242 | },
243 | }).end({ prettyPrint: true });
244 |
245 | res.type('application/xml');
246 | res.send(resResult);
247 | params.image_path = imageurl;
248 | const command = await this.wechatAuthService.getCommand(from);
249 | if (command.includes('图生视频')) {
250 | drawmodel = 'img2video';
251 | } else {
252 | drawmodel = 'image2img';
253 | params.positive = command.replace('图生图', '');
254 | }
255 | }
256 | // 启用了远程绘画服务器
257 | if (this.drawService.remote_comfyui) {
258 | this.logger.log(`启动远程服务器绘画功能,绘画模式是${drawmodel}`);
259 | const result = await this.drawService.wechatDrawFromRemoteServer(
260 | drawmodel,
261 | params,
262 | );
263 | // 调用客服接口回复消息
264 | const mediaId = await this.wechatAuthService.getMediaId(
265 | result,
266 | drawmodel.includes('video') ? 'video' : 'image',
267 | );
268 | await this.wechatAuthService.sendServiceImageMessge(
269 | mediaId,
270 | from,
271 | drawmodel.includes('video') ? 'video' : 'image',
272 | );
273 | } else {
274 | // 本地绘画
275 | this.logger.log('启动本地绘画功能');
276 | if (drawmodel === 'text2img') {
277 | //文生图
278 | const result = await this.drawService.text2img(from, undefined, params);
279 | // 调用客服接口回复消息
280 | const mediaId = await this.wechatAuthService.getMediaId(result);
281 | await this.wechatAuthService.sendServiceImageMessge(mediaId, from);
282 | }
283 | if (drawmodel === 'image2img') {
284 | // 图生图
285 | const result = await this.drawService.image2img(
286 | from,
287 | undefined,
288 | params,
289 | );
290 | // 调用客服接口回复消息
291 | const mediaId = await this.wechatAuthService.getMediaId(result);
292 | await this.wechatAuthService.sendServiceImageMessge(mediaId, from);
293 | }
294 | if (drawmodel === 'img2video') {
295 | // 图生视频
296 | const result = await this.drawService.image2video(
297 | from,
298 | undefined,
299 | params,
300 | );
301 | // 调用客服接口回复消息
302 | const mediaId = await this.wechatAuthService.getMediaId(
303 | result,
304 | 'video',
305 | );
306 | await this.wechatAuthService.sendServiceImageMessge(
307 | mediaId,
308 | from,
309 | 'video',
310 | );
311 | }
312 | }
313 | }
314 |
315 | /**
316 | * 对接服务器验证
317 | * @param signature
318 | * @param timestamp
319 | * @param nonce
320 | * @param echostr
321 | */
322 | @ApiOperation({
323 | summary: '对接服务器验证',
324 | description: '自动对接微信服务器设置,微信服务器配置时,请选择明文模式',
325 | operationId: 'handleMessage',
326 | tags: ['wechat'],
327 | externalDocs: {
328 | description: 'wechat',
329 | url: 'https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html',
330 | },
331 | })
332 | @Get('handleMessage')
333 | receiveMessage(
334 | @Query('signature') signature: string,
335 | @Query('timestamp') timestamp: string,
336 | @Query('nonce') nonce: string,
337 | @Query('echostr') echostr: string,
338 | ) {
339 | // this.logger.log(echostr);
340 | return echostr || 'error';
341 | }
342 | }
343 |
--------------------------------------------------------------------------------
/src/wechat-auth/wechat-auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { WechatAuthService } from './wechat-auth.service';
3 | import { WechatAuthController } from './wechat-auth.controller';
4 | import { DrawModule } from '../draw/draw.module';
5 | // import { DrawService } from '../draw/DrawService';
6 | import { UsersModule } from '../users/users.module';
7 | import { FileModule } from '../file/file.module';
8 |
9 | @Module({
10 | imports: [DrawModule, UsersModule, FileModule],
11 | controllers: [WechatAuthController],
12 | providers: [WechatAuthService],
13 | exports: [WechatAuthService],
14 | })
15 | export class WechatAuthModule {}
16 |
--------------------------------------------------------------------------------
/src/wechat-auth/wechat-auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Logger } from '@nestjs/common';
2 | import axios from 'axios';
3 | import { ConfigService } from '@nestjs/config/dist';
4 | import { DrawService } from '../draw/draw.service';
5 | import { CacheService } from '../cache/cache.service';
6 | import { FileService } from '../file/file.service';
7 |
8 | @Injectable()
9 | export class WechatAuthService {
10 | constructor(
11 | // @InjectQueue('draw') private drawQueue: Queue,
12 | private readonly configService: ConfigService,
13 | private readonly drawService: DrawService,
14 | private cacheService: CacheService,
15 | private readonly fileService: FileService,
16 | ) {}
17 |
18 | private readonly logger = new Logger(WechatAuthService.name);
19 | private APPID = this.configService.get('CONFIG_AUTH_WECHAT_APPID');
20 | private SECRET = this.configService.get('CONFIG_AUTH_WECHAT_SECRET');
21 | private wx_baseurl = 'https://api.weixin.qq.com';
22 |
23 | // private uni_baseurl = 'https://unicloudapi.gptpro.ink'; //小程序账号登录链接
24 | /**
25 | *获取Access_token
26 | * @param code 用户扫码后获取到的code
27 | * @returns 返回相应结果,{"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE","unionid": "UNIONID"}
28 | */
29 | private async getAccess_token(code) {
30 | const access_url =
31 | 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' +
32 | this.APPID +
33 | '&secret=' +
34 | this.SECRET +
35 | '&code=' +
36 | code +
37 | '&grant_type=authorization_code';
38 | const access_res = await axios.get(access_url);
39 | return access_res.data;
40 | }
41 |
42 | /**
43 | *根据code获取用户信息
44 | * @param code 微信返回的code,页面参数
45 | * @returns
46 | */
47 | async getUserinfo(code: string) {
48 | this.logger.debug('传入的code', code);
49 | const Access_res = await this.getAccess_token(code);
50 | this.logger.debug('获取token', Access_res);
51 |
52 | const { access_token, openid } = Access_res;
53 | if (access_token) {
54 | const userinfo_url =
55 | 'https://api.weixin.qq.com/sns/userinfo?access_token=' +
56 | access_token +
57 | '&openid=' +
58 | openid;
59 | const { data } = await axios.get(userinfo_url);
60 | return data;
61 | } else {
62 | return null;
63 | }
64 | }
65 |
66 | /**
67 | * 上传图片文件作为临时素材,获取微信服务器的media_id
68 | * @param imageUrl
69 | * @param type
70 | */
71 | async getMediaId(imageUrl: string, type?: string): Promise {
72 | const access_token = await this.getAccessToken();
73 | const upload_url =
74 | this.wx_baseurl +
75 | `/cgi-bin/media/upload?access_token=${access_token}&type=${type || 'image'}`;
76 | const file = await this.fileService.urlToFile(
77 | imageUrl,
78 | type === 'video' ? 'video.mp4' : 'image.png',
79 | type === 'video' ? 'video/mp4' : 'image/png',
80 | );
81 | const formdata = new FormData();
82 | console.log('上传的文件', file);
83 | formdata.append(type || 'image', file);
84 | const res = await axios.post(upload_url, formdata, {
85 | headers: {
86 | 'Content-Type': 'multipart/form-data',
87 | },
88 | });
89 | console.log('上传微信服务返回的数据', res.data);
90 | const { media_id } = res.data;
91 | return media_id;
92 | }
93 |
94 | private draw_access_token = {
95 | token: '',
96 | expires_in: Date.now(),
97 | };
98 |
99 | /**
100 | * 获取微信公众号的token,如果token没有过期,直接使用,过期重新获取
101 | * @private
102 | */
103 | private async getAccessToken(): Promise {
104 | const APPID = this.configService.get('CONFIG_OFFICIAL_WECHAT_APPID');
105 | const AppSecret = this.configService.get('CONFIG_OFFICIAL_WECHAT_SECRET');
106 | const token_url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${AppSecret}`;
107 | if (
108 | !this.draw_access_token.token ||
109 | this.draw_access_token.expires_in <= Date.now()
110 | ) {
111 | // 获取access_token
112 | const resResult = await axios.get(token_url);
113 | console.log(resResult);
114 | const { access_token } = resResult.data;
115 | this.draw_access_token.token = access_token;
116 | this.draw_access_token.expires_in = Date.now() + 7000 * 1000;
117 | return access_token;
118 | }
119 | if (
120 | this.draw_access_token.token &&
121 | this.draw_access_token.expires_in > Date.now()
122 | ) {
123 | return this.draw_access_token.token;
124 | }
125 | }
126 |
127 | /**
128 | * 发送客服图片消息
129 | * @param media_id
130 | * @param touser
131 | * @param msgtype
132 | */
133 | async sendServiceImageMessge(
134 | media_id: string,
135 | touser: string,
136 | msgtype?: string,
137 | ) {
138 | const access_token = await this.getAccessToken();
139 | const url = `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${access_token}`;
140 | let message;
141 | if (msgtype === 'video') {
142 | message = {
143 | touser: touser,
144 | msgtype: 'video',
145 | video: {
146 | media_id: media_id,
147 | title: 'AI视频',
148 | description: '视频由AI生成',
149 | },
150 | };
151 | } else {
152 | message = {
153 | touser: touser,
154 | msgtype: 'image',
155 | image: {
156 | media_id: media_id,
157 | },
158 | };
159 | }
160 |
161 | const res = await axios.post(url, message);
162 | console.log('发送结果:', res.data);
163 | }
164 |
165 | /**
166 | * 保存用户的指令
167 | */
168 | async saveCommand(command: string, openid: string) {
169 | const key = `command:${openid}`;
170 | await this.cacheService.set(key, command);
171 | }
172 |
173 | /**
174 | * 获取用户的指令
175 | */
176 | async getCommand(openid: string) {
177 | const key = `command:${openid}`;
178 | return await this.cacheService.get(key);
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/ws/ws.gateway.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SubscribeMessage,
3 | WebSocketGateway,
4 | OnGatewayInit,
5 | OnGatewayConnection,
6 | } from '@nestjs/websockets';
7 | import { Logger } from '@nestjs/common';
8 | import { Socket } from 'socket.io';
9 | import { DrawService } from '../draw/draw.service';
10 | import { DrawTask } from '../draw/data/DrawConfig';
11 | @WebSocketGateway(3002, {
12 | // 解决跨域
13 | allowEIO3: true,
14 | cors: {
15 | origin: '*',
16 | },
17 | })
18 | export class WsGateway implements OnGatewayInit, OnGatewayConnection {
19 | constructor(
20 | private readonly drawService: DrawService, //绘画任务队列
21 | ) {}
22 |
23 | private readonly logger = new Logger(WsGateway.name);
24 |
25 | afterInit(server: any) {
26 | this.logger.log(`WebSocket server started on port ${server}`);
27 | WsGateway.server = server;
28 | }
29 |
30 | public static server;
31 |
32 | @SubscribeMessage('connection')
33 | handleConnection(client: Socket) {
34 | this.logger.log(client.id, '@连接到了服务器');
35 | }
36 |
37 | @SubscribeMessage('message')
38 | handleMessage(client: any, payload: any): string {
39 | this.logger.log('来自客户端:' + client.id + '@@@@@@@的消息:' + payload);
40 | return 'Hello world1!';
41 | }
42 |
43 | @SubscribeMessage('draw')
44 | async handleDrawMessage(client: any, payload: any): Promise {
45 | this.logger.log('来自客户端' + client.id + '发来绘画指令');
46 |
47 | const { client_id, prompt, api } = payload;
48 | //黑名单,如果满足条件发送系统消息给客户端
49 | const userList = ['65dab231189f86e370b2967a'];
50 | if (client_id && userList.includes(client_id)) {
51 | //返送警告信息
52 | this.sendSystemMessage(
53 | client.id,
54 | '您已被加入黑名单,请勿再次发送绘画指令',
55 | );
56 | return;
57 | }
58 | // AI推文不限制加入队列
59 | if ((await this.drawService.isInQueue(client_id)) && api != 'AI推文') {
60 | const message = {
61 | type: 'reject',
62 | queue_remaining: await this.drawService.getQueueLength(),
63 | };
64 | client.emit('message', JSON.stringify(message));
65 | } else {
66 | //加入队列
67 | const data = {
68 | source: 'web',
69 | client_id,
70 | prompt,
71 | api,
72 | socket_id: client.id,
73 | } as DrawTask;
74 | await this.drawService.sendToQueue(data);
75 | //回复消息给客户端
76 | const message = {
77 | type: 'receive',
78 | queue_remaining: await this.drawService.getQueueLength(),
79 | };
80 | client.emit('message', JSON.stringify(message));
81 | }
82 | return 'Hello world1!';
83 | }
84 |
85 | /**
86 | * 发送系统消息
87 | */
88 | sendSystemMessage(socketid: string, message: string) {
89 | const sysinfo = {
90 | message: message,
91 | type: 'warning',
92 | timeout: -1,
93 | };
94 | const client = WsGateway.server.sockets.connected[socketid];
95 | if (client) {
96 | client.emit('sysinfo', sysinfo);
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "allowSyntheticDefaultImports": true,
9 | "target": "ES2021",
10 | "sourceMap": true,
11 | "outDir": "./dist",
12 | "baseUrl": "./",
13 | "incremental": true,
14 | "skipLibCheck": true,
15 | "strictNullChecks": false,
16 | "noImplicitAny": false,
17 | "strictBindCallApply": false,
18 | "forceConsistentCasingInFileNames": false,
19 | "noFallthroughCasesInSwitch": false,
20 | "resolveJsonModule":true,
21 | }
22 | }
23 |
--------------------------------------------------------------------------------