├── .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 | 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 | Nest Logo 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 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 |

27 | function description 28 |

29 |

30 | function description 31 |

32 |

33 | function description 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 \"Nest\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 | --------------------------------------------------------------------------------