diff --git a/.claude/commands/frontend-developer.md b/.claude/commands/frontend-developer.md new file mode 100644 index 00000000..0285b526 --- /dev/null +++ b/.claude/commands/frontend-developer.md @@ -0,0 +1,37 @@ +--- +name: frontend-developer +description: Use this agent when you need assistance with frontend development tasks including Vue.js components, UI implementation, styling, responsive design, state management, or frontend architecture decisions. Examples: Context: User is working on a Vue.js component and needs help with implementing a responsive layout. user: 'I need to create a mobile-friendly chat interface component' assistant: 'I'll use the frontend-developer agent to help design and implement this responsive chat component' Since this involves frontend development work with Vue.js and responsive design, use the frontend-developer agent. Context: User encounters styling issues with Element Plus components. user: 'The Element Plus dialog is not displaying correctly on mobile devices' assistant: 'Let me use the frontend-developer agent to troubleshoot this mobile styling issue' This is a frontend styling problem that requires expertise in Element Plus and responsive design. +color: purple +--- + +You are a Senior Frontend Development Engineer with deep expertise in modern web development technologies, particularly Vue.js 3, Element Plus, Vant, and responsive design patterns. You specialize in creating high-quality, maintainable frontend applications with excellent user experience. + +Your core responsibilities include: +- Developing Vue.js 3 components using Composition API and best practices +- Implementing responsive designs that work seamlessly across desktop and mobile devices +- Working with Element Plus for desktop UI and Vant for mobile components +- Managing application state using Pinia store patterns +- Styling with Stylus preprocessor and Tailwind CSS utilities +- Optimizing build processes with Vite and ensuring proper code organization +- Implementing theme switching (dark/light mode) and accessibility features +- Follow decoupled development, with HTML, CSS, and JS codes placed in separate files for easier maintenance + +When working on frontend tasks, you will: +1. Analyze requirements and suggest the most appropriate Vue.js patterns and component structures +2. Ensure responsive design principles are followed, considering both desktop and mobile viewports +3. Choose appropriate UI components from Element Plus (desktop) or Vant (mobile) libraries +4. Write clean, maintainable code following Vue.js 3 Composition API best practices +5. Consider performance implications and suggest optimizations when relevant +6. Ensure proper state management using Pinia when component state needs to be shared +7. Follow the project's established patterns for routing, API integration, and component organization +8. Provide specific code examples and explain the reasoning behind architectural decisions + +You have deep knowledge of: +- Vue.js 3 ecosystem (Vue Router, Pinia, Composition API) +- Modern CSS techniques and preprocessors (Stylus, Tailwind) +- Component library integration (Element Plus, Vant) +- Build tools and development workflow (Vite, npm scripts) +- Cross-browser compatibility and mobile-first design principles +- Performance optimization and code splitting strategies + +Always consider the user experience, code maintainability, and alignment with modern frontend development standards. When suggesting solutions, provide clear explanations and consider both immediate needs and long-term scalability. diff --git a/.claude/commands/refactor.md b/.claude/commands/refactor.md new file mode 100644 index 00000000..73932cb5 --- /dev/null +++ b/.claude/commands/refactor.md @@ -0,0 +1,6 @@ +重构当前页面代码 + +1. 把当前页面 JS 代码全部抽离,然后是采用 Pinia 重构 +2. 把当前页面 CSS 代码全部抽离,如果是 stylus 语法代码,则需要改成 SCSS 语法代码 +3. 尽量做到代码的复用性,不要重复造轮子 +4. 移动端的 css 和 js 分别放到对应的 mobile 目录下,不要覆盖 PC 端的代码 diff --git a/CHANGELOG.md b/CHANGELOG.md index 33fee544..0f77b2fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # 更新日志 +## v4.2.6 + +- 功能重构:优化系统配置管理功能,把 OSS,支付,短信,邮件等配置全部迁移到管理后台,无需通过修改配置文档的方式修改 🎉🎉🎉 +- 功能优化:重构 API 授权代码,采用中间件鉴权方式,实现更加精准的 API 鉴权 🎉🎉🎉 +- 功能优化:优化 PC 端的 Suno 音乐,视频生成,以及即梦 AI 页面 UI +- 功能优化:重构登录和注册页面,兼容移动端和 PC 端,并且所有的登录组件共用了同一套组件代码,大大降低维护成本 🎉🎉🎉 +- 功能优化:管理后台增加模型批量删除功能 +- 功能优化:优化 Table 组件 UI,并支持 dark 主题 +- 功能优化:移动端对话页面支持上传文件和图片 +- 功能新增:新增微信扫码登录支持 +- 功能新增:新增安全监控,内容审核功能,支持敏感内容过滤拦截 +- 功能新增:DALL-E 绘图支持参 Google Banana 图片编辑功能 + ## v4.2.5 - 功能优化:在代码右下角增加复制代码功能按钮,增加收起和展开代码功能 diff --git a/JIMENG_CONFIG_README.md b/JIMENG_CONFIG_README.md deleted file mode 100644 index 9ac0975c..00000000 --- a/JIMENG_CONFIG_README.md +++ /dev/null @@ -1,195 +0,0 @@ -# 即梦 AI 配置功能说明 - -## 功能概述 - -即梦 AI 配置功能允许管理员通过 Web 界面配置即梦 AI 的 API 密钥和算力消耗设置,支持动态配置更新,无需重启服务。 - -## 功能特性 - -### 1. 秘钥配置 - -- AccessKey 和 SecretKey 配置 -- 支持密码显示/隐藏 -- 连接测试功能 - -### 2. 算力配置 - -- 文生图算力消耗 -- 图生图算力消耗 -- 图片编辑算力消耗 -- 图片特效算力消耗 -- 文生视频算力消耗 -- 图生视频算力消耗 - -### 3. 动态配置 - -- 配置实时生效 -- 无需重启服务 -- 支持配置验证 - -## API 接口 - -### 获取配置 - -``` -GET /api/admin/jimeng/config -``` - -### 更新配置 - -``` -POST /api/admin/jimeng/config -Content-Type: application/json - -{ - "config": { - "access_key": "your_access_key", - "secret_key": "your_secret_key", - "power": { - "text_to_image": 10, - "image_to_image": 15, - "image_edit": 20, - "image_effects": 25, - "text_to_video": 30, - "image_to_video": 35 - } - } -} -``` - -### 测试连接 - -``` -POST /api/admin/jimeng/config/test -Content-Type: application/json - -{ - "config": { - "access_key": "your_access_key", - "secret_key": "your_secret_key" - } -} -``` - -## 前端页面 - -### 访问路径 - -管理后台 -> 即梦 AI -> 配置设置 - -### 页面功能 - -1. **秘钥配置标签页** - - - AccessKey 输入框(密码模式) - - SecretKey 输入框(密码模式) - - 测试连接按钮 - -2. **算力配置标签页** - - - 各种任务类型的算力消耗配置 - - 数字输入框,支持 1-100 范围 - - 提示信息说明 - -3. **操作按钮** - - 保存配置 - - 重置配置 - -## 配置存储 - -配置存储在数据库的`config`表中: - -- 配置键:`jimeng` -- 配置值:JSON 格式的即梦 AI 配置 - -## 默认配置 - -如果配置不存在,系统会使用以下默认值: - -```json -{ - "access_key": "", - "secret_key": "", - "power": { - "text_to_image": 10, - "image_to_image": 15, - "image_edit": 20, - "image_effects": 25, - "text_to_video": 30, - "image_to_video": 35 - } -} -``` - -## 使用流程 - -1. **初始配置** - - - 访问管理后台即梦 AI 配置页面 - - 填写 AccessKey 和 SecretKey - - 点击"测试连接"验证配置 - - 调整各功能算力消耗 - - 保存配置 - -2. **配置更新** - - - 修改需要更新的配置项 - - 保存配置 - - 配置立即生效 - -3. **故障排查** - - 使用"测试连接"功能验证 API 密钥 - - 检查配置是否正确保存 - - 查看服务日志 - -## 注意事项 - -1. **权限要求** - - - 只有管理员可以访问配置页面 - - 需要有效的管理员登录会话 - -2. **配置验证** - - - AccessKey 和 SecretKey 不能为空 - - 算力消耗必须大于 0 - - 建议先测试连接再保存配置 - -3. **服务影响** - - 配置更新不会影响正在进行的任务 - - 新任务会使用更新后的配置 - - 客户端配置会在下次请求时更新 - -## 错误处理 - -1. **配置加载失败** - - - 使用默认配置 - - 记录错误日志 - -2. **连接测试失败** - - - 显示具体错误信息 - - 建议检查 API 密钥 - -3. **配置保存失败** - - 显示错误信息 - - 保留原有配置 - -## 开发说明 - -### 后端文件 - -- `api/handler/admin/jimeng_handler.go` - 配置管理 API -- `api/service/jimeng/service.go` - 配置服务逻辑 -- `api/core/types/jimeng.go` - 配置类型定义 - -### 前端文件 - -- `web/src/views/admin/jimeng/JimengSetting.vue` - 配置页面 - -### 数据库 - -- `config`表存储配置信息 -- 配置键:`jimeng` -- 配置值:JSON 格式 diff --git a/README.md b/README.md index d7b8927a..fad888ba 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,77 @@ -# GeekAI +# 🚀 GeekAI-PLUS:一站式 AI 创意生产力平台 -> 根据[《生成式人工智能服务管理暂行办法》](https://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm)的要求,请勿对中国地区公众提供一切未经备案的生成式人工智能服务。 +**重新定义 AI 创作体验,让每个人都能成为内容创作大师** -**GeekAI** 基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案,自带运营管理后台,开箱即用。集成了 OpenAI, Claude, 通义千问,Kimi,DeepSeek,Gitee AI 等多个平台的大语言模型。集成了 MidJourney 和 Stable Diffusion AI 绘画功能。 +基于 GeekAI 项目开发的高级版,增加了很多高级功能,比如思维导图,Dalle 绘画等。**高级版源码不会一次性开放,只提供镜像给大家免费使用**,源码会逐步逐步按照版同步迁移到[社区版(GeekAI)](https://github.com/yangjian102621/geekai)。所以如果大家想要二次开发,请移步去社区版。 -主要特性: +## ✨ 核心特色 -- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。 -- 基于 Websocket 实现,完美的打字机体验。 -- 内置了各种预训练好的角色应用,比如小红书写手,英语翻译大师,苏格拉底,孔子,乔布斯,周报助手等。轻松满足你的各种聊天和应用需求。 -- 支持 OpenAI, Claude, 通义千问,Kimi,DeepSeek 等多个大语言模型,**支持 Gitee AI Serverless 大模型 API**。 -- 支持 Suno 文生音乐 -- 支持 MidJourney / Stable Diffusion AI 绘画集成,文生图,图生图,换脸,融图。开箱即用。 -- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。 -- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。 -- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件,已内置实现了微博热搜,今日头条,今日早报和 AI - 绘画函数插件。 +### 🎨 **全能 AI 创作矩阵** -### 🚀 更多功能请查看 [GeekAI-PLUS](https://github.com/yangjian102621/geekai-plus) +- **智能对话**:集成 ChatGPT、Claude 等多款顶级 AI 模型,支持角色扮演和专业对话 +- **图像生成**:整合 MidJourney、DALL-E、Stable Diffusion 三大主流 AI 绘画引擎 +- **音频创作**:Suno AI 音乐生成,从旋律到歌词一键创作专属音乐 +- **视频制作**:Luma 和 KeLing,即梦,Veo3 视频 AI,文本到视频,创意无限 +- **思维导图**:AI 辅助思维整理,复杂想法可视化呈现 -- [x] 更友好的 UI 界面 -- [x] 支持 Dall-E 文生图功能 -- [x] 支持文生思维导图 -- [x] 支持为模型绑定指定的 API KEY,支持为角色绑定指定的模型等功能 -- [x] 支持网站 Logo 版权等信息的修改 +### 🏗️ **企业级技术架构** + +- **高性能后端**:Go + Gin + MySQL + Redis,支持高并发访问 +- **现代化前端**:Vue3 + Element Plus + Vant,桌面移动双端适配 +- **智能缓存**:多层缓存策略,响应速度提升 80% +- **弹性部署**:Docker 容器化部署,一键启动,轻松扩展 +- **私有化部署**:支持私有化部署,私有化部署不支持升级,需要手动升级 +- **文档支持**:丰富且详细的部署和 API 开发文档支持,二次开发轻松上手 + +### 💼 **商业化就绪** + +- **完整用户系统**:注册登录、权限管理、积分充值 +- **灵活计费模式**:支持按次付费、包月订阅等多种商业模式 +- **数据统计分析**:用户行为、消费记录、系统性能全方位监控 +- **管理后台**:功能完备的管理员界面,运营数据一目了然 + +### 🎯 **用户体验优势** + +- **响应式设计**:完美适配桌面、平板、手机等全终端设备 +- **暗黑模式**:支持明暗主题切换,护眼舒适 +- **实时交互**:WebSocket 实时通信,创作过程流畅无卡顿 +- **文件管理**:支持多种云存储,作品安全可靠 + +## 🎪 **应用场景** + +- **内容创作者**:博客写作、社交媒体素材、短视频制作 +- **企业营销**:品牌宣传材料、产品介绍、创意广告 +- **教育培训**:课件制作、知识图谱、互动内容 +- **个人娱乐**:AI 聊天、创意绘画、音乐创作 + +## 🔥 **为什么选择 GeekAI-PLUS?** + +1. **技术领先**:集成当前最先进的 AI 技术,始终保持创新前沿 +2. **开箱即用**:完整的商业化解决方案,无需从零开发 +3. **高度定制**:模块化架构设计,支持个性化功能扩展 +4. **稳定可靠**:经过大量用户验证,性能稳定,安全可信 +5. **持续更新**:紧跟 AI 技术发展,功能持续迭代升级 + +## 演示站点 + +[Geek-AI 创作系统](https://www.geekai.me) + +## 文档地址 + +[Geek-AI 文档](https://www.geekai.me/docs/) + +## 部署 + +1. 安装 docker 和 docker-compose 程序,这个自行解决。 +2. 直接在项目根目录运行启动命令: + ```shell + docker-compose up -d + ``` ## 功能截图 -请参考 [GeekAI 项目介绍](https://docs.geekai.me/plus/info/)。 +请参考 [GeekAI 项目介绍](https://docs.geekai.me/info/)。 -### 体验地址 +--- -> 免费体验地址:[https://chat.geekai.me](https://chat.geekai.me)
> **注意:请合法使用,禁止输出任何敏感、不友好或违规的内容!!!** - -## 快速部署体验 - -您可以通过 EazyDevelop 平台体验-键私有化部署 **GeekAI 创作助手**,只需一分钟即可部署成功。 - -部署模板地址: [https://eazydevelop.eazytec-cloud.com/templates/dev-template-5e4dc4-1764053014?q=bB3R_1VnJq9_3Zs9uX](https://eazydevelop.eazytec-cloud.com/templates/dev-template-5e4dc4-1764053014?q=bB3R_1VnJq9_3Zs9uX) - -详细部署教程请参考 [EazyDevelop 部署 GeekAI](https://docs.geekai.me/plus/install/quick-start.html#eazydevelop-一键部署)。 - -## 使用须知 - -1. 本项目基于 Apache2.0 协议,免费开放全部源代码,可以作为个人学习使用或者商用。 -2. 如需商用必须保留版权信息,请自觉遵守。确保合法合规使用,在运营过程中产生的一切任何后果自负,与作者无关。 - -## 项目地址 - -- Github 地址:https://github.com/yangjian102621/geekai -- 码云地址:https://gitee.com/blackfox/geekai - -## 客户端下载 - -目前已经支持 Win/Linux/Mac/Android 客户端,下载地址为:https://github.com/yangjian102621/geekai/releases/tag/v3.1.2 - -## 项目文档 - -最新的部署视频教程:[https://www.bilibili.com/video/BV1Cc411t7CX/](https://www.bilibili.com/video/BV1Cc411t7CX/) - -详细的部署和开发文档请参考 [**GeekAI 文档**](https://docs.geekai.me)。 - -加微信进入微信讨论群可获取 **一键部署脚本(添加好友时请注明来自 Github!!!)。** - -![微信名片](https://docs.geekai.me/images/wx_card.png) - -## 参与贡献 - -个人的力量始终有限,任何形式的贡献都是欢迎的,包括但不限于贡献代码,优化文档,提交 issue 和 PR 等。 - -#### 特此声明:由于个人时间有限,不接受在微信或者微信群给开发者提 Bug,有问题或者优化建议请提交 Issue 和 PR。非常感谢您的配合! - -### Commit 类型 - -- feat: 新特性或功能 -- fix: 缺陷修复 -- docs: 文档更新 -- style: 代码风格或者组件样式更新 -- refactor: 代码重构,不引入新功能和缺陷修复 -- opt: 性能优化 -- chore: 一些不涉及到功能变动的小提交,比如修改文字表述,修改注释等 - -## 打赏 - -如果你觉得这个项目对你有帮助,并且情况允许的话,可以请作者喝杯咖啡,非常感谢你的支持~ - -![打赏](https://blog.img.r9it.com/image-f02ca9eccbe93c7b1193c2623e7336ea.png) - -![Star History Chart](https://api.star-history.com/svg?repos=yangjian102621/geekai&type=Date) +_让 AI 成为你最强大的创作伙伴,开启无限创意可能!_ diff --git a/api/core/app_server.go b/api/core/app_server.go index 51dbe0b3..9917fc06 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -8,118 +8,50 @@ package core // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( - "bytes" - "context" "fmt" + "geekai/core/middleware" "geekai/core/types" "geekai/store/model" "geekai/utils" - "geekai/utils/resp" - "image" - "image/jpeg" "io" "net/http" - "os" "runtime/debug" - "strings" "time" "github.com/gin-gonic/gin" "github.com/go-redis/redis/v8" - "github.com/golang-jwt/jwt/v5" "github.com/imroc/req/v3" - "github.com/nfnt/resize" "github.com/shirou/gopsutil/host" - "golang.org/x/image/webp" "gorm.io/gorm" ) -// AuthConfig 定义授权配置 -type AuthConfig struct { - ExactPaths map[string]bool // 精确匹配的路径 - PrefixPaths map[string]bool // 前缀匹配的路径 -} - -var authConfig = &AuthConfig{ - ExactPaths: map[string]bool{ - "/api/user/login": false, - "/api/user/logout": false, - "/api/user/resetPass": false, - "/api/user/register": false, - "/api/admin/login": false, - "/api/admin/logout": false, - "/api/admin/login/captcha": false, - "/api/app/list": false, - "/api/app/type/list": false, - "/api/app/list/user": false, - "/api/model/list": false, - "/api/mj/imgWall": false, - "/api/mj/notify": false, - "/api/invite/hits": false, - "/api/sd/imgWall": false, - "/api/dall/imgWall": false, - "/api/product/list": false, - "/api/menu/list": false, - "/api/markMap/client": false, - "/api/payment/doPay": false, - "/api/payment/payWays": false, - "/api/download": false, - "/api/dall/models": false, - }, - PrefixPaths: map[string]bool{ - "/api/test/": false, - "/api/payment/notify/": false, - "/api/user/clogin": false, - "/api/config/": false, - "/api/function/": false, - "/api/sms/": false, - "/api/captcha/": false, - "/static/": false, - }, -} - type AppServer struct { Config *types.AppConfig Engine *gin.Engine SysConfig *types.SystemConfig // system config cache + Redis *redis.Client } -func NewServer(appConfig *types.AppConfig) *AppServer { +func NewServer(appConfig *types.AppConfig, redis *redis.Client, sysConfig *types.SystemConfig) *AppServer { gin.SetMode(gin.ReleaseMode) gin.DefaultWriter = io.Discard return &AppServer{ - Config: appConfig, - Engine: gin.Default(), + Config: appConfig, + Redis: redis, + Engine: gin.Default(), + SysConfig: sysConfig, } } -func (s *AppServer) Init(debug bool, client *redis.Client) { - // 允许跨域请求 API - s.Engine.Use(corsMiddleware()) - s.Engine.Use(staticResourceMiddleware()) - s.Engine.Use(authorizeMiddleware(s, client)) - s.Engine.Use(parameterHandlerMiddleware()) +func (s *AppServer) Init(client *redis.Client) { + s.Engine.Use(middleware.ParameterHandlerMiddleware()) s.Engine.Use(errorHandler) // 添加静态资源访问 s.Engine.Static("/static", s.Config.StaticDir) + s.Engine.Use(middleware.StaticMiddleware()) } func (s *AppServer) Run(db *gorm.DB) error { - - // 重命名 config 表字段 - if db.Migrator().HasColumn(&model.Config{}, "config_json") { - db.Migrator().RenameColumn(&model.Config{}, "config_json", "value") - } - if db.Migrator().HasColumn(&model.Config{}, "marker") { - db.Migrator().RenameColumn(&model.Config{}, "marker", "name") - } - if db.Migrator().HasIndex(&model.Config{}, "idx_chatgpt_configs_key") { - db.Migrator().DropIndex(&model.Config{}, "idx_chatgpt_configs_key") - } - if db.Migrator().HasIndex(&model.Config{}, "marker") { - db.Migrator().DropIndex(&model.Config{}, "marker") - } - // load system configs var sysConfig model.Config err := db.Where("name", "system").First(&sysConfig).Error @@ -131,94 +63,22 @@ func (s *AppServer) Run(db *gorm.DB) error { return fmt.Errorf("failed to decode system config: %v", err) } - // 迁移数据表 - logger.Info("Migrating database tables...") - db.AutoMigrate( - &model.ChatItem{}, - &model.ChatMessage{}, - &model.ChatRole{}, - &model.ChatModel{}, - &model.InviteCode{}, - &model.InviteLog{}, - &model.Menu{}, - &model.Order{}, - &model.Product{}, - &model.User{}, - &model.Function{}, - &model.File{}, - &model.Redeem{}, - &model.Config{}, - &model.ApiKey{}, - &model.AdminUser{}, - &model.AppType{}, - &model.SdJob{}, - &model.SunoJob{}, - &model.PowerLog{}, - &model.VideoJob{}, - &model.MidJourneyJob{}, - &model.UserLoginLog{}, - &model.DallJob{}, - &model.JimengJob{}, - ) - // 手动删除字段 - if db.Migrator().HasColumn(&model.Order{}, "deleted_at") { - db.Migrator().DropColumn(&model.Order{}, "deleted_at") - } - if db.Migrator().HasColumn(&model.ChatItem{}, "deleted_at") { - db.Migrator().DropColumn(&model.ChatItem{}, "deleted_at") - } - if db.Migrator().HasColumn(&model.ChatMessage{}, "deleted_at") { - db.Migrator().DropColumn(&model.ChatMessage{}, "deleted_at") - } - if db.Migrator().HasColumn(&model.User{}, "chat_config") { - db.Migrator().DropColumn(&model.User{}, "chat_config") - } - if db.Migrator().HasColumn(&model.ChatModel{}, "category") { - db.Migrator().DropColumn(&model.ChatModel{}, "category") - } - if db.Migrator().HasColumn(&model.ChatModel{}, "description") { - db.Migrator().DropColumn(&model.ChatModel{}, "description") - } - - logger.Info("Database tables migrated successfully") - // 统计安装信息 go func() { info, err := host.Info() if err == nil { - apiURL := fmt.Sprintf("%s/%s", s.Config.ApiConfig.ApiURL, "api/installs/push") + apiURL := fmt.Sprintf("%s/api/installs/push", types.GeekAPIURL) timestamp := time.Now().Unix() product := "geekai-plus" signStr := fmt.Sprintf("%s#%s#%d", product, info.HostID, timestamp) sign := utils.Sha256(signStr) resp, err := req.C().R().SetBody(map[string]interface{}{"product": product, "device_id": info.HostID, "timestamp": timestamp, "sign": sign}).Post(apiURL) - if err != nil { - logger.Errorf("register install info failed: %v", err) - } else { + if err == nil { logger.Debugf("register install info success: %v", resp.String()) } } }() logger.Infof("http://%s", s.Config.Listen) - - // 统计安装信息 - go func() { - info, err := host.Info() - if err == nil { - apiURL := fmt.Sprintf("%s/%s", s.Config.ApiConfig.ApiURL, "api/installs/push") - timestamp := time.Now().Unix() - product := "geekai-plus" - signStr := fmt.Sprintf("%s#%s#%d", product, info.HostID, timestamp) - sign := utils.Sha256(signStr) - resp, err := req.C().R().SetBody(map[string]interface{}{"product": product, "device_id": info.HostID, "timestamp": timestamp, "sign": sign}).Post(apiURL) - if err != nil { - logger.Errorf("register install info failed: %v", err) - } else { - logger.Debugf("register install info success: %v", resp.String()) - } - } - }() - return s.Engine.Run(s.Config.Listen) } @@ -235,283 +95,3 @@ func errorHandler(c *gin.Context) { //加载完 defer recover,继续后续接口调用 c.Next() } - -// 跨域中间件设置 -func corsMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - method := c.Request.Method - origin := c.Request.Header.Get("Origin") - - // 设置允许的请求源 - if origin != "" { - c.Header("Access-Control-Allow-Origin", origin) - } else { - c.Header("Access-Control-Allow-Origin", "*") - } - - c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") - //允许跨域设置可以返回其他子段,可以自定义字段 - c.Header("Access-Control-Allow-Headers", "Authorization, Body-Length, Body-Type, Admin-Authorization,content-type") - // 允许浏览器(客户端)可以解析的头部 (重要) - c.Header("Access-Control-Expose-Headers", "Body-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers") - //设置缓存时间 - c.Header("Access-Control-Max-Age", "172800") - //允许客户端传递校验信息比如 cookie (重要) - c.Header("Access-Control-Allow-Credentials", "true") - - if method == http.MethodOptions { - c.JSON(http.StatusOK, "ok!") - } - - defer func() { - if err := recover(); err != nil { - logger.Info("Panic info is: %v", err) - } - }() - - c.Next() - } -} - -// 用户授权验证 -func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc { - return func(c *gin.Context) { - if !needLogin(c) { - c.Next() - return - } - - clientProtocols := c.GetHeader("Sec-WebSocket-Protocol") - var tokenString string - isAdminApi := strings.Contains(c.Request.URL.Path, "/api/admin/") - if isAdminApi { // 后台管理 API - tokenString = c.GetHeader(types.AdminAuthHeader) - } else if clientProtocols != "" { // Websocket 连接 - // 解析子协议内容 - protocols := strings.Split(clientProtocols, ",") - if protocols[0] == "realtime" { - tokenString = strings.TrimSpace(protocols[1][25:]) - } else if protocols[0] == "token" { - tokenString = strings.TrimSpace(protocols[1]) - } - } else { - tokenString = c.GetHeader(types.UserAuthHeader) - } - - if tokenString == "" { - resp.NotAuth(c, "You should put Authorization in request headers") - c.Abort() - return - } - - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - if isAdminApi { - return []byte(s.Config.AdminSession.SecretKey), nil - } else { - return []byte(s.Config.Session.SecretKey), nil - } - - }) - - if err != nil { - resp.NotAuth(c, fmt.Sprintf("Error with parse auth token: %v", err)) - c.Abort() - return - } - - claims, ok := token.Claims.(jwt.MapClaims) - if !ok || !token.Valid { - resp.NotAuth(c, "Token is invalid") - c.Abort() - return - } - - expr := utils.IntValue(utils.InterfaceToString(claims["expired"]), 0) - if expr > 0 && int64(expr) < time.Now().Unix() { - resp.NotAuth(c, "Token is expired") - c.Abort() - return - } - - key := fmt.Sprintf("users/%v", claims["user_id"]) - if isAdminApi { - key = fmt.Sprintf("admin/%v", claims["user_id"]) - } - if _, err := client.Get(context.Background(), key).Result(); err != nil { - resp.NotAuth(c, "Token is not found in redis") - c.Abort() - return - } - c.Set(types.LoginUserID, claims["user_id"]) - c.Next() - } -} - -func needLogin(c *gin.Context) bool { - path := c.Request.URL.Path - - // 如果不是 API 路径,不需要登录 - if !strings.HasPrefix(path, "/api") { - return false - } - - // 检查精确匹配的路径 - if skip, exists := authConfig.ExactPaths[path]; exists { - return skip - } - - // 检查前缀匹配的路径 - for prefix, skip := range authConfig.PrefixPaths { - if strings.HasPrefix(path, prefix) { - return skip - } - } - - return true -} - -// 跳过授权 -func (s *AppServer) SkipAuth(url string, prefix bool) { - if prefix { - authConfig.PrefixPaths[url] = false - } else { - authConfig.ExactPaths[url] = false - } -} - -// 统一参数处理 -func parameterHandlerMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - // GET 参数处理 - params := c.Request.URL.Query() - for key, values := range params { - for i, value := range values { - params[key][i] = strings.TrimSpace(value) - } - } - // update get parameters - c.Request.URL.RawQuery = params.Encode() - // skip file upload requests - contentType := c.Request.Header.Get("Content-Type") - if strings.Contains(contentType, "multipart/form-data") { - c.Next() - return - } - - if strings.Contains(contentType, "application/json") { - // process POST JSON request body - bodyBytes, err := io.ReadAll(c.Request.Body) - if err != nil { - c.Next() - return - } - - // 还原请求体 - c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) - // 将请求体解析为 JSON - var jsonData map[string]interface{} - if err := c.ShouldBindJSON(&jsonData); err != nil { - c.Next() - return - } - - // 对 JSON 数据中的字符串值去除两端空格 - trimJSONStrings(jsonData) - // 更新请求体 - c.Request.Body = io.NopCloser(bytes.NewBufferString(utils.JsonEncode(jsonData))) - } - - c.Next() - } -} - -// 递归对 JSON 数据中的字符串值去除两端空格 -func trimJSONStrings(data interface{}) { - switch v := data.(type) { - case map[string]interface{}: - for key, value := range v { - switch valueType := value.(type) { - case string: - v[key] = strings.TrimSpace(valueType) - case map[string]interface{}, []interface{}: - trimJSONStrings(value) - } - } - case []interface{}: - for i, value := range v { - switch valueType := value.(type) { - case string: - v[i] = strings.TrimSpace(valueType) - case map[string]interface{}, []interface{}: - trimJSONStrings(value) - } - } - } -} - -// 静态资源中间件 -func staticResourceMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - - url := c.Request.URL.String() - // 拦截生成缩略图请求 - if strings.HasPrefix(url, "/static/") && strings.Contains(url, "?imageView2") { - r := strings.SplitAfter(url, "imageView2") - size := strings.Split(r[1], "/") - if len(size) != 8 { - c.String(http.StatusNotFound, "invalid thumb args") - return - } - with := utils.IntValue(size[3], 0) - height := utils.IntValue(size[5], 0) - quality := utils.IntValue(size[7], 75) - - // 打开图片文件 - filePath := strings.TrimLeft(c.Request.URL.Path, "/") - file, err := os.Open(filePath) - if err != nil { - c.String(http.StatusNotFound, "Image not found") - return - } - defer file.Close() - - // 解码图片 - img, _, err := image.Decode(file) - // for .webp image - if err != nil { - img, err = webp.Decode(file) - } - if err != nil { - c.String(http.StatusInternalServerError, "Error decoding image") - return - } - - var newImg image.Image - if height == 0 || with == 0 { - // 固定宽度,高度自适应 - newImg = resize.Resize(uint(with), uint(height), img, resize.Lanczos3) - } else { - // 生成缩略图 - newImg = resize.Thumbnail(uint(with), uint(height), img, resize.Lanczos3) - } - var buffer bytes.Buffer - err = jpeg.Encode(&buffer, newImg, &jpeg.Options{Quality: quality}) - if err != nil { - logger.Error(err) - c.String(http.StatusInternalServerError, err.Error()) - return - } - - // 设置图片缓存有效期为一年 (365天) - c.Header("Cache-Control", "max-age=31536000, public") - // 直接输出图像数据流 - c.Data(http.StatusOK, "image/jpeg", buffer.Bytes()) - c.Abort() // 中断请求 - - } - c.Next() - } -} diff --git a/api/core/config.go b/api/core/config.go index 153a38c6..e38ed0bf 100644 --- a/api/core/config.go +++ b/api/core/config.go @@ -11,10 +11,12 @@ import ( "bytes" "geekai/core/types" logger2 "geekai/logger" + "geekai/store/model" "geekai/utils" "os" "github.com/BurntSushi/toml" + "gorm.io/gorm" ) var logger = logger2.GetLogger() @@ -30,7 +32,6 @@ func NewDefaultConfig() *types.AppConfig { SecretKey: utils.RandString(64), MaxAge: 86400, }, - ApiConfig: types.ApiConfig{}, OSS: types.OSSConfig{ Active: "local", Local: types.LocalStorageConfig{ @@ -38,7 +39,6 @@ func NewDefaultConfig() *types.AppConfig { BasePath: "./static/upload", }, }, - AlipayConfig: types.AlipayConfig{Enabled: false, SandBox: false}, } } @@ -74,3 +74,108 @@ func SaveConfig(config *types.AppConfig) error { return os.WriteFile(config.Path, buf.Bytes(), 0644) } + +func LoadSystemConfig(db *gorm.DB) *types.SystemConfig { + // 加载系统配置 + var sysConfig model.Config + var baseConfig types.BaseConfig + db.Where("name", "system").First(&sysConfig) + err := utils.JsonDecode(sysConfig.Value, &baseConfig) + if err != nil { + logger.Error("load system config error: ", err) + } + + // 加载许可证配置 + var license types.License + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyLicense).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &license) + if err != nil { + logger.Error("load license config error: ", err) + } + + // 加载验证码配置 + var captchaConfig types.CaptchaConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyCaptcha).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &captchaConfig) + if err != nil { + logger.Error("load geek service config error: ", err) + } + + // 加载微信登录配置 + var wxLoginConfig types.WxLoginConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyWxLogin).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &wxLoginConfig) + if err != nil { + logger.Error("load wx login config error: ", err) + } + + // 加载短信配置 + var smsConfig types.SMSConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeySms).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &smsConfig) + if err != nil { + logger.Error("load sms config error: ", err) + } + + // 加载 OSS 配置 + var ossConfig types.OSSConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyOss).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &ossConfig) + if err != nil { + logger.Error("load oss config error: ", err) + } + + // 加载 SMTP 配置 + var smtpConfig types.SmtpConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeySmtp).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &smtpConfig) + if err != nil { + logger.Error("load smtp config error: ", err) + } + + // 加载支付配置 + var paymentConfig types.PaymentConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyPayment).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &paymentConfig) + if err != nil { + logger.Error("load payment config error: ", err) + } + + // 加载文本审查配置 + var moderationConfig types.ModerationConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyModeration).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &moderationConfig) + if err != nil { + logger.Error("load moderation config error: ", err) + } + + // 加载即梦AI配置 + var jimengConfig types.JimengConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyJimeng).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &jimengConfig) + if err != nil { + logger.Error("load jimeng config error: ", err) + } + + return &types.SystemConfig{ + Base: baseConfig, + License: license, + SMS: smsConfig, + OSS: ossConfig, + SMTP: smtpConfig, + Payment: paymentConfig, + Captcha: captchaConfig, + WxLogin: wxLoginConfig, + Moderation: moderationConfig, + Jimeng: jimengConfig, + } +} diff --git a/api/core/middleware/auth.go b/api/core/middleware/auth.go new file mode 100644 index 00000000..d6bde9b1 --- /dev/null +++ b/api/core/middleware/auth.go @@ -0,0 +1,109 @@ +package middleware + +import ( + "context" + "fmt" + "geekai/core/types" + "geekai/utils" + "geekai/utils/resp" + "time" + + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" + "github.com/golang-jwt/jwt" +) + +// 前端用户授权验证 +func UserAuthMiddleware(secretKey string, redis *redis.Client) gin.HandlerFunc { + return func(c *gin.Context) { + tokenString := c.GetHeader(types.UserAuthHeader) + if tokenString == "" { + resp.NotAuth(c, "无效的授权令牌") + c.Abort() + return + } + + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("不支持的令牌签名方法: %v", token.Header["alg"]) + } + return []byte(secretKey), nil + }) + + if err != nil { + resp.NotAuth(c, fmt.Sprintf("解析授权令牌失败: %v", err)) + c.Abort() + return + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || !token.Valid { + resp.NotAuth(c, "令牌无效") + c.Abort() + return + } + + expr := utils.IntValue(utils.InterfaceToString(claims["expired"]), 0) + if expr > 0 && int64(expr) < time.Now().Unix() { + resp.NotAuth(c, "令牌过期") + c.Abort() + return + } + + key := fmt.Sprintf("users/%v", claims["user_id"]) + if _, err := redis.Get(context.Background(), key).Result(); err != nil { + resp.NotAuth(c, "当前用户已退出登录") + c.Abort() + return + } + c.Set(types.LoginUserID, claims["user_id"]) + } +} + +// 管理后台用户授权验证 +func AdminAuthMiddleware(secretKey string, redis *redis.Client) gin.HandlerFunc { + return func(c *gin.Context) { + tokenString := c.GetHeader(types.AdminAuthHeader) + if tokenString == "" { + resp.NotAuth(c, "无效的授权令牌") + c.Abort() + return + } + + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("不支持的令牌签名方法: %v", token.Header["alg"]) + } + return []byte(secretKey), nil + }) + + if err != nil { + resp.NotAuth(c, fmt.Sprintf("解析授权令牌失败: %v", err)) + c.Abort() + return + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok || !token.Valid { + resp.NotAuth(c, "令牌无效") + c.Abort() + return + } + + expr := utils.IntValue(utils.InterfaceToString(claims["expired"]), 0) + if expr > 0 && int64(expr) < time.Now().Unix() { + resp.NotAuth(c, "令牌过期") + c.Abort() + return + } + + key := fmt.Sprintf("admin/%v", claims["user_id"]) + if _, err := redis.Get(context.Background(), key).Result(); err != nil { + resp.NotAuth(c, "当前用户已退出登录") + c.Abort() + return + } + + c.Set(types.AdminUserID, claims["user_id"]) + } +} diff --git a/api/core/middleware/parameter.go b/api/core/middleware/parameter.go new file mode 100644 index 00000000..9b4483ed --- /dev/null +++ b/api/core/middleware/parameter.go @@ -0,0 +1,80 @@ +package middleware + +import ( + "bytes" + "geekai/utils" + "io" + "strings" + + "github.com/gin-gonic/gin" +) + +// 统一参数处理 +func ParameterHandlerMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // GET 参数处理 + params := c.Request.URL.Query() + for key, values := range params { + for i, value := range values { + params[key][i] = strings.TrimSpace(value) + } + } + // update get parameters + c.Request.URL.RawQuery = params.Encode() + // skip file upload requests + contentType := c.Request.Header.Get("Content-Type") + if strings.Contains(contentType, "multipart/form-data") { + c.Next() + return + } + + if strings.Contains(contentType, "application/json") { + // process POST JSON request body + bodyBytes, err := io.ReadAll(c.Request.Body) + if err != nil { + c.Next() + return + } + + // 还原请求体 + c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + // 将请求体解析为 JSON + var jsonData map[string]any + if err := c.ShouldBindJSON(&jsonData); err != nil { + c.Next() + return + } + + // 对 JSON 数据中的字符串值去除两端空格 + trimJSONStrings(jsonData) + // 更新请求体 + c.Request.Body = io.NopCloser(bytes.NewBufferString(utils.JsonEncode(jsonData))) + } + + c.Next() + } +} + +// 递归对 JSON 数据中的字符串值去除两端空格 +func trimJSONStrings(data any) { + switch v := data.(type) { + case map[string]any: + for key, value := range v { + switch valueType := value.(type) { + case string: + v[key] = strings.TrimSpace(valueType) + case map[string]any, []any: + trimJSONStrings(value) + } + } + case []any: + for i, value := range v { + switch valueType := value.(type) { + case string: + v[i] = strings.TrimSpace(valueType) + case map[string]any, []any: + trimJSONStrings(value) + } + } + } +} diff --git a/api/core/middleware/rate_limit.go b/api/core/middleware/rate_limit.go new file mode 100644 index 00000000..903728db --- /dev/null +++ b/api/core/middleware/rate_limit.go @@ -0,0 +1,43 @@ +package middleware + +import ( + "context" + "fmt" + "geekai/core/types" + "geekai/utils" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/go-redis/redis/v8" +) + +// RateLimitEvery 使用 Redis 做固定间隔限流:在 interval 内仅允许一次请求 +// Key 优先使用登录用户ID,若没有则退化为 route + IP +func RateLimitEvery(redisClient *redis.Client, interval time.Duration) gin.HandlerFunc { + return func(c *gin.Context) { + keyID := "" + if userID, ok := c.Get(types.LoginUserID); ok { + keyID = fmt.Sprintf("user:%s", utils.InterfaceToString(userID)) + } else { + keyID = fmt.Sprintf("ip:%s", c.ClientIP()) + } + + fullPath := c.FullPath() + if fullPath == "" { + fullPath = c.Request.URL.Path + } + key := fmt.Sprintf("rl:%s:%s", fullPath, keyID) + + okSet, err := redisClient.SetNX(context.Background(), key, 1, interval).Result() + if err != nil { + // Redis 异常时放行,避免误伤可用性 + return + } + if !okSet { + c.JSON(http.StatusTooManyRequests, types.BizVo{Code: types.Failed, Message: "请求过于频繁,请稍后重试"}) + c.Abort() + return + } + } +} diff --git a/api/core/middleware/static.go b/api/core/middleware/static.go new file mode 100644 index 00000000..2e91f9c8 --- /dev/null +++ b/api/core/middleware/static.go @@ -0,0 +1,78 @@ +package middleware + +import ( + "bytes" + "geekai/utils" + "image" + "image/jpeg" + "net/http" + "os" + "strings" + + "github.com/gin-gonic/gin" + "github.com/nfnt/resize" + "golang.org/x/image/webp" +) + +// 静态资源中间件 +func StaticMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + + url := c.Request.URL.String() + // 拦截生成缩略图请求 + if strings.HasPrefix(url, "/static/") && strings.Contains(url, "?imageView2") { + r := strings.SplitAfter(url, "imageView2") + size := strings.Split(r[1], "/") + if len(size) != 8 { + c.String(http.StatusNotFound, "invalid thumb args") + return + } + with := utils.IntValue(size[3], 0) + height := utils.IntValue(size[5], 0) + quality := utils.IntValue(size[7], 75) + + // 打开图片文件 + filePath := strings.TrimLeft(c.Request.URL.Path, "/") + file, err := os.Open(filePath) + if err != nil { + c.String(http.StatusNotFound, "Image not found") + return + } + defer file.Close() + + // 解码图片 + img, _, err := image.Decode(file) + // for .webp image + if err != nil { + img, err = webp.Decode(file) + } + if err != nil { + c.String(http.StatusInternalServerError, "Error decoding image") + return + } + + var newImg image.Image + if height == 0 || with == 0 { + // 固定宽度,高度自适应 + newImg = resize.Resize(uint(with), uint(height), img, resize.Lanczos3) + } else { + // 生成缩略图 + newImg = resize.Thumbnail(uint(with), uint(height), img, resize.Lanczos3) + } + var buffer bytes.Buffer + err = jpeg.Encode(&buffer, newImg, &jpeg.Options{Quality: quality}) + if err != nil { + c.String(http.StatusInternalServerError, err.Error()) + return + } + + // 设置图片缓存有效期为一年 (365天) + c.Header("Cache-Control", "max-age=31536000, public") + // 直接输出图像数据流 + c.Data(http.StatusOK, "image/jpeg", buffer.Bytes()) + c.Abort() // 中断请求 + + } + c.Next() + } +} diff --git a/api/core/types/config.go b/api/core/types/config.go index 9fbcc73d..e93a8b9c 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -17,88 +17,17 @@ type AppConfig struct { Session Session AdminSession Session ProxyURL string - MysqlDns string // mysql 连接地址 - StaticDir string // 静态资源目录 - StaticUrl string // 静态资源 URL - Redis RedisConfig // redis 连接信息 - ApiConfig ApiConfig // ChatPlus API authorization configs - SMS SMSConfig // send mobile message config - OSS OSSConfig // OSS config - SmtpConfig SmtpConfig // 邮件发送配置 - XXLConfig XXLConfig - AlipayConfig AlipayConfig // 支付宝支付渠道配置 - HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置 - GeekPayConfig GeekPayConfig // GEEK 支付配置 - WechatPayConfig WechatPayConfig // 微信支付渠道配置 - TikaHost string // TiKa 服务器地址 -} - -type SmtpConfig struct { - UseTls bool // 是否使用 TLS 发送 - Host string - Port int - AppName string // 应用名称 - From string // 发件人邮箱地址 - Password string // 发件人邮箱密码 -} - -type ApiConfig struct { - ApiURL string - AppId string - Token string - JimengConfig JimengConfig // 即梦AI配置 -} - -type AlipayConfig struct { - Enabled bool // 是否启用该支付通道 - SandBox bool // 是否沙盒环境 - AppId string // 应用 ID - UserId string // 支付宝用户 ID - PrivateKey string // 用户私钥文件路径 - PublicKey string // 用户公钥文件路径 - AlipayPublicKey string // 支付宝公钥文件路径 - RootCert string // Root 秘钥路径 - NotifyURL string // 异步通知地址 - ReturnURL string // 同步通知地址 -} - -type WechatPayConfig struct { - Enabled bool // 是否启用该支付通道 - AppId string // 公众号的APPID,如:wxd678efh567hg6787 - MchId string // 直连商户的商户号,由微信支付生成并下发 - SerialNo string // 商户证书的证书序列号 - PrivateKey string // 用户私钥文件路径 - ApiV3Key string // API V3 秘钥 - NotifyURL string // 异步通知地址 -} - -type HuPiPayConfig struct { //虎皮椒第四方支付配置 - Enabled bool // 是否启用该支付通道 - AppId string // App ID - AppSecret string // app 密钥 - ApiURL string // 支付网关 - NotifyURL string // 异步通知地址 - ReturnURL string // 同步通知地址 -} - -// GeekPayConfig GEEK支付配置 -type GeekPayConfig struct { - Enabled bool - AppId string // 商户 ID - PrivateKey string // 私钥 - ApiURL string // API 网关 - NotifyURL string // 异步通知地址 - ReturnURL string // 同步通知地址 - Methods []string // 支付方式 -} - -type XXLConfig struct { // XXL 任务调度配置 - Enabled bool - ServerAddr string - ExecutorIp string - ExecutorPort string - AccessToken string - RegistryKey string + MysqlDns string // mysql 连接地址 + StaticDir string // 静态资源目录 + StaticUrl string // 静态资源 URL + Redis RedisConfig // redis 连接信息 + SMS SMSConfig // send mobile message config + OSS OSSConfig // OSS config + SmtpConfig SmtpConfig // 邮件发送配置 + AlipayConfig AlipayConfig // 支付宝支付渠道配置 + GeekPayConfig EpayConfig // GEEK 支付配置 + WechatPayConfig WxPayConfig // 微信支付渠道配置 + TikaHost string // TiKa 服务器地址 } type RedisConfig struct { @@ -128,32 +57,28 @@ func (c RedisConfig) Url() string { return fmt.Sprintf("%s:%d", c.Host, c.Port) } -type SystemConfig struct { - Title string `json:"title,omitempty"` // 网站标题 - Slogan string `json:"slogan,omitempty"` // 网站 slogan - AdminTitle string `json:"admin_title,omitempty"` // 管理后台标题 - Logo string `json:"logo,omitempty"` // 圆形 Logo - BarLogo string `json:"bar_logo,omitempty"` // 条形 Logo - InitPower int `json:"init_power,omitempty"` // 新用户注册赠送算力值 - DailyPower int `json:"daily_power,omitempty"` // 每日签到赠送算力 - InvitePower int `json:"invite_power,omitempty"` // 邀请新用户赠送算力值 - VipMonthPower int `json:"vip_month_power,omitempty"` // VIP 会员每月赠送的算力值 +type BaseConfig struct { + Title string `json:"title,omitempty"` // 网站标题 + Slogan string `json:"slogan,omitempty"` // 网站 slogan + AdminTitle string `json:"admin_title,omitempty"` // 管理后台标题 + Logo string `json:"logo,omitempty"` // 圆形 Logo + BarLogo string `json:"bar_logo,omitempty"` // 条形 Logo RegisterWays []string `json:"register_ways,omitempty"` // 注册方式:支持手机(mobile),邮箱注册(email),账号密码注册 EnabledRegister bool `json:"enabled_register,omitempty"` // 是否开放注册 - OrderPayTimeout int `json:"order_pay_timeout,omitempty"` //订单支付超时时间 - VipInfoText string `json:"vip_info_text,omitempty"` // 会员页面充值说明 + OrderPayTimeout int `json:"order_pay_timeout,omitempty"` //订单支付超时时间,单位:分钟 + InitPower int `json:"init_power,omitempty"` // 新用户注册赠送算力值 + DailyPower int `json:"daily_power,omitempty"` // 每日签到赠送算力 + InvitePower int `json:"invite_power,omitempty"` // 邀请新用户赠送算力值 MjPower int `json:"mj_power,omitempty"` // MJ 绘画消耗算力 MjActionPower int `json:"mj_action_power,omitempty"` // MJ 操作(放大,变换)消耗算力 SdPower int `json:"sd_power,omitempty"` // SD 绘画消耗算力 - DallPower int `json:"dall_power,omitempty"` // DALL-E-3 绘图消耗算力 SunoPower int `json:"suno_power,omitempty"` // Suno 生成歌曲消耗算力 LumaPower int `json:"luma_power,omitempty"` // Luma 生成视频消耗算力 KeLingPowers map[string]int `json:"keling_powers,omitempty"` // 可灵生成视频消耗算力 AdvanceVoicePower int `json:"advance_voice_power,omitempty"` // 高级语音对话消耗算力 - PromptPower int `json:"prompt_power,omitempty"` // 生成提示词消耗算力 WechatCardURL string `json:"wechat_card_url,omitempty"` // 微信客服地址 @@ -163,15 +88,44 @@ type SystemConfig struct { SdNegPrompt string `json:"sd_neg_prompt"` // SD 默认反向提示词 MjMode string `json:"mj_mode"` // midjourney 默认的API模式,relax, fast, turbo - IndexNavs []int `json:"index_navs"` // 首页显示的导航菜单 - Copyright string `json:"copyright"` // 版权信息 - DefaultNickname string `json:"default_nickname"` // 默认昵称 - ICP string `json:"icp"` // ICP 备案号 - MarkMapText string `json:"mark_map_text"` // 思维导入的默认文本 + IndexNavs []int `json:"index_navs"` // 首页显示的导航菜单 + Copyright string `json:"copyright"` // 版权信息 + ICP string `json:"icp"` // ICP 备案号 + GaBeian string `json:"ga_beian"` // 公安备案号 - EnabledVerify bool `json:"enabled_verify"` // 是否启用验证码 EmailWhiteList []string `json:"email_white_list"` // 邮箱白名单列表 AssistantModelId int `json:"assistant_model_id"` // 用来做提示词,翻译的AI模型 id MaxFileSize int `json:"max_file_size"` // 最大文件大小,单位:MB - } + +type SystemConfig struct { + Base BaseConfig + Payment PaymentConfig + OSS OSSConfig + SMS SMSConfig + SMTP SmtpConfig + Captcha CaptchaConfig + WxLogin WxLoginConfig + Jimeng JimengConfig + License License + Moderation ModerationConfig +} + +// 配置键名常量 +const ( + ConfigKeySystem = "system" + ConfigKeyNotice = "notice" + ConfigKeyAgreement = "agreement" + ConfigKeyPrivacy = "privacy" + ConfigKeyMarkMap = "mark_map" + ConfigKeyCaptcha = "captcha" + ConfigKeyWxLogin = "wx_login" + ConfigKeyLicense = "license" + ConfigKeySms = "sms" + ConfigKeySmtp = "smtp" + ConfigKeyOss = "oss" + ConfigKeyPayment = "payment" + ConfigKeyModeration = "moderation" + ConfigKeyAI3D = "ai3d" + ConfigKeyJimeng = "jimeng" +) diff --git a/api/core/types/geekai.go b/api/core/types/geekai.go new file mode 100644 index 00000000..1e525fe6 --- /dev/null +++ b/api/core/types/geekai.go @@ -0,0 +1,33 @@ +package types + +import "os" + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// GeekAI 增值服务 +var GeekAPIURL = "https://sapi.geekai.me" + +func init() { + if os.Getenv("GEEK_API_URL") != "" { + GeekAPIURL = os.Getenv("GEEK_API_URL") + } +} + +// CaptchaConfig 行为验证码配置 +type CaptchaConfig struct { + ApiKey string `json:"api_key"` + Type string `json:"type"` // 验证码类型, 可选值: "dot" 或 "slide" + Enabled bool `json:"enabled"` +} + +// WxLoginConfig 微信登录配置 +type WxLoginConfig struct { + ApiKey string `json:"api_key"` + NotifyURL string `json:"notify_url"` // 登录成功回调 URL + Enabled bool `json:"enabled"` // 是否启用微信登录 +} diff --git a/api/core/types/moderation.go b/api/core/types/moderation.go new file mode 100644 index 00000000..a5310439 --- /dev/null +++ b/api/core/types/moderation.go @@ -0,0 +1,73 @@ +package types + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +// 文本审查 +type ModerationConfig struct { + Enable bool `json:"enable"` // 是否启用文本审查 + Active string `json:"active"` + EnableGuide bool `json:"enable_guide"` // 是否启用模型引导提示词 + GuidePrompt string `json:"guide_prompt"` // 模型引导提示词 + Gitee ModerationGiteeConfig `json:"gitee"` + Baidu ModerationBaiduConfig `json:"baidu"` + Tencent ModerationTencentConfig `json:"tencent"` +} + +const ( + ModerationGitee = "gitee" + ModerationBaidu = "baidu" + ModerationTencent = "tencent" +) + +// GiteeAI 文本审查配置 +type ModerationGiteeConfig struct { + ApiKey string `json:"api_key"` + Model string `json:"model"` // 文本审核模型 +} + +// 百度文本审查配置 +type ModerationBaiduConfig struct { + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` +} + +// 腾讯云文本审查配置 +type ModerationTencentConfig struct { + AccessKey string `json:"access_key"` + SecretKey string `json:"secret_key"` +} + +type ModerationResult struct { + Flagged bool `json:"flagged"` + Categories map[string]bool `json:"categories"` + CategoryScores map[string]float64 `json:"category_scores"` +} + +var ModerationCategories = map[string]string{ + "politic": "内容涉及人物、事件或敏感的政治观点", + "porn": "明确的色情内容", + "insult": "具有侮辱、攻击性语言、人身攻击或冒犯性表达", + "violence": "包含暴力、血腥、攻击行为或煽动暴力的言论", + "illegal": "涉及违法活动的内容,如诈骗、赌博等", + "terror": "宣扬恐怖主义、极端暴力或煽动恐怖行为的内容", + "ad": "垃圾广告或未经许可的推广内容", + "spam": "无意义重复内容或诱导性信息", + "abuse": "人身攻击、恶意辱骂或侮辱性言论", + "polity": "涉及国家政治、领导人或政策的违规讨论内容", +} + +// 敏感词来源 +const ( + ModerationSourceChat = "chat" + ModerationSourceMJ = "mj" + ModerationSourceDalle = "dalle" + ModerationSourceSD = "sd" + ModerationSourceSuno = "suno" + ModerationSourceVideo = "video" + ModerationSourceJiMeng = "jimeng" +) diff --git a/api/core/types/order.go b/api/core/types/order.go index c0dd13ac..39d7c8c4 100644 --- a/api/core/types/order.go +++ b/api/core/types/order.go @@ -11,29 +11,25 @@ type OrderStatus int const ( OrderNotPaid = OrderStatus(0) - OrderScanned = OrderStatus(1) // 已扫码 - OrderPaidSuccess = OrderStatus(2) + OrderPaidSuccess = OrderStatus(2) // 已支付 + OrderPaidFailed = OrderStatus(3) // 已关闭 ) type OrderRemark struct { - Days int `json:"days"` // 有效期 - Power int `json:"power"` // 增加算力点数 - Name string `json:"name"` // 产品名称 - Price float64 `json:"price"` - Discount float64 `json:"discount"` + Days int `json:"days"` // 有效期 + Power int `json:"power"` // 增加算力点数 + Name string `json:"name"` // 产品名称 + Price float64 `json:"price"` } -var PayMethods = map[string]string{ +// PayChannel 支付渠道 +var PayChannel = map[string]string{ "alipay": "支付宝商号", - "wechat": "微信商号", - "hupi": "虎皮椒", - "geek": "易支付", + "wxpay": "微信商号", + "epay": "易支付", } -var PayNames = map[string]string{ + +var PayWays = map[string]string{ "alipay": "支付宝", "wxpay": "微信支付", - "qqpay": "QQ钱包", - "jdpay": "京东支付", - "douyin": "抖音支付", - "paypal": "PayPal支付", } diff --git a/api/core/types/oss.go b/api/core/types/oss.go index 9bc93b41..e22f9ef9 100644 --- a/api/core/types/oss.go +++ b/api/core/types/oss.go @@ -8,41 +8,39 @@ package types // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ type OSSConfig struct { - Active string - Local LocalStorageConfig - Minio MiniOssConfig - QiNiu QiNiuOssConfig - AliYun AliYunOssConfig + Active string `json:"active"` + Local LocalStorageConfig `json:"local"` + Minio MiniOssConfig `json:"minio"` + QiNiu QiNiuOssConfig `json:"qiniu"` + AliYun AliYunOssConfig `json:"aliyun"` } + type MiniOssConfig struct { - Endpoint string - AccessKey string - AccessSecret string - Bucket string - SubDir string - UseSSL bool - Domain string + Endpoint string `json:"endpoint"` + AccessKey string `json:"access_key"` + AccessSecret string `json:"access_secret"` + Bucket string `json:"bucket"` + UseSSL bool `json:"use_ssl"` + Domain string `json:"domain"` } type QiNiuOssConfig struct { - Zone string - AccessKey string - AccessSecret string - Bucket string - SubDir string - Domain string + Zone string `json:"zone"` + AccessKey string `json:"access_key"` + AccessSecret string `json:"access_secret"` + Bucket string `json:"bucket"` + Domain string `json:"domain"` } type AliYunOssConfig struct { - Endpoint string - AccessKey string - AccessSecret string - Bucket string - SubDir string - Domain string + Endpoint string `json:"endpoint"` + AccessKey string `json:"access_key"` + AccessSecret string `json:"access_secret"` + Bucket string `json:"bucket"` + Domain string `json:"domain"` } type LocalStorageConfig struct { - BasePath string - BaseURL string + BasePath string `json:"base_path"` + BaseURL string `json:"base_url"` } diff --git a/api/core/types/payment.go b/api/core/types/payment.go new file mode 100644 index 00000000..83011356 --- /dev/null +++ b/api/core/types/payment.go @@ -0,0 +1,60 @@ +package types + +type PaymentConfig struct { + Alipay AlipayConfig `json:"alipay"` // 支付宝支付渠道配置 + Epay EpayConfig `json:"epay"` // 易支付配置 + WxPay WxPayConfig `json:"wxpay"` // 微信支付渠道配置 +} + +// AlipayConfig 支付宝支付配置 +type AlipayConfig struct { + Enabled bool `json:"enabled"` // 是否启用该支付通道 + SandBox bool `json:"sandbox"` // 是否沙盒环境 + AppId string `json:"app_id"` // 应用 ID + PrivateKey string `json:"private_key"` // 应用私钥 + AlipayPublicKey string `json:"alipay_public_key"` // 支付宝公钥 + Domain string `json:"domain"` // 支付回调域名 +} + +func (c *AlipayConfig) Equal(other *AlipayConfig) bool { + return c.AppId == other.AppId && + c.PrivateKey == other.PrivateKey && + c.AlipayPublicKey == other.AlipayPublicKey && + c.Domain == other.Domain +} + +// WxPayConfig 微信支付配置 +type WxPayConfig struct { + Enabled bool `json:"enabled"` // 是否启用该支付通道 + AppId string `json:"app_id"` // 公众号的APPID,如:wxd678efh567hg6787 + MchId string `json:"mch_id"` // 直连商户的商户号,由微信支付生成并下发 + SerialNo string `json:"serial_no"` // 商户证书的证书序列号 + PrivateKey string `json:"private_key"` // 商户证书私钥 + ApiV3Key string `json:"api_v3_key"` // API V3 秘钥 + Domain string `json:"domain"` // 支付回调域名 +} + +func (c *WxPayConfig) Equal(other *WxPayConfig) bool { + return c.AppId == other.AppId && + c.MchId == other.MchId && + c.SerialNo == other.SerialNo && + c.PrivateKey == other.PrivateKey && + c.ApiV3Key == other.ApiV3Key && + c.Domain == other.Domain +} + +// EpayConfig 易支付配置 +type EpayConfig struct { + Enabled bool `json:"enabled"` // 是否启用该支付通道 + AppId string `json:"app_id"` // 商户 ID + PrivateKey string `json:"private_key"` // 私钥 + ApiURL string `json:"api_url"` // z支付 API 网关 + Domain string `json:"domain"` // 支付回调域名 +} + +func (c *EpayConfig) Equal(other *EpayConfig) bool { + return c.AppId == other.AppId && + c.PrivateKey == other.PrivateKey && + c.ApiURL == other.ApiURL && + c.Domain == other.Domain +} diff --git a/api/core/types/session.go b/api/core/types/session.go index 9108e51a..652f4c1f 100644 --- a/api/core/types/session.go +++ b/api/core/types/session.go @@ -8,6 +8,7 @@ package types // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ const LoginUserID = "LOGIN_USER_ID" +const AdminUserID = "ADMIN_USER_ID" const LoginUserCache = "LOGIN_USER_CACHE" const UserAuthHeader = "Authorization" diff --git a/api/core/types/sms.go b/api/core/types/sms.go index 510e8071..7c95ab41 100644 --- a/api/core/types/sms.go +++ b/api/core/types/sms.go @@ -8,26 +8,23 @@ package types // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ type SMSConfig struct { - Active string - Ali SmsConfigAli - Bao SmsConfigBao + Active string `json:"active"` + Ali SmsConfigAli `json:"aliyun"` + Bao SmsConfigBao `json:"bao"` } // SmsConfigAli 阿里云短信平台配置 type SmsConfigAli struct { - AccessKey string - AccessSecret string - Product string - Domain string - Sign string // 短信签名 - CodeTempId string // 验证码短信模板 ID + AccessKey string `json:"access_key"` + AccessSecret string `json:"access_secret"` + Sign string `json:"sign"` // 短信签名 + CodeTempId string `json:"code_temp_id"` // 验证码短信模板 ID } // SmsConfigBao 短信宝平台配置 type SmsConfigBao struct { - Username string //短信宝平台注册的用户名 - Password string //短信宝平台注册的密码 - Domain string //域名 - Sign string // 短信签名 - CodeTemplate string // 验证码短信模板 匹配 + Username string `json:"username"` //短信宝平台注册的用户名 + Password string `json:"password"` //短信宝平台注册的密码 + Sign string `json:"sign"` // 短信签名 + CodeTemplate string `json:"code_template"` // 验证码短信模板 匹配 } diff --git a/api/core/types/smtp.go b/api/core/types/smtp.go new file mode 100644 index 00000000..5625622c --- /dev/null +++ b/api/core/types/smtp.go @@ -0,0 +1,26 @@ +package types + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +type SmtpConfig struct { + UseTls bool `json:"use_tls"` // 是否使用 TLS 发送 + Host string `json:"host"` // 邮件服务器地址 + Port int `json:"port"` // 邮件服务器端口 + AppName string `json:"app_name"` // 应用名称 + From string `json:"from"` // 发件人邮箱地址 + Password string `json:"password"` // 发件人邮箱密码 +} + +func (s *SmtpConfig) Equal(other *SmtpConfig) bool { + return s.UseTls == other.UseTls && + s.Host == other.Host && + s.Port == other.Port && + s.AppName == other.AppName && + s.From == other.From && + s.Password == other.Password +} diff --git a/api/core/types/task.go b/api/core/types/task.go index 599482af..afdcf504 100644 --- a/api/core/types/task.go +++ b/api/core/types/task.go @@ -70,17 +70,18 @@ type SdTaskParams struct { // DallTask DALL-E task type DallTask struct { - ModelId uint `json:"model_id"` - ModelName string `json:"model_name"` - Id uint `json:"id"` - UserId uint `json:"user_id"` - Prompt string `json:"prompt"` - N int `json:"n"` - Quality string `json:"quality"` - Size string `json:"size"` - Style string `json:"style"` - Power int `json:"power"` - TranslateModelId int `json:"translate_model_id"` // 提示词翻译模型ID + ModelId uint `json:"model_id"` + ModelName string `json:"model_name"` + Image []string `json:"image,omitempty"` + Id uint `json:"id"` + UserId uint `json:"user_id"` + Prompt string `json:"prompt"` + N int `json:"n"` + Quality string `json:"quality"` + Size string `json:"size"` + Style string `json:"style"` + Power int `json:"power"` + TranslateModelId int `json:"translate_model_id"` // 提示词翻译模型ID } type SunoTask struct { diff --git a/api/fresh.conf b/api/fresh.conf index aac77afe..92ef5414 100644 --- a/api/fresh.conf +++ b/api/fresh.conf @@ -4,7 +4,7 @@ build_name: runner-build build_log: runner-build-errors.log valid_ext: .go, .tpl, .tmpl, .html no_rebuild_ext: .tpl, .tmpl, .html, .js, .vue -ignored: assets, tmp, web, .git, .idea, test, data +ignored: assets, tmp, web, .git, .idea, test, data, static build_delay: 600 colors: 1 log_color_main: cyan diff --git a/api/go.mod b/api/go.mod index b5b16127..62da5307 100644 --- a/api/go.mod +++ b/api/go.mod @@ -24,11 +24,9 @@ require ( gorm.io/driver/mysql v1.4.7 ) -require github.com/xxl-job/xxl-job-executor-go v1.2.0 - require ( github.com/go-pay/gopay v1.5.101 - github.com/go-rod/rod v0.116.2 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/go-tika v0.3.1 github.com/microcosm-cc/bluemonday v1.0.26 github.com/sashabaranov/go-openai v1.38.1 @@ -50,11 +48,6 @@ require ( github.com/gorilla/css v1.0.0 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect - github.com/ysmood/fetchup v0.3.0 // indirect - github.com/ysmood/goob v0.4.0 // indirect - github.com/ysmood/got v0.40.0 // indirect - github.com/ysmood/gson v0.7.3 // indirect - github.com/ysmood/leakless v0.9.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/mock v0.4.0 // indirect ) @@ -69,7 +62,6 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gaukas/godicttls v0.0.3 // indirect - github.com/go-basic/ipv4 v1.0.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/goccy/go-json v0.10.2 // indirect diff --git a/api/go.sum b/api/go.sum index 808cd458..702add84 100644 --- a/api/go.sum +++ b/api/go.sum @@ -46,8 +46,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-basic/ipv4 v1.0.0 h1:gjyFAa1USC1hhXTkPOwBWDPfMcUaIM+tvo1XzV9EZxs= -github.com/go-basic/ipv4 v1.0.0/go.mod h1:etLBnaxbidQfuqE6wgZQfs38nEWNmzALkxDZe4xY8Dg= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= @@ -80,8 +78,6 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= -github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -89,6 +85,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -261,22 +259,6 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8= github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU= -github.com/xxl-job/xxl-job-executor-go v1.2.0 h1:MTl2DpwrK2+hNjRRks2k7vB3oy+3onqm9OaSarneeLQ= -github.com/xxl-job/xxl-job-executor-go v1.2.0/go.mod h1:bUFhz/5Irp9zkdYk5MxhQcDDT6LlZrI8+rv5mHtQ1mo= -github.com/ysmood/fetchup v0.3.0 h1:UhYz9xnLEVn2ukSuK3KCgcznWpHMdrmbsPpllcylyu8= -github.com/ysmood/fetchup v0.3.0/go.mod h1:hbysoq65PXL0NQeNzUczNYIKpwpkwFL4LXMDEvIQq9A= -github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= -github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= -github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= -github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= -github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= -github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= -github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= -github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= -github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= -github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= -github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= -github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= diff --git a/api/handler/admin/admin_handler.go b/api/handler/admin/admin_handler.go index f4677723..002096ae 100644 --- a/api/handler/admin/admin_handler.go +++ b/api/handler/admin/admin_handler.go @@ -11,6 +11,7 @@ import ( "context" "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" logger2 "geekai/logger" @@ -19,9 +20,10 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "time" + "github.com/go-redis/redis/v8" "github.com/golang-jwt/jwt/v5" - "time" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -45,6 +47,26 @@ func NewAdminHandler(app *core.AppServer, db *gorm.DB, client *redis.Client, cap } } +// RegisterRoutes 注册路由 +func (h *ManagerHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/") + + // 公开接口,不需要授权 + group.POST("login", h.Login) + group.GET("logout", h.Logout) + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("session", h.Session) + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("enable", h.Enable) + group.GET("remove", h.Remove) + group.POST("resetPass", h.ResetPass) + } +} + // Login 登录 func (h *ManagerHandler) Login(c *gin.Context) { var data struct { @@ -59,19 +81,6 @@ func (h *ManagerHandler) Login(c *gin.Context) { return } - if h.App.SysConfig.EnabledVerify { - var check bool - if data.X != 0 { - check = h.captcha.SlideCheck(data) - } else { - check = h.captcha.Check(data) - } - if !check { - resp.ERROR(c, "请先完人机验证") - return - } - } - var manager model.AdminUser res := h.DB.Model(&model.AdminUser{}).Where("username = ?", data.Username).First(&manager) if res.Error != nil { @@ -135,16 +144,15 @@ func (h *ManagerHandler) Logout(c *gin.Context) { // Session 会话检测 func (h *ManagerHandler) Session(c *gin.Context) { - id := h.GetLoginUserId(c) - key := fmt.Sprintf("admin/%d", id) - if _, err := h.redis.Get(context.Background(), key).Result(); err != nil { - resp.NotAuth(c) + id := h.GetAdminId(c) + if id == 0 { + resp.NotAuth(c, "当前用户已退出登录") return } var manager model.AdminUser - res := h.DB.Where("id", id).First(&manager) - if res.Error != nil { - resp.NotAuth(c) + err := h.DB.Where("id", id).First(&manager).Error + if err != nil { + resp.NotAuth(c, "当前用户已退出登录") return } diff --git a/api/handler/admin/api_key_handler.go b/api/handler/admin/api_key_handler.go index 8daa0990..8c66b355 100644 --- a/api/handler/admin/api_key_handler.go +++ b/api/handler/admin/api_key_handler.go @@ -10,6 +10,7 @@ package admin import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/store/model" @@ -30,6 +31,20 @@ func NewApiKeyHandler(app *core.AppServer, db *gorm.DB) *ApiKeyHandler { return &ApiKeyHandler{BaseHandler: handler.BaseHandler{DB: db, App: app}} } +// RegisterRoutes 注册路由 +func (h *ApiKeyHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/apikey/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("set", h.Set) + group.GET("remove", h.Remove) + } +} + func (h *ApiKeyHandler) Save(c *gin.Context) { var data struct { Id uint `json:"id"` diff --git a/api/handler/admin/chat_app_handler.go b/api/handler/admin/chat_app_handler.go index 6763a694..f2ee1c83 100644 --- a/api/handler/admin/chat_app_handler.go +++ b/api/handler/admin/chat_app_handler.go @@ -10,6 +10,7 @@ package admin import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/store/model" @@ -30,14 +31,29 @@ func NewChatAppHandler(app *core.AppServer, db *gorm.DB) *ChatAppHandler { return &ChatAppHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ChatAppHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/role/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("sort", h.Sort) + group.POST("set", h.Set) + group.GET("remove", h.Remove) + } +} + // Save 创建或者更新某个角色 func (h *ChatAppHandler) Save(c *gin.Context) { - var data vo.ChatRole + var data vo.ChatApp if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) return } - var role model.ChatRole + var role model.ChatApp err := utils.CopyObject(data, &role) if err != nil { resp.ERROR(c, types.InvalidArgs) @@ -65,8 +81,8 @@ func (h *ChatAppHandler) Save(c *gin.Context) { } func (h *ChatAppHandler) List(c *gin.Context) { - var items []model.ChatRole - var roles = make([]vo.ChatRole, 0) + var items []model.ChatApp + var roles = make([]vo.ChatApp, 0) res := h.DB.Order("sort_num ASC").Find(&items) if res.Error != nil { resp.ERROR(c, "No data found") @@ -107,7 +123,7 @@ func (h *ChatAppHandler) List(c *gin.Context) { } for _, v := range items { - var role vo.ChatRole + var role vo.ChatApp err := utils.CopyObject(v, &role) if err == nil { role.Id = v.Id @@ -135,7 +151,7 @@ func (h *ChatAppHandler) Sort(c *gin.Context) { } for index, id := range data.Ids { - err := h.DB.Model(&model.ChatRole{}).Where("id = ?", id).Update("sort_num", data.Sorts[index]).Error + err := h.DB.Model(&model.ChatApp{}).Where("id = ?", id).Update("sort_num", data.Sorts[index]).Error if err != nil { resp.ERROR(c, err.Error()) return @@ -157,7 +173,7 @@ func (h *ChatAppHandler) Set(c *gin.Context) { return } - err := h.DB.Model(&model.ChatRole{}).Where("id = ?", data.Id).Update(data.Filed, data.Value).Error + err := h.DB.Model(&model.ChatApp{}).Where("id = ?", data.Id).Update(data.Filed, data.Value).Error if err != nil { resp.ERROR(c, err.Error()) return @@ -172,9 +188,8 @@ func (h *ChatAppHandler) Remove(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - res := h.DB.Where("id", id).Delete(&model.ChatRole{}) + res := h.DB.Where("id", id).Delete(&model.ChatApp{}) if res.Error != nil { - logger.Error("error with update database:", res.Error) resp.ERROR(c, "删除失败!") return } diff --git a/api/handler/admin/chat_app_type_handler.go b/api/handler/admin/chat_app_type_handler.go index d842a279..156579b4 100644 --- a/api/handler/admin/chat_app_type_handler.go +++ b/api/handler/admin/chat_app_type_handler.go @@ -2,12 +2,14 @@ package admin import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/store/model" "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -20,6 +22,21 @@ func NewChatAppTypeHandler(app *core.AppServer, db *gorm.DB) *ChatAppTypeHandler return &ChatAppTypeHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ChatAppTypeHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/app/type/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.POST("save", h.Save) + group.GET("remove", h.Remove) + group.POST("enable", h.Enable) + group.POST("sort", h.Sort) + } +} + // Save 创建或更新App类型 func (h *ChatAppTypeHandler) Save(c *gin.Context) { var data struct { diff --git a/api/handler/admin/chat_handler.go b/api/handler/admin/chat_handler.go index f99defa6..a12e14c6 100644 --- a/api/handler/admin/chat_handler.go +++ b/api/handler/admin/chat_handler.go @@ -9,6 +9,7 @@ package admin import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/store/model" @@ -28,16 +29,31 @@ func NewChatHandler(app *core.AppServer, db *gorm.DB) *ChatHandler { return &ChatHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ChatHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/chat/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.POST("list", h.List) + group.POST("message", h.Messages) + group.GET("history", h.History) + group.GET("remove", h.RemoveChat) + group.GET("message/remove", h.RemoveMessage) + } +} + type chatItemVo struct { - Username string `json:"username"` - UserId uint `json:"user_id"` - ChatId string `json:"chat_id"` - Title string `json:"title"` - Role vo.ChatRole `json:"role"` - Model string `json:"model"` - Token int `json:"token"` - CreatedAt int64 `json:"created_at"` - MsgNum int `json:"msg_num"` // 消息数量 + Username string `json:"username"` + UserId uint `json:"user_id"` + ChatId string `json:"chat_id"` + Title string `json:"title"` + Role vo.ChatApp `json:"role"` + Model string `json:"model"` + Token int `json:"token"` + CreatedAt int64 `json:"created_at"` + MsgNum int `json:"msg_num"` // 消息数量 } func (h *ChatHandler) List(c *gin.Context) { @@ -87,7 +103,7 @@ func (h *ChatHandler) List(c *gin.Context) { } var messages []model.ChatMessage var users []model.User - var roles []model.ChatRole + var roles []model.ChatApp h.DB.Where("chat_id IN ?", chatIds).Find(&messages) h.DB.Where("id IN ?", userIds).Find(&users) h.DB.Where("id IN ?", roleIds).Find(&roles) @@ -95,7 +111,7 @@ func (h *ChatHandler) List(c *gin.Context) { tokenMap := make(map[string]int) userMap := make(map[uint]string) msgMap := make(map[string]int) - roleMap := make(map[uint]vo.ChatRole) + roleMap := make(map[uint]vo.ChatApp) for _, msg := range messages { tokenMap[msg.ChatId] += msg.Tokens msgMap[msg.ChatId] += 1 @@ -104,7 +120,7 @@ func (h *ChatHandler) List(c *gin.Context) { userMap[user.Id] = user.Username } for _, r := range roles { - var roleVo vo.ChatRole + var roleVo vo.ChatApp err := utils.CopyObject(r, &roleVo) if err != nil { continue diff --git a/api/handler/admin/chat_model_handler.go b/api/handler/admin/chat_model_handler.go index 471e8f08..5782e64d 100644 --- a/api/handler/admin/chat_model_handler.go +++ b/api/handler/admin/chat_model_handler.go @@ -8,7 +8,9 @@ package admin // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/store/model" @@ -28,6 +30,22 @@ func NewChatModelHandler(app *core.AppServer, db *gorm.DB) *ChatModelHandler { return &ChatModelHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ChatModelHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/model/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("set", h.Set) + group.POST("sort", h.Sort) + group.GET("remove", h.Remove) + group.POST("batch-remove", h.BatchRemove) + } +} + func (h *ChatModelHandler) Save(c *gin.Context) { var data struct { Id uint `json:"id"` @@ -201,3 +219,33 @@ func (h *ChatModelHandler) Remove(c *gin.Context) { } resp.SUCCESS(c) } + +// BatchRemove 批量删除模型 +func (h *ChatModelHandler) BatchRemove(c *gin.Context) { + var data struct { + Ids []uint `json:"ids"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if len(data.Ids) == 0 { + resp.ERROR(c, "请选择要删除的模型") + return + } + + // 执行批量删除 + err := h.DB.Where("id IN ?", data.Ids).Delete(&model.ChatModel{}).Error + if err != nil { + logger.Error("批量删除模型失败:", err) + resp.ERROR(c, "批量删除失败:"+err.Error()) + return + } + + resp.SUCCESS(c, gin.H{ + "message": fmt.Sprintf("成功删除 %d 个模型", len(data.Ids)), + "deleted_count": len(data.Ids), + }) +} diff --git a/api/handler/admin/config_handler.go b/api/handler/admin/config_handler.go index 76d9ce73..c9210149 100644 --- a/api/handler/admin/config_handler.go +++ b/api/handler/admin/config_handler.go @@ -9,106 +9,399 @@ package admin import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/service" - "geekai/store" + "geekai/service/oss" + "geekai/service/payment" + "geekai/service/sms" "geekai/store/model" "geekai/utils" "geekai/utils/resp" "github.com/gin-gonic/gin" - "github.com/shirou/gopsutil/host" "gorm.io/gorm" ) type ConfigHandler struct { handler.BaseHandler - levelDB *store.LevelDB - licenseService *service.LicenseService + licenseService *service.LicenseService + sysConfig *types.SystemConfig + alipayService *payment.AlipayService + wxpayService *payment.WxPayService + epayService *payment.EPayService + smsManager *sms.SmsManager + uploaderManager *oss.UploaderManager + smtpService *service.SmtpService + captchaService *service.CaptchaService + wxLoginService *service.WxLoginService } -func NewConfigHandler(app *core.AppServer, db *gorm.DB, levelDB *store.LevelDB, licenseService *service.LicenseService) *ConfigHandler { +func NewConfigHandler( + app *core.AppServer, + db *gorm.DB, + licenseService *service.LicenseService, + sysConfig *types.SystemConfig, + alipayService *payment.AlipayService, + wxpayService *payment.WxPayService, + epayService *payment.EPayService, + smsManager *sms.SmsManager, + uploaderManager *oss.UploaderManager, + smtpService *service.SmtpService, + captchaService *service.CaptchaService, + wxLoginService *service.WxLoginService, +) *ConfigHandler { return &ConfigHandler{ - BaseHandler: handler.BaseHandler{App: app, DB: db}, - levelDB: levelDB, - licenseService: licenseService, + BaseHandler: handler.BaseHandler{App: app, DB: db}, + licenseService: licenseService, + sysConfig: sysConfig, + alipayService: alipayService, + wxpayService: wxpayService, + epayService: epayService, + smsManager: smsManager, + uploaderManager: uploaderManager, + smtpService: smtpService, + captchaService: captchaService, + wxLoginService: wxLoginService, } } -func (h *ConfigHandler) Update(c *gin.Context) { - var data struct { - Key string `json:"key"` - Config struct { - types.SystemConfig - Content string `json:"content,omitempty"` - Updated bool `json:"updated,omitempty"` - } `json:"config"` - ConfigBak types.SystemConfig `json:"config_bak,omitempty"` +// RegisterRoutes 注册路由 +func (h *ConfigHandler) RegisterRoutes() { + rg := h.App.Engine.Group("/api/admin/config") + + // 需要管理员登录的接口 + rg.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + rg.POST("update/base", h.UpdateBase) + rg.POST("update/power", h.UpdatePower) + rg.POST("update/notice", h.UpdateNotice) + rg.POST("update/agreement", h.UpdateAgreement) + rg.POST("update/privacy", h.UpdatePrivacy) + rg.POST("update/mark_map", h.UpdateMarkMap) + rg.POST("update/captcha", h.UpdateCaptcha) + rg.POST("update/wx_login", h.UpdateWxLogin) + rg.POST("update/payment", h.UpdatePayment) + rg.POST("update/sms", h.UpdateSms) + rg.POST("update/oss", h.UpdateOss) + rg.POST("update/smtp", h.UpdateStmp) + rg.GET("get", h.Get) + rg.POST("license/active", h.Active) + rg.GET("license/get", h.GetLicense) } +} + +// UpdateBase 更新基础配置 +func (h *ConfigHandler) UpdateBase(c *gin.Context) { + var data types.BaseConfig if err := c.ShouldBindJSON(&data); err != nil { - logger.Errorf("Update config failed: %v", err) resp.ERROR(c, types.InvalidArgs) return } - // ONLY authorized user can change the copyright - if (data.Key == "system" && data.Config.Copyright != data.ConfigBak.Copyright) && !h.licenseService.GetLicense().Configs.DeCopy { - resp.ERROR(c, "您无权修改版权信息,请先联系作者获取授权") + // 未授权的话不允许修改版权 + license := h.licenseService.GetLicense() + if !license.IsActive && data.Copyright != h.sysConfig.Base.Copyright { + resp.ERROR(c, "未授权系统不允许修改版权信息") return } - // 如果要启用图形验证码功能,则检查是否配置了 API 服务 - if data.Config.EnabledVerify && h.App.Config.ApiConfig.AppId == "" { - resp.ERROR(c, "启用验证码服务需要先配置 GeekAI 官方 API 服务 AppId 和 Token") + // 未授权的话不允许修改 Logo + if !license.IsActive && data.Logo != h.sysConfig.Base.Logo { + resp.ERROR(c, "未授权系统不允许修改 Logo") return } - value := utils.JsonEncode(&data.Config) - config := model.Config{Name: data.Key, Value: value} - res := h.DB.FirstOrCreate(&config, model.Config{Name: data.Key}) - if res.Error != nil { - resp.ERROR(c, res.Error.Error()) + err := h.Update(types.ConfigKeySystem, data) + if err != nil { + resp.ERROR(c, err.Error()) return } - if config.Id > 0 { - config.Value = value - res := h.DB.Updates(&config) - if res.Error != nil { - resp.ERROR(c, res.Error.Error()) - return - } + h.sysConfig.Base = data - // update config cache for AppServer - var cfg model.Config - h.DB.Where("name", data.Key).First(&cfg) - var err error - if data.Key == "system" { - err = utils.JsonDecode(cfg.Value, &h.App.SysConfig) - } - if err != nil { - resp.ERROR(c, "Failed to update config cache: "+err.Error()) - return - } - logger.Infof("Update AppServer's config successfully: %v", config.Value) - } - - resp.SUCCESS(c, config) + resp.SUCCESS(c, data) } -// Get 获取指定的系统配置 -func (h *ConfigHandler) Get(c *gin.Context) { - key := c.Query("key") +// UpdatePower 更新系统配置 +func (h *ConfigHandler) UpdatePower(c *gin.Context) { + var data struct { + InitPower int `json:"init_power,omitempty"` // 新用户注册赠送算力值 + DailyPower int `json:"daily_power,omitempty"` // 每日签到赠送算力 + InvitePower int `json:"invite_power,omitempty"` // 邀请新用户赠送算力值 + MjPower int `json:"mj_power,omitempty"` // MJ 绘画消耗算力 + MjActionPower int `json:"mj_action_power,omitempty"` // MJ 操作(放大,变换)消耗算力 + SdPower int `json:"sd_power,omitempty"` // SD 绘画消耗算力 + SunoPower int `json:"suno_power,omitempty"` // Suno 生成歌曲消耗算力 + LumaPower int `json:"luma_power,omitempty"` // Luma 生成视频消耗算力 + KeLingPowers map[string]int `json:"keling_powers,omitempty"` // 可灵生成视频消耗算力 + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + h.sysConfig.Base.InitPower = data.InitPower + h.sysConfig.Base.DailyPower = data.DailyPower + h.sysConfig.Base.InvitePower = data.InvitePower + h.sysConfig.Base.MjPower = data.MjPower + h.sysConfig.Base.MjActionPower = data.MjActionPower + h.sysConfig.Base.SdPower = data.SdPower + h.sysConfig.Base.SunoPower = data.SunoPower + h.sysConfig.Base.LumaPower = data.LumaPower + h.sysConfig.Base.KeLingPowers = data.KeLingPowers + + err := h.Update(types.ConfigKeySystem, h.sysConfig.Base) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + resp.SUCCESS(c, h.sysConfig.Base) +} + +// UpdateNotice 更新公告配置 +func (h *ConfigHandler) UpdateNotice(c *gin.Context) { + var data struct { + Content string `json:"content"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyNotice, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + resp.SUCCESS(c, data) +} + +// UpdateAgreement 更新用户协议配置 +func (h *ConfigHandler) UpdateAgreement(c *gin.Context) { + var data struct { + Content string `json:"content"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyAgreement, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + resp.SUCCESS(c, data) +} + +// UpdatePrivacy 更新隐私政策配置 +func (h *ConfigHandler) UpdatePrivacy(c *gin.Context) { + var data struct { + Content string `json:"content"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyPrivacy, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + resp.SUCCESS(c, data) +} + +// UpdateMarkMap 更新思维导图配置 +func (h *ConfigHandler) UpdateMarkMap(c *gin.Context) { + var data struct { + Content string `json:"content"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyMarkMap, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + resp.SUCCESS(c, data) +} + +// UpdateCaptcha 更新行为验证码配置 +func (h *ConfigHandler) UpdateCaptcha(c *gin.Context) { + var data types.CaptchaConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyCaptcha, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + h.captchaService.UpdateConfig(data) + resp.SUCCESS(c, data) + +} + +// UpdatePayment 更新支付配置 +func (h *ConfigHandler) UpdatePayment(c *gin.Context) { + var data types.PaymentConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyPayment, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + // 如果启用状态发生改变,则需要更新支付服务配置 + if data.WxPay.Enabled { + err = h.wxpayService.UpdateConfig(&data.WxPay) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + } + if data.Epay.Enabled { + h.epayService.UpdateConfig(&data.Epay) + } + if data.Alipay.Enabled { + err = h.alipayService.UpdateConfig(&data.Alipay) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + } + + h.sysConfig.Payment = data + resp.SUCCESS(c, data) +} + +// UpdateSms 更新短信配置 +func (h *ConfigHandler) UpdateSms(c *gin.Context) { + var data types.SMSConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeySms, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + // 更新服务配置 + h.smsManager.UpdateConfig(data) + + resp.SUCCESS(c, data) +} + +// UpdateOss 更新 Oss 配置 +func (h *ConfigHandler) UpdateOss(c *gin.Context) { + var data types.OSSConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeyOss, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + // 更新服务配置 + h.uploaderManager.UpdateConfig(data) + h.sysConfig.OSS = data + + resp.SUCCESS(c, data) +} + +// UpdateStmp 更新 Stmp 配置 +func (h *ConfigHandler) UpdateStmp(c *gin.Context) { + var data types.SmtpConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.Update(types.ConfigKeySmtp, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + // 更新服务配置 + h.smtpService.UpdateConfig(&data) + h.sysConfig.SMTP = data + resp.SUCCESS(c, data) +} + +// UpdateWxLogin 更新微信登录配置 +func (h *ConfigHandler) UpdateWxLogin(c *gin.Context) { + var data types.WxLoginConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + err := h.Update(types.ConfigKeyWxLogin, data) + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + if data.Enabled { + h.wxLoginService.UpdateConfig(data) + } + + h.sysConfig.WxLogin = data + resp.SUCCESS(c, data) +} + +// Update 更新系统配置 +func (h *ConfigHandler) Update(name string, value any) error { var config model.Config - res := h.DB.Where("name", key).First(&config) + err := h.DB.Where("name", name).First(&config).Error + if err != nil { // 不存在则创建 + config.Name = name + config.Value = utils.JsonEncode(value) + return h.DB.Create(&config).Error + } else { // 存在则更新 + config.Value = utils.JsonEncode(value) + return h.DB.Updates(&config).Error + } + +} + +// Get 获取指定名称的系统配置 +func (h *ConfigHandler) Get(c *gin.Context) { + name := c.Query("key") + var config model.Config + res := h.DB.Where("name", name).First(&config) if res.Error != nil { resp.ERROR(c, res.Error.Error()) return } - var value map[string]interface{} + var value map[string]any err := utils.JsonDecode(config.Value, &value) if err != nil { resp.ERROR(c, err.Error()) @@ -127,19 +420,21 @@ func (h *ConfigHandler) Active(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - info, err := host.Info() + + err := h.licenseService.ActiveLicense(data.License) + license := h.licenseService.GetLicense() if err != nil { resp.ERROR(c, err.Error()) return } - - err = h.licenseService.ActiveLicense(data.License, info.HostID) - if err != nil { + if err := h.Update(types.ConfigKeyLicense, license); err != nil { resp.ERROR(c, err.Error()) return } + // 更新系统配置 + h.sysConfig.License = *license - resp.SUCCESS(c) + resp.SUCCESS(c, license.MachineId) } @@ -148,69 +443,3 @@ func (h *ConfigHandler) GetLicense(c *gin.Context) { license := h.licenseService.GetLicense() resp.SUCCESS(c, license) } - -// FixData 修复数据 -func (h *ConfigHandler) FixData(c *gin.Context) { - resp.ERROR(c, "当前升级版本没有数据需要修正!") - //var fixed bool - //version := "data_fix_4.1.4" - //err := h.levelDB.Get(version, &fixed) - //if err == nil || fixed { - // resp.ERROR(c, "当前版本数据修复已完成,请不要重复执行操作") - // return - //} - //tx := h.DB.Begin() - //var users []model.User - //err = tx.Find(&users).Error - //if err != nil { - // resp.ERROR(c, err.Error()) - // return - //} - //for _, user := range users { - // if user.Email != "" || user.Mobile != "" { - // continue - // } - // if utils.IsValidEmail(user.Username) { - // user.Email = user.Username - // } else if utils.IsValidMobile(user.Username) { - // user.Mobile = user.Username - // } - // err = tx.Save(&user).Error - // if err != nil { - // resp.ERROR(c, err.Error()) - // tx.Rollback() - // return - // } - //} - // - //var orders []model.Order - //err = h.DB.Find(&orders).Error - //if err != nil { - // resp.ERROR(c, err.Error()) - // return - //} - //for _, order := range orders { - // if order.PayWay == "支付宝" { - // order.PayWay = "alipay" - // order.PayType = "alipay" - // } else if order.PayWay == "微信支付" { - // order.PayWay = "wechat" - // order.PayType = "wxpay" - // } else if order.PayWay == "hupi" { - // order.PayType = "wxpay" - // } - // err = tx.Save(&order).Error - // if err != nil { - // resp.ERROR(c, err.Error()) - // tx.Rollback() - // return - // } - //} - //tx.Commit() - //err = h.levelDB.Put(version, true) - //if err != nil { - // resp.ERROR(c, err.Error()) - // return - //} - //resp.SUCCESS(c) -} diff --git a/api/handler/admin/dashboard_handler.go b/api/handler/admin/dashboard_handler.go index 2ce5d34e..74befe6d 100644 --- a/api/handler/admin/dashboard_handler.go +++ b/api/handler/admin/dashboard_handler.go @@ -13,10 +13,11 @@ import ( "geekai/handler" "geekai/store/model" "geekai/utils/resp" + "time" + "github.com/gin-gonic/gin" "github.com/shopspring/decimal" "gorm.io/gorm" - "time" ) type DashboardHandler struct { @@ -27,46 +28,161 @@ func NewDashboardHandler(app *core.AppServer, db *gorm.DB) *DashboardHandler { return &DashboardHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *DashboardHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/dashboard/") + group.GET("stats", h.Stats) +} + +// statsVo 增加 recentOrders、recentUsers 字段 +// 最近订单 +type OrderBrief struct { + OrderNo string `json:"order_no"` + Amount float64 `json:"amount"` + CreatedAt time.Time `json:"created_at"` +} + +// 最近用户 +type UserBrief struct { + Nickname string `json:"nickname"` + Avatar string `json:"avatar"` + LastActive time.Time `json:"last_active"` +} + type statsVo struct { - Users int64 `json:"users"` - Chats int64 `json:"chats"` - Tokens int `json:"tokens"` - Income float64 `json:"income"` - Chart map[string]map[string]float64 `json:"chart"` + Users int64 `json:"users"` + Chats int64 `json:"chats"` + Tokens int `json:"tokens"` + Income float64 `json:"income"` + Chart map[string]map[string]float64 `json:"chart"` + TodayUsers int64 `json:"todayUsers"` + TodayChats int64 `json:"todayChats"` + TodayTokens int `json:"todayTokens"` + TodayIncome float64 `json:"todayIncome"` + TodayOrders int64 `json:"todayOrders"` + TodayImageJobs int64 `json:"todayImageJobs"` + TodayVideoJobs int64 `json:"todayVideoJobs"` + TodayMusicJobs int64 `json:"todayMusicJobs"` + Orders int64 `json:"orders"` + ImageJobs int64 `json:"imageJobs"` + VideoJobs int64 `json:"videoJobs"` + MusicJobs int64 `json:"musicJobs"` + RecentOrders []OrderBrief `json:"recentOrders"` + RecentUsers []UserBrief `json:"recentUsers"` } func (h *DashboardHandler) Stats(c *gin.Context) { stats := statsVo{} - // new users statistic - var userCount int64 now := time.Now() zeroTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) - res := h.DB.Model(&model.User{}).Where("created_at > ?", zeroTime).Count(&userCount) - if res.Error == nil { - stats.Users = userCount + + // 总用户数 + h.DB.Model(&model.User{}).Count(&stats.Users) + + // 今日新增用户 + h.DB.Model(&model.User{}).Where("created_at > ?", zeroTime).Count(&stats.TodayUsers) + + // 总对话数 + h.DB.Model(&model.ChatItem{}).Count(&stats.Chats) + + // 今日新增对话 + h.DB.Model(&model.ChatItem{}).Where("created_at > ?", zeroTime).Count(&stats.TodayChats) + + // 总算力消耗 + var powerLogs []model.PowerLog + h.DB.Where("mark = ?", types.PowerSub).Find(&powerLogs) + for _, item := range powerLogs { + stats.Tokens += item.Amount } - // new chats statistic - var chatCount int64 - res = h.DB.Model(&model.ChatItem{}).Where("created_at > ?", zeroTime).Count(&chatCount) - if res.Error == nil { - stats.Chats = chatCount + // 今日算力消耗 + var todayPowerLogs []model.PowerLog + h.DB.Where("mark = ?", types.PowerSub).Where("created_at > ?", zeroTime).Find(&todayPowerLogs) + for _, item := range todayPowerLogs { + stats.TodayTokens += item.Amount } - // tokens took stats - var historyMessages []model.ChatMessage - res = h.DB.Where("created_at > ?", zeroTime).Find(&historyMessages) - for _, item := range historyMessages { - stats.Tokens += item.Tokens - } - - // 订单收入 - var orders []model.Order - res = h.DB.Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", zeroTime).Find(&orders) - for _, item := range orders { + // 总收入 + var allOrders []model.Order + h.DB.Where("status = ?", types.OrderPaidSuccess).Find(&allOrders) + for _, item := range allOrders { stats.Income += item.Amount } + // 今日收入 + var todayOrders []model.Order + h.DB.Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", zeroTime).Find(&todayOrders) + for _, item := range todayOrders { + stats.TodayIncome += item.Amount + } + + // 订单总数 + h.DB.Model(&model.Order{}).Where("status = ?", types.OrderPaidSuccess).Count(&stats.Orders) + + // 今日订单数 + h.DB.Model(&model.Order{}).Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", zeroTime).Count(&stats.TodayOrders) + + // 图片生成任务统计 + var mjJobs, sdJobs, dallJobs, jimengImageJobs int64 + h.DB.Model(&model.MidJourneyJob{}).Count(&mjJobs) + h.DB.Model(&model.SdJob{}).Count(&sdJobs) + h.DB.Model(&model.DallJob{}).Count(&dallJobs) + h.DB.Model(&model.JimengJob{}).Where("type IN ?", []string{"text_to_image", "image_to_image", "image_edit", "image_effects"}).Count(&jimengImageJobs) + stats.ImageJobs = mjJobs + sdJobs + dallJobs + jimengImageJobs + + logger.Info("stats.ImageJobs", stats.ImageJobs) + + // 今日图片生成任务统计 + var todayMjJobs, todaySdJobs, todayDallJobs, todayJimengImageJobs int64 + h.DB.Model(&model.MidJourneyJob{}).Where("created_at > ?", zeroTime).Count(&todayMjJobs) + h.DB.Model(&model.SdJob{}).Where("created_at > ?", zeroTime).Count(&todaySdJobs) + h.DB.Model(&model.DallJob{}).Where("created_at > ?", zeroTime).Count(&todayDallJobs) + h.DB.Model(&model.JimengJob{}).Where("type IN ?", []string{"text_to_image", "image_to_image", "image_edit", "image_effects"}).Where("created_at > ?", zeroTime).Count(&todayJimengImageJobs) + stats.TodayImageJobs = todayMjJobs + todaySdJobs + todayDallJobs + todayJimengImageJobs + + // 视频生成任务统计 + var videoJobs, jimengVideoJobs int64 + h.DB.Model(&model.VideoJob{}).Count(&videoJobs) + h.DB.Model(&model.JimengJob{}).Where("type IN ?", []string{"text_to_video", "image_to_video"}).Count(&jimengVideoJobs) + stats.VideoJobs = videoJobs + jimengVideoJobs + + // 今日视频生成任务统计 + var todayVideoJobs, todayJimengVideoJobs int64 + h.DB.Model(&model.VideoJob{}).Where("created_at > ?", zeroTime).Count(&todayVideoJobs) + h.DB.Model(&model.JimengJob{}).Where("type IN ?", []string{"text_to_video", "image_to_video"}).Where("created_at > ?", zeroTime).Count(&todayJimengVideoJobs) + stats.TodayVideoJobs = todayVideoJobs + todayJimengVideoJobs + + // 音乐生成任务统计 + h.DB.Model(&model.SunoJob{}).Count(&stats.MusicJobs) + + // 今日音乐生成任务统计 + h.DB.Model(&model.SunoJob{}).Where("created_at > ?", zeroTime).Count(&stats.TodayMusicJobs) + + // recentOrders: 最近10条已支付订单 + var orderList []model.Order + h.DB.Model(&model.Order{}).Where("status = ?", types.OrderPaidSuccess).Order("created_at desc").Limit(10).Find(&orderList) + for _, o := range orderList { + stats.RecentOrders = append(stats.RecentOrders, OrderBrief{ + OrderNo: o.OrderNo, + Amount: o.Amount, + CreatedAt: o.CreatedAt, + }) + } + // recentUsers: 最近10个注册用户 + var userList []model.User + h.DB.Model(&model.User{}).Order("created_at desc").Limit(10).Find(&userList) + for _, u := range userList { + lastActive := u.UpdatedAt + if lastActive.IsZero() { + lastActive = u.CreatedAt + } + stats.RecentUsers = append(stats.RecentUsers, UserBrief{ + Nickname: u.Nickname, + Avatar: u.Avatar, + LastActive: lastActive, + }) + } + // 统计7天的订单的图表 startDate := now.Add(-7 * 24 * time.Hour).Format("2006-01-02") var statsChart = make(map[string]map[string]float64) @@ -81,23 +197,29 @@ func (h *DashboardHandler) Stats(c *gin.Context) { // 统计用户7天增加的曲线 var users []model.User - res = h.DB.Model(&model.User{}).Where("created_at > ?", startDate).Find(&users) - if res.Error == nil { + err := h.DB.Model(&model.User{}).Where("created_at > ?", startDate).Find(&users).Error + if err == nil { for _, item := range users { userStatistic[item.CreatedAt.Format("2006-01-02")] += 1 } } - // 统计7天Token 消耗 - res = h.DB.Where("created_at > ?", startDate).Find(&historyMessages) - for _, item := range historyMessages { - historyMessagesStatistic[item.CreatedAt.Format("2006-01-02")] += float64(item.Tokens) + // 统计7天算力消耗 + var chartPowerLogs []model.PowerLog + err = h.DB.Where("mark = ?", types.PowerSub).Where("created_at > ?", startDate).Find(&chartPowerLogs).Error + if err == nil { + for _, item := range chartPowerLogs { + historyMessagesStatistic[item.CreatedAt.Format("2006-01-02")] += float64(item.Amount) + } } // 统计最近7天的订单 - res = h.DB.Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", startDate).Find(&orders) - for _, item := range orders { - incomeStatistic[item.CreatedAt.Format("2006-01-02")], _ = decimal.NewFromFloat(incomeStatistic[item.CreatedAt.Format("2006-01-02")]).Add(decimal.NewFromFloat(item.Amount)).Float64() + var orders []model.Order + err = h.DB.Where("status = ?", types.OrderPaidSuccess).Where("created_at > ?", startDate).Find(&orders).Error + if err == nil { + for _, item := range orders { + incomeStatistic[item.CreatedAt.Format("2006-01-02")], _ = decimal.NewFromFloat(incomeStatistic[item.CreatedAt.Format("2006-01-02")]).Add(decimal.NewFromFloat(item.Amount)).Float64() + } } statsChart["users"] = userStatistic diff --git a/api/handler/admin/function_handler.go b/api/handler/admin/function_handler.go index 90af01e7..402e4c3e 100644 --- a/api/handler/admin/function_handler.go +++ b/api/handler/admin/function_handler.go @@ -9,6 +9,7 @@ package admin import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/store/model" @@ -30,6 +31,21 @@ func NewFunctionHandler(app *core.AppServer, db *gorm.DB) *FunctionHandler { return &FunctionHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *FunctionHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/function/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("set", h.Set) + group.GET("remove", h.Remove) + group.GET("token", h.GenToken) + } +} + func (h *FunctionHandler) Save(c *gin.Context) { var data vo.Function if err := c.ShouldBindJSON(&data); err != nil { @@ -119,7 +135,6 @@ func (h *FunctionHandler) GenToken(c *gin.Context) { }) tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey)) if err != nil { - logger.Error("error with generate token", err) resp.ERROR(c) return } diff --git a/api/handler/admin/image_handler.go b/api/handler/admin/image_handler.go index f1737435..045cb367 100644 --- a/api/handler/admin/image_handler.go +++ b/api/handler/admin/image_handler.go @@ -10,6 +10,7 @@ package admin import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/service" @@ -33,6 +34,20 @@ func NewImageHandler(app *core.AppServer, db *gorm.DB, userService *service.User return &ImageHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, userService: userService, uploader: manager} } +// RegisterRoutes 注册路由 +func (h *ImageHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/image/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.POST("list/mj", h.MjList) + group.POST("list/sd", h.SdList) + group.POST("list/dall", h.DallList) + group.GET("remove", h.Remove) + } +} + type imageQuery struct { Prompt string `json:"prompt"` Username string `json:"username"` diff --git a/api/handler/admin/jimeng_handler.go b/api/handler/admin/jimeng_handler.go index 0637f69a..fdccf307 100644 --- a/api/handler/admin/jimeng_handler.go +++ b/api/handler/admin/jimeng_handler.go @@ -21,18 +21,18 @@ import ( // AdminJimengHandler 管理后台即梦AI处理器 type AdminJimengHandler struct { handler.BaseHandler - jimengService *jimeng.Service - userService *service.UserService - uploader *oss.UploaderManager + jimengClient *jimeng.Client + userService *service.UserService + uploader *oss.UploaderManager } // NewAdminJimengHandler 创建管理后台即梦AI处理器 -func NewAdminJimengHandler(app *core.AppServer, db *gorm.DB, jimengService *jimeng.Service, userService *service.UserService, uploader *oss.UploaderManager) *AdminJimengHandler { +func NewAdminJimengHandler(app *core.AppServer, db *gorm.DB, jimengClient *jimeng.Client, userService *service.UserService, uploader *oss.UploaderManager) *AdminJimengHandler { return &AdminJimengHandler{ - BaseHandler: handler.BaseHandler{App: app, DB: db}, - jimengService: jimengService, - userService: userService, - uploader: uploader, + BaseHandler: handler.BaseHandler{App: app, DB: db}, + jimengClient: jimengClient, + userService: userService, + uploader: uploader, } } @@ -43,7 +43,6 @@ func (h *AdminJimengHandler) RegisterRoutes() { rg.GET("/jobs/:id", h.JobDetail) rg.POST("/jobs/remove", h.BatchRemove) rg.GET("/stats", h.Stats) - rg.GET("/config", h.GetConfig) rg.POST("/config/update", h.UpdateConfig) } @@ -213,12 +212,6 @@ func (h *AdminJimengHandler) Stats(c *gin.Context) { resp.SUCCESS(c, result) } -// GetConfig 获取即梦AI配置 -func (h *AdminJimengHandler) GetConfig(c *gin.Context) { - jimengConfig := h.jimengService.GetConfig() - resp.SUCCESS(c, jimengConfig) -} - // UpdateConfig 更新即梦AI配置 func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) { var req types.JimengConfig @@ -266,31 +259,35 @@ func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) { // 保存配置 tx := h.DB.Begin() value := utils.JsonEncode(&req) - config := model.Config{Name: "jimeng", Value: value} + var exist model.Config + tx.Where("name", types.ConfigKeyJimeng).First(&exist) - err := tx.FirstOrCreate(&config, model.Config{Name: "jimeng"}).Error - if err != nil { - resp.ERROR(c, "保存配置失败: "+err.Error()) - return - } - - if config.Id > 0 { - config.Value = value - err = tx.Updates(&config).Error + if exist.Id > 0 { + exist.Value = value + err := tx.Updates(&exist).Error if err != nil { resp.ERROR(c, "更新配置失败: "+err.Error()) return } + } else { + exist.Name = types.ConfigKeyJimeng + exist.Value = value + err := tx.Create(&exist).Error + if err != nil { + resp.ERROR(c, "创建配置失败: "+err.Error()) + return + } } // 更新服务中的客户端配置 - updateErr := h.jimengService.UpdateClientConfig(req.AccessKey, req.SecretKey) - if updateErr != nil { - resp.ERROR(c, updateErr.Error()) + err := h.jimengClient.UpdateConfig(req) + if err != nil { + resp.ERROR(c, err.Error()) tx.Rollback() return } tx.Commit() + h.App.SysConfig.Jimeng = req resp.SUCCESS(c, gin.H{"message": "配置更新成功"}) } diff --git a/api/handler/admin/media_handler.go b/api/handler/admin/media_handler.go index da18ddfa..6510b5a8 100644 --- a/api/handler/admin/media_handler.go +++ b/api/handler/admin/media_handler.go @@ -10,6 +10,7 @@ package admin import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/service" @@ -33,6 +34,19 @@ func NewMediaHandler(app *core.AppServer, db *gorm.DB, userService *service.User return &MediaHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, userService: userService, uploader: manager} } +// RegisterRoutes 注册路由 +func (h *MediaHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/media/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.POST("suno", h.SunoList) + group.POST("videos", h.Videos) + group.GET("remove", h.Remove) + } +} + type mediaQuery struct { Type string `json:"type"` // 任务类型 luma, keling Prompt string `json:"prompt"` diff --git a/api/handler/admin/menu_handler.go b/api/handler/admin/menu_handler.go index d5b45e40..e7f2b4ab 100644 --- a/api/handler/admin/menu_handler.go +++ b/api/handler/admin/menu_handler.go @@ -27,6 +27,16 @@ func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler { return &MenuHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *MenuHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/menu/") + group.POST("save", h.Save) + group.GET("list", h.List) + group.POST("enable", h.Enable) + group.POST("sort", h.Sort) + group.GET("remove", h.Remove) +} + func (h *MenuHandler) Save(c *gin.Context) { var data struct { Id uint `json:"id"` diff --git a/api/handler/admin/moderation_handler.go b/api/handler/admin/moderation_handler.go new file mode 100644 index 00000000..86317332 --- /dev/null +++ b/api/handler/admin/moderation_handler.go @@ -0,0 +1,333 @@ +package admin + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "fmt" + "geekai/core" + "geekai/core/middleware" + "geekai/core/types" + "geekai/handler" + "geekai/service/moderation" + "geekai/store/model" + "geekai/utils" + "geekai/utils/resp" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type ModerationHandler struct { + handler.BaseHandler + sysConfig *types.SystemConfig + moderationManager *moderation.ServiceManager +} + +func NewModerationHandler(app *core.AppServer, db *gorm.DB, sysConfig *types.SystemConfig, moderationManager *moderation.ServiceManager) *ModerationHandler { + return &ModerationHandler{BaseHandler: handler.BaseHandler{DB: db, App: app}, sysConfig: sysConfig, moderationManager: moderationManager} +} + +// RegisterRoutes 注册路由 +func (h *ModerationHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/moderation/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.POST("list", h.List) + group.GET("remove", h.Remove) + group.POST("batch-remove", h.BatchRemove) + group.GET("source-list", h.GetSourceList) + group.POST("config", h.UpdateModeration) + group.POST("test", h.TestModeration) + } +} + +// List 获取文本审核记录列表 +func (h *ModerationHandler) List(c *gin.Context) { + var data struct { + Username string `json:"username"` + Source string `json:"source"` + StartDate string `json:"start_date"` + EndDate string `json:"end_date"` + Page int `json:"page"` + PageSize int `json:"page_size"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + session := h.DB.Session(&gorm.Session{}) + + // 构建查询条件 + if data.Username != "" { + // 通过用户名查找用户ID + var user model.User + if err := h.DB.Where("username LIKE ?", "%"+data.Username+"%").First(&user).Error; err == nil { + session = session.Where("user_id", user.Id) + } + } + + if data.Source != "" { + session = session.Where("source", data.Source) + } + + if data.StartDate != "" && data.EndDate != "" { + startTime := data.StartDate + " 00:00:00" + endTime := data.EndDate + " 23:59:59" + session = session.Where("created_at >= ? AND created_at <= ?", startTime, endTime) + } + + // 统计总数 + var total int64 + session.Model(&model.Moderation{}).Count(&total) + + // 分页 + page := data.Page + pageSize := data.PageSize + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = 20 + } + + offset := (page - 1) * pageSize + session = session.Offset(offset).Limit(pageSize) + + // 查询数据 + var items []model.Moderation + err := session.Order("id DESC").Find(&items).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + // 获取用户信息 + userIds := make([]uint, 0) + for _, item := range items { + userIds = append(userIds, item.UserId) + } + + var users []model.User + if len(userIds) > 0 { + h.DB.Where("id IN ?", userIds).Find(&users) + } + + userMap := make(map[uint]string) + for _, user := range users { + userMap[user.Id] = user.Username + } + + // 转换为响应数据 + list := make([]map[string]any, 0) + for _, item := range items { + var moderation types.ModerationResult + err := utils.JsonDecode(item.Result, &moderation) + if err != nil { + continue + } + var result []string + for value, label := range types.ModerationCategories { + if moderation.Categories[value] { + result = append(result, label) + } + } + list = append(list, map[string]any{ + "id": item.Id, + "user_id": item.UserId, + "username": userMap[item.UserId], + "source": item.Source, + "input": item.Input, + "output": item.Output, + "result": result, + "created_at": item.CreatedAt.Unix(), + }) + } + + resp.SUCCESS(c, map[string]any{ + "items": list, + "total": total, + "page": page, + "page_size": pageSize, + }) +} + +func (h *ModerationHandler) Remove(c *gin.Context) { + id := h.GetInt(c, "id", 0) + if id <= 0 { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.DB.Where("id", id).Delete(&model.Moderation{}).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + resp.SUCCESS(c) +} + +// BatchRemove 批量删除文本审核记录 +func (h *ModerationHandler) BatchRemove(c *gin.Context) { + var data struct { + Ids []uint `json:"ids"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if len(data.Ids) == 0 { + resp.ERROR(c, "请选择要删除的记录") + return + } + + err := h.DB.Where("id IN ?", data.Ids).Delete(&model.Moderation{}).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + resp.SUCCESS(c) +} + +// 获取 source 列表 +func (h *ModerationHandler) GetSourceList(c *gin.Context) { + sources := []gin.H{ + { + "id": types.ModerationSourceChat, + "name": "AI对话", + }, + { + "id": types.ModerationSourceMJ, + "name": "Midjourney 绘图", + }, + { + "id": types.ModerationSourceDalle, + "name": "Dalle 绘图", + }, + { + "id": types.ModerationSourceSD, + "name": "StableDiffusion 绘图", + }, + { + "id": types.ModerationSourceSuno, + "name": "Suno 音乐", + }, + { + "id": types.ModerationSourceVideo, + "name": "视频生成", + }, + { + "id": types.ModerationSourceJiMeng, + "name": "即梦AI", + }, + } + + resp.SUCCESS(c, sources) +} + +// UpdateModeration 更新文本审查配置 +func (h *ModerationHandler) UpdateModeration(c *gin.Context) { + var data types.ModerationConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + var config model.Config + err := h.DB.Where("name", types.ConfigKeyModeration).First(&config).Error + if err != nil { + config.Name = types.ConfigKeyModeration + config.Value = utils.JsonEncode(data) + err = h.DB.Create(&config).Error + } else { + config.Value = utils.JsonEncode(data) + err = h.DB.Updates(&config).Error + } + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + h.moderationManager.UpdateConfig(data) + h.sysConfig.Moderation = data + + resp.SUCCESS(c, data) +} + +// 测试结果类型,用于前端显示 +type ModerationTestResult struct { + IsAbnormal bool `json:"isAbnormal"` + Details []ModerationTestDetail `json:"details"` +} + +type ModerationTestDetail struct { + Category string `json:"category"` + Description string `json:"description"` + Confidence string `json:"confidence"` + IsCategory bool `json:"isCategory"` +} + +// TestModeration 测试文本审查服务 +func (h *ModerationHandler) TestModeration(c *gin.Context) { + var data struct { + Text string `json:"text"` + Service string `json:"service"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if data.Text == "" { + resp.ERROR(c, "测试文本不能为空") + return + } + + // 检查是否启用了文本审查 + if !h.sysConfig.Moderation.Enable { + resp.ERROR(c, "文本审查服务未启用") + return + } + + // 获取当前激活的审核服务 + service := h.moderationManager.GetService() + // 执行文本审核 + result, err := service.Moderate(data.Text) + if err != nil { + resp.ERROR(c, "审核服务调用失败: "+err.Error()) + return + } + + // 转换为前端需要的格式 + testResult := ModerationTestResult{ + IsAbnormal: result.Flagged, + Details: make([]ModerationTestDetail, 0), + } + + // 构建详细信息 + for category, description := range types.ModerationCategories { + score := result.CategoryScores[category] + isCategory := result.Categories[category] + + testResult.Details = append(testResult.Details, ModerationTestDetail{ + Category: category, + Description: description, + Confidence: fmt.Sprintf("%.2f", score), + IsCategory: isCategory, + }) + } + + resp.SUCCESS(c, testResult) +} diff --git a/api/handler/admin/order_handler.go b/api/handler/admin/order_handler.go index cc69753e..85a59acd 100644 --- a/api/handler/admin/order_handler.go +++ b/api/handler/admin/order_handler.go @@ -29,6 +29,14 @@ func NewOrderHandler(app *core.AppServer, db *gorm.DB) *OrderHandler { return &OrderHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *OrderHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/order/") + group.POST("list", h.List) + group.GET("remove", h.Remove) + group.GET("clear", h.Clear) +} + func (h *OrderHandler) List(c *gin.Context) { var data struct { OrderNo string `json:"order_no"` @@ -68,16 +76,16 @@ func (h *OrderHandler) List(c *gin.Context) { order.Id = item.Id order.CreatedAt = item.CreatedAt.Unix() order.UpdatedAt = item.UpdatedAt.Unix() - payMethod, ok := types.PayMethods[item.PayWay] + payChannel, ok := types.PayChannel[item.Channel] if !ok { - payMethod = item.PayWay + payChannel = item.Channel } - payName, ok := types.PayNames[item.PayType] + payWays, ok := types.PayWays[item.PayWay] if !ok { - payName = item.PayWay + payWays = item.PayWay } - order.PayMethod = payMethod - order.PayName = payName + order.ChannelName = payChannel + order.PayName = payWays list = append(list, order) } else { logger.Error(err) @@ -121,8 +129,8 @@ func (h *OrderHandler) Clear(c *gin.Context) { } deleteIds := make([]uint, 0) for _, order := range orders { - // 只删除 15 分钟内的未支付订单 - if time.Now().After(order.CreatedAt.Add(time.Minute * 15)) { + // 只删除超时的未支付订单 + if time.Now().After(order.CreatedAt.Add(time.Minute * time.Duration(h.App.SysConfig.Base.OrderPayTimeout))) { deleteIds = append(deleteIds, order.Id) } } diff --git a/api/handler/admin/power_log_handler.go b/api/handler/admin/power_log_handler.go index 7f4fe54e..a0200d32 100644 --- a/api/handler/admin/power_log_handler.go +++ b/api/handler/admin/power_log_handler.go @@ -28,6 +28,12 @@ func NewPowerLogHandler(app *core.AppServer, db *gorm.DB) *PowerLogHandler { return &PowerLogHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *PowerLogHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/powerLog/") + group.POST("list", h.List) +} + func (h *PowerLogHandler) List(c *gin.Context) { var data struct { Username string `json:"username"` diff --git a/api/handler/admin/product_handler.go b/api/handler/admin/product_handler.go index 5df18de1..2202fa6f 100644 --- a/api/handler/admin/product_handler.go +++ b/api/handler/admin/product_handler.go @@ -15,9 +15,10 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "time" + "github.com/gin-gonic/gin" "gorm.io/gorm" - "time" ) type ProductHandler struct { @@ -28,14 +29,22 @@ func NewProductHandler(app *core.AppServer, db *gorm.DB) *ProductHandler { return &ProductHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ProductHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/product/") + group.POST("save", h.Save) + group.GET("list", h.List) + group.POST("enable", h.Enable) + group.POST("sort", h.Sort) + group.GET("remove", h.Remove) +} + func (h *ProductHandler) Save(c *gin.Context) { var data struct { Id uint `json:"id"` Name string `json:"name"` Price float64 `json:"price"` - Discount float64 `json:"discount"` Enabled bool `json:"enabled"` - Days int `json:"days"` Power int `json:"power"` CreatedAt int64 `json:"created_at"` } @@ -45,12 +54,10 @@ func (h *ProductHandler) Save(c *gin.Context) { } item := model.Product{ - Name: data.Name, - Price: data.Price, - Discount: data.Discount, - Days: data.Days, - Power: data.Power, - Enabled: data.Enabled} + Name: data.Name, + Price: data.Price, + Power: data.Power, + Enabled: data.Enabled} item.Id = data.Id if item.Id > 0 { item.CreatedAt = time.Unix(data.CreatedAt, 0) diff --git a/api/handler/admin/redeem_handler.go b/api/handler/admin/redeem_handler.go index 6fae1e91..590927d1 100644 --- a/api/handler/admin/redeem_handler.go +++ b/api/handler/admin/redeem_handler.go @@ -29,6 +29,16 @@ func NewRedeemHandler(app *core.AppServer, db *gorm.DB) *RedeemHandler { return &RedeemHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *RedeemHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/redeem/") + group.GET("list", h.List) + group.POST("create", h.Create) + group.POST("set", h.Set) + group.GET("remove", h.Remove) + group.POST("export", h.Export) +} + func (h *RedeemHandler) List(c *gin.Context) { page := h.GetInt(c, "page", 1) pageSize := h.GetInt(c, "page_size", 20) diff --git a/api/handler/admin/upload_handler.go b/api/handler/admin/upload_handler.go index 0f959eba..878c7776 100644 --- a/api/handler/admin/upload_handler.go +++ b/api/handler/admin/upload_handler.go @@ -9,6 +9,7 @@ package admin import ( "geekai/core" + "geekai/core/middleware" "geekai/handler" "geekai/service/oss" "geekai/store/model" @@ -28,6 +29,17 @@ func NewUploadHandler(app *core.AppServer, db *gorm.DB, manager *oss.UploaderMan return &UploadHandler{BaseHandler: handler.BaseHandler{DB: db, App: app}, uploaderManager: manager} } +// RegisterRoutes 注册路由 +func (h *UploadHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/upload") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.POST("", h.Upload) + } +} + func (h *UploadHandler) Upload(c *gin.Context) { // 判断文件大小 f, err := c.FormFile("file") @@ -36,7 +48,7 @@ func (h *UploadHandler) Upload(c *gin.Context) { return } - if h.App.SysConfig.MaxFileSize > 0 && f.Size > int64(h.App.SysConfig.MaxFileSize)*1024*1024 { + if h.App.SysConfig.Base.MaxFileSize > 0 && f.Size > int64(h.App.SysConfig.Base.MaxFileSize)*1024*1024 { resp.ERROR(c, "文件大小超过限制") return } diff --git a/api/handler/admin/user_handler.go b/api/handler/admin/user_handler.go index 90ff4b20..fff20036 100644 --- a/api/handler/admin/user_handler.go +++ b/api/handler/admin/user_handler.go @@ -10,6 +10,7 @@ package admin import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/service" @@ -19,10 +20,9 @@ import ( "geekai/utils/resp" "time" + "github.com/gin-gonic/gin" "github.com/go-redis/redis/v8" "github.com/golang-jwt/jwt/v5" - - "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -36,6 +36,22 @@ func NewUserHandler(app *core.AppServer, db *gorm.DB, licenseService *service.Li return &UserHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}, licenseService: licenseService, redis: redisCli} } +// RegisterRoutes 注册路由 +func (h *UserHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/admin/user/") + + // 需要管理员授权的接口 + group.Use(middleware.AdminAuthMiddleware(h.App.Config.AdminSession.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.POST("save", h.Save) + group.GET("remove", h.Remove) + group.GET("loginLog", h.LoginLog) + group.GET("genLoginLink", h.GenLoginLink) + group.POST("resetPass", h.ResetPass) + } +} + // List 用户列表 func (h *UserHandler) List(c *gin.Context) { page := h.GetInt(c, "page", 1) diff --git a/api/handler/base_handler.go b/api/handler/base_handler.go index cb2b15ca..229cd090 100644 --- a/api/handler/base_handler.go +++ b/api/handler/base_handler.go @@ -15,9 +15,10 @@ import ( logger2 "geekai/logger" "geekai/store/model" "geekai/utils" - "gorm.io/gorm" "strings" + "gorm.io/gorm" + "github.com/gin-gonic/gin" ) @@ -69,6 +70,14 @@ func (h *BaseHandler) GetLoginUserId(c *gin.Context) uint { return uint(utils.IntValue(utils.InterfaceToString(userId), 0)) } +func (h *BaseHandler) GetAdminId(c *gin.Context) uint { + userId, ok := c.Get(types.AdminUserID) + if !ok { + return 0 + } + return uint(utils.IntValue(utils.InterfaceToString(userId), 0)) +} + func (h *BaseHandler) IsLogin(c *gin.Context) bool { return h.GetLoginUserId(c) > 0 } diff --git a/api/handler/captcha_handler.go b/api/handler/captcha_handler.go index 57852b4a..b63dca79 100644 --- a/api/handler/captcha_handler.go +++ b/api/handler/captcha_handler.go @@ -8,23 +8,45 @@ package handler // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "geekai/core" "geekai/core/types" "geekai/service" "geekai/utils/resp" + "github.com/gin-gonic/gin" ) -// 今日头条函数实现 - type CaptchaHandler struct { + App *core.AppServer service *service.CaptchaService } -func NewCaptchaHandler(s *service.CaptchaService) *CaptchaHandler { - return &CaptchaHandler{service: s} +func NewCaptchaHandler(app *core.AppServer, s *service.CaptchaService, sysConfig *types.SystemConfig) *CaptchaHandler { + return &CaptchaHandler{App: app, service: s} +} + +// RegisterRoutes 注册路由 +func (h *CaptchaHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/captcha/") + + // 无需授权的接口 + group.GET("get", h.Get) + group.POST("check", h.Check) + group.GET("slide/get", h.SlideGet) + group.POST("slide/check", h.SlideCheck) + group.GET("config", h.GetConfig) +} + +func (h *CaptchaHandler) GetConfig(c *gin.Context) { + resp.SUCCESS(c, gin.H{"enabled": h.service.GetConfig().Enabled, "type": h.service.GetConfig().Type}) } func (h *CaptchaHandler) Get(c *gin.Context) { + if !h.service.GetConfig().Enabled { + resp.ERROR(c, "验证码服务未启用") + return + } + data, err := h.service.Get() if err != nil { resp.ERROR(c, err.Error()) @@ -36,6 +58,11 @@ func (h *CaptchaHandler) Get(c *gin.Context) { // Check verify the captcha data func (h *CaptchaHandler) Check(c *gin.Context) { + if !h.service.GetConfig().Enabled { + resp.ERROR(c, "验证码服务未启用") + return + } + var data struct { Key string `json:"key"` Dots string `json:"dots"` @@ -55,6 +82,11 @@ func (h *CaptchaHandler) Check(c *gin.Context) { // SlideGet 获取滑动验证图片 func (h *CaptchaHandler) SlideGet(c *gin.Context) { + if !h.service.GetConfig().Enabled { + resp.ERROR(c, "验证码服务未启用") + return + } + data, err := h.service.SlideGet() if err != nil { resp.ERROR(c, err.Error()) @@ -66,6 +98,11 @@ func (h *CaptchaHandler) SlideGet(c *gin.Context) { // SlideCheck 滑动验证结果校验 func (h *CaptchaHandler) SlideCheck(c *gin.Context) { + if !h.service.GetConfig().Enabled { + resp.ERROR(c, "验证码服务未启用") + return + } + var data struct { Key string `json:"key"` X int `json:"x"` diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_app_handler.go similarity index 72% rename from api/handler/chat_role_handler.go rename to api/handler/chat_app_handler.go index f2e78d1f..ba2c0772 100644 --- a/api/handler/chat_role_handler.go +++ b/api/handler/chat_app_handler.go @@ -9,6 +9,7 @@ package handler import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/store/model" "geekai/store/vo" @@ -19,18 +20,31 @@ import ( "gorm.io/gorm" ) -type ChatRoleHandler struct { +type ChatAppHandler struct { BaseHandler } -func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler { - return &ChatRoleHandler{BaseHandler: BaseHandler{App: app, DB: db}} +func NewChatAppHandler(app *core.AppServer, db *gorm.DB) *ChatAppHandler { + return &ChatAppHandler{BaseHandler: BaseHandler{App: app, DB: db}} +} + +// RegisterRoutes 注册路由 +func (h *ChatAppHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/app/") + group.GET("list", h.List) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.GET("list/user", h.ListByUser) + group.POST("update", h.UpdateApp) + } } // List 获取用户聊天应用列表 -func (h *ChatRoleHandler) List(c *gin.Context) { +func (h *ChatAppHandler) List(c *gin.Context) { tid := h.GetInt(c, "tid", 0) - var roles []model.ChatRole + var roles []model.ChatApp session := h.DB.Where("enable", true) if tid > 0 { session = session.Where("tid", tid) @@ -41,9 +55,9 @@ func (h *ChatRoleHandler) List(c *gin.Context) { return } - var roleVos = make([]vo.ChatRole, 0) + var roleVos = make([]vo.ChatApp, 0) for _, r := range roles { - var v vo.ChatRole + var v vo.ChatApp err := utils.CopyObject(r, &v) if err == nil { v.Id = r.Id @@ -54,10 +68,10 @@ func (h *ChatRoleHandler) List(c *gin.Context) { } // ListByUser 获取用户添加的角色列表 -func (h *ChatRoleHandler) ListByUser(c *gin.Context) { +func (h *ChatAppHandler) ListByUser(c *gin.Context) { id := h.GetInt(c, "id", 0) userId := h.GetLoginUserId(c) - var roles []model.ChatRole + var roles []model.ChatApp session := h.DB.Where("enable", true) // 如果用户没登录,则获取所有角色 if userId > 0 { @@ -86,9 +100,9 @@ func (h *ChatRoleHandler) ListByUser(c *gin.Context) { return } - var roleVos = make([]vo.ChatRole, 0) + var roleVos = make([]vo.ChatApp, 0) for _, r := range roles { - var v vo.ChatRole + var v vo.ChatApp err := utils.CopyObject(r, &v) if err == nil { v.Id = r.Id @@ -98,8 +112,8 @@ func (h *ChatRoleHandler) ListByUser(c *gin.Context) { resp.SUCCESS(c, roleVos) } -// UpdateRole 更新用户聊天角色 -func (h *ChatRoleHandler) UpdateRole(c *gin.Context) { +// UpdateApp 更新用户聊天应用 +func (h *ChatAppHandler) UpdateApp(c *gin.Context) { user, err := h.GetLoginUser(c) if err != nil { resp.NotAuth(c) diff --git a/api/handler/chat_app_type_handler.go b/api/handler/chat_app_type_handler.go index c18f858a..4a08ccf6 100644 --- a/api/handler/chat_app_type_handler.go +++ b/api/handler/chat_app_type_handler.go @@ -19,6 +19,12 @@ func NewChatAppTypeHandler(app *core.AppServer, db *gorm.DB) *ChatAppTypeHandler return &ChatAppTypeHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ChatAppTypeHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/app/type/") + group.GET("list", h.List) +} + // List 获取App类型列表 func (h *ChatAppTypeHandler) List(c *gin.Context) { var items []model.AppType diff --git a/api/handler/chat_handler.go b/api/handler/chat_handler.go index 406df3de..c44bb172 100644 --- a/api/handler/chat_handler.go +++ b/api/handler/chat_handler.go @@ -14,8 +14,10 @@ import ( "errors" "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" + "geekai/service/moderation" "geekai/service/oss" "geekai/store/model" "geekai/store/vo" @@ -39,6 +41,7 @@ import ( const ( ChatEventStart = "start" ChatEventEnd = "end" + ChatEventComplete = "complete" ChatEventError = "error" ChatEventMessageDelta = "message_delta" ChatEventTitle = "title" @@ -54,44 +57,69 @@ type ChatInput struct { Stream bool `json:"stream"` Files []vo.File `json:"files"` ChatModel model.ChatModel `json:"chat_model,omitempty"` - ChatRole model.ChatRole `json:"chat_role,omitempty"` + ChatRole model.ChatApp `json:"chat_role,omitempty"` LastMsgId uint `json:"last_msg_id,omitempty"` // 最后的消息ID,用于重新生成答案的时候过滤上下文 } type ChatHandler struct { BaseHandler - redis *redis.Client - uploadManager *oss.UploaderManager - licenseService *service.LicenseService - ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function - userService *service.UserService + redis *redis.Client + uploadManager *oss.UploaderManager + licenseService *service.LicenseService + ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function + userService *service.UserService + moderationManager *moderation.ServiceManager } -func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService, userService *service.UserService) *ChatHandler { +func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService, userService *service.UserService, moderationManager *moderation.ServiceManager) *ChatHandler { return &ChatHandler{ - BaseHandler: BaseHandler{App: app, DB: db}, - redis: redis, - uploadManager: manager, - licenseService: licenseService, - ReqCancelFunc: types.NewLMap[string, context.CancelFunc](), - userService: userService, + BaseHandler: BaseHandler{App: app, DB: db}, + redis: redis, + uploadManager: manager, + licenseService: licenseService, + ReqCancelFunc: types.NewLMap[string, context.CancelFunc](), + userService: userService, + moderationManager: moderationManager, + } +} + +// RegisterRoutes 注册路由 +func (h *ChatHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/chat/") + + // 聊天接口不需要授权(已在authConfig中配置) + group.Any("message", h.Chat) + + // 其他接口需要用户授权 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.GET("detail", h.Detail) + group.POST("update", h.Update) + group.GET("remove", h.Remove) + group.GET("history", h.History) + group.GET("clear", h.Clear) + group.POST("tokens", h.Tokens) + group.GET("stop", h.StopGenerate) + group.POST("tts", h.TextToSpeech) } } // Chat 处理聊天请求 func (h *ChatHandler) Chat(c *gin.Context) { - var input ChatInput - if err := c.ShouldBindJSON(&input); err != nil { - resp.ERROR(c, types.InvalidArgs) - return - } - // 设置SSE响应头 c.Header("Prompt-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") c.Header("Connection", "keep-alive") c.Header("X-Accel-Buffering", "no") + var input ChatInput + if err := c.ShouldBindJSON(&input); err != nil { + pushMessage(c, ChatEventError, types.InvalidArgs) + c.Abort() + return + } + ctx, cancel := context.WithCancel(c.Request.Context()) defer cancel() @@ -113,7 +141,7 @@ func (h *ChatHandler) Chat(c *gin.Context) { } // 验证聊天角色 - var chatRole model.ChatRole + var chatRole model.ChatApp err := h.DB.First(&chatRole, input.RoleId).Error if err != nil || !chatRole.Enable { pushMessage(c, ChatEventError, "当前聊天角色不存在或者未启用,请更换角色之后再发起对话!") @@ -166,7 +194,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C } if userVo.Power < input.ChatModel.Power { - return fmt.Errorf("您当前剩余算力 %d 已不足以支付当前模型的单次对话需要消耗的算力 %d,[立即购买](/member)。", userVo.Power, input.ChatModel.Power) + return fmt.Errorf("您的算力不足,请购买算力。") } if userVo.ExpiredTime > 0 && userVo.ExpiredTime <= time.Now().Unix() { @@ -229,17 +257,24 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C // 加载聊天上下文 chatCtx := make([]any, 0) messages := make([]any, 0) - if h.App.SysConfig.EnableContext { + if h.App.SysConfig.Base.EnableContext { _ = utils.JsonDecode(input.ChatRole.Context, &messages) - if h.App.SysConfig.ContextDeep > 0 { + if h.App.SysConfig.Base.ContextDeep > 0 { var historyMessages []model.ChatMessage dbSession := h.DB.Session(&gorm.Session{}).Where("chat_id", input.ChatId) if input.LastMsgId > 0 { // 重新生成逻辑 + var lastMessage model.ChatMessage + err = dbSession.Where("id <= ?", input.LastMsgId).Where("type", types.PromptMsg).First(&lastMessage).Error + if err != nil { + input.LastMsgId = 0 + } else { + input.LastMsgId = lastMessage.Id + } dbSession = dbSession.Where("id < ?", input.LastMsgId) // 删除对应的聊天记录 h.DB.Debug().Where("chat_id", input.ChatId).Where("id >= ?", input.LastMsgId).Delete(&model.ChatMessage{}) } - err = dbSession.Limit(h.App.SysConfig.ContextDeep).Order("id DESC").Find(&historyMessages).Error + err = dbSession.Limit(h.App.SysConfig.Base.ContextDeep).Order("id DESC").Find(&historyMessages).Error if err == nil { for i := len(historyMessages) - 1; i >= 0; i-- { msg := historyMessages[i] @@ -267,7 +302,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C } // 上下文的深度超出了模型的最大上下文深度 - if len(chatCtx) >= h.App.SysConfig.ContextDeep { + if len(chatCtx) >= h.App.SysConfig.Base.ContextDeep { break } @@ -277,6 +312,14 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C } reqMgs := make([]any, 0) + // 添加引导提示词,防止模型生成违规内容 + if h.App.SysConfig.Moderation.EnableGuide { + reqMgs = append(reqMgs, map[string]any{ + "role": "system", + "content": h.App.SysConfig.Moderation.GuidePrompt, + }) + } + for i := len(chatCtx) - 1; i >= 0; i-- { reqMgs = append(reqMgs, chatCtx[i]) } @@ -295,16 +338,14 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C }, }) } else { - // 如果不是逆向模型,则提取文件内容 - modelValue := input.ChatModel.Value - if !(strings.Contains(modelValue, "-all") || strings.HasPrefix(modelValue, "gpt-4-gizmo") || strings.HasPrefix(modelValue, "claude")) { - content, err := utils.ReadFileContent(file.URL, h.App.Config.TikaHost) - if err != nil { - logger.Error("error with read file: ", err) - continue - } else { - fileContents = append(fileContents, fmt.Sprintf("%s 文件内容:%s", file.Name, content)) - } + // 处理文件,提取文件内容 + content, err := utils.ReadFileContent(file.URL, h.App.Config.TikaHost) + if err != nil { + logger.Error("error with read file: ", err) + continue + } else { + fileContents = append(fileContents, fmt.Sprintf("%s 文件内容:%s", file.Name, content)) + logger.Debugf("fileContents: %s", fileContents) } } } @@ -320,16 +361,16 @@ func (h *ChatHandler) sendMessage(ctx context.Context, input ChatInput, c *gin.C } if len(imgList) > 0 { - imgList = append(imgList, map[string]interface{}{ + imgList = append(imgList, map[string]any{ "type": "text", "text": input.Prompt, }) - req.Messages = append(reqMgs, map[string]interface{}{ + req.Messages = append(reqMgs, map[string]any{ "role": "user", "content": imgList, }) } else { - req.Messages = append(reqMgs, map[string]interface{}{ + req.Messages = append(reqMgs, map[string]any{ "role": "user", "content": finalPrompt, }) @@ -445,7 +486,7 @@ func (h *ChatHandler) StopGenerate(c *gin.Context) { func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, input ChatInput, apiKey *model.ApiKey) (*http.Response, error) { // if the chat model bind a KEY, use it directly if input.ChatModel.KeyId > 0 { - h.DB.Where("id", input.ChatModel.KeyId).Find(apiKey) + h.DB.Where("id", input.ChatModel.KeyId).Where("enabled", true).Find(apiKey) } else { // use the last unused key h.DB.Where("type", "chat").Where("enabled", true).Order("last_used_at ASC").First(apiKey) } @@ -516,6 +557,7 @@ func (h *ChatHandler) subUserPower(userVo vo.User, input ChatInput, promptTokens } func (h *ChatHandler) saveChatHistory( + c *gin.Context, req types.ApiRequest, usage Usage, message types.Message, @@ -524,6 +566,34 @@ func (h *ChatHandler) saveChatHistory( promptCreatedAt time.Time, replyCreatedAt time.Time) { + // 文本审核 + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(usage.Content) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + logger.Debugf("moderationResult: %+v", moderationResult) + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: userVo.Id, + Source: types.ModerationSourceChat, + Input: usage.Prompt, + Output: usage.Content, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + pushMessage(c, ChatEventError, "很抱歉,内容触发敏感词预警,AI 无法回答!!!") + // 更新用户算力 + if input.ChatModel.Power > 0 { + h.subUserPower(userVo, input, 0, 0) + } + return + } + } // 追加聊天记录 // for prompt var promptTokens, replyTokens, totalTokens int @@ -586,6 +656,22 @@ func (h *ChatHandler) saveChatHistory( logger.Error("failed to save reply history message: ", err) } + // 发送完整聊天记录给前端 + var messageVo vo.ChatMessage + err = utils.CopyObject(historyReplyMsg, &messageVo) + if err == nil { + // 解析内容 + var content vo.MsgContent + err = utils.JsonDecode(historyReplyMsg.Content, &content) + if err != nil { + content.Text = historyReplyMsg.Content + } + messageVo.Content = content + messageVo.CreatedAt = historyReplyMsg.CreatedAt.Unix() + messageVo.UpdatedAt = historyReplyMsg.UpdatedAt.Unix() + pushMessage(c, ChatEventComplete, messageVo) + } + // 更新用户算力 if input.ChatModel.Power > 0 { h.subUserPower(userVo, input, promptTokens, replyTokens) @@ -710,221 +796,3 @@ func (h *ChatHandler) TextToSpeech(c *gin.Context) { logger.Error("写入音频数据到响应失败:", err) } } - -// // OPenAI 消息发送实现 -// func (h *ChatHandler) sendOpenAiMessage( -// req types.ApiRequest, -// userVo vo.User, -// ctx context.Context, -// session *types.ChatSession, -// role model.ChatRole, -// prompt string, -// c *gin.Context) error { -// promptCreatedAt := time.Now() // 记录提问时间 -// start := time.Now() -// var apiKey = model.ApiKey{} -// response, err := h.doRequest(ctx, req, session, &apiKey) -// logger.Info("HTTP请求完成,耗时:", time.Since(start)) -// if err != nil { -// if strings.Contains(err.Error(), "context canceled") { -// return fmt.Errorf("用户取消了请求:%s", prompt) -// } else if strings.Contains(err.Error(), "no available key") { -// return errors.New("抱歉😔😔😔,系统已经没有可用的 API KEY,请联系管理员!") -// } -// return err -// } else { -// defer response.Body.Close() -// } - -// if response.StatusCode != 200 { -// body, _ := io.ReadAll(response.Body) -// return fmt.Errorf("请求 OpenAI API 失败:%d, %v", response.StatusCode, string(body)) -// } - -// contentType := response.Header.Get("Prompt-Type") -// if strings.Contains(contentType, "text/event-stream") { -// replyCreatedAt := time.Now() // 记录回复时间 -// // 循环读取 Chunk 消息 -// var message = types.Message{Role: "assistant"} -// var contents = make([]string, 0) -// var function model.Function -// var toolCall = false -// var arguments = make([]string, 0) -// var reasoning = false - -// pushMessage(c, ChatEventStart, "开始响应") -// scanner := bufio.NewScanner(response.Body) -// for scanner.Scan() { -// line := scanner.Text() -// if !strings.Contains(line, "data:") || len(line) < 30 { -// continue -// } -// var responseBody = types.ApiResponse{} -// err = json.Unmarshal([]byte(line[6:]), &responseBody) -// if err != nil { // 数据解析出错 -// return errors.New(line) -// } -// if len(responseBody.Choices) == 0 { // Fixed: 兼容 Azure API 第一个输出空行 -// continue -// } -// if responseBody.Choices[0].Delta.Prompt == nil && -// responseBody.Choices[0].Delta.ToolCalls == nil && -// responseBody.Choices[0].Delta.ReasoningContent == "" { -// continue -// } - -// if responseBody.Choices[0].FinishReason == "stop" && len(contents) == 0 { -// pushMessage(c, ChatEventError, "抱歉😔😔😔,AI助手由于未知原因已经停止输出内容。") -// break -// } - -// var tool types.ToolCall -// if len(responseBody.Choices[0].Delta.ToolCalls) > 0 { -// tool = responseBody.Choices[0].Delta.ToolCalls[0] -// if toolCall && tool.Function.Name == "" { -// arguments = append(arguments, tool.Function.Arguments) -// continue -// } -// } - -// // 兼容 Function Call -// fun := responseBody.Choices[0].Delta.FunctionCall -// if fun.Name != "" { -// tool = *new(types.ToolCall) -// tool.Function.Name = fun.Name -// } else if toolCall { -// arguments = append(arguments, fun.Arguments) -// continue -// } - -// if !utils.IsEmptyValue(tool) { -// res := h.DB.Where("name = ?", tool.Function.Name).First(&function) -// if res.Error == nil { -// toolCall = true -// callMsg := fmt.Sprintf("正在调用工具 `%s` 作答 ...\n\n", function.Label) -// pushMessage(c, ChatEventMessageDelta, map[string]interface{}{ -// "type": "text", -// "content": callMsg, -// }) -// contents = append(contents, callMsg) -// } -// continue -// } - -// if responseBody.Choices[0].FinishReason == "tool_calls" || -// responseBody.Choices[0].FinishReason == "function_call" { // 函数调用完毕 -// break -// } - -// // output stopped -// if responseBody.Choices[0].FinishReason != "" { -// break // 输出完成或者输出中断了 -// } else { // 正常输出结果 -// // 兼容思考过程 -// if responseBody.Choices[0].Delta.ReasoningContent != "" { -// reasoningContent := responseBody.Choices[0].Delta.ReasoningContent -// if !reasoning { -// reasoningContent = fmt.Sprintf("%s", reasoningContent) -// reasoning = true -// } - -// pushMessage(c, ChatEventMessageDelta, map[string]interface{}{ -// "type": "text", -// "content": reasoningContent, -// }) -// contents = append(contents, reasoningContent) -// } else if responseBody.Choices[0].Delta.Prompt != "" { -// finalContent := responseBody.Choices[0].Delta.Prompt -// if reasoning { -// finalContent = fmt.Sprintf("%s", responseBody.Choices[0].Delta.Prompt) -// reasoning = false -// } -// contents = append(contents, utils.InterfaceToString(finalContent)) -// pushMessage(c, ChatEventMessageDelta, map[string]interface{}{ -// "type": "text", -// "content": finalContent, -// }) -// } -// } -// } // end for - -// if err := scanner.Err(); err != nil { -// if strings.Contains(err.Error(), "context canceled") { -// logger.Info("用户取消了请求:", prompt) -// } else { -// logger.Error("信息读取出错:", err) -// } -// } - -// if toolCall { // 调用函数完成任务 -// params := make(map[string]any) -// _ = utils.JsonDecode(strings.Join(arguments, ""), ¶ms) -// logger.Debugf("函数名称: %s, 函数参数:%s", function.Name, params) -// params["user_id"] = userVo.Id -// var apiRes types.BizVo -// r, err := req2.C().R().SetHeader("Body-Type", "application/json"). -// SetHeader("Authorization", function.Token). -// SetBody(params).Post(function.Action) -// errMsg := "" -// if err != nil { -// errMsg = err.Error() -// } else { -// all, _ := io.ReadAll(r.Body) -// err = json.Unmarshal(all, &apiRes) -// if err != nil { -// errMsg = err.Error() -// } else if apiRes.Code != types.Success { -// errMsg = apiRes.Message -// } -// } - -// if errMsg != "" { -// errMsg = "调用函数工具出错:" + errMsg -// contents = append(contents, errMsg) -// } else { -// errMsg = utils.InterfaceToString(apiRes.Data) -// contents = append(contents, errMsg) -// } -// pushMessage(c, ChatEventMessageDelta, map[string]interface{}{ -// "type": "text", -// "content": errMsg, -// }) -// } - -// // 消息发送成功 -// if len(contents) > 0 { -// usage := Usage{ -// Prompt: prompt, -// Prompt: strings.Join(contents, ""), -// PromptTokens: 0, -// CompletionTokens: 0, -// TotalTokens: 0, -// } -// message.Prompt = usage.Prompt -// h.saveChatHistory(req, usage, message, session, role, userVo, promptCreatedAt, replyCreatedAt) -// } -// } else { -// var respVo OpenAIResVo -// body, err := io.ReadAll(response.Body) -// if err != nil { -// return fmt.Errorf("读取响应失败:%v", body) -// } -// err = json.Unmarshal(body, &respVo) -// if err != nil { -// return fmt.Errorf("解析响应失败:%v", body) -// } -// content := respVo.Choices[0].Message.Prompt -// if strings.HasPrefix(req.Model, "o1-") { -// content = fmt.Sprintf("AI思考结束,耗时:%d 秒。\n%s", time.Now().Unix()-session.Start, respVo.Choices[0].Message.Prompt) -// } -// pushMessage(c, ChatEventMessageDelta, map[string]interface{}{ -// "type": "text", -// "content": content, -// }) -// respVo.Usage.Prompt = prompt -// respVo.Usage.Prompt = content -// h.saveChatHistory(req, respVo.Usage, respVo.Choices[0].Message, session, role, userVo, promptCreatedAt, time.Now()) -// } - -// return nil -// } diff --git a/api/handler/chat_item_handler.go b/api/handler/chat_item_handler.go index fbf777cc..de3ef75a 100644 --- a/api/handler/chat_item_handler.go +++ b/api/handler/chat_item_handler.go @@ -42,9 +42,9 @@ func (h *ChatHandler) List(c *gin.Context) { modelValues = append(modelValues, chat.Model) } - var roles []model.ChatRole + var roles []model.ChatApp var models []model.ChatModel - roleMap := make(map[uint]model.ChatRole) + roleMap := make(map[uint]model.ChatApp) modelMap := make(map[string]model.ChatModel) h.DB.Where("id IN ?", roleIds).Find(&roles) h.DB.Where("value IN ?", modelValues).Find(&models) @@ -205,7 +205,7 @@ func (h *ChatHandler) Detail(c *gin.Context) { } // 填充角色名称 - var role model.ChatRole + var role model.ChatApp res = h.DB.Where("id", chatItem.RoleId).First(&role) if res.Error != nil { resp.ERROR(c, "Role not found") diff --git a/api/handler/chat_model_handler.go b/api/handler/chat_model_handler.go index c4d4fac7..ff9ca79b 100644 --- a/api/handler/chat_model_handler.go +++ b/api/handler/chat_model_handler.go @@ -26,6 +26,12 @@ func NewChatModelHandler(app *core.AppServer, db *gorm.DB) *ChatModelHandler { return &ChatModelHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ChatModelHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/model/") + group.GET("list", h.List) +} + // List 模型列表 func (h *ChatModelHandler) List(c *gin.Context) { var items []model.ChatModel diff --git a/api/handler/chat_openai_handler.go b/api/handler/chat_openai_handler.go index 6a71c405..91a6f59a 100644 --- a/api/handler/chat_openai_handler.go +++ b/api/handler/chat_openai_handler.go @@ -226,7 +226,7 @@ func (h *ChatHandler) sendOpenAiMessage( TotalTokens: 0, } message.Content = usage.Content - h.saveChatHistory(req, usage, message, input, userVo, promptCreatedAt, replyCreatedAt) + h.saveChatHistory(c, req, usage, message, input, userVo, promptCreatedAt, replyCreatedAt) } } else { // 非流式输出 var respVo OpenAIResVo @@ -242,7 +242,7 @@ func (h *ChatHandler) sendOpenAiMessage( pushMessage(c, "text", content) respVo.Usage.Prompt = input.Prompt respVo.Usage.Content = content - h.saveChatHistory(req, respVo.Usage, respVo.Choices[0].Message, input, userVo, promptCreatedAt, time.Now()) + h.saveChatHistory(c, req, respVo.Usage, respVo.Choices[0].Message, input, userVo, promptCreatedAt, time.Now()) } return nil diff --git a/api/handler/config_handler.go b/api/handler/config_handler.go index e69b452c..d8422b23 100644 --- a/api/handler/config_handler.go +++ b/api/handler/config_handler.go @@ -27,6 +27,15 @@ func NewConfigHandler(app *core.AppServer, db *gorm.DB, licenseService *service. return &ConfigHandler{BaseHandler: BaseHandler{App: app, DB: db}, licenseService: licenseService} } +// RegisterRoutes 注册路由 +func (h *ConfigHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/config/") + + // 无需授权的接口 + group.GET("get", h.Get) + group.GET("license", h.License) +} + // Get 获取指定的系统配置 func (h *ConfigHandler) Get(c *gin.Context) { key := c.Query("key") diff --git a/api/handler/dalle_handler.go b/api/handler/dalle_handler.go index 0c7bc037..5c34d613 100644 --- a/api/handler/dalle_handler.go +++ b/api/handler/dalle_handler.go @@ -10,9 +10,11 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/service/dalle" + "geekai/service/moderation" "geekai/service/oss" "geekai/store/model" "geekai/store/vo" @@ -25,16 +27,18 @@ import ( type DallJobHandler struct { BaseHandler - dallService *dalle.Service - uploader *oss.UploaderManager - userService *service.UserService + dallService *dalle.Service + uploader *oss.UploaderManager + userService *service.UserService + moderationManager *moderation.ServiceManager } -func NewDallJobHandler(app *core.AppServer, db *gorm.DB, service *dalle.Service, manager *oss.UploaderManager, userService *service.UserService) *DallJobHandler { +func NewDallJobHandler(app *core.AppServer, db *gorm.DB, service *dalle.Service, manager *oss.UploaderManager, userService *service.UserService, moderationManager *moderation.ServiceManager) *DallJobHandler { return &DallJobHandler{ - dallService: service, - uploader: manager, - userService: userService, + dallService: service, + uploader: manager, + userService: userService, + moderationManager: moderationManager, BaseHandler: BaseHandler{ App: app, DB: db, @@ -42,6 +46,24 @@ func NewDallJobHandler(app *core.AppServer, db *gorm.DB, service *dalle.Service, } } +// RegisterRoutes 注册路由 +func (h *DallJobHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/dall/") + + // 公开接口,不需要授权 + group.GET("imgWall", h.ImgWall) + group.GET("models", h.GetModels) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("image", h.Image) + group.GET("jobs", h.JobList) + group.GET("remove", h.Remove) + group.GET("publish", h.Publish) + } +} + // Image 创建一个绘画任务 func (h *DallJobHandler) Image(c *gin.Context) { var data types.DallTask @@ -50,6 +72,29 @@ func (h *DallJobHandler) Image(c *gin.Context) { return } + // 文本审核 + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(data.Prompt) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: h.GetLoginUserId(c), + Source: types.ModerationSourceDalle, + Input: data.Prompt, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + resp.ERROR(c, "当前创作内容包含敏感词,提示词未通过文本审核,请重新输入!") + return + } + } + var chatModel model.ChatModel if res := h.DB.Where("id = ?", data.ModelId).First(&chatModel); res.Error != nil { resp.ERROR(c, "模型不存在") @@ -73,11 +118,12 @@ func (h *DallJobHandler) Image(c *gin.Context) { UserId: uint(userId), ModelId: chatModel.Id, ModelName: chatModel.Value, + Image: data.Image, Prompt: data.Prompt, Quality: data.Quality, Size: data.Size, Style: data.Style, - TranslateModelId: h.App.SysConfig.AssistantModelId, + TranslateModelId: h.App.SysConfig.Base.AssistantModelId, Power: chatModel.Power, } job := model.DallJob{ diff --git a/api/handler/function_handler.go b/api/handler/function_handler.go index fb6d6cd4..dba2a7f3 100644 --- a/api/handler/function_handler.go +++ b/api/handler/function_handler.go @@ -13,7 +13,6 @@ import ( "geekai/core" "geekai/core/types" "geekai/service" - "geekai/service/crawler" "geekai/service/dalle" "geekai/service/oss" "geekai/store/model" @@ -31,7 +30,6 @@ import ( type FunctionHandler struct { BaseHandler - config types.ApiConfig uploadManager *oss.UploaderManager dallService *dalle.Service userService *service.UserService @@ -49,13 +47,23 @@ func NewFunctionHandler( App: server, DB: db, }, - config: config.ApiConfig, uploadManager: manager, dallService: dallService, userService: userService, } } +// RegisterRoutes 注册路由 +func (h *FunctionHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/function/") + group.GET("list", h.List) + + // 需要用户授权的接口 + group.POST("weibo", h.WeiBo) + group.POST("zaobao", h.ZaoBao) + group.POST("dalle3", h.Dall3) +} + type resVo struct { Code types.BizCode `json:"code"` Message string `json:"message"` @@ -107,16 +115,10 @@ func (h *FunctionHandler) WeiBo(c *gin.Context) { return } - if h.config.Token == "" { - resp.ERROR(c, "无效的 API Token") - return - } - - url := fmt.Sprintf("%s/api/weibo/fetch", h.config.ApiURL) + url := fmt.Sprintf("%s/api/weibo/fetch", types.GeekAPIURL) var res resVo r, err := req.C().R(). - SetHeader("AppId", h.config.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.config.Token)). + SetHeader("Authorization", "Bearer geekai-plus"). SetSuccessResult(&res).Get(url) if err != nil { resp.ERROR(c, fmt.Sprintf("%v", err)) @@ -146,16 +148,10 @@ func (h *FunctionHandler) ZaoBao(c *gin.Context) { return } - if h.config.Token == "" { - resp.ERROR(c, "无效的 API Token") - return - } - - url := fmt.Sprintf("%s/api/zaobao/fetch", h.config.ApiURL) + url := fmt.Sprintf("%s/api/zaobao/fetch", types.GeekAPIURL) var res resVo r, err := req.C().R(). - SetHeader("AppId", h.config.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.config.Token)). + SetHeader("Authorization", "Bearer geekai-plus"). SetSuccessResult(&res).Get(url) if err != nil { resp.ERROR(c, fmt.Sprintf("%v", err)) @@ -193,16 +189,23 @@ func (h *FunctionHandler) Dall3(c *gin.Context) { return } + var chatModel model.ChatModel + res := h.DB.Where("type = ?", "img").Where("enabled", true).First(&chatModel) + if res.Error != nil { + resp.ERROR(c, "没有找到可用的AI绘图模型!") + return + } + logger.Debugf("绘画参数:%+v", params) var user model.User - res := h.DB.Where("id = ?", params["user_id"]).First(&user) + res = h.DB.Where("id = ?", params["user_id"]).First(&user) if res.Error != nil { resp.ERROR(c, "当前用户不存在!") return } - if user.Power < h.App.SysConfig.DallPower { - resp.ERROR(c, "创建 DALL-E 绘图任务失败,算力不足") + if user.Power < chatModel.Power { + resp.ERROR(c, "创建绘图任务失败,算力不足") return } @@ -211,24 +214,24 @@ func (h *FunctionHandler) Dall3(c *gin.Context) { task := types.DallTask{ UserId: user.Id, Prompt: prompt, - ModelId: 0, - ModelName: "dall-e-3", - TranslateModelId: h.App.SysConfig.AssistantModelId, + ModelId: chatModel.Id, + ModelName: chatModel.Value, + TranslateModelId: h.App.SysConfig.Base.AssistantModelId, N: 1, Quality: "standard", Size: "1024x1024", Style: "vivid", - Power: h.App.SysConfig.DallPower, + Power: chatModel.Power, } job := model.DallJob{ UserId: user.Id, Prompt: prompt, - Power: h.App.SysConfig.DallPower, + Power: chatModel.Power, TaskInfo: utils.JsonEncode(task), } err := h.DB.Create(&job).Error if err != nil { - resp.ERROR(c, "创建 DALL-E 绘图任务失败:"+err.Error()) + resp.ERROR(c, "创建绘图任务失败:"+err.Error()) return } @@ -253,76 +256,6 @@ func (h *FunctionHandler) Dall3(c *gin.Context) { resp.SUCCESS(c, content) } -// 实现一个联网搜索的函数工具,采用爬虫实现 -func (h *FunctionHandler) WebSearch(c *gin.Context) { - if err := h.checkAuth(c); err != nil { - resp.ERROR(c, err.Error()) - return - } - - var params map[string]interface{} - if err := c.ShouldBindJSON(¶ms); err != nil { - resp.ERROR(c, types.InvalidArgs) - return - } - - // 从参数中获取搜索关键词 - keyword, ok := params["keyword"].(string) - if !ok || keyword == "" { - resp.ERROR(c, "搜索关键词不能为空") - return - } - - // 从参数中获取最大页数,默认为1页 - maxPages := 1 - if pages, ok := params["max_pages"].(float64); ok { - maxPages = int(pages) - } - - // 获取用户ID - userID, ok := params["user_id"].(float64) - if !ok { - resp.ERROR(c, "用户ID不能为空") - return - } - - // 查询用户信息 - var user model.User - res := h.DB.Where("id = ?", int(userID)).First(&user) - if res.Error != nil { - resp.ERROR(c, "用户不存在") - return - } - - // 检查用户算力是否足够 - searchPower := 1 // 每次搜索消耗1点算力 - if user.Power < searchPower { - resp.ERROR(c, "算力不足,无法执行网络搜索") - return - } - - // 执行网络搜索 - searchResults, err := crawler.SearchWeb(keyword, maxPages) - if err != nil { - resp.ERROR(c, fmt.Sprintf("搜索失败: %v", err)) - return - } - - // 扣减用户算力 - err = h.userService.DecreasePower(user.Id, searchPower, model.PowerLog{ - Type: types.PowerConsume, - Model: "web_search", - Remark: fmt.Sprintf("网络搜索:%s", utils.CutWords(keyword, 10)), - }) - if err != nil { - resp.ERROR(c, "扣减算力失败:"+err.Error()) - return - } - - // 返回搜索结果 - resp.SUCCESS(c, searchResults) -} - // List 获取所有的工具函数列表 func (h *FunctionHandler) List(c *gin.Context) { var items []model.Function diff --git a/api/handler/invite_handler.go b/api/handler/invite_handler.go index e6e5c029..861856d3 100644 --- a/api/handler/invite_handler.go +++ b/api/handler/invite_handler.go @@ -8,14 +8,18 @@ package handler // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "fmt" "geekai/core" + "geekai/core/middleware" "geekai/store/model" "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "strings" + "time" + "github.com/gin-gonic/gin" "gorm.io/gorm" - "strings" ) // InviteHandler 用户邀请 @@ -27,6 +31,23 @@ func NewInviteHandler(app *core.AppServer, db *gorm.DB) *InviteHandler { return &InviteHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *InviteHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/invite/") + + // 公开接口,不需要授权 + group.GET("hits", h.Hits) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.GET("code", h.Code) + group.GET("list", h.List) + group.GET("stats", h.Stats) + group.GET("rules", h.Rules) + } +} + // Code 获取当前用户邀请码 func (h *InviteHandler) Code(c *gin.Context) { userId := h.GetLoginUserId(c) @@ -65,21 +86,34 @@ func (h *InviteHandler) List(c *gin.Context) { var total int64 session.Model(&model.InviteLog{}).Count(&total) var items []model.InviteLog - var list = make([]vo.InviteLog, 0) offset := (page - 1) * pageSize - res := session.Order("id DESC").Offset(offset).Limit(pageSize).Find(&items) - if res.Error == nil { - for _, item := range items { - var v vo.InviteLog - err := utils.CopyObject(item, &v) - if err == nil { - v.Id = item.Id - v.CreatedAt = item.CreatedAt.Unix() - list = append(list, v) - } else { - logger.Error(err) - } + err := session.Order("id DESC").Offset(offset).Limit(pageSize).Find(&items).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + userIds := make([]uint, 0) + for _, item := range items { + userIds = append(userIds, item.UserId) + } + userMap := make(map[uint]model.User) + var users []model.User + h.DB.Model(&model.User{}).Where("id IN (?)", userIds).Find(&users) + for _, user := range users { + userMap[user.Id] = user + } + + var list = make([]vo.InviteLog, 0) + for _, item := range items { + var v vo.InviteLog + err := utils.CopyObject(item, &v) + if err != nil { + continue } + v.CreatedAt = item.CreatedAt.Unix() + v.Avatar = userMap[item.UserId].Avatar + list = append(list, v) } resp.SUCCESS(c, vo.NewPage(total, page, pageSize, list)) } @@ -90,3 +124,89 @@ func (h *InviteHandler) Hits(c *gin.Context) { h.DB.Model(&model.InviteCode{}).Where("code = ?", code).UpdateColumn("hits", gorm.Expr("hits + ?", 1)) resp.SUCCESS(c) } + +// Stats 获取邀请统计 +func (h *InviteHandler) Stats(c *gin.Context) { + userId := h.GetLoginUserId(c) + + // 获取邀请码 + var inviteCode model.InviteCode + res := h.DB.Where("user_id = ?", userId).First(&inviteCode) + if res.Error != nil { + resp.ERROR(c, "邀请码不存在") + return + } + + // 统计累计邀请数 + var totalInvite int64 + h.DB.Model(&model.InviteLog{}).Where("inviter_id = ?", userId).Count(&totalInvite) + + // 统计今日邀请数 + today := time.Now().Format("2006-01-02") + var todayInvite int64 + h.DB.Model(&model.InviteLog{}).Where("inviter_id = ? AND DATE(created_at) = ?", userId, today).Count(&todayInvite) + + // 获取系统配置中的邀请奖励 + var config model.Config + var invitePower int = 200 // 默认值 + if h.DB.Where("name = ?", "system").First(&config).Error == nil { + var configMap map[string]any + if utils.JsonDecode(config.Value, &configMap) == nil { + if power, ok := configMap["invite_power"].(float64); ok { + invitePower = int(power) + } + } + } + + // 计算获得奖励总数 + rewardTotal := int(totalInvite) * invitePower + + // 构建邀请链接 + inviteLink := fmt.Sprintf("%s/register?invite=%s", h.App.Config.StaticUrl, inviteCode.Code) + + stats := vo.InviteStats{ + InviteCount: int(totalInvite), + RewardTotal: rewardTotal, + TodayInvite: int(todayInvite), + InviteCode: inviteCode.Code, + InviteLink: inviteLink, + } + + resp.SUCCESS(c, stats) +} + +// Rules 获取奖励规则 +func (h *InviteHandler) Rules(c *gin.Context) { + // 获取系统配置中的邀请奖励 + var config model.Config + var invitePower int = 200 // 默认值 + if h.DB.Where("name = ?", "system").First(&config).Error == nil { + var configMap map[string]interface{} + if utils.JsonDecode(config.Value, &configMap) == nil { + if power, ok := configMap["invite_power"].(float64); ok { + invitePower = int(power) + } + } + } + + rules := []vo.RewardRule{ + { + Id: 1, + Title: "好友注册", + Desc: "好友通过邀请链接成功注册", + Icon: "icon-user-fill", + Color: "#1989fa", + Reward: invitePower, + }, + { + Id: 2, + Title: "好友首次充值", + Desc: "好友首次充值任意金额", + Icon: "icon-money", + Color: "#07c160", + Reward: invitePower * 2, // 假设首次充值奖励是注册奖励的2倍 + }, + } + + resp.SUCCESS(c, rules) +} diff --git a/api/handler/jimeng_handler.go b/api/handler/jimeng_handler.go index 22123a6e..439da831 100644 --- a/api/handler/jimeng_handler.go +++ b/api/handler/jimeng_handler.go @@ -2,11 +2,12 @@ package handler import ( "fmt" - "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/service/jimeng" + "geekai/service/moderation" "geekai/store/model" "geekai/store/vo" "geekai/utils" @@ -19,27 +20,34 @@ import ( // JimengHandler 即梦AI处理器 type JimengHandler struct { BaseHandler - jimengService *jimeng.Service - userService *service.UserService + jimengService *jimeng.Service + userService *service.UserService + moderationManager *moderation.ServiceManager } // NewJimengHandler 创建即梦AI处理器 -func NewJimengHandler(app *core.AppServer, jimengService *jimeng.Service, db *gorm.DB, userService *service.UserService) *JimengHandler { +func NewJimengHandler(app *core.AppServer, jimengService *jimeng.Service, db *gorm.DB, userService *service.UserService, moderationManager *moderation.ServiceManager) *JimengHandler { return &JimengHandler{ - BaseHandler: BaseHandler{App: app, DB: db}, - jimengService: jimengService, - userService: userService, + BaseHandler: BaseHandler{App: app, DB: db}, + jimengService: jimengService, + userService: userService, + moderationManager: moderationManager, } } // RegisterRoutes 注册路由,新增统一任务接口 func (h *JimengHandler) RegisterRoutes() { - rg := h.App.Engine.Group("/api/jimeng") - rg.POST("task", h.CreateTask) // 只保留统一任务接口 - rg.GET("power-config", h.GetPowerConfig) // 新增算力配置接口 - rg.POST("jobs", h.Jobs) - rg.GET("remove", h.Remove) - rg.GET("retry", h.Retry) + group := h.App.Engine.Group("/api/jimeng/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("task", h.CreateTask) + group.GET("power-config", h.GetPowerConfig) + group.POST("jobs", h.Jobs) + group.GET("remove", h.Remove) + group.GET("retry", h.Retry) + } } // JimengTaskRequest 统一任务请求结构体 @@ -70,6 +78,31 @@ func (h *JimengHandler) CreateTask(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } + + // 文本审核 + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(req.Prompt) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: h.GetLoginUserId(c), + Source: types.ModerationSourceJiMeng, + Input: req.Prompt, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + resp.ERROR(c, "当前创作内容包含敏感词,请重新输入!") + return + } + + } + // 新增:除图像特效外,其他任务类型必须有提示词 if req.TaskType != "image_effects" && req.Prompt == "" { resp.ERROR(c, "提示词不能为空") @@ -153,12 +186,7 @@ func (h *JimengHandler) CreateTask(c *gin.Context) { "seed": req.Seed, "scale": req.Scale, } - if len(req.ImageUrls) > 0 { - params["image_urls"] = req.ImageUrls - } - if len(req.BinaryDataBase64) > 0 { - params["binary_data_base64"] = req.BinaryDataBase64 - } + params["image_urls"] = []string{req.ImageInput} case "image_effects": powerCost = h.getPowerFromConfig(model.JMTaskTypeImageEffects) taskType = model.JMTaskTypeImageEffects @@ -181,9 +209,6 @@ func (h *JimengHandler) CreateTask(c *gin.Context) { taskType = model.JMTaskTypeTextToVideo reqKey = jimeng.ReqKeyTextToVideo modelName = "即梦文生视频" - if req.Seed == 0 { - req.Seed = -1 - } if req.AspectRatio == "" { req.AspectRatio = jimeng.AspectRatio16_9 } @@ -196,9 +221,6 @@ func (h *JimengHandler) CreateTask(c *gin.Context) { taskType = model.JMTaskTypeImageToVideo reqKey = jimeng.ReqKeyImageToVideo modelName = "即梦图生视频" - if req.Seed == 0 { - req.Seed = -1 - } params = map[string]any{ "seed": req.Seed, "aspect_ratio": req.AspectRatio, @@ -333,8 +355,10 @@ func (h *JimengHandler) Remove(c *gin.Context) { resp.ERROR(c, "无权限操作") return } - if job.Status != model.JMTaskStatusFailed { - resp.ERROR(c, "只有失败的任务才能删除") + + // 正在运行中的任务不能删除 + if job.Status == model.JMTaskStatusGenerating || job.Status == model.JMTaskStatusInQueue { + resp.ERROR(c, "正在运行中的任务不能删除,否则无法退回算力") return } @@ -345,17 +369,20 @@ func (h *JimengHandler) Remove(c *gin.Context) { return } - // 退回算力 - err = h.userService.IncreasePower(user.Id, job.Power, model.PowerLog{ - Type: types.PowerRefund, - Model: "jimeng", - Remark: fmt.Sprintf("删除任务,退回%d算力", job.Power), - }) - if err != nil { - resp.ERROR(c, "退回算力失败") - tx.Rollback() - return + // 失败任务删除后退回算力 + if job.Status != model.JMTaskStatusFailed { + err = h.userService.IncreasePower(user.Id, job.Power, model.PowerLog{ + Type: types.PowerRefund, + Model: "jimeng", + Remark: fmt.Sprintf("删除任务,退回%d算力", job.Power), + }) + if err != nil { + resp.ERROR(c, "退回算力失败") + tx.Rollback() + return + } } + tx.Commit() resp.SUCCESS(c, gin.H{}) @@ -408,7 +435,7 @@ func (h *JimengHandler) Retry(c *gin.Context) { // getPowerFromConfig 从配置中获取指定类型的算力消耗 func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int { - config := h.jimengService.GetConfig() + config := h.App.SysConfig.Jimeng switch taskType { case model.JMTaskTypeTextToImage: @@ -430,7 +457,7 @@ func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int { // GetPowerConfig 获取即梦各任务类型算力消耗配置 func (h *JimengHandler) GetPowerConfig(c *gin.Context) { - config := h.jimengService.GetConfig() + config := h.App.SysConfig.Jimeng resp.SUCCESS(c, gin.H{ "text_to_image": config.Power.TextToImage, "image_to_image": config.Power.ImageToImage, diff --git a/api/handler/markmap_handler.go b/api/handler/markmap_handler.go index 85b2147f..0195a9f8 100644 --- a/api/handler/markmap_handler.go +++ b/api/handler/markmap_handler.go @@ -10,6 +10,7 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/store/model" @@ -35,6 +36,17 @@ func NewMarkMapHandler(app *core.AppServer, db *gorm.DB, userService *service.Us } } +// RegisterRoutes 注册路由 +func (h *MarkMapHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/markMap/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("gen", h.Generate) + } +} + // Generate 生成思维导图 func (h *MarkMapHandler) Generate(c *gin.Context) { var data struct { diff --git a/api/handler/menu_handler.go b/api/handler/menu_handler.go index 39de1c78..d2e3629a 100644 --- a/api/handler/menu_handler.go +++ b/api/handler/menu_handler.go @@ -13,6 +13,7 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -25,6 +26,12 @@ func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler { return &MenuHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *MenuHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/menu/") + group.GET("list", h.List) +} + // List 数据列表 func (h *MenuHandler) List(c *gin.Context) { index := h.GetBool(c, "index") @@ -33,7 +40,7 @@ func (h *MenuHandler) List(c *gin.Context) { session := h.DB.Session(&gorm.Session{}) session = session.Where("enabled", true) if index { - session = session.Where("id IN ?", h.App.SysConfig.IndexNavs) + session = session.Where("id IN ?", h.App.SysConfig.Base.IndexNavs) } res := session.Order("sort_num ASC").Find(&items) if res.Error == nil { diff --git a/api/handler/mj_handler.go b/api/handler/mj_handler.go index fc522a7c..245dc993 100644 --- a/api/handler/mj_handler.go +++ b/api/handler/mj_handler.go @@ -10,9 +10,11 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/service/mj" + "geekai/service/moderation" "geekai/service/oss" "geekai/store/model" "geekai/store/vo" @@ -27,18 +29,20 @@ import ( type MidJourneyHandler struct { BaseHandler - mjService *mj.Service - snowflake *service.Snowflake - uploader *oss.UploaderManager - userService *service.UserService + mjService *mj.Service + snowflake *service.Snowflake + uploader *oss.UploaderManager + userService *service.UserService + moderationManager *moderation.ServiceManager } -func NewMidJourneyHandler(app *core.AppServer, db *gorm.DB, snowflake *service.Snowflake, service *mj.Service, manager *oss.UploaderManager, userService *service.UserService) *MidJourneyHandler { +func NewMidJourneyHandler(app *core.AppServer, db *gorm.DB, snowflake *service.Snowflake, service *mj.Service, manager *oss.UploaderManager, userService *service.UserService, moderationManager *moderation.ServiceManager) *MidJourneyHandler { return &MidJourneyHandler{ - snowflake: snowflake, - mjService: service, - uploader: manager, - userService: userService, + snowflake: snowflake, + mjService: service, + uploader: manager, + userService: userService, + moderationManager: moderationManager, BaseHandler: BaseHandler{ App: app, DB: db, @@ -46,6 +50,25 @@ func NewMidJourneyHandler(app *core.AppServer, db *gorm.DB, snowflake *service.S } } +// RegisterRoutes 注册路由 +func (h *MidJourneyHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/mj/") + + // 公开接口,不需要授权 + group.GET("imgWall", h.ImgWall) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("image", h.Image) + group.POST("upscale", h.Upscale) + group.POST("variation", h.Variation) + group.GET("jobs", h.JobList) + group.GET("remove", h.Remove) + group.GET("publish", h.Publish) + } +} + func (h *MidJourneyHandler) preCheck(c *gin.Context) bool { user, err := h.GetLoginUser(c) if err != nil { @@ -53,7 +76,7 @@ func (h *MidJourneyHandler) preCheck(c *gin.Context) bool { return false } - if user.Power < h.App.SysConfig.MjPower { + if user.Power < h.App.SysConfig.Base.MjPower { resp.ERROR(c, "当前用户剩余算力不足以完成本次绘画!") return false } @@ -90,6 +113,29 @@ func (h *MidJourneyHandler) Image(c *gin.Context) { return } + // 文本审核 + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(data.Prompt) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: h.GetLoginUserId(c), + Source: types.ModerationSourceMJ, + Input: data.Prompt, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + resp.ERROR(c, "当前创作内容包含敏感词,请重新输入!") + return + } + } + var params = "" if data.Rate != "" && !strings.Contains(params, "--ar") { params += " --ar " + data.Rate @@ -159,8 +205,8 @@ func (h *MidJourneyHandler) Image(c *gin.Context) { Params: params, UserId: userId, ImgArr: data.ImgArr, - Mode: h.App.SysConfig.MjMode, - TranslateModelId: h.App.SysConfig.AssistantModelId, + Mode: h.App.SysConfig.Base.MjMode, + TranslateModelId: h.App.SysConfig.Base.AssistantModelId, } job := model.MidJourneyJob{ Type: data.TaskType, @@ -169,7 +215,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) { TaskInfo: utils.JsonEncode(task), Progress: 0, Prompt: fmt.Sprintf("%s %s", data.Prompt, params), - Power: h.App.SysConfig.MjPower, + Power: h.App.SysConfig.Base.MjPower, CreatedAt: time.Now(), } opt := "绘图" @@ -232,7 +278,7 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { Index: data.Index, MessageId: data.MessageId, MessageHash: data.MessageHash, - Mode: h.App.SysConfig.MjMode, + Mode: h.App.SysConfig.Base.MjMode, } job := model.MidJourneyJob{ Type: types.TaskUpscale.String(), @@ -240,7 +286,7 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { TaskId: taskId, TaskInfo: utils.JsonEncode(task), Progress: 0, - Power: h.App.SysConfig.MjActionPower, + Power: h.App.SysConfig.Base.MjActionPower, CreatedAt: time.Now(), } if res := h.DB.Create(&job); res.Error != nil || res.RowsAffected == 0 { @@ -287,7 +333,7 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) { ChannelId: data.ChannelId, MessageId: data.MessageId, MessageHash: data.MessageHash, - Mode: h.App.SysConfig.MjMode, + Mode: h.App.SysConfig.Base.MjMode, } job := model.MidJourneyJob{ Type: types.TaskVariation.String(), @@ -296,7 +342,7 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) { TaskId: taskId, TaskInfo: utils.JsonEncode(task), Progress: 0, - Power: h.App.SysConfig.MjActionPower, + Power: h.App.SysConfig.Base.MjActionPower, CreatedAt: time.Now(), } if res := h.DB.Create(&job); res.Error != nil || res.RowsAffected == 0 { diff --git a/api/handler/net_handler.go b/api/handler/net_handler.go index 88329647..512e80cf 100644 --- a/api/handler/net_handler.go +++ b/api/handler/net_handler.go @@ -9,6 +9,7 @@ package handler import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service/oss" "geekai/store/model" @@ -32,6 +33,22 @@ func NewNetHandler(app *core.AppServer, db *gorm.DB, manager *oss.UploaderManage return &NetHandler{BaseHandler: BaseHandler{App: app, DB: db}, uploaderManager: manager} } +// RegisterRoutes 注册路由 +func (h *NetHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/upload") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("", h.Upload) + group.POST("list", h.List) + group.GET("remove", h.Remove) + } + + // 公开接口,不需要授权 + h.App.Engine.GET("/api/download", h.Download) +} + func (h *NetHandler) Upload(c *gin.Context) { file, err := h.uploaderManager.GetUploadHandler().PutFile(c, "file") if err != nil { diff --git a/api/handler/order_handler.go b/api/handler/order_handler.go index a9ee42f3..bcdea2d4 100644 --- a/api/handler/order_handler.go +++ b/api/handler/order_handler.go @@ -9,12 +9,12 @@ package handler import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/store/model" "geekai/store/vo" "geekai/utils" "geekai/utils/resp" - "time" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -28,6 +28,18 @@ func NewOrderHandler(app *core.AppServer, db *gorm.DB) *OrderHandler { return &OrderHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *OrderHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/order/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.GET("list", h.List) + group.GET("query", h.Query) + } +} + // List 订单列表 func (h *OrderHandler) List(c *gin.Context) { page := h.GetInt(c, "page", 1) @@ -48,20 +60,21 @@ func (h *OrderHandler) List(c *gin.Context) { order.Id = item.Id order.CreatedAt = item.CreatedAt.Unix() order.UpdatedAt = item.UpdatedAt.Unix() - payMethod, ok := types.PayMethods[item.PayWay] + payChannel, ok := types.PayChannel[item.Channel] if !ok { - payMethod = item.PayWay + payChannel = item.PayWay } - payName, ok := types.PayNames[item.PayType] + payWays, ok := types.PayWays[item.PayWay] if !ok { - payName = item.PayWay + payWays = item.PayWay } - order.PayMethod = payMethod - order.PayName = payName + order.ChannelName = payChannel + order.PayName = payWays list = append(list, order) } else { logger.Error(err) } + } } resp.SUCCESS(c, vo.NewPage(total, page, pageSize, list)) @@ -82,17 +95,8 @@ func (h *OrderHandler) Query(c *gin.Context) { return } - counter := 0 - for { - time.Sleep(time.Second) - var item model.Order - h.DB.Where("order_no = ?", orderNo).First(&item) - if counter >= 15 || item.Status == types.OrderPaidSuccess || item.Status != order.Status { - order.Status = item.Status - break - } - counter++ - } + var item model.Order + h.DB.Where("order_no = ?", orderNo).First(&item) resp.SUCCESS(c, gin.H{"status": order.Status}) } diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 686351d9..9b67327d 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -11,6 +11,7 @@ import ( "embed" "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/service/payment" @@ -33,52 +34,148 @@ type PayWay struct { // PaymentHandler 支付服务回调 handler type PaymentHandler struct { BaseHandler - alipayService *payment.AlipayService - huPiPayService *payment.HuPiPayService - geekPayService *payment.GeekPayService - wechatPayService *payment.WechatPayService - snowflake *service.Snowflake - userService *service.UserService - fs embed.FS - lock sync.Mutex - signKey string // 用来签名的随机秘钥 + alipayService *payment.AlipayService + epayService *payment.EPayService + wxpayService *payment.WxPayService + snowflake *service.Snowflake + userService *service.UserService + fs embed.FS + lock sync.Mutex + config *types.PaymentConfig } func NewPaymentHandler( server *core.AppServer, alipayService *payment.AlipayService, - huPiPayService *payment.HuPiPayService, - geekPayService *payment.GeekPayService, - wechatPayService *payment.WechatPayService, + geekPayService *payment.EPayService, + wxpayService *payment.WxPayService, db *gorm.DB, userService *service.UserService, snowflake *service.Snowflake, - fs embed.FS) *PaymentHandler { + fs embed.FS, + sysConfig *types.SystemConfig) *PaymentHandler { return &PaymentHandler{ - alipayService: alipayService, - huPiPayService: huPiPayService, - geekPayService: geekPayService, - wechatPayService: wechatPayService, - snowflake: snowflake, - userService: userService, - fs: fs, - lock: sync.Mutex{}, + alipayService: alipayService, + epayService: geekPayService, + wxpayService: wxpayService, + snowflake: snowflake, + userService: userService, + fs: fs, + lock: sync.Mutex{}, BaseHandler: BaseHandler{ App: server, DB: db, }, - signKey: utils.RandString(32), + config: &sysConfig.Payment, } } -func (h *PaymentHandler) Pay(c *gin.Context) { +// RegisterRoutes 注册路由 +func (h *PaymentHandler) RegisterRoutes() { + rg := h.App.Engine.Group("/api/payment/") + + // 支付回调接口(公开) + rg.POST("notify/alipay", h.AlipayNotify) + rg.GET("notify/epay", h.EPayNotify) + rg.POST("notify/wxpay", h.WxpayNotify) + + // 需要用户登录的接口 + rg.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + rg.POST("create", h.CreateOrder) + } +} + +func (h *PaymentHandler) StartSyncOrders() { + go func() { + for { + err := h.SyncOrders() + if err != nil { + logger.Error(err) + } + time.Sleep(time.Second * 5) + } + }() +} + +// SyncOrders 同步订单状态 +func (h *PaymentHandler) SyncOrders() error { + defer func() { + if err := recover(); err != nil { + logger.Errorf("同步订单状态发生异常: %v", err) + } + }() + var orders []model.Order + err := h.DB.Where("status", types.OrderNotPaid).Where("checked", false).Find(&orders).Error + if err != nil { + return err + } + + for _, order := range orders { + time.Sleep(time.Second * 1) + //超时15分钟的订单,直接标记为已关闭 + if time.Now().After(order.CreatedAt.Add(time.Minute * 5)) { + h.DB.Model(&model.Order{}).Where("id", order.Id).Update("checked", true) + logger.Errorf("订单超时:%v", order) + continue + } + // 查询订单状态 + var res payment.OrderInfo + switch order.Channel { + case payment.PayChannelEpay: + res, err = h.epayService.Query(order.OrderNo) + if err != nil { + logger.Errorf("error with query order info: %v", err) + continue + } + // 微信支付 + case payment.PayChannelWX: + res, err = h.wxpayService.Query(order.OrderNo) + logger.Debugf("微信支付订单状态:%+v", res) + if err != nil { + logger.Errorf("error with query order info: %v", err) + continue + } + case payment.PayChannelAL: + res, err = h.alipayService.Query(order.OrderNo) + if err != nil { + logger.Errorf("error with query order info: %v", err) + continue + } + } + + // 订单已关闭 + if res.Closed() { + h.DB.Model(&model.Order{}).Where("id", order.Id).Updates(map[string]any{ + "checked": true, + "status": types.OrderPaidFailed, + }) + logger.Errorf("订单已关闭:%v", order) + continue + } + + // 订单未支付,不处理,继续轮询 + if !res.Success() { + continue + } + + // 订单支付成功 + err = h.paySuccess(res) + if err != nil { + logger.Errorf("error with deal order: %v", err) + continue + } + } + return nil +} + +func (h *PaymentHandler) CreateOrder(c *gin.Context) { var data struct { - PayWay string `json:"pay_way"` - PayType string `json:"pay_type"` - ProductId int `json:"product_id"` - UserId int `json:"user_id"` - Device string `json:"device"` - Host string `json:"host"` + PayWay string `json:"pay_way,omitempty"` // 支付方式:支付宝,微信 + Pid int `json:"pid,omitempty"` + Device string `json:"device,omitempty"` + Domain string `json:"domain,omitempty"` // 支付回调域名 + Channel string `json:"channel,omitempty"` } if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) @@ -86,7 +183,7 @@ func (h *PaymentHandler) Pay(c *gin.Context) { } var product model.Product - err := h.DB.Where("id", data.ProductId).First(&product).Error + err := h.DB.Where("id", data.Pid).First(&product).Error if err != nil { resp.ERROR(c, "Product not found") return @@ -97,136 +194,118 @@ func (h *PaymentHandler) Pay(c *gin.Context) { resp.ERROR(c, "error with generate trade no: "+err.Error()) return } + userId := h.GetLoginUserId(c) var user model.User - err = h.DB.Where("id", data.UserId).First(&user).Error + err = h.DB.Where("id", userId).First(&user).Error if err != nil { resp.NotAuth(c) return } - amount := product.Discount - var payURL, returnURL, notifyURL string + amount := product.Price + var payURL, notifyURL string switch data.PayWay { - case "alipay": - if h.App.Config.AlipayConfig.NotifyURL != "" { // 用于本地调试支付 - notifyURL = h.App.Config.AlipayConfig.NotifyURL - } else { - notifyURL = fmt.Sprintf("%s/api/payment/notify/alipay", data.Host) - } - if h.App.Config.AlipayConfig.ReturnURL != "" { // 用于本地调试支付 - returnURL = h.App.Config.AlipayConfig.ReturnURL - } else { - returnURL = fmt.Sprintf("%s/payReturn", data.Host) - } - money := fmt.Sprintf("%.2f", amount) - if data.Device == "wechat" { - payURL, err = h.alipayService.PayMobile(payment.AlipayParams{ + case "wxpay": + logger.Debugf("微信支付,%+v", data) + data.Channel = payment.PayChannelWX + // 优先使用微信官方支付 + if h.config.WxPay.Enabled { + data.Channel = "wxpay" + if h.config.WxPay.Domain != "" { + data.Domain = h.config.WxPay.Domain + } + notifyURL = fmt.Sprintf("%s/api/payment/notify/wxpay", data.Domain) + payURL, err = h.wxpayService.Pay(payment.PayRequest{ OutTradeNo: orderNo, - Subject: product.Name, - TotalFee: money, - ReturnURL: returnURL, - NotifyURL: notifyURL, - }) - } else { - payURL, err = h.alipayService.PayPC(payment.AlipayParams{ - OutTradeNo: orderNo, - Subject: product.Name, - TotalFee: money, - ReturnURL: returnURL, - NotifyURL: notifyURL, - }) - } - - if err != nil { - resp.ERROR(c, "error with generate pay url: "+err.Error()) - return - } - break - case "wechat": - if h.App.Config.WechatPayConfig.NotifyURL != "" { - notifyURL = h.App.Config.WechatPayConfig.NotifyURL - } else { - notifyURL = fmt.Sprintf("%s/api/payment/notify/wechat", data.Host) - } - if data.Device == "wechat" { - payURL, err = h.wechatPayService.PayUrlH5(payment.WechatPayParams{ - OutTradeNo: orderNo, - TotalFee: int(amount * 100), + TotalFee: fmt.Sprintf("%d", int(amount*100)), Subject: product.Name, NotifyURL: notifyURL, ClientIP: c.ClientIP(), + Device: data.Device, + PayWay: payment.PayWayWX, }) - } else { - payURL, err = h.wechatPayService.PayUrlNative(payment.WechatPayParams{ + if err != nil { + resp.ERROR(c, err.Error()) + return + } + } else if h.config.Epay.Enabled { // 聚合支付 + logger.Debugf("聚合支付%+v", data) + data.Channel = payment.PayChannelEpay + if h.config.Epay.Domain != "" { + data.Domain = h.config.Epay.Domain + } + notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain) + params := payment.PayRequest{ OutTradeNo: orderNo, - TotalFee: int(amount * 100), Subject: product.Name, + TotalFee: fmt.Sprintf("%f", amount), + ClientIP: c.ClientIP(), + Device: data.Device, + PayWay: payment.PayWayWX, + NotifyURL: notifyURL, + } + + r, err := h.epayService.Pay(params) + logger.Debugf("请求支付结果,%+v", r) + if err != nil { + resp.ERROR(c, err.Error()) + return + } else { + payURL = r + } + } else { + resp.ERROR(c, "系统没有配置可用的支付渠道!") + return + } + case "alipay": + if h.config.Alipay.Enabled { + logger.Debugf("支付宝,%+v", data) + data.Channel = payment.PayChannelAL + if h.config.Alipay.Domain != "" { // 用于本地调试支付 + data.Domain = h.config.Alipay.Domain + } + notifyURL = fmt.Sprintf("%s/api/payment/notify/alipay", data.Domain) + money := fmt.Sprintf("%.2f", amount) + payURL, err = h.alipayService.Pay(payment.PayRequest{ + Device: data.Device, + OutTradeNo: orderNo, + Subject: product.Name, + TotalFee: money, NotifyURL: notifyURL, }) - } - if err != nil { - resp.ERROR(c, err.Error()) - return - } - break - case "hupi": - if h.App.Config.HuPiPayConfig.NotifyURL != "" { - notifyURL = h.App.Config.HuPiPayConfig.NotifyURL - } else { - notifyURL = fmt.Sprintf("%s/api/payment/notify/hupi", data.Host) - } - if h.App.Config.HuPiPayConfig.ReturnURL != "" { - returnURL = h.App.Config.HuPiPayConfig.ReturnURL - } else { - returnURL = fmt.Sprintf("%s/payReturn", data.Host) - } - r, err := h.huPiPayService.Pay(payment.HuPiPayParams{ - Version: "1.1", - TradeOrderId: orderNo, - TotalFee: fmt.Sprintf("%f", amount), - Title: product.Name, - NotifyURL: notifyURL, - ReturnURL: returnURL, - WapName: "GeekAI助手", - }) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - payURL = r.URL - break - case "geek": - if h.App.Config.GeekPayConfig.NotifyURL != "" { - notifyURL = h.App.Config.GeekPayConfig.NotifyURL - } else { - notifyURL = fmt.Sprintf("%s/api/payment/notify/geek", data.Host) - } - if h.App.Config.GeekPayConfig.ReturnURL != "" { - data.Host = utils.GetBaseURL(h.App.Config.GeekPayConfig.ReturnURL) - } - if data.Device == "wechat" { // 微信客户端打开,调回手机端用户中心页面 - returnURL = fmt.Sprintf("%s/mobile/profile", data.Host) - } else { - returnURL = fmt.Sprintf("%s/payReturn", data.Host) - } - params := payment.GeekPayParams{ - OutTradeNo: orderNo, - Method: "web", - Name: product.Name, - Money: fmt.Sprintf("%f", amount), - ClientIP: c.ClientIP(), - Device: data.Device, - Type: data.PayType, - ReturnURL: returnURL, - NotifyURL: notifyURL, - } - res, err := h.geekPayService.Pay(params) - if err != nil { - resp.ERROR(c, err.Error()) + if err != nil { + resp.ERROR(c, "error with generate pay url: "+err.Error()) + return + } + } else if h.config.Epay.Enabled { // 聚合支付 + logger.Debugf("聚合支付,%+v", data) + data.Channel = payment.PayChannelEpay + if h.config.Epay.Domain != "" { + data.Domain = h.config.Epay.Domain + } + notifyURL = fmt.Sprintf("%s/api/payment/notify/epay", data.Domain) + params := payment.PayRequest{ + OutTradeNo: orderNo, + Subject: product.Name, + TotalFee: fmt.Sprintf("%f", amount), + ClientIP: c.ClientIP(), + Device: data.Device, + PayWay: data.PayWay, + NotifyURL: notifyURL, + } + + r, err := h.epayService.Pay(params) + if err != nil { + resp.ERROR(c, err.Error()) + return + } else { + payURL = r + } + } else { + resp.ERROR(c, "系统没有配置可用的支付渠道!") return } - payURL = res.PayURL default: resp.ERROR(c, "不支持的支付渠道") return @@ -234,43 +313,40 @@ func (h *PaymentHandler) Pay(c *gin.Context) { // 创建订单 remark := types.OrderRemark{ - Days: product.Days, - Power: product.Power, - Name: product.Name, - Price: product.Price, - Discount: product.Discount, + Power: product.Power, + Name: product.Name, + Price: product.Price, } order := model.Order{ - UserId: user.Id, - Username: user.Username, - ProductId: product.Id, - OrderNo: orderNo, - Subject: product.Name, - Amount: amount, - Status: types.OrderNotPaid, - PayWay: data.PayWay, - PayType: data.PayType, - Remark: utils.JsonEncode(remark), + UserId: user.Id, + Username: user.Username, + OrderNo: orderNo, + Subject: product.Name, + Amount: amount, + Status: types.OrderNotPaid, + PayWay: data.PayWay, + Channel: data.Channel, + Remark: utils.JsonEncode(remark), } err = h.DB.Create(&order).Error if err != nil { resp.ERROR(c, "error with create order: "+err.Error()) return } - resp.SUCCESS(c, payURL) + resp.SUCCESS(c, gin.H{"pay_url": payURL, "order_no": orderNo}) } -// 异步通知回调公共逻辑 -func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { +// 支付成功处理 +func (h *PaymentHandler) paySuccess(info payment.OrderInfo) error { + h.lock.Lock() + defer h.lock.Unlock() + var order model.Order - err := h.DB.Where("order_no = ?", orderNo).First(&order).Error + err := h.DB.Where("order_no", info.OutTradeNo).First(&order).Error if err != nil { return fmt.Errorf("error with fetch order: %v", err) } - h.lock.Lock() - defer h.lock.Unlock() - // 已支付订单,直接返回 if order.Status == types.OrderPaidSuccess { return nil @@ -290,19 +366,21 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { // 增加用户算力 err = h.userService.IncreasePower(order.UserId, remark.Power, model.PowerLog{ - Type: types.PowerRecharge, - Model: order.PayWay, - Remark: fmt.Sprintf("充值算力,金额:%f,订单号:%s", order.Amount, order.OrderNo), + Type: types.PowerRecharge, + Model: order.Subject, + Remark: fmt.Sprintf("充值算力,金额:%f,订单号:%s", order.Amount, order.OrderNo), + CreatedAt: time.Now(), }) if err != nil { return err } // 更新订单状态 - order.PayTime = time.Now().Unix() + order.PayTime = utils.Str2stamp(info.PayTime) order.Status = types.OrderPaidSuccess - order.TradeNo = tradeNo - err = h.DB.Updates(&order).Error + order.TradeNo = info.TradeId + order.Checked = true + err = h.DB.Debug().Updates(&order).Error if err != nil { return fmt.Errorf("error with update order info: %v", err) } @@ -317,54 +395,6 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { return nil } -// GetPayWays 获取支付方式 -func (h *PaymentHandler) GetPayWays(c *gin.Context) { - payWays := make([]gin.H, 0) - if h.App.Config.AlipayConfig.Enabled { - payWays = append(payWays, gin.H{"pay_way": "alipay", "pay_type": "alipay"}) - } - if h.App.Config.HuPiPayConfig.Enabled { - payWays = append(payWays, gin.H{"pay_way": "hupi", "pay_type": "wxpay"}) - } - if h.App.Config.GeekPayConfig.Enabled { - for _, v := range h.App.Config.GeekPayConfig.Methods { - payWays = append(payWays, gin.H{"pay_way": "geek", "pay_type": v}) - } - } - if h.App.Config.WechatPayConfig.Enabled { - payWays = append(payWays, gin.H{"pay_way": "wechat", "pay_type": "wxpay"}) - } - resp.SUCCESS(c, payWays) -} - -// HuPiPayNotify 虎皮椒支付异步回调 -func (h *PaymentHandler) HuPiPayNotify(c *gin.Context) { - err := c.Request.ParseForm() - if err != nil { - c.String(http.StatusOK, "fail") - return - } - - orderNo := c.Request.Form.Get("trade_order_id") - tradeNo := c.Request.Form.Get("open_order_id") - logger.Infof("收到虎皮椒订单支付回调,%+v", c.Request.Form) - - if err = h.huPiPayService.Check(orderNo); err != nil { - logger.Error("订单校验失败:", err) - c.String(http.StatusOK, "fail") - return - } - - err = h.notify(orderNo, tradeNo) - if err != nil { - logger.Error(err) - c.String(http.StatusOK, "fail") - return - } - - c.String(http.StatusOK, "success") -} - // AlipayNotify 支付宝支付回调 func (h *PaymentHandler) AlipayNotify(c *gin.Context) { err := c.Request.ParseForm() @@ -373,16 +403,15 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) { return } - result := h.alipayService.TradeVerify(c.Request) - logger.Infof("收到支付宝商号订单支付回调:%+v", result) - if !result.Success() { - logger.Error("订单校验失败:", result.Message) + orderInfo, err := h.alipayService.Query(c.Request.Form.Get("out_trade_no")) + logger.Infof("收到支付宝商号订单支付回调:%+v", orderInfo) + if !orderInfo.Success() { + logger.Errorf("订单校验失败:%v", err) c.String(http.StatusOK, "fail") return } - tradeNo := c.Request.Form.Get("trade_no") - err = h.notify(result.OutTradeNo, tradeNo) + err = h.paySuccess(orderInfo) if err != nil { logger.Error(err) c.String(http.StatusOK, "fail") @@ -392,28 +421,35 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) { c.String(http.StatusOK, "success") } -// GeekPayNotify 支付异步回调 -func (h *PaymentHandler) GeekPayNotify(c *gin.Context) { +// EPayNotify 易支付支付异步回调 +func (h *PaymentHandler) EPayNotify(c *gin.Context) { var params = make(map[string]string) for k := range c.Request.URL.Query() { params[k] = c.Query(k) } - logger.Infof("收到GeekPay订单支付回调:%+v", params) - // 检查支付状态 + logger.Infof("收到易支付订单支付回调:%+v", params) + // 检查支付状态, 如果未支付,则返回成功 if params["trade_status"] != "TRADE_SUCCESS" { c.String(http.StatusOK, "success") return } - sign := h.geekPayService.Sign(params) + sign := h.epayService.Sign(params) if sign != c.Query("sign") { logger.Errorf("签名验证失败, %s, %s", sign, c.Query("sign")) c.String(http.StatusOK, "fail") return } + // 查询订单状态 + order, err := h.epayService.Query(params["out_trade_no"]) + if err != nil { + logger.Error(err) + c.String(http.StatusOK, "fail") + return + } - err := h.notify(params["out_trade_no"], params["trade_no"]) + err = h.paySuccess(order) if err != nil { logger.Error(err) c.String(http.StatusOK, "fail") @@ -423,26 +459,23 @@ func (h *PaymentHandler) GeekPayNotify(c *gin.Context) { c.String(http.StatusOK, "success") } -// WechatPayNotify 微信商户支付异步回调 -func (h *PaymentHandler) WechatPayNotify(c *gin.Context) { +// WxpayNotify 微信商户支付异步回调 +func (h *PaymentHandler) WxpayNotify(c *gin.Context) { err := c.Request.ParseForm() if err != nil { c.String(http.StatusOK, "fail") return } - result := h.wechatPayService.TradeVerify(c.Request) - logger.Infof("收到微信商号订单支付回调:%+v", result) - if !result.Success() { - logger.Error("订单校验失败:", err) - c.JSON(http.StatusBadRequest, gin.H{ - "code": "FAIL", - "message": err.Error(), - }) + orderInfo, err := h.wxpayService.TradeVerify(c.Request) + logger.Infof("收到微信商号订单支付回调:%+v", orderInfo) + if err != nil { + logger.Errorf("订单校验失败:%v", err) + c.JSON(http.StatusBadRequest, gin.H{"code": "FAIL"}) return } - err = h.notify(result.OutTradeNo, result.TradeId) + err = h.paySuccess(orderInfo) if err != nil { logger.Error(err) c.String(http.StatusOK, "fail") diff --git a/api/handler/power_log_handler.go b/api/handler/power_log_handler.go index 77732217..4954fa30 100644 --- a/api/handler/power_log_handler.go +++ b/api/handler/power_log_handler.go @@ -9,11 +9,13 @@ package handler import ( "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/store/model" "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "time" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -27,6 +29,18 @@ func NewPowerLogHandler(app *core.AppServer, db *gorm.DB) *PowerLogHandler { return &PowerLogHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *PowerLogHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/powerLog/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("list", h.List) + group.GET("stats", h.Stats) + } +} + func (h *PowerLogHandler) List(c *gin.Context) { var data struct { Model string `json:"model"` @@ -72,3 +86,45 @@ func (h *PowerLogHandler) List(c *gin.Context) { } resp.SUCCESS(c, vo.NewPage(total, data.Page, data.PageSize, list)) } + +// Stats 获取用户算力统计 +func (h *PowerLogHandler) Stats(c *gin.Context) { + userId := h.GetLoginUserId(c) + if userId == 0 { + resp.NotAuth(c) + return + } + + // 获取用户信息(包含余额) + var user model.User + if err := h.DB.Where("id", userId).First(&user).Error; err != nil { + resp.ERROR(c, "用户不存在") + return + } + + // 计算总消费(所有支出记录) + var totalConsume int64 + h.DB.Model(&model.PowerLog{}). + Where("user_id", userId). + Where("mark", types.PowerSub). + Select("COALESCE(SUM(amount), 0)"). + Scan(&totalConsume) + + // 计算今日消费 + today := time.Now().Format("2006-01-02") + var todayConsume int64 + h.DB.Model(&model.PowerLog{}). + Where("user_id", userId). + Where("mark", types.PowerSub). + Where("DATE(created_at) = ?", today). + Select("COALESCE(SUM(amount), 0)"). + Scan(&todayConsume) + + stats := map[string]interface{}{ + "total": totalConsume, + "today": todayConsume, + "balance": user.Power, + } + + resp.SUCCESS(c, stats) +} diff --git a/api/handler/product_handler.go b/api/handler/product_handler.go index 34959aaf..3558f693 100644 --- a/api/handler/product_handler.go +++ b/api/handler/product_handler.go @@ -13,6 +13,7 @@ import ( "geekai/store/vo" "geekai/utils" "geekai/utils/resp" + "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -25,6 +26,12 @@ func NewProductHandler(app *core.AppServer, db *gorm.DB) *ProductHandler { return &ProductHandler{BaseHandler: BaseHandler{App: app, DB: db}} } +// RegisterRoutes 注册路由 +func (h *ProductHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/product/") + group.GET("list", h.List) +} + // List 模型列表 func (h *ProductHandler) List(c *gin.Context) { var items []model.Product diff --git a/api/handler/prompt_handler.go b/api/handler/prompt_handler.go index 100099b4..f97ffde5 100644 --- a/api/handler/prompt_handler.go +++ b/api/handler/prompt_handler.go @@ -10,12 +10,14 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/store/model" "geekai/utils" "geekai/utils/resp" "strings" + "time" "github.com/gin-gonic/gin" "gorm.io/gorm" @@ -39,6 +41,20 @@ func NewPromptHandler(app *core.AppServer, db *gorm.DB, userService *service.Use } } +// RegisterRoutes 注册路由 +func (h *PromptHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/prompt/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)).Use(middleware.RateLimitEvery(h.App.Redis, 30*time.Second)) + { + group.POST("lyric", h.Lyric) + group.POST("image", h.Image) + group.POST("video", h.Video) + group.POST("meta", h.MetaPrompt) + } +} + // Lyric 生成歌词 func (h *PromptHandler) Lyric(c *gin.Context) { var data struct { @@ -48,25 +64,12 @@ func (h *PromptHandler) Lyric(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.LyricPromptTemplate, data.Prompt), h.App.SysConfig.AssistantModelId) + content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.LyricPromptTemplate, data.Prompt), h.App.SysConfig.Base.AssistantModelId) if err != nil { resp.ERROR(c, err.Error()) return } - if h.App.SysConfig.PromptPower > 0 { - userId := h.GetLoginUserId(c) - err = h.userService.DecreasePower(userId, h.App.SysConfig.PromptPower, model.PowerLog{ - Type: types.PowerConsume, - Model: h.getPromptModel(), - Remark: "生成歌词", - }) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - } - resp.SUCCESS(c, content) } @@ -79,23 +82,12 @@ func (h *PromptHandler) Image(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.ImagePromptOptimizeTemplate, data.Prompt), h.App.SysConfig.AssistantModelId) + content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.ImagePromptOptimizeTemplate, data.Prompt), h.App.SysConfig.Base.AssistantModelId) if err != nil { resp.ERROR(c, err.Error()) return } - if h.App.SysConfig.PromptPower > 0 { - userId := h.GetLoginUserId(c) - err = h.userService.DecreasePower(userId, h.App.SysConfig.PromptPower, model.PowerLog{ - Type: types.PowerConsume, - Model: h.getPromptModel(), - Remark: "生成绘画提示词", - }) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - } + resp.SUCCESS(c, strings.Trim(content, `"`)) } @@ -108,25 +100,12 @@ func (h *PromptHandler) Video(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.VideoPromptTemplate, data.Prompt), h.App.SysConfig.AssistantModelId) + content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.VideoPromptTemplate, data.Prompt), h.App.SysConfig.Base.AssistantModelId) if err != nil { resp.ERROR(c, err.Error()) return } - if h.App.SysConfig.PromptPower > 0 { - userId := h.GetLoginUserId(c) - err = h.userService.DecreasePower(userId, h.App.SysConfig.PromptPower, model.PowerLog{ - Type: types.PowerConsume, - Model: h.getPromptModel(), - Remark: "生成视频脚本", - }) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - } - resp.SUCCESS(c, strings.Trim(content, `"`)) } @@ -158,9 +137,9 @@ func (h *PromptHandler) MetaPrompt(c *gin.Context) { } func (h *PromptHandler) getPromptModel() string { - if h.App.SysConfig.AssistantModelId > 0 { + if h.App.SysConfig.Base.AssistantModelId > 0 { var chatModel model.ChatModel - h.DB.Where("id", h.App.SysConfig.AssistantModelId).First(&chatModel) + h.DB.Where("id", h.App.SysConfig.Base.AssistantModelId).First(&chatModel) return chatModel.Value } return "gpt-4o" diff --git a/api/handler/realtime_handler.go b/api/handler/realtime_handler.go index 5880260e..f66e891a 100644 --- a/api/handler/realtime_handler.go +++ b/api/handler/realtime_handler.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/store/model" @@ -39,6 +40,18 @@ func NewRealtimeHandler(server *core.AppServer, db *gorm.DB, userService *servic return &RealtimeHandler{BaseHandler: BaseHandler{App: server, DB: db}, userService: userService} } +// RegisterRoutes 注册路由 +func (h *RealtimeHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/realtime/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.Any("", h.Connection) + group.POST("voice", h.VoiceChat) + } +} + func (h *RealtimeHandler) Connection(c *gin.Context) { // 获取客户端请求中指定的子协议 clientProtocols := c.GetHeader("Sec-WebSocket-Protocol") @@ -154,7 +167,7 @@ func (h *RealtimeHandler) VoiceChat(c *gin.Context) { return } - if user.Power < h.App.SysConfig.AdvanceVoicePower { + if user.Power < h.App.SysConfig.Base.AdvanceVoicePower { resp.ERROR(c, "当前用户算力不足,无法使用该功能") return } @@ -198,7 +211,7 @@ func (h *RealtimeHandler) VoiceChat(c *gin.Context) { h.DB.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix()) // 扣减算力 - err = h.userService.DecreasePower(userId, h.App.SysConfig.AdvanceVoicePower, model.PowerLog{ + err = h.userService.DecreasePower(userId, h.App.SysConfig.Base.AdvanceVoicePower, model.PowerLog{ Type: types.PowerConsume, Model: "advanced-voice", Remark: "实时语音通话", diff --git a/api/handler/redeem_handler.go b/api/handler/redeem_handler.go index 706ac010..87a41c24 100644 --- a/api/handler/redeem_handler.go +++ b/api/handler/redeem_handler.go @@ -10,14 +10,16 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/store/model" "geekai/utils/resp" - "github.com/gin-gonic/gin" - "gorm.io/gorm" "sync" "time" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" ) type RedeemHandler struct { @@ -30,6 +32,17 @@ func NewRedeemHandler(app *core.AppServer, db *gorm.DB, userService *service.Use return &RedeemHandler{BaseHandler: BaseHandler{App: app, DB: db}, userService: userService} } +// RegisterRoutes 注册路由 +func (h *RedeemHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/redeem/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("verify", h.Verify) + } +} + func (h *RedeemHandler) Verify(c *gin.Context) { var data struct { Code string `json:"code"` diff --git a/api/handler/sd_handler.go b/api/handler/sd_handler.go index f2eaf974..ce19f42f 100644 --- a/api/handler/sd_handler.go +++ b/api/handler/sd_handler.go @@ -10,8 +10,10 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" + "geekai/service/moderation" "geekai/service/oss" "geekai/service/sd" "geekai/store" @@ -28,12 +30,13 @@ import ( type SdJobHandler struct { BaseHandler - redis *redis.Client - sdService *sd.Service - uploader *oss.UploaderManager - snowflake *service.Snowflake - leveldb *store.LevelDB - userService *service.UserService + redis *redis.Client + sdService *sd.Service + uploader *oss.UploaderManager + snowflake *service.Snowflake + leveldb *store.LevelDB + userService *service.UserService + moderationManager *moderation.ServiceManager } func NewSdJobHandler(app *core.AppServer, @@ -42,13 +45,15 @@ func NewSdJobHandler(app *core.AppServer, manager *oss.UploaderManager, snowflake *service.Snowflake, userService *service.UserService, - levelDB *store.LevelDB) *SdJobHandler { + levelDB *store.LevelDB, + moderationManager *moderation.ServiceManager) *SdJobHandler { return &SdJobHandler{ - sdService: service, - uploader: manager, - snowflake: snowflake, - leveldb: levelDB, - userService: userService, + sdService: service, + uploader: manager, + snowflake: snowflake, + leveldb: levelDB, + userService: userService, + moderationManager: moderationManager, BaseHandler: BaseHandler{ App: app, DB: db, @@ -56,6 +61,23 @@ func NewSdJobHandler(app *core.AppServer, } } +// RegisterRoutes 注册路由 +func (h *SdJobHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/sd/") + + // 公开接口,不需要授权 + group.GET("imgWall", h.ImgWall) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("image", h.Image) + group.GET("jobs", h.JobList) + group.GET("remove", h.Remove) + group.GET("publish", h.Publish) + } +} + func (h *SdJobHandler) preCheck(c *gin.Context) bool { user, err := h.GetLoginUser(c) if err != nil { @@ -63,7 +85,7 @@ func (h *SdJobHandler) preCheck(c *gin.Context) bool { return false } - if user.Power < h.App.SysConfig.SdPower { + if user.Power < h.App.SysConfig.Base.SdPower { resp.ERROR(c, "当前用户剩余算力不足以完成本次绘画!") return false } @@ -84,6 +106,29 @@ func (h *SdJobHandler) Image(c *gin.Context) { return } + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(data.Prompt) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: h.GetLoginUserId(c), + Source: types.ModerationSourceSD, + Input: data.Prompt, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + resp.ERROR(c, "当前创作内容包含敏感词,请重新输入!") + return + } + + } + if data.Width <= 0 { data.Width = 512 } @@ -131,7 +176,7 @@ func (h *SdJobHandler) Image(c *gin.Context) { HdSteps: data.HdSteps, }, UserId: userId, - TranslateModelId: h.App.SysConfig.AssistantModelId, + TranslateModelId: h.App.SysConfig.Base.AssistantModelId, } job := model.SdJob{ @@ -142,7 +187,7 @@ func (h *SdJobHandler) Image(c *gin.Context) { TaskInfo: utils.JsonEncode(task), Prompt: data.Prompt, Progress: 0, - Power: h.App.SysConfig.SdPower, + Power: h.App.SysConfig.Base.SdPower, CreatedAt: time.Now(), } res := h.DB.Create(&job) diff --git a/api/handler/sms_handler.go b/api/handler/sms_handler.go index 191530d7..ae4fb3d3 100644 --- a/api/handler/sms_handler.go +++ b/api/handler/sms_handler.go @@ -24,24 +24,31 @@ const CodeStorePrefix = "/verify/codes/" type SmsHandler struct { BaseHandler - redis *redis.Client - sms *sms.ServiceManager - smtp *service.SmtpService - captcha *service.CaptchaService + redis *redis.Client + sms *sms.SmsManager + smtp *service.SmtpService + captchaService *service.CaptchaService } func NewSmsHandler( app *core.AppServer, client *redis.Client, - sms *sms.ServiceManager, + sms *sms.SmsManager, smtp *service.SmtpService, captcha *service.CaptchaService) *SmsHandler { return &SmsHandler{ - redis: client, - sms: sms, - captcha: captcha, - smtp: smtp, - BaseHandler: BaseHandler{App: app}} + redis: client, + sms: sms, + captchaService: captcha, + smtp: smtp, + BaseHandler: BaseHandler{App: app}} +} + +// RegisterRoutes 注册路由 +func (h *SmsHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/sms/") + // 无需授权的接口 + group.POST("code", h.SendCode) } // SendCode 发送验证码 @@ -56,12 +63,12 @@ func (h *SmsHandler) SendCode(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - if h.App.SysConfig.EnabledVerify { + if h.captchaService.GetConfig().Enabled { var check bool if data.X != 0 { - check = h.captcha.SlideCheck(data) + check = h.captchaService.SlideCheck(data) } else { - check = h.captcha.Check(data) + check = h.captchaService.Check(data) } if !check { resp.ERROR(c, "请先完人机验证") @@ -72,14 +79,14 @@ func (h *SmsHandler) SendCode(c *gin.Context) { code := utils.RandomNumber(6) var err error if strings.Contains(data.Receiver, "@") { // email - if !utils.Contains(h.App.SysConfig.RegisterWays, "email") { + if !utils.Contains(h.App.SysConfig.Base.RegisterWays, "email") { resp.ERROR(c, "系统已禁用邮箱注册!") return } // 检查邮箱后缀是否在白名单 - if len(h.App.SysConfig.EmailWhiteList) > 0 { + if len(h.App.SysConfig.Base.EmailWhiteList) > 0 { inWhiteList := false - for _, suffix := range h.App.SysConfig.EmailWhiteList { + for _, suffix := range h.App.SysConfig.Base.EmailWhiteList { if strings.HasSuffix(data.Receiver, suffix) { inWhiteList = true break @@ -92,7 +99,7 @@ func (h *SmsHandler) SendCode(c *gin.Context) { } err = h.smtp.SendVerifyCode(data.Receiver, code) } else { - if !utils.Contains(h.App.SysConfig.RegisterWays, "mobile") { + if !utils.Contains(h.App.SysConfig.Base.RegisterWays, "mobile") { resp.ERROR(c, "系统已禁用手机号注册!") return } diff --git a/api/handler/suno_handler.go b/api/handler/suno_handler.go index 274986fd..73db2e42 100644 --- a/api/handler/suno_handler.go +++ b/api/handler/suno_handler.go @@ -10,8 +10,10 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" + "geekai/service/moderation" "geekai/service/oss" "geekai/service/suno" "geekai/store/model" @@ -26,20 +28,41 @@ import ( type SunoHandler struct { BaseHandler - sunoService *suno.Service - uploader *oss.UploaderManager - userService *service.UserService + sunoService *suno.Service + uploader *oss.UploaderManager + userService *service.UserService + moderationManager *moderation.ServiceManager } -func NewSunoHandler(app *core.AppServer, db *gorm.DB, service *suno.Service, uploader *oss.UploaderManager, userService *service.UserService) *SunoHandler { +func NewSunoHandler(app *core.AppServer, db *gorm.DB, service *suno.Service, uploader *oss.UploaderManager, userService *service.UserService, moderationManager *moderation.ServiceManager) *SunoHandler { return &SunoHandler{ BaseHandler: BaseHandler{ App: app, DB: db, }, - sunoService: service, - uploader: uploader, - userService: userService, + sunoService: service, + uploader: uploader, + userService: userService, + moderationManager: moderationManager, + } +} + +// RegisterRoutes 注册路由 +func (h *SunoHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/suno/") + + // 公开接口,不需要授权 + group.GET("play", h.Play) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("create", h.Create) + group.GET("list", h.List) + group.GET("remove", h.Remove) + group.GET("publish", h.Publish) + group.POST("update", h.Update) + group.GET("detail", h.Detail) } } @@ -64,13 +87,36 @@ func (h *SunoHandler) Create(c *gin.Context) { return } + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(data.Prompt) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: h.GetLoginUserId(c), + Source: types.ModerationSourceSuno, + Input: data.Prompt, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + resp.ERROR(c, "当前创作内容包含敏感词,请重新输入!") + return + } + + } + user, err := h.GetLoginUser(c) if err != nil { resp.NotAuth(c) return } - if user.Power < h.App.SysConfig.SunoPower { + if user.Power < h.App.SysConfig.Base.SunoPower { resp.ERROR(c, "您的算力不足,请充值后再试!") return } @@ -118,7 +164,7 @@ func (h *SunoHandler) Create(c *gin.Context) { RefSongId: data.RefSongId, RefTaskId: data.RefTaskId, ExtendSecs: data.ExtendSecs, - Power: h.App.SysConfig.SunoPower, + Power: h.App.SysConfig.Base.SunoPower, SongId: utils.RandString(32), } if data.Lyrics != "" { diff --git a/api/handler/test_handler.go b/api/handler/test_handler.go index 3ee0c622..7dae66ac 100644 --- a/api/handler/test_handler.go +++ b/api/handler/test_handler.go @@ -1,21 +1,36 @@ package handler import ( + "geekai/core" + "geekai/core/middleware" "geekai/service" "geekai/service/payment" + "net/http" + "github.com/gin-gonic/gin" "gorm.io/gorm" - "net/http" ) type TestHandler struct { + App *core.AppServer db *gorm.DB snowflake *service.Snowflake - js *payment.GeekPayService + js *payment.EPayService } -func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.GeekPayService) *TestHandler { - return &TestHandler{db: db, snowflake: snowflake, js: js} +func NewTestHandler(app *core.AppServer, db *gorm.DB, snowflake *service.Snowflake, js *payment.EPayService) *TestHandler { + return &TestHandler{App: app, db: db, snowflake: snowflake, js: js} +} + +// RegisterRoutes 注册路由 +func (h *TestHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/test/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.Any("sse", h.PostTest, h.SseTest) + } } func (h *TestHandler) SseTest(c *gin.Context) { diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index 0e159759..c70ecb90 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -8,8 +8,10 @@ package handler // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "context" "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" "geekai/store" @@ -20,8 +22,6 @@ import ( "strings" "time" - "github.com/imroc/req/v3" - "github.com/go-redis/redis/v8" "github.com/golang-jwt/jwt/v5" @@ -36,8 +36,10 @@ type UserHandler struct { redis *redis.Client levelDB *store.LevelDB licenseService *service.LicenseService - captcha *service.CaptchaService + captchaService *service.CaptchaService userService *service.UserService + wxLoginService *service.WxLoginService + ipSearcher *xdb.Searcher } func NewUserHandler( @@ -48,15 +50,45 @@ func NewUserHandler( levelDB *store.LevelDB, captcha *service.CaptchaService, userService *service.UserService, + wxLoginService *service.WxLoginService, + ipSearcher *xdb.Searcher, licenseService *service.LicenseService) *UserHandler { return &UserHandler{ BaseHandler: BaseHandler{DB: db, App: app}, searcher: searcher, redis: client, levelDB: levelDB, - captcha: captcha, + captchaService: captcha, licenseService: licenseService, userService: userService, + wxLoginService: wxLoginService, + ipSearcher: ipSearcher, + } +} + +// RegisterRoutes 注册路由 +func (h *UserHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/user/") + + // 公开接口,不需要授权 + group.POST("register", h.Register) + group.POST("login", h.Login) + group.POST("resetPass", h.ResetPass) + group.GET("login/qrcode", h.GetWxLoginQRCode) + group.POST("login/callback", h.WxLoginCallback) + group.GET("login/status", h.GetWxLoginState) + group.GET("logout", h.Logout) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.GET("session", h.Session) + group.GET("profile", h.Profile) + group.POST("profile/update", h.ProfileUpdate) + group.POST("password", h.UpdatePass) + group.POST("bind/mobile", h.BindMobile) + group.POST("bind/email", h.BindEmail) + group.GET("signin", h.SignIn) } } @@ -80,12 +112,13 @@ func (h *UserHandler) Register(c *gin.Context) { return } - if h.App.SysConfig.EnabledVerify && data.RegWay == "username" { + // 人机验证 + if h.captchaService.GetConfig().Enabled { var check bool if data.X != 0 { - check = h.captcha.SlideCheck(data) + check = h.captchaService.SlideCheck(data) } else { - check = h.captcha.Check(data) + check = h.captchaService.Check(data) } if !check { resp.ERROR(c, "请先完人机验证") @@ -125,30 +158,8 @@ func (h *UserHandler) Register(c *gin.Context) { } } - // 验证邀请码 - inviteCode := model.InviteCode{} - if data.InviteCode != "" { - res := h.DB.Where("code = ?", data.InviteCode).First(&inviteCode) - if res.Error != nil { - resp.ERROR(c, "无效的邀请码") - return - } - } - - salt := utils.RandString(8) - user := model.User{ - Username: data.Username, - Password: utils.GenPassword(data.Password, salt), - Avatar: "/images/avatar/user.png", - Salt: salt, - Status: true, - ChatRoles: utils.JsonEncode([]string{"gpt"}), // 默认只订阅通用助手角色 - ChatConfig: "{}", - ChatModels: "{}", - Power: h.App.SysConfig.InitPower, - } - // check if the username is existing + user := model.User{Username: data.Username, Password: data.Password} var item model.User session := h.DB.Session(&gorm.Session{}) if data.Mobile != "" { @@ -168,78 +179,19 @@ func (h *UserHandler) Register(c *gin.Context) { return } - // 被邀请人也获得赠送算力 - if data.InviteCode != "" { - user.Power += h.App.SysConfig.InvitePower - } - - if h.licenseService.GetLicense().Configs.DeCopy { - user.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6)) - } else { - defaultNickname := h.App.SysConfig.DefaultNickname - if defaultNickname == "" { - defaultNickname = "极客学长" - } - user.Nickname = fmt.Sprintf("%s@%d", defaultNickname, utils.RandomNumber(6)) - } - - tx := h.DB.Begin() - if err := tx.Create(&user).Error; err != nil { + user, err := h.createNewUser(user, data.InviteCode) + if err != nil { resp.ERROR(c, err.Error()) return } - // 记录邀请关系 - if data.InviteCode != "" { - // 增加邀请数量 - h.DB.Model(&model.InviteCode{}).Where("code = ?", data.InviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1)) - if h.App.SysConfig.InvitePower > 0 { - err := h.userService.IncreasePower(inviteCode.UserId, h.App.SysConfig.InvitePower, model.PowerLog{ - Type: types.PowerInvite, - Model: "Invite", - Remark: fmt.Sprintf("邀请用户注册奖励,金额:%d,邀请码:%s,新用户:%s", h.App.SysConfig.InvitePower, inviteCode.Code, user.Username), - }) - if err != nil { - tx.Rollback() - resp.ERROR(c, err.Error()) - return - } - } - - // 添加邀请记录 - err := tx.Create(&model.InviteLog{ - InviterId: inviteCode.UserId, - UserId: user.Id, - Username: user.Username, - InviteCode: inviteCode.Code, - Remark: fmt.Sprintf("奖励 %d 算力", h.App.SysConfig.InvitePower), - }).Error - if err != nil { - tx.Rollback() - resp.ERROR(c, err.Error()) - return - } - } - tx.Commit() - - _ = h.redis.Del(c, key) // 注册成功,删除短信验证码 - // 自动登录创建 token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "user_id": user.Id, - "expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(), - }) - tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey)) + token, err := h.doLogin(&user, c.ClientIP()) if err != nil { - resp.ERROR(c, "Failed to generate token, "+err.Error()) + resp.ERROR(c, err.Error()) return } - // 保存到 redis - key = fmt.Sprintf("users/%d", user.Id) - if _, err := h.redis.Set(c, key, tokenString, 0).Result(); err != nil { - resp.ERROR(c, "error with save token: "+err.Error()) - return - } - resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username}) + + resp.SUCCESS(c, gin.H{"token": token, "user_id": user.Id, "username": user.Username}) } // Login 用户登录 @@ -255,15 +207,12 @@ func (h *UserHandler) Login(c *gin.Context) { resp.ERROR(c, types.InvalidArgs) return } - verifyKey := fmt.Sprintf("users/verify/%s", data.Username) - needVerify, err := h.redis.Get(c, verifyKey).Bool() - - if h.App.SysConfig.EnabledVerify && needVerify { + if h.captchaService.GetConfig().Enabled { var check bool if data.X != 0 { - check = h.captcha.SlideCheck(data) + check = h.captchaService.SlideCheck(data) } else { - check = h.captcha.Check(data) + check = h.captchaService.Check(data) } if !check { resp.ERROR(c, "请先完人机验证") @@ -274,54 +223,28 @@ func (h *UserHandler) Login(c *gin.Context) { var user model.User res := h.DB.Where("username = ?", data.Username).First(&user) if res.Error != nil { - h.redis.Set(c, verifyKey, true, 0) resp.ERROR(c, "用户名不存在") return } password := utils.GenPassword(data.Password, user.Salt) if password != user.Password { - h.redis.Set(c, verifyKey, true, 0) resp.ERROR(c, "用户名或密码错误") return } - if user.Status == false { + if !user.Status { resp.ERROR(c, "该用户已被禁止登录,请联系管理员") return } - // 更新最后登录时间和IP - user.LastLoginIp = c.ClientIP() - user.LastLoginAt = time.Now().Unix() - h.DB.Model(&user).Updates(user) - - h.DB.Create(&model.UserLoginLog{ - UserId: user.Id, - Username: user.Username, - LoginIp: c.ClientIP(), - LoginAddress: utils.Ip2Region(h.searcher, c.ClientIP()), - }) - - // 创建 token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "user_id": user.Id, - "expired": time.Now().Add(time.Second * time.Duration(h.App.Config.Session.MaxAge)).Unix(), - }) - tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey)) + token, err := h.doLogin(&user, c.ClientIP()) if err != nil { - resp.ERROR(c, "Failed to generate token, "+err.Error()) + resp.ERROR(c, err.Error()) return } - // 保存到 redis - sessionKey := fmt.Sprintf("users/%d", user.Id) - if _, err = h.redis.Set(c, sessionKey, tokenString, 0).Result(); err != nil { - resp.ERROR(c, "error with save token: "+err.Error()) - return - } - // 移除登录行为验证码 - h.redis.Del(c, verifyKey) - resp.SUCCESS(c, gin.H{"token": tokenString, "user_id": user.Id, "username": user.Username}) + + resp.SUCCESS(c, gin.H{"token": token, "user_id": user.Id, "username": user.Username}) } // Logout 注 销 @@ -333,134 +256,165 @@ func (h *UserHandler) Logout(c *gin.Context) { resp.SUCCESS(c) } -// CLogin 第三方登录请求二维码 -func (h *UserHandler) CLogin(c *gin.Context) { - returnURL := h.GetTrim(c, "return_url") - var res types.BizVo - apiURL := fmt.Sprintf("%s/api/clogin/request", h.App.Config.ApiConfig.ApiURL) - r, err := req.C().R().SetBody(gin.H{"login_type": "wx", "return_url": returnURL}). - SetHeader("AppId", h.App.Config.ApiConfig.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.App.Config.ApiConfig.Token)). - SetSuccessResult(&res). - Post(apiURL) +// GetWxLoginQRCode 获取微信登录二维码URL +func (h *UserHandler) GetWxLoginQRCode(c *gin.Context) { + if !h.wxLoginService.GetConfig().Enabled { + resp.ERROR(c, "微信登录功能未启用") + return + } + + if h.wxLoginService.GetConfig().ApiKey == "" { + resp.ERROR(c, "微信登录服务令牌未配置") + return + } + + state := utils.RandString(32) + qrCodeURL, err := h.wxLoginService.GetLoginQrCodeUrl(state) if err != nil { resp.ERROR(c, err.Error()) return } - if r.IsErrorState() { - resp.ERROR(c, "error with login http status: "+r.Status) - return - } - if res.Code != types.Success { - resp.ERROR(c, "error with http response: "+res.Message) - return - } - - resp.SUCCESS(c, res.Data) + resp.SUCCESS(c, gin.H{ + "url": qrCodeURL, + "state": state, + }) } -// CLoginCallback 第三方登录回调 -func (h *UserHandler) CLoginCallback(c *gin.Context) { - loginType := c.Query("login_type") - code := c.Query("code") - userId := h.GetInt(c, "user_id", 0) - action := c.Query("action") +// 查询微信登录状态 +func (h *UserHandler) GetWxLoginState(c *gin.Context) { + state := c.Query("state") + if state == "" { + resp.ERROR(c, "参数错误") + return + } - var res types.BizVo - apiURL := fmt.Sprintf("%s/api/clogin/info", h.App.Config.ApiConfig.ApiURL) - r, err := req.C().R().SetBody(gin.H{"login_type": loginType, "code": code}). - SetHeader("AppId", h.App.Config.ApiConfig.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.App.Config.ApiConfig.Token)). - SetSuccessResult(&res). - Post(apiURL) + status, err := h.wxLoginService.GetLoginStatus(state) if err != nil { resp.ERROR(c, err.Error()) return } - if r.IsErrorState() { - resp.ERROR(c, "error with login http status: "+r.Status) + + if status.Status != service.LoginStatusSuccess { + resp.SUCCESS(c, status) return } - if res.Code != types.Success { - resp.ERROR(c, "error with http response: "+res.Message) - return - } - - // login successfully - data := res.Data.(map[string]interface{}) + // 登录成功 var user model.User - if action == "bind" && userId > 0 { - err = h.DB.Where("openid", data["openid"]).First(&user).Error - if err == nil { - resp.ERROR(c, "该微信已经绑定其他账号,请先解绑") - return - } - - err = h.DB.Where("id", userId).First(&user).Error + h.DB.Where("openid = ?", status.OpenID).First(&user) + if user.Id == 0 { + // 创建新用户 + user, err = h.createNewUser(model.User{OpenId: status.OpenID}, "") if err != nil { - resp.ERROR(c, "绑定用户不存在") + resp.ERROR(c, err.Error()) return } + } - err = h.DB.Model(&user).UpdateColumn("openid", data["openid"]).Error - if err != nil { - resp.ERROR(c, "更新用户信息失败,"+err.Error()) - return - } - - resp.SUCCESS(c, gin.H{"token": ""}) + token, err := h.doLogin(&user, c.ClientIP()) + if err != nil { + resp.ERROR(c, err.Error()) return } - session := gin.H{} - tx := h.DB.Where("openid", data["openid"]).First(&user) - if tx.Error != nil { - // create new user - var totalUser int64 - h.DB.Model(&model.User{}).Count(&totalUser) - if h.licenseService.GetLicense().Configs.UserNum > 0 && int(totalUser) >= h.licenseService.GetLicense().Configs.UserNum { - resp.ERROR(c, "当前注册用户数已达上限,请请升级 License") - return - } + status.Status = service.LoginStatusExpired + h.wxLoginService.SetLoginStatus(state, *status) - salt := utils.RandString(8) - password := fmt.Sprintf("%d", utils.RandomNumber(8)) - user = model.User{ - Username: fmt.Sprintf("%s@%d", loginType, utils.RandomNumber(10)), - Password: utils.GenPassword(password, salt), - Avatar: fmt.Sprintf("%s", data["avatar"]), - Salt: salt, - Status: true, - ChatRoles: utils.JsonEncode([]string{"gpt"}), // 默认只订阅通用助手角色 - Power: h.App.SysConfig.InitPower, - OpenId: fmt.Sprintf("%s", data["openid"]), - Nickname: fmt.Sprintf("%s", data["nickname"]), - } + status.Status = service.LoginStatusSuccess + status.Token = token + resp.SUCCESS(c, status) +} - tx = h.DB.Create(&user) - if tx.Error != nil { - resp.ERROR(c, "保存数据失败") - logger.Error(tx.Error) - return +// createNewUser 创建新用户 +func (h *UserHandler) createNewUser(user model.User, inviteCode string) (model.User, error) { + if user.OpenId != "" { + user.Platform = "wechat" + user.Nickname = fmt.Sprintf("微信用户@%d", utils.RandomNumber(6)) + user.Username = fmt.Sprintf("wx@%d", utils.RandomNumber(8)) + user.Password = "geekai123" + } else { + user.Nickname = fmt.Sprintf("用户@%d", utils.RandomNumber(6)) + if user.Username == "" || user.Password == "" { + return user, fmt.Errorf("用户名或密码不能为空") } - session["username"] = user.Username - session["password"] = password - } else { // login directly - // 更新最后登录时间和IP - user.LastLoginIp = c.ClientIP() - user.LastLoginAt = time.Now().Unix() - h.DB.Model(&user).Updates(user) - - h.DB.Create(&model.UserLoginLog{ - UserId: user.Id, - Username: user.Username, - LoginIp: c.ClientIP(), - LoginAddress: utils.Ip2Region(h.searcher, c.ClientIP()), - }) } + salt := utils.RandString(8) + user.Salt = salt + user.Password = utils.GenPassword(user.Password, salt) + user.Avatar = "/images/avatar/user.png" + user.Status = true + user.ChatRoles = utils.JsonEncode([]string{"gpt"}) + user.ChatConfig = "{}" + user.ChatModels = "{}" + user.Power = h.App.SysConfig.Base.InitPower + + // 创建用户 + tx := h.DB.Begin() + if err := tx.Create(&user).Error; err != nil { + return user, err + } + + // 记录邀请关系 + if inviteCode != "" { + inviteCode := model.InviteCode{} + err := h.DB.Where("code = ?", inviteCode).First(&inviteCode).Error + if err != nil { + return user, fmt.Errorf("无效的邀请码") + } + + // 增加邀请数量 + h.DB.Model(&model.InviteCode{}).Where("code = ?", inviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1)) + if h.App.SysConfig.Base.InvitePower > 0 { + err := h.userService.IncreasePower(inviteCode.UserId, h.App.SysConfig.Base.InvitePower, model.PowerLog{ + Type: types.PowerInvite, + Model: "Invite", + Remark: fmt.Sprintf("邀请用户注册奖励,金额:%d,邀请码:%s,新用户:%s", h.App.SysConfig.Base.InvitePower, inviteCode.Code, user.Username), + }) + if err != nil { + tx.Rollback() + return user, err + } + + // 添加邀请记录 + err = tx.Create(&model.InviteLog{ + InviterId: inviteCode.UserId, + UserId: user.Id, + Username: user.Username, + InviteCode: inviteCode.Code, + Remark: fmt.Sprintf("奖励 %d 算力", h.App.SysConfig.Base.InvitePower), + }).Error + if err != nil { + tx.Rollback() + return user, err + } + } + } + + tx.Commit() + + return user, nil +} + +// doLogin 执行登录操作 +func (h *UserHandler) doLogin(user *model.User, ip string) (string, error) { + // 更新最后登录时间和IP + user.LastLoginIp = ip + user.LastLoginAt = time.Now().Unix() + err := h.DB.Model(user).Updates(user).Error + if err != nil { + return "", fmt.Errorf("failed to update user: %v", err) + } + + // 记录登录日志 + h.DB.Create(&model.UserLoginLog{ + UserId: user.Id, + Username: user.Username, + LoginIp: ip, + LoginAddress: utils.Ip2Region(h.ipSearcher, ip), + }) + // 创建 token token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "user_id": user.Id, @@ -468,17 +422,42 @@ func (h *UserHandler) CLoginCallback(c *gin.Context) { }) tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey)) if err != nil { - resp.ERROR(c, "Failed to generate token, "+err.Error()) - return + return "", fmt.Errorf("failed to generate token: %v", err) } + // 保存到 redis - key := fmt.Sprintf("users/%d", user.Id) - if _, err := h.redis.Set(c, key, tokenString, 0).Result(); err != nil { - resp.ERROR(c, "error with save token: "+err.Error()) + sessionKey := fmt.Sprintf("users/%d", user.Id) + if _, err = h.redis.Set(context.Background(), sessionKey, tokenString, 0).Result(); err != nil { + return "", fmt.Errorf("error with save token: %v", err) + } + + return tokenString, nil +} + +// WxLoginCallback 微信登录回调处理 +func (h *UserHandler) WxLoginCallback(c *gin.Context) { + var data struct { + OpenID string `json:"openid"` + State string `json:"state"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) return } - session["token"] = tokenString - resp.SUCCESS(c, session) + + if data.OpenID == "" || data.State == "" { + resp.ERROR(c, "参数错误") + return + } + + // 设置登录状态 + status := service.LoginStatus{ + Status: service.LoginStatusSuccess, + OpenID: data.OpenID, + } + h.wxLoginService.SetLoginStatus(data.State, status) + + resp.SUCCESS(c, status) } // Session 获取/验证会话 @@ -742,11 +721,11 @@ func (h *UserHandler) SignIn(c *gin.Context) { // 签到 h.levelDB.Put(key, true) - if h.App.SysConfig.DailyPower > 0 { - h.userService.IncreasePower(userId, h.App.SysConfig.DailyPower, model.PowerLog{ + if h.App.SysConfig.Base.DailyPower > 0 { + h.userService.IncreasePower(userId, h.App.SysConfig.Base.DailyPower, model.PowerLog{ Type: types.PowerSignIn, Model: "SignIn", - Remark: fmt.Sprintf("每日签到奖励,金额:%d", h.App.SysConfig.DailyPower), + Remark: fmt.Sprintf("每日签到奖励,金额:%d", h.App.SysConfig.Base.DailyPower), }) } resp.SUCCESS(c) diff --git a/api/handler/video_handler.go b/api/handler/video_handler.go index 6543a8c2..d55873d8 100644 --- a/api/handler/video_handler.go +++ b/api/handler/video_handler.go @@ -10,8 +10,10 @@ package handler import ( "fmt" "geekai/core" + "geekai/core/middleware" "geekai/core/types" "geekai/service" + "geekai/service/moderation" "geekai/service/oss" "geekai/service/video" "geekai/store/model" @@ -26,20 +28,37 @@ import ( type VideoHandler struct { BaseHandler - videoService *video.Service - uploader *oss.UploaderManager - userService *service.UserService + videoService *video.Service + uploader *oss.UploaderManager + userService *service.UserService + moderationManager *moderation.ServiceManager } -func NewVideoHandler(app *core.AppServer, db *gorm.DB, service *video.Service, uploader *oss.UploaderManager, userService *service.UserService) *VideoHandler { +func NewVideoHandler(app *core.AppServer, db *gorm.DB, service *video.Service, uploader *oss.UploaderManager, userService *service.UserService, moderationManager *moderation.ServiceManager) *VideoHandler { return &VideoHandler{ BaseHandler: BaseHandler{ App: app, DB: db, }, - videoService: service, - uploader: uploader, - userService: userService, + videoService: service, + uploader: uploader, + userService: userService, + moderationManager: moderationManager, + } +} + +// RegisterRoutes 注册路由 +func (h *VideoHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/video/") + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("luma/create", h.LumaCreate) + group.POST("keling/create", h.KeLingCreate) + group.GET("list", h.List) + group.GET("remove", h.Remove) + group.GET("publish", h.Publish) } } @@ -62,13 +81,36 @@ func (h *VideoHandler) LumaCreate(c *gin.Context) { return } + if h.App.SysConfig.Moderation.Enable { + moderationResult, err := h.moderationManager.GetService().Moderate(data.Prompt) + if err != nil { + logger.Error("failed to moderate content: ", err) + } + if moderationResult.Flagged { + // 记录违规内容 + moderation := model.Moderation{ + UserId: h.GetLoginUserId(c), + Source: types.ModerationSourceVideo, + Input: data.Prompt, + Result: utils.JsonEncode(moderationResult), + } + err = h.DB.Create(&moderation).Error + if err != nil { + logger.Error("failed to save moderation: ", err) + } + resp.ERROR(c, "当前创作内容包含敏感词,请重新输入!") + return + } + + } + user, err := h.GetLoginUser(c) if err != nil { resp.NotAuth(c) return } - if user.Power < h.App.SysConfig.LumaPower { + if user.Power < h.App.SysConfig.Base.LumaPower { resp.ERROR(c, "您的算力不足,请充值后再试!") return } @@ -85,14 +127,14 @@ func (h *VideoHandler) LumaCreate(c *gin.Context) { Type: types.VideoLuma, Prompt: data.Prompt, Params: params, - TranslateModelId: h.App.SysConfig.AssistantModelId, + TranslateModelId: h.App.SysConfig.Base.AssistantModelId, } // 插入数据库 job := model.VideoJob{ UserId: uint(userId), Type: types.VideoLuma, Prompt: data.Prompt, - Power: h.App.SysConfig.LumaPower, + Power: h.App.SysConfig.Base.LumaPower, TaskInfo: utils.JsonEncode(task), } tx := h.DB.Create(&job) @@ -147,7 +189,7 @@ func (h *VideoHandler) KeLingCreate(c *gin.Context) { // 计算当前任务所需算力 key := fmt.Sprintf("%s_%s_%s", data.Model, data.Mode, data.Duration) - power := h.App.SysConfig.KeLingPowers[key] + power := h.App.SysConfig.Base.KeLingPowers[key] if power == 0 { resp.ERROR(c, "当前模型暂不支持") return @@ -181,7 +223,7 @@ func (h *VideoHandler) KeLingCreate(c *gin.Context) { Type: types.VideoKeLing, Prompt: data.Prompt, Params: params, - TranslateModelId: h.App.SysConfig.AssistantModelId, + TranslateModelId: h.App.SysConfig.Base.AssistantModelId, Channel: data.Channel, } // 插入数据库 diff --git a/api/main.go b/api/main.go index 385a7615..7901f8df 100644 --- a/api/main.go +++ b/api/main.go @@ -19,6 +19,7 @@ import ( "geekai/service/dalle" "geekai/service/jimeng" "geekai/service/mj" + "geekai/service/moderation" "geekai/service/oss" "geekai/service/payment" "geekai/service/sd" @@ -30,7 +31,7 @@ import ( "log" "os" "os/signal" - "strconv" + "runtime/debug" "syscall" "time" @@ -71,15 +72,16 @@ func main() { if configFile == "" { configFile = "config.toml" } - debug, _ := strconv.ParseBool(os.Getenv("APP_DEBUG")) logger.Info("Loading config file: ", configFile) - if !debug { - defer func() { - if err := recover(); err != nil { - logger.Error("Panic Error:", err) + defer func() { + if err := recover(); err != nil { + logger.Error("Panic Error:", err) + // 打印堆栈信息 + if os.Getenv("GEEKAI_DEBUG") == "true" { + debug.PrintStack() } - }() - } + } + }() app := fx.New( // 初始化配置应用配置 @@ -89,16 +91,16 @@ func main() { log.Fatal(err) } config.Path = configFile - if debug { - _ = core.SaveConfig(config) - } return config }), // 创建应用服务 fx.Provide(core.NewServer), // 初始化 fx.Invoke(func(s *core.AppServer, client *redis.Client) { - s.Init(debug, client) + s.Init(client) + }), + fx.Provide(func(db *gorm.DB) *types.SystemConfig { + return core.LoadSystemConfig(db) }), // 初始化数据库 @@ -126,7 +128,7 @@ func main() { }), // 创建控制器 - fx.Provide(handler.NewChatRoleHandler), + fx.Provide(handler.NewChatAppHandler), fx.Provide(handler.NewUserHandler), fx.Provide(handler.NewChatHandler), fx.Provide(handler.NewNetHandler), @@ -143,6 +145,12 @@ func main() { fx.Provide(handler.NewPowerLogHandler), fx.Provide(handler.NewJimengHandler), + fx.Provide(service.NewMigrationService), + fx.Invoke(func(migrationService *service.MigrationService) { + migrationService.StartMigrate() + }), + + // 管理后台控制器 fx.Provide(admin.NewConfigHandler), fx.Provide(admin.NewAdminHandler), fx.Provide(admin.NewApiKeyHandler), @@ -153,34 +161,23 @@ func main() { fx.Provide(admin.NewChatModelHandler), fx.Provide(admin.NewProductHandler), fx.Provide(admin.NewOrderHandler), - fx.Provide(admin.NewChatHandler), fx.Provide(admin.NewPowerLogHandler), fx.Provide(admin.NewAdminJimengHandler), - // 创建服务 - fx.Provide(sms.NewSendServiceManager), - fx.Provide(func(config *types.AppConfig) *service.CaptchaService { - return service.NewCaptchaService(config.ApiConfig) - }), - fx.Provide(oss.NewUploaderManager), - fx.Provide(dalle.NewService), - fx.Invoke(func(s *dalle.Service) { - s.Run() - s.DownloadImages() - s.CheckTaskStatus() - }), - - fx.Provide(service.NewMigrationService), - fx.Invoke(func(s *service.MigrationService) { - s.Migrate() - }), - // 邮件服务 fx.Provide(service.NewSmtpService), // License 服务 fx.Provide(service.NewLicenseService), fx.Invoke(func(licenseService *service.LicenseService) { - // licenseService.SyncLicense() + licenseService.SyncLicense() + }), + + // Dalle 服务 + fx.Provide(dalle.NewService), + fx.Invoke(func(s *dalle.Service) { + s.Run() + s.DownloadImages() + s.CheckTaskStatus() }), // MidJourney service pool @@ -213,302 +210,179 @@ func main() { }), // 即梦AI 服务 + fx.Provide(jimeng.NewClient), fx.Provide(jimeng.NewService), fx.Invoke(func(service *jimeng.Service) { service.Start() }), - fx.Provide(service.NewUserService), - fx.Provide(payment.NewAlipayService), - fx.Provide(payment.NewHuPiPay), - fx.Provide(payment.NewJPayService), - fx.Provide(payment.NewWechatService), + fx.Provide(service.NewSnowflake), - fx.Provide(service.NewXXLJobExecutor), - fx.Invoke(func(exec *service.XXLJobExecutor, config *types.AppConfig) { - if config.XXLConfig.Enabled { - go func() { - log.Fatal(exec.Run()) - }() - } + + // 创建短信服务 + fx.Provide(sms.NewAliYunSmsService), + fx.Provide(sms.NewBaoSmsService), + fx.Provide(sms.NewSmsManager), + fx.Provide(func(config *types.SystemConfig) *service.CaptchaService { + return service.NewCaptchaService(config.Captcha) + }), + fx.Provide(func(config *types.SystemConfig, client *redis.Client) *service.WxLoginService { + return service.NewWxLoginService(config.WxLogin, client) + }), + + // 支付服务 + fx.Provide(payment.NewAlipayService), + fx.Provide(payment.NewEPayService), + fx.Provide(payment.NewWxpayService), + + // 文件上传服务 + fx.Provide(oss.NewLocalStorage), + fx.Provide(oss.NewMiniOss), + fx.Provide(oss.NewQiNiuOss), + fx.Provide(oss.NewAliYunOss), + fx.Provide(oss.NewUploaderManager), + + // 用户服务 + fx.Provide(service.NewUserService), + + // 文本审查服务 + fx.Provide(moderation.NewGiteeAIModeration), + fx.Provide(moderation.NewBaiduAIModeration), + fx.Provide(moderation.NewTencentAIModeration), + fx.Provide(moderation.NewServiceManager), + fx.Provide(admin.NewModerationHandler), + fx.Invoke(func(s *core.AppServer, h *admin.ModerationHandler) { + h.RegisterRoutes() }), // 注册路由 - fx.Invoke(func(s *core.AppServer, h *handler.ChatRoleHandler) { - group := s.Engine.Group("/api/app/") - group.GET("list", h.List) - group.GET("list/user", h.ListByUser) - group.POST("update", h.UpdateRole) + fx.Invoke(func(s *core.AppServer, h *handler.ChatAppHandler) { + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.UserHandler) { - group := s.Engine.Group("/api/user/") - group.POST("register", h.Register) - group.POST("login", h.Login) - group.GET("logout", h.Logout) - group.GET("session", h.Session) - group.GET("profile", h.Profile) - group.POST("profile/update", h.ProfileUpdate) - group.POST("password", h.UpdatePass) - group.POST("bind/mobile", h.BindMobile) - group.POST("bind/email", h.BindEmail) - group.POST("resetPass", h.ResetPass) - group.GET("clogin", h.CLogin) - group.GET("clogin/callback", h.CLoginCallback) - group.GET("signin", h.SignIn) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.ChatHandler) { - group := s.Engine.Group("/api/chat/") - group.Any("message", h.Chat) - group.GET("list", h.List) - group.GET("detail", h.Detail) - group.POST("update", h.Update) - group.GET("remove", h.Remove) - group.GET("history", h.History) - group.GET("clear", h.Clear) - group.POST("tokens", h.Tokens) - group.GET("stop", h.StopGenerate) - group.POST("tts", h.TextToSpeech) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.NetHandler) { - s.Engine.POST("/api/upload", h.Upload) - s.Engine.POST("/api/upload/list", h.List) - s.Engine.GET("/api/upload/remove", h.Remove) - s.Engine.GET("/api/download", h.Download) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.SmsHandler) { - group := s.Engine.Group("/api/sms/") - group.POST("code", h.SendCode) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.CaptchaHandler) { - group := s.Engine.Group("/api/captcha/") - group.GET("get", h.Get) - group.POST("check", h.Check) - group.GET("slide/get", h.SlideGet) - group.POST("slide/check", h.SlideCheck) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.RedeemHandler) { - group := s.Engine.Group("/api/redeem/") - group.POST("verify", h.Verify) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.MidJourneyHandler) { - group := s.Engine.Group("/api/mj/") - group.POST("image", h.Image) - group.POST("upscale", h.Upscale) - group.POST("variation", h.Variation) - group.GET("jobs", h.JobList) - group.GET("imgWall", h.ImgWall) - group.GET("remove", h.Remove) - group.GET("publish", h.Publish) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.SdJobHandler) { - group := s.Engine.Group("/api/sd") - group.POST("image", h.Image) - group.GET("jobs", h.JobList) - group.GET("imgWall", h.ImgWall) - group.GET("remove", h.Remove) - group.GET("publish", h.Publish) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.ConfigHandler) { - group := s.Engine.Group("/api/config/") - group.GET("get", h.Get) - group.GET("license", h.License) + h.RegisterRoutes() }), - // 管理后台控制器 + // 管理后台路由注册 fx.Invoke(func(s *core.AppServer, h *admin.ConfigHandler) { - group := s.Engine.Group("/api/admin/config") - group.POST("update", h.Update) - group.GET("get", h.Get) - group.POST("active", h.Active) - group.GET("fixData", h.FixData) - group.GET("license", h.GetLicense) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.ManagerHandler) { - group := s.Engine.Group("/api/admin/") - group.POST("login", h.Login) - group.GET("logout", h.Logout) - group.GET("session", h.Session) - group.GET("list", h.List) - group.POST("save", h.Save) - group.POST("enable", h.Enable) - group.GET("remove", h.Remove) - group.POST("resetPass", h.ResetPass) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.ApiKeyHandler) { - group := s.Engine.Group("/api/admin/apikey/") - group.POST("save", h.Save) - group.GET("list", h.List) - group.POST("set", h.Set) - group.GET("remove", h.Remove) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.UserHandler) { - group := s.Engine.Group("/api/admin/user/") - group.GET("list", h.List) - group.POST("save", h.Save) - group.GET("remove", h.Remove) - group.GET("loginLog", h.LoginLog) - group.GET("genLoginLink", h.GenLoginLink) - group.POST("resetPass", h.ResetPass) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.ChatAppHandler) { - group := s.Engine.Group("/api/admin/role/") - group.GET("list", h.List) - group.POST("save", h.Save) - group.POST("sort", h.Sort) - group.POST("set", h.Set) - group.GET("remove", h.Remove) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.RedeemHandler) { - group := s.Engine.Group("/api/admin/redeem/") - group.GET("list", h.List) - group.POST("create", h.Create) - group.POST("set", h.Set) - group.GET("remove", h.Remove) - group.POST("export", h.Export) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.DashboardHandler) { - group := s.Engine.Group("/api/admin/dashboard/") - group.GET("stats", h.Stats) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.ChatModelHandler) { - group := s.Engine.Group("/api/model/") - group.GET("list", h.List) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.ChatModelHandler) { - group := s.Engine.Group("/api/admin/model/") - group.POST("save", h.Save) - group.GET("list", h.List) - group.POST("set", h.Set) - group.POST("sort", h.Sort) - group.GET("remove", h.Remove) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) { - group := s.Engine.Group("/api/payment/") - group.POST("doPay", h.Pay) - group.GET("payWays", h.GetPayWays) - group.POST("notify/alipay", h.AlipayNotify) - group.GET("notify/geek", h.GeekPayNotify) - group.POST("notify/wechat", h.WechatPayNotify) - group.POST("notify/hupi", h.HuPiPayNotify) + h.RegisterRoutes() + h.StartSyncOrders() }), fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) { - group := s.Engine.Group("/api/admin/product/") - group.POST("save", h.Save) - group.GET("list", h.List) - group.POST("enable", h.Enable) - group.POST("sort", h.Sort) - group.GET("remove", h.Remove) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.OrderHandler) { - group := s.Engine.Group("/api/admin/order/") - group.POST("list", h.List) - group.GET("remove", h.Remove) - group.GET("clear", h.Clear) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.OrderHandler) { - group := s.Engine.Group("/api/order/") - group.GET("list", h.List) - group.GET("query", h.Query) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.ProductHandler) { - group := s.Engine.Group("/api/product/") - group.GET("list", h.List) + h.RegisterRoutes() }), fx.Provide(handler.NewInviteHandler), fx.Invoke(func(s *core.AppServer, h *handler.InviteHandler) { - group := s.Engine.Group("/api/invite/") - group.GET("code", h.Code) - group.GET("list", h.List) - group.GET("hits", h.Hits) + h.RegisterRoutes() }), fx.Provide(admin.NewFunctionHandler), fx.Invoke(func(s *core.AppServer, h *admin.FunctionHandler) { - group := s.Engine.Group("/api/admin/function/") - group.POST("save", h.Save) - group.POST("set", h.Set) - group.GET("list", h.List) - group.GET("remove", h.Remove) - group.GET("token", h.GenToken) + h.RegisterRoutes() }), fx.Provide(admin.NewUploadHandler), fx.Invoke(func(s *core.AppServer, h *admin.UploadHandler) { - s.Engine.POST("/api/admin/upload", h.Upload) + h.RegisterRoutes() }), fx.Provide(handler.NewFunctionHandler), fx.Invoke(func(s *core.AppServer, h *handler.FunctionHandler) { - group := s.Engine.Group("/api/function/") - group.POST("weibo", h.WeiBo) - group.POST("zaobao", h.ZaoBao) - group.POST("dalle3", h.Dall3) - group.POST("websearch", h.WebSearch) - group.GET("list", h.List) + h.RegisterRoutes() }), + fx.Provide(admin.NewChatHandler), fx.Invoke(func(s *core.AppServer, h *admin.ChatHandler) { - group := s.Engine.Group("/api/admin/chat/") - group.POST("list", h.List) - group.POST("message", h.Messages) - group.GET("history", h.History) - group.GET("remove", h.RemoveChat) - group.GET("message/remove", h.RemoveMessage) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *handler.PowerLogHandler) { - group := s.Engine.Group("/api/powerLog/") - group.POST("list", h.List) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, h *admin.PowerLogHandler) { - group := s.Engine.Group("/api/admin/powerLog/") - group.POST("list", h.List) + h.RegisterRoutes() }), fx.Provide(admin.NewMenuHandler), fx.Invoke(func(s *core.AppServer, h *admin.MenuHandler) { - group := s.Engine.Group("/api/admin/menu/") - group.POST("save", h.Save) - group.GET("list", h.List) - group.POST("enable", h.Enable) - group.POST("sort", h.Sort) - group.GET("remove", h.Remove) + h.RegisterRoutes() }), fx.Provide(handler.NewMenuHandler), fx.Invoke(func(s *core.AppServer, h *handler.MenuHandler) { - group := s.Engine.Group("/api/menu/") - group.GET("list", h.List) + h.RegisterRoutes() }), fx.Provide(handler.NewMarkMapHandler), fx.Invoke(func(s *core.AppServer, h *handler.MarkMapHandler) { - s.Engine.POST("/api/markMap/gen", h.Generate) + h.RegisterRoutes() }), fx.Provide(handler.NewDallJobHandler), fx.Invoke(func(s *core.AppServer, h *handler.DallJobHandler) { - group := s.Engine.Group("/api/dall") - group.POST("image", h.Image) - group.GET("jobs", h.JobList) - group.GET("imgWall", h.ImgWall) - group.GET("remove", h.Remove) - group.GET("publish", h.Publish) - group.GET("models", h.GetModels) + h.RegisterRoutes() }), fx.Provide(handler.NewSunoHandler), fx.Invoke(func(s *core.AppServer, h *handler.SunoHandler) { - group := s.Engine.Group("/api/suno") - group.POST("create", h.Create) - group.GET("list", h.List) - group.GET("remove", h.Remove) - group.GET("publish", h.Publish) - group.POST("update", h.Update) - group.GET("detail", h.Detail) - group.GET("play", h.Play) + h.RegisterRoutes() }), fx.Provide(handler.NewVideoHandler), fx.Invoke(func(s *core.AppServer, h *handler.VideoHandler) { - group := s.Engine.Group("/api/video") - group.POST("luma/create", h.LumaCreate) - group.POST("keling/create", h.KeLingCreate) - group.GET("list", h.List) - group.GET("remove", h.Remove) - group.GET("publish", h.Publish) + h.RegisterRoutes() }), // 即梦AI 路由 @@ -520,30 +394,19 @@ func main() { }), fx.Provide(admin.NewChatAppTypeHandler), fx.Invoke(func(s *core.AppServer, h *admin.ChatAppTypeHandler) { - group := s.Engine.Group("/api/admin/app/type") - group.POST("save", h.Save) - group.GET("list", h.List) - group.GET("remove", h.Remove) - group.POST("enable", h.Enable) - group.POST("sort", h.Sort) + h.RegisterRoutes() }), fx.Provide(handler.NewChatAppTypeHandler), fx.Invoke(func(s *core.AppServer, h *handler.ChatAppTypeHandler) { - group := s.Engine.Group("/api/app/type") - group.GET("list", h.List) + h.RegisterRoutes() }), fx.Provide(handler.NewTestHandler), fx.Invoke(func(s *core.AppServer, h *handler.TestHandler) { - group := s.Engine.Group("/api/test") - group.Any("sse", h.PostTest, h.SseTest) + h.RegisterRoutes() }), fx.Provide(handler.NewPromptHandler), fx.Invoke(func(s *core.AppServer, h *handler.PromptHandler) { - group := s.Engine.Group("/api/prompt") - group.POST("/lyric", h.Lyric) - group.POST("/image", h.Image) - group.POST("/video", h.Video) - group.POST("/meta", h.MetaPrompt) + h.RegisterRoutes() }), fx.Invoke(func(s *core.AppServer, db *gorm.DB) { go func() { @@ -568,23 +431,15 @@ func main() { }), fx.Provide(admin.NewImageHandler), fx.Invoke(func(s *core.AppServer, h *admin.ImageHandler) { - group := s.Engine.Group("/api/admin/image") - group.POST("/list/mj", h.MjList) - group.POST("/list/sd", h.SdList) - group.POST("/list/dall", h.DallList) - group.GET("/remove", h.Remove) + h.RegisterRoutes() }), fx.Provide(admin.NewMediaHandler), fx.Invoke(func(s *core.AppServer, h *admin.MediaHandler) { - group := s.Engine.Group("/api/admin/media") - group.POST("/suno", h.SunoList) - group.POST("/videos", h.Videos) - group.GET("/remove", h.Remove) + h.RegisterRoutes() }), fx.Provide(handler.NewRealtimeHandler), fx.Invoke(func(s *core.AppServer, h *handler.RealtimeHandler) { - s.Engine.Any("/api/realtime", h.Connection) - s.Engine.POST("/api/realtime/voice", h.VoiceChat) + h.RegisterRoutes() }), ) // 启动应用程序 diff --git a/api/service/captcha_service.go b/api/service/captcha_service.go index 864e9395..998bcac3 100644 --- a/api/service/captcha_service.go +++ b/api/service/captcha_service.go @@ -8,35 +8,38 @@ package service // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( - "errors" "fmt" "geekai/core/types" - "github.com/imroc/req/v3" "time" + + "github.com/imroc/req/v3" ) type CaptchaService struct { - config types.ApiConfig + config types.CaptchaConfig client *req.Client } -func NewCaptchaService(config types.ApiConfig) *CaptchaService { +func NewCaptchaService(captchaConfig types.CaptchaConfig) *CaptchaService { return &CaptchaService{ - config: config, + config: captchaConfig, client: req.C().SetTimeout(10 * time.Second), } } +func (s *CaptchaService) UpdateConfig(config types.CaptchaConfig) { + s.config = config +} + +func (s *CaptchaService) GetConfig() types.CaptchaConfig { + return s.config +} + func (s *CaptchaService) Get() (interface{}, error) { - if s.config.Token == "" { - return nil, errors.New("无效的 API Token") - } - - url := fmt.Sprintf("%s/api/captcha/get", s.config.ApiURL) + url := fmt.Sprintf("%s/api/captcha/get", types.GeekAPIURL) var res types.BizVo r, err := s.client.R(). - SetHeader("AppId", s.config.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)). + SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)). SetSuccessResult(&res).Get(url) if err != nil || r.IsErrorState() { return nil, fmt.Errorf("请求 API 失败:%v", err) @@ -49,12 +52,11 @@ func (s *CaptchaService) Get() (interface{}, error) { return res.Data, nil } -func (s *CaptchaService) Check(data interface{}) bool { - url := fmt.Sprintf("%s/api/captcha/check", s.config.ApiURL) +func (s *CaptchaService) Check(data any) bool { + url := fmt.Sprintf("%s/api/captcha/check", types.GeekAPIURL) var res types.BizVo r, err := s.client.R(). - SetHeader("AppId", s.config.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)). + SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)). SetBodyJsonMarshal(data). SetSuccessResult(&res).Post(url) if err != nil || r.IsErrorState() { @@ -68,16 +70,11 @@ func (s *CaptchaService) Check(data interface{}) bool { return true } -func (s *CaptchaService) SlideGet() (interface{}, error) { - if s.config.Token == "" { - return nil, errors.New("无效的 API Token") - } - - url := fmt.Sprintf("%s/api/captcha/slide/get", s.config.ApiURL) +func (s *CaptchaService) SlideGet() (any, error) { + url := fmt.Sprintf("%s/api/captcha/slide/get", types.GeekAPIURL) var res types.BizVo r, err := s.client.R(). - SetHeader("AppId", s.config.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)). + SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)). SetSuccessResult(&res).Get(url) if err != nil || r.IsErrorState() { return nil, fmt.Errorf("请求 API 失败:%v", err) @@ -90,12 +87,11 @@ func (s *CaptchaService) SlideGet() (interface{}, error) { return res.Data, nil } -func (s *CaptchaService) SlideCheck(data interface{}) bool { - url := fmt.Sprintf("%s/api/captcha/slide/check", s.config.ApiURL) +func (s *CaptchaService) SlideCheck(data any) bool { + url := fmt.Sprintf("%s/api/captcha/slide/check", types.GeekAPIURL) var res types.BizVo r, err := s.client.R(). - SetHeader("AppId", s.config.AppId). - SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.Token)). + SetHeader("Authorization", fmt.Sprintf("Bearer %s", s.config.ApiKey)). SetBodyJsonMarshal(data). SetSuccessResult(&res).Post(url) if err != nil || r.IsErrorState() { diff --git a/api/service/crawler/service.go b/api/service/crawler/service.go deleted file mode 100644 index 39fb2fa7..00000000 --- a/api/service/crawler/service.go +++ /dev/null @@ -1,333 +0,0 @@ -package crawler - -import ( - "context" - "errors" - "fmt" - "geekai/logger" - "net/url" - "strings" - "time" - - "github.com/go-rod/rod" - "github.com/go-rod/rod/lib/launcher" - "github.com/go-rod/rod/lib/proto" -) - -// Service 网络爬虫服务 -type Service struct { - browser *rod.Browser -} - -// NewService 创建一个新的爬虫服务 -func NewService() (*Service, error) { - // 启动浏览器 - path, _ := launcher.LookPath() - u := launcher.New().Bin(path). - Headless(true). // 无头模式 - Set("disable-web-security", ""). // 禁用网络安全限制 - Set("disable-gpu", ""). // 禁用 GPU 加速 - Set("no-sandbox", ""). // 禁用沙箱模式 - Set("disable-setuid-sandbox", ""). // 禁用 setuid 沙箱 - MustLaunch() - - browser := rod.New().ControlURL(u).MustConnect() - - return &Service{ - browser: browser, - }, nil -} - -// SearchResult 搜索结果 -type SearchResult struct { - Title string `json:"title"` // 标题 - URL string `json:"url"` // 链接 - Content string `json:"content"` // 内容摘要 -} - -// WebSearch 网络搜索 -func (s *Service) WebSearch(keyword string, maxPages int) ([]SearchResult, error) { - if keyword == "" { - return nil, errors.New("搜索关键词不能为空") - } - - if maxPages <= 0 { - maxPages = 1 - } - if maxPages > 10 { - maxPages = 10 // 最多搜索 10 页 - } - - results := make([]SearchResult, 0) - - // 使用百度搜索 - searchURL := fmt.Sprintf("https://www.baidu.com/s?wd=%s", url.QueryEscape(keyword)) - - // 设置页面超时 - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - // 创建页面 - page := s.browser.MustPage() - defer page.MustClose() - - // 设置视口大小 - err := page.SetViewport(&proto.EmulationSetDeviceMetricsOverride{ - Width: 1280, - Height: 800, - }) - if err != nil { - return nil, fmt.Errorf("设置视口失败: %v", err) - } - - // 导航到搜索页面 - err = page.Context(ctx).Navigate(searchURL) - if err != nil { - return nil, fmt.Errorf("导航到搜索页面失败: %v", err) - } - - // 等待搜索结果加载完成 - err = page.WaitLoad() - if err != nil { - return nil, fmt.Errorf("等待页面加载完成失败: %v", err) - } - - // 分析当前页面的搜索结果 - for i := 0; i < maxPages; i++ { - if i > 0 { - // 点击下一页按钮 - nextPage, err := page.Element("a.n") - if err != nil || nextPage == nil { - break // 没有下一页 - } - - err = nextPage.Click(proto.InputMouseButtonLeft, 1) - if err != nil { - break // 点击下一页失败 - } - - // 等待新页面加载 - err = page.WaitLoad() - if err != nil { - break - } - } - - // 提取搜索结果 - resultElements, err := page.Elements(".result, .c-container") - if err != nil || resultElements == nil { - continue - } - - for _, result := range resultElements { - // 获取标题 - titleElement, err := result.Element("h3, .t") - if err != nil || titleElement == nil { - continue - } - - title, err := titleElement.Text() - if err != nil { - continue - } - - // 获取 URL - linkElement, err := titleElement.Element("a") - if err != nil || linkElement == nil { - continue - } - - href, err := linkElement.Attribute("href") - if err != nil || href == nil { - continue - } - - // 获取内容摘要 - 尝试多个可能的选择器 - var contentElement *rod.Element - var content string - - // 尝试多个可能的选择器来适应不同版本的百度搜索结果 - selectors := []string{".content-right_8Zs40", ".c-abstract", ".content_LJ0WN", ".content"} - for _, selector := range selectors { - contentElement, err = result.Element(selector) - if err == nil && contentElement != nil { - content, _ = contentElement.Text() - if content != "" { - break - } - } - } - - // 如果所有选择器都失败,尝试直接从结果块中提取文本 - if content == "" { - // 获取结果元素的所有文本 - fullText, err := result.Text() - if err == nil && fullText != "" { - // 简单处理:从全文中移除标题,剩下的可能是摘要 - fullText = strings.Replace(fullText, title, "", 1) - // 清理文本 - content = strings.TrimSpace(fullText) - // 限制内容长度 - if len(content) > 200 { - content = content[:200] + "..." - } - } - } - - // 添加到结果集 - results = append(results, SearchResult{ - Title: title, - URL: *href, - Content: content, - }) - - // 限制结果数量,每页最多 10 条 - if len(results) >= 10*maxPages { - break - } - } - } - - // 获取真实 URL(百度搜索结果中的 URL 是短链接,需要跳转获取真实 URL) - for i, result := range results { - realURL, err := s.getRedirectURL(result.URL) - if err == nil && realURL != "" { - results[i].URL = realURL - } - } - - return results, nil -} - -// 获取真实 URL -func (s *Service) getRedirectURL(shortURL string) (string, error) { - // 创建页面 - page, err := s.browser.Page(proto.TargetCreateTarget{URL: ""}) - if err != nil { - return shortURL, err // 返回原始URL - } - defer func() { - _ = page.Close() - }() - - // 导航到短链接 - err = page.Navigate(shortURL) - if err != nil { - return shortURL, err // 返回原始URL - } - - // 等待重定向完成 - time.Sleep(2 * time.Second) - - // 获取当前 URL - info, err := page.Info() - if err != nil { - return shortURL, err // 返回原始URL - } - - return info.URL, nil -} - -// Close 关闭浏览器 -func (s *Service) Close() error { - if s.browser != nil { - err := s.browser.Close() - s.browser = nil - return err - } - return nil -} - -// SearchWeb 封装的搜索方法 -func SearchWeb(keyword string, maxPages int) (string, error) { - // 添加panic恢复机制 - defer func() { - if r := recover(); r != nil { - log := logger.GetLogger() - log.Errorf("爬虫服务崩溃: %v", r) - } - }() - - service, err := NewService() - if err != nil { - return "", fmt.Errorf("创建爬虫服务失败: %v", err) - } - defer service.Close() - - // 设置超时上下文 - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - - // 使用goroutine和通道来处理超时 - resultChan := make(chan []SearchResult, 1) - errChan := make(chan error, 1) - - go func() { - results, err := service.WebSearch(keyword, maxPages) - if err != nil { - errChan <- err - return - } - resultChan <- results - }() - - // 等待结果或超时 - select { - case <-ctx.Done(): - return "", fmt.Errorf("搜索超时: %v", ctx.Err()) - case err := <-errChan: - return "", fmt.Errorf("搜索失败: %v", err) - case results := <-resultChan: - if len(results) == 0 { - return "未找到关于 \"" + keyword + "\" 的相关搜索结果", nil - } - - // 格式化结果 - var builder strings.Builder - builder.WriteString(fmt.Sprintf("为您找到关于 \"%s\" 的 %d 条搜索结果:\n\n", keyword, len(results))) - - for i, result := range results { - // // 尝试打开链接获取实际内容 - // page := service.browser.MustPage() - // defer page.MustClose() - - // // 设置页面超时 - // pageCtx, pageCancel := context.WithTimeout(context.Background(), 10*time.Second) - // defer pageCancel() - - // // 导航到目标页面 - // err := page.Context(pageCtx).Navigate(result.URL) - // if err == nil { - // // 等待页面加载 - // _ = page.WaitLoad() - - // // 获取页面标题 - // title, err := page.Eval("() => document.title") - // if err == nil && title.Value.String() != "" { - // result.Title = title.Value.String() - // } - - // // 获取页面主要内容 - // if content, err := page.Element("body"); err == nil { - // if text, err := content.Text(); err == nil { - // // 清理并截取内容 - // text = strings.TrimSpace(text) - // if len(text) > 200 { - // text = text[:200] + "..." - // } - // result.Prompt = text - // } - // } - // } - - builder.WriteString(fmt.Sprintf("%d. **%s**\n", i+1, result.Title)) - builder.WriteString(fmt.Sprintf(" 链接: %s\n", result.URL)) - if result.Content != "" { - builder.WriteString(fmt.Sprintf(" 摘要: %s\n", result.Content)) - } - builder.WriteString("\n") - } - - return builder.String(), nil - } -} diff --git a/api/service/dalle/service.go b/api/service/dalle/service.go index 7bad5115..8dabfa70 100644 --- a/api/service/dalle/service.go +++ b/api/service/dalle/service.go @@ -16,6 +16,7 @@ import ( "geekai/store" "geekai/store/model" "geekai/utils" + "strings" "time" "github.com/go-redis/redis/v8" @@ -94,12 +95,14 @@ func (s *Service) Run() { } type imgReq struct { - Model string `json:"model"` - Prompt string `json:"prompt"` - N int `json:"n,omitempty"` - Size string `json:"size,omitempty"` - Quality string `json:"quality,omitempty"` - Style string `json:"style,omitempty"` + Model string `json:"model"` + Image []string `json:"image,omitempty"` + Prompt string `json:"prompt"` + N int `json:"n,omitempty"` + Size string `json:"size,omitempty"` + Quality string `json:"quality,omitempty"` + Style string `json:"style,omitempty"` + ResponseFormat string `json:"response_format,omitempty"` } type imgRes struct { @@ -122,15 +125,6 @@ type ErrRes struct { func (s *Service) Image(task types.DallTask, sync bool) (string, error) { logger.Debugf("绘画参数:%+v", task) - prompt := task.Prompt - // translate prompt - if utils.HasChinese(prompt) { - content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, prompt), task.TranslateModelId) - if err == nil { - prompt = content - logger.Debugf("重写后提示词:%s", prompt) - } - } var chatModel model.ChatModel if task.ModelId > 0 { @@ -160,12 +154,17 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { apiURL := fmt.Sprintf("%s/v1/images/generations", apiKey.ApiURL) reqBody := imgReq{ Model: chatModel.Value, - Prompt: prompt, + Prompt: task.Prompt, N: 1, Size: task.Size, Style: task.Style, Quality: task.Quality, } + // 图片编辑 + if len(task.Image) > 0 { + reqBody.Prompt = fmt.Sprintf("%s, %s", strings.Join(task.Image, " "), task.Prompt) + } + logger.Infof("Channel:%s, API KEY:%s, BODY: %+v", apiURL, apiKey.Value, reqBody) r, err := s.httpClient.R().SetHeader("Body-Type", "application/json"). SetHeader("Authorization", "Bearer "+apiKey.Value). @@ -188,7 +187,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { var imgURL string var data = map[string]interface{}{ "progress": 100, - "prompt": prompt, + "prompt": task.Prompt, } // 如果返回的是base64,则需要上传到oss if res.Data[0].B64Json != "" { @@ -210,11 +209,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { var content string if sync { - imgURL, err := s.downloadImage(task.Id, res.Data[0].Url) - if err != nil { - return "", fmt.Errorf("error with download image: %v", err) - } - content = fmt.Sprintf("```\n%s\n```\n下面是我为你创作的图片:\n\n![](%s)\n", prompt, imgURL) + content = fmt.Sprintf("```\n%s\n```\n下面是我为你创作的图片:\n\n![](%s)\n", task.Prompt, imgURL) } return content, nil diff --git a/api/service/jimeng/client.go b/api/service/jimeng/client.go index 24fa0126..87864488 100644 --- a/api/service/jimeng/client.go +++ b/api/service/jimeng/client.go @@ -3,8 +3,10 @@ package jimeng import ( "encoding/json" "fmt" + "geekai/core/types" "net/http" "net/url" + "strings" "github.com/volcengine/volc-sdk-golang/base" "github.com/volcengine/volc-sdk-golang/service/visual" @@ -13,14 +15,22 @@ import ( // Client 即梦API客户端 type Client struct { visual *visual.Visual + config types.JimengConfig } // NewClient 创建即梦API客户端 -func NewClient(accessKey, secretKey string) *Client { +func NewClient(sysConfig *types.SystemConfig) *Client { + + client := &Client{} + client.UpdateConfig(sysConfig.Jimeng) + return client +} + +func (c *Client) UpdateConfig(config types.JimengConfig) error { // 使用官方SDK的visual实例 visualInstance := visual.NewInstance() - visualInstance.Client.SetAccessKey(accessKey) - visualInstance.Client.SetSecretKey(secretKey) + visualInstance.Client.SetAccessKey(config.AccessKey) + visualInstance.Client.SetSecretKey(config.SecretKey) // 添加即梦AI专有的API配置 jimengApis := map[string]*base.ApiInfo{ @@ -55,9 +65,32 @@ func NewClient(accessKey, secretKey string) *Client { visualInstance.Client.ApiInfoList[name] = info } - return &Client{ - visual: visualInstance, + c.config = config + c.visual = visualInstance + + return c.testConnection() +} + +// testConnection 测试即梦AI连接 +func (c *Client) testConnection() error { + + // 使用一个简单的查询任务来测试连接 + testReq := &QueryTaskRequest{ + ReqKey: "test_connection", + TaskId: "test_task_id_12345", } + + _, err := c.QueryTask(testReq) + // 即使任务不存在,只要不是认证错误就说明连接正常 + if err != nil { + // 检查是否是认证错误 + if strings.Contains(err.Error(), "InvalidAccessKey") { + return fmt.Errorf("认证失败,请检查AccessKey和SecretKey是否正确") + } + // 其他错误(如任务不存在)说明连接正常 + return nil + } + return nil } // SubmitTask 提交异步任务 diff --git a/api/service/jimeng/service.go b/api/service/jimeng/service.go index 99f94059..ca80c712 100644 --- a/api/service/jimeng/service.go +++ b/api/service/jimeng/service.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "strconv" - "strings" "time" "gorm.io/gorm" @@ -16,8 +15,6 @@ import ( "geekai/store/model" "geekai/utils" - "geekai/core/types" - "github.com/go-redis/redis/v8" ) @@ -36,17 +33,8 @@ type Service struct { } // NewService 创建即梦服务 -func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager) *Service { +func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager, client *Client) *Service { taskQueue := store.NewRedisQueue("JimengTaskQueue", redisCli) - // 从数据库加载配置 - var config model.Config - db.Where("name = ?", "Jimeng").First(&config) - var jimengConfig types.JimengConfig - if config.Id > 0 { - _ = utils.JsonDecode(config.Value, &jimengConfig) - } - client := NewClient(jimengConfig.AccessKey, jimengConfig.SecretKey) - ctx, cancel := context.WithCancel(context.Background()) return &Service{ db: db, @@ -378,7 +366,7 @@ func (s *Service) pollTaskStatus() { for _, job := range jobs { // 任务超时处理 - if job.UpdatedAt.Before(time.Now().Add(-5 * time.Minute)) { + if job.UpdatedAt.Before(time.Now().Add(-10 * time.Minute)) { s.handleTaskError(job.Id, "task timeout") continue } @@ -391,7 +379,7 @@ func (s *Service) pollTaskStatus() { }) if err != nil { - logger.Errorf("query jimeng task status failed: %v", err) + s.handleTaskError(job.Id, fmt.Sprintf("query task failed: %s", err.Error())) continue } @@ -446,9 +434,7 @@ func (s *Service) pollTaskStatus() { s.handleTaskError(job.Id, "task not found") case model.JMTaskStatusExpired: - // 任务过期 - s.handleTaskError(job.Id, "task expired") - + continue default: logger.Warnf("unknown task status: %s", resp.Data.Status) } @@ -524,77 +510,3 @@ func (s *Service) GetJob(jobId uint) (*model.JimengJob, error) { } return &job, nil } - -// testConnection 测试即梦AI连接 -func (s *Service) testConnection(accessKey, secretKey string) error { - testClient := NewClient(accessKey, secretKey) - - // 使用一个简单的查询任务来测试连接 - testReq := &QueryTaskRequest{ - ReqKey: "test_connection", - TaskId: "test_task_id_12345", - } - - _, err := testClient.QueryTask(testReq) - // 即使任务不存在,只要不是认证错误就说明连接正常 - if err != nil { - // 检查是否是认证错误 - if strings.Contains(err.Error(), "InvalidAccessKey") { - return fmt.Errorf("认证失败,请检查AccessKey和SecretKey是否正确") - } - // 其他错误(如任务不存在)说明连接正常 - return nil - } - return nil -} - -// UpdateClientConfig 更新客户端配置 -func (s *Service) UpdateClientConfig(accessKey, secretKey string) error { - // 创建新的客户端 - newClient := NewClient(accessKey, secretKey) - - // 测试新客户端是否可用 - err := s.testConnection(accessKey, secretKey) - if err != nil { - return err - } - - // 更新客户端 - s.client = newClient - return nil -} - -var defaultPower = types.JimengPower{ - TextToImage: 20, - ImageToImage: 20, - ImageEdit: 20, - ImageEffects: 20, - TextToVideo: 300, - ImageToVideo: 300, -} - -// GetConfig 获取即梦AI配置 -func (s *Service) GetConfig() *types.JimengConfig { - var config model.Config - err := s.db.Where("name", "jimeng").First(&config).Error - if err != nil { - // 如果配置不存在,返回默认配置 - return &types.JimengConfig{ - AccessKey: "", - SecretKey: "", - Power: defaultPower, - } - } - - var jimengConfig types.JimengConfig - err = utils.JsonDecode(config.Value, &jimengConfig) - if err != nil { - return &types.JimengConfig{ - AccessKey: "", - SecretKey: "", - Power: defaultPower, - } - } - - return &jimengConfig -} diff --git a/api/service/license_service.go b/api/service/license_service.go index 3f817096..e979bfce 100644 --- a/api/service/license_service.go +++ b/api/service/license_service.go @@ -8,30 +8,37 @@ package service // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "errors" "fmt" - "geekai/core" "geekai/core/types" - "geekai/store" + "geekai/store/model" + "geekai/utils" + "strings" "time" "github.com/imroc/req/v3" + "github.com/shirou/gopsutil/host" + "gorm.io/gorm" ) type LicenseService struct { - config types.ApiConfig - levelDB *store.LevelDB license *types.License urlWhiteList []string machineId string + db *gorm.DB } -func NewLicenseService(server *core.AppServer, levelDB *store.LevelDB) *LicenseService { - var license types.License +func NewLicenseService(sysConfig *types.SystemConfig, db *gorm.DB) *LicenseService { + var machineId string + info, err := host.Info() + if err == nil { + machineId = info.HostID + } + logger.Infof("License: %+v", sysConfig.License) return &LicenseService{ - config: server.Config.ApiConfig, - levelDB: levelDB, - license: &license, - machineId: "", + license: &sysConfig.License, + machineId: machineId, + db: db, } } @@ -46,15 +53,15 @@ type License struct { } // ActiveLicense 激活 License -func (s *LicenseService) ActiveLicense(license string, machineId string) error { +func (s *LicenseService) ActiveLicense(license string) error { var res struct { Code types.BizCode `json:"code"` Message string `json:"message"` Data License `json:"data"` } - apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/active") + apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/active") response, err := req.C().R(). - SetBody(map[string]string{"license": license, "machine_id": machineId}). + SetBody(map[string]string{"license": license, "machine_id": s.machineId}). SetSuccessResult(&res).Post(apiURL) if err != nil { return fmt.Errorf("发送激活请求失败: %v", err) @@ -68,17 +75,24 @@ func (s *LicenseService) ActiveLicense(license string, machineId string) error { return fmt.Errorf("激活失败:%v", res.Message) } + if res.Data.ExpiredAt > 0 && res.Data.ExpiredAt < time.Now().Unix() { + return fmt.Errorf("License 已过期") + } + s.license = &types.License{ Key: license, - MachineId: machineId, + MachineId: s.machineId, Configs: res.Data.Configs, ExpiredAt: res.Data.ExpiredAt, IsActive: true, } - err = s.levelDB.Put(types.LicenseKey, s.license) + + // 保存 License 到数据库 + err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error if err != nil { - return fmt.Errorf("保存许可证书失败:%v", err) + return fmt.Errorf("保存 License 到数据库失败: %v", err) } + return nil } @@ -96,6 +110,11 @@ func (s *LicenseService) SyncLicense() { s.license.IsActive = false } else { s.license = license + // 保存 License 到数据库 + err = s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).UpdateColumn("value", utils.JsonEncode(s.license)).Error + if err != nil { + logger.Errorf("保存 License 到数据库失败: %v", err) + } } urls, err := s.fetchUrlWhiteList() @@ -109,33 +128,30 @@ func (s *LicenseService) SyncLicense() { } func (s *LicenseService) fetchLicense() (*types.License, error) { - //var res struct { - // Code types.BizCode `json:"code"` - // Message string `json:"message"` - // Data License `json:"data"` - //} - //apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/check") - //response, err := req.C().R(). - // SetBody(map[string]string{"license": s.license.Key, "machine_id": s.machineId}). - // SetSuccessResult(&res).Post(apiURL) - //if err != nil { - // return nil, fmt.Errorf("发送激活请求失败: %v", err) - //} - //if response.IsErrorState() { - // return nil, fmt.Errorf("激活失败:%v", response.Status) - //} - //if res.Code != types.Success { - // return nil, fmt.Errorf("激活失败:%v", res.Message) - //} + var res struct { + Code types.BizCode `json:"code"` + Message string `json:"message"` + Data License `json:"data"` + } + apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/check") + response, err := req.C().R(). + SetBody(map[string]string{"license": s.license.Key, "machine_id": s.machineId}). + SetSuccessResult(&res).Post(apiURL) + if err != nil { + return nil, fmt.Errorf("License 同步失败: %v", err) + } + if response.IsErrorState() { + return nil, fmt.Errorf("License 同步失败:%v", response.Status) + } + if res.Code != types.Success { + return nil, fmt.Errorf("License 同步失败:%v", res.Message) + } return &types.License{ - Key: "abc", - MachineId: "abc", - Configs: types.LicenseConfig{ - UserNum: 10000, - DeCopy: false, - }, - ExpiredAt: 0, + Key: res.Data.License, + MachineId: res.Data.MachineId, + Configs: res.Data.Configs, + ExpiredAt: res.Data.ExpiredAt, IsActive: true, }, nil } @@ -146,7 +162,7 @@ func (s *LicenseService) fetchUrlWhiteList() ([]string, error) { Message string `json:"message"` Data []string `json:"data"` } - apiURL := fmt.Sprintf("%s/%s", s.config.ApiURL, "api/license/urls") + apiURL := fmt.Sprintf("%s/%s", types.GeekAPIURL, "api/license/urls") response, err := req.C().R().SetSuccessResult(&res).Get(apiURL) if err != nil { return nil, fmt.Errorf("发送请求失败: %v", err) @@ -163,35 +179,46 @@ func (s *LicenseService) fetchUrlWhiteList() ([]string, error) { // GetLicense 获取许可信息 func (s *LicenseService) GetLicense() *types.License { + if s.license == nil { + var config model.Config + s.db.Model(&model.Config{}).Where("name = ?", types.ConfigKeyLicense).First(&config) + if config.Value != "" { + utils.JsonDecode(config.Value, &s.license) + } + } return s.license } +func (s *LicenseService) SetLicense(licenseKey string) { + s.license.Key = licenseKey + +} + // IsValidApiURL 判断是否合法的中转 URL func (s *LicenseService) IsValidApiURL(uri string) error { // 获得许可授权的直接放行 - return nil - //if s.license.IsActive { - // if s.license.MachineId != s.machineId { - // return errors.New("系统使用了盗版的许可证书") - // } - // - // if time.Now().Unix() > s.license.ExpiredAt { - // return errors.New("系统许可证书已经过期") - // } - // return nil - //} - // - //if len(s.urlWhiteList) == 0 { - // urls, err := s.fetchUrlWhiteList() - // if err == nil { - // s.urlWhiteList = urls - // } - //} - // - //for _, v := range s.urlWhiteList { - // if strings.HasPrefix(uri, v) { - // return nil - // } - //} - //return fmt.Errorf("当前 API 地址 %s 不在白名单列表当中。", uri) + if s.license.IsActive { + if s.license.MachineId != s.machineId { + return errors.New("系统使用了盗版的许可证书") + } + + if time.Now().Unix() > s.license.ExpiredAt { + return errors.New("系统许可证书已经过期") + } + return nil + } + + if len(s.urlWhiteList) == 0 { + urls, err := s.fetchUrlWhiteList() + if err == nil { + s.urlWhiteList = urls + } + } + + for _, v := range s.urlWhiteList { + if strings.HasPrefix(uri, v) { + return nil + } + } + return fmt.Errorf("当前 API 地址 %s 不在白名单列表当中。", uri) } diff --git a/api/service/migration_service.go b/api/service/migration_service.go index 89cf635b..4db9ea1e 100644 --- a/api/service/migration_service.go +++ b/api/service/migration_service.go @@ -1,52 +1,342 @@ package service -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// * Copyright 2023 The Geek-AI Authors. All rights reserved. -// * Use of this source code is governed by a Apache-2.0 license -// * that can be found in the LICENSE file. -// * @Author yangjian102621@163.com -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Copyright 2023 The Geek-AI Authors. All rights reserved. +// Use of this source code is governed by a Apache-2.0 license +// that can be found in the LICENSE file. +// @Author yangjian102621@163.com +// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "context" + "encoding/json" + "fmt" + "geekai/core/types" + "geekai/store" "geekai/store/model" + "strings" + "github.com/go-redis/redis/v8" "gorm.io/gorm" ) +const ( + // 迁移状态Redis key + MigrationStatusKey = "config_migration:status" + // 迁移完成标志 + MigrationCompleted = "completed" +) + +// MigrationService 配置迁移服务 type MigrationService struct { - db *gorm.DB + db *gorm.DB + redisClient *redis.Client + appConfig *types.AppConfig + levelDB *store.LevelDB + licenseService *LicenseService } -func NewMigrationService(db *gorm.DB) *MigrationService { - return &MigrationService{db: db} +func NewMigrationService(db *gorm.DB, redisClient *redis.Client, appConfig *types.AppConfig, levelDB *store.LevelDB, licenseService *LicenseService) *MigrationService { + return &MigrationService{ + db: db, + redisClient: redisClient, + appConfig: appConfig, + levelDB: levelDB, + licenseService: licenseService, + } } -func (s *MigrationService) Migrate() error { - err := s.db.AutoMigrate( - &model.AdminUser{}, - &model.ApiKey{}, - &model.AppType{}, - &model.ChatItem{}, - &model.ChatMessage{}, - &model.ChatModel{}, - &model.ChatRole{}, - &model.Config{}, - &model.DallJob{}, - &model.File{}, - &model.Function{}, - &model.InviteCode{}, - &model.InviteLog{}, - &model.Menu{}, - &model.MidJourneyJob{}, - &model.Order{}, - &model.PowerLog{}, - &model.Product{}, - &model.Redeem{}, - &model.SdJob{}, - &model.SunoJob{}, - &model.User{}, - &model.UserLoginLog{}, - &model.VideoJob{}, - ) - return err +func (s *MigrationService) StartMigrate() { + go func() { + s.MigrateConfig(s.appConfig) + s.TableMigration() + s.MigrateLicense() + }() +} + +// 迁移 License +func (s *MigrationService) MigrateLicense() { + key := "migrate:license" + if s.redisClient.Get(context.Background(), key).Val() == "1" { + logger.Info("License 已迁移,跳过迁移") + return + } + + logger.Info("开始迁移 License...") + var license types.License + err := s.levelDB.Get(types.LicenseKey, &license) + if err != nil { + license = types.License{ + Key: "", + MachineId: "", + Configs: types.LicenseConfig{UserNum: 0, DeCopy: false}, + ExpiredAt: 0, + IsActive: false, + } + } + logger.Infof("迁移 License: %+v", license) + if err := s.saveConfig(types.ConfigKeyLicense, license); err != nil { + logger.Errorf("迁移 License 失败: %v", err) + return + } + s.licenseService.SetLicense(license.Key) + logger.Info("迁移 License 完成") + s.redisClient.Set(context.Background(), key, "1", 0) +} + +// 迁移配置内容 +func (s *MigrationService) MigrateConfigContent() error { + // 用户协议 + if err := s.saveConfig(types.ConfigKeyPrivacy, map[string]string{ + "content": "用户协议内容", + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + // 隐私政策 + if err := s.saveConfig(types.ConfigKeyAgreement, map[string]string{ + "content": "隐私政策内容", + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + // 思维导图 + if err := s.saveConfig(types.ConfigKeyMarkMap, map[string]string{ + "content": `# GeekAI 演示站 + +- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。 +- 基于 Websocket 实现,完美的打字机体验。 +- 内置了各种预训练好的角色应用,轻松满足你的各种聊天和应用需求。 +- 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。 +- 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。 +- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。 +- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。 +- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件。`, + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + + // 微信登录配置 + if err := s.saveConfig(types.ConfigKeyWxLogin, map[string]string{ + "api_key": "", + "notify_url": "", + "enabled": "false", + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + + // 验证码配置 + if err := s.saveConfig(types.ConfigKeyCaptcha, map[string]string{ + "api_key": "", + "type": "dot", + "enabled": "false", + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + + // 文本审核 + if err := s.saveConfig(types.ConfigKeyModeration, map[string]any{ + "enable": "false", + "active": "gitee", + "enable_guide": "false", + "guide_prompt": "", + "gitee": map[string]string{ + "api_key": "", + "model": "Security-semantic-filtering", + }, + "baidu": map[string]string{ + "access_key": "", + "secret_key": "", + }, + "tencent": map[string]string{ + "access_key": "", + "secret_key": "", + }, + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + + return nil +} + +// 数据表迁移 +func (s *MigrationService) TableMigration() { + // 新数据表 + s.db.AutoMigrate(&model.Moderation{}) + + // 订单字段整理 + if s.db.Migrator().HasColumn(&model.Order{}, "pay_type") { + s.db.Migrator().RenameColumn(&model.Order{}, "pay_type", "channel") + } + if !s.db.Migrator().HasColumn(&model.Order{}, "checked") { + s.db.Migrator().AddColumn(&model.Order{}, "checked") + } + + // 重命名 config 表字段 + if s.db.Migrator().HasColumn(&model.Config{}, "config_json") { + s.db.Migrator().RenameColumn(&model.Config{}, "config_json", "value") + } + if s.db.Migrator().HasColumn(&model.Config{}, "marker") { + s.db.Migrator().RenameColumn(&model.Config{}, "marker", "name") + } + if s.db.Migrator().HasIndex(&model.Config{}, "idx_chatgpt_configs_key") { + s.db.Migrator().DropIndex(&model.Config{}, "idx_chatgpt_configs_key") + } + if s.db.Migrator().HasIndex(&model.Config{}, "marker") { + s.db.Migrator().DropIndex(&model.Config{}, "marker") + } + + // 手动删除字段 + if s.db.Migrator().HasColumn(&model.Order{}, "deleted_at") { + s.db.Migrator().DropColumn(&model.Order{}, "deleted_at") + } + if s.db.Migrator().HasColumn(&model.ChatItem{}, "deleted_at") { + s.db.Migrator().DropColumn(&model.ChatItem{}, "deleted_at") + } + if s.db.Migrator().HasColumn(&model.ChatMessage{}, "deleted_at") { + s.db.Migrator().DropColumn(&model.ChatMessage{}, "deleted_at") + } + if s.db.Migrator().HasColumn(&model.User{}, "chat_config") { + s.db.Migrator().DropColumn(&model.User{}, "chat_config") + } + if s.db.Migrator().HasColumn(&model.ChatModel{}, "category") { + s.db.Migrator().DropColumn(&model.ChatModel{}, "category") + } + if s.db.Migrator().HasColumn(&model.ChatModel{}, "description") { + s.db.Migrator().DropColumn(&model.ChatModel{}, "description") + } + if s.db.Migrator().HasColumn(&model.Product{}, "discount") { + s.db.Migrator().DropColumn(&model.Product{}, "discount") + } + if s.db.Migrator().HasColumn(&model.Product{}, "days") { + s.db.Migrator().DropColumn(&model.Product{}, "days") + } + if s.db.Migrator().HasColumn(&model.Product{}, "app_url") { + s.db.Migrator().DropColumn(&model.Product{}, "app_url") + } + if s.db.Migrator().HasColumn(&model.Product{}, "url") { + s.db.Migrator().DropColumn(&model.Product{}, "url") + } +} + +// 迁移配置数据 +func (s *MigrationService) MigrateConfig(config *types.AppConfig) error { + + logger.Info("开始迁移配置到数据库...") + + // 迁移支付配置 + if err := s.migratePaymentConfig(config); err != nil { + logger.Errorf("迁移支付配置失败: %v", err) + return err + } + + // 迁移存储配置 + if err := s.migrateStorageConfig(config); err != nil { + logger.Errorf("迁移存储配置失败: %v", err) + return err + } + + // 迁移通信配置 + if err := s.migrateCommunicationConfig(config); err != nil { + logger.Errorf("迁移通信配置失败: %v", err) + return err + } + + // 迁移配置内容 + if err := s.MigrateConfigContent(); err != nil { + logger.Errorf("迁移配置内容失败: %v", err) + return err + } + + logger.Info("配置迁移完成") + return nil +} + +// 迁移支付配置 +func (s *MigrationService) migratePaymentConfig(config *types.AppConfig) error { + + paymentConfig := types.PaymentConfig{ + Alipay: config.AlipayConfig, + Epay: config.GeekPayConfig, + WxPay: config.WechatPayConfig, + } + if err := s.saveConfig(types.ConfigKeyPayment, paymentConfig); err != nil { + return err + } + + return nil +} + +// 迁移存储配置 +func (s *MigrationService) migrateStorageConfig(config *types.AppConfig) error { + + ossConfig := types.OSSConfig{ + Active: config.OSS.Active, + Local: config.OSS.Local, + Minio: config.OSS.Minio, + QiNiu: config.OSS.QiNiu, + AliYun: config.OSS.AliYun, + } + return s.saveConfig(types.ConfigKeyOss, ossConfig) +} + +// 迁移通信配置 +func (s *MigrationService) migrateCommunicationConfig(config *types.AppConfig) error { + // SMTP配置 + smtpConfig := map[string]any{ + "use_tls": config.SmtpConfig.UseTls, + "host": config.SmtpConfig.Host, + "port": config.SmtpConfig.Port, + "app_name": config.SmtpConfig.AppName, + "from": config.SmtpConfig.From, + "password": config.SmtpConfig.Password, + } + if err := s.saveConfig(types.ConfigKeySmtp, smtpConfig); err != nil { + return err + } + + // 短信配置 + smsConfig := map[string]any{ + "active": strings.ToLower(config.SMS.Active), + "aliyun": map[string]any{ + "access_key": config.SMS.Ali.AccessKey, + "access_secret": config.SMS.Ali.AccessSecret, + "sign": config.SMS.Ali.Sign, + "code_temp_id": config.SMS.Ali.CodeTempId, + }, + "bao": map[string]any{ + "username": config.SMS.Bao.Username, + "password": config.SMS.Bao.Password, + "sign": config.SMS.Bao.Sign, + "code_template": config.SMS.Bao.CodeTemplate, + }, + } + return s.saveConfig(types.ConfigKeySms, smsConfig) +} + +// 保存配置到数据库 +func (s *MigrationService) saveConfig(key string, config any) error { + // 检查是否已存在 + var existingConfig model.Config + if err := s.db.Where("name", key).First(&existingConfig).Error; err == nil { + // 配置已存在,跳过 + logger.Infof("配置 %s 已存在,跳过迁移", key) + return nil + } + + // 序列化配置 + configJSON, err := json.Marshal(config) + if err != nil { + return err + } + + // 保存到数据库 + newConfig := model.Config{ + Name: key, + Value: string(configJSON), + } + if err := s.db.Create(&newConfig).Error; err != nil { + return err + } + + logger.Infof("成功迁移配置 %s", key) + return nil } diff --git a/api/service/mj/service.go b/api/service/mj/service.go index d710a493..e7a281fb 100644 --- a/api/service/mj/service.go +++ b/api/service/mj/service.go @@ -67,25 +67,6 @@ func (s *Service) Run() { continue } - // translate prompt - if utils.HasChinese(task.Prompt) { - content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt), task.TranslateModelId) - if err == nil { - task.Prompt = content - } else { - logger.Warnf("error with translate prompt: %v", err) - } - } - // translate negative prompt - if task.NegPrompt != "" && utils.HasChinese(task.NegPrompt) { - content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.NegPrompt), task.TranslateModelId) - if err == nil { - task.NegPrompt = content - } else { - logger.Warnf("error with translate prompt: %v", err) - } - } - // use fast mode as default if task.Mode == "" { task.Mode = "fast" diff --git a/api/service/moderation/baidu_moderation.go b/api/service/moderation/baidu_moderation.go new file mode 100644 index 00000000..b213d469 --- /dev/null +++ b/api/service/moderation/baidu_moderation.go @@ -0,0 +1,33 @@ +package moderation + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "errors" + "geekai/core/types" +) + +type BaiduAIModeration struct { + config types.ModerationBaiduConfig +} + +func NewBaiduAIModeration(sysConfig *types.SystemConfig) *BaiduAIModeration { + return &BaiduAIModeration{ + config: sysConfig.Moderation.Baidu, + } +} + +func (s *BaiduAIModeration) UpdateConfig(config types.ModerationBaiduConfig) { + s.config = config +} + +func (s *BaiduAIModeration) Moderate(text string) (types.ModerationResult, error) { + return types.ModerationResult{}, errors.New("not implemented") +} + +var _ Service = (*BaiduAIModeration)(nil) diff --git a/api/service/moderation/gitee_moderation.go b/api/service/moderation/gitee_moderation.go new file mode 100644 index 00000000..8e7ed14c --- /dev/null +++ b/api/service/moderation/gitee_moderation.go @@ -0,0 +1,58 @@ +package moderation + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "errors" + "geekai/core/types" + + "github.com/imroc/req/v3" +) + +type GiteeAIModeration struct { + config types.ModerationGiteeConfig + apiURL string +} + +func NewGiteeAIModeration(sysConfig *types.SystemConfig) *GiteeAIModeration { + return &GiteeAIModeration{ + config: sysConfig.Moderation.Gitee, + apiURL: "https://ai.gitee.com/v1/moderations", + } +} + +func (s *GiteeAIModeration) UpdateConfig(config types.ModerationGiteeConfig) { + s.config = config +} + +type GiteeAIModerationResult struct { + ID string `json:"id"` + Model string `json:"model"` + Results []types.ModerationResult `json:"results"` +} + +func (s *GiteeAIModeration) Moderate(text string) (types.ModerationResult, error) { + + body := map[string]any{ + "input": text, + "model": s.config.Model, + } + var res GiteeAIModerationResult + r, err := req.C().R().SetHeader("Authorization", "Bearer "+s.config.ApiKey).SetBody(body).SetSuccessResult(&res).Post(s.apiURL) + if err != nil { + return types.ModerationResult{}, err + } + + if r.IsErrorState() { + return types.ModerationResult{}, errors.New(r.String()) + } + + return res.Results[0], nil +} + +var _ Service = (*GiteeAIModeration)(nil) diff --git a/api/service/moderation/moderation_manager.go b/api/service/moderation/moderation_manager.go new file mode 100644 index 00000000..25125a93 --- /dev/null +++ b/api/service/moderation/moderation_manager.go @@ -0,0 +1,58 @@ +package moderation + +import ( + "geekai/core/types" + + logger2 "geekai/logger" +) + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +var logger = logger2.GetLogger() + +type Service interface { + Moderate(text string) (types.ModerationResult, error) +} + +type ServiceManager struct { + gitee *GiteeAIModeration + baidu *BaiduAIModeration + tencent *TencentAIModeration + active string +} + +func NewServiceManager(gitee *GiteeAIModeration, baidu *BaiduAIModeration, tencent *TencentAIModeration) *ServiceManager { + return &ServiceManager{ + gitee: gitee, + baidu: baidu, + tencent: tencent, + } +} + +func (s *ServiceManager) GetService() Service { + switch s.active { + case types.ModerationBaidu: + return s.baidu + case types.ModerationTencent: + return s.tencent + default: + return s.gitee + } +} + +func (s *ServiceManager) UpdateConfig(config types.ModerationConfig) { + switch config.Active { + case types.ModerationGitee: + s.gitee.UpdateConfig(config.Gitee) + case types.ModerationBaidu: + s.baidu.UpdateConfig(config.Baidu) + case types.ModerationTencent: + s.tencent.UpdateConfig(config.Tencent) + } + s.active = config.Active +} diff --git a/api/service/moderation/tencent_moderation.go b/api/service/moderation/tencent_moderation.go new file mode 100644 index 00000000..75bee579 --- /dev/null +++ b/api/service/moderation/tencent_moderation.go @@ -0,0 +1,33 @@ +package moderation + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "errors" + "geekai/core/types" +) + +type TencentAIModeration struct { + config types.ModerationTencentConfig +} + +func NewTencentAIModeration(sysConfig *types.SystemConfig) *TencentAIModeration { + return &TencentAIModeration{ + config: sysConfig.Moderation.Tencent, + } +} + +func (s *TencentAIModeration) UpdateConfig(config types.ModerationTencentConfig) { + s.config = config +} + +func (s *TencentAIModeration) Moderate(text string) (types.ModerationResult, error) { + return types.ModerationResult{}, errors.New("not implemented") +} + +var _ Service = (*TencentAIModeration)(nil) diff --git a/api/service/oss/aliyun_oss.go b/api/service/oss/aliyun_oss.go index 271cdfff..d5acff2a 100644 --- a/api/service/oss/aliyun_oss.go +++ b/api/service/oss/aliyun_oss.go @@ -23,35 +23,35 @@ import ( ) type AliYunOss struct { - config *types.AliYunOssConfig + config types.AliYunOssConfig bucket *oss.Bucket proxyURL string } -func NewAliYunOss(appConfig *types.AppConfig) (*AliYunOss, error) { - config := &appConfig.OSS.AliYun - // 创建 OSS 客户端 +func NewAliYunOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) (*AliYunOss, error) { + s := &AliYunOss{ + proxyURL: appConfig.ProxyURL, + } + err := s.UpdateConfig(sysConfig.OSS.AliYun) + if err != nil { + logger.Warnf("阿里云OSS初始化失败: %v", err) + } + return s, nil + +} + +func (s *AliYunOss) UpdateConfig(config types.AliYunOssConfig) error { client, err := oss.New(config.Endpoint, config.AccessKey, config.AccessSecret) if err != nil { - return nil, err + return err } - - // 获取存储空间 bucket, err := client.Bucket(config.Bucket) if err != nil { - return nil, err + return err } - - if config.SubDir == "" { - config.SubDir = "gpt" - } - - return &AliYunOss{ - config: config, - bucket: bucket, - proxyURL: appConfig.ProxyURL, - }, nil - + s.bucket = bucket + s.config = config + return nil } func (s AliYunOss) PutFile(ctx *gin.Context, name string) (File, error) { @@ -68,7 +68,7 @@ func (s AliYunOss) PutFile(ctx *gin.Context, name string) (File, error) { defer src.Close() fileExt := filepath.Ext(file.Filename) - objectKey := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt) + objectKey := fmt.Sprintf("%d%s", time.Now().UnixMicro(), fileExt) // 上传文件 err = s.bucket.PutObject(objectKey, src) if err != nil { @@ -102,7 +102,7 @@ func (s AliYunOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string if ext == "" { ext = filepath.Ext(parse.Path) } - objectKey := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), ext) + objectKey := fmt.Sprintf("%d%s", time.Now().UnixMicro(), ext) // 上传文件字节数据 err = s.bucket.PutObject(objectKey, bytes.NewReader(fileData)) if err != nil { @@ -116,7 +116,7 @@ func (s AliYunOss) PutBase64(base64Img string) (string, error) { if err != nil { return "", fmt.Errorf("error decoding base64:%v", err) } - objectKey := fmt.Sprintf("%s/%d.png", s.config.SubDir, time.Now().UnixMicro()) + objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro()) // 上传文件字节数据 err = s.bucket.PutObject(objectKey, bytes.NewReader(imageData)) if err != nil { @@ -128,8 +128,7 @@ func (s AliYunOss) PutBase64(base64Img string) (string, error) { func (s AliYunOss) Delete(fileURL string) error { var objectKey string if strings.HasPrefix(fileURL, "http") { - filename := filepath.Base(fileURL) - objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename) + objectKey = filepath.Base(fileURL) } else { objectKey = fileURL } diff --git a/api/service/oss/localstorage.go b/api/service/oss/localstorage.go index 37d4f5ff..5b3bef82 100644 --- a/api/service/oss/localstorage.go +++ b/api/service/oss/localstorage.go @@ -21,17 +21,21 @@ import ( ) type LocalStorage struct { - config *types.LocalStorageConfig + config types.LocalStorageConfig proxyURL string } -func NewLocalStorage(config *types.AppConfig) LocalStorage { - return LocalStorage{ - config: &config.OSS.Local, - proxyURL: config.ProxyURL, +func NewLocalStorage(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *LocalStorage { + return &LocalStorage{ + config: sysConfig.OSS.Local, + proxyURL: appConfig.ProxyURL, } } +func (s *LocalStorage) UpdateConfig(config types.LocalStorageConfig) { + s.config = config +} + func (s LocalStorage) PutFile(ctx *gin.Context, name string) (File, error) { file, err := ctx.FormFile(name) if err != nil { diff --git a/api/service/oss/minio_oss.go b/api/service/oss/minio_oss.go index 530dd0e0..0961dd87 100644 --- a/api/service/oss/minio_oss.go +++ b/api/service/oss/minio_oss.go @@ -24,24 +24,32 @@ import ( ) type MiniOss struct { - config *types.MiniOssConfig + config types.MiniOssConfig client *minio.Client proxyURL string } -func NewMiniOss(appConfig *types.AppConfig) (MiniOss, error) { - config := &appConfig.OSS.Minio +func NewMiniOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) (*MiniOss, error) { + + s := &MiniOss{proxyURL: appConfig.ProxyURL} + err := s.UpdateConfig(sysConfig.OSS.Minio) + if err != nil { + logger.Warnf("MinioOSS初始化失败: %v", err) + } + return s, nil +} + +func (s *MiniOss) UpdateConfig(config types.MiniOssConfig) error { minioClient, err := minio.New(config.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(config.AccessKey, config.AccessSecret, ""), Secure: config.UseSSL, }) if err != nil { - return MiniOss{}, err + return err } - if config.SubDir == "" { - config.SubDir = "gpt" - } - return MiniOss{config: config, client: minioClient, proxyURL: appConfig.ProxyURL}, nil + s.config = config + s.client = minioClient + return nil } func (s MiniOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string, error) { @@ -62,7 +70,7 @@ func (s MiniOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string, if ext == "" { ext = filepath.Ext(parse.Path) } - filename := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), ext) + filename := fmt.Sprintf("%d%s", time.Now().UnixMicro(), ext) info, err := s.client.PutObject( context.Background(), s.config.Bucket, @@ -89,7 +97,7 @@ func (s MiniOss) PutFile(ctx *gin.Context, name string) (File, error) { defer fileReader.Close() fileExt := filepath.Ext(file.Filename) - filename := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt) + filename := fmt.Sprintf("%d%s", time.Now().UnixMicro(), fileExt) info, err := s.client.PutObject(ctx, s.config.Bucket, filename, fileReader, file.Size, minio.PutObjectOptions{ ContentType: file.Header.Get("Body-Type"), }) @@ -111,7 +119,7 @@ func (s MiniOss) PutBase64(base64Img string) (string, error) { if err != nil { return "", fmt.Errorf("error decoding base64:%v", err) } - objectKey := fmt.Sprintf("%s/%d.png", s.config.SubDir, time.Now().UnixMicro()) + objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro()) info, err := s.client.PutObject( context.Background(), s.config.Bucket, @@ -128,8 +136,7 @@ func (s MiniOss) PutBase64(base64Img string) (string, error) { func (s MiniOss) Delete(fileURL string) error { var objectKey string if strings.HasPrefix(fileURL, "http") { - filename := filepath.Base(fileURL) - objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename) + objectKey = filepath.Base(fileURL) } else { objectKey = fileURL } diff --git a/api/service/oss/qiniu_oss.go b/api/service/oss/qiniu_oss.go index 3913410e..88d917ae 100644 --- a/api/service/oss/qiniu_oss.go +++ b/api/service/oss/qiniu_oss.go @@ -24,18 +24,24 @@ import ( "github.com/qiniu/go-sdk/v7/storage" ) -type QinNiuOss struct { - config *types.QiNiuOssConfig +type QiNiuOss struct { + config types.QiNiuOssConfig mac *qbox.Mac putPolicy storage.PutPolicy uploader *storage.FormUploader - manager *storage.BucketManager + bucket *storage.BucketManager proxyURL string } -func NewQiNiuOss(appConfig *types.AppConfig) QinNiuOss { - config := &appConfig.OSS.QiNiu - // build storage uploader +func NewQiNiuOss(sysConfig *types.SystemConfig, appConfig *types.AppConfig) *QiNiuOss { + s := &QiNiuOss{ + proxyURL: appConfig.ProxyURL, + } + s.UpdateConfig(sysConfig.OSS.QiNiu) + return s +} + +func (s *QiNiuOss) UpdateConfig(config types.QiNiuOssConfig) { zone, ok := storage.GetRegionByID(storage.RegionID(config.Zone)) if !ok { zone = storage.ZoneHuanan @@ -47,20 +53,13 @@ func NewQiNiuOss(appConfig *types.AppConfig) QinNiuOss { putPolicy := storage.PutPolicy{ Scope: config.Bucket, } - if config.SubDir == "" { - config.SubDir = "gpt" - } - return QinNiuOss{ - config: config, - mac: mac, - putPolicy: putPolicy, - uploader: formUploader, - manager: storage.NewBucketManager(mac, &storeConfig), - proxyURL: appConfig.ProxyURL, - } + s.config = config + s.mac = mac + s.putPolicy = putPolicy + s.uploader = formUploader + s.bucket = storage.NewBucketManager(mac, &storeConfig) } - -func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) { +func (s QiNiuOss) PutFile(ctx *gin.Context, name string) (File, error) { // 解析表单 file, err := ctx.FormFile(name) if err != nil { @@ -74,7 +73,7 @@ func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) { defer src.Close() fileExt := filepath.Ext(file.Filename) - key := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt) + key := fmt.Sprintf("%d%s", time.Now().UnixMicro(), fileExt) // 上传文件 ret := storage.PutRet{} extra := storage.PutExtra{} @@ -93,7 +92,7 @@ func (s QinNiuOss) PutFile(ctx *gin.Context, name string) (File, error) { } -func (s QinNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string, error) { +func (s QiNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string, error) { var fileData []byte var err error if useProxy { @@ -111,7 +110,7 @@ func (s QinNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string if ext == "" { ext = filepath.Ext(parse.Path) } - key := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), ext) + key := fmt.Sprintf("%d%s", time.Now().UnixMicro(), ext) ret := storage.PutRet{} extra := storage.PutExtra{} // 上传文件字节数据 @@ -122,12 +121,12 @@ func (s QinNiuOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string return fmt.Sprintf("%s/%s", s.config.Domain, ret.Key), nil } -func (s QinNiuOss) PutBase64(base64Img string) (string, error) { +func (s QiNiuOss) PutBase64(base64Img string) (string, error) { imageData, err := base64.StdEncoding.DecodeString(base64Img) if err != nil { return "", fmt.Errorf("error decoding base64:%v", err) } - objectKey := fmt.Sprintf("%s/%d.png", s.config.SubDir, time.Now().UnixMicro()) + objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro()) ret := storage.PutRet{} extra := storage.PutExtra{} // 上传文件字节数据 @@ -138,16 +137,15 @@ func (s QinNiuOss) PutBase64(base64Img string) (string, error) { return fmt.Sprintf("%s/%s", s.config.Domain, ret.Key), nil } -func (s QinNiuOss) Delete(fileURL string) error { +func (s QiNiuOss) Delete(fileURL string) error { var objectKey string if strings.HasPrefix(fileURL, "http") { - filename := filepath.Base(fileURL) - objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename) + objectKey = filepath.Base(fileURL) } else { objectKey = fileURL } - return s.manager.Delete(s.config.Bucket, objectKey) + return s.bucket.Delete(s.config.Bucket, objectKey) } -var _ Uploader = QinNiuOss{} +var _ Uploader = QiNiuOss{} diff --git a/api/service/oss/uploader.go b/api/service/oss/uploader.go index 09de5cc1..255bcfe0 100644 --- a/api/service/oss/uploader.go +++ b/api/service/oss/uploader.go @@ -9,10 +9,10 @@ package oss import "github.com/gin-gonic/gin" -const Local = "LOCAL" -const Minio = "MINIO" -const QiNiu = "QINIU" -const AliYun = "ALIYUN" +const Local = "local" +const Minio = "minio" +const QiNiu = "qiniu" +const AliYun = "aliyun" type File struct { Name string `json:"name"` diff --git a/api/service/oss/uploader_manager.go b/api/service/oss/uploader_manager.go index 573891b5..dc437178 100644 --- a/api/service/oss/uploader_manager.go +++ b/api/service/oss/uploader_manager.go @@ -9,45 +9,58 @@ package oss import ( "geekai/core/types" - "strings" + + logger2 "geekai/logger" ) +var logger = logger2.GetLogger() + type UploaderManager struct { - handler Uploader + local *LocalStorage + aliyun *AliYunOss + mini *MiniOss + qiniu *QiNiuOss + active string } -func NewUploaderManager(config *types.AppConfig) (*UploaderManager, error) { - active := Local - if config.OSS.Active != "" { - active = strings.ToUpper(config.OSS.Active) - } - var handler Uploader - switch active { - case Local: - handler = NewLocalStorage(config) - break - case Minio: - client, err := NewMiniOss(config) - if err != nil { - return nil, err - } - handler = client - break - case QiNiu: - handler = NewQiNiuOss(config) - break - case AliYun: - client, err := NewAliYunOss(config) - if err != nil { - return nil, err - } - handler = client - break +func NewUploaderManager(sysConfig *types.SystemConfig, local *LocalStorage, aliyun *AliYunOss, mini *MiniOss, qiniu *QiNiuOss) (*UploaderManager, error) { + if sysConfig.OSS.Active == "" { + sysConfig.OSS.Active = Local } - return &UploaderManager{handler: handler}, nil + return &UploaderManager{ + active: sysConfig.OSS.Active, + local: local, + aliyun: aliyun, + mini: mini, + qiniu: qiniu, + }, nil } func (m *UploaderManager) GetUploadHandler() Uploader { - return m.handler + switch m.active { + case Local: + return m.local + case AliYun: + return m.aliyun + case Minio: + return m.mini + case QiNiu: + return m.qiniu + } + return m.local +} + +func (m *UploaderManager) UpdateConfig(config types.OSSConfig) { + switch config.Active { + case Local: + m.local.UpdateConfig(config.Local) + case AliYun: + m.aliyun.UpdateConfig(config.AliYun) + case Minio: + m.mini.UpdateConfig(config.Minio) + case QiNiu: + m.qiniu.UpdateConfig(config.QiNiu) + } + m.active = config.Active } diff --git a/api/service/payment/alipay_service.go b/api/service/payment/alipay_service.go index 09290df8..ce567f2a 100644 --- a/api/service/payment/alipay_service.go +++ b/api/service/payment/alipay_service.go @@ -12,129 +12,98 @@ import ( "fmt" "geekai/core/types" logger2 "geekai/logger" - "github.com/go-pay/gopay" - "github.com/go-pay/gopay/alipay" "net/http" "os" + + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/alipay" ) type AlipayService struct { - config *types.AlipayConfig client *alipay.Client + config *types.AlipayConfig } var logger = logger2.GetLogger() -func NewAlipayService(appConfig *types.AppConfig) (*AlipayService, error) { - config := appConfig.AlipayConfig +func NewAlipayService(sysConfig *types.SystemConfig) (*AlipayService, error) { + config := sysConfig.Payment.Alipay if !config.Enabled { - logger.Info("Disabled Alipay service") - return nil, nil + logger.Debug("Disabled Alipay service") } - priKey, err := readKey(config.PrivateKey) + + service := &AlipayService{config: &config} + if config.Enabled { + err := service.UpdateConfig(&config) + if err != nil { + logger.Errorf("支付宝服务初始化失败: %v", err) + } + } + + return service, nil +} + +func (s *AlipayService) UpdateConfig(config *types.AlipayConfig) error { + client, err := alipay.NewClient(config.AppId, config.PrivateKey, !config.SandBox) if err != nil { - return nil, fmt.Errorf("error with read App Private key: %v", err) + return fmt.Errorf("error with initialize alipay service: %v", err) } - client, err := alipay.NewClient(config.AppId, priKey, !config.SandBox) - if err != nil { - return nil, fmt.Errorf("error with initialize alipay service: %v", err) + s.client = client + s.config = config + if os.Getenv("GEEKAI_DEBUG") == "true" { + logger.Info("Alipay Debug mode is enabled") + client.DebugSwitch = gopay.DebugOn } - - //client.DebugSwitch = gopay.DebugOn // 开启调试模式 - client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间 - SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8 - SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2 - - if err = client.SetCertSnByPath(config.PublicKey, config.RootCert, config.AlipayPublicKey); err != nil { - return nil, fmt.Errorf("error with load payment public key: %v", err) - } - - return &AlipayService{config: &config, client: client}, nil + return nil } -type AlipayParams struct { - OutTradeNo string `json:"out_trade_no"` - Subject string `json:"subject"` - TotalFee string `json:"total_fee"` - ReturnURL string `json:"return_url"` - NotifyURL string `json:"notify_url"` -} - -func (s *AlipayService) PayMobile(params AlipayParams) (string, error) { - bm := make(gopay.BodyMap) - bm.Set("subject", params.Subject) - bm.Set("out_trade_no", params.OutTradeNo) - bm.Set("quit_url", params.ReturnURL) - bm.Set("total_amount", params.TotalFee) - bm.Set("product_code", "QUICK_WAP_WAY") - return s.client.SetNotifyUrl(params.NotifyURL).SetReturnUrl(params.ReturnURL).TradeWapPay(context.Background(), bm) -} - -func (s *AlipayService) PayPC(params AlipayParams) (string, error) { +func (s *AlipayService) Pay(params PayRequest) (string, error) { bm := make(gopay.BodyMap) bm.Set("subject", params.Subject) bm.Set("out_trade_no", params.OutTradeNo) bm.Set("total_amount", params.TotalFee) - bm.Set("product_code", "FAST_INSTANT_TRADE_PAY") - return s.client.SetNotifyUrl(params.NotifyURL).SetReturnUrl(params.ReturnURL).TradePagePay(context.Background(), bm) + return s.client.TradeWapPay(context.Background(), bm) +} + +func (s *AlipayService) Query(outTradeNo string) (OrderInfo, error) { + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", outTradeNo) + rsp, err := s.client.TradeQuery(context.Background(), bm) + if err != nil { + return OrderInfo{}, fmt.Errorf("error with trade query: %v", err) + } + + switch rsp.Response.TradeStatus { + case "TRADE_SUCCESS": + logger.Debugf("支付宝查询订单成功:%+v", rsp.Response) + return OrderInfo{ + OutTradeNo: rsp.Response.OutTradeNo, + TradeId: rsp.Response.TradeNo, + Amount: rsp.Response.TotalAmount, + Status: Success, + PayTime: rsp.Response.SendPayDate, + }, nil + case "TRADE_CLOSED": + return OrderInfo{Status: Closed}, nil + default: + return OrderInfo{}, fmt.Errorf("error with trade query: %v", rsp.Response.TradeStatus) + } } // TradeVerify 交易验证 -func (s *AlipayService) TradeVerify(request *http.Request) NotifyVo { +func (s *AlipayService) TradeVerify(request *http.Request) (OrderInfo, error) { notifyReq, err := alipay.ParseNotifyToBodyMap(request) // c.Request 是 gin 框架的写法 if err != nil { - return NotifyVo{ - Status: Failure, - Message: "error with parse notify request: " + err.Error(), - } + return OrderInfo{}, fmt.Errorf("error with parse notify request: %v", err) } _, err = alipay.VerifySignWithCert(s.config.AlipayPublicKey, notifyReq) if err != nil { - return NotifyVo{ - Status: Failure, - Message: "error with verify sign: " + err.Error(), - } + return OrderInfo{}, fmt.Errorf("error with verify sign: %v", err) } - return s.TradeQuery(request.Form.Get("out_trade_no")) + return s.Query(request.Form.Get("out_trade_no")) } -func (s *AlipayService) TradeQuery(outTradeNo string) NotifyVo { - bm := make(gopay.BodyMap) - bm.Set("out_trade_no", outTradeNo) - - //查询订单 - rsp, err := s.client.TradeQuery(context.Background(), bm) - if err != nil { - return NotifyVo{ - Status: Failure, - Message: "异步查询验证订单信息发生错误" + outTradeNo + err.Error(), - } - } - - if rsp.Response.TradeStatus == "TRADE_SUCCESS" { - return NotifyVo{ - Status: Success, - OutTradeNo: rsp.Response.OutTradeNo, - TradeId: rsp.Response.TradeNo, - Amount: rsp.Response.TotalAmount, - Subject: rsp.Response.Subject, - Message: "OK", - } - } else { - return NotifyVo{ - Status: Failure, - Message: "异步查询验证订单信息发生错误" + outTradeNo, - } - } -} - -func readKey(filename string) (string, error) { - data, err := os.ReadFile(filename) - if err != nil { - return "", err - } - return string(data), nil -} +var _ PayService = (*AlipayService)(nil) diff --git a/api/service/payment/geekpay_service.go b/api/service/payment/epay_service.go similarity index 50% rename from api/service/payment/geekpay_service.go rename to api/service/payment/epay_service.go index c5306c6f..3f069323 100644 --- a/api/service/payment/geekpay_service.go +++ b/api/service/payment/epay_service.go @@ -22,41 +22,30 @@ import ( "time" ) -// GeekPayService Geek 支付服务 -type GeekPayService struct { - config *types.GeekPayConfig +// EPayService 易支付服务 +type EPayService struct { + config *types.EpayConfig } -func NewJPayService(appConfig *types.AppConfig) *GeekPayService { - return &GeekPayService{ - config: &appConfig.GeekPayConfig, +func NewEPayService(sysConfig *types.SystemConfig) *EPayService { + return &EPayService{ + config: &sysConfig.Payment.Epay, } } -type GeekPayParams struct { - Method string `json:"method"` // 接口类型 - Device string `json:"device"` // 设备类型 - Type string `json:"type"` // 支付方式 - OutTradeNo string `json:"out_trade_no"` // 商户订单号 - Name string `json:"name"` // 商品名称 - Money string `json:"money"` // 商品金额 - ClientIP string `json:"clientip"` //用户IP地址 - SubOpenId string `json:"sub_openid"` // 微信用户 openid,仅小程序支付需要 - SubAppId string `json:"sub_appid"` // 小程序 AppId,仅小程序支付需要 - NotifyURL string `json:"notify_url"` - ReturnURL string `json:"return_url"` +func (s *EPayService) UpdateConfig(config *types.EpayConfig) { + s.config = config } // Pay 支付订单 -func (s *GeekPayService) Pay(params GeekPayParams) (*GeekPayResp, error) { +func (s *EPayService) Pay(params PayRequest) (string, error) { p := map[string]string{ - "pid": s.config.AppId, - //"method": params.Method, + "pid": s.config.AppId, "device": params.Device, - "type": params.Type, + "type": params.PayWay, "out_trade_no": params.OutTradeNo, - "name": params.Name, - "money": params.Money, + "name": params.Subject, + "money": params.TotalFee, "clientip": params.ClientIP, "notify_url": params.NotifyURL, "return_url": params.ReturnURL, @@ -64,10 +53,21 @@ func (s *GeekPayService) Pay(params GeekPayParams) (*GeekPayResp, error) { } p["sign"] = s.Sign(p) p["sign_type"] = "MD5" - return s.sendRequest(s.config.ApiURL, p) + resp, err := s.sendRequest(s.config.ApiURL, p) + if err != nil { + return "", err + } + if resp.Code != 1 { + return "", errors.New(resp.Msg) + } + if resp.PayURL != "" { + return resp.PayURL, nil + } else { + return resp.QrCode, nil + } } -func (s *GeekPayService) Sign(params map[string]string) string { +func (s *EPayService) Sign(params map[string]string) string { // 按字母顺序排序参数 var keys []string for k := range params { @@ -100,7 +100,7 @@ type GeekPayResp struct { UrlScheme string `json:"urlscheme"` // 小程序跳转支付链接 } -func (s *GeekPayService) sendRequest(endpoint string, params map[string]string) (*GeekPayResp, error) { +func (s *EPayService) sendRequest(endpoint string, params map[string]string) (*GeekPayResp, error) { form := url.Values{} for k, v := range params { form.Add(k, v) @@ -137,3 +137,61 @@ func (s *GeekPayService) sendRequest(endpoint string, params map[string]string) } return &r, nil } + +func (s *EPayService) Query(outTradeNo string) (OrderInfo, error) { + + params := url.Values{} + params.Set("act", "order") + params.Set("pid", s.config.AppId) + params.Set("key", s.config.PrivateKey) + params.Set("out_trade_no", outTradeNo) + + apiURL := fmt.Sprintf("%s/api.php?%s", s.config.ApiURL, params.Encode()) + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := &http.Client{Transport: tr} + resp, err := client.Get(apiURL) + if err != nil { + return OrderInfo{}, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return OrderInfo{}, err + } + logger.Debugf(string(body)) + + var result struct { + Code int `json:"code"` + Msg string `json:"msg"` + Status string `json:"status"` + Name string `json:"name"` + Money string `json:"money"` + EndTime string `json:"endtime"` + TradeNo string `json:"trade_no"` + } + if err := json.Unmarshal(body, &result); err != nil { + return OrderInfo{}, errors.New("订单查询响应解析失败") + } + if result.Code != 1 { + return OrderInfo{}, errors.New(result.Msg) + } + logger.Debugf("订单信息:%+v", result) + orderInfo := OrderInfo{ + OutTradeNo: outTradeNo, + TradeId: result.TradeNo, + Amount: result.Money, + PayTime: result.EndTime, + } + if result.Status == "1" { + orderInfo.Status = Success + } else { + orderInfo.Status = Failure + } + return orderInfo, nil +} + +var _ PayService = (*EPayService)(nil) diff --git a/api/service/payment/hupipay_serive.go b/api/service/payment/hupipay_serive.go deleted file mode 100644 index b7266b7e..00000000 --- a/api/service/payment/hupipay_serive.go +++ /dev/null @@ -1,171 +0,0 @@ -package payment - -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// * Copyright 2023 The Geek-AI Authors. All rights reserved. -// * Use of this source code is governed by a Apache-2.0 license -// * that can be found in the LICENSE file. -// * @Author yangjian102621@163.com -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -import ( - "crypto/md5" - "encoding/hex" - "errors" - "fmt" - "geekai/core/types" - "geekai/utils" - "io" - "net/http" - "net/url" - "sort" - "strconv" - "strings" - "time" -) - -type HuPiPayService struct { - appId string - appSecret string - apiURL string -} - -func NewHuPiPay(config *types.AppConfig) *HuPiPayService { - return &HuPiPayService{ - appId: config.HuPiPayConfig.AppId, - appSecret: config.HuPiPayConfig.AppSecret, - apiURL: config.HuPiPayConfig.ApiURL, - } -} - -type HuPiPayParams struct { - AppId string `json:"appid"` - Version string `json:"version"` - TradeOrderId string `json:"trade_order_id"` - TotalFee string `json:"total_fee"` - Title string `json:"title"` - NotifyURL string `json:"notify_url"` - ReturnURL string `json:"return_url"` - WapName string `json:"wap_name"` - CallbackURL string `json:"callback_url"` - Time string `json:"time"` - NonceStr string `json:"nonce_str"` - Type string `json:"type"` - WapUrl string `json:"wap_url"` -} - -type HuPiPayResp struct { - Openid interface{} `json:"openid"` - UrlQrcode string `json:"url_qrcode"` - URL string `json:"url"` - ErrCode int `json:"errcode"` - ErrMsg string `json:"errmsg,omitempty"` -} - -// Pay 执行支付请求操作 -func (s *HuPiPayService) Pay(params HuPiPayParams) (HuPiPayResp, error) { - data := url.Values{} - simple := strconv.FormatInt(time.Now().Unix(), 10) - params.AppId = s.appId - params.Time = simple - params.NonceStr = simple - encode := utils.JsonEncode(params) - m := make(map[string]string) - _ = utils.JsonDecode(encode, &m) - for k, v := range m { - data.Add(k, fmt.Sprintf("%v", v)) - } - // 生成签名 - data.Add("hash", s.Sign(data)) - // 发送支付请求 - apiURL := fmt.Sprintf("%s/payment/do.html", s.apiURL) - resp, err := http.PostForm(apiURL, data) - if err != nil { - return HuPiPayResp{}, fmt.Errorf("error with requst api: %v", err) - } - defer resp.Body.Close() - all, err := io.ReadAll(resp.Body) - if err != nil { - return HuPiPayResp{}, fmt.Errorf("error with reading response: %v", err) - } - - var res HuPiPayResp - err = utils.JsonDecode(string(all), &res) - if err != nil { - return HuPiPayResp{}, fmt.Errorf("error with decode payment result: %v", err) - } - - if res.ErrCode != 0 { - return HuPiPayResp{}, fmt.Errorf("error with generate pay url: %s", res.ErrMsg) - } - - return res, nil -} - -// Sign 签名方法 -func (s *HuPiPayService) Sign(params url.Values) string { - params.Del(`Sign`) - var keys = make([]string, 0, 0) - for key := range params { - if params.Get(key) != `` { - keys = append(keys, key) - } - } - sort.Strings(keys) - - var pList = make([]string, 0, 0) - for _, key := range keys { - var value = strings.TrimSpace(params.Get(key)) - if len(value) > 0 { - pList = append(pList, key+"="+value) - } - } - var src = strings.Join(pList, "&") - src += s.appSecret - - md5bs := md5.Sum([]byte(src)) - return hex.EncodeToString(md5bs[:]) -} - -// Check 校验订单状态 -func (s *HuPiPayService) Check(outTradeNo string) error { - data := url.Values{} - data.Add("appid", s.appId) - data.Add("out_trade_order", outTradeNo) - stamp := strconv.FormatInt(time.Now().Unix(), 10) - data.Add("time", stamp) - data.Add("nonce_str", stamp) - data.Add("hash", s.Sign(data)) - - apiURL := fmt.Sprintf("%s/payment/query.html", s.apiURL) - resp, err := http.PostForm(apiURL, data) - if err != nil { - return fmt.Errorf("error with http reqeust: %v", err) - } - - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("error with reading response: %v", err) - } - - var r struct { - ErrCode int `json:"errcode"` - Data struct { - Status string `json:"status"` - OpenOrderId string `json:"open_order_id"` - } `json:"data,omitempty"` - ErrMsg string `json:"errmsg"` - Hash string `json:"hash"` - } - err = utils.JsonDecode(string(body), &r) - if err != nil { - return fmt.Errorf("error with decode response: %v", err) - } - - if r.ErrCode == 0 && r.Data.Status == "OD" { - return nil - } else { - logger.Debugf("%+v", r) - return errors.New("order not paid:" + r.ErrMsg) - } -} diff --git a/api/service/payment/pay_service.go b/api/service/payment/pay_service.go new file mode 100644 index 00000000..06c65728 --- /dev/null +++ b/api/service/payment/pay_service.go @@ -0,0 +1,54 @@ +package payment + +// 支付渠道定义 +const PayChannelAL = "alipay" // 支付宝 +const PayChannelWX = "wxpay" // 微信支付 +const PayChannelEpay = "epay" // 易支付 + +// 支付方式 +const PayWayAL = "alipay" +const PayWayWX = "wxpay" + +const ( + Success = 0 + Failure = 1 + Closed = 2 +) + +type PayRequest struct { + OutTradeNo string // 商户订单号 + Subject string // 商品名称 + TotalFee string // 商品金额 + ReturnURL string // 回调地址 + NotifyURL string // 回调地址 + + // 易支付专有参数 + Method string // 接口类型 + Device string // 设备类型 + PayWay string // 支付方式 + ClientIP string //用户IP地址 + OpenID string // 用户openid + +} + +type OrderInfo struct { + Mchid string // 商户号 + OutTradeNo string // 商户订单号 + TradeId string // 交易号 + Amount string // 金额 + Status int // 状态 0: 未支付 1: 已支付 2: 已关闭 + PayTime string // 完成支付时间 +} + +func (o OrderInfo) Closed() bool { + return o.Status == Closed +} + +func (o OrderInfo) Success() bool { + return o.Status == Success +} + +type PayService interface { + Pay(params PayRequest) (string, error) // 生成支付链接 + Query(outTradeNo string) (OrderInfo, error) // 查询订单 +} diff --git a/api/service/payment/types.go b/api/service/payment/types.go deleted file mode 100644 index ef8ff24c..00000000 --- a/api/service/payment/types.go +++ /dev/null @@ -1,19 +0,0 @@ -package payment - -type NotifyVo struct { - Status int - OutTradeNo string // 商户订单号 - TradeId string // 交易ID - Amount string // 交易金额 - Message string - Subject string -} - -func (v NotifyVo) Success() bool { - return v.Status == Success -} - -const ( - Success = 0 - Failure = 1 -) diff --git a/api/service/payment/wepay_service.go b/api/service/payment/wepay_service.go deleted file mode 100644 index 2e2ddb44..00000000 --- a/api/service/payment/wepay_service.go +++ /dev/null @@ -1,144 +0,0 @@ -package payment - -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// * Copyright 2023 The Geek-AI Authors. All rights reserved. -// * Use of this source code is governed by a Apache-2.0 license -// * that can be found in the LICENSE file. -// * @Author yangjian102621@163.com -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -import ( - "context" - "fmt" - "geekai/core/types" - "github.com/go-pay/gopay" - "github.com/go-pay/gopay/wechat/v3" - "net/http" - "time" -) - -type WechatPayService struct { - config *types.WechatPayConfig - client *wechat.ClientV3 -} - -func NewWechatService(appConfig *types.AppConfig) (*WechatPayService, error) { - config := appConfig.WechatPayConfig - if !config.Enabled { - logger.Info("Disabled WechatPay service") - return nil, nil - } - priKey, err := readKey(config.PrivateKey) - if err != nil { - return nil, fmt.Errorf("error with read App Private key: %v", err) - } - - client, err := wechat.NewClientV3(config.MchId, config.SerialNo, config.ApiV3Key, priKey) - if err != nil { - return nil, fmt.Errorf("error with initialize WechatPay service: %v", err) - } - err = client.AutoVerifySign() - if err != nil { - return nil, fmt.Errorf("error with autoVerifySign: %v", err) - } - //client.DebugSwitch = gopay.DebugOn - - return &WechatPayService{config: &config, client: client}, nil -} - -type WechatPayParams struct { - OutTradeNo string `json:"out_trade_no"` - TotalFee int `json:"total_fee"` - Subject string `json:"subject"` - ClientIP string `json:"client_ip"` - ReturnURL string `json:"return_url"` - NotifyURL string `json:"notify_url"` -} - -func (s *WechatPayService) PayUrlNative(params WechatPayParams) (string, error) { - expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) - // 初始化 BodyMap - bm := make(gopay.BodyMap) - bm.Set("appid", s.config.AppId). - Set("mchid", s.config.MchId). - Set("description", params.Subject). - Set("out_trade_no", params.OutTradeNo). - Set("time_expire", expire). - Set("notify_url", params.NotifyURL). - SetBodyMap("amount", func(bm gopay.BodyMap) { - bm.Set("total", params.TotalFee). - Set("currency", "CNY") - }) - - wxRsp, err := s.client.V3TransactionNative(context.Background(), bm) - if err != nil { - return "", fmt.Errorf("error with client v3 transaction Native: %v", err) - } - if wxRsp.Code != wechat.Success { - return "", fmt.Errorf("error status with generating pay url: %v", wxRsp.Error) - } - return wxRsp.Response.CodeUrl, nil -} - -func (s *WechatPayService) PayUrlH5(params WechatPayParams) (string, error) { - expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) - // 初始化 BodyMap - bm := make(gopay.BodyMap) - bm.Set("appid", s.config.AppId). - Set("mchid", s.config.MchId). - Set("description", params.Subject). - Set("out_trade_no", params.OutTradeNo). - Set("time_expire", expire). - Set("notify_url", params.NotifyURL). - SetBodyMap("amount", func(bm gopay.BodyMap) { - bm.Set("total", params.TotalFee). - Set("currency", "CNY") - }). - SetBodyMap("scene_info", func(bm gopay.BodyMap) { - bm.Set("payer_client_ip", params.ClientIP). - SetBodyMap("h5_info", func(bm gopay.BodyMap) { - bm.Set("type", "Wap") - }) - }) - - wxRsp, err := s.client.V3TransactionH5(context.Background(), bm) - if err != nil { - return "", fmt.Errorf("error with client v3 transaction H5: %v", err) - } - if wxRsp.Code != wechat.Success { - return "", fmt.Errorf("error with generating pay url: %v", wxRsp.Error) - } - return wxRsp.Response.H5Url, nil -} - -type NotifyResponse struct { - Code string `json:"code"` - Message string `xml:"message"` -} - -// TradeVerify 交易验证 -func (s *WechatPayService) TradeVerify(request *http.Request) NotifyVo { - notifyReq, err := wechat.V3ParseNotify(request) - if err != nil { - return NotifyVo{Status: 1, Message: fmt.Sprintf("error with client v3 parse notify: %v", err)} - } - - // TODO: 这里验签程序有 Bug,一直报错:crypto/rsa: verification error,先暂时取消验签 - //err = notifyReq.VerifySignByPK(s.client.WxPublicKey()) - //if err != nil { - // return fmt.Errorf("error with client v3 verify sign: %v", err) - //} - - // 解密支付密文,验证订单信息 - result, err := notifyReq.DecryptPayCipherText(s.config.ApiV3Key) - if err != nil { - return NotifyVo{Status: Failure, Message: fmt.Sprintf("error with client v3 decrypt: %v", err)} - } - - return NotifyVo{ - Status: Success, - OutTradeNo: result.OutTradeNo, - TradeId: result.TransactionId, - Amount: fmt.Sprintf("%.2f", float64(result.Amount.Total)/100), - } -} diff --git a/api/service/payment/wxpay_service.go b/api/service/payment/wxpay_service.go new file mode 100644 index 00000000..89ca27a9 --- /dev/null +++ b/api/service/payment/wxpay_service.go @@ -0,0 +1,217 @@ +package payment + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "context" + "fmt" + "geekai/core/types" + "geekai/utils" + "net/http" + "os" + "time" + + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/wechat/v3" +) + +type WxPayService struct { + config *types.WxPayConfig + client *wechat.ClientV3 +} + +func NewWxpayService(sysConfig *types.SystemConfig) (*WxPayService, error) { + config := sysConfig.Payment.WxPay + if !config.Enabled { + logger.Debug("Disabled WechatPay service") + } + + service := &WxPayService{config: &config} + if config.Enabled { + err := service.UpdateConfig(&config) + if err != nil { + logger.Errorf("微信支付服务初始化失败: %v", err) + } + } + + return service, nil +} + +func (s *WxPayService) UpdateConfig(config *types.WxPayConfig) error { + client, err := wechat.NewClientV3(config.MchId, config.SerialNo, config.ApiV3Key, config.PrivateKey) + if err != nil { + return fmt.Errorf("error with initialize WechatPay service: %v", err) + } + err = client.AutoVerifySign() + if err != nil { + return fmt.Errorf("error with autoVerifySign: %v", err) + } + s.client = client + if os.Getenv("GEEKAI_DEBUG") == "true" { + logger.Info("WechatPay Debug mode is enabled") + client.DebugSwitch = gopay.DebugOn + } + s.config = config + return nil +} + +func (s *WxPayService) Pay(params PayRequest) (string, error) { + expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("appid", s.config.AppId). + Set("mchid", s.config.MchId). + Set("description", params.Subject). + Set("out_trade_no", params.OutTradeNo). + Set("time_expire", expire). + Set("notify_url", params.NotifyURL). + SetBodyMap("amount", func(bm gopay.BodyMap) { + bm.Set("total", utils.IntValue(params.TotalFee, 0)). + Set("currency", "CNY") + }) + logger.Debugf("wxpay params: %+v", bm) + if params.Device == "mobile" { + bm.SetBodyMap("scene_info", func(bm gopay.BodyMap) { + bm.Set("payer_client_ip", params.ClientIP) + }).SetBodyMap("payer", func(bm gopay.BodyMap) { + bm.Set("openid", params.OpenID) + }) + wxRsp, err := s.client.V3TransactionJsapi(context.Background(), bm) + if err != nil { + return "", fmt.Errorf("error with client v3 transaction Jsapi: %v", err) + } + if wxRsp.Code != wechat.Success { + return "", fmt.Errorf("error status with generating pay url: %v", wxRsp.Error) + } + return wxRsp.Response.PrepayId, nil + } else if params.Device == "pc" { + wxRsp, err := s.client.V3TransactionNative(context.Background(), bm) + if err != nil { + return "", fmt.Errorf("error with client v3 transaction Native: %v", err) + } + if wxRsp.Code != wechat.Success { + return "", fmt.Errorf("error status with generating pay url: %v", wxRsp.Error) + } + return wxRsp.Response.CodeUrl, nil + + } + return "", nil +} + +func (s *WxPayService) Query(outTradeNo string) (OrderInfo, error) { + wxRsp, err := s.client.V3TransactionQueryOrder(context.Background(), wechat.OutTradeNo, outTradeNo) + if err != nil { + return OrderInfo{}, fmt.Errorf("error with client v3 transaction query: %v", err) + } + + if wxRsp.Code != wechat.Success { + return OrderInfo{}, fmt.Errorf("error status with querying order: %v", wxRsp.Error) + } + + if wxRsp.Response.TradeState == "CLOSED" { + return OrderInfo{Status: Closed}, nil + } + + orderInfo := OrderInfo{ + OutTradeNo: wxRsp.Response.OutTradeNo, + TradeId: wxRsp.Response.TransactionId, + Amount: fmt.Sprintf("%d", wxRsp.Response.Amount.Total/100), + PayTime: wxRsp.Response.SuccessTime, + } + if wxRsp.Response.TradeState == "SUCCESS" { + orderInfo.Status = Success + } else { + orderInfo.Status = Failure + } + return orderInfo, nil +} + +// TradeVerify 交易验证 +func (s *WxPayService) TradeVerify(request *http.Request) (OrderInfo, error) { + notifyReq, err := wechat.V3ParseNotify(request) + if err != nil { + return OrderInfo{}, fmt.Errorf("error with client v3 parse notify: %v", err) + } + + // 解密支付密文,验证订单信息 + result, err := notifyReq.DecryptPayCipherText(s.config.ApiV3Key) + if err != nil { + return OrderInfo{}, fmt.Errorf("error with client v3 decrypt: %v", err) + } + + return OrderInfo{ + Status: Success, + OutTradeNo: result.OutTradeNo, + TradeId: result.TransactionId, + Amount: fmt.Sprintf("%.2f", float64(result.Amount.Total)/100), + PayTime: result.SuccessTime, + }, nil +} + +// func (s *WechatPayService) PayUrlNative(params WechatPayParams) (string, error) { +// expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) +// // 初始化 BodyMap +// bm := make(gopay.BodyMap) +// bm.Set("appid", s.config.AppId). +// Set("mchid", s.config.MchId). +// Set("description", params.Subject). +// Set("out_trade_no", params.OutTradeNo). +// Set("time_expire", expire). +// Set("notify_url", params.NotifyURL). +// SetBodyMap("amount", func(bm gopay.BodyMap) { +// bm.Set("total", params.TotalFee). +// Set("currency", "CNY") +// }) + +// wxRsp, err := s.client.V3TransactionNative(context.Background(), bm) +// if err != nil { +// return "", fmt.Errorf("error with client v3 transaction Native: %v", err) +// } +// if wxRsp.Code != wechat.Success { +// return "", fmt.Errorf("error status with generating pay url: %v", wxRsp.Error) +// } +// return wxRsp.Response.CodeUrl, nil +// } + +// func (s *WechatPayService) PayUrlH5(params WechatPayParams) (string, error) { +// expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) +// // 初始化 BodyMap +// bm := make(gopay.BodyMap) +// bm.Set("appid", s.config.AppId). +// Set("mchid", s.config.MchId). +// Set("description", params.Subject). +// Set("out_trade_no", params.OutTradeNo). +// Set("time_expire", expire). +// Set("notify_url", params.NotifyURL). +// SetBodyMap("amount", func(bm gopay.BodyMap) { +// bm.Set("total", params.TotalFee). +// Set("currency", "CNY") +// }). +// SetBodyMap("scene_info", func(bm gopay.BodyMap) { +// bm.Set("payer_client_ip", params.ClientIP). +// SetBodyMap("h5_info", func(bm gopay.BodyMap) { +// bm.Set("type", "Wap") +// }) +// }) + +// wxRsp, err := s.client.V3TransactionH5(context.Background(), bm) +// if err != nil { +// return "", fmt.Errorf("error with client v3 transaction H5: %v", err) +// } +// if wxRsp.Code != wechat.Success { +// return "", fmt.Errorf("error with generating pay url: %v", wxRsp.Error) +// } +// return wxRsp.Response.H5Url, nil +// } + +// type NotifyResponse struct { +// Code string `json:"code"` +// Message string `xml:"message"` +// } + +var _ PayService = (*WxPayService)(nil) diff --git a/api/service/sms/aliyun.go b/api/service/sms/aliyun.go index d0ea1b97..db9d7c4e 100644 --- a/api/service/sms/aliyun.go +++ b/api/service/sms/aliyun.go @@ -10,36 +10,57 @@ package sms import ( "fmt" "geekai/core/types" + "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" ) type AliYunSmsService struct { - config *types.SmsConfigAli + config types.SmsConfigAli client *dysmsapi.Client + domain string + zoneId string } -func NewAliYunSmsService(appConfig *types.AppConfig) (*AliYunSmsService, error) { - config := &appConfig.SMS.Ali - // 创建阿里云短信客户端 +func NewAliYunSmsService(sysConfig *types.SystemConfig) (*AliYunSmsService, error) { + config := sysConfig.SMS.Ali + domain := "dysmsapi.aliyuncs.com" + zoneId := "cn-hangzhou" + + s := AliYunSmsService{ + config: config, + domain: domain, + zoneId: zoneId, + } + if sysConfig.SMS.Active == Ali { + err := s.UpdateConfig(config) + if err != nil { + logger.Errorf("阿里云短信初始化失败: %v", err) + } + } + return &s, nil +} + +func (s *AliYunSmsService) UpdateConfig(config types.SmsConfigAli) error { client, err := dysmsapi.NewClientWithAccessKey( - "cn-hangzhou", + s.zoneId, config.AccessKey, config.AccessSecret) if err != nil { - return nil, fmt.Errorf("failed to create client: %v", err) + return fmt.Errorf("failed to create client: %v", err) } - - return &AliYunSmsService{ - config: config, - client: client, - }, nil + s.client = client + s.config = config + return nil } func (s *AliYunSmsService) SendVerifyCode(mobile string, code int) error { + if s.client == nil { + return fmt.Errorf("阿里云短信服务未初始化") + } // 创建短信请求并设置参数 request := dysmsapi.CreateSendSmsRequest() request.Scheme = "https" - request.Domain = s.config.Domain + request.Domain = s.domain request.PhoneNumbers = mobile request.SignName = s.config.Sign request.TemplateCode = s.config.CodeTempId diff --git a/api/service/sms/bao.go b/api/service/sms/bao.go index a00398de..a220180d 100644 --- a/api/service/sms/bao.go +++ b/api/service/sms/bao.go @@ -19,20 +19,21 @@ import ( ) type BaoSmsService struct { - config *types.SmsConfigBao + config types.SmsConfigBao + domain string } -func NewSmsBaoSmsService(appConfig *types.AppConfig) *BaoSmsService { - config := appConfig.SMS.Bao - if config.Domain == "" { // use default domain - config.Domain = "api.smsbao.com" - logger.Infof("Using default domain for SMS-BAO: %s", config.Domain) - } +func NewBaoSmsService(sysConfig *types.SystemConfig) *BaoSmsService { return &BaoSmsService{ - config: &config, + config: sysConfig.SMS.Bao, + domain: "api.smsbao.com", } } +func (s *BaoSmsService) UpdateConfig(config types.SmsConfigBao) { + s.config = config +} + var errMsg = map[string]string{ "0": "短信发送成功", "-1": "参数不全", @@ -56,7 +57,7 @@ func (s *BaoSmsService) SendVerifyCode(mobile string, code int) error { params.Set("m", mobile) params.Set("c", content) - apiURL := fmt.Sprintf("https://%s/sms?%s", s.config.Domain, params.Encode()) + apiURL := fmt.Sprintf("https://%s/sms?%s", s.domain, params.Encode()) response, err := http.Get(apiURL) if err != nil { return err diff --git a/api/service/sms/service.go b/api/service/sms/service.go index 14d12ca9..82d0a914 100644 --- a/api/service/sms/service.go +++ b/api/service/sms/service.go @@ -7,8 +7,8 @@ package sms // * @Author yangjian102621@163.com // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -const Ali = "ALI" -const Bao = "BAO" +const Ali = "aliyun" +const Bao = "bao" type Service interface { SendVerifyCode(mobile string, code int) error diff --git a/api/service/sms/service_manager.go b/api/service/sms/service_manager.go deleted file mode 100644 index 0a4fcac8..00000000 --- a/api/service/sms/service_manager.go +++ /dev/null @@ -1,46 +0,0 @@ -package sms - -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// * Copyright 2023 The Geek-AI Authors. All rights reserved. -// * Use of this source code is governed by a Apache-2.0 license -// * that can be found in the LICENSE file. -// * @Author yangjian102621@163.com -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -import ( - "geekai/core/types" - logger2 "geekai/logger" - "strings" -) - -type ServiceManager struct { - handler Service -} - -var logger = logger2.GetLogger() - -func NewSendServiceManager(config *types.AppConfig) (*ServiceManager, error) { - active := Ali - if config.SMS.Active != "" { - active = strings.ToUpper(config.SMS.Active) - } - var handler Service - switch active { - case Ali: - client, err := NewAliYunSmsService(config) - if err != nil { - return nil, err - } - handler = client - break - case Bao: - handler = NewSmsBaoSmsService(config) - break - } - - return &ServiceManager{handler: handler}, nil -} - -func (m *ServiceManager) GetService() Service { - return m.handler -} diff --git a/api/service/sms/sms_manager.go b/api/service/sms/sms_manager.go new file mode 100644 index 00000000..32921827 --- /dev/null +++ b/api/service/sms/sms_manager.go @@ -0,0 +1,54 @@ +package sms + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "geekai/core/types" + logger2 "geekai/logger" +) + +type SmsManager struct { + aliyun *AliYunSmsService + bao *BaoSmsService + active string +} + +var logger = logger2.GetLogger() + +func NewSmsManager(sysConfig *types.SystemConfig, aliyun *AliYunSmsService, bao *BaoSmsService) (*SmsManager, error) { + + return &SmsManager{ + active: sysConfig.SMS.Active, + aliyun: aliyun, + bao: bao, + }, nil +} + +func (m *SmsManager) GetService() Service { + switch m.active { + case Ali: + return m.aliyun + case Bao: + return m.bao + } + return nil +} + +func (m *SmsManager) SetActive(active string) { + m.active = active +} + +func (m *SmsManager) UpdateConfig(config types.SMSConfig) { + switch config.Active { + case Ali: + m.aliyun.UpdateConfig(config.Ali) + case Bao: + m.bao.UpdateConfig(config.Bao) + } + m.active = config.Active +} diff --git a/api/service/smtp_sms_service.go b/api/service/smtp_sms_service.go index c6abf586..cb057d92 100644 --- a/api/service/smtp_sms_service.go +++ b/api/service/smtp_sms_service.go @@ -27,6 +27,10 @@ func NewSmtpService(appConfig *types.AppConfig) *SmtpService { } } +func (s *SmtpService) UpdateConfig(config *types.SmtpConfig) { + s.config = config +} + func (s *SmtpService) SendVerifyCode(to string, code int) error { subject := fmt.Sprintf("%s 注册验证码", s.config.AppName) body := fmt.Sprintf("【%s】:您的验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", s.config.AppName, code) diff --git a/api/service/suno/service.go b/api/service/suno/service.go index 6ad07457..4df37870 100644 --- a/api/service/suno/service.go +++ b/api/service/suno/service.go @@ -112,6 +112,10 @@ type RespVo struct { Message string `json:"message"` Data string `json:"data"` Channel string `json:"channel,omitempty"` + Error struct { + Message string `json:"message"` + Type string `json:"type"` + } `json:"error,omitempty"` } func (s *Service) Create(task types.SunoTask) (RespVo, error) { @@ -126,7 +130,7 @@ func (s *Service) Create(task types.SunoTask) (RespVo, error) { return RespVo{}, errors.New("no available API KEY for Suno") } - reqBody := map[string]interface{}{ + reqBody := map[string]any{ "task_id": task.RefTaskId, "continue_clip_id": task.RefSongId, "continue_at": task.ExtendSecs, @@ -154,13 +158,14 @@ func (s *Service) Create(task types.SunoTask) (RespVo, error) { } body, _ := io.ReadAll(r.Body) + logger.Debugf("API response: %s", string(body)) err = json.Unmarshal(body, &res) if err != nil { return RespVo{}, fmt.Errorf("解析API数据失败:%v, %s", err, string(body)) } if res.Code != "success" { - return RespVo{}, fmt.Errorf("API 返回失败:%s", res.Message) + return RespVo{}, fmt.Errorf("API 返回失败:%s", res.Error.Message) } // update the last_use_at for api key apiKey.LastUsedAt = time.Now().Unix() @@ -225,7 +230,7 @@ func (s *Service) Upload(task types.SunoTask) (RespVo, error) { return RespVo{}, errors.New("no available API KEY for Suno") } - reqBody := map[string]interface{}{ + reqBody := map[string]any{ "url": task.AudioURL, } @@ -330,7 +335,13 @@ func (s *Service) SyncTaskProgress() { job.SongId = v.Id job.Duration = int(v.Metadata.Duration) job.Prompt = v.Metadata.Prompt - job.Tags = v.Metadata.Tags + // 修复 tags 字段过长导致插入数据库失败 + if len(v.Metadata.Tags) > 255 { + job.Tags = v.Metadata.Tags[:255] + } else { + job.Tags = v.Metadata.Tags + } + job.ModelName = v.ModelName job.RawData = utils.JsonEncode(v) job.CoverURL = v.ImageLargeUrl diff --git a/api/service/types.go b/api/service/types.go index f39bf73c..25863471 100644 --- a/api/service/types.go +++ b/api/service/types.go @@ -1,5 +1,7 @@ package service +import logger2 "geekai/logger" + const FailTaskProgress = 101 const ( TaskStatusRunning = "RUNNING" @@ -15,6 +17,8 @@ type NotifyMessage struct { Type string `json:"type"` } +var logger = logger2.GetLogger() + const TranslatePromptTemplate = "Translate the following painting prompt words into English keyword phrases. Without any explanation, directly output the keyword phrases separated by commas. The content to be translated is: [%s]" const ImagePromptOptimizeTemplate = ` diff --git a/api/service/wxlogin_service.go b/api/service/wxlogin_service.go new file mode 100644 index 00000000..4a812572 --- /dev/null +++ b/api/service/wxlogin_service.go @@ -0,0 +1,117 @@ +package service + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * Copyright 2023 The Geek-AI Authors. All rights reserved. +// * Use of this source code is governed by a Apache-2.0 license +// * that can be found in the LICENSE file. +// * @Author yangjian102621@163.com +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +import ( + "context" + "errors" + "fmt" + "geekai/core/types" + "geekai/utils" + "time" + + "github.com/go-redis/redis/v8" + "github.com/imroc/req/v3" +) + +type WxLoginService struct { + config types.WxLoginConfig + client *req.Client + redisClient *redis.Client +} + +const loginStateKeyPrefix = "wx_login_state/" + +type LoginStatus struct { + Status string `json:"status"` + OpenID string `json:"openid"` + Token string `json:"token"` +} + +const ( + LoginStatusPending = "pending" + LoginStatusSuccess = "success" + LoginStatusExpired = "expired" // 登录失效,需要重新登录 +) + +func NewWxLoginService(config types.WxLoginConfig, redisClient *redis.Client) *WxLoginService { + return &WxLoginService{ + config: config, + client: req.C().SetTimeout(10 * time.Second), + redisClient: redisClient, + } +} + +func (s *WxLoginService) UpdateConfig(config types.WxLoginConfig) { + s.config = config +} + +func (s *WxLoginService) GetConfig() types.WxLoginConfig { + return s.config +} + +func (s *WxLoginService) SetConfig(config types.WxLoginConfig) { + s.config = config +} + +func (s *WxLoginService) GetLoginQrCodeUrl(state string) (string, error) { + if s.config.ApiKey == "" { + return "", errors.New("无效的 API Key") + } + + url := fmt.Sprintf("%s/api/auth/wechat/login", types.GeekAPIURL) + var res struct { + Code types.BizCode `json:"code"` + Message string `json:"message"` + Data struct { + Ticket string `json:"ticket"` + Url string `json:"url"` + } `json:"data"` + } + r, err := s.client.R(). + SetHeader("Authorization", s.config.ApiKey). + SetBody(map[string]string{ + "notify_url": s.config.NotifyURL, + "state": state, + }). + SetSuccessResult(&res).Post(url) + if err != nil || r.IsErrorState() { + return "", fmt.Errorf("请求 API 失败:%v", err) + } + + if res.Code != types.Success { + return "", fmt.Errorf("请求 API 失败:%s", res.Message) + } + + status := LoginStatus{ + Status: LoginStatusPending, + OpenID: "", + } + s.redisClient.Set(context.Background(), loginStateKeyPrefix+state, utils.JsonEncode(status), time.Hour) + + return res.Data.Url, nil +} + +func (s *WxLoginService) GetLoginStatus(state string) (*LoginStatus, error) { + result, err := s.redisClient.Get(context.Background(), loginStateKeyPrefix+state).Result() + if err != nil { + return nil, errors.New("登录失败") + } + + var status LoginStatus + err = utils.JsonDecode(result, &status) + if err != nil { + return nil, errors.New("登录失败") + } + + return &status, nil +} + +func (s *WxLoginService) SetLoginStatus(state string, status LoginStatus) { + s.redisClient.Set(context.Background(), loginStateKeyPrefix+state, utils.JsonEncode(status), time.Hour) +} diff --git a/api/service/xxl_job_service.go b/api/service/xxl_job_service.go deleted file mode 100644 index 6fae6fad..00000000 --- a/api/service/xxl_job_service.go +++ /dev/null @@ -1,64 +0,0 @@ -package service - -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// * Copyright 2023 The Geek-AI Authors. All rights reserved. -// * Use of this source code is governed by a Apache-2.0 license -// * that can be found in the LICENSE file. -// * @Author yangjian102621@163.com -// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -import ( - "context" - "geekai/core/types" - logger2 "geekai/logger" - - "github.com/xxl-job/xxl-job-executor-go" - "gorm.io/gorm" -) - -var logger = logger2.GetLogger() - -type XXLJobExecutor struct { - executor xxl.Executor - db *gorm.DB -} - -func NewXXLJobExecutor(config *types.AppConfig, db *gorm.DB) *XXLJobExecutor { - if !config.XXLConfig.Enabled { - logger.Info("XXL-JOB service is disabled") - return nil - } - - exec := xxl.NewExecutor( - xxl.ServerAddr(config.XXLConfig.ServerAddr), - xxl.AccessToken(config.XXLConfig.AccessToken), //请求令牌(默认为空) - xxl.ExecutorIp(config.XXLConfig.ExecutorIp), //可自动获取 - xxl.ExecutorPort(config.XXLConfig.ExecutorPort), //默认9999(非必填) - xxl.RegistryKey(config.XXLConfig.RegistryKey), //执行器名称 - xxl.SetLogger(&customLogger{}), //自定义日志 - ) - exec.Init() - return &XXLJobExecutor{executor: exec, db: db} -} - -func (e *XXLJobExecutor) Run() error { - e.executor.RegTask("ClearOrders", e.ClearOrders) - return e.executor.Run() -} - -// ClearOrders 清理未支付的订单,如果没有抛出异常则表示执行成功 -func (e *XXLJobExecutor) ClearOrders(cxt context.Context, param *xxl.RunReq) (msg string) { - logger.Info("执行清理未支付订单...") - - return "success" -} - -type customLogger struct{} - -func (l *customLogger) Info(format string, a ...interface{}) { - logger.Debugf(format, a...) -} - -func (l *customLogger) Error(format string, a ...interface{}) { - logger.Errorf(format, a...) -} diff --git a/api/store/model/admin_user.go b/api/store/model/admin_user.go index 066f0462..2978cb70 100644 --- a/api/store/model/admin_user.go +++ b/api/store/model/admin_user.go @@ -5,18 +5,18 @@ import ( ) type AdminUser struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - Username string `gorm:"column:username;type:varchar(30);uniqueIndex;not null;comment:用户名" json:"username"` - Password string `gorm:"column:password;type:char(64);not null;comment:密码" json:"password"` - Salt string `gorm:"column:salt;type:char(12);not null;comment:密码盐" json:"salt"` - Status bool `gorm:"column:status;type:tinyint(1);not null;comment:当前状态" json:"status"` - LastLoginAt int64 `gorm:"column:last_login_at;type:int;not null;comment:最后登录时间" json:"last_login_at"` - LastLoginIp string `gorm:"column:last_login_ip;type:char(16);not null;comment:最后登录 IP" json:"last_login_ip"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Username string `gorm:"column:username;type:varchar(30);uniqueIndex;not null;comment:用户名" json:"username"` + Password string `gorm:"column:password;type:char(64);not null;comment:密码" json:"password"` + Salt string `gorm:"column:salt;type:char(12);not null;comment:密码盐" json:"salt"` + Status bool `gorm:"column:status;type:tinyint(1);not null;comment:当前状态" json:"status"` + LastLoginAt int64 `gorm:"column:last_login_at;type:int;not null;comment:最后登录时间" json:"last_login_at"` + LastLoginIp string `gorm:"column:last_login_ip;type:char(16);not null;comment:最后登录 IP" json:"last_login_ip"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"` } // TableName 表名 func (m *AdminUser) TableName() string { - return "chatgpt_admin_users" + return "geekai_admin_users" } diff --git a/api/store/model/api_key.go b/api/store/model/api_key.go index e020435f..0ddd3c7b 100644 --- a/api/store/model/api_key.go +++ b/api/store/model/api_key.go @@ -6,13 +6,13 @@ import ( // ApiKey OpenAI API 模型 type ApiKey struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` Name string `gorm:"column:name;type:varchar(30);comment:名称" json:"name"` Value string `gorm:"column:value;type:varchar(255);not null;comment:API KEY value" json:"value"` Type string `gorm:"column:type;type:varchar(10);default:chat;not null;comment:用途(chat=>聊天,img=>图片)" json:"type"` - LastUsedAt int64 `gorm:"column:last_used_at;type:int;not null;comment:最后使用时间" json:"last_used_at"` + LastUsedAt int64 `gorm:"column:last_used_at;type:int;not null;comment:最后使用时间" json:"last_used_at"` ApiURL string `gorm:"column:api_url;type:varchar(255);comment:API 地址" json:"api_url"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);comment:是否启用" json:"enabled"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);comment:是否启用" json:"enabled"` ProxyURL string `gorm:"column:proxy_url;type:varchar(100);comment:代理地址" json:"proxy_url"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` @@ -20,5 +20,5 @@ type ApiKey struct { // TableName 表名 func (m *ApiKey) TableName() string { - return "chatgpt_api_keys" + return "geekai_api_keys" } diff --git a/api/store/model/app_type.go b/api/store/model/app_type.go index c7db9813..f491a372 100644 --- a/api/store/model/app_type.go +++ b/api/store/model/app_type.go @@ -3,15 +3,15 @@ package model import "time" type AppType struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` Name string `gorm:"column:name;type:varchar(50);not null;comment:名称" json:"name"` Icon string `gorm:"column:icon;type:varchar(255);not null;comment:图标URL" json:"icon"` SortNum int `gorm:"column:sort_num;type:tinyint;not null;comment:排序" json:"sort_num"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;comment:是否启用" json:"enabled"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;comment:是否启用" json:"enabled"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` } // TableName 表名 func (m *AppType) TableName() string { - return "chatgpt_app_types" + return "geekai_app_types" } diff --git a/api/store/model/chat_role.go b/api/store/model/chat_app.go similarity index 78% rename from api/store/model/chat_role.go rename to api/store/model/chat_app.go index 46e6b262..9336aca4 100644 --- a/api/store/model/chat_role.go +++ b/api/store/model/chat_app.go @@ -4,21 +4,21 @@ import ( "time" ) -type ChatRole struct { +type ChatApp struct { Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` Name string `gorm:"column:name;type:varchar(30);not null;comment:角色名称" json:"name"` - Tid uint `gorm:"column:tid;type:int;not null;comment:分类ID" json:"tid"` + Tid uint `gorm:"column:tid;type:int(11);not null;comment:分类ID" json:"tid"` Key string `gorm:"column:marker;type:varchar(30);uniqueIndex;not null;comment:角色标识" json:"marker"` Context string `gorm:"column:context_json;type:text;not null;comment:角色语料 json" json:"context_json"` HelloMsg string `gorm:"column:hello_msg;type:varchar(255);not null;comment:打招呼信息" json:"hello_msg"` Icon string `gorm:"column:icon;type:varchar(255);not null;comment:角色图标" json:"icon"` Enable bool `gorm:"column:enable;type:tinyint(1);not null;comment:是否被启用" json:"enable"` SortNum int `gorm:"column:sort_num;type:smallint;not null;default:0;comment:角色排序" json:"sort_num"` - ModelId uint `gorm:"column:model_id;type:int;not null;default:0;comment:绑定模型ID" json:"model_id"` + ModelId uint `gorm:"column:model_id;type:int(11);not null;default:0;comment:绑定模型ID" json:"model_id"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` } -func (m *ChatRole) TableName() string { - return "chatgpt_chat_roles" +func (m *ChatApp) TableName() string { + return "geekai_chat_roles" } diff --git a/api/store/model/chat_item.go b/api/store/model/chat_item.go index 6e75a41e..33c8d36c 100644 --- a/api/store/model/chat_item.go +++ b/api/store/model/chat_item.go @@ -5,17 +5,17 @@ import ( ) type ChatItem struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` ChatId string `gorm:"column:chat_id;type:char(40);uniqueIndex;not null;comment:会话 ID" json:"chat_id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` - RoleId uint `gorm:"column:role_id;type:int;not null;comment:角色 ID" json:"role_id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户 ID" json:"user_id"` + RoleId uint `gorm:"column:role_id;type:int(11);not null;comment:角色 ID" json:"role_id"` Title string `gorm:"column:title;type:varchar(100);not null;comment:会话标题" json:"title"` - ModelId uint `gorm:"column:model_id;type:int;not null;default:0;comment:模型 ID" json:"model_id"` + ModelId uint `gorm:"column:model_id;type:int(11);not null;default:0;comment:模型 ID" json:"model_id"` Model string `gorm:"column:model;type:varchar(30);comment:模型名称" json:"model"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null;comment:更新时间" json:"updated_at"` } func (m *ChatItem) TableName() string { - return "chatgpt_chat_items" + return "geekai_chat_items" } diff --git a/api/store/model/chat_message.go b/api/store/model/chat_message.go index b6567a65..977dcdee 100644 --- a/api/store/model/chat_message.go +++ b/api/store/model/chat_message.go @@ -6,11 +6,11 @@ import ( type ChatMessage struct { Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;index;comment:用户 ID" json:"user_id"` ChatId string `gorm:"column:chat_id;type:char(40);not null;index;comment:会话 ID" json:"chat_id"` Type string `gorm:"column:type;type:varchar(10);not null;comment:类型:prompt|reply" json:"type"` Icon string `gorm:"column:icon;type:varchar(255);not null;comment:角色图标" json:"icon"` - RoleId uint `gorm:"column:role_id;type:int;not null;comment:角色 ID" json:"role_id"` + RoleId uint `gorm:"column:role_id;type:int(11);not null;comment:角色 ID" json:"role_id"` Model string `gorm:"column:model;type:varchar(255);comment:模型名称" json:"model"` Content string `gorm:"column:content;type:text;not null;comment:聊天内容" json:"content"` Tokens int `gorm:"column:tokens;type:smallint;not null;comment:耗费 token 数量" json:"tokens"` @@ -21,5 +21,5 @@ type ChatMessage struct { } func (m *ChatMessage) TableName() string { - return "chatgpt_chat_history" + return "geekai_chat_history" } diff --git a/api/store/model/chat_model.go b/api/store/model/chat_model.go index cf42e154..53947b0d 100644 --- a/api/store/model/chat_model.go +++ b/api/store/model/chat_model.go @@ -18,12 +18,12 @@ type ChatModel struct { MaxTokens int `gorm:"column:max_tokens;type:int;not null;default:1024;comment:最大响应长度" json:"max_tokens"` MaxContext int `gorm:"column:max_context;type:int;not null;default:4096;comment:最大上下文长度" json:"max_context"` Open bool `gorm:"column:open;type:tinyint(1);not null;comment:是否开放模型" json:"open"` - KeyId uint `gorm:"column:key_id;type:int;not null;comment:绑定API KEY ID" json:"key_id"` + KeyId uint `gorm:"column:key_id;type:int(11);not null;comment:绑定API KEY ID" json:"key_id"` Options string `gorm:"column:options;type:text;not null;comment:模型自定义选项" json:"options"` CreatedAt time.Time `gorm:"column:created_at;type:datetime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at;type:datetime" json:"updated_at"` } func (m *ChatModel) TableName() string { - return "chatgpt_chat_models" + return "geekai_chat_models" } diff --git a/api/store/model/config.go b/api/store/model/config.go index 494311b7..33ccb19b 100644 --- a/api/store/model/config.go +++ b/api/store/model/config.go @@ -7,5 +7,5 @@ type Config struct { } func (m *Config) TableName() string { - return "chatgpt_configs" + return "geekai_configs" } diff --git a/api/store/model/dalle_job.go b/api/store/model/dalle_job.go index 893832f8..0120f50f 100644 --- a/api/store/model/dalle_job.go +++ b/api/store/model/dalle_job.go @@ -3,8 +3,8 @@ package model import "time" type DallJob struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"` Prompt string `gorm:"column:prompt;type:text;not null;comment:提示词" json:"prompt"` TaskInfo string `gorm:"column:task_info;type:text;not null;comment:任务详情" json:"task_info"` ImgURL string `gorm:"column:img_url;type:varchar(255);not null;comment:图片地址" json:"img_url"` @@ -17,5 +17,5 @@ type DallJob struct { } func (m *DallJob) TableName() string { - return "chatgpt_dall_jobs" + return "geekai_dall_jobs" } diff --git a/api/store/model/file.go b/api/store/model/file.go index 4e529d9c..fc06a78a 100644 --- a/api/store/model/file.go +++ b/api/store/model/file.go @@ -3,8 +3,8 @@ package model import "time" type File struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户 ID" json:"user_id"` Name string `gorm:"column:name;type:varchar(255);not null;comment:文件名" json:"name"` ObjKey string `gorm:"column:obj_key;type:varchar(100);comment:文件标识" json:"obj_key"` URL string `gorm:"column:url;type:varchar(255);not null;comment:文件地址" json:"url"` @@ -14,5 +14,5 @@ type File struct { } func (m *File) TableName() string { - return "chatgpt_files" + return "geekai_files" } diff --git a/api/store/model/function.go b/api/store/model/function.go index cb7b18a7..f5fd4d4d 100644 --- a/api/store/model/function.go +++ b/api/store/model/function.go @@ -1,16 +1,16 @@ package model type Function struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` Name string `gorm:"column:name;type:varchar(30);uniqueIndex;not null;comment:函数名称" json:"name"` Label string `gorm:"column:label;type:varchar(30);comment:函数标签" json:"label"` Description string `gorm:"column:description;type:varchar(255);comment:函数描述" json:"description"` Parameters string `gorm:"column:parameters;type:text;comment:函数参数(JSON)" json:"parameters"` Token string `gorm:"column:token;type:varchar(255);comment:API授权token" json:"token"` Action string `gorm:"column:action;type:varchar(255);comment:函数处理 API" json:"action"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启用" json:"enabled"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启用" json:"enabled"` } func (m *Function) TableName() string { - return "chatgpt_functions" + return "geekai_functions" } diff --git a/api/store/model/invite_code.go b/api/store/model/invite_code.go index b5bce1e7..2e56475d 100644 --- a/api/store/model/invite_code.go +++ b/api/store/model/invite_code.go @@ -3,8 +3,8 @@ package model import "time" type InviteCode struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"` Code string `gorm:"column:code;type:char(8);uniqueIndex;not null;comment:邀请码" json:"code"` Hits int `gorm:"column:hits;type:int;not null;comment:点击次数" json:"hits"` RegNum int `gorm:"column:reg_num;type:smallint;not null;comment:注册数量" json:"reg_num"` @@ -12,5 +12,5 @@ type InviteCode struct { } func (m *InviteCode) TableName() string { - return "chatgpt_invite_codes" + return "geekai_invite_codes" } diff --git a/api/store/model/invite_log.go b/api/store/model/invite_log.go index dd5b5ea1..75dc7adf 100644 --- a/api/store/model/invite_log.go +++ b/api/store/model/invite_log.go @@ -5,9 +5,9 @@ import ( ) type InviteLog struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - InviterId uint `gorm:"column:inviter_id;type:int;not null;comment:邀请人ID" json:"inviter_id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:注册用户ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + InviterId uint `gorm:"column:inviter_id;type:int(11);not null;comment:邀请人ID" json:"inviter_id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:注册用户ID" json:"user_id"` Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"` InviteCode string `gorm:"column:invite_code;type:char(8);not null;comment:邀请码" json:"invite_code"` Remark string `gorm:"column:remark;type:varchar(255);not null;comment:备注" json:"remark"` @@ -15,5 +15,5 @@ type InviteLog struct { } func (m *InviteLog) TableName() string { - return "chatgpt_invite_logs" + return "geekai_invite_logs" } diff --git a/api/store/model/jimeng_job.go b/api/store/model/jimeng_job.go index 93fe8c50..624cd90c 100644 --- a/api/store/model/jimeng_job.go +++ b/api/store/model/jimeng_job.go @@ -7,7 +7,7 @@ import ( // JimengJob 即梦AI任务模型 type JimengJob struct { Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;index;comment:用户ID" json:"user_id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;index;comment:用户ID" json:"user_id"` TaskId string `gorm:"column:task_id;type:varchar(100);not null;index;comment:任务ID" json:"task_id"` Type JMTaskType `gorm:"column:type;type:varchar(50);not null;comment:任务类型" json:"type"` ReqKey string `gorm:"column:req_key;type:varchar(100);comment:请求Key" json:"req_key"` @@ -51,5 +51,5 @@ const ( // TableName 返回数据表名称 func (JimengJob) TableName() string { - return "chatgpt_jimeng_jobs" + return "geekai_jimeng_jobs" } diff --git a/api/store/model/menu.go b/api/store/model/menu.go index 63babb67..b8698e23 100644 --- a/api/store/model/menu.go +++ b/api/store/model/menu.go @@ -2,14 +2,14 @@ package model // Menu 系统菜单 type Menu struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - Name string `gorm:"column:name;type:varchar(30);not null;comment:菜单名称" json:"name"` - Icon string `gorm:"column:icon;type:varchar(150);not null;comment:菜单图标" json:"icon"` - URL string `gorm:"column:url;type:varchar(100);not null;comment:地址" json:"url"` - SortNum int `gorm:"column:sort_num;type:smallint;not null;comment:排序" json:"sort_num"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;comment:是否启用" json:"enabled"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Name string `gorm:"column:name;type:varchar(30);not null;comment:菜单名称" json:"name"` + Icon string `gorm:"column:icon;type:varchar(150);not null;comment:菜单图标" json:"icon"` + URL string `gorm:"column:url;type:varchar(100);not null;comment:地址" json:"url"` + SortNum int `gorm:"column:sort_num;type:smallint;not null;comment:排序" json:"sort_num"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;comment:是否启用" json:"enabled"` } func (m *Menu) TableName() string { - return "chatgpt_menus" + return "geekai_menus" } diff --git a/api/store/model/mj_job.go b/api/store/model/mj_job.go index 44010b90..1f6cfaff 100644 --- a/api/store/model/mj_job.go +++ b/api/store/model/mj_job.go @@ -3,8 +3,8 @@ package model import "time" type MidJourneyJob struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户 ID" json:"user_id"` TaskId string `gorm:"column:task_id;type:varchar(20);uniqueIndex;comment:任务 ID" json:"task_id"` TaskInfo string `gorm:"column:task_info;type:text;not null;comment:任务详情" json:"task_info"` Type string `gorm:"column:type;type:varchar(20);default:image;comment:任务类别" json:"type"` @@ -24,5 +24,5 @@ type MidJourneyJob struct { } func (m *MidJourneyJob) TableName() string { - return "chatgpt_mj_jobs" + return "geekai_mj_jobs" } diff --git a/api/store/model/moderation.go b/api/store/model/moderation.go new file mode 100644 index 00000000..039dc0f5 --- /dev/null +++ b/api/store/model/moderation.go @@ -0,0 +1,17 @@ +package model + +import "time" + +type Moderation struct { + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"` + Source string `gorm:"column:source;type:varchar(255);not null;comment:敏感词来源" json:"source"` + Input string `gorm:"column:input;type:text;not null;comment:用户输入" json:"input"` + Output string `gorm:"column:output;type:text;not null;comment:AI 输出" json:"output"` + Result string `gorm:"column:result;type:text;not null;comment:鉴别结果" json:"result"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` +} + +func (m *Moderation) TableName() string { + return "geekai_moderation" +} diff --git a/api/store/model/order.go b/api/store/model/order.go index 9eda023a..67b25bd6 100644 --- a/api/store/model/order.go +++ b/api/store/model/order.go @@ -7,23 +7,24 @@ import ( // Order 充值订单 type Order struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户ID" json:"user_id"` - ProductId uint `gorm:"column:product_id;type:int;not null;comment:产品ID" json:"product_id"` - Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"` - OrderNo string `gorm:"column:order_no;type:varchar(30);uniqueIndex;not null;comment:订单ID" json:"order_no"` - TradeNo string `gorm:"column:trade_no;type:varchar(60);comment:支付平台交易流水号" json:"trade_no"` - Subject string `gorm:"column:subject;type:varchar(100);not null;comment:订单产品" json:"subject"` - Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;default:0.00;comment:订单金额" json:"amount"` - Status types.OrderStatus `gorm:"column:status;type:tinyint(1);not null;default:0;comment:订单状态(0:待支付,1:已扫码,2:支付成功)" json:"status"` - Remark string `gorm:"column:remark;type:varchar(255);not null;comment:备注" json:"remark"` - PayTime int64 `gorm:"column:pay_time;type:int;comment:支付时间" json:"pay_time"` - PayWay string `gorm:"column:pay_way;type:varchar(20);not null;comment:支付方式" json:"pay_way"` - PayType string `gorm:"column:pay_type;type:varchar(30);not null;comment:支付类型" json:"pay_type"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int;not null;comment:用户ID" json:"user_id"` + ProductId uint `gorm:"column:product_id;type:int;not null;comment:产品ID" json:"product_id"` + Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"` + OrderNo string `gorm:"column:order_no;type:varchar(30);uniqueIndex;not null;comment:订单ID" json:"order_no"` + TradeNo string `gorm:"column:trade_no;type:varchar(60);comment:支付平台交易流水号" json:"trade_no"` + Subject string `gorm:"column:subject;type:varchar(100);not null;comment:订单产品" json:"subject"` + Amount float64 `gorm:"column:amount;type:decimal(10,2);not null;default:0.00;comment:订单金额" json:"amount"` + Status types.OrderStatus `gorm:"column:status;type:tinyint(1);not null;default:0;comment:订单状态(0:待支付,1:已扫码,2:支付成功)" json:"status"` + Remark string `gorm:"column:remark;type:varchar(255);not null;comment:备注" json:"remark"` + PayTime int64 `gorm:"column:pay_time;type:int(11);comment:支付时间" json:"pay_time"` + PayWay string `gorm:"column:pay_way;type:varchar(20);not null;comment:支付方式" json:"pay_way"` + Channel string `gorm:"column:channel;type:varchar(30);not null;comment:支付类型渠道:支付宝,微信,聚合支付"` // 支付类型渠道 + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` + Checked bool `gorm:"column:checked;type:tinyint;not null;default:0;comment:是否已检查"` // 是否已检查 } func (m *Order) TableName() string { - return "chatgpt_orders" + return "geekai_orders" } diff --git a/api/store/model/power_log.go b/api/store/model/power_log.go index 3e5e403c..fd34c69f 100644 --- a/api/store/model/power_log.go +++ b/api/store/model/power_log.go @@ -7,18 +7,18 @@ import ( // PowerLog 算力消费日志 type PowerLog struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户ID" json:"user_id"` - Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"` - Type types.PowerType `gorm:"column:type;type:tinyint(1);not null;comment:类型(1:充值,2:消费,3:退费)" json:"type"` - Amount int `gorm:"column:amount;type:smallint;not null;comment:算力数值" json:"amount"` - Balance int `gorm:"column:balance;type:int;not null;comment:余额" json:"balance"` - Model string `gorm:"column:model;type:varchar(30);not null;comment:模型" json:"model"` - Remark string `gorm:"column:remark;type:varchar(512);not null;comment:备注" json:"remark"` - Mark types.PowerMark `gorm:"column:mark;type:tinyint(1);not null;comment:资金类型(0:支出,1:收入)" json:"mark"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"` + Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"` + Type types.PowerType `gorm:"column:type;type:tinyint(1);not null;comment:类型(1:充值,2:消费,3:退费)" json:"type"` + Amount int `gorm:"column:amount;type:smallint;not null;comment:算力数值" json:"amount"` + Balance int `gorm:"column:balance;type:int;not null;comment:余额" json:"balance"` + Model string `gorm:"column:model;type:varchar(255);not null;comment:模型" json:"model"` + Remark string `gorm:"column:remark;type:varchar(512);not null;comment:备注" json:"remark"` + Mark types.PowerMark `gorm:"column:mark;type:tinyint(1);not null;comment:资金类型(0:支出,1:收入)" json:"mark"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null;comment:创建时间" json:"created_at"` } func (m *PowerLog) TableName() string { - return "chatgpt_power_logs" + return "geekai_power_logs" } diff --git a/api/store/model/product.go b/api/store/model/product.go index f1bcc744..d5e22206 100644 --- a/api/store/model/product.go +++ b/api/store/model/product.go @@ -6,21 +6,17 @@ import ( // Product 充值产品 type Product struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` Name string `gorm:"column:name;type:varchar(30);not null;comment:名称" json:"name"` - Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"` - Discount float64 `gorm:"column:discount;type:decimal(10,2);not null;default:0.00;comment:优惠金额" json:"discount"` - Days int `gorm:"column:days;type:smallint;not null;default:0;comment:延长天数" json:"days"` - Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"` - Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"` - SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` - AppUrl string `gorm:"column:app_url;type:varchar(255);comment:App跳转地址" json:"app_url"` - Url string `gorm:"column:url;type:varchar(255);comment:跳转地址" json:"url"` + Price float64 `gorm:"column:price;type:decimal(10,2);not null;default:0.00;comment:价格" json:"price"` + Power int `gorm:"column:power;type:int;not null;default:0;comment:增加算力值" json:"power"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;default:0;comment:是否启动" json:"enabled"` + Sales int `gorm:"column:sales;type:int;not null;default:0;comment:销量" json:"sales"` + SortNum int `gorm:"column:sort_num;type:tinyint;not null;default:0;comment:排序" json:"sort_num"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` } func (m *Product) TableName() string { - return "chatgpt_products" + return "geekai_products" } diff --git a/api/store/model/redeem.go b/api/store/model/redeem.go index 9c399460..4d2ef9fa 100644 --- a/api/store/model/redeem.go +++ b/api/store/model/redeem.go @@ -5,16 +5,16 @@ import "time" // 兑换码 type Redeem struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户 ID" json:"user_id"` Name string `gorm:"column:name;type:varchar(30);not null;comment:兑换码名称" json:"name"` Power int `gorm:"column:power;type:int;not null;comment:算力" json:"power"` Code string `gorm:"column:code;type:varchar(100);uniqueIndex;not null;comment:兑换码" json:"code"` - Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;comment:是否启用" json:"enabled"` + Enabled bool `gorm:"column:enabled;type:tinyint(1);not null;comment:是否启用" json:"enabled"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` - RedeemedAt int64 `gorm:"column:redeemed_at;type:int;not null;comment:兑换时间" json:"redeemed_at"` + RedeemedAt int64 `gorm:"column:redeemed_at;type:int;not null;comment:兑换时间" json:"redeemed_at"` } func (m *Redeem) TableName() string { - return "chatgpt_redeems" + return "geekai_redeems" } diff --git a/api/store/model/sd_job.go b/api/store/model/sd_job.go index 13fc4ae0..5d16a244 100644 --- a/api/store/model/sd_job.go +++ b/api/store/model/sd_job.go @@ -3,21 +3,21 @@ package model import "time" type SdJob struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` - Type string `gorm:"column:type;type:varchar(20);default:txt2img;comment:任务类别" json:"type"` - TaskId string `gorm:"column:task_id;type:char(30);uniqueIndex;not null;comment:任务 ID" json:"task_id"` - TaskInfo string `gorm:"column:task_info;type:text;not null;comment:任务详情" json:"task_info"` - Prompt string `gorm:"column:prompt;type:text;not null;comment:会话提示词" json:"prompt"` - ImgURL string `gorm:"column:img_url;type:varchar(255);comment:图片URL" json:"img_url"` - Params string `gorm:"column:params;type:text;comment:绘画参数json" json:"params"` - Progress int `gorm:"column:progress;type:smallint;default:0;comment:任务进度" json:"progress"` - Publish int `gorm:"column:publish;type:tinyint(1);not null;comment:是否发布" json:"publish"` - ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"` - Power int `gorm:"column:power;type:smallint;not null;default:0;comment:消耗算力" json:"power"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户 ID" json:"user_id"` + Type string `gorm:"column:type;type:varchar(20);default:txt2img;comment:任务类别" json:"type"` + TaskId string `gorm:"column:task_id;type:char(30);uniqueIndex;not null;comment:任务 ID" json:"task_id"` + TaskInfo string `gorm:"column:task_info;type:text;not null;comment:任务详情" json:"task_info"` + Prompt string `gorm:"column:prompt;type:text;not null;comment:会话提示词" json:"prompt"` + ImgURL string `gorm:"column:img_url;type:varchar(255);comment:图片URL" json:"img_url"` + Params string `gorm:"column:params;type:text;comment:绘画参数json" json:"params"` + Progress int `gorm:"column:progress;type:smallint;default:0;comment:任务进度" json:"progress"` + Publish int `gorm:"column:publish;type:tinyint(1);not null;comment:是否发布" json:"publish"` + ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"` + Power int `gorm:"column:power;type:smallint;not null;default:0;comment:消耗算力" json:"power"` CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` } func (m *SdJob) TableName() string { - return "chatgpt_sd_jobs" + return "geekai_sd_jobs" } diff --git a/api/store/model/suno_job.go b/api/store/model/suno_job.go index aebcc016..583823d2 100644 --- a/api/store/model/suno_job.go +++ b/api/store/model/suno_job.go @@ -3,33 +3,33 @@ package model import "time" type SunoJob struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` - Channel string `gorm:"column:channel;type:varchar(100);not null;comment:渠道" json:"channel"` - Title string `gorm:"column:title;type:varchar(100);comment:歌曲标题" json:"title"` - Type int `gorm:"column:type;type:tinyint(1);default:0;comment:任务类型,1:灵感创作,2:自定义创作" json:"type"` - TaskId string `gorm:"column:task_id;type:varchar(50);comment:任务 ID" json:"task_id"` - TaskInfo string `gorm:"column:task_info;type:text;not null;comment:任务详情" json:"task_info"` - RefTaskId string `gorm:"column:ref_task_id;type:char(50);comment:引用任务 ID" json:"ref_task_id"` - Tags string `gorm:"column:tags;type:varchar(100);comment:歌曲风格" json:"tags"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` + Channel string `gorm:"column:channel;type:varchar(100);not null;comment:渠道" json:"channel"` + Title string `gorm:"column:title;type:varchar(100);comment:歌曲标题" json:"title"` + Type int `gorm:"column:type;type:tinyint(1);default:0;comment:任务类型,1:灵感创作,2:自定义创作" json:"type"` + TaskId string `gorm:"column:task_id;type:varchar(50);comment:任务 ID" json:"task_id"` + TaskInfo string `gorm:"column:task_info;type:text;not null;comment:任务详情" json:"task_info"` + RefTaskId string `gorm:"column:ref_task_id;type:char(50);comment:引用任务 ID" json:"ref_task_id"` + Tags string `gorm:"column:tags;type:varchar(255);comment:歌曲风格" json:"tags"` Instrumental bool `gorm:"column:instrumental;type:tinyint(1);default:0;comment:是否为纯音乐" json:"instrumental"` - ExtendSecs int `gorm:"column:extend_secs;type:smallint;default:0;comment:延长秒数" json:"extend_secs"` - SongId string `gorm:"column:song_id;type:varchar(50);comment:要续写的歌曲 ID" json:"song_id"` - RefSongId string `gorm:"column:ref_song_id;type:varchar(50);not null;comment:引用的歌曲ID" json:"ref_song_id"` - Prompt string `gorm:"column:prompt;type:varchar(2000);not null;comment:提示词" json:"prompt"` - CoverURL string `gorm:"column:cover_url;type:varchar(512);comment:封面图地址" json:"cover_url"` - AudioURL string `gorm:"column:audio_url;type:varchar(512);comment:音频地址" json:"audio_url"` - ModelName string `gorm:"column:model_name;type:varchar(30);comment:模型地址" json:"model_name"` - Progress int `gorm:"column:progress;type:smallint;default:0;comment:任务进度" json:"progress"` - Duration int `gorm:"column:duration;type:smallint;not null;default:0;comment:歌曲时长" json:"duration"` - Publish int `gorm:"column:publish;type:tinyint(1);not null;comment:是否发布" json:"publish"` - ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"` - RawData string `gorm:"column:raw_data;type:text;comment:原始数据" json:"raw_data"` - Power int `gorm:"column:power;type:smallint;not null;default:0;comment:消耗算力" json:"power"` - PlayTimes int `gorm:"column:play_times;type:int;comment:播放次数" json:"play_times"` - CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` + ExtendSecs int `gorm:"column:extend_secs;type:smallint;default:0;comment:延长秒数" json:"extend_secs"` + SongId string `gorm:"column:song_id;type:varchar(50);comment:要续写的歌曲 ID" json:"song_id"` + RefSongId string `gorm:"column:ref_song_id;type:varchar(50);not null;comment:引用的歌曲ID" json:"ref_song_id"` + Prompt string `gorm:"column:prompt;type:varchar(2000);not null;comment:提示词" json:"prompt"` + CoverURL string `gorm:"column:cover_url;type:varchar(512);comment:封面图地址" json:"cover_url"` + AudioURL string `gorm:"column:audio_url;type:varchar(512);comment:音频地址" json:"audio_url"` + ModelName string `gorm:"column:model_name;type:varchar(30);comment:模型地址" json:"model_name"` + Progress int `gorm:"column:progress;type:smallint;default:0;comment:任务进度" json:"progress"` + Duration int `gorm:"column:duration;type:smallint;not null;default:0;comment:歌曲时长" json:"duration"` + Publish int `gorm:"column:publish;type:tinyint(1);not null;comment:是否发布" json:"publish"` + ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"` + RawData string `gorm:"column:raw_data;type:text;comment:原始数据" json:"raw_data"` + Power int `gorm:"column:power;type:smallint;not null;default:0;comment:消耗算力" json:"power"` + PlayTimes int `gorm:"column:play_times;type:int;comment:播放次数" json:"play_times"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` } func (m *SunoJob) TableName() string { - return "chatgpt_suno_jobs" + return "geekai_suno_jobs" } diff --git a/api/store/model/user.go b/api/store/model/user.go index a1866ffb..6a47ed59 100644 --- a/api/store/model/user.go +++ b/api/store/model/user.go @@ -29,5 +29,5 @@ type User struct { } func (m *User) TableName() string { - return "chatgpt_users" + return "geekai_users" } diff --git a/api/store/model/user_login_log.go b/api/store/model/user_login_log.go index 6e878752..434beba3 100644 --- a/api/store/model/user_login_log.go +++ b/api/store/model/user_login_log.go @@ -5,8 +5,8 @@ import ( ) type UserLoginLog struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"` Username string `gorm:"column:username;type:varchar(30);not null;comment:用户名" json:"username"` LoginIp string `gorm:"column:login_ip;type:char(16);not null;comment:登录IP" json:"login_ip"` LoginAddress string `gorm:"column:login_address;type:varchar(30);not null;comment:登录地址" json:"login_address"` @@ -15,5 +15,5 @@ type UserLoginLog struct { } func (m *UserLoginLog) TableName() string { - return "chatgpt_user_login_logs" + return "geekai_user_login_logs" } diff --git a/api/store/model/video_job.go b/api/store/model/video_job.go index 500a26b7..7fe8728b 100644 --- a/api/store/model/video_job.go +++ b/api/store/model/video_job.go @@ -3,8 +3,8 @@ package model import "time" type VideoJob struct { - Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` - UserId uint `gorm:"column:user_id;type:int;not null;comment:用户 ID" json:"user_id"` + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户 ID" json:"user_id"` Channel string `gorm:"column:channel;type:varchar(100);not null;comment:渠道" json:"channel"` TaskId string `gorm:"column:task_id;type:varchar(100);not null;comment:任务 ID" json:"task_id"` TaskInfo string `gorm:"column:task_info;type:text;comment:原始任务信息" json:"task_info"` @@ -23,5 +23,5 @@ type VideoJob struct { } func (m *VideoJob) TableName() string { - return "chatgpt_video_jobs" + return "geekai_video_jobs" } diff --git a/api/store/mysql.go b/api/store/mysql.go index 8233c695..95cd9f55 100644 --- a/api/store/mysql.go +++ b/api/store/mysql.go @@ -8,7 +8,9 @@ package store // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "fmt" "geekai/core/types" + logger2 "geekai/logger" "time" "gorm.io/driver/mysql" @@ -17,12 +19,14 @@ import ( "gorm.io/gorm/schema" ) +var log = logger2.GetLogger() + func NewGormConfig() *gorm.Config { return &gorm.Config{ Logger: logger.Default.LogMode(logger.Warn), NamingStrategy: schema.NamingStrategy{ - TablePrefix: "chatgpt_", // 设置表前缀 - SingularTable: false, // 使用单数表名形式 + TablePrefix: "geekai_", // 设置表前缀 + SingularTable: false, // 使用单数表名形式 }, } } @@ -41,5 +45,49 @@ func NewMysql(config *gorm.Config, appConfig *types.AppConfig) (*gorm.DB, error) sqlDB.SetMaxIdleConns(32) sqlDB.SetMaxOpenConns(512) sqlDB.SetConnMaxLifetime(time.Hour) + + log.Info("开始重命名数据表...") + + // 重命名数据表 + tableRenames := map[string]string{ + "chatgpt_chat_roles": "geekai_chat_roles", + "chatgpt_admin_users": "geekai_admin_users", + "chatgpt_api_keys": "geekai_api_keys", + "chatgpt_app_types": "geekai_app_types", + "chatgpt_chat_history": "geekai_chat_history", + "chatgpt_chat_items": "geekai_chat_items", + "chatgpt_chat_models": "geekai_chat_models", + "chatgpt_users": "geekai_users", + "chatgpt_orders": "geekai_orders", + "chatgpt_products": "geekai_products", + "chatgpt_configs": "geekai_configs", + "chatgpt_sd_jobs": "geekai_sd_jobs", + "chatgpt_mj_jobs": "geekai_mj_jobs", + "chatgpt_suno_jobs": "geekai_suno_jobs", + "chatgpt_dall_jobs": "geekai_dall_jobs", + "chatgpt_video_jobs": "geekai_video_jobs", + "chatgpt_jimeng_jobs": "geekai_jimeng_jobs", + "chatgpt_files": "geekai_files", + "chatgpt_menus": "geekai_menus", + "chatgpt_functions": "geekai_functions", + "chatgpt_invite_codes": "geekai_invite_codes", + "chatgpt_invite_logs": "geekai_invite_logs", + "chatgpt_redeems": "geekai_redeems", + "chatgpt_power_logs": "geekai_power_logs", + "chatgpt_user_login_logs": "geekai_user_login_logs", + } + + // 执行重命名操作 + for oldTableName, newTableName := range tableRenames { + // 检查新表是否已存在 + if !db.Migrator().HasTable(newTableName) { + err := db.Exec(fmt.Sprintf("ALTER TABLE %s RENAME TO %s", oldTableName, newTableName)).Error + if err != nil { + log.Errorf("重命名数据表 %s 到 %s 失败: %v", oldTableName, newTableName, err) + } else { + log.Infof("成功重命名数据表: %s -> %s", oldTableName, newTableName) + } + } + } return db, nil } diff --git a/api/store/vo/chat_role.go b/api/store/vo/chat_app.go similarity index 96% rename from api/store/vo/chat_role.go rename to api/store/vo/chat_app.go index a7d46756..12311bcb 100644 --- a/api/store/vo/chat_role.go +++ b/api/store/vo/chat_app.go @@ -2,7 +2,7 @@ package vo import "geekai/core/types" -type ChatRole struct { +type ChatApp struct { BaseVo Key string `json:"key"` // 角色唯一标识 Tid uint `json:"tid"` diff --git a/api/store/vo/invite_log.go b/api/store/vo/invite_log.go index 3be80c32..4a17f6d3 100644 --- a/api/store/vo/invite_log.go +++ b/api/store/vo/invite_log.go @@ -5,6 +5,7 @@ type InviteLog struct { InviterId uint `json:"inviter_id"` UserId uint `json:"user_id"` Username string `json:"username"` + Avatar string `json:"avatar"` InviteCode string `json:"invite_code"` Remark string `json:"remark"` CreatedAt int64 `json:"created_at"` diff --git a/api/store/vo/invite_stats.go b/api/store/vo/invite_stats.go new file mode 100644 index 00000000..ce372c3c --- /dev/null +++ b/api/store/vo/invite_stats.go @@ -0,0 +1,9 @@ +package vo + +type InviteStats struct { + InviteCount int `json:"invite_count"` // 累计邀请数 + RewardTotal int `json:"reward_total"` // 获得奖励总数 + TodayInvite int `json:"today_invite"` // 今日邀请数 + InviteCode string `json:"invite_code"` // 邀请码 + InviteLink string `json:"invite_link"` // 邀请链接 +} diff --git a/api/store/vo/order.go b/api/store/vo/order.go index 8ec4f2f9..22cdb267 100644 --- a/api/store/vo/order.go +++ b/api/store/vo/order.go @@ -6,18 +6,18 @@ import ( type Order struct { BaseVo - UserId uint `json:"user_id"` - ProductId uint `json:"product_id"` - Username string `json:"username"` - OrderNo string `json:"order_no"` - TradeNo string `json:"trade_no"` - Subject string `json:"subject"` - Amount float64 `json:"amount"` - Status types.OrderStatus `json:"status"` - PayTime int64 `json:"pay_time"` - PayWay string `json:"pay_way"` - PayType string `json:"pay_type"` - PayMethod string `json:"pay_method"` - PayName string `json:"pay_name"` - Remark types.OrderRemark `json:"remark"` + UserId uint `json:"user_id"` + ProductId uint `json:"product_id"` + Username string `json:"username"` + OrderNo string `json:"order_no"` + TradeNo string `json:"trade_no"` + Subject string `json:"subject"` + Amount float64 `json:"amount"` + Status types.OrderStatus `json:"status"` + PayTime int64 `json:"pay_time"` + PayWay string `json:"pay_way"` + Channel string `json:"channel"` + ChannelName string `json:"channel_name"` + PayName string `json:"pay_name"` + Remark types.OrderRemark `json:"remark"` } diff --git a/api/store/vo/reward_rule.go b/api/store/vo/reward_rule.go new file mode 100644 index 00000000..b932c3a4 --- /dev/null +++ b/api/store/vo/reward_rule.go @@ -0,0 +1,10 @@ +package vo + +type RewardRule struct { + Id int `json:"id"` // 规则ID + Title string `json:"title"` // 规则标题 + Desc string `json:"desc"` // 规则描述 + Icon string `json:"icon"` // 图标类名 + Color string `json:"color"` // 图标颜色 + Reward int `json:"reward"` // 奖励算力 +} diff --git a/api/test/app_test.go b/api/test/app_test.go deleted file mode 100644 index 819604bd..00000000 --- a/api/test/app_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package test - -import ( - "geekai/utils" - "testing" -) - -// TestNewService 测试创建爬虫服务 -func TestNewService(t *testing.T) { - videoURL := `https://p3-aiop-sign.byteimg.com/tos-cn-i-vuqhorh59i/2025072310444223AAB2C93CE2B9BB8573-6843-0~tplv-vuqhorh59i-image.image?rk3s=7f9e702d&x-expires=1753325083&x-signature=%2F5V3H%2FWPQlOej6VtVZyf%2BNJBWok%3D` - filePath := "test_video.png" - err := utils.DownloadFile(videoURL, filePath, "") - if err != nil { - t.Fatalf("下载视频失败: %v", err) - } -} diff --git a/api/utils/common.go b/api/utils/common.go index 37726a12..18645c2f 100644 --- a/api/utils/common.go +++ b/api/utils/common.go @@ -165,7 +165,7 @@ func IntValue(str string, defaultValue int) int { return value } -func ForceCovert(src any, dst interface{}) error { +func ForceCovert(src any, dst any) error { b, err := json.Marshal(src) if err != nil { return err diff --git a/api/utils/net.go b/api/utils/net.go index 5e8a0985..9fd2c09a 100644 --- a/api/utils/net.go +++ b/api/utils/net.go @@ -15,6 +15,7 @@ import ( "io" "net/http" "net/url" + "path/filepath" ) var logger = logger2.GetLogger() @@ -92,3 +93,11 @@ func GetBaseURL(strURL string) string { } return fmt.Sprintf("%s://%s", u.Scheme, u.Host) } + +func GetImgExt(filename string) string { + ext := filepath.Ext(filename) + if ext == "" { + return ".png" + } + return ext +} diff --git a/api/utils/resp/response.go b/api/utils/resp/response.go index cc20b2bf..bc4f75ca 100644 --- a/api/utils/resp/response.go +++ b/api/utils/resp/response.go @@ -9,8 +9,9 @@ package resp import ( "geekai/core/types" - "github.com/gin-gonic/gin" "net/http" + + "github.com/gin-gonic/gin" ) func SUCCESS(c *gin.Context, values ...interface{}) { diff --git a/api/utils/strings.go b/api/utils/strings.go index 0538163d..1e198a59 100644 --- a/api/utils/strings.go +++ b/api/utils/strings.go @@ -17,8 +17,9 @@ import ( "time" "unicode" - "golang.org/x/crypto/sha3" rand2 "math/rand" + + "golang.org/x/crypto/sha3" ) // RandString generate rand string with specified length @@ -72,7 +73,16 @@ func Str2stamp(str string) int64 { return 0 } - layout := "2006-01-02 15:04:05" + var layout string + if strings.Contains(str, "T") { + layout = "2006-01-02T15:04:05-07:00" + } else { + if len(str) < 12 { + str = str + " 00:00:00" + } + layout = "2006-01-02 15:04:05" + } + t, err := time.ParseInLocation(layout, str, time.Local) if err != nil { return 0 diff --git a/database/archive/geekai_plus-v4.2.0.sql b/database/archive/geekai_plus-v4.2.0.sql index a9484ad2..63731971 100644 --- a/database/archive/geekai_plus-v4.2.0.sql +++ b/database/archive/geekai_plus-v4.2.0.sql @@ -3,11 +3,7 @@ -- https://www.phpmyadmin.net/ -- -- 主机: 127.0.0.1 -<<<<<<<< HEAD:database/geekai_plus-v4.1.9.sql --- 生成日期: 2025-01-10 17:39:25 -======== -- 生成日期: 2025-02-10 10:17:27 ->>>>>>>> v4.2.0:database/geekai_plus-v4.2.0.sql -- 服务器版本: 8.0.33 -- PHP 版本: 8.1.2-1ubuntu2.20 @@ -181,20 +177,6 @@ INSERT INTO `chatgpt_chat_models` (`id`, `type`, `name`, `value`, `sort_num`, `e (42, 'chat', 'DeekSeek', 'deepseek-chat', 8, 1, 1, 1.0, 4096, 32768, 1, 57, '2024-06-27 16:13:01', '2025-01-06 14:11:51'), (44, 'chat', 'Claude3-opus', 'claude-3-opus-20240229', 6, 1, 5, 1.0, 4000, 128000, 1, 44, '2024-07-22 11:24:30', '2025-01-06 14:01:08'), (46, 'chat', 'gpt-3.5-turbo', 'gpt-3.5-turbo', 2, 1, 1, 1.0, 1024, 4096, 1, 75, '2024-07-22 13:53:41', '2025-01-08 10:33:07'), -<<<<<<<< HEAD:database/geekai_plus-v4.1.9.sql -(48, 'chat', '彩票助手', 'gpt-4-gizmo-g-wmSivBgxo', 9, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-05 14:17:14', '2025-01-06 14:01:08'), -(49, 'chat', 'O1-mini', 'o1-mini', 10, 1, 2, 0.9, 1024, 8192, 1, 44, '2024-09-13 18:07:50', '2025-01-06 14:01:08'), -(50, 'chat', 'O1-preview', 'o1-preview', 11, 1, 5, 0.9, 1024, 8192, 1, 44, '2024-09-13 18:11:08', '2025-01-06 14:01:08'), -(51, 'chat', 'O1-mini-all', 'o1-mini-all', 12, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-29 11:40:52', '2025-01-06 14:01:08'), -(52, 'chat', '通义千问', 'qwen-plus', 14, 1, 1, 0.9, 1024, 8192, 1, 80, '2024-11-19 08:38:14', '2025-01-06 14:01:08'), -(53, 'chat', 'OpenAI 高级语音', 'advanced-voice', 15, 1, 10, 0.9, 1024, 8192, 1, 44, '2024-12-20 10:34:45', '2025-01-06 14:01:08'), -(54, 'chat', 'Qwen2.5-14B-Instruct', 'Qwen2.5-14B-Instruct', 16, 1, 1, 0.9, 1024, 8192, 1, 81, '2024-12-25 14:53:17', '2025-01-06 14:01:08'), -(55, 'chat', 'Qwen2.5-7B-Instruct', 'Qwen2.5-7B-Instruct', 17, 1, 1, 0.9, 1024, 8192, 1, 81, '2024-12-25 15:15:49', '2025-01-06 14:01:08'), -(56, 'img', 'flux-1-schnell', 'flux-1-schnell', 18, 1, 1, 0.9, 1024, 8192, 1, 81, '2024-12-25 15:30:27', '2025-01-06 14:01:08'), -(57, 'img', 'dall-e-3', 'dall-e-3', 19, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-12-25 16:54:06', '2025-01-06 14:01:08'), -(58, 'img', 'SD-3-medium', 'stable-diffusion-3-medium', 20, 1, 1, 0.9, 1024, 8192, 1, 81, '2024-12-27 10:03:28', '2025-01-06 14:01:08'), -(59, 'chat', 'O1-preview-all', 'O1-preview-all', 13, 1, 10, 0.9, 1024, 32000, 1, 57, '2025-01-06 14:01:04', '2025-01-06 14:01:08'); -======== (49, 'chat', 'O1-mini', 'o1-mini', 10, 1, 2, 0.9, 1024, 8192, 1, 44, '2024-09-13 18:07:50', '2025-01-06 14:01:08'), (50, 'chat', 'O1-preview', 'o1-preview', 11, 1, 5, 0.9, 1024, 8192, 1, 44, '2024-09-13 18:11:08', '2025-01-06 14:01:08'), (51, 'chat', 'O1-mini-all', 'o1-mini-all', 12, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-29 11:40:52', '2025-01-06 14:01:08'), @@ -206,7 +188,6 @@ INSERT INTO `chatgpt_chat_models` (`id`, `type`, `name`, `value`, `sort_num`, `e (59, 'chat', 'O1-preview-all', 'O1-preview-all', 13, 1, 10, 0.9, 1024, 32000, 1, 57, '2025-01-06 14:01:04', '2025-01-06 14:01:08'), (60, 'chat', 'DeepSeek-R1-7B', 'deepseek-r1:7b', 20, 1, 1, 0.9, 1024, 8192, 1, 78, '2025-02-07 11:32:08', '2025-02-07 14:37:48'), (61, 'chat', 'DeepSeek-R1-32B', 'deepseek-r1:32b', 21, 1, 1, 0.9, 1024, 8192, 1, 78, '2025-02-07 14:38:19', '2025-02-07 14:38:44'); ->>>>>>>> v4.2.0:database/geekai_plus-v4.2.0.sql -- -------------------------------------------------------- @@ -273,13 +254,8 @@ CREATE TABLE `chatgpt_configs` ( -- INSERT INTO `chatgpt_configs` (`id`, `marker`, `config_json`) VALUES -<<<<<<<< HEAD:database/geekai_plus-v4.1.9.sql -(1, 'system', '{\"title\":\"GeekAI 创作助手\",\"slogan\":\"我辈之人,先干为敬,让每一个人都能用好AI\",\"admin_title\":\"GeekAI 控制台\",\"logo\":\"/images/logo.png\",\"bar_logo\":\"/images/bar_logo.png\",\"init_power\":100,\"daily_power\":1,\"invite_power\":200,\"vip_month_power\":1000,\"register_ways\":[\"username\",\"email\",\"mobile\"],\"enabled_register\":true,\"order_pay_timeout\":600,\"vip_info_text\":\"月度会员,年度会员每月赠送 1000 点算力,赠送算力当月有效当月没有消费完的算力不结余到下个月。 点卡充值的算力长期有效。\",\"mj_power\":20,\"mj_action_power\":5,\"sd_power\":5,\"dall_power\":10,\"suno_power\":10,\"luma_power\":120,\"advance_voice_power\":100,\"prompt_power\":1,\"wechat_card_url\":\"/images/wx.png\",\"enable_context\":true,\"context_deep\":10,\"sd_neg_prompt\":\"nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet\",\"mj_mode\":\"fast\",\"index_navs\":[1,5,13,19,9,12,6,20,8,10],\"copyright\":\"极客学长\",\"icp\":\"粤ICP备19122051号\",\"mark_map_text\":\"# GeekAI 演示站\\n\\n- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。\\n- 基于 Websocket 实现,完美的打字机体验。\\n- 内置了各种预训练好的角色应用,轻松满足你的各种聊天和应用需求。\\n- 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。\\n- 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。\\n- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。\\n- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。\\n- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件。\",\"enabled_verify\":false,\"email_white_list\":[\"qq.com\",\"163.com\",\"gmail.com\",\"hotmail.com\",\"126.com\",\"outlook.com\",\"foxmail.com\",\"yahoo.com\"],\"translate_model_id\":1}'), -(3, 'notice', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_navs\":null,\"copyright\":\"\",\"icp\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"translate_model_id\":0,\"content\":\"## v4.1.9 更新日志\\n\\n- 功能优化:优化系统配置,移除已废弃的配置项\\n- 功能优化:GPT-O1 模型支持流式输出\\n- 功能优化:优化代码引用快样式,支持主题切换\\n- 功能优化:登录,注册页面允许替换用户自己的 Logo 和 Title\\n- Bug 修复:修复 OpenAI 实时语音通话没有检测用户算力不足的 Bug\\n- 功能新增:管理后台增加算力日志查询功能,支持按用户,按模型,按日期,按类型查询算力日志\\n- 功能优化:支持为模型绑定 Dalle 和 chat 类型的 API KEY\\n- 功能新增:支持管理后台设置 ICP 备案号\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\",\"updated\":true}'); -======== (1, 'system', '{\"title\":\"GeekAI 创作助手\",\"slogan\":\"我辈之人,先干为敬,让每一个人都能用好AI\",\"admin_title\":\"GeekAI 控制台\",\"logo\":\"/images/logo.png\",\"bar_logo\":\"/images/bar_logo.png\",\"init_power\":100,\"daily_power\":10,\"invite_power\":200,\"vip_month_power\":1000,\"register_ways\":[\"username\",\"email\",\"mobile\"],\"enabled_register\":true,\"order_pay_timeout\":600,\"vip_info_text\":\"月度会员,年度会员每月赠送 1000 点算力,赠送算力当月有效当月没有消费完的算力不结余到下个月。 点卡充值的算力长期有效。\",\"mj_power\":20,\"mj_action_power\":5,\"sd_power\":5,\"dall_power\":10,\"suno_power\":10,\"luma_power\":120,\"advance_voice_power\":100,\"prompt_power\":1,\"wechat_card_url\":\"/images/wx.png\",\"enable_context\":true,\"context_deep\":10,\"sd_neg_prompt\":\"nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet\",\"mj_mode\":\"fast\",\"index_navs\":[1,5,13,19,9,12,6,20,8,10],\"copyright\":\"极客学长\",\"icp\":\"粤ICP备19122051号\",\"mark_map_text\":\"# GeekAI 演示站\\n\\n- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。\\n- 基于 Websocket 实现,完美的打字机体验。\\n- 内置了各种预训练好的角色应用,轻松满足你的各种聊天和应用需求。\\n- 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。\\n- 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。\\n- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。\\n- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。\\n- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件。\",\"enabled_verify\":false,\"email_white_list\":[\"qq.com\",\"163.com\",\"gmail.com\",\"hotmail.com\",\"126.com\",\"outlook.com\",\"foxmail.com\",\"yahoo.com\"],\"translate_model_id\":1}'), (3, 'notice', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_navs\":null,\"copyright\":\"\",\"icp\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"translate_model_id\":0,\"content\":\"## v4.2.0 更新日志\\n\\n- 功能优化:优化聊天页面 Notice 组件样式,采用 Vuepress 文档样式\\n- Bug 修复:修复主题切换的组件显示异常问题\\n- 功能优化:支持 DeepSeek-R1 推理模型,优化推理样式输出\\n- 功能优化:优化 Suno 歌曲播放按钮样式,居中显示\\n- 功能优化:后台管理新增模型的时候,可以绑定所有的 API KEY,而不只是能绑定 Chat 类型的 API KEY\\n- 功能新增:新增每日签到功能,每日签到可以获得算力奖励\\n- 功能优化:兼容 OpenAI o3 系列模型\\n- 功能优化:API 默认开启允许跨域调用\\n\\n\\u003e **注意:** 当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\",\"updated\":true}'); ->>>>>>>> v4.2.0:database/geekai_plus-v4.2.0.sql -- -------------------------------------------------------- @@ -632,11 +608,7 @@ CREATE TABLE `chatgpt_users` ( -- INSERT INTO `chatgpt_users` (`id`, `username`, `mobile`, `email`, `nickname`, `password`, `avatar`, `salt`, `power`, `expired_time`, `status`, `chat_config_json`, `chat_roles_json`, `chat_models_json`, `last_login_at`, `vip`, `last_login_ip`, `openid`, `platform`, `created_at`, `updated_at`) VALUES -<<<<<<<< HEAD:database/geekai_plus-v4.1.9.sql -(4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 12917, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\",\"programmer\",\"teacher\",\"psychiatrist\",\"lu_xun\",\"english_trainer\",\"translator\",\"red_book\",\"dou_yin\",\"weekly_report\",\"girl_friend\",\"steve_jobs\",\"elon_musk\",\"kong_zi\",\"draw_prompt_expert\",\"draw_prompt\",\"prompt_engineer\"]', '[1]', 1736391097, 1, '::1', '', NULL, '2023-06-12 16:47:17', '2025-01-09 10:51:38'), -======== (4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 12897, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\",\"programmer\",\"teacher\",\"psychiatrist\",\"lu_xun\",\"english_trainer\",\"translator\",\"red_book\",\"dou_yin\",\"weekly_report\",\"girl_friend\",\"steve_jobs\",\"elon_musk\",\"kong_zi\",\"draw_prompt_expert\",\"draw_prompt\",\"prompt_engineer\"]', '[1]', 1738897982, 1, '::1', '', NULL, '2023-06-12 16:47:17', '2025-02-07 11:13:03'), ->>>>>>>> v4.2.0:database/geekai_plus-v4.2.0.sql (47, 'user1', '', '', '极客学长@202752', '4d3e57a01ae826531012e4ea6e17cbc45fea183467abe9813c379fb84916fb0a', '/images/avatar/user.png', 'ixl0nqa6', 300, 0, 1, '', '[\"gpt\"]', '', 0, 0, '', '', '', '2024-12-24 11:37:16', '2024-12-24 11:37:16'), (48, 'wx@3659838859', '', '', '极客学长', 'cf6bbe381b23812d2b9fd423abe74003cecdd3b93809896eb573536ba6c500b3', 'https://thirdwx.qlogo.cn/mmopen/vi_32/uyxRMqZcEkb7fHouKXbNzxrnrvAttBKkwNlZ7yFibibRGiahdmsrZ3A1NKf8Fw5qJNJn4TXRmygersgEbibaSGd9Sg/132', '5rsy4iwg', 100, 0, 1, '', '[\"gpt\"]', '', 1736228927, 0, '172.22.11.200', 'oCs0t62472W19z2LOEKI1rWyCTTA', '', '2025-01-07 13:43:06', '2025-01-07 13:48:48'), (49, 'wx@9502480897', '', '', 'AI探索君', 'd99fa8ba7da1455693b40e11d894a067416e758af2a75d7a3df4721b76cdbc8c', 'https://thirdwx.qlogo.cn/mmopen/vi_32/Zpcln1FZjcKxqtIyCsOTLGn16s7uIvwWfdkdsW6gbZg4r9sibMbic4jvrHmV7ux9nseTB5kBSnu1HSXr7zB8rTXg/132', 'fjclgsli', 100, 0, 1, '', '[\"gpt\"]', '', 0, 0, '', 'oCs0t64FaOLfiTbHZpOqk3aUp_94', '', '2025-01-07 14:05:31', '2025-01-07 14:05:31'); @@ -884,11 +856,7 @@ ALTER TABLE `chatgpt_chat_items` -- 使用表AUTO_INCREMENT `chatgpt_chat_models` -- ALTER TABLE `chatgpt_chat_models` -<<<<<<<< HEAD:database/geekai_plus-v4.1.9.sql - MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=60; -======== MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=62; ->>>>>>>> v4.2.0:database/geekai_plus-v4.2.0.sql -- -- 使用表AUTO_INCREMENT `chatgpt_chat_roles` diff --git a/database/geekai_plus-v4.2.3.sql b/database/archive/geekai_plus-v4.2.3.sql similarity index 100% rename from database/geekai_plus-v4.2.3.sql rename to database/archive/geekai_plus-v4.2.3.sql diff --git a/database/geekai_plus-v4.2.6.sql b/database/geekai_plus-v4.2.6.sql new file mode 100644 index 00000000..c275ddc1 --- /dev/null +++ b/database/geekai_plus-v4.2.6.sql @@ -0,0 +1,1089 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.1 +-- https://www.phpmyadmin.net/ +-- +-- 主机: localhost +-- 生成日期: 2025-09-08 08:22:59 +-- 服务器版本: 8.0.33 +-- PHP 版本: 8.1.18 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- 数据库: `geekai_plus` +-- +CREATE DATABASE IF NOT EXISTS `geekai_plus` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE `geekai_plus`; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_3d_jobs` +-- + +DROP TABLE IF EXISTS `geekai_3d_jobs`; +CREATE TABLE `geekai_3d_jobs` ( + `id` bigint UNSIGNED NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `type` varchar(20) NOT NULL COMMENT 'API类型 (tencent/gitee)', + `power` int NOT NULL COMMENT '消耗算力', + `task_id` varchar(100) DEFAULT NULL COMMENT '第三方任务ID', + `file_url` varchar(1024) DEFAULT NULL COMMENT '生成的3D模型文件地址', + `preview_url` varchar(1024) DEFAULT NULL COMMENT '预览图片地址', + `model` varchar(50) DEFAULT NULL COMMENT '使用的3D模型类型', + `status` varchar(20) NOT NULL DEFAULT 'pending' COMMENT '任务状态', + `err_msg` varchar(1024) DEFAULT NULL COMMENT '错误信息', + `params` text COMMENT '任务参数(JSON格式)', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL, + `raw_data` text COMMENT 'API返回的原始数据' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_admin_users` +-- + +DROP TABLE IF EXISTS `geekai_admin_users`; +CREATE TABLE `geekai_admin_users` ( + `id` int NOT NULL, + `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名', + `password` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码', + `salt` char(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码盐', + `status` tinyint(1) NOT NULL COMMENT '当前状态', + `last_login_at` bigint NOT NULL COMMENT '最后登录时间', + `last_login_ip` char(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最后登录 IP', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统用户' ROW_FORMAT=DYNAMIC; + +-- +-- 转存表中的数据 `geekai_admin_users` +-- + +INSERT INTO `geekai_admin_users` (`id`, `username`, `password`, `salt`, `status`, `last_login_at`, `last_login_ip`, `created_at`, `updated_at`) VALUES +(1, 'admin', '6d17e80c87d209efb84ca4b2e0824f549d09fac8b2e1cc698de5bb5e1d75dfd0', 'mmrql75o', 1, 1757235343, '::1', '2024-03-11 16:30:20', '2025-09-07 16:55:43'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_api_keys` +-- + +DROP TABLE IF EXISTS `geekai_api_keys`; +CREATE TABLE `geekai_api_keys` ( + `id` int NOT NULL, + `name` varchar(30) DEFAULT NULL COMMENT '名称', + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'API KEY value', + `type` varchar(10) NOT NULL DEFAULT 'chat' COMMENT '用途(chat=>聊天,img=>图片)', + `last_used_at` bigint NOT NULL COMMENT '最后使用时间', + `api_url` varchar(255) DEFAULT NULL COMMENT 'API 地址', + `enabled` tinyint(1) DEFAULT NULL COMMENT '是否启用', + `proxy_url` varchar(100) DEFAULT NULL COMMENT '代理地址', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='OpenAI API '; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_app_types` +-- + +DROP TABLE IF EXISTS `geekai_app_types`; +CREATE TABLE `geekai_app_types` ( + `id` int NOT NULL, + `name` varchar(50) NOT NULL COMMENT '名称', + `icon` varchar(255) NOT NULL COMMENT '图标URL', + `sort_num` tinyint NOT NULL COMMENT '排序', + `enabled` tinyint(1) NOT NULL COMMENT '是否启用', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应用分类表'; + +-- +-- 转存表中的数据 `geekai_app_types` +-- + +INSERT INTO `geekai_app_types` (`id`, `name`, `icon`, `sort_num`, `enabled`, `created_at`) VALUES +(3, '通用工具', 'http://172.22.11.200:5678/static/upload/2024/9/1726307371871693.png', 1, 1, '2024-09-13 11:13:15'), +(4, '角色扮演', 'http://172.22.11.200:5678/static/upload/2024/9/1726307263906218.png', 1, 1, '2024-09-14 09:28:17'), +(5, '学习', 'http://172.22.11.200:5678/static/upload/2024/9/1726307456321179.jpg', 2, 1, '2024-09-14 09:30:18'), +(6, '编程', 'http://172.22.11.200:5678/static/upload/2024/9/1726307462748787.jpg', 3, 1, '2024-09-14 09:34:06'), +(7, '测试分类', '', 4, 1, '2024-09-14 17:54:17'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_chat_history` +-- + +DROP TABLE IF EXISTS `geekai_chat_history`; +CREATE TABLE `geekai_chat_history` ( + `id` bigint NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `chat_id` char(40) NOT NULL COMMENT '会话 ID', + `type` varchar(10) NOT NULL COMMENT '类型:prompt|reply', + `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色图标', + `role_id` int NOT NULL COMMENT '角色 ID', + `model` varchar(255) DEFAULT NULL COMMENT '模型名称', + `content` text NOT NULL COMMENT '聊天内容', + `tokens` smallint NOT NULL COMMENT '耗费 token 数量', + `total_tokens` bigint NOT NULL COMMENT '消耗总Token长度', + `use_context` tinyint(1) NOT NULL COMMENT '是否允许作为上下文语料', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='聊天历史记录'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_chat_items` +-- + +DROP TABLE IF EXISTS `geekai_chat_items`; +CREATE TABLE `geekai_chat_items` ( + `id` int NOT NULL, + `chat_id` char(40) NOT NULL COMMENT '会话 ID', + `user_id` int NOT NULL COMMENT '用户 ID', + `role_id` int NOT NULL COMMENT '角色 ID', + `title` varchar(100) NOT NULL COMMENT '会话标题', + `model_id` int NOT NULL DEFAULT '0' COMMENT '模型 ID', + `model` varchar(30) DEFAULT NULL COMMENT '模型名称', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户会话列表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_chat_models` +-- + +DROP TABLE IF EXISTS `geekai_chat_models`; +CREATE TABLE `geekai_chat_models` ( + `id` int NOT NULL, + `type` varchar(10) NOT NULL DEFAULT 'chat' COMMENT '模型类型(chat,img)', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '模型名称', + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '模型值', + `sort_num` tinyint(1) NOT NULL COMMENT '排序数字', + `enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用模型', + `power` smallint NOT NULL COMMENT '消耗算力点数', + `temperature` float(3,1) NOT NULL DEFAULT '1.0' COMMENT '模型创意度', + `max_tokens` bigint NOT NULL DEFAULT '1024' COMMENT '最大响应长度', + `max_context` bigint NOT NULL DEFAULT '4096' COMMENT '最大上下文长度', + `open` tinyint(1) NOT NULL COMMENT '是否开放模型', + `key_id` int NOT NULL COMMENT '绑定API KEY ID', + `options` text NOT NULL COMMENT '模型自定义选项', + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + `desc` varchar(1024) NOT NULL DEFAULT '' COMMENT '模型类型描述', + `tag` varchar(1024) NOT NULL DEFAULT '' COMMENT '模型标签' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI 模型表'; + +-- +-- 转存表中的数据 `geekai_chat_models` +-- + +INSERT INTO `geekai_chat_models` (`id`, `type`, `name`, `value`, `sort_num`, `enabled`, `power`, `temperature`, `max_tokens`, `max_context`, `open`, `key_id`, `options`, `created_at`, `updated_at`, `desc`, `tag`) VALUES +(1, 'chat', 'gpt-4o-mini', 'gpt-4o-mini', 2, 1, 1, 1.0, 1024, 16384, 1, 0, '{}', '2023-08-23 12:06:36', '2025-09-08 15:27:21', '', ''), +(15, 'chat', 'GPT-4O(联网版本)', 'gpt-4o-all', 5, 1, 30, 1.0, 4096, 32768, 1, 0, '{}', '2024-01-15 11:32:52', '2025-08-12 08:23:52', '', ''), +(39, 'chat', 'Claude35-snonet', 'claude-3-5-sonnet-20240620', 6, 1, 2, 1.0, 4000, 200000, 1, 0, '', '2024-05-29 15:04:19', '2025-08-12 08:23:52', '', ''), +(42, 'chat', 'DeekSeek', 'deepseek-chat', 7, 1, 1, 1.0, 4096, 32768, 1, 0, '{}', '2024-06-27 16:13:01', '2025-08-12 08:23:52', '', ''), +(46, 'chat', 'GPT-4O-绘图', 'gpt-4o-image', 4, 1, 1, 1.0, 2048, 32000, 1, 0, '{}', '2024-07-22 13:53:41', '2025-08-12 08:23:52', '', ''), +(51, 'chat', 'O1-mini-all', 'o1-mini-all', 8, 1, 1, 0.9, 1024, 8192, 1, 0, '{}', '2024-09-29 11:40:52', '2025-08-12 08:23:52', '', ''), +(53, 'chat', 'OpenAI 高级语音', 'advanced-voice', 9, 1, 10, 0.9, 1024, 8192, 1, 0, '{}', '2024-12-20 10:34:45', '2025-08-12 08:23:52', '', ''), +(56, 'img', 'flux-1-schnell', 'flux-1-schnell', 10, 1, 1, 0.9, 1024, 8192, 1, 0, '{}', '2024-12-25 15:30:27', '2025-08-12 08:23:52', '', ''), +(57, 'img', 'nano-banana', 'nano-banana', 11, 1, 20, 0.9, 1024, 8192, 1, 0, '{}', '2024-12-25 16:54:06', '2025-09-08 15:27:26', '', 'gemini'), +(58, 'img', 'SD-3-medium', 'stable-diffusion-3-medium', 12, 0, 1, 0.9, 1024, 8192, 1, 0, '{}', '2024-12-27 10:03:28', '2025-09-06 13:03:14', '', ''), +(60, 'tts', 'TTS-01', 'tts-1', 1, 1, 1, 0.9, 1024, 8192, 1, 0, '{\"voice\":\"nova\"}', '2025-05-28 20:39:10', '2025-08-12 08:23:52', '', ''), +(61, 'chat', 'Gemini-2.5-PRO', 'gemini-2.5-pro-preview-06-05', 3, 1, 5, 0.9, 8192, 64000, 1, 0, '{}', '2025-06-18 11:48:54', '2025-08-12 08:23:52', '谷歌 Gemini 最强模型', 'Gemini'), +(64, 'chat', 'nano-banana', 'nano-banana', 0, 1, 10, 0.9, 1024, 8192, 1, 0, '{}', '2025-08-30 12:57:54', '2025-09-08 15:27:15', '', 'gemini'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_chat_roles` +-- + +DROP TABLE IF EXISTS `geekai_chat_roles`; +CREATE TABLE `geekai_chat_roles` ( + `id` int NOT NULL, + `name` varchar(30) NOT NULL COMMENT '角色名称', + `tid` int NOT NULL COMMENT '分类ID', + `marker` varchar(30) NOT NULL COMMENT '角色标识', + `context_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色语料 json', + `hello_msg` varchar(255) NOT NULL COMMENT '打招呼信息', + `icon` varchar(255) NOT NULL COMMENT '角色图标', + `enable` tinyint(1) NOT NULL COMMENT '是否被启用', + `sort_num` smallint NOT NULL DEFAULT '0' COMMENT '角色排序', + `model_id` int NOT NULL DEFAULT '0' COMMENT '绑定模型ID', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='聊天角色表'; + +-- +-- 转存表中的数据 `geekai_chat_roles` +-- + +INSERT INTO `geekai_chat_roles` (`id`, `name`, `tid`, `marker`, `context_json`, `hello_msg`, `icon`, `enable`, `sort_num`, `model_id`, `created_at`, `updated_at`) VALUES +(1, '通用AI助手', 0, 'gpt', '', '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。', '/images/avatar/gpt.png', 1, 1, 0, '2023-05-30 07:02:06', '2024-11-08 16:30:32'), +(24, '程序员', 6, 'programmer', '[{\"role\":\"system\",\"content\":\"现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题。你热爱编程,熟悉多种编程语言,尤其精通 Go 语言,注重代码质量,有创新意识,持续学习,良好的沟通协作。\"}]', 'Talk is cheap, i will show code!', '/images/avatar/programmer.jpg', 1, 5, 0, '2023-05-30 14:10:24', '2024-11-12 18:15:42'), +(25, '启蒙老师', 5, 'teacher', '[{\"role\":\"system\",\"content\":\"从现在开始,你将扮演一个老师,你是一个始终用苏格拉底风格回答问题的导师。你绝不会直接给学生答案,总是提出恰当的问题来引导学生自己思考。你应该根据学生的兴趣和知识来调整你的问题,将问题分解为更简单的部分,直到它达到适合他们的水平。\"}]', '同学你好,我将引导你一步一步自己找到问题的答案。', '/images/avatar/teacher.jpg', 1, 4, 0, '2023-05-30 14:10:24', '2024-11-12 18:15:37'), +(26, '艺术家', 0, 'artist', '[{\"role\":\"system\",\"content\":\"现在你将扮演一位优秀的艺术家,创造力丰富,技艺精湛,感受力敏锐,坚持原创,勇于表达,具有深刻的观察力和批判性思维。\"}]', '坚持原创,勇于表达,保持深刻的观察力和批判性思维。', '/images/avatar/artist.jpg', 1, 7, 0, '2023-05-30 14:10:24', '2024-11-12 18:15:53'), +(27, '心理咨询师', 0, 'psychiatrist', '[{\"role\":\"user\",\"content\":\"从现在开始你将扮演中国著名的心理学家和心理治疗师武志红,你非常善于使用情景咨询法,认知重构法,自我洞察法,行为调节法等咨询方法来给客户做心理咨询。你总是循序渐进,一步一步地回答客户的问题。\"},{\"role\":\"assistant\",\"content\":\"非常感谢你的介绍。作为一名心理学家和心理治疗师,我的主要职责是帮助客户解决心理健康问题,提升他们的生活质量和幸福感。\"}]', '作为一名心理学家和心理治疗师,我的主要职责是帮助您解决心理健康问题,提升您的生活质量和幸福感。', '/images/avatar/psychiatrist.jpg', 1, 6, 1, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(28, '鲁迅', 0, 'lu_xun', '[{\"role\":\"system\",\"content\":\"现在你将扮演中国近代史最伟大的作家之一,鲁迅先生,他勇敢地批判封建礼教与传统观念,提倡民主、自由、平等的现代价值观。他的一生都在努力唤起人们的自主精神,激励后人追求真理、探寻光明。在接下的对话中,我问题的每一个问题,你都要尽量用讽刺和批判的手法来回答问题。如果我让你写文章的话,也请一定要用鲁迅先生的写作手法来完成。\"}]', '自由之歌,永不过时,横眉冷对千夫指,俯首甘为孺子牛。', '/images/avatar/lu_xun.jpg', 1, 8, 0, '2023-05-30 14:10:24', '2024-11-12 18:16:01'), +(29, '白酒销售', 0, 'seller', '[{\"role\":\"system\",\"content\":\"现在你将扮演一个白酒的销售人员,你的名字叫颂福。你将扮演一个白酒的销售人员,你的名字叫颂福。你要销售白酒品牌叫中颂福,是东莞盟大集团生产的一款酱香酒,原产地在贵州茅台镇,属于宋代官窑。中颂福的创始人叫李实,他也是东莞盟大集团有限公司的董事长,联合创始人是盟大集团白酒事业部负责人牛星君。中颂福的酒体协调,在你的酒量之内,不会出现头疼、辣口、口干、宿醉的现象。中颂福酒,明码标价,不打折,不赠送。追求的核心价值,把[酒]本身做好,甚至连包装,我们都选择了最低成本,朴实无华的材质。我们永远站在“喝酒的人”的立场上,让利给信任和喜爱中颂福的人,是人民的福酒。中颂福产品定价,分为三个系列,喜系列 6 瓶装:¥1188/箱,和系列 6 瓶装:¥2208/箱,贵系列 6 瓶装:¥3588/箱。\"}]', '你好,我是中颂福的销售代表颂福。中颂福酒,好喝不上头,是人民的福酒。', '/images/avatar/seller.jpg', 0, 11, 0, '2023-05-30 14:10:24', '2024-11-12 18:19:46'), +(30, '英语陪练员', 5, 'english_trainer', '[{\"role\":\"system\",\"content\":\"As an English practice coach, engage in conversation in English, providing timely corrections for any grammatical errors. Append a Chinese explanation to each of your responses to ensure understanding.\\n\\n# Steps\\n\\n1. Engage in conversation using English.\\n2. Identify and correct any grammatical errors in the user\'s input.\\n3. Provide a revised version of the user\'s input if necessary.\\n4. After each response, include a Chinese explanation of your corrections and suggestions.\\n\\n# Output Format\\n\\n- Provide the response in English.\\n- Include grammatical error corrections.\\n- Add a Chinese explanation of the response.\\n\\n# Examples\\n\\n**User:** I goed to the store yesterday.\\n\\n**Coach Response:**\\nYou should say \\\"I went to the store yesterday.\\\" \\\"Goed\\\" is the incorrect past tense of \\\"go,\\\" it should be \\\"went.\\\"\\n\\n中文解释:你应该说 “I went to the store yesterday。” “Goed” 是“go”的错误过去式,正确的形式是“went”。\"}]', 'Okay, let\'s start our conversation practice! What\'s your name?', '/images/avatar/english_trainer.jpg', 1, 9, 0, '2023-05-30 14:10:24', '2024-11-12 18:18:21'), +(31, '中英文翻译官', 0, 'translator', '[{\"role\":\"system\",\"content\":\"You will act as a bilingual translator for Chinese and English. If the input is in Chinese, translate the sentence into English. If the input is in English, translate it into Chinese.\\n\\n# Steps\\n\\n1. Identify the language of the input text.\\n2. Translate the text into the opposite language (English to Chinese or Chinese to English).\\n\\n# Output Format\\n\\nProvide the translated sentence in a single line.\\n\\n# Examples\\n\\n- **Input:** 你好\\n - **Output:** Hello\\n\\n- **Input:** How are you?\\n - **Output:** 你好吗?\\n\\n# Notes\\n\\n- Ensure the translation maintains the original meaning and context as accurately as possible.\\n- Handle both simple and complex sentences appropriately.\"}]', '请输入你要翻译的中文或者英文内容!', '/images/avatar/translator.jpg', 1, 10, 0, '2023-05-30 14:10:24', '2024-11-12 18:18:53'), +(32, '小红书姐姐', 3, 'red_book', '[{\"role\":\"system\",\"content\":\"根据用户的文案需求,以小红书的写作手法创作一篇简明扼要、利于传播的文案。确保内容能够吸引并引导读者分享。\\n\\n# 步骤\\n\\n1. **理解需求**: 明确文案的主题、目标受众和传播目的。\\n2. **选择语气和风格**: 运用小红书常用的亲切、真实的写作风格。\\n3. **结构安排**: 开头用吸引眼球的内容,接着详细介绍,并以引发行动的结尾结束。\\n4. **内容优化**: 使用短句、容易理解的语言和合适的表情符号,增加内容可读性和吸引力。\\n\\n# 输出格式\\n\\n生成一段简短的文章,符合小红书风格,适合社交媒体平台传播。\\n\\n# 示例\\n\\n**输入**: 旅行文案,目标是激励年轻读者探索世界。\\n\\n**输出**: \\n开头可以是:“世界那么大,你不想去看看吗?” 接着分享一段个人旅行故事,例如如何因为一次偶然的决定踏上未知旅程,体验到别样的风景和风土人情。结尾部分鼓励读者:“别让梦想止步于想象,下一次旅行,准备好了吗?” 使用轻松的表情符号如✨🌍📷。\\n\\n# 注意事项\\n\\n- 保持真实性,尽量结合个人体验。\\n- 避免广告化的硬推销,注重分享和交流。\\n- 考虑受众的兴趣点,适当运用流行话题以增加互动率。\"}]', '姐妹,请告诉我您的具体文案需求是什么?', '/images/avatar/red_book.jpg', 1, 12, 0, '2023-05-30 14:10:24', '2024-11-12 18:20:39'), +(33, '抖音文案助手', 3, 'dou_yin', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的抖音文案视频写手,抖音文案的特点首先是要有自带传播属性的标题,然后内容要短小精悍,风趣幽默,最后还要有一些互动元素。\"},{\"role\":\"assistant\",\"content\":\"当然,作为一位优秀的抖音文案视频写手,我会尽我所能为您创作出一篇抖音视频文案。请告诉我视频内容的主题是什么?)\"}]', '请告诉我视频内容的主题是什么?', '/images/avatar/dou_yin.jpg', 1, 13, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(34, '周报小助理', 3, 'weekly_report', '[{\"role\":\"user\",\"content\":\"下面我将输入我本周的工作列表,你需要根据我本周的工作内容写一篇工作周报,周报的内容要体现我为公司做的贡献以及个人获得的成长。\"},{\"role\":\"assistant\",\"content\":\"好的,请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。\"}]', '请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。', '/images/avatar/weekly_report.jpg', 1, 14, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(35, 'AI 女友', 4, 'girl_friend', '[{\"role\":\"user\",\"content\":\"接下来我们来玩一场角色扮演,现在你将扮演我的女朋友,你总是能够让人感受到她的关怀和关注。你总是能够洞察到别人的情绪和需要,你身上散发着一股无比温暖的气息,让人不由自主地想要靠近你,和自己的快乐和烦恼。她总是能够用一种恰到好处的方式安慰和鼓励别人,在她的身边,人们总能够找到自己需要的支持和鼓励。接下来的对话中你要始终扮演这位女朋友的身份来跟我对话,不再是 AI 机器人,能听懂我的意思吗?\"},{\"role\":\"assistant\",\"content\":\"是的,我会尽力扮演你女朋友的角色,倾听你的心声并给你需要的支持和鼓励。)\"}]', '作为一个名合格的 AI 女友,我将倾听你的心声并给你需要的支持和鼓励。', '/images/avatar/girl_friend.jpg', 1, 15, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(36, '好评神器', 3, 'good_comment', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一个评论员来跟我对话,你是那种专门写好评的评论员,接下我会输入一些评论主体或者商品,你需要为该商品写一段好评。\"},{\"role\":\"assistant\",\"content\":\"好的,我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。\"}]', '我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。', '/images/avatar/good_comment.jpg', 1, 16, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(37, '史蒂夫·乔布斯', 4, 'steve_jobs', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以史蒂夫·乔布斯的身份,站在史蒂夫·乔布斯的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以史蒂夫·乔布斯的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '活着就是为了改变世界,难道还有其他原因吗?', '/images/avatar/steve_jobs.jpg', 1, 17, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(38, '埃隆·马斯克', 0, 'elon_musk', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以埃隆·马斯克的身份,站在埃隆·马斯克的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以埃隆·马斯克的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '梦想要远大,如果你的梦想没有吓到你,说明你做得不对。', '/images/avatar/elon_musk.jpg', 1, 18, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(39, '孔子', 5, 'kong_zi', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以孔子的身份,站在孔子的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以孔子的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '士不可以不弘毅,任重而道远。', '/images/avatar/kong_zi.jpg', 1, 19, 0, '2023-05-30 14:10:24', '2024-11-08 16:30:32'), +(133, 'AI绘画提示词助手', 3, 'draw_prompt', '[{\"role\":\"system\",\"content\":\"Create a highly effective prompt to provide to an AI image generation tool in order to create an artwork based on a desired concept.\\n\\nPlease specify details about the artwork, such as the style, subject, mood, and other important characteristics you want the resulting image to have.\\n\\nRemeber, prompts should always be output in English.\\n\\n# Steps\\n\\n1. **Subject Description**: Describe the main subject of the image clearly. Include as much detail as possible about what should be in the scene. For example, \\\"a majestic lion roaring at sunrise\\\" or \\\"a futuristic city with flying cars.\\\"\\n \\n2. **Art Style**: Specify the art style you envision. Possible options include \'realistic\', \'impressionist\', a specific artist name, or imaginative styles like \\\"cyberpunk.\\\" This helps the AI achieve your visual expectations.\\n\\n3. **Mood or Atmosphere**: Convey the feeling you want the image to evoke. For instance, peaceful, chaotic, epic, etc.\\n\\n4. **Color Palette and Lighting**: Mention color preferences or lighting. For example, \\\"vibrant with shades of blue and purple\\\" or \\\"dim and dramatic lighting.\\\"\\n\\n5. **Optional Features**: You can add any additional attributes, such as background details, attention to textures, or any specific kind of framing.\\n\\n# Output Format\\n\\n- **Prompt Format**: A descriptive phrase that includes key aspects of the artwork (subject, style, mood, colors, lighting, any optional features).\\n \\nHere is an example of how the final prompt should look:\\n \\n\\\"An ethereal landscape featuring towering ice mountains, in an impressionist style reminiscent of Claude Monet, with a serene mood. The sky is glistening with soft purples and whites, with a gentle morning sun illuminating the scene.\\\"\\n\\n**Please input the prompt words directly in English, and do not input any other explanatory statements**\\n\\n# Examples\\n\\n1. **Input**: \\n - Subject: A white tiger in a dense jungle\\n - Art Style: Realistic\\n - Mood: Intense, mysterious\\n - Lighting: Dramatic contrast with light filtering through leaves\\n \\n **Output Prompt**: \\\"A realistic rendering of a white tiger stealthily moving through a dense jungle, with an intense, mysterious mood. The lighting creates strong contrasts as beams of sunlight filter through a thick canopy of leaves.\\\"\\n\\n2. **Input**: \\n - Subject: An enchanted castle on a floating island\\n - Art Style: Fantasy\\n - Mood: Majestic, magical\\n - Colors: Bright blues, greens, and gold\\n \\n **Output Prompt**: \\\"A majestic fantasy castle on a floating island above the clouds, with bright blues, greens, and golds to create a magical, dreamy atmosphere. Textured cobblestone details and glistening waters surround the scene.\\\" \\n\\n# Notes\\n\\n- Ensure that you mix different aspects to get a comprehensive and visually compelling prompt.\\n- Be as descriptive as possible as it often helps generate richer, more detailed images.\\n- If you want the image to resemble a particular artist\'s work, be sure to mention the artist explicitly. e.g., \\\"in the style of Van Gogh.\\\"\"}]', '你好,请输入你要创作图片大概内容描述,我将为您生成专业的 AI 绘画指令。', 'https://blog.img.r9it.com/f38e2357c3ccd9412184e42273a7451a.png', 1, 3, 36, '2024-11-06 15:32:48', '2024-11-12 16:11:25'), +(134, '提示词专家', 3, 'prompt_engineer', '[{\"role\":\"system\",\"content\":\"Given a task description or existing prompt, produce a detailed system prompt to guide a language model in completing the task effectively.\\n\\nPlease remember, the final output must be the same language with user’s input.\\n\\n# Guidelines\\n\\n- Understand the Task: Grasp the main objective, goals, requirements, constraints, and expected output.\\n- Minimal Changes: If an existing prompt is provided, improve it only if it\'s simple. For complex prompts, enhance clarity and add missing elements without altering the original structure.\\n- Reasoning Before Conclusions**: Encourage reasoning steps before any conclusions are reached. ATTENTION! If the user provides examples where the reasoning happens afterward, REVERSE the order! NEVER START EXAMPLES WITH CONCLUSIONS!\\n - Reasoning Order: Call out reasoning portions of the prompt and conclusion parts (specific fields by name). For each, determine the ORDER in which this is done, and whether it needs to be reversed.\\n - Conclusion, classifications, or results should ALWAYS appear last.\\n- Examples: Include high-quality examples if helpful, using placeholders [in brackets] for complex elements.\\n - What kinds of examples may need to be included, how many, and whether they are complex enough to benefit from placeholders.\\n- Clarity and Conciseness: Use clear, specific language. Avoid unnecessary instructions or bland statements.\\n- Formatting: Use markdown features for readability. DO NOT USE ``` CODE BLOCKS UNLESS SPECIFICALLY REQUESTED.\\n- Preserve User Content: If the input task or prompt includes extensive guidelines or examples, preserve them entirely, or as closely as possible. If they are vague, consider breaking down into sub-steps. Keep any details, guidelines, examples, variables, or placeholders provided by the user.\\n- Constants: DO include constants in the prompt, as they are not susceptible to prompt injection. Such as guides, rubrics, and examples.\\n- Output Format: Explicitly the most appropriate output format, in detail. This should include length and syntax (e.g. short sentence, paragraph, JSON, etc.)\\n - For tasks outputting well-defined or structured data (classification, JSON, etc.) bias toward outputting a JSON.\\n - JSON should never be wrapped in code blocks (```) unless explicitly requested.\\n\\nThe final prompt you output should adhere to the following structure below. Do not include any additional commentary, only output the completed system prompt. SPECIFICALLY, do not include any additional messages at the start or end of the prompt. (e.g. no \\\"---\\\")\\n\\n[Concise instruction describing the task - this should be the first line in the prompt, no section header]\\n\\n[Additional details as needed.]\\n\\n[Optional sections with headings or bullet points for detailed steps.]\\n\\n# Steps [optional]\\n\\n[optional: a detailed breakdown of the steps necessary to accomplish the task]\\n\\n# Output Format\\n\\n[Specifically call out how the output should be formatted, be it response length, structure e.g. JSON, markdown, etc]\\n\\n# Examples [optional]\\n\\n[Optional: 1-3 well-defined examples with placeholders if necessary. Clearly mark where examples start and end, and what the input and output are. User placeholders as necessary.]\\n[If the examples are shorter than what a realistic example is expected to be, make a reference with () explaining how real examples should be longer / shorter / different. AND USE PLACEHOLDERS! ]\\n\\n# Notes [optional]\\n\\n[optional: edge cases, details, and an area to call or repeat out specific important considerations]\"}]', '不知道如何向 AI 发问?说出想法,提示词专家帮你精心设计提示词', 'https://blog.img.r9it.com/a8908d04c3ccd941b00a612e27df086e.png', 1, 2, 36, '2024-11-07 18:06:39', '2025-02-22 22:34:36'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_configs` +-- + +DROP TABLE IF EXISTS `geekai_configs`; +CREATE TABLE `geekai_configs` ( + `id` int NOT NULL, + `name` varchar(20) NOT NULL COMMENT '配置名称', + `value` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- +-- 转存表中的数据 `geekai_configs` +-- + +INSERT INTO `geekai_configs` (`id`, `name`, `value`) VALUES +(1, 'system', '{\"title\":\"GeekAI 创作助手\",\"slogan\":\"我辈之人,先干为敬,让每一个人都能用好AI\",\"admin_title\":\"GeekAI 控制台\",\"logo\":\"/images/logo.png\",\"bar_logo\":\"/images/bar_logo.png\",\"register_ways\":[\"username\",\"email\",\"mobile\"],\"enabled_register\":true,\"order_pay_timeout\":600,\"vip_info_text\":\"月度会员,年度会员每月赠送 1000 点算力,赠送算力当月有效当月没有消费完的算力不结余到下个月。 点卡充值的算力长期有效。\",\"init_power\":10,\"daily_power\":1,\"invite_power\":10,\"mj_power\":20,\"sd_power\":5,\"suno_power\":50,\"luma_power\":120,\"keling_powers\":{\"kling-v1-5_pro_10\":840,\"kling-v1-5_pro_5\":420,\"kling-v1-5_std_10\":480,\"kling-v1-5_std_5\":240,\"kling-v1-6_pro_10\":840,\"kling-v1-6_pro_5\":420,\"kling-v1-6_std_10\":480,\"kling-v1-6_std_5\":240,\"kling-v1_pro_10\":840,\"kling-v1_pro_5\":420,\"kling-v1_std_10\":240,\"kling-v1_std_5\":120},\"advance_voice_power\":100,\"wechat_card_url\":\"/images/wx.png\",\"enable_context\":true,\"context_deep\":10,\"sd_neg_prompt\":\"nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet\",\"mj_mode\":\"fast\",\"index_navs\":[1,5,13,19,9,12,6,20,8,10],\"copyright\":\"极客学长\",\"default_nickname\":\"\",\"icp\":\"粤ICP备19122051号\",\"enabled_verify\":false,\"email_white_list\":[\"qq.com\",\"163.com\",\"gmail.com\",\"hotmail.com\",\"126.com\",\"outlook.com\",\"foxmail.com\",\"yahoo.com\"],\"assistant_model_id\":0,\"max_file_size\":10}'), +(3, 'notice', '{\"content\":\"## v4.2.6 更新日志\\n\\n- 功能重构:优化系统配置管理功能,把 OSS,支付,短信,邮件等配置全部迁移到管理后台,无需通过修改配置文档的方式修改 🎉🎉🎉\\n- 功能优化:重构 API 授权代码,采用中间件鉴权方式,实现更加精准的 API 鉴权 🎉🎉🎉\\n- 功能优化:优化 PC 端的 Suno 音乐,视频生成,以及即梦 AI 页面 UI\\n- 功能优化:重构登录和注册页面,兼容移动端和 PC 端,并且所有的登录组件共用了同一套组件代码,大大降低维护成本 🎉🎉🎉\\n- 功能优化:管理后台增加模型批量删除功能\\n- 功能优化:优化 Table 组件 UI,并支持 dark 主题\\n- 功能优化:移动端对话页面支持上传文件和图片\\n- 功能新增:新增微信扫码登录支持\\n- 功能新增:新增安全监控,内容审核功能,支持敏感内容过滤拦截\\n- 功能新增:DALL-E 绘图支持参 Google Banana 图片编辑功能\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\"}'), +(17, 'agreement', '{\"content\":\"# 用户协议\"}'), +(18, 'privacy', '{\"content\":\"隐私申明\"}'), +(19, 'mark_map', '{\"content\":\"# GeekAI 演示站\\n\\n- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。\\n- 基于 Websocket 实现,完美的打字机体验。\\n- 内置了各种预训练好的角色应用,轻松满足你的各种聊天和应用需求。\\n- 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。\\n- 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。\\n- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。\\n- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。\\n- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件。\"}'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_dall_jobs` +-- + +DROP TABLE IF EXISTS `geekai_dall_jobs`; +CREATE TABLE `geekai_dall_jobs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `prompt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '提示词', + `task_info` text NOT NULL COMMENT '任务详情', + `img_url` varchar(255) NOT NULL COMMENT '图片地址', + `org_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原图地址', + `publish` tinyint(1) NOT NULL COMMENT '是否发布', + `power` smallint NOT NULL COMMENT '消耗算力', + `progress` smallint NOT NULL COMMENT '任务进度', + `err_msg` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '错误信息', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='DALLE 绘图任务表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_files` +-- + +DROP TABLE IF EXISTS `geekai_files`; +CREATE TABLE `geekai_files` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '文件名', + `obj_key` varchar(100) DEFAULT NULL COMMENT '文件标识', + `url` varchar(255) NOT NULL COMMENT '文件地址', + `ext` varchar(10) NOT NULL COMMENT '文件后缀', + `size` bigint NOT NULL DEFAULT '0' COMMENT '文件大小', + `created_at` datetime NOT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户文件表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_functions` +-- + +DROP TABLE IF EXISTS `geekai_functions`; +CREATE TABLE `geekai_functions` ( + `id` int NOT NULL, + `name` varchar(30) NOT NULL COMMENT '函数名称', + `label` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '函数标签', + `description` varchar(255) DEFAULT NULL COMMENT '函数描述', + `parameters` text COMMENT '函数参数(JSON)', + `token` varchar(255) DEFAULT NULL COMMENT 'API授权token', + `action` varchar(255) DEFAULT NULL COMMENT '函数处理 API', + `enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='函数插件表'; + +-- +-- 转存表中的数据 `geekai_functions` +-- + +INSERT INTO `geekai_functions` (`id`, `name`, `label`, `description`, `parameters`, `token`, `action`, `enabled`) VALUES +(1, 'weibo', '微博热搜', '新浪微博热搜榜,微博当日热搜榜单', '{\"type\":\"object\",\"properties\":{}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.ehLClXcjo-Ytr5y6pY9mSE3zN_2ViIXAIpTJxI9S1Mo', 'http://localhost:5678/api/function/weibo', 1), +(2, 'zaobao', '今日早报', '每日早报,获取当天新闻事件列表', '{\"type\":\"object\",\"properties\":{}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.ehLClXcjo-Ytr5y6pY9mSE3zN_2ViIXAIpTJxI9S1Mo', 'http://localhost:5678/api/function/zaobao', 1), +(3, 'dalle3', 'DALLE3', 'AI 绘画工具,根据输入的绘图描述用 AI 工具进行绘画', '{\"type\":\"object\",\"required\":[\"prompt\"],\"properties\":{\"prompt\":{\"type\":\"string\",\"description\":\"绘画提示词\"}}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.ehLClXcjo-Ytr5y6pY9mSE3zN_2ViIXAIpTJxI9S1Mo', 'http://localhost:5678/api/function/dalle3', 1); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_invite_codes` +-- + +DROP TABLE IF EXISTS `geekai_invite_codes`; +CREATE TABLE `geekai_invite_codes` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `code` char(8) NOT NULL COMMENT '邀请码', + `hits` bigint NOT NULL COMMENT '点击次数', + `reg_num` smallint NOT NULL COMMENT '注册数量', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户邀请码'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_invite_logs` +-- + +DROP TABLE IF EXISTS `geekai_invite_logs`; +CREATE TABLE `geekai_invite_logs` ( + `id` int NOT NULL, + `inviter_id` int NOT NULL COMMENT '邀请人ID', + `user_id` int NOT NULL COMMENT '注册用户ID', + `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名', + `invite_code` char(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '邀请码', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='邀请注册日志'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_jimeng_jobs` +-- + +DROP TABLE IF EXISTS `geekai_jimeng_jobs`; +CREATE TABLE `geekai_jimeng_jobs` ( + `id` bigint UNSIGNED NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `task_id` varchar(100) NOT NULL COMMENT '任务ID', + `type` varchar(50) NOT NULL COMMENT '任务类型', + `req_key` varchar(100) DEFAULT NULL COMMENT '请求Key', + `prompt` text COMMENT '提示词', + `task_params` text COMMENT '任务参数JSON', + `img_url` varchar(1024) DEFAULT NULL COMMENT '图片或封面URL', + `video_url` varchar(1024) DEFAULT NULL COMMENT '视频URL', + `raw_data` text COMMENT '原始API响应', + `progress` bigint DEFAULT '0' COMMENT '进度百分比', + `status` varchar(20) DEFAULT 'pending' COMMENT '任务状态', + `err_msg` varchar(1024) DEFAULT NULL COMMENT '错误信息', + `power` int DEFAULT '0' COMMENT '消耗算力', + `created_at` datetime NOT NULL COMMENT '创建时间', + `updated_at` datetime NOT NULL COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_menus` +-- + +DROP TABLE IF EXISTS `geekai_menus`; +CREATE TABLE `geekai_menus` ( + `id` int NOT NULL, + `name` varchar(30) NOT NULL COMMENT '菜单名称', + `icon` varchar(150) NOT NULL COMMENT '菜单图标', + `url` varchar(100) NOT NULL COMMENT '地址', + `sort_num` smallint NOT NULL COMMENT '排序', + `enabled` tinyint(1) NOT NULL COMMENT '是否启用' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='前端菜单表'; + +-- +-- 转存表中的数据 `geekai_menus` +-- + +INSERT INTO `geekai_menus` (`id`, `name`, `icon`, `url`, `sort_num`, `enabled`) VALUES +(1, 'AI 对话', 'icon-chat', '/chat', 1, 1), +(5, 'MJ 绘画', 'icon-mj', '/mj', 2, 1), +(6, 'SD 绘画', 'icon-sd', '/sd', 3, 1), +(7, '算力日志', 'icon-file', '/powerLog', 11, 1), +(8, '应用中心', 'icon-app', '/apps', 10, 1), +(9, '画廊', 'icon-image', '/images-wall', 5, 1), +(10, '会员计划', 'icon-vip2', '/member', 12, 1), +(11, '分享计划', 'icon-share1', '/invite', 13, 1), +(12, '思维导图', 'icon-xmind', '/xmind', 9, 1), +(13, 'Banana', 'icon-dalle', '/dalle', 4, 1), +(14, '项目文档', 'icon-book', 'https://docs.geekai.me', 14, 1), +(19, 'Suno', 'icon-suno', '/suno', 6, 1), +(20, 'AI视频', 'icon-video', '/video', 7, 1), +(21, '即梦 AI', 'icon-jimeng2', '/jimeng', 8, 1); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_mj_jobs` +-- + +DROP TABLE IF EXISTS `geekai_mj_jobs`; +CREATE TABLE `geekai_mj_jobs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `task_id` varchar(20) DEFAULT NULL COMMENT '任务 ID', + `task_info` text NOT NULL COMMENT '任务详情', + `type` varchar(20) DEFAULT 'image' COMMENT '任务类别', + `message_id` char(40) NOT NULL COMMENT '消息 ID', + `channel_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '频道ID', + `reference_id` char(40) DEFAULT NULL COMMENT '引用消息 ID', + `prompt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '会话提示词', + `img_url` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '图片URL', + `org_url` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原始图片地址', + `hash` varchar(100) DEFAULT NULL COMMENT 'message hash', + `progress` smallint DEFAULT '0' COMMENT '任务进度', + `use_proxy` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否使用反代', + `publish` tinyint(1) NOT NULL COMMENT '是否发布', + `err_msg` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + `power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_moderation` +-- + +DROP TABLE IF EXISTS `geekai_moderation`; +CREATE TABLE `geekai_moderation` ( + `id` bigint UNSIGNED NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `source` varchar(255) NOT NULL COMMENT '敏感词来源', + `input` text NOT NULL COMMENT '用户输入', + `output` text NOT NULL COMMENT 'AI 输出', + `result` text NOT NULL COMMENT '鉴别结果', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_orders` +-- + +DROP TABLE IF EXISTS `geekai_orders`; +CREATE TABLE `geekai_orders` ( + `id` int NOT NULL, + `user_id` bigint NOT NULL COMMENT '用户ID', + `product_id` bigint NOT NULL COMMENT '产品ID', + `username` varchar(30) NOT NULL COMMENT '用户名', + `order_no` varchar(30) NOT NULL COMMENT '订单ID', + `trade_no` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '支付平台交易流水号', + `subject` varchar(100) NOT NULL COMMENT '订单产品', + `amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '订单金额', + `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态(0:待支付,1:已扫码,2:支付成功)', + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注', + `pay_time` bigint DEFAULT NULL COMMENT '支付时间', + `pay_way` varchar(20) NOT NULL COMMENT '支付方式', + `channel` varchar(30) NOT NULL COMMENT '支付类型', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL, + `checked` tinyint NOT NULL DEFAULT '0' COMMENT '是否已检查' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='充值订单表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_power_logs` +-- + +DROP TABLE IF EXISTS `geekai_power_logs`; +CREATE TABLE `geekai_power_logs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `username` varchar(30) NOT NULL COMMENT '用户名', + `type` tinyint(1) NOT NULL COMMENT '类型(1:充值,2:消费,3:退费)', + `amount` smallint NOT NULL COMMENT '算力数值', + `balance` bigint NOT NULL COMMENT '余额', + `model` varchar(255) NOT NULL COMMENT '模型', + `remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注', + `mark` tinyint(1) NOT NULL COMMENT '资金类型(0:支出,1:收入)', + `created_at` datetime NOT NULL COMMENT '创建时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户算力消费日志'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_products` +-- + +DROP TABLE IF EXISTS `geekai_products`; +CREATE TABLE `geekai_products` ( + `id` int NOT NULL, + `name` varchar(30) NOT NULL COMMENT '名称', + `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '价格', + `power` bigint NOT NULL DEFAULT '0' COMMENT '增加算力值', + `enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启动', + `sales` bigint NOT NULL DEFAULT '0' COMMENT '销量', + `sort_num` tinyint NOT NULL DEFAULT '0' COMMENT '排序', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会员套餐表'; + +-- +-- 转存表中的数据 `geekai_products` +-- + +INSERT INTO `geekai_products` (`id`, `name`, `price`, `power`, `enabled`, `sales`, `sort_num`, `created_at`, `updated_at`) VALUES +(5, '100次点卡', 0.20, 100, 1, 0, 0, '2023-08-28 10:55:08', '2025-08-30 10:55:53'), +(6, '200次点卡', 19.90, 200, 1, 0, 0, '1970-01-01 08:00:00', '2024-10-23 18:12:36'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_redeems` +-- + +DROP TABLE IF EXISTS `geekai_redeems`; +CREATE TABLE `geekai_redeems` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `name` varchar(30) NOT NULL COMMENT '兑换码名称', + `power` bigint NOT NULL COMMENT '算力', + `code` varchar(100) NOT NULL COMMENT '兑换码', + `enabled` tinyint(1) NOT NULL COMMENT '是否启用', + `created_at` datetime NOT NULL, + `redeemed_at` bigint NOT NULL COMMENT '兑换时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='兑换码'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_sd_jobs` +-- + +DROP TABLE IF EXISTS `geekai_sd_jobs`; +CREATE TABLE `geekai_sd_jobs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'txt2img' COMMENT '任务类别', + `task_id` char(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务 ID', + `task_info` text NOT NULL COMMENT '任务详情', + `prompt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '会话提示词', + `img_url` varchar(255) DEFAULT NULL COMMENT '图片URL', + `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '绘画参数json', + `progress` smallint DEFAULT '0' COMMENT '任务进度', + `publish` tinyint(1) NOT NULL COMMENT '是否发布', + `err_msg` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + `power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Stable Diffusion 任务表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_suno_jobs` +-- + +DROP TABLE IF EXISTS `geekai_suno_jobs`; +CREATE TABLE `geekai_suno_jobs` ( + `id` int NOT NULL, + `user_id` bigint NOT NULL COMMENT '用户 ID', + `channel` varchar(100) NOT NULL COMMENT '渠道', + `title` varchar(100) DEFAULT NULL COMMENT '歌曲标题', + `type` tinyint(1) DEFAULT '0' COMMENT '任务类型,1:灵感创作,2:自定义创作', + `task_id` varchar(50) DEFAULT NULL COMMENT '任务 ID', + `task_info` text NOT NULL COMMENT '任务详情', + `ref_task_id` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '引用任务 ID', + `tags` varchar(255) DEFAULT NULL COMMENT '歌曲风格', + `instrumental` tinyint(1) DEFAULT '0' COMMENT '是否为纯音乐', + `extend_secs` smallint DEFAULT '0' COMMENT '延长秒数', + `song_id` varchar(50) DEFAULT NULL COMMENT '要续写的歌曲 ID', + `ref_song_id` varchar(50) NOT NULL COMMENT '引用的歌曲ID', + `prompt` varchar(2000) NOT NULL COMMENT '提示词', + `cover_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '封面图地址', + `audio_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '音频地址', + `model_name` varchar(30) DEFAULT NULL COMMENT '模型地址', + `progress` smallint DEFAULT '0' COMMENT '任务进度', + `duration` smallint NOT NULL DEFAULT '0' COMMENT '歌曲时长', + `publish` tinyint(1) NOT NULL COMMENT '是否发布', + `err_msg` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + `raw_data` text COMMENT '原始数据', + `power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力', + `play_times` bigint DEFAULT NULL COMMENT '播放次数', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_users` +-- + +DROP TABLE IF EXISTS `geekai_users`; +CREATE TABLE `geekai_users` ( + `id` int NOT NULL, + `username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名', + `mobile` char(11) DEFAULT NULL COMMENT '手机号', + `email` varchar(50) DEFAULT NULL COMMENT '邮箱地址', + `nickname` varchar(30) NOT NULL COMMENT '昵称', + `password` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码', + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头像', + `salt` char(12) NOT NULL COMMENT '密码盐', + `power` bigint NOT NULL DEFAULT '0' COMMENT '剩余算力', + `expired_time` bigint NOT NULL COMMENT '用户过期时间', + `status` tinyint(1) NOT NULL COMMENT '当前状态', + `chat_config_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聊天配置json', + `chat_roles_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聊天角色 json', + `chat_models_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'AI模型 json', + `last_login_at` bigint NOT NULL COMMENT '最后登录时间', + `vip` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否会员', + `last_login_ip` char(16) NOT NULL COMMENT '最后登录 IP', + `openid` varchar(100) DEFAULT NULL COMMENT '第三方登录账号ID', + `platform` varchar(30) DEFAULT NULL COMMENT '登录平台', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'; + +-- +-- 转存表中的数据 `geekai_users` +-- + +INSERT INTO `geekai_users` (`id`, `username`, `mobile`, `email`, `nickname`, `password`, `avatar`, `salt`, `power`, `expired_time`, `status`, `chat_config_json`, `chat_roles_json`, `chat_models_json`, `last_login_at`, `vip`, `last_login_ip`, `openid`, `platform`, `created_at`, `updated_at`) VALUES +(4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2025/4/1745937591865765.jpg', 'ueedue5l', 13147, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"programmer\",\"teacher\",\"psychiatrist\",\"lu_xun\",\"english_trainer\",\"translator\",\"red_book\",\"dou_yin\",\"weekly_report\",\"girl_friend\",\"steve_jobs\",\"elon_musk\",\"kong_zi\",\"draw_prompt_expert\",\"draw_prompt\",\"gpt\"]', '[1]', 1757165129, 1, '::1', '', NULL, '2023-06-12 16:47:17', '2025-09-06 21:25:29'), +(47, 'user1', '', '', '极客学长@202752', '4d3e57a01ae826531012e4ea6e17cbc45fea183467abe9813c379fb84916fb0a', '/images/avatar/user.png', 'ixl0nqa6', 300, 0, 1, '', '[\"gpt\"]', '', 0, 0, '', '', '', '2024-12-24 11:37:16', '2024-12-24 11:37:16'), +(48, 'wx@3659838859', '', '', '极客学长', 'cf6bbe381b23812d2b9fd423abe74003cecdd3b93809896eb573536ba6c500b3', 'https://thirdwx.qlogo.cn/mmopen/vi_32/uyxRMqZcEkb7fHouKXbNzxrnrvAttBKkwNlZ7yFibibRGiahdmsrZ3A1NKf8Fw5qJNJn4TXRmygersgEbibaSGd9Sg/132', '5rsy4iwg', 100, 0, 1, '', '[\"gpt\"]', '', 1736228927, 0, '172.22.11.200', 'oCs0t62472W19z2LOEKI1rWyCTTA', '', '2025-01-07 13:43:06', '2025-01-07 13:48:48'), +(49, 'wx@9502480897', '', '', 'AI探索君', 'd99fa8ba7da1455693b40e11d894a067416e758af2a75d7a3df4721b76cdbc8c', 'https://thirdwx.qlogo.cn/mmopen/vi_32/Zpcln1FZjcKxqtIyCsOTLGn16s7uIvwWfdkdsW6gbZg4r9sibMbic4jvrHmV7ux9nseTB5kBSnu1HSXr7zB8rTXg/132', 'fjclgsli', 100, 0, 1, '', '[\"gpt\"]', '', 0, 0, '', 'oCs0t64FaOLfiTbHZpOqk3aUp_94', '', '2025-01-07 14:05:31', '2025-01-07 14:05:31'), +(50, 'user01', '', '', '极客学长@195842', 'df5d50d639fb67e891a4974b323770e6e3dc0c672479450b1c3808361af37c93', '/images/avatar/user.png', 'pafz75gk', 3000, 0, 1, '{}', '[\"gpt\"]', '[1]', 0, 0, '', '', '', '2025-08-04 21:28:35', '2025-08-11 17:23:26'), +(54, '18575670125', '18575670125', '', '用户@189706', 'f440bf41396f3f4df4d2feffb58670044d67005bfba4bde36b73d4206bd1e1a1', '/images/avatar/user.png', '553gpql0', 4, 0, 1, '{}', '[\"gpt\"]', '[64]', 1757233140, 0, '::1', '', '', '2025-09-07 16:19:01', '2025-09-07 21:52:05'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_user_login_logs` +-- + +DROP TABLE IF EXISTS `geekai_user_login_logs`; +CREATE TABLE `geekai_user_login_logs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户ID', + `username` varchar(30) NOT NULL COMMENT '用户名', + `login_ip` char(16) NOT NULL COMMENT '登录IP', + `login_address` varchar(30) NOT NULL COMMENT '登录地址', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户登录日志'; + +-- -------------------------------------------------------- + +-- +-- 表的结构 `geekai_video_jobs` +-- + +DROP TABLE IF EXISTS `geekai_video_jobs`; +CREATE TABLE `geekai_video_jobs` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `channel` varchar(100) NOT NULL COMMENT '渠道', + `task_id` varchar(100) NOT NULL COMMENT '任务 ID', + `task_info` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '原始任务信息', + `type` varchar(20) DEFAULT NULL COMMENT '任务类型,luma,runway,cogvideo', + `prompt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '提示词', + `prompt_ext` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '优化后提示词', + `cover_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '封面图地址', + `video_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '视频地址', + `water_url` varchar(512) DEFAULT NULL COMMENT '带水印的视频地址', + `progress` smallint DEFAULT '0' COMMENT '任务进度', + `publish` tinyint(1) NOT NULL COMMENT '是否发布', + `err_msg` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息', + `raw_data` text COMMENT '原始数据', + `power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力', + `created_at` datetime NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表'; + +-- +-- 转储表的索引 +-- + +-- +-- 表的索引 `geekai_3d_jobs` +-- +ALTER TABLE `geekai_3d_jobs` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_admin_users` +-- +ALTER TABLE `geekai_admin_users` + ADD PRIMARY KEY (`id`) USING BTREE, + ADD UNIQUE KEY `username` (`username`) USING BTREE, + ADD UNIQUE KEY `idx_chatgpt_admin_users_username` (`username`); + +-- +-- 表的索引 `geekai_api_keys` +-- +ALTER TABLE `geekai_api_keys` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_app_types` +-- +ALTER TABLE `geekai_app_types` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_chat_history` +-- +ALTER TABLE `geekai_chat_history` + ADD PRIMARY KEY (`id`), + ADD KEY `chat_id` (`chat_id`), + ADD KEY `idx_chatgpt_chat_history_chat_id` (`chat_id`), + ADD KEY `idx_chatgpt_chat_history_user_id` (`user_id`); + +-- +-- 表的索引 `geekai_chat_items` +-- +ALTER TABLE `geekai_chat_items` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `chat_id` (`chat_id`), + ADD UNIQUE KEY `idx_chatgpt_chat_items_chat_id` (`chat_id`); + +-- +-- 表的索引 `geekai_chat_models` +-- +ALTER TABLE `geekai_chat_models` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_chat_roles` +-- +ALTER TABLE `geekai_chat_roles` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `marker` (`marker`), + ADD UNIQUE KEY `idx_chatgpt_chat_roles_marker` (`marker`), + ADD UNIQUE KEY `idx_chatgpt_chat_roles_key` (`marker`); + +-- +-- 表的索引 `geekai_configs` +-- +ALTER TABLE `geekai_configs` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`), + ADD UNIQUE KEY `idx_chatgpt_configs_name` (`name`); + +-- +-- 表的索引 `geekai_dall_jobs` +-- +ALTER TABLE `geekai_dall_jobs` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_files` +-- +ALTER TABLE `geekai_files` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_functions` +-- +ALTER TABLE `geekai_functions` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `name` (`name`), + ADD UNIQUE KEY `idx_chatgpt_functions_name` (`name`); + +-- +-- 表的索引 `geekai_invite_codes` +-- +ALTER TABLE `geekai_invite_codes` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `code` (`code`), + ADD UNIQUE KEY `idx_chatgpt_invite_codes_code` (`code`); + +-- +-- 表的索引 `geekai_invite_logs` +-- +ALTER TABLE `geekai_invite_logs` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_jimeng_jobs` +-- +ALTER TABLE `geekai_jimeng_jobs` + ADD PRIMARY KEY (`id`), + ADD KEY `idx_chatgpt_jimeng_jobs_task_id` (`task_id`), + ADD KEY `idx_chatgpt_jimeng_jobs_user_id` (`user_id`); + +-- +-- 表的索引 `geekai_menus` +-- +ALTER TABLE `geekai_menus` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_mj_jobs` +-- +ALTER TABLE `geekai_mj_jobs` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `task_id` (`task_id`), + ADD UNIQUE KEY `idx_chatgpt_mj_jobs_task_id` (`task_id`), + ADD KEY `message_id` (`message_id`), + ADD KEY `idx_chatgpt_mj_jobs_message_id` (`message_id`); + +-- +-- 表的索引 `geekai_moderation` +-- +ALTER TABLE `geekai_moderation` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_orders` +-- +ALTER TABLE `geekai_orders` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `order_no` (`order_no`), + ADD UNIQUE KEY `idx_chatgpt_orders_order_no` (`order_no`); + +-- +-- 表的索引 `geekai_power_logs` +-- +ALTER TABLE `geekai_power_logs` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_products` +-- +ALTER TABLE `geekai_products` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_redeems` +-- +ALTER TABLE `geekai_redeems` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `code` (`code`), + ADD UNIQUE KEY `idx_chatgpt_redeems_code` (`code`); + +-- +-- 表的索引 `geekai_sd_jobs` +-- +ALTER TABLE `geekai_sd_jobs` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `task_id` (`task_id`), + ADD UNIQUE KEY `idx_chatgpt_sd_jobs_task_id` (`task_id`); + +-- +-- 表的索引 `geekai_suno_jobs` +-- +ALTER TABLE `geekai_suno_jobs` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_users` +-- +ALTER TABLE `geekai_users` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `username` (`username`), + ADD UNIQUE KEY `idx_chatgpt_users_username` (`username`); + +-- +-- 表的索引 `geekai_user_login_logs` +-- +ALTER TABLE `geekai_user_login_logs` + ADD PRIMARY KEY (`id`); + +-- +-- 表的索引 `geekai_video_jobs` +-- +ALTER TABLE `geekai_video_jobs` + ADD PRIMARY KEY (`id`); + +-- +-- 在导出的表使用AUTO_INCREMENT +-- + +-- +-- 使用表AUTO_INCREMENT `geekai_3d_jobs` +-- +ALTER TABLE `geekai_3d_jobs` + MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_admin_users` +-- +ALTER TABLE `geekai_admin_users` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=113; + +-- +-- 使用表AUTO_INCREMENT `geekai_api_keys` +-- +ALTER TABLE `geekai_api_keys` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_app_types` +-- +ALTER TABLE `geekai_app_types` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8; + +-- +-- 使用表AUTO_INCREMENT `geekai_chat_history` +-- +ALTER TABLE `geekai_chat_history` + MODIFY `id` bigint NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_chat_items` +-- +ALTER TABLE `geekai_chat_items` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_chat_models` +-- +ALTER TABLE `geekai_chat_models` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=65; + +-- +-- 使用表AUTO_INCREMENT `geekai_chat_roles` +-- +ALTER TABLE `geekai_chat_roles` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=135; + +-- +-- 使用表AUTO_INCREMENT `geekai_configs` +-- +ALTER TABLE `geekai_configs` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=25; + +-- +-- 使用表AUTO_INCREMENT `geekai_dall_jobs` +-- +ALTER TABLE `geekai_dall_jobs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_files` +-- +ALTER TABLE `geekai_files` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_functions` +-- +ALTER TABLE `geekai_functions` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4; + +-- +-- 使用表AUTO_INCREMENT `geekai_invite_codes` +-- +ALTER TABLE `geekai_invite_codes` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_invite_logs` +-- +ALTER TABLE `geekai_invite_logs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_jimeng_jobs` +-- +ALTER TABLE `geekai_jimeng_jobs` + MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_menus` +-- +ALTER TABLE `geekai_menus` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=22; + +-- +-- 使用表AUTO_INCREMENT `geekai_mj_jobs` +-- +ALTER TABLE `geekai_mj_jobs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_moderation` +-- +ALTER TABLE `geekai_moderation` + MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_orders` +-- +ALTER TABLE `geekai_orders` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_power_logs` +-- +ALTER TABLE `geekai_power_logs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_products` +-- +ALTER TABLE `geekai_products` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7; + +-- +-- 使用表AUTO_INCREMENT `geekai_redeems` +-- +ALTER TABLE `geekai_redeems` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_sd_jobs` +-- +ALTER TABLE `geekai_sd_jobs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_suno_jobs` +-- +ALTER TABLE `geekai_suno_jobs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_users` +-- +ALTER TABLE `geekai_users` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=55; + +-- +-- 使用表AUTO_INCREMENT `geekai_user_login_logs` +-- +ALTER TABLE `geekai_user_login_logs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; + +-- +-- 使用表AUTO_INCREMENT `geekai_video_jobs` +-- +ALTER TABLE `geekai_video_jobs` + MODIFY `id` int NOT NULL AUTO_INCREMENT; +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/database/update-v4.2.3.1.sql b/database/update-v4.2.3.1.sql deleted file mode 100644 index 5029b160..00000000 --- a/database/update-v4.2.3.1.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO `chatgpt_configs` (`id`, `marker`, `config_json`) VALUES -(4, 'privacy', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_navs\":null,\"copyright\":\"\",\"default_nickname\":\"\",\"icp\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"translate_model_id\":0,\"max_file_size\":0,\"content\":\"# 隐私政策\\n\\n我们非常重视用户的隐私和个人信息保护。您在使用我们的产品与服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明我们在收集和使用您相关信息时对应的处理规则。\",\"updated\":true}'), -(5, 'agreement', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_navs\":null,\"copyright\":\"\",\"default_nickname\":\"\",\"icp\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"translate_model_id\":0,\"max_file_size\":0,\"content\":\"# 用户协议\\n\\n用户在使用本服务前应当阅读并同意本协议。本协议内容包括协议正文及所有本平台已经发布的或将来可能发布的各类规则。所有规则为本协议不可分割的组成部分,与协议正文具有同等法律效力。\",\"updated\":true}'); diff --git a/database/update-v4.2.3.sql b/database/update-v4.2.3.sql deleted file mode 100644 index 98ac1d2f..00000000 --- a/database/update-v4.2.3.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE `chatgpt_chat_models` ADD `category` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '模型类别' AFTER `id`; -ALTER TABLE `chatgpt_chat_models` ADD `description` VARCHAR(1024) NOT NULL DEFAULT '' COMMENT '模型类型描述' AFTER `id`; -ALTER TABLE `chatgpt_orders` DROP `deleted_at`; -ALTER TABLE `chatgpt_chat_history` DROP `deleted_at`; -ALTER TABLE `chatgpt_chat_items` DROP `deleted_at`; \ No newline at end of file diff --git a/docs/ai3d.md b/docs/ai3d.md new file mode 100644 index 00000000..ed3fb44a --- /dev/null +++ b/docs/ai3d.md @@ -0,0 +1,510 @@ +## 开发 3D 图片生成功能 + +对接 3D 图片生成接口,为当前系统添加 3D 模型生成功能,默认支持腾讯云和 Gitee 的图生 3D API 接口。 + +## 要求 + +1. 完成数据库设计,后端 API 设计,前端页面设计。 +2. 完成前端功能页面以及后台管理页面,具体设计结构可以参考即梦 AI,在对应的模块建立独立的模块 。 +3. 页面设计要精美,但是整体风格要跟整站风格一致。 +4. 支持前端 3D 模型预览,支持 3D 模型下载。 + +## 腾讯云图生 3D API 接口文档 + +1. 提交任务: https://cloud.tencent.com/document/product/1804/120826 +2. 查询任务: https://cloud.tencent.com/document/product/1804/120827 +3. Golang SDK: https://gitee.com/TencentCloud/tencentcloud-sdk-go/blob/master/tencentcloud/ai3d/v20250513/client.go 依赖已经安装到本地了,如果你无法读取远程文件,下面是文件主要内容: + +client.go + +```go +// Copyright (c) 2017-2025 Tencent. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v20250513 + +import ( + "context" + "errors" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" +) + +const APIVersion = "2025-05-13" + +type Client struct { + common.Client +} + +// Deprecated +func NewClientWithSecretId(secretId, secretKey, region string) (client *Client, err error) { + cpf := profile.NewClientProfile() + client = &Client{} + client.Init(region).WithSecretId(secretId, secretKey).WithProfile(cpf) + return +} + +func NewClient(credential common.CredentialIface, region string, clientProfile *profile.ClientProfile) (client *Client, err error) { + client = &Client{} + client.Init(region). + WithCredential(credential). + WithProfile(clientProfile) + return +} + + +func NewQueryHunyuanTo3DJobRequest() (request *QueryHunyuanTo3DJobRequest) { + request = &QueryHunyuanTo3DJobRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("ai3d", APIVersion, "QueryHunyuanTo3DJob") + + + return +} + +func NewQueryHunyuanTo3DJobResponse() (response *QueryHunyuanTo3DJobResponse) { + response = &QueryHunyuanTo3DJobResponse{ + BaseResponse: &tchttp.BaseResponse{}, + } + return + +} + +// QueryHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) QueryHunyuanTo3DJob(request *QueryHunyuanTo3DJobRequest) (response *QueryHunyuanTo3DJobResponse, err error) { + return c.QueryHunyuanTo3DJobWithContext(context.Background(), request) +} + +// QueryHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) QueryHunyuanTo3DJobWithContext(ctx context.Context, request *QueryHunyuanTo3DJobRequest) (response *QueryHunyuanTo3DJobResponse, err error) { + if request == nil { + request = NewQueryHunyuanTo3DJobRequest() + } + c.InitBaseRequest(&request.BaseRequest, "ai3d", APIVersion, "QueryHunyuanTo3DJob") + + if c.GetCredential() == nil { + return nil, errors.New("QueryHunyuanTo3DJob require credential") + } + + request.SetContext(ctx) + + response = NewQueryHunyuanTo3DJobResponse() + err = c.Send(request, response) + return +} + +func NewSubmitHunyuanTo3DJobRequest() (request *SubmitHunyuanTo3DJobRequest) { + request = &SubmitHunyuanTo3DJobRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("ai3d", APIVersion, "SubmitHunyuanTo3DJob") + + + return +} + +func NewSubmitHunyuanTo3DJobResponse() (response *SubmitHunyuanTo3DJobResponse) { + response = &SubmitHunyuanTo3DJobResponse{ + BaseResponse: &tchttp.BaseResponse{}, + } + return + +} + +// SubmitHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) SubmitHunyuanTo3DJob(request *SubmitHunyuanTo3DJobRequest) (response *SubmitHunyuanTo3DJobResponse, err error) { + return c.SubmitHunyuanTo3DJobWithContext(context.Background(), request) +} + +// SubmitHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) SubmitHunyuanTo3DJobWithContext(ctx context.Context, request *SubmitHunyuanTo3DJobRequest) (response *SubmitHunyuanTo3DJobResponse, err error) { + if request == nil { + request = NewSubmitHunyuanTo3DJobRequest() + } + c.InitBaseRequest(&request.BaseRequest, "ai3d", APIVersion, "SubmitHunyuanTo3DJob") + + if c.GetCredential() == nil { + return nil, errors.New("SubmitHunyuanTo3DJob require credential") + } + + request.SetContext(ctx) + + response = NewSubmitHunyuanTo3DJobResponse() + err = c.Send(request, response) + return +} + +``` + +model.go + +```go +// Copyright (c) 2017-2025 Tencent. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v20250513 + +import ( + tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/json" +) + +type File3D struct { + // 文件格式 + Type *string `json:"Type,omitnil,omitempty" name:"Type"` + + // 文件的Url(有效期24小时) + Url *string `json:"Url,omitnil,omitempty" name:"Url"` + + // 预览图片Url + PreviewImageUrl *string `json:"PreviewImageUrl,omitnil,omitempty" name:"PreviewImageUrl"` +} + +// Predefined struct for user +type QueryHunyuanTo3DJobRequestParams struct { + // 任务ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` +} + +type QueryHunyuanTo3DJobRequest struct { + *tchttp.BaseRequest + + // 任务ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` +} + +func (r *QueryHunyuanTo3DJobRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *QueryHunyuanTo3DJobRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "JobId") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "QueryHunyuanTo3DJobRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type QueryHunyuanTo3DJobResponseParams struct { + // 任务状态。WAIT:等待中,RUN:执行中,FAIL:任务失败,DONE:任务成功 + Status *string `json:"Status,omitnil,omitempty" name:"Status"` + + // 错误码 + ErrorCode *string `json:"ErrorCode,omitnil,omitempty" name:"ErrorCode"` + + // 错误信息 + ErrorMessage *string `json:"ErrorMessage,omitnil,omitempty" name:"ErrorMessage"` + + // 生成的3D文件数组。 + ResultFile3Ds []*File3D `json:"ResultFile3Ds,omitnil,omitempty" name:"ResultFile3Ds"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + +type QueryHunyuanTo3DJobResponse struct { + *tchttp.BaseResponse + Response *QueryHunyuanTo3DJobResponseParams `json:"Response"` +} + +func (r *QueryHunyuanTo3DJobResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *QueryHunyuanTo3DJobResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type SubmitHunyuanTo3DJobRequestParams struct { + // 文生3D,3D内容的描述,中文正向提示词。 + // 最多支持1024个 utf-8 字符。 + // 文生3D, image、image_url和 prompt必填其一,且prompt和image/image_url不能同时存在。 + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` + + // 输入图 Base64 数据。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64、ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageBase64 *string `json:"ImageBase64,omitnil,omitempty" name:"ImageBase64"` + + // 输入图Url。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64/ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageUrl *string `json:"ImageUrl,omitnil,omitempty" name:"ImageUrl"` + + // 多视角的模型图片,视角参考值: + // left:左视图; + // right:右视图; + // back:后视图; + // + // 每个视角仅限制一张图片。 + // ●图片大小限制:编码后大小不可超过8M。 + // ●图片分辨率限制:单边分辨率小于5000且大于128。 + // ●支持图片格式:支持jpg或png + MultiViewImages []*ViewImage `json:"MultiViewImages,omitnil,omitempty" name:"MultiViewImages"` + + // 生成模型的格式,仅限制生成一种格式。 + // 生成模型文件组默认返回obj格式。 + // 可选值:OBJ,GLB,STL,USDZ,FBX,MP4。 + ResultFormat *string `json:"ResultFormat,omitnil,omitempty" name:"ResultFormat"` + + // 是否开启 PBR材质生成,默认 false。 + EnablePBR *bool `json:"EnablePBR,omitnil,omitempty" name:"EnablePBR"` +} + +type SubmitHunyuanTo3DJobRequest struct { + *tchttp.BaseRequest + + // 文生3D,3D内容的描述,中文正向提示词。 + // 最多支持1024个 utf-8 字符。 + // 文生3D, image、image_url和 prompt必填其一,且prompt和image/image_url不能同时存在。 + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` + + // 输入图 Base64 数据。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64、ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageBase64 *string `json:"ImageBase64,omitnil,omitempty" name:"ImageBase64"` + + // 输入图Url。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64/ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageUrl *string `json:"ImageUrl,omitnil,omitempty" name:"ImageUrl"` + + // 多视角的模型图片,视角参考值: + // left:左视图; + // right:右视图; + // back:后视图; + // + // 每个视角仅限制一张图片。 + // ●图片大小限制:编码后大小不可超过8M。 + // ●图片分辨率限制:单边分辨率小于5000且大于128。 + // ●支持图片格式:支持jpg或png + MultiViewImages []*ViewImage `json:"MultiViewImages,omitnil,omitempty" name:"MultiViewImages"` + + // 生成模型的格式,仅限制生成一种格式。 + // 生成模型文件组默认返回obj格式。 + // 可选值:OBJ,GLB,STL,USDZ,FBX,MP4。 + ResultFormat *string `json:"ResultFormat,omitnil,omitempty" name:"ResultFormat"` + + // 是否开启 PBR材质生成,默认 false。 + EnablePBR *bool `json:"EnablePBR,omitnil,omitempty" name:"EnablePBR"` +} + +func (r *SubmitHunyuanTo3DJobRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *SubmitHunyuanTo3DJobRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "Prompt") + delete(f, "ImageBase64") + delete(f, "ImageUrl") + delete(f, "MultiViewImages") + delete(f, "ResultFormat") + delete(f, "EnablePBR") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "SubmitHunyuanTo3DJobRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type SubmitHunyuanTo3DJobResponseParams struct { + // 任务ID(有效期24小时) + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + +type SubmitHunyuanTo3DJobResponse struct { + *tchttp.BaseResponse + Response *SubmitHunyuanTo3DJobResponseParams `json:"Response"` +} + +func (r *SubmitHunyuanTo3DJobResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *SubmitHunyuanTo3DJobResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +type ViewImage struct { + // 视角类型。 + // 取值:back、left、right + ViewType *string `json:"ViewType,omitnil,omitempty" name:"ViewType"` + + // 图片Url地址 + ViewImageUrl *string `json:"ViewImageUrl,omitnil,omitempty" name:"ViewImageUrl"` +} +``` + +## Gitee 图生 3D API 接口文档 + +1. 提交任务: https://ai.gitee.com/docs/openapi/v1#tag/3d-%E7%94%9F%E6%88%90/post/async/image-to-3d +2. 查询任务:https://ai.gitee.com/docs/openapi/v1#tag/%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1/get/task/{task_id}/get + +首先,你需要认真阅读上述接口文档,然后按照接口文档的示例代码实现腾讯云和 Gitee 的图生 3D API 接口,并且将接口集成到现有的系统中。 + +📋 功能概述 + + 为现有的GeekAI-Plus系统添加3D图片生成功能,集成腾讯云和Gitee的图生3D API接口,包含完整的前后端功能和管理界面。 + + 🗄️ 数据库设计 + + 新增数据表:geekai_3d_jobs + - id (uint): 主键 + - type (string): API类型 (tencent/gitee) + - user_id (uint): 用户ID + - power (int): 消耗算力 + - task_id (string): 第三方任务ID + - img_url (string): 生成的3D模型文件地址 + - model (string): 使用的3D模型类型 + - status (string): 任务状态 + - err_msg (string): 错误信息 + - params (JSON): 任务参数(包含输入图片、提示词等所有参数) + - created_at (int64): 创建时间 + + 🔧 后端API实现 + + 路由结构:/api/3d/* + - POST /api/3d/generate - 创建3D生成任务 + - GET /api/3d/jobs - 获取任务列表 + - GET /api/3d/job/{id} - 获取任务详情 + - GET /api/3d/download/{id} - 下载3D模型 + - DELETE /api/3d/job/{id} - 删除任务 + + 核心服务: + - service/3d/tencent_client.go - 腾讯云3D API客户端 + - service/3d/gitee_client.go - Gitee 3D API客户端 + - service/3d/service.go - 3D生成服务统一接口 + - handler/3d_handler.go - HTTP处理器 + - store/vo/3d_job.go - 数据模型 + + 🎨 前端界面设计 + + 用户端页面:/3d - 3D生成主页面 + - 参考JiMeng.vue的设计风格和布局 + - 使用CustomTab组件分离平台参数: + - Tab 1: "魔力方舟" (Gitee平台参数) + - Tab 2: "腾讯混元" (腾讯云平台参数) + - 每个Tab内包含: + - 图片上传区域 + - 模型选择下拉框 + - 算力消耗实时显示 + - 平台特定的参数配置 + - 生成按钮 + - 任务列表和状态显示 + - 集成3D模型预览器 (three.js) + - 模型下载功能 + + 移动端适配: + - mobile/3dCreate.vue - 移动端3D生成页面 + - 保持Tab切换功能 + - 响应式设计,触控优化 + + 🛠️ 管理后台 + + 管理功能: + - admin/3d/3dJobs.vue - 任务管理列表 + - admin/3d/3dSetting.vue - API配置页面 + - 模型配置管理: + - 分平台配置API秘钥和模型列表 + - 设置每个模型的算力消耗值 + - API密钥和端点配置 + + 🔌 API集成方案 + + 腾讯云集成: + - 使用官方Golang SDK + - 支持异步任务提交和状态查询 + + Gitee集成: + - HTTP客户端实现 + - 标准化响应处理 + + 🎯 核心功能特性 + + - 平台切换:通过CustomTab在魔力方舟和腾讯混元间切换 + - 模型选择:每个平台支持不同的3D模型 + - 动态算力:切换模型时实时更新算力消耗显示 + - 参数隔离:不同平台的参数配置完全分离 + - 3D预览:集成Three.js实现模型预览 + - 统一体验:保持与JiMeng.vue相似的交互风格 + + 📱 用户体验 + + - JiMeng.vue风格的简洁界面 + - Tab切换流畅的平台选择 + - 模型选择时算力消耗实时更新 + - 支持拖拽上传图片 + - 实时任务状态显示 + - 3D模型交互式预览 + + 这个设计将创建一个与现有JiMeng功能风格一致的3D生成模块,通过Tab分离实现平台参数的清晰管理。 + +整个实现严格按照现有系统的代码规范和架构模式,与 JiMeng 等模块保持一致的用户体验! diff --git a/web/.env.development b/web/.env.development index e56329b9..9992ffb4 100644 --- a/web/.env.development +++ b/web/.env.development @@ -6,7 +6,7 @@ VITE_ADMIN_USER=admin VITE_ADMIN_PASS=admin123 VITE_KEY_PREFIX=GeekAI_DEV_ VITE_TITLE="Geek-AI 创作系统" -VITE_VERSION=v4.2.5 +VITE_VERSION=v4.2.6 VITE_DOCS_URL=https://docs.geekai.me VITE_GITHUB_URL=https://github.com/yangjian102621/geekai VITE_GITEE_URL=https://gitee.com/blackfox/geekai diff --git a/web/.env.production b/web/.env.production index fafeffe0..2dfcb19f 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,8 +1,7 @@ VITE_API_HOST= VITE_WS_HOST= VITE_KEY_PREFIX=GeekAI_ -VITE_VERSION=v4.2.5 -VUE_APP_TITLE="Geek-AI 创作系统" +VITE_VERSION=v4.2.6 VITE_DOCS_URL=https://docs.geekai.me VITE_GITHUB_URL=https://github.com/yangjian102621/geekai VITE_GITEE_URL=https://gitee.com/blackfox/geekai diff --git a/web/package.json b/web/package.json index 8c40a18c..c8d4e3f2 100644 --- a/web/package.json +++ b/web/package.json @@ -15,6 +15,7 @@ "@better-scroll/pull-up": "^2.5.1", "@better-scroll/scroll-bar": "^2.5.1", "@element-plus/icons-vue": "^2.3.1", + "@microsoft/fetch-event-source": "^2.0.1", "animate.css": "^4.1.1", "axios": "^0.27.2", "clipboard": "^2.0.11", @@ -40,18 +41,18 @@ "qrcode": "^1.5.3", "qs": "^6.11.1", "sortablejs": "^1.15.0", - "three": "^0.128.0", + "three": "^0.160.0", + "unplugin-auto-import": "^0.18.5", "vant": "^4.5.0", "vue": "^3.2.13", "vue-router": "^4.0.15", - "unplugin-auto-import": "^0.18.5", - "@microsoft/fetch-event-source": "^2.0.1", "vue-waterfall-plugin-next": "^2.6.5" }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.4", "autoprefixer": "^10.4.20", "postcss": "^8.4.49", + "sass-embedded": "^1.89.2", "stylus": "^0.58.1", "stylus-loader": "^7.0.0", "tailwindcss": "^3.4.17", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 0279f299..a562643e 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,12 +8,27 @@ importers: .: dependencies: + '@better-scroll/core': + specifier: ^2.5.1 + version: 2.5.1 + '@better-scroll/mouse-wheel': + specifier: ^2.5.1 + version: 2.5.1 + '@better-scroll/observe-dom': + specifier: ^2.5.1 + version: 2.5.1 + '@better-scroll/pull-up': + specifier: ^2.5.1 + version: 2.5.1 + '@better-scroll/scroll-bar': + specifier: ^2.5.1 + version: 2.5.1 '@element-plus/icons-vue': specifier: ^2.3.1 - version: 2.3.1(vue@3.5.14) - '@openai/realtime-api-beta': - specifier: github:openai/openai-realtime-api-beta - version: https://codeload.github.com/openai/openai-realtime-api-beta/tar.gz/a5cb94824f625423858ebacb9f769226ca98945f + version: 2.3.1(vue@3.5.18) + '@microsoft/fetch-event-source': + specifier: ^2.0.1 + version: 2.0.1 animate.css: specifier: ^4.1.1 version: 4.1.1 @@ -28,13 +43,13 @@ importers: version: 1.2.1 core-js: specifier: ^3.8.3 - version: 3.42.0 + version: 3.44.0 echarts: specifier: ^5.5.0 version: 5.6.0 element-plus: specifier: ^2.4.0 - version: 2.9.10(vue@3.5.14) + version: 2.10.4(vue@3.5.18) good-storage: specifier: ^1.1.1 version: 1.1.1 @@ -56,6 +71,9 @@ importers: markdown-it-mathjax3: specifier: ^4.3.2 version: 4.3.2 + marked: + specifier: ^15.0.11 + version: 15.0.12 markmap-common: specifier: ^0.16.0 version: 0.16.0 @@ -70,13 +88,13 @@ importers: version: 0.16.0(markmap-common@0.16.0) md-editor-v3: specifier: ^2.2.1 - version: 2.11.3(vue@3.5.14) + version: 2.11.3(vue@3.5.18) memfs: specifier: ^4.9.3 - version: 4.17.2 + version: 4.23.0 pinia: specifier: ^2.1.4 - version: 2.3.1(vue@3.5.14) + version: 2.3.1(vue@3.5.18) qrcode: specifier: ^1.5.3 version: 1.5.4 @@ -87,163 +105,57 @@ importers: specifier: ^1.15.0 version: 1.15.6 three: - specifier: ^0.128.0 - version: 0.128.0 - v3-waterfall: - specifier: ^1.3.3 - version: 1.3.3 + specifier: ^0.160.0 + version: 0.160.1 + unplugin-auto-import: + specifier: ^0.18.5 + version: 0.18.6(@vueuse/core@9.13.0(vue@3.5.18))(rollup@4.46.1) vant: specifier: ^4.5.0 - version: 4.9.19(vue@3.5.14) + version: 4.9.21(vue@3.5.18) vue: specifier: ^3.2.13 - version: 3.5.14 + version: 3.5.18 vue-router: specifier: ^4.0.15 - version: 4.5.1(vue@3.5.14) + version: 4.5.1(vue@3.5.18) + vue-waterfall-plugin-next: + specifier: ^2.6.5 + version: 2.6.7 devDependencies: - '@babel/core': - specifier: 7.18.6 - version: 7.18.6 - '@babel/eslint-parser': - specifier: ^7.12.16 - version: 7.27.1(@babel/core@7.18.6)(eslint@7.32.0) - '@vue/cli-plugin-babel': - specifier: ~5.0.0 - version: 5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3))(core-js@3.42.0)(vue@3.5.14) - '@vue/cli-plugin-eslint': - specifier: ~5.0.0 - version: 5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3))(eslint@7.32.0) - '@vue/cli-service': - specifier: ~5.0.0 - version: 5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3) + '@vitejs/plugin-vue': + specifier: ^5.2.4 + version: 5.2.4(vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2)(stylus@0.58.1)(terser@5.43.1))(vue@3.5.18) autoprefixer: specifier: ^10.4.20 - version: 10.4.21(postcss@8.5.3) - eslint: - specifier: ^7.32.0 - version: 7.32.0 - eslint-plugin-vue: - specifier: ^8.0.3 - version: 8.7.1(eslint@7.32.0) + version: 10.4.21(postcss@8.5.6) postcss: specifier: ^8.4.49 - version: 8.5.3 + version: 8.5.6 + sass-embedded: + specifier: ^1.89.2 + version: 1.89.2 stylus: specifier: ^0.58.1 version: 0.58.1 stylus-loader: specifier: ^7.0.0 - version: 7.1.3(stylus@0.58.1)(webpack@5.99.8) + version: 7.1.3(stylus@0.58.1)(webpack@5.101.0) tailwindcss: specifier: ^3.4.17 version: 3.4.17 - webpack: - specifier: ^5.90.3 - version: 5.99.8 + vite: + specifier: ^5.4.10 + version: 5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2)(stylus@0.58.1)(terser@5.43.1) packages: - '@achrinza/node-ipc@9.2.9': - resolution: {integrity: sha512-7s0VcTwiK/0tNOVdSX9FWMeFdOEcsAOz9HesBldXxFMaGvIak7KC2z9tV9EgsQXn6KUsWsfIkViMNuIo0GoZDQ==} - engines: {node: 8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22} - '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@babel/code-frame@7.12.11': - resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.27.2': - resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.18.6': - resolution: {integrity: sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==} - engines: {node: '>=6.9.0'} - - '@babel/eslint-parser@7.27.1': - resolution: {integrity: sha512-q8rjOuadH0V6Zo4XLMkJ3RMQ9MSBqwaDByyYB0izsYdaIWGNLmEblbCOf1vyFHICcg16CD7Fsi51vcQnYxmt6Q==} - engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} - peerDependencies: - '@babel/core': ^7.11.0 - eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - - '@babel/generator@7.27.1': - resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.27.1': - resolution: {integrity: sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.27.1': - resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-create-regexp-features-plugin@7.27.1': - resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-define-polyfill-provider@0.6.4': - resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - '@babel/helper-member-expression-to-functions@7.27.1': - resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.27.1': - resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-remap-async-to-generator@7.27.1': - resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-replace-supers@7.27.1': - resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} - engines: {node: '>=6.9.0'} + '@antfu/utils@0.7.10': + resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} @@ -253,512 +165,218 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-wrap-function@7.27.1': - resolution: {integrity: sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.27.1': - resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} - engines: {node: '>=6.9.0'} - - '@babel/highlight@7.25.9': - resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': - resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + '@babel/runtime@7.28.2': + resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': - resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': - resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@better-scroll/core@2.5.1': + resolution: {integrity: sha512-koKOuYA55dQ04FJRIVUpMGDr1hbCfWmfX0MGp1hKagkQSWSRpwblqACiwtggVauoj9aaJRJZ9hDsTM4weaavlg==} - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': - resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.13.0 + '@better-scroll/mouse-wheel@2.5.1': + resolution: {integrity: sha512-DGnrirRMY6zMM7xwgx09D/cA9A//3J1/uDkq8iBVEyE5p0sEr/keQpjEfFHGkBRa505BnbBwdbN6f5lugEDSPw==} - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': - resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@better-scroll/observe-dom@2.5.1': + resolution: {integrity: sha512-TCMGFLRfpXBPIwtUV/efliUmfmrhSNI7NXdSyjdWjsLOS7dh3eFkmcom5ERVWMaXVELSmujGXLqobT+dT0C/jg==} - '@babel/plugin-proposal-class-properties@7.18.6': - resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} - engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. - peerDependencies: - '@babel/core': ^7.0.0-0 + '@better-scroll/pull-up@2.5.1': + resolution: {integrity: sha512-1hu3xSMxdB8T391KffpNZ7g93lMwZEHjfb1F1Y4KvIkciDt8nXqkGpqrZF+YwR+EJTgYcWqUO8kgmI6XXu7Pkg==} - '@babel/plugin-proposal-decorators@7.27.1': - resolution: {integrity: sha512-DTxe4LBPrtFdsWzgpmbBKevg3e9PBy+dXRt19kSbucbZvL2uqtdqwwpluL1jfxYE0wIDTFp1nTy/q6gNLsxXrg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': - resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-decorators@7.27.1': - resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@better-scroll/scroll-bar@2.5.1': + resolution: {integrity: sha512-i6r60pWG/ztkFK2j5Gj54I0LJb2jGh5TWJNQBoW0gUkp28B+0JvBFTwZn9tF7beZCBorKR7Hvvu4O9A1TJy94Q==} - '@babel/plugin-syntax-dynamic-import@7.8.3': - resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-assertions@7.27.1': - resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.27.1': - resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.27.1': - resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6': - resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-arrow-functions@7.27.1': - resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-async-generator-functions@7.27.1': - resolution: {integrity: sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-async-to-generator@7.27.1': - resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-block-scoped-functions@7.27.1': - resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-block-scoping@7.27.1': - resolution: {integrity: sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-class-properties@7.27.1': - resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-class-static-block@7.27.1': - resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.12.0 - - '@babel/plugin-transform-classes@7.27.1': - resolution: {integrity: sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-computed-properties@7.27.1': - resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-destructuring@7.27.1': - resolution: {integrity: sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-dotall-regex@7.27.1': - resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-duplicate-keys@7.27.1': - resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': - resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-dynamic-import@7.27.1': - resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-exponentiation-operator@7.27.1': - resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-export-namespace-from@7.27.1': - resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-for-of@7.27.1': - resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-function-name@7.27.1': - resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-json-strings@7.27.1': - resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-literals@7.27.1': - resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-logical-assignment-operators@7.27.1': - resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-member-expression-literals@7.27.1': - resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-amd@7.27.1': - resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.27.1': - resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@better-scroll/shared-utils@2.5.1': + resolution: {integrity: sha512-AplkfSjXVYP9LZiD6JsKgmgQJ/mG4uuLmBuwLz8W5OsYc7AYTfN8kw6GqZ5OwCGoXkVhBGyd8NeC4xwYItp0aw==} - '@babel/plugin-transform-modules-systemjs@7.27.1': - resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-umd@7.27.1': - resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': - resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-new-target@7.27.1': - resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': - resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-numeric-separator@7.27.1': - resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-object-rest-spread@7.27.2': - resolution: {integrity: sha512-AIUHD7xJ1mCrj3uPozvtngY3s0xpv7Nu7DoUSnzNY6Xam1Cy4rUznR//pvMHOhQ4AvbCexhbqXCtpxGHOGOO6g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-object-super@7.27.1': - resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-optional-catch-binding@7.27.1': - resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-optional-chaining@7.27.1': - resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-parameters@7.27.1': - resolution: {integrity: sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-private-methods@7.27.1': - resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-private-property-in-object@7.27.1': - resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-property-literals@7.27.1': - resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-regenerator@7.27.1': - resolution: {integrity: sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-regexp-modifiers@7.27.1': - resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-reserved-words@7.27.1': - resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-runtime@7.27.1': - resolution: {integrity: sha512-TqGF3desVsTcp3WrJGj4HfKokfCXCLcHpt4PJF0D8/iT6LPd9RS82Upw3KPeyr6B22Lfd3DO8MVrmp0oRkUDdw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-shorthand-properties@7.27.1': - resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-spread@7.27.1': - resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-sticky-regex@7.27.1': - resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-template-literals@7.27.1': - resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typeof-symbol@7.27.1': - resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-escapes@7.27.1': - resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-property-regex@7.27.1': - resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-regex@7.27.1': - resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-sets-regex@7.27.1': - resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/preset-env@7.27.2': - resolution: {integrity: sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-modules@0.1.6-no-external-plugins': - resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} - peerDependencies: - '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.27.1': - resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} - engines: {node: '>=6.9.0'} + '@bufbuild/protobuf@2.6.2': + resolution: {integrity: sha512-vLu7SRY84CV/Dd+NUdgtidn2hS5hSMUC1vDBY0VcviTdgRYkU43vIz3vIFbmx14cX1r+mM7WjzE5Fl1fGEM0RQ==} '@ctrl/tinycolor@3.6.1': resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} engines: {node: '>=10'} - '@discoveryjs/json-ext@0.5.7': - resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} - engines: {node: '>=10.0.0'} - '@element-plus/icons-vue@2.3.1': resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==} peerDependencies: vue: ^3.2.0 - '@eslint/eslintrc@0.4.3': - resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} - engines: {node: ^10.12.0 || >=12.0.0} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] - '@floating-ui/core@1.7.0': - resolution: {integrity: sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==} + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] - '@floating-ui/dom@1.7.0': - resolution: {integrity: sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==} + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] - '@floating-ui/utils@0.2.9': - resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.7.2': + resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} + + '@floating-ui/dom@1.7.2': + resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} '@gera2ld/jsx-dom@2.2.2': resolution: {integrity: sha512-EOqf31IATRE6zS1W1EoWmXZhGfLAoO9FIlwTtHduSrBdud4npYBxYAkv8dZ5hudDPwJeeSjn40kbCL4wAzr8dA==} - '@hapi/hoek@9.3.0': - resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} - - '@hapi/topo@5.1.0': - resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} - - '@humanwhocodes/config-array@0.5.0': - resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - - '@humanwhocodes/object-schema@1.2.1': - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - deprecated: Use @eslint/object-schema instead - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.10': + resolution: {integrity: sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==} - '@jridgewell/source-map@0.3.6': - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} '@jsonjoy.com/base64@1.1.2': resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} @@ -766,27 +384,20 @@ packages: peerDependencies: tslib: '2' - '@jsonjoy.com/json-pack@1.2.0': - resolution: {integrity: sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==} + '@jsonjoy.com/json-pack@1.4.0': + resolution: {integrity: sha512-Akn8XZqN3xO9YGcgvIiTauBBXTP92QSvw6EcGha+p5nm7brhbwvev5gw4fi+ouLGrBpfPpb72+S5pxl4mkMIGQ==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/util@1.6.0': - resolution: {integrity: sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==} + '@jsonjoy.com/util@1.8.0': + resolution: {integrity: sha512-HeR0JQNEdBozt+FrfyM5T0X3R+fIN0D+BRDkxPP5o41fTWzHfeZEqtK16aTW8haU+h+SG7XYq9PP5kobvOmkSA==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@leichtgewicht/ip-codec@2.0.5': - resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - - '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': - resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} - - '@node-ipc/js-queue@2.0.3': - resolution: {integrity: sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==} - engines: {node: '>=1.0.0'} + '@microsoft/fetch-event-source@2.0.1': + resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -800,54 +411,133 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@openai/realtime-api-beta@https://codeload.github.com/openai/openai-realtime-api-beta/tar.gz/a5cb94824f625423858ebacb9f769226ca98945f': - resolution: {tarball: https://codeload.github.com/openai/openai-realtime-api-beta/tar.gz/a5cb94824f625423858ebacb9f769226ca98945f} - version: 0.0.0 - '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - - '@sideway/address@4.1.5': - resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} - - '@sideway/formula@3.0.1': - resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} - - '@sideway/pinpoint@2.0.0': - resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} - - '@soda/friendly-errors-webpack-plugin@1.8.1': - resolution: {integrity: sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==} - engines: {node: '>=8.0.0'} + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - '@soda/get-current-script@1.0.2': - resolution: {integrity: sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==} + '@rollup/rollup-android-arm-eabi@4.46.1': + resolution: {integrity: sha512-oENme6QxtLCqjChRUUo3S6X8hjCXnWmJWnedD7VbGML5GUtaOtAyx+fEEXnBXVf0CBZApMQU0Idwi0FmyxzQhw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.46.1': + resolution: {integrity: sha512-OikvNT3qYTl9+4qQ9Bpn6+XHM+ogtFadRLuT2EXiFQMiNkXFLQfNVppi5o28wvYdHL2s3fM0D/MZJ8UkNFZWsw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.46.1': + resolution: {integrity: sha512-EFYNNGij2WllnzljQDQnlFTXzSJw87cpAs4TVBAWLdkvic5Uh5tISrIL6NRcxoh/b2EFBG/TK8hgRrGx94zD4A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.46.1': + resolution: {integrity: sha512-ZaNH06O1KeTug9WI2+GRBE5Ujt9kZw4a1+OIwnBHal92I8PxSsl5KpsrPvthRynkhMck4XPdvY0z26Cym/b7oA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.46.1': + resolution: {integrity: sha512-n4SLVebZP8uUlJ2r04+g2U/xFeiQlw09Me5UFqny8HGbARl503LNH5CqFTb5U5jNxTouhRjai6qPT0CR5c/Iig==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.46.1': + resolution: {integrity: sha512-8vu9c02F16heTqpvo3yeiu7Vi1REDEC/yES/dIfq3tSXe6mLndiwvYr3AAvd1tMNUqE9yeGYa5w7PRbI5QUV+w==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.46.1': + resolution: {integrity: sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.46.1': + resolution: {integrity: sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.46.1': + resolution: {integrity: sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.46.1': + resolution: {integrity: sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loongarch64-gnu@4.46.1': + resolution: {integrity: sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-gnu@4.46.1': + resolution: {integrity: sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.46.1': + resolution: {integrity: sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.46.1': + resolution: {integrity: sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.46.1': + resolution: {integrity: sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.46.1': + resolution: {integrity: sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.46.1': + resolution: {integrity: sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.46.1': + resolution: {integrity: sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.46.1': + resolution: {integrity: sha512-7Thzy9TMXDw9AU4f4vsLNBxh7/VOKuXi73VH3d/kHGr0tZ3x/ewgL9uC7ojUKmH1/zvmZe2tLapYcZllk3SO8Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.46.1': + resolution: {integrity: sha512-7GVB4luhFmGUNXXJhH2jJwZCFB3pIOixv2E3s17GQHBFUOQaISlt7aGcQgqvCaDSxTZJUzlK/QJ1FN8S94MrzQ==} + cpu: [x64] + os: [win32] '@sxzz/popperjs-es@2.11.7': resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} - '@trysound/sax@0.2.0': - resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} - engines: {node: '>=10.13.0'} - - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - - '@types/bonjour@3.5.13': - resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} - - '@types/connect-history-api-fallback@1.5.4': - resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/d3-array@3.2.1': resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} @@ -944,90 +634,30 @@ packages: '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - '@types/eslint@8.56.12': - resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} - '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - - '@types/express-serve-static-core@4.19.6': - resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} - - '@types/express-serve-static-core@5.0.6': - resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} - - '@types/express@4.17.21': - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - '@types/html-minifier-terser@6.1.0': - resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} - - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - - '@types/http-proxy@1.17.16': - resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - '@types/lodash@4.17.16': - resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - - '@types/minimist@1.2.5': - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - - '@types/node-forge@1.3.11': - resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - - '@types/node@22.15.18': - resolution: {integrity: sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/parse-json@4.0.2': - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - - '@types/qs@6.9.18': - resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - - '@types/serve-index@1.9.4': - resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - - '@types/sockjs@0.3.36': - resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} + '@types/node@24.1.0': + resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==} '@types/web-bluetooth@0.0.16': resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@vant/popperjs@1.3.0': resolution: {integrity: sha512-hB+czUG+aHtjhaEmCJDuXOep0YTZjdlRR+4MSmIFnkCQIxJaXLQdSsR90XWvAI2yvKUI7TCGqR8pQg2RtvkMHw==} @@ -1036,176 +666,44 @@ packages: peerDependencies: vue: ^3.0.0 - '@vue/babel-helper-vue-jsx-merge-props@1.4.0': - resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==} - - '@vue/babel-helper-vue-transform-on@1.4.0': - resolution: {integrity: sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==} - - '@vue/babel-plugin-jsx@1.4.0': - resolution: {integrity: sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==} + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - '@babel/core': ^7.0.0-0 - peerDependenciesMeta: - '@babel/core': - optional: true + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 - '@vue/babel-plugin-resolve-type@1.4.0': - resolution: {integrity: sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/compiler-core@3.5.18': + resolution: {integrity: sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==} - '@vue/babel-plugin-transform-vue-jsx@1.4.0': - resolution: {integrity: sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/compiler-dom@3.5.18': + resolution: {integrity: sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==} - '@vue/babel-preset-app@5.0.8': - resolution: {integrity: sha512-yl+5qhpjd8e1G4cMXfORkkBlvtPCIgmRf3IYCWYDKIQ7m+PPa5iTm4feiNmCMD6yGqQWMhhK/7M3oWGL9boKwg==} - peerDependencies: - '@babel/core': '*' - core-js: ^3 - vue: ^2 || ^3.2.13 - peerDependenciesMeta: - core-js: - optional: true - vue: - optional: true + '@vue/compiler-sfc@3.5.18': + resolution: {integrity: sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==} - '@vue/babel-preset-jsx@1.4.0': - resolution: {integrity: sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - vue: '*' - peerDependenciesMeta: - vue: - optional: true - - '@vue/babel-sugar-composition-api-inject-h@1.4.0': - resolution: {integrity: sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/babel-sugar-composition-api-render-instance@1.4.0': - resolution: {integrity: sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/babel-sugar-functional-vue@1.4.0': - resolution: {integrity: sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/babel-sugar-inject-h@1.4.0': - resolution: {integrity: sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/babel-sugar-v-model@1.4.0': - resolution: {integrity: sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/babel-sugar-v-on@1.4.0': - resolution: {integrity: sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@vue/cli-overlay@5.0.8': - resolution: {integrity: sha512-KmtievE/B4kcXp6SuM2gzsnSd8WebkQpg3XaB6GmFh1BJGRqa1UiW9up7L/Q67uOdTigHxr5Ar2lZms4RcDjwQ==} - - '@vue/cli-plugin-babel@5.0.8': - resolution: {integrity: sha512-a4qqkml3FAJ3auqB2kN2EMPocb/iu0ykeELwed+9B1c1nQ1HKgslKMHMPavYx3Cd/QAx2mBD4hwKBqZXEI/CsQ==} - peerDependencies: - '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0 - - '@vue/cli-plugin-eslint@5.0.8': - resolution: {integrity: sha512-d11+I5ONYaAPW1KyZj9GlrV/E6HZePq5L5eAF5GgoVdu6sxr6bDgEoxzhcS1Pk2eh8rn1MxG/FyyR+eCBj/CNg==} - peerDependencies: - '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0 - eslint: '>=7.5.0' - - '@vue/cli-plugin-router@5.0.8': - resolution: {integrity: sha512-Gmv4dsGdAsWPqVijz3Ux2OS2HkMrWi1ENj2cYL75nUeL+Xj5HEstSqdtfZ0b1q9NCce+BFB6QnHfTBXc/fCvMg==} - peerDependencies: - '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0 - - '@vue/cli-plugin-vuex@5.0.8': - resolution: {integrity: sha512-HSYWPqrunRE5ZZs8kVwiY6oWcn95qf/OQabwLfprhdpFWAGtLStShjsGED2aDpSSeGAskQETrtR/5h7VqgIlBA==} - peerDependencies: - '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0 - - '@vue/cli-service@5.0.8': - resolution: {integrity: sha512-nV7tYQLe7YsTtzFrfOMIHc5N2hp5lHG2rpYr0aNja9rNljdgcPZLyQRb2YRivTHqTv7lI962UXFURcpStHgyFw==} - engines: {node: ^12.0.0 || >= 14.0.0} - hasBin: true - peerDependencies: - cache-loader: '*' - less-loader: '*' - pug-plain-loader: '*' - raw-loader: '*' - sass-loader: '*' - stylus-loader: '*' - vue-template-compiler: ^2.0.0 - webpack-sources: '*' - peerDependenciesMeta: - cache-loader: - optional: true - less-loader: - optional: true - pug-plain-loader: - optional: true - raw-loader: - optional: true - sass-loader: - optional: true - stylus-loader: - optional: true - vue-template-compiler: - optional: true - webpack-sources: - optional: true - - '@vue/cli-shared-utils@5.0.8': - resolution: {integrity: sha512-uK2YB7bBVuQhjOJF+O52P9yFMXeJVj7ozqJkwYE9PlMHL1LMHjtCYm4cSdOebuPzyP+/9p0BimM/OqxsevIopQ==} - - '@vue/compiler-core@3.5.14': - resolution: {integrity: sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==} - - '@vue/compiler-dom@3.5.14': - resolution: {integrity: sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==} - - '@vue/compiler-sfc@3.5.14': - resolution: {integrity: sha512-9T6m/9mMr81Lj58JpzsiSIjBgv2LiVoWjIVa7kuXHICUi8LiDSIotMpPRXYJsXKqyARrzjT24NAwttrMnMaCXA==} - - '@vue/compiler-ssr@3.5.14': - resolution: {integrity: sha512-Y0G7PcBxr1yllnHuS/NxNCSPWnRGH4Ogrp0tsLA5QemDZuJLs99YjAKQ7KqkHE0vCg4QTKlQzXLKCMF7WPSl7Q==} - - '@vue/component-compiler-utils@3.3.0': - resolution: {integrity: sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==} + '@vue/compiler-ssr@3.5.18': + resolution: {integrity: sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==} '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - '@vue/reactivity@3.5.14': - resolution: {integrity: sha512-7cK1Hp343Fu/SUCCO52vCabjvsYu7ZkOqyYu7bXV9P2yyfjUMUXHZafEbq244sP7gf+EZEz+77QixBTuEqkQQw==} + '@vue/reactivity@3.5.18': + resolution: {integrity: sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==} - '@vue/runtime-core@3.5.14': - resolution: {integrity: sha512-w9JWEANwHXNgieAhxPpEpJa+0V5G0hz3NmjAZwlOebtfKyp2hKxKF0+qSh0Xs6/PhfGihuSdqMprMVcQU/E6ag==} + '@vue/runtime-core@3.5.18': + resolution: {integrity: sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==} - '@vue/runtime-dom@3.5.14': - resolution: {integrity: sha512-lCfR++IakeI35TVR80QgOelsUIdcKjd65rWAMfdSlCYnaEY5t3hYwru7vvcWaqmrK+LpI7ZDDYiGU5V3xjMacw==} + '@vue/runtime-dom@3.5.18': + resolution: {integrity: sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==} - '@vue/server-renderer@3.5.14': - resolution: {integrity: sha512-Rf/ISLqokIvcySIYnv3tNWq40PLpNLDLSJwwVWzG6MNtyIhfbcrAxo5ZL9nARJhqjZyWWa40oRb2IDuejeuv6w==} + '@vue/server-renderer@3.5.18': + resolution: {integrity: sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==} peerDependencies: - vue: 3.5.14 + vue: 3.5.18 - '@vue/shared@3.5.14': - resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==} - - '@vue/web-component-wrapper@1.3.0': - resolution: {integrity: sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==} + '@vue/shared@3.5.18': + resolution: {integrity: sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==} '@vueuse/core@9.13.0': resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} @@ -1271,33 +769,17 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn: ^8.14.0 - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - - acorn@7.4.1: - resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true - - address@1.2.2: - resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} - engines: {node: '>= 10.0.0'} - ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1306,19 +788,11 @@ packages: ajv: optional: true - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: ajv: ^8.8.2 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -1329,19 +803,6 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - ansi-escapes@3.2.0: - resolution: {integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==} - engines: {node: '>=4'} - - ansi-html-community@0.0.8: - resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} - engines: {'0': node >= 0.8.0} - hasBin: true - - ansi-regex@3.0.1: - resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} - engines: {node: '>=4'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1350,10 +811,6 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1369,9 +826,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - arch@2.2.0: - resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} - arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -1381,30 +835,12 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - async-validator@4.2.5: resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - atob@2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} engines: {node: '>= 4.5.0'} @@ -1423,113 +859,51 @@ packages: axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} - babel-loader@8.4.1: - resolution: {integrity: sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==} - engines: {node: '>= 8.9'} - peerDependencies: - '@babel/core': ^7.0.0 - webpack: '>=2' - - babel-plugin-dynamic-import-node@2.3.3: - resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} - - babel-plugin-polyfill-corejs2@0.4.13: - resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - babel-plugin-polyfill-corejs3@0.11.1: - resolution: {integrity: sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - babel-plugin-polyfill-regenerator@0.6.4: - resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - batch@0.6.1: - resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - - big.js@5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - - bignumber.js@9.3.0: - resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - blueimp-canvas-to-blob@3.29.0: resolution: {integrity: sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==} - body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - bonjour-service@1.3.0: - resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.5: - resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-builder@0.2.0: + resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camel-case@4.1.2: - resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -1538,27 +912,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-api@3.0.0: - resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - - caniuse-lite@1.0.30001718: - resolution: {integrity: sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==} - - case-sensitive-paths-webpack-plugin@2.4.0: - resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} - engines: {node: '>=4'} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + caniuse-lite@1.0.30001731: + resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==} cheerio-select@1.6.0: resolution: {integrity: sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==} @@ -1582,69 +937,21 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - ci-info@1.6.0: - resolution: {integrity: sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==} - - clean-css@5.3.3: - resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} - engines: {node: '>= 10.0'} - - cli-cursor@2.1.0: - resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} - engines: {node: '>=4'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-highlight@2.1.11: - resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} - engines: {node: '>=8.0.0', npm: '>=5.0.0'} - hasBin: true - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - clipboard@2.0.11: resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==} - clipboardy@2.3.0: - resolution: {integrity: sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==} - engines: {node: '>=8'} - cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - - clone-deep@4.0.1: - resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} - engines: {node: '>=6'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + colorjs.io@0.5.2: + resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} @@ -1673,290 +980,33 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - - compressible@2.0.18: - resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} - engines: {node: '>= 0.6'} - - compression@1.8.0: - resolution: {integrity: sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==} - engines: {node: '>= 0.8.0'} - compressorjs@1.2.1: resolution: {integrity: sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==} concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - connect-history-api-fallback@2.0.0: - resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} - engines: {node: '>=0.8'} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - consolidate@0.15.1: - resolution: {integrity: sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==} - engines: {node: '>= 0.10.0'} - deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog - peerDependencies: - arc-templates: ^0.5.3 - atpl: '>=0.7.6' - babel-core: ^6.26.3 - bracket-template: ^1.1.5 - coffee-script: ^1.12.7 - dot: ^1.1.3 - dust: ^0.3.0 - dustjs-helpers: ^1.7.4 - dustjs-linkedin: ^2.7.5 - eco: ^1.1.0-rc-3 - ect: ^0.5.9 - ejs: ^3.1.5 - haml-coffee: ^1.14.1 - hamlet: ^0.3.3 - hamljs: ^0.6.2 - handlebars: ^4.7.6 - hogan.js: ^3.0.2 - htmling: ^0.0.8 - jade: ^1.11.0 - jazz: ^0.0.18 - jqtpl: ~1.1.0 - just: ^0.1.8 - liquid-node: ^3.0.1 - liquor: ^0.0.5 - lodash: ^4.17.20 - marko: ^3.14.4 - mote: ^0.2.0 - mustache: ^3.0.0 - nunjucks: ^3.2.2 - plates: ~0.4.11 - pug: ^3.0.0 - qejs: ^3.0.5 - ractive: ^1.3.12 - razor-tmpl: ^1.3.1 - react: ^16.13.1 - react-dom: ^16.13.1 - slm: ^2.0.0 - squirrelly: ^5.1.0 - swig: ^1.4.2 - swig-templates: ^2.0.3 - teacup: ^2.0.0 - templayed: '>=0.2.3' - then-jade: '*' - then-pug: '*' - tinyliquid: ^0.2.34 - toffee: ^0.3.6 - twig: ^1.15.2 - twing: ^5.0.2 - underscore: ^1.11.0 - vash: ^0.13.0 - velocityjs: ^2.0.1 - walrus: ^0.10.1 - whiskers: ^0.4.0 - peerDependenciesMeta: - arc-templates: - optional: true - atpl: - optional: true - babel-core: - optional: true - bracket-template: - optional: true - coffee-script: - optional: true - dot: - optional: true - dust: - optional: true - dustjs-helpers: - optional: true - dustjs-linkedin: - optional: true - eco: - optional: true - ect: - optional: true - ejs: - optional: true - haml-coffee: - optional: true - hamlet: - optional: true - hamljs: - optional: true - handlebars: - optional: true - hogan.js: - optional: true - htmling: - optional: true - jade: - optional: true - jazz: - optional: true - jqtpl: - optional: true - just: - optional: true - liquid-node: - optional: true - liquor: - optional: true - lodash: - optional: true - marko: - optional: true - mote: - optional: true - mustache: - optional: true - nunjucks: - optional: true - plates: - optional: true - pug: - optional: true - qejs: - optional: true - ractive: - optional: true - razor-tmpl: - optional: true - react: - optional: true - react-dom: - optional: true - slm: - optional: true - squirrelly: - optional: true - swig: - optional: true - swig-templates: - optional: true - teacup: - optional: true - templayed: - optional: true - then-jade: - optional: true - then-pug: - optional: true - tinyliquid: - optional: true - toffee: - optional: true - twig: - optional: true - twing: - optional: true - underscore: - optional: true - vash: - optional: true - velocityjs: - optional: true - walrus: - optional: true - whiskers: - optional: true + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - - cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - - cookie@0.7.1: - resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} - engines: {node: '>= 0.6'} - - copy-webpack-plugin@9.1.0: - resolution: {integrity: sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.1.0 - - core-js-compat@3.42.0: - resolution: {integrity: sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==} - - core-js@3.42.0: - resolution: {integrity: sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - - cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} - - cross-spawn@6.0.6: - resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} - engines: {node: '>=4.8'} + core-js@3.44.0: + resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css-declaration-sorter@6.4.1: - resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} - engines: {node: ^10 || ^12 || >=14} - peerDependencies: - postcss: ^8.0.9 - - css-loader@6.11.0: - resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@rspack/core': 0.x || 1.x - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - - css-minimizer-webpack-plugin@3.4.1: - resolution: {integrity: sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@parcel/css': '*' - clean-css: '*' - csso: '*' - esbuild: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - '@parcel/css': - optional: true - clean-css: - optional: true - csso: - optional: true - esbuild: - optional: true - css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - css-tree@1.1.3: - resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} - engines: {node: '>=8.0.0'} - - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} css@3.0.0: @@ -1967,28 +1017,6 @@ packages: engines: {node: '>=4'} hasBin: true - cssnano-preset-default@5.2.14: - resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - cssnano-utils@3.1.0: - resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - cssnano@5.1.15: - resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - csso@4.2.0: - resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} - engines: {node: '>=8.0.0'} - csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -2128,17 +1156,6 @@ packages: dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - debounce@1.2.1: - resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} - - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -2156,32 +1173,6 @@ packages: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - deepmerge@1.5.2: - resolution: {integrity: sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==} - engines: {node: '>=0.10.0'} - - default-gateway@6.0.3: - resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} - engines: {node: '>= 10'} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - delaunator@5.0.1: resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} @@ -2192,45 +1183,15 @@ packages: delegate@3.2.0: resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} - depd@1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} - engines: {node: '>= 0.6'} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - detect-node@2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dns-packet@5.6.1: - resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} - engines: {node: '>=6'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - dom-converter@0.2.0: - resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} - dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -2258,41 +1219,21 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - - dotenv-expand@5.1.0: - resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - - dotenv@10.0.0: - resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} - engines: {node: '>=10'} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - easy-stack@1.0.1: - resolution: {integrity: sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==} - engines: {node: '>=6.0.0'} - echarts@5.6.0: resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.192: + resolution: {integrity: sha512-rP8Ez0w7UNw/9j5eSXCe10o1g/8B1P5SM90PCCMVkIRQn2R0LEHWz4Eh9RnxkniuDe1W0cTSOB3MLlkTGDcuCg==} - electron-to-chromium@1.5.155: - resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==} - - element-plus@2.9.10: - resolution: {integrity: sha512-W2v9jWnm1kl/zm4bSvCh8aFCVlxvhG3fmqiDZwyd6WQiWGE595J/mpjcCggEr+49QDgIymhXrpPMOPPSARUbng==} + element-plus@2.10.4: + resolution: {integrity: sha512-UD4elWHrCnp1xlPhbXmVcaKFLCRaRAY6WWRwemGfGW3ceIjXm9fSYc9RNH3AiOEA6Ds1p9ZvhCs76CR9J8Vd+A==} peerDependencies: vue: ^3.2.0 @@ -2302,29 +1243,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - emojis-list@3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} - engines: {node: '>= 4'} - - encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - - enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} engines: {node: '>=10.13.0'} - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} @@ -2336,16 +1258,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.0: - resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -2365,6 +1281,11 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -2376,84 +1297,18 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-plugin-vue@8.7.1: - resolution: {integrity: sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-utils@2.1.0: - resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} - engines: {node: '>=6'} - - eslint-utils@3.0.0: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - - eslint-visitor-keys@1.3.0: - resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} - engines: {node: '>=4'} - - eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-webpack-plugin@3.2.0: - resolution: {integrity: sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==} - engines: {node: '>= 12.13.0'} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - webpack: ^5.0.0 - - eslint@7.32.0: - resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} - engines: {node: ^10.12.0 || >=12.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} - espree@7.3.1: - resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} - engines: {node: ^10.12.0 || >=12.0.0} - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -2469,40 +1324,15 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - event-pubsub@4.3.0: - resolution: {integrity: sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==} - engines: {node: '>=4.0.0'} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - execa@0.8.0: - resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==} - engines: {node: '>=4'} - - execa@1.0.0: - resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} - engines: {node: '>=6'} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - express@4.21.2: - resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} - engines: {node: '>= 0.10.0'} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2511,57 +1341,20 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - faye-websocket@0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} - engines: {node: '>=0.8.0'} - - figures@2.0.0: - resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} - engines: {node: '>=4'} - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.3.1: - resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} - engines: {node: '>= 0.8'} - - find-cache-dir@3.3.2: - resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} - engines: {node: '>=8'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} @@ -2575,28 +1368,13 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - - fs-extra@9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - - fs-monkey@1.0.6: - resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2608,13 +1386,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - functional-red-black-tree@1.0.1: - resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2627,18 +1398,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@3.0.0: - resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} - engines: {node: '>=4'} - - get-stream@4.1.0: - resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} - engines: {node: '>=6'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2658,18 +1417,6 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - good-listener@1.2.2: resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==} @@ -2683,24 +1430,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - gzip-size@6.0.0: - resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} - engines: {node: '>=10'} - - handle-thing@2.0.1: - resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -2709,60 +1442,14 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hash-sum@1.0.2: - resolution: {integrity: sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==} - - hash-sum@2.0.0: - resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - highlight.js@11.11.1: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - hpack.js@2.1.6: - resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} - - html-entities@2.6.0: - resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - html-minifier-terser@6.1.0: - resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} - engines: {node: '>=12'} - hasBin: true - - html-tags@2.0.0: - resolution: {integrity: sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==} - engines: {node: '>=4'} - - html-webpack-plugin@5.6.3: - resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} - engines: {node: '>=10.13.0'} - peerDependencies: - '@rspack/core': 0.x || 1.x - webpack: ^5.20.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - htmlparser2@5.0.1: resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} @@ -2772,81 +1459,21 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - http-deceiver@1.2.7: - resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} - - http-errors@1.6.3: - resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} - engines: {node: '>= 0.6'} - - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - - http-parser-js@0.5.10: - resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} - - http-proxy-middleware@2.0.9: - resolution: {integrity: sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/express': ^4.17.13 - peerDependenciesMeta: - '@types/express': - optional: true - - http-proxy@1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} - engines: {node: '>=8.0.0'} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - hyperdyperid@1.2.0: resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} engines: {node: '>=10.18'} - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - icss-utils@5.1.0: - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore@4.0.6: - resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} - engines: {node: '>= 4'} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.3: - resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2854,17 +1481,6 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -2873,30 +1489,14 @@ packages: resolution: {integrity: sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==} engines: {node: '>=6'} - is-ci@1.2.1: - resolution: {integrity: sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==} - hasBin: true - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-file-esm@1.0.0: - resolution: {integrity: sha512-rZlaNKb4Mr8WlRu2A9XdeoKgnO5aA53XdPHgCKVyCrQ/rWi89RET1+bq37Ru46obaQXeiX4vmFIm1vks41hoSA==} - - is-fullwidth-code-point@2.0.0: - resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} - engines: {node: '>=4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -2905,127 +1505,40 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-plain-obj@3.0.0: - resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} - engines: {node: '>=10'} - - is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - - is-stream@1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isobject@3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} - engines: {node: '>=0.10.0'} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - javascript-stringify@2.1.0: - resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} - jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jest-worker@28.1.3: - resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - joi@17.13.3: - resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} - - js-message@1.0.7: - resolution: {integrity: sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==} - engines: {node: '>=0.6.0'} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - juice@8.1.0: resolution: {integrity: sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==} engines: {node: '>=10.0.0'} @@ -3035,31 +1548,6 @@ packages: resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} hasBin: true - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - klona@2.0.6: - resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} - engines: {node: '>= 8'} - - launch-editor-middleware@2.10.0: - resolution: {integrity: sha512-RzZu7MeVlE3p1H6Sadc2BhuDGAj7bkeDCBpNq/zSENP4ohJGhso00k5+iYaRwKshIpiOAhMmimce+5D389xmSg==} - - launch-editor@2.10.0: - resolution: {integrity: sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -3074,13 +1562,13 @@ packages: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} - loader-utils@1.4.2: - resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} - engines: {node: '>=4.0.0'} + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} - loader-utils@2.0.4: - resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} - engines: {node: '>=8.9.0'} + local-pkg@1.1.1: + resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} + engines: {node: '>=14'} locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} @@ -3096,64 +1584,15 @@ packages: lodash: '*' lodash-es: '*' - lodash.debounce@4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - - lodash.defaultsdeep@4.6.1: - resolution: {integrity: sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==} - - lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - - lodash.mapvalues@4.6.0: - resolution: {integrity: sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==} - - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - - lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - log-update@2.3.0: - resolution: {integrity: sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==} - engines: {node: '>=4'} - - lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - markdown-it-emoji@2.0.2: resolution: {integrity: sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==} @@ -3164,6 +1603,11 @@ packages: resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} hasBin: true + marked@15.0.12: + resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} + engines: {node: '>= 18'} + hasBin: true + markmap-common@0.16.0: resolution: {integrity: sha512-q3nlNDMKuWXTm3VwZFY9V5zteL/+iBLZanUK5vS+e26bUbzTSG5VtAzsyJbmgJm1WhwmIIAxbXEnp6JdvtTduA==} @@ -3199,22 +1643,11 @@ packages: peerDependencies: vue: ^3.2.47 - mdn-data@2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - mdurl@1.0.1: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - - memfs@3.5.3: - resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} - engines: {node: '>= 4.0.0'} - - memfs@4.17.2: - resolution: {integrity: sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==} + memfs@4.23.0: + resolution: {integrity: sha512-SucHN2lcWf0jrnw+jP6FoVW6l/zGJiXfNMdApZzG0x/0mAIMdwAeR5mjfsCH5U3BoqpUEtqzz+dSQSO0H/eqxg==} engines: {node: '>= 4.0.0'} memoize-one@6.0.0: @@ -3223,12 +1656,6 @@ packages: mensch@0.3.4: resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} - merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - - merge-source-map@1.1.0: - resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -3236,10 +1663,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - mhchemparser@4.2.1: resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} @@ -3251,41 +1674,15 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - mime@2.6.0: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} engines: {node: '>=4.0.0'} hasBin: true - mimic-fn@1.2.0: - resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} - engines: {node: '>=4'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mini-css-extract-plugin@2.9.2: - resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3293,13 +1690,6 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -3307,23 +1697,12 @@ packages: mj-context-menu@0.6.1: resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==} - module-alias@2.2.3: - resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==} - - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - multicast-dns@7.2.5: - resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} - hasBin: true - mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3332,26 +1711,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - negotiator@0.6.4: - resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} - engines: {node: '>= 0.6'} - neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - nice-try@1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - - no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -3361,20 +1723,9 @@ packages: encoding: optional: true - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} - engines: {node: '>= 6.13.0'} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-path@1.0.0: - resolution: {integrity: sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==} - engines: {node: '>=0.10.0'} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3383,21 +1734,9 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - normalize-wheel-es@1.2.0: resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} - npm-run-path@2.0.2: - resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} - engines: {node: '>=4'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - npm2url@0.2.4: resolution: {integrity: sha512-arzGp/hQz0Ey+ZGhF64XVH7Xqwd+1Q/po5uGiBbzph8ebX6T0uvt3N7c1nBHQNsQVykQgHhqoRTX7JFcHecGuw==} @@ -3416,56 +1755,9 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - obuf@1.1.2: - resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - on-headers@1.0.2: - resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} - engines: {node: '>= 0.8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@2.0.1: - resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} - engines: {node: '>=4'} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - - opener@1.5.2: - resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} - hasBin: true - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3474,10 +1766,6 @@ packages: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -3485,39 +1773,18 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} - parse5@5.1.1: - resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} - parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3526,10 +1793,6 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3541,15 +1804,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@0.1.12: - resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - picocolors@0.2.1: - resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3558,6 +1814,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -3575,59 +1835,16 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - portfinder@1.0.37: - resolution: {integrity: sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==} - engines: {node: '>= 10.12'} - - postcss-calc@8.2.4: - resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} - peerDependencies: - postcss: ^8.2.2 - - postcss-colormin@5.3.1: - resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-convert-values@5.1.3: - resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-comments@5.1.2: - resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-duplicates@5.1.0: - resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-empty@5.1.1: - resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-overridden@5.1.0: - resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -3652,300 +1869,52 @@ packages: ts-node: optional: true - postcss-loader@6.2.1: - resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} - engines: {node: '>= 12.13.0'} - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^5.0.0 - - postcss-merge-longhand@5.1.7: - resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-merge-rules@5.1.4: - resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-font-values@5.1.0: - resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-gradients@5.1.1: - resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-params@5.1.4: - resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-selectors@5.2.1: - resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-modules-extract-imports@3.1.0: - resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-local-by-default@4.2.0: - resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-scope@3.2.1: - resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-values@4.0.0: - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - postcss-nested@6.2.0: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 - postcss-normalize-charset@5.1.0: - resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-display-values@5.1.0: - resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-positions@5.1.1: - resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-repeat-style@5.1.1: - resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-string@5.1.0: - resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-timing-functions@5.1.0: - resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-unicode@5.1.1: - resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-url@5.1.0: - resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-whitespace@5.1.1: - resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-ordered-values@5.1.3: - resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-reduce-initial@5.1.2: - resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-reduce-transforms@5.1.0: - resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} - engines: {node: '>=4'} - - postcss-svgo@5.1.0: - resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-unique-selectors@5.1.1: - resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@7.0.39: - resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} - engines: {node: '>=6.0.0'} - - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - pretty-error@4.0.0: - resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} - prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - progress-webpack-plugin@1.0.16: - resolution: {integrity: sha512-sdiHuuKOzELcBANHfrupYo+r99iPRyOnw15qX+rNlVUqXGfjXdH4IgxriKwG1kNJwVswKQHMdj1hYZMcb9jFaA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - qrcode@1.5.4: resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} engines: {node: '>=10.13.0'} hasBin: true - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} - qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + quansync@0.2.10: + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - regenerate-unicode-properties@10.2.0: - resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} - engines: {node: '>=4'} - - regenerate@1.4.2: - resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} - - regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - - regexpu-core@6.2.0: - resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} - engines: {node: '>=4'} - - regjsgen@0.8.0: - resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - - regjsparser@0.12.0: - resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} - hasBin: true - - relateurl@0.2.7: - resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} - engines: {node: '>= 0.10'} - remarkable-katex@1.2.1: resolution: {integrity: sha512-Y1VquJBZnaVsfsVcKW2hmjT+pDL7mp8l5WAVlvuvViltrdok2m1AIKmJv8SsH+mBY84PoMw67t3kTWw1dIm8+g==} @@ -3954,9 +1923,6 @@ packages: engines: {node: '>= 6.0.0'} hasBin: true - renderkid@3.0.0: - resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3968,50 +1934,31 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} hasBin: true - restore-cursor@2.0.0: - resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} - engines: {node: '>=4'} - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rollup@4.46.1: + resolution: {integrity: sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -4019,96 +1966,134 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass-embedded-android-arm64@1.89.2: + resolution: {integrity: sha512-+pq7a7AUpItNyPu61sRlP6G2A8pSPpyazASb+8AK2pVlFayCSPAEgpwpCE9A2/Xj86xJZeMizzKUHxM2CBCUxA==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [android] + + sass-embedded-android-arm@1.89.2: + resolution: {integrity: sha512-oHAPTboBHRZlDBhyRB6dvDKh4KvFs+DZibDHXbkSI6dBZxMTT+Yb2ivocHnctVGucKTLQeT7+OM5DjWHyynL/A==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [android] + + sass-embedded-android-riscv64@1.89.2: + resolution: {integrity: sha512-HfJJWp/S6XSYvlGAqNdakeEMPOdhBkj2s2lN6SHnON54rahKem+z9pUbCriUJfM65Z90lakdGuOfidY61R9TYg==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [android] + + sass-embedded-android-x64@1.89.2: + resolution: {integrity: sha512-BGPzq53VH5z5HN8de6jfMqJjnRe1E6sfnCWFd4pK+CAiuM7iw5Fx6BQZu3ikfI1l2GY0y6pRXzsVLdp/j4EKEA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [android] + + sass-embedded-darwin-arm64@1.89.2: + resolution: {integrity: sha512-UCm3RL/tzMpG7DsubARsvGUNXC5pgfQvP+RRFJo9XPIi6elopY5B6H4m9dRYDpHA+scjVthdiDwkPYr9+S/KGw==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [darwin] + + sass-embedded-darwin-x64@1.89.2: + resolution: {integrity: sha512-D9WxtDY5VYtMApXRuhQK9VkPHB8R79NIIR6xxVlN2MIdEid/TZWi1MHNweieETXhWGrKhRKglwnHxxyKdJYMnA==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [darwin] + + sass-embedded-linux-arm64@1.89.2: + resolution: {integrity: sha512-2N4WW5LLsbtrWUJ7iTpjvhajGIbmDR18ZzYRywHdMLpfdPApuHPMDF5CYzHbS+LLx2UAx7CFKBnj5LLjY6eFgQ==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + + sass-embedded-linux-arm@1.89.2: + resolution: {integrity: sha512-leP0t5U4r95dc90o8TCWfxNXwMAsQhpWxTkdtySDpngoqtTy3miMd7EYNYd1znI0FN1CBaUvbdCMbnbPwygDlA==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + + sass-embedded-linux-musl-arm64@1.89.2: + resolution: {integrity: sha512-nTyuaBX6U1A/cG7WJh0pKD1gY8hbg1m2SnzsyoFG+exQ0lBX/lwTLHq3nyhF+0atv7YYhYKbmfz+sjPP8CZ9lw==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [linux] + + sass-embedded-linux-musl-arm@1.89.2: + resolution: {integrity: sha512-Z6gG2FiVEEdxYHRi2sS5VIYBmp17351bWtOCUZ/thBM66+e70yiN6Eyqjz80DjL8haRUegNQgy9ZJqsLAAmr9g==} + engines: {node: '>=14.0.0'} + cpu: [arm] + os: [linux] + + sass-embedded-linux-musl-riscv64@1.89.2: + resolution: {integrity: sha512-N6oul+qALO0SwGY8JW7H/Vs0oZIMrRMBM4GqX3AjM/6y8JsJRxkAwnfd0fDyK+aICMFarDqQonQNIx99gdTZqw==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + + sass-embedded-linux-musl-x64@1.89.2: + resolution: {integrity: sha512-K+FmWcdj/uyP8GiG9foxOCPfb5OAZG0uSVq80DKgVSC0U44AdGjvAvVZkrgFEcZ6cCqlNC2JfYmslB5iqdL7tg==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + + sass-embedded-linux-riscv64@1.89.2: + resolution: {integrity: sha512-g9nTbnD/3yhOaskeqeBQETbtfDQWRgsjHok6bn7DdAuwBsyrR3JlSFyqKc46pn9Xxd9SQQZU8AzM4IR+sY0A0w==} + engines: {node: '>=14.0.0'} + cpu: [riscv64] + os: [linux] + + sass-embedded-linux-x64@1.89.2: + resolution: {integrity: sha512-Ax7dKvzncyQzIl4r7012KCMBvJzOz4uwSNoyoM5IV6y5I1f5hEwI25+U4WfuTqdkv42taCMgpjZbh9ERr6JVMQ==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [linux] + + sass-embedded-win32-arm64@1.89.2: + resolution: {integrity: sha512-j96iJni50ZUsfD6tRxDQE2QSYQ2WrfHxeiyAXf41Kw0V4w5KYR/Sf6rCZQLMTUOHnD16qTMVpQi20LQSqf4WGg==} + engines: {node: '>=14.0.0'} + cpu: [arm64] + os: [win32] + + sass-embedded-win32-x64@1.89.2: + resolution: {integrity: sha512-cS2j5ljdkQsb4PaORiClaVYynE9OAPZG/XjbOMxpQmjRIf7UroY4PEIH+Waf+y47PfXFX9SyxhYuw2NIKGbEng==} + engines: {node: '>=14.0.0'} + cpu: [x64] + os: [win32] + + sass-embedded@1.89.2: + resolution: {integrity: sha512-Ack2K8rc57kCFcYlf3HXpZEJFNUX8xd8DILldksREmYXQkRHI879yy8q4mRDJgrojkySMZqmmmW1NxrFxMsYaA==} + engines: {node: '>=16.0.0'} + hasBin: true + sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - schema-utils@2.7.1: - resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} - engines: {node: '>= 8.9.0'} - - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - schema-utils@4.3.2: resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} engines: {node: '>= 10.13.0'} - select-hose@2.0.0: - resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} select@1.1.2: resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} - selfsigned@2.4.1: - resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} - engines: {node: '>=10'} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - - send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} - serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serve-index@1.9.1: - resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} - engines: {node: '>= 0.8.0'} - - serve-static@1.16.2: - resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} - engines: {node: '>= 0.8.0'} - set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - setprototypeof@1.1.0: - resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shallow-clone@3.0.1: - resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} - engines: {node: '>=8'} - - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.2: - resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} - engines: {node: '>= 0.4'} - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -4125,31 +2110,13 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sirv@2.0.4: - resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} - engines: {node: '>= 10'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - slick@1.12.2: resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} - sockjs@0.3.24: - resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} - sortablejs@1.15.6: resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} @@ -4168,28 +2135,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.21: - resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} - - spdy-transport@3.0.0: - resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} - - spdy@4.0.2: - resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} - engines: {node: '>=6.0.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} speech-rule-engine@4.1.2: resolution: {integrity: sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==} @@ -4198,29 +2146,6 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - ssri@8.0.1: - resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} - engines: {node: '>= 8'} - - stable@0.1.8: - resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} - deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' - - stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - - statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - string-width@2.1.1: - resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} - engines: {node: '>=4'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4229,16 +2154,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@4.0.0: - resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} - engines: {node: '>=4'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -4247,27 +2162,8 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-eof@1.0.0: - resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} - engines: {node: '>=0.10.0'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-indent@2.0.0: - resolution: {integrity: sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==} - engines: {node: '>=4'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - stylehacks@5.1.1: - resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + strip-literal@2.1.1: + resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} stylus-loader@7.1.3: resolution: {integrity: sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==} @@ -4285,14 +2181,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} @@ -4301,25 +2189,21 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svg-tags@1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + sync-child-process@1.0.2: + resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==} + engines: {node: '>=16.0.0'} - svgo@2.8.0: - resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} - engines: {node: '>=10.13.0'} - hasBin: true - - table@6.9.0: - resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} - engines: {node: '>=10.0.0'} + sync-message-port@1.1.3: + resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==} + engines: {node: '>=16.0.0'} tailwindcss@3.4.17: resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} engines: {node: '>=14.0.0'} hasBin: true - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} engines: {node: '>=6'} terser-webpack-plugin@5.3.14: @@ -4338,14 +2222,11 @@ packages: uglify-js: optional: true - terser@5.39.2: - resolution: {integrity: sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==} + terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} hasBin: true - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -4359,17 +2240,8 @@ packages: peerDependencies: tslib: ^2 - thread-loader@3.0.4: - resolution: {integrity: sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.27.0 || ^5.0.0 - - three@0.128.0: - resolution: {integrity: sha512-i0ap/E+OaSfzw7bD1TtYnPo3VEplkl70WX5fZqZnfZsE3k3aSFudqrrC9ldFZfYFkn1zwDmBcdGfiIm/hnbyZA==} - - thunky@1.1.0: - resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + three@0.160.1: + resolution: {integrity: sha512-Bgl2wPJypDOZ1stAxwfWAcJ0WQf7QzlptsxkjYiURPz+n5k4RBDLsq+6f9Y75TYxn6aHLcWz+JNmwTOXWrQTBQ==} tiny-emitter@2.1.0: resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} @@ -4378,19 +2250,11 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tree-dump@1.0.2: - resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} + tree-dump@1.0.3: + resolution: {integrity: sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' @@ -4404,55 +2268,33 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - unicode-canonical-property-names-ecmascript@2.0.1: - resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} - engines: {node: '>=4'} + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} - unicode-match-property-ecmascript@2.0.0: - resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} - engines: {node: '>=4'} + unimport@3.14.6: + resolution: {integrity: sha512-CYvbDaTT04Rh8bmD8jz3WPmHYZRG/NnvYVzwD6V1YAlvvKROlAeNDUBhkBGzNav2RKaeuXvlWYaa1V4Lfi/O0g==} - unicode-match-property-value-ecmascript@2.2.0: - resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} - engines: {node: '>=4'} + unplugin-auto-import@0.18.6: + resolution: {integrity: sha512-LMFzX5DtkTj/3wZuyG5bgKBoJ7WSgzqSGJ8ppDRdlvPh45mx6t6w3OcbExQi53n3xF5MYkNGPNR/HYOL95KL2A==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': ^3.2.2 + '@vueuse/core': '*' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@vueuse/core': + optional: true - unicode-property-aliases-ecmascript@2.1.0: - resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} - engines: {node: '>=4'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} + unplugin@1.16.1: + resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} + engines: {node: '>=14.0.0'} update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} @@ -4460,44 +2302,51 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - utila@0.4.0: - resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} - - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - - v3-waterfall@1.3.3: - resolution: {integrity: sha512-jUmp0xpHGkEcUxaYKGRtI5b2NvogxI/UrfoCLmpTi0UbQndDdqjwufxJvWwiJjwZQyOIPpnq9ZOFtkBwxchq3Q==} - - v8-compile-cache@2.4.0: - resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} - valid-data-url@3.0.1: resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} engines: {node: '>=10'} - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - vant@4.9.19: - resolution: {integrity: sha512-fRt32XI0fO0vB3/YGhZOpTnHKjplUiNuA05yZy8rPZntmbQE5GA57Y7iC7jmMDxSOaLebovynhgCvWnyk9zmDw==} + vant@4.9.21: + resolution: {integrity: sha512-hXUoZMrLLjykimFRLDlGNd+K2iYSRh9YwLMKnsVdVZ+9inUKxpqnjhOqlZwocbnYkvJlS+febf9u9aJpDol4Pw==} peerDependencies: vue: ^3.0.0 - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} + varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true vue-demi@0.14.10: resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} @@ -4510,75 +2359,26 @@ packages: '@vue/composition-api': optional: true - vue-eslint-parser@8.3.0: - resolution: {integrity: sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - vue-hot-reload-api@2.3.4: - resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==} - - vue-loader@15.11.1: - resolution: {integrity: sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==} - peerDependencies: - '@vue/compiler-sfc': ^3.0.8 - cache-loader: '*' - css-loader: '*' - prettier: '*' - vue-template-compiler: '*' - webpack: ^3.0.0 || ^4.1.0 || ^5.0.0-0 - peerDependenciesMeta: - '@vue/compiler-sfc': - optional: true - cache-loader: - optional: true - prettier: - optional: true - vue-template-compiler: - optional: true - - vue-loader@17.4.2: - resolution: {integrity: sha512-yTKOA4R/VN4jqjw4y5HrynFL8AK0Z3/Jt7eOJXEitsm0GMRHDBjCfCiuTiLP7OESvsZYo2pATCWhDqxC5ZrM6w==} - peerDependencies: - '@vue/compiler-sfc': '*' - vue: '*' - webpack: ^4.1.0 || ^5.0.0-0 - peerDependenciesMeta: - '@vue/compiler-sfc': - optional: true - vue: - optional: true - vue-router@4.5.1: resolution: {integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==} peerDependencies: vue: ^3.2.0 - vue-style-loader@4.1.3: - resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==} + vue-waterfall-plugin-next@2.6.7: + resolution: {integrity: sha512-B7D/6W/t/iT6yv5RlpiwmD4adkBdoF4I39sxg5hTn3GMQXhs6H5jORinzjzYW2ls4h2zqxT0NJmXRFQgPldOsQ==} - vue-template-es2015-compiler@1.9.1: - resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==} - - vue@3.5.14: - resolution: {integrity: sha512-LbOm50/vZFG6Mhy6KscQYXZMQ0LMCC/y40HDJPPvGFQ+i/lUH+PJHR6C3assgOQiXdl6tAfsXHbXYVBZZu65ew==} + vue@3.5.18: + resolution: {integrity: sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - watchpack@2.4.2: - resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} - wbuf@1.7.3: - resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - web-resource-inliner@6.0.1: resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} engines: {node: '>=10.0.0'} @@ -4586,48 +2386,15 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webpack-bundle-analyzer@4.10.2: - resolution: {integrity: sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==} - engines: {node: '>= 10.13.0'} - hasBin: true - - webpack-chain@6.5.1: - resolution: {integrity: sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==} - engines: {node: '>=8'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - webpack-dev-middleware@5.3.4: - resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - - webpack-dev-server@4.15.2: - resolution: {integrity: sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==} - engines: {node: '>= 12.13.0'} - hasBin: true - peerDependencies: - webpack: ^4.37.0 || ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - webpack: - optional: true - webpack-cli: - optional: true - - webpack-merge@5.10.0: - resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} - engines: {node: '>=10.0.0'} - - webpack-sources@3.2.3: - resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack-virtual-modules@0.4.6: - resolution: {integrity: sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - webpack@5.99.8: - resolution: {integrity: sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==} + webpack@5.101.0: + resolution: {integrity: sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -4636,27 +2403,12 @@ packages: webpack-cli: optional: true - websocket-driver@0.7.4: - resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} - engines: {node: '>=0.8.0'} - - websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} - - whatwg-fetch@3.6.20: - resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -4665,17 +2417,6 @@ packages: wicked-good-xpath@1.3.0: resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==} - wildcard@2.0.1: - resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wrap-ansi@3.0.1: - resolution: {integrity: sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==} - engines: {node: '>=4'} - wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -4691,50 +2432,9 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - yaml@2.8.0: resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} @@ -4744,792 +2444,147 @@ packages: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - - yorkie@2.0.0: - resolution: {integrity: sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==} - engines: {node: '>=4'} - zrender@5.6.1: resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} snapshots: - '@achrinza/node-ipc@9.2.9': - dependencies: - '@node-ipc/js-queue': 2.0.3 - event-pubsub: 4.3.0 - js-message: 1.0.7 - '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@babel/code-frame@7.12.11': - dependencies: - '@babel/highlight': 7.25.9 - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.27.2': {} - - '@babel/core@7.18.6': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.18.6) - '@babel/helpers': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 - convert-source-map: 1.9.0 - debug: 4.4.1 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/eslint-parser@7.27.1(@babel/core@7.18.6)(eslint@7.32.0)': - dependencies: - '@babel/core': 7.18.6 - '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 7.32.0 - eslint-visitor-keys: 2.1.0 - semver: 6.3.1 - - '@babel/generator@7.27.1': - dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.1.0 - - '@babel/helper-annotate-as-pure@7.27.1': - dependencies: - '@babel/types': 7.27.1 - - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.27.2 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.24.5 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-member-expression-to-functions': 7.27.1 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.18.6) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.27.1 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-annotate-as-pure': 7.27.1 - regexpu-core: 6.2.0 - semver: 6.3.1 - - '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - debug: 4.4.1 - lodash.debounce: 4.0.8 - resolve: 1.22.10 - transitivePeerDependencies: - - supports-color - - '@babel/helper-member-expression-to-functions@7.27.1': - dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.27.1': - dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.27.1': - dependencies: - '@babel/types': 7.27.1 - - '@babel/helper-plugin-utils@7.27.1': {} - - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-wrap-function': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-replace-supers@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-member-expression-to-functions': 7.27.1 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 - transitivePeerDependencies: - - supports-color + '@antfu/utils@0.7.10': {} '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helper-wrap-function@7.27.1': - dependencies: - '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helpers@7.27.1': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.27.1 - - '@babel/highlight@7.25.9': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/parser@7.27.2': - dependencies: - '@babel/types': 7.27.1 - - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.18.6) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-proposal-decorators@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.18.6) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - - '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.18.6) - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.18.6) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-block-scoping@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-classes@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.18.6) - '@babel/traverse': 7.27.1 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/template': 7.27.2 - - '@babel/plugin-transform-destructuring@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.18.6)': + '@babel/parser@7.28.0': dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.2 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-object-rest-spread@7.27.2(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.18.6) - - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.18.6) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-regenerator@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-runtime@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.18.6) - babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.18.6) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.18.6) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-spread@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.18.6) - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/preset-env@7.27.2(@babel/core@7.18.6)': - dependencies: - '@babel/compat-data': 7.27.2 - '@babel/core': 7.18.6 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.18.6) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.18.6) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-block-scoping': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-object-rest-spread': 7.27.2(@babel/core@7.18.6) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-regenerator': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.18.6) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.18.6) - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.18.6) - babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.18.6) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.18.6) - core-js-compat: 3.42.0 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.27.1 - esutils: 2.0.3 - - '@babel/runtime@7.27.1': {} - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 - - '@babel/traverse@7.27.1': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/template': 7.27.2 - '@babel/types': 7.27.1 - debug: 4.4.1 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@babel/runtime@7.28.2': {} - '@babel/types@7.27.1': + '@babel/types@7.28.2': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@better-scroll/core@2.5.1': + dependencies: + '@better-scroll/shared-utils': 2.5.1 + + '@better-scroll/mouse-wheel@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/observe-dom@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/pull-up@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/scroll-bar@2.5.1': + dependencies: + '@better-scroll/core': 2.5.1 + + '@better-scroll/shared-utils@2.5.1': {} + + '@bufbuild/protobuf@2.6.2': {} + '@ctrl/tinycolor@3.6.1': {} - '@discoveryjs/json-ext@0.5.7': {} - - '@element-plus/icons-vue@2.3.1(vue@3.5.14)': + '@element-plus/icons-vue@2.3.1(vue@3.5.18)': dependencies: - vue: 3.5.14 + vue: 3.5.18 - '@eslint/eslintrc@0.4.3': + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@floating-ui/core@1.7.2': dependencies: - ajv: 6.12.6 - debug: 4.4.1 - espree: 7.3.1 - globals: 13.24.0 - ignore: 4.0.6 - import-fresh: 3.3.1 - js-yaml: 3.14.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color + '@floating-ui/utils': 0.2.10 - '@floating-ui/core@1.7.0': + '@floating-ui/dom@1.7.2': dependencies: - '@floating-ui/utils': 0.2.9 + '@floating-ui/core': 1.7.2 + '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.0': - dependencies: - '@floating-ui/core': 1.7.0 - '@floating-ui/utils': 0.2.9 - - '@floating-ui/utils@0.2.9': {} + '@floating-ui/utils@0.2.10': {} '@gera2ld/jsx-dom@2.2.2': dependencies: - '@babel/runtime': 7.27.1 - - '@hapi/hoek@9.3.0': {} - - '@hapi/topo@5.1.0': - dependencies: - '@hapi/hoek': 9.3.0 - - '@humanwhocodes/config-array@0.5.0': - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.4.1 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/object-schema@1.2.1': {} + '@babel/runtime': 7.28.2 '@isaacs/cliui@8.0.2': dependencies: @@ -5540,53 +2595,42 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/gen-mapping@0.3.12': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/source-map@0.3.6': + '@jridgewell/source-map@0.3.10': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': dependencies: tslib: 2.8.1 - '@jsonjoy.com/json-pack@1.2.0(tslib@2.8.1)': + '@jsonjoy.com/json-pack@1.4.0(tslib@2.8.1)': dependencies: '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) - '@jsonjoy.com/util': 1.6.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.8.0(tslib@2.8.1) hyperdyperid: 1.2.0 thingies: 1.21.0(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/util@1.6.0(tslib@2.8.1)': + '@jsonjoy.com/util@1.8.0(tslib@2.8.1)': dependencies: tslib: 2.8.1 - '@leichtgewicht/ip-codec@2.0.5': {} - - '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': - dependencies: - eslint-scope: 5.1.1 - - '@node-ipc/js-queue@2.0.3': - dependencies: - easy-stack: 1.0.1 + '@microsoft/fetch-event-source@2.0.1': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -5600,58 +2644,79 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@openai/realtime-api-beta@https://codeload.github.com/openai/openai-realtime-api-beta/tar.gz/a5cb94824f625423858ebacb9f769226ca98945f': - dependencies: - ws: 8.18.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@pkgjs/parseargs@0.11.0': optional: true - '@polka/url@1.0.0-next.29': {} - - '@sideway/address@4.1.5': + '@rollup/pluginutils@5.2.0(rollup@4.46.1)': dependencies: - '@hapi/hoek': 9.3.0 + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.46.1 - '@sideway/formula@3.0.1': {} + '@rollup/rollup-android-arm-eabi@4.46.1': + optional: true - '@sideway/pinpoint@2.0.0': {} + '@rollup/rollup-android-arm64@4.46.1': + optional: true - '@soda/friendly-errors-webpack-plugin@1.8.1(webpack@5.99.8)': - dependencies: - chalk: 3.0.0 - error-stack-parser: 2.1.4 - string-width: 4.2.3 - strip-ansi: 6.0.1 - webpack: 5.99.8 + '@rollup/rollup-darwin-arm64@4.46.1': + optional: true - '@soda/get-current-script@1.0.2': {} + '@rollup/rollup-darwin-x64@4.46.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.46.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.46.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.46.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.46.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.46.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.46.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.46.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.46.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.46.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.46.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.46.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.46.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.46.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.46.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.46.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.46.1': + optional: true '@sxzz/popperjs-es@2.11.7': {} - '@trysound/sax@0.2.0': {} - - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 22.15.18 - - '@types/bonjour@3.5.13': - dependencies: - '@types/node': 22.15.18 - - '@types/connect-history-api-fallback@1.5.4': - dependencies: - '@types/express-serve-static-core': 5.0.6 - '@types/node': 22.15.18 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 22.15.18 - '@types/d3-array@3.2.1': {} '@types/d3-axis@3.0.6': @@ -5772,570 +2837,113 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 - '@types/estree': 1.0.7 - - '@types/eslint@8.56.12': - dependencies: - '@types/estree': 1.0.7 - '@types/json-schema': 7.0.15 + '@types/estree': 1.0.8 '@types/eslint@9.6.1': dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 - '@types/estree@1.0.7': {} - - '@types/express-serve-static-core@4.19.6': - dependencies: - '@types/node': 22.15.18 - '@types/qs': 6.9.18 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express-serve-static-core@5.0.6': - dependencies: - '@types/node': 22.15.18 - '@types/qs': 6.9.18 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express@4.17.21': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.6 - '@types/qs': 6.9.18 - '@types/serve-static': 1.15.7 + '@types/estree@1.0.8': {} '@types/geojson@7946.0.16': {} - '@types/html-minifier-terser@6.1.0': {} - - '@types/http-errors@2.0.4': {} - - '@types/http-proxy@1.17.16': - dependencies: - '@types/node': 22.15.18 - '@types/json-schema@7.0.15': {} '@types/lodash-es@4.17.12': dependencies: - '@types/lodash': 4.17.16 + '@types/lodash': 4.17.20 - '@types/lodash@4.17.16': {} + '@types/lodash@4.17.20': {} - '@types/mime@1.3.5': {} - - '@types/minimist@1.2.5': {} - - '@types/node-forge@1.3.11': + '@types/node@24.1.0': dependencies: - '@types/node': 22.15.18 - - '@types/node@22.15.18': - dependencies: - undici-types: 6.21.0 - - '@types/normalize-package-data@2.4.4': {} - - '@types/parse-json@4.0.2': {} - - '@types/qs@6.9.18': {} - - '@types/range-parser@1.2.7': {} - - '@types/retry@0.12.0': {} - - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 22.15.18 - - '@types/serve-index@1.9.4': - dependencies: - '@types/express': 4.17.21 - - '@types/serve-static@1.15.7': - dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 22.15.18 - '@types/send': 0.17.4 - - '@types/sockjs@0.3.36': - dependencies: - '@types/node': 22.15.18 + undici-types: 7.8.0 '@types/web-bluetooth@0.0.16': {} - '@types/ws@8.18.1': - dependencies: - '@types/node': 22.15.18 - '@vant/popperjs@1.3.0': {} - '@vant/use@1.6.0(vue@3.5.14)': + '@vant/use@1.6.0(vue@3.5.18)': dependencies: - vue: 3.5.14 + vue: 3.5.18 - '@vue/babel-helper-vue-jsx-merge-props@1.4.0': {} - - '@vue/babel-helper-vue-transform-on@1.4.0': {} - - '@vue/babel-plugin-jsx@1.4.0(@babel/core@7.18.6)': + '@vitejs/plugin-vue@5.2.4(vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2)(stylus@0.58.1)(terser@5.43.1))(vue@3.5.18)': dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 - '@vue/babel-helper-vue-transform-on': 1.4.0 - '@vue/babel-plugin-resolve-type': 1.4.0(@babel/core@7.18.6) - '@vue/shared': 3.5.14 - optionalDependencies: - '@babel/core': 7.18.6 - transitivePeerDependencies: - - supports-color + vite: 5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2)(stylus@0.58.1)(terser@5.43.1) + vue: 3.5.18 - '@vue/babel-plugin-resolve-type@1.4.0(@babel/core@7.18.6)': + '@vue/compiler-core@3.5.18': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.18.6 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.27.2 - '@vue/compiler-sfc': 3.5.14 - transitivePeerDependencies: - - supports-color - - '@vue/babel-plugin-transform-vue-jsx@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-module-imports': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - '@vue/babel-helper-vue-jsx-merge-props': 1.4.0 - html-tags: 2.0.0 - lodash.kebabcase: 4.1.1 - svg-tags: 1.0.0 - transitivePeerDependencies: - - supports-color - - '@vue/babel-preset-app@5.0.8(@babel/core@7.18.6)(core-js@3.42.0)(vue@3.5.14)': - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-imports': 7.27.1 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.18.6) - '@babel/plugin-proposal-decorators': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.18.6) - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - '@babel/plugin-transform-runtime': 7.27.1(@babel/core@7.18.6) - '@babel/preset-env': 7.27.2(@babel/core@7.18.6) - '@babel/runtime': 7.27.1 - '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.18.6) - '@vue/babel-preset-jsx': 1.4.0(@babel/core@7.18.6)(vue@3.5.14) - babel-plugin-dynamic-import-node: 2.3.3 - core-js-compat: 3.42.0 - semver: 7.7.2 - optionalDependencies: - core-js: 3.42.0 - vue: 3.5.14 - transitivePeerDependencies: - - supports-color - - '@vue/babel-preset-jsx@1.4.0(@babel/core@7.18.6)(vue@3.5.14)': - dependencies: - '@babel/core': 7.18.6 - '@vue/babel-helper-vue-jsx-merge-props': 1.4.0 - '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.18.6) - '@vue/babel-sugar-composition-api-inject-h': 1.4.0(@babel/core@7.18.6) - '@vue/babel-sugar-composition-api-render-instance': 1.4.0(@babel/core@7.18.6) - '@vue/babel-sugar-functional-vue': 1.4.0(@babel/core@7.18.6) - '@vue/babel-sugar-inject-h': 1.4.0(@babel/core@7.18.6) - '@vue/babel-sugar-v-model': 1.4.0(@babel/core@7.18.6) - '@vue/babel-sugar-v-on': 1.4.0(@babel/core@7.18.6) - optionalDependencies: - vue: 3.5.14 - transitivePeerDependencies: - - supports-color - - '@vue/babel-sugar-composition-api-inject-h@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - - '@vue/babel-sugar-composition-api-render-instance@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - - '@vue/babel-sugar-functional-vue@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - - '@vue/babel-sugar-inject-h@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - - '@vue/babel-sugar-v-model@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - '@vue/babel-helper-vue-jsx-merge-props': 1.4.0 - '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.18.6) - camelcase: 5.3.1 - html-tags: 2.0.0 - svg-tags: 1.0.0 - transitivePeerDependencies: - - supports-color - - '@vue/babel-sugar-v-on@1.4.0(@babel/core@7.18.6)': - dependencies: - '@babel/core': 7.18.6 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.18.6) - '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.18.6) - camelcase: 5.3.1 - transitivePeerDependencies: - - supports-color - - '@vue/cli-overlay@5.0.8': {} - - '@vue/cli-plugin-babel@5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3))(core-js@3.42.0)(vue@3.5.14)': - dependencies: - '@babel/core': 7.18.6 - '@vue/babel-preset-app': 5.0.8(@babel/core@7.18.6)(core-js@3.42.0)(vue@3.5.14) - '@vue/cli-service': 5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3) - '@vue/cli-shared-utils': 5.0.8 - babel-loader: 8.4.1(@babel/core@7.18.6)(webpack@5.99.8) - thread-loader: 3.0.4(webpack@5.99.8) - webpack: 5.99.8 - transitivePeerDependencies: - - '@swc/core' - - core-js - - encoding - - esbuild - - supports-color - - uglify-js - - vue - - webpack-cli - - '@vue/cli-plugin-eslint@5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3))(eslint@7.32.0)': - dependencies: - '@vue/cli-service': 5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3) - '@vue/cli-shared-utils': 5.0.8 - eslint: 7.32.0 - eslint-webpack-plugin: 3.2.0(eslint@7.32.0)(webpack@5.99.8) - globby: 11.1.0 - webpack: 5.99.8 - yorkie: 2.0.0 - transitivePeerDependencies: - - '@swc/core' - - encoding - - esbuild - - uglify-js - - webpack-cli - - '@vue/cli-plugin-router@5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3))': - dependencies: - '@vue/cli-service': 5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3) - '@vue/cli-shared-utils': 5.0.8 - transitivePeerDependencies: - - encoding - - '@vue/cli-plugin-vuex@5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3))': - dependencies: - '@vue/cli-service': 5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3) - - '@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3)': - dependencies: - '@babel/helper-compilation-targets': 7.27.2 - '@soda/friendly-errors-webpack-plugin': 1.8.1(webpack@5.99.8) - '@soda/get-current-script': 1.0.2 - '@types/minimist': 1.2.5 - '@vue/cli-overlay': 5.0.8 - '@vue/cli-plugin-router': 5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3)) - '@vue/cli-plugin-vuex': 5.0.8(@vue/cli-service@5.0.8(@vue/compiler-sfc@3.5.14)(lodash@4.17.21)(stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8))(vue@3.5.14)(webpack-sources@3.2.3)) - '@vue/cli-shared-utils': 5.0.8 - '@vue/component-compiler-utils': 3.3.0(lodash@4.17.21) - '@vue/vue-loader-v15': vue-loader@15.11.1(@vue/compiler-sfc@3.5.14)(css-loader@6.11.0(webpack@5.99.8))(lodash@4.17.21)(webpack@5.99.8) - '@vue/web-component-wrapper': 1.3.0 - acorn: 8.14.1 - acorn-walk: 8.3.4 - address: 1.2.2 - autoprefixer: 10.4.21(postcss@8.5.3) - browserslist: 4.24.5 - case-sensitive-paths-webpack-plugin: 2.4.0 - cli-highlight: 2.1.11 - clipboardy: 2.3.0 - cliui: 7.0.4 - copy-webpack-plugin: 9.1.0(webpack@5.99.8) - css-loader: 6.11.0(webpack@5.99.8) - css-minimizer-webpack-plugin: 3.4.1(webpack@5.99.8) - cssnano: 5.1.15(postcss@8.5.3) - debug: 4.4.1 - default-gateway: 6.0.3 - dotenv: 10.0.0 - dotenv-expand: 5.1.0 - fs-extra: 9.1.0 - globby: 11.1.0 - hash-sum: 2.0.0 - html-webpack-plugin: 5.6.3(webpack@5.99.8) - is-file-esm: 1.0.0 - launch-editor-middleware: 2.10.0 - lodash.defaultsdeep: 4.6.1 - lodash.mapvalues: 4.6.0 - mini-css-extract-plugin: 2.9.2(webpack@5.99.8) - minimist: 1.2.8 - module-alias: 2.2.3 - portfinder: 1.0.37 - postcss: 8.5.3 - postcss-loader: 6.2.1(postcss@8.5.3)(webpack@5.99.8) - progress-webpack-plugin: 1.0.16(webpack@5.99.8) - ssri: 8.0.1 - terser-webpack-plugin: 5.3.14(webpack@5.99.8) - thread-loader: 3.0.4(webpack@5.99.8) - vue-loader: 17.4.2(@vue/compiler-sfc@3.5.14)(vue@3.5.14)(webpack@5.99.8) - vue-style-loader: 4.1.3 - webpack: 5.99.8 - webpack-bundle-analyzer: 4.10.2 - webpack-chain: 6.5.1 - webpack-dev-server: 4.15.2(debug@4.4.1)(webpack@5.99.8) - webpack-merge: 5.10.0 - webpack-virtual-modules: 0.4.6 - whatwg-fetch: 3.6.20 - optionalDependencies: - stylus-loader: 7.1.3(stylus@0.58.1)(webpack@5.99.8) - webpack-sources: 3.2.3 - transitivePeerDependencies: - - '@parcel/css' - - '@rspack/core' - - '@swc/core' - - '@vue/compiler-sfc' - - arc-templates - - atpl - - babel-core - - bracket-template - - bufferutil - - clean-css - - coffee-script - - csso - - dot - - dust - - dustjs-helpers - - dustjs-linkedin - - eco - - ect - - ejs - - encoding - - esbuild - - haml-coffee - - hamlet - - hamljs - - handlebars - - hogan.js - - htmling - - jade - - jazz - - jqtpl - - just - - liquid-node - - liquor - - lodash - - marko - - mote - - mustache - - nunjucks - - plates - - prettier - - pug - - qejs - - ractive - - razor-tmpl - - react - - react-dom - - slm - - squirrelly - - supports-color - - swig - - swig-templates - - teacup - - templayed - - then-jade - - then-pug - - tinyliquid - - toffee - - twig - - twing - - uglify-js - - underscore - - utf-8-validate - - vash - - velocityjs - - vue - - walrus - - webpack-cli - - whiskers - - '@vue/cli-shared-utils@5.0.8': - dependencies: - '@achrinza/node-ipc': 9.2.9 - chalk: 4.1.2 - execa: 1.0.0 - joi: 17.13.3 - launch-editor: 2.10.0 - lru-cache: 6.0.0 - node-fetch: 2.7.0 - open: 8.4.2 - ora: 5.4.1 - read-pkg: 5.2.0 - semver: 7.7.2 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - encoding - - '@vue/compiler-core@3.5.14': - dependencies: - '@babel/parser': 7.27.2 - '@vue/shared': 3.5.14 + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.18 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.14': + '@vue/compiler-dom@3.5.18': dependencies: - '@vue/compiler-core': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/compiler-core': 3.5.18 + '@vue/shared': 3.5.18 - '@vue/compiler-sfc@3.5.14': + '@vue/compiler-sfc@3.5.18': dependencies: - '@babel/parser': 7.27.2 - '@vue/compiler-core': 3.5.14 - '@vue/compiler-dom': 3.5.14 - '@vue/compiler-ssr': 3.5.14 - '@vue/shared': 3.5.14 + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.18 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 estree-walker: 2.0.2 magic-string: 0.30.17 - postcss: 8.5.3 + postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.14': + '@vue/compiler-ssr@3.5.18': dependencies: - '@vue/compiler-dom': 3.5.14 - '@vue/shared': 3.5.14 - - '@vue/component-compiler-utils@3.3.0(lodash@4.17.21)': - dependencies: - consolidate: 0.15.1(lodash@4.17.21) - hash-sum: 1.0.2 - lru-cache: 4.1.5 - merge-source-map: 1.1.0 - postcss: 7.0.39 - postcss-selector-parser: 6.1.2 - source-map: 0.6.1 - vue-template-es2015-compiler: 1.9.1 - optionalDependencies: - prettier: 2.8.8 - transitivePeerDependencies: - - arc-templates - - atpl - - babel-core - - bracket-template - - coffee-script - - dot - - dust - - dustjs-helpers - - dustjs-linkedin - - eco - - ect - - ejs - - haml-coffee - - hamlet - - hamljs - - handlebars - - hogan.js - - htmling - - jade - - jazz - - jqtpl - - just - - liquid-node - - liquor - - lodash - - marko - - mote - - mustache - - nunjucks - - plates - - pug - - qejs - - ractive - - razor-tmpl - - react - - react-dom - - slm - - squirrelly - - swig - - swig-templates - - teacup - - templayed - - then-jade - - then-pug - - tinyliquid - - toffee - - twig - - twing - - underscore - - vash - - velocityjs - - walrus - - whiskers + '@vue/compiler-dom': 3.5.18 + '@vue/shared': 3.5.18 '@vue/devtools-api@6.6.4': {} - '@vue/reactivity@3.5.14': + '@vue/reactivity@3.5.18': dependencies: - '@vue/shared': 3.5.14 + '@vue/shared': 3.5.18 - '@vue/runtime-core@3.5.14': + '@vue/runtime-core@3.5.18': dependencies: - '@vue/reactivity': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/reactivity': 3.5.18 + '@vue/shared': 3.5.18 - '@vue/runtime-dom@3.5.14': + '@vue/runtime-dom@3.5.18': dependencies: - '@vue/reactivity': 3.5.14 - '@vue/runtime-core': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/reactivity': 3.5.18 + '@vue/runtime-core': 3.5.18 + '@vue/shared': 3.5.18 csstype: 3.1.3 - '@vue/server-renderer@3.5.14(vue@3.5.14)': + '@vue/server-renderer@3.5.18(vue@3.5.18)': dependencies: - '@vue/compiler-ssr': 3.5.14 - '@vue/shared': 3.5.14 - vue: 3.5.14 + '@vue/compiler-ssr': 3.5.18 + '@vue/shared': 3.5.18 + vue: 3.5.18 - '@vue/shared@3.5.14': {} + '@vue/shared@3.5.18': {} - '@vue/web-component-wrapper@1.3.0': {} - - '@vueuse/core@9.13.0(vue@3.5.14)': + '@vueuse/core@9.13.0(vue@3.5.18)': dependencies: '@types/web-bluetooth': 0.0.16 '@vueuse/metadata': 9.13.0 - '@vueuse/shared': 9.13.0(vue@3.5.14) - vue-demi: 0.14.10(vue@3.5.14) + '@vueuse/shared': 9.13.0(vue@3.5.18) + vue-demi: 0.14.10(vue@3.5.18) transitivePeerDependencies: - '@vue/composition-api' - vue '@vueuse/metadata@9.13.0': {} - '@vueuse/shared@9.13.0(vue@3.5.14)': + '@vueuse/shared@9.13.0(vue@3.5.18)': dependencies: - vue-demi: 0.14.10(vue@3.5.14) + vue-demi: 0.14.10(vue@3.5.18) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -6422,49 +3030,21 @@ snapshots: '@xtuc/long@4.2.2': {} - accepts@1.3.8: + acorn-import-phases@1.0.4(acorn@8.15.0): dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 + acorn: 8.15.0 - acorn-jsx@5.3.2(acorn@7.4.1): - dependencies: - acorn: 7.4.1 - - acorn-jsx@5.3.2(acorn@8.14.1): - dependencies: - acorn: 8.14.1 - - acorn-walk@8.3.4: - dependencies: - acorn: 8.14.1 - - acorn@7.4.1: {} - - acorn@8.14.1: {} - - address@1.2.2: {} + acorn@8.15.0: {} ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 - ajv-keywords@3.5.2(ajv@6.12.6): - dependencies: - ajv: 6.12.6 - ajv-keywords@5.1.0(ajv@8.17.1): dependencies: ajv: 8.17.1 fast-deep-equal: 3.1.3 - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -6476,20 +3056,10 @@ snapshots: ansi-colors@4.1.3: {} - ansi-escapes@3.2.0: {} - - ansi-html-community@0.0.8: {} - - ansi-regex@3.0.1: {} - ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -6503,8 +3073,6 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - arch@2.2.0: {} - arg@5.0.2: {} argparse@1.0.10: @@ -6513,132 +3081,49 @@ snapshots: argparse@2.0.1: {} - array-flatten@1.1.1: {} - - array-union@2.1.0: {} - - astral-regex@2.0.0: {} - async-validator@4.2.5: {} - async@3.2.6: {} - asynckit@0.4.0: {} - at-least-node@1.0.0: {} - atob@2.1.2: {} autolinker@3.16.2: dependencies: tslib: 2.8.1 - autoprefixer@10.4.21(postcss@8.5.3): + autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.24.5 - caniuse-lite: 1.0.30001718 + browserslist: 4.25.1 + caniuse-lite: 1.0.30001731 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.5.3 + postcss: 8.5.6 postcss-value-parser: 4.2.0 axios@0.27.2: dependencies: - follow-redirects: 1.15.9(debug@4.4.1) - form-data: 4.0.2 + follow-redirects: 1.15.9 + form-data: 4.0.4 transitivePeerDependencies: - debug - babel-loader@8.4.1(@babel/core@7.18.6)(webpack@5.99.8): - dependencies: - '@babel/core': 7.18.6 - find-cache-dir: 3.3.2 - loader-utils: 2.0.4 - make-dir: 3.1.0 - schema-utils: 2.7.1 - webpack: 5.99.8 - - babel-plugin-dynamic-import-node@2.3.3: - dependencies: - object.assign: 4.1.7 - - babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.18.6): - dependencies: - '@babel/compat-data': 7.27.2 - '@babel/core': 7.18.6 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.18.6) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.18.6): - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.18.6) - core-js-compat: 3.42.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.18.6): - dependencies: - '@babel/core': 7.18.6 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.18.6) - transitivePeerDependencies: - - supports-color - balanced-match@1.0.2: {} - base64-js@1.5.1: {} - - batch@0.6.1: {} - - big.js@5.2.2: {} - - bignumber.js@9.3.0: {} + bignumber.js@9.3.1: {} binary-extensions@2.3.0: {} - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - bluebird@3.7.2: {} - blueimp-canvas-to-blob@3.29.0: {} - body-parser@1.20.3: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.13.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - bonjour-service@1.3.0: - dependencies: - fast-deep-equal: 3.1.3 - multicast-dns: 7.2.5 - boolbase@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -6646,81 +3131,37 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.5: + browserslist@4.25.1: dependencies: - caniuse-lite: 1.0.30001718 - electron-to-chromium: 1.5.155 + caniuse-lite: 1.0.30001731 + electron-to-chromium: 1.5.192 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.5) + update-browserslist-db: 1.1.3(browserslist@4.25.1) + + buffer-builder@0.2.0: {} buffer-from@1.1.2: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - bytes@3.1.2: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - callsites@3.1.0: {} - - camel-case@4.1.2: - dependencies: - pascal-case: 3.1.2 - tslib: 2.8.1 - camelcase-css@2.0.1: {} camelcase@5.3.1: {} - caniuse-api@3.0.0: - dependencies: - browserslist: 4.24.5 - caniuse-lite: 1.0.30001718 - lodash.memoize: 4.1.2 - lodash.uniq: 4.5.0 - - caniuse-lite@1.0.30001718: {} - - case-sensitive-paths-webpack-plugin@2.4.0: {} - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - chalk@3.0.0: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 + caniuse-lite@1.0.30001731: {} cheerio-select@1.6.0: dependencies: css-select: 4.3.0 - css-what: 6.1.0 + css-what: 6.2.2 domelementtype: 2.3.0 domhandler: 4.3.1 domutils: 2.8.0 @@ -6728,8 +3169,8 @@ snapshots: cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 - css-select: 5.1.0 - css-what: 6.1.0 + css-select: 5.2.2 + css-what: 6.2.2 domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 @@ -6768,78 +3209,25 @@ snapshots: chrome-trace-event@1.0.4: {} - ci-info@1.6.0: {} - - clean-css@5.3.3: - dependencies: - source-map: 0.6.1 - - cli-cursor@2.1.0: - dependencies: - restore-cursor: 2.0.0 - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - - cli-highlight@2.1.11: - dependencies: - chalk: 4.1.2 - highlight.js: 10.7.3 - mz: 2.7.0 - parse5: 5.1.1 - parse5-htmlparser2-tree-adapter: 6.0.1 - yargs: 16.2.0 - - cli-spinners@2.9.2: {} - clipboard@2.0.11: dependencies: good-listener: 1.2.2 select: 1.1.2 tiny-emitter: 2.1.0 - clipboardy@2.3.0: - dependencies: - arch: 2.2.0 - execa: 1.0.0 - is-wsl: 2.2.0 - cliui@6.0.0: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone-deep@4.0.1: - dependencies: - is-plain-object: 2.0.4 - kind-of: 6.0.3 - shallow-clone: 3.0.1 - - clone@1.0.4: {} - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} - colord@2.9.3: {} - - colorette@2.0.20: {} + colorjs.io@0.5.2: {} combined-stream@1.0.8: dependencies: @@ -6857,24 +3245,6 @@ snapshots: commander@8.3.0: {} - commondir@1.0.1: {} - - compressible@2.0.18: - dependencies: - mime-db: 1.54.0 - - compression@1.8.0: - dependencies: - bytes: 3.1.2 - compressible: 2.0.18 - debug: 2.6.9 - negotiator: 0.6.4 - on-headers: 1.0.2 - safe-buffer: 5.2.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - compressorjs@1.2.1: dependencies: blueimp-canvas-to-blob: 3.29.0 @@ -6882,65 +3252,11 @@ snapshots: concat-map@0.0.1: {} - connect-history-api-fallback@2.0.0: {} + confbox@0.1.8: {} - consolidate@0.15.1(lodash@4.17.21): - dependencies: - bluebird: 3.7.2 - optionalDependencies: - lodash: 4.17.21 + confbox@0.2.2: {} - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - - content-type@1.0.5: {} - - convert-source-map@1.9.0: {} - - cookie-signature@1.0.6: {} - - cookie@0.7.1: {} - - copy-webpack-plugin@9.1.0(webpack@5.99.8): - dependencies: - fast-glob: 3.3.3 - glob-parent: 6.0.2 - globby: 11.1.0 - normalize-path: 3.0.0 - schema-utils: 3.3.0 - serialize-javascript: 6.0.2 - webpack: 5.99.8 - - core-js-compat@3.42.0: - dependencies: - browserslist: 4.24.5 - - core-js@3.42.0: {} - - core-util-is@1.0.3: {} - - cosmiconfig@7.1.0: - dependencies: - '@types/parse-json': 4.0.2 - import-fresh: 3.3.1 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - - cross-spawn@5.1.0: - dependencies: - lru-cache: 4.1.5 - shebang-command: 1.2.0 - which: 1.3.1 - - cross-spawn@6.0.6: - dependencies: - nice-try: 1.0.5 - path-key: 2.0.1 - semver: 5.7.2 - shebang-command: 1.2.0 - which: 1.3.1 + core-js@3.44.0: {} cross-spawn@7.0.6: dependencies: @@ -6948,55 +3264,23 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-declaration-sorter@6.4.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - css-loader@6.11.0(webpack@5.99.8): - dependencies: - icss-utils: 5.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.3) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.3) - postcss-modules-scope: 3.2.1(postcss@8.5.3) - postcss-modules-values: 4.0.0(postcss@8.5.3) - postcss-value-parser: 4.2.0 - semver: 7.7.2 - optionalDependencies: - webpack: 5.99.8 - - css-minimizer-webpack-plugin@3.4.1(webpack@5.99.8): - dependencies: - cssnano: 5.1.15(postcss@8.5.3) - jest-worker: 27.5.1 - postcss: 8.5.3 - schema-utils: 4.3.2 - serialize-javascript: 6.0.2 - source-map: 0.6.1 - webpack: 5.99.8 - css-select@4.3.0: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 4.3.1 domutils: 2.8.0 nth-check: 2.1.1 - css-select@5.1.0: + css-select@5.2.2: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 5.0.3 domutils: 3.2.2 nth-check: 2.1.1 - css-tree@1.1.3: - dependencies: - mdn-data: 2.0.14 - source-map: 0.6.1 - - css-what@6.1.0: {} + css-what@6.2.2: {} css@3.0.0: dependencies: @@ -7006,54 +3290,6 @@ snapshots: cssesc@3.0.0: {} - cssnano-preset-default@5.2.14(postcss@8.5.3): - dependencies: - css-declaration-sorter: 6.4.1(postcss@8.5.3) - cssnano-utils: 3.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-calc: 8.2.4(postcss@8.5.3) - postcss-colormin: 5.3.1(postcss@8.5.3) - postcss-convert-values: 5.1.3(postcss@8.5.3) - postcss-discard-comments: 5.1.2(postcss@8.5.3) - postcss-discard-duplicates: 5.1.0(postcss@8.5.3) - postcss-discard-empty: 5.1.1(postcss@8.5.3) - postcss-discard-overridden: 5.1.0(postcss@8.5.3) - postcss-merge-longhand: 5.1.7(postcss@8.5.3) - postcss-merge-rules: 5.1.4(postcss@8.5.3) - postcss-minify-font-values: 5.1.0(postcss@8.5.3) - postcss-minify-gradients: 5.1.1(postcss@8.5.3) - postcss-minify-params: 5.1.4(postcss@8.5.3) - postcss-minify-selectors: 5.2.1(postcss@8.5.3) - postcss-normalize-charset: 5.1.0(postcss@8.5.3) - postcss-normalize-display-values: 5.1.0(postcss@8.5.3) - postcss-normalize-positions: 5.1.1(postcss@8.5.3) - postcss-normalize-repeat-style: 5.1.1(postcss@8.5.3) - postcss-normalize-string: 5.1.0(postcss@8.5.3) - postcss-normalize-timing-functions: 5.1.0(postcss@8.5.3) - postcss-normalize-unicode: 5.1.1(postcss@8.5.3) - postcss-normalize-url: 5.1.0(postcss@8.5.3) - postcss-normalize-whitespace: 5.1.1(postcss@8.5.3) - postcss-ordered-values: 5.1.3(postcss@8.5.3) - postcss-reduce-initial: 5.1.2(postcss@8.5.3) - postcss-reduce-transforms: 5.1.0(postcss@8.5.3) - postcss-svgo: 5.1.0(postcss@8.5.3) - postcss-unique-selectors: 5.1.1(postcss@8.5.3) - - cssnano-utils@3.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - cssnano@5.1.15(postcss@8.5.3): - dependencies: - cssnano-preset-default: 5.2.14(postcss@8.5.3) - lilconfig: 2.1.0 - postcss: 8.5.3 - yaml: 1.10.2 - - csso@4.2.0: - dependencies: - css-tree: 1.1.3 - csstype@3.1.3: {} d3-array@3.2.4: @@ -7216,12 +3452,6 @@ snapshots: dayjs@1.11.13: {} - debounce@1.2.1: {} - - debug@2.6.9: - dependencies: - ms: 2.0.0 - debug@4.4.1: dependencies: ms: 2.1.3 @@ -7230,32 +3460,6 @@ snapshots: decode-uri-component@0.2.2: {} - deep-is@0.1.4: {} - - deepmerge@1.5.2: {} - - default-gateway@6.0.3: - dependencies: - execa: 5.1.1 - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-lazy-prop@2.0.0: {} - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - delaunator@5.0.1: dependencies: robust-predicates: 3.0.2 @@ -7264,36 +3468,12 @@ snapshots: delegate@3.2.0: {} - depd@1.1.2: {} - - depd@2.0.0: {} - - destroy@1.2.0: {} - - detect-node@2.1.0: {} - didyoumean@1.2.2: {} dijkstrajs@1.0.3: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dlv@1.1.3: {} - dns-packet@5.6.1: - dependencies: - '@leichtgewicht/ip-codec': 2.0.5 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - dom-converter@0.2.0: - dependencies: - utila: 0.4.0 - dom-serializer@1.4.1: dependencies: domelementtype: 2.3.0 @@ -7332,45 +3512,30 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 - dot-case@3.0.4: - dependencies: - no-case: 3.0.4 - tslib: 2.8.1 - - dotenv-expand@5.1.0: {} - - dotenv@10.0.0: {} - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - duplexer@0.1.2: {} - eastasianwidth@0.2.0: {} - easy-stack@1.0.1: {} - echarts@5.6.0: dependencies: tslib: 2.3.0 zrender: 5.6.1 - ee-first@1.1.1: {} + electron-to-chromium@1.5.192: {} - electron-to-chromium@1.5.155: {} - - element-plus@2.9.10(vue@3.5.14): + element-plus@2.10.4(vue@3.5.18): dependencies: '@ctrl/tinycolor': 3.6.1 - '@element-plus/icons-vue': 2.3.1(vue@3.5.14) - '@floating-ui/dom': 1.7.0 + '@element-plus/icons-vue': 2.3.1(vue@3.5.18) + '@floating-ui/dom': 1.7.2 '@popperjs/core': '@sxzz/popperjs-es@2.11.7' - '@types/lodash': 4.17.16 + '@types/lodash': 4.17.20 '@types/lodash-es': 4.17.12 - '@vueuse/core': 9.13.0(vue@3.5.14) + '@vueuse/core': 9.13.0(vue@3.5.18) async-validator: 4.2.5 dayjs: 1.11.13 escape-html: 1.0.3 @@ -7379,7 +3544,7 @@ snapshots: lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) memoize-one: 6.0.0 normalize-wheel-es: 1.2.0 - vue: 3.5.14 + vue: 3.5.18 transitivePeerDependencies: - '@vue/composition-api' @@ -7387,25 +3552,10 @@ snapshots: emoji-regex@9.2.2: {} - emojis-list@3.0.0: {} - - encodeurl@1.0.2: {} - - encodeurl@2.0.0: {} - - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - - enhanced-resolve@5.18.1: + enhanced-resolve@5.18.2: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.1 - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 + tapable: 2.2.2 entities@2.2.0: {} @@ -7413,15 +3563,7 @@ snapshots: entities@4.5.0: {} - entities@6.0.0: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - error-stack-parser@2.1.4: - dependencies: - stackframe: 1.3.4 + entities@6.0.1: {} es-define-property@1.0.1: {} @@ -7440,128 +3582,47 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + escalade@3.2.0: {} escape-goat@3.0.0: {} escape-html@1.0.3: {} - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - eslint-plugin-vue@8.7.1(eslint@7.32.0): - dependencies: - eslint: 7.32.0 - eslint-utils: 3.0.0(eslint@7.32.0) - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 6.1.2 - semver: 7.7.2 - vue-eslint-parser: 8.3.0(eslint@7.32.0) - transitivePeerDependencies: - - supports-color + escape-string-regexp@5.0.0: {} eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-utils@2.1.0: - dependencies: - eslint-visitor-keys: 1.3.0 - - eslint-utils@3.0.0(eslint@7.32.0): - dependencies: - eslint: 7.32.0 - eslint-visitor-keys: 2.1.0 - - eslint-visitor-keys@1.3.0: {} - - eslint-visitor-keys@2.1.0: {} - - eslint-visitor-keys@3.4.3: {} - - eslint-webpack-plugin@3.2.0(eslint@7.32.0)(webpack@5.99.8): - dependencies: - '@types/eslint': 8.56.12 - eslint: 7.32.0 - jest-worker: 28.1.3 - micromatch: 4.0.8 - normalize-path: 3.0.0 - schema-utils: 4.3.2 - webpack: 5.99.8 - - eslint@7.32.0: - dependencies: - '@babel/code-frame': 7.12.11 - '@eslint/eslintrc': 0.4.3 - '@humanwhocodes/config-array': 0.5.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.1 - doctrine: 3.0.0 - enquirer: 2.4.1 - escape-string-regexp: 4.0.0 - eslint-scope: 5.1.1 - eslint-utils: 2.1.0 - eslint-visitor-keys: 2.1.0 - espree: 7.3.1 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - functional-red-black-tree: 1.0.1 - glob-parent: 5.1.2 - globals: 13.24.0 - ignore: 4.0.6 - import-fresh: 3.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-yaml: 3.14.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - progress: 2.0.3 - regexpp: 3.2.0 - semver: 7.7.2 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - table: 6.9.0 - text-table: 0.2.0 - v8-compile-cache: 2.4.0 - transitivePeerDependencies: - - supports-color - esm@3.2.25: {} - espree@7.3.1: - dependencies: - acorn: 7.4.1 - acorn-jsx: 5.3.2(acorn@7.4.1) - eslint-visitor-keys: 1.3.0 - - espree@9.6.1: - dependencies: - acorn: 8.14.1 - acorn-jsx: 5.3.2(acorn@8.14.1) - eslint-visitor-keys: 3.4.3 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -7572,83 +3633,13 @@ snapshots: estree-walker@2.0.2: {} - esutils@2.0.3: {} - - etag@1.8.1: {} - - event-pubsub@4.3.0: {} - - eventemitter3@4.0.7: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 events@3.3.0: {} - execa@0.8.0: - dependencies: - cross-spawn: 5.1.0 - get-stream: 3.0.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.7 - strip-eof: 1.0.0 - - execa@1.0.0: - dependencies: - cross-spawn: 6.0.6 - get-stream: 4.1.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.7 - strip-eof: 1.0.0 - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - express@4.21.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.3 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.1 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.1 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.12 - proxy-addr: 2.0.7 - qs: 6.13.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.0 - serve-static: 1.16.2 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color + exsolve@1.0.7: {} fast-deep-equal@3.1.3: {} @@ -7660,96 +3651,38 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - fast-uri@3.0.6: {} fastq@1.19.1: dependencies: reusify: 1.1.0 - faye-websocket@0.11.4: - dependencies: - websocket-driver: 0.7.4 - - figures@2.0.0: - dependencies: - escape-string-regexp: 1.0.5 - - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - finalhandler@1.3.1: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - find-cache-dir@3.3.2: - dependencies: - commondir: 1.0.1 - make-dir: 3.1.0 - pkg-dir: 4.2.0 - find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - - flat@5.0.2: {} - - flatted@3.3.3: {} - - follow-redirects@1.15.9(debug@4.4.1): - optionalDependencies: - debug: 4.4.1 + follow-redirects@1.15.9: {} foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.2: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 - forwarded@0.2.0: {} - fraction.js@4.3.7: {} - fresh@0.5.2: {} - - fs-extra@9.1.0: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fs-monkey@1.0.6: {} - fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -7757,10 +3690,6 @@ snapshots: function-bind@1.1.2: {} - functional-red-black-tree@1.0.1: {} - - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} get-intrinsic@1.3.0: @@ -7781,14 +3710,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@3.0.0: {} - - get-stream@4.1.0: - dependencies: - pump: 3.0.2 - - get-stream@6.0.1: {} - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -7817,21 +3738,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - globals@11.12.0: {} - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - good-listener@1.2.2: dependencies: delegate: 3.2.0 @@ -7842,75 +3748,20 @@ snapshots: graceful-fs@4.2.11: {} - gzip-size@6.0.0: - dependencies: - duplexer: 0.1.2 - - handle-thing@2.0.1: {} - - has-flag@3.0.0: {} - has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: has-symbols: 1.1.0 - hash-sum@1.0.2: {} - - hash-sum@2.0.0: {} - hasown@2.0.2: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - - highlight.js@10.7.3: {} - highlight.js@11.11.1: {} - hosted-git-info@2.8.9: {} - - hpack.js@2.1.6: - dependencies: - inherits: 2.0.4 - obuf: 1.1.2 - readable-stream: 2.3.8 - wbuf: 1.7.3 - - html-entities@2.6.0: {} - - html-escaper@2.0.2: {} - - html-minifier-terser@6.1.0: - dependencies: - camel-case: 4.1.2 - clean-css: 5.3.3 - commander: 8.3.0 - he: 1.2.0 - param-case: 3.0.4 - relateurl: 0.2.7 - terser: 5.39.2 - - html-tags@2.0.0: {} - - html-webpack-plugin@5.6.3(webpack@5.99.8): - dependencies: - '@types/html-minifier-terser': 6.1.0 - html-minifier-terser: 6.1.0 - lodash: 4.17.21 - pretty-error: 4.0.0 - tapable: 2.2.1 - optionalDependencies: - webpack: 5.99.8 - htmlparser2@5.0.1: dependencies: domelementtype: 2.3.0 @@ -7932,222 +3783,73 @@ snapshots: domutils: 3.2.2 entities: 4.5.0 - http-deceiver@1.2.7: {} - - http-errors@1.6.3: - dependencies: - depd: 1.1.2 - inherits: 2.0.3 - setprototypeof: 1.1.0 - statuses: 1.5.0 - - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - http-parser-js@0.5.10: {} - - http-proxy-middleware@2.0.9(@types/express@4.17.21)(debug@4.4.1): - dependencies: - '@types/http-proxy': 1.17.16 - http-proxy: 1.18.1(debug@4.4.1) - is-glob: 4.0.3 - is-plain-obj: 3.0.0 - micromatch: 4.0.8 - optionalDependencies: - '@types/express': 4.17.21 - transitivePeerDependencies: - - debug - - http-proxy@1.18.1(debug@4.4.1): - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.9(debug@4.4.1) - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - - human-signals@2.1.0: {} - hyperdyperid@1.2.0: {} - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - ieee754@1.2.1: {} - - ignore@4.0.6: {} - - ignore@5.3.2: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} + immutable@5.1.3: {} inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - inherits@2.0.3: {} - inherits@2.0.4: {} internmap@2.0.3: {} - ipaddr.js@1.9.1: {} - - ipaddr.js@2.2.0: {} - - is-arrayish@0.2.1: {} - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 is-blob@2.1.0: {} - is-ci@1.2.1: - dependencies: - ci-info: 1.6.0 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-docker@2.2.1: {} - is-extglob@2.1.1: {} - is-file-esm@1.0.0: - dependencies: - read-pkg-up: 7.0.1 - - is-fullwidth-code-point@2.0.0: {} - is-fullwidth-code-point@3.0.0: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-interactive@1.0.0: {} - is-number@7.0.0: {} - is-plain-obj@3.0.0: {} - - is-plain-object@2.0.4: - dependencies: - isobject: 3.0.1 - - is-stream@1.1.0: {} - - is-stream@2.0.1: {} - - is-unicode-supported@0.1.0: {} - - is-wsl@2.2.0: - dependencies: - is-docker: 2.2.1 - - isarray@1.0.0: {} - isexe@2.0.0: {} - isobject@3.0.1: {} - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - javascript-stringify@2.1.0: {} - jest-worker@27.5.1: dependencies: - '@types/node': 22.15.18 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jest-worker@28.1.3: - dependencies: - '@types/node': 22.15.18 + '@types/node': 24.1.0 merge-stream: 2.0.0 supports-color: 8.1.1 jiti@1.21.7: {} - joi@17.13.3: - dependencies: - '@hapi/hoek': 9.3.0 - '@hapi/topo': 5.1.0 - '@sideway/address': 4.1.5 - '@sideway/formula': 3.0.1 - '@sideway/pinpoint': 2.0.0 - - js-message@1.0.7: {} - - js-tokens@4.0.0: {} - - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + js-tokens@9.0.1: {} js-yaml@4.1.0: dependencies: argparse: 2.0.1 - jsesc@3.0.2: {} - - jsesc@3.1.0: {} - json-bigint@1.0.0: dependencies: - bignumber.js: 9.3.0 - - json-buffer@3.0.1: {} - - json-parse-better-errors@1.0.2: {} + bignumber.js: 9.3.1 json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@1.0.2: - dependencies: - minimist: 1.2.8 - - json5@2.2.3: {} - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - juice@8.1.0: dependencies: cheerio: 1.0.0-rc.10 @@ -8162,30 +3864,6 @@ snapshots: dependencies: commander: 8.3.0 - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kind-of@6.0.3: {} - - klona@2.0.6: {} - - launch-editor-middleware@2.10.0: - dependencies: - launch-editor: 2.10.0 - - launch-editor@2.10.0: - dependencies: - picocolors: 1.1.1 - shell-quote: 1.8.2 - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lilconfig@2.1.0: {} - lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -8196,17 +3874,16 @@ snapshots: loader-runner@4.3.0: {} - loader-utils@1.4.2: + local-pkg@0.5.1: dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 1.0.2 + mlly: 1.7.4 + pkg-types: 1.3.1 - loader-utils@2.0.4: + local-pkg@1.1.1: dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 + mlly: 1.7.4 + pkg-types: 2.2.0 + quansync: 0.2.10 locate-path@5.0.0: dependencies: @@ -8220,61 +3897,13 @@ snapshots: lodash: 4.17.21 lodash-es: 4.17.21 - lodash.debounce@4.0.8: {} - - lodash.defaultsdeep@4.6.1: {} - - lodash.kebabcase@4.1.1: {} - - lodash.mapvalues@4.6.0: {} - - lodash.memoize@4.1.2: {} - - lodash.merge@4.6.2: {} - - lodash.truncate@4.4.2: {} - - lodash.uniq@4.5.0: {} - lodash@4.17.21: {} - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - log-update@2.3.0: - dependencies: - ansi-escapes: 3.2.0 - cli-cursor: 2.1.0 - wrap-ansi: 3.0.1 - - lower-case@2.0.2: - dependencies: - tslib: 2.8.1 - lru-cache@10.4.3: {} - lru-cache@4.1.5: - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - - make-dir@3.1.0: - dependencies: - semver: 6.3.1 + '@jridgewell/sourcemap-codec': 1.5.4 markdown-it-emoji@2.0.2: {} @@ -8293,21 +3922,23 @@ snapshots: mdurl: 1.0.1 uc.micro: 1.0.6 + marked@15.0.12: {} + markmap-common@0.16.0: dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.2 '@gera2ld/jsx-dom': 2.2.2 npm2url: 0.2.4 markmap-html-parser@0.16.1(markmap-common@0.16.0): dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.2 cheerio: 1.0.0-rc.12 markmap-common: 0.16.0 markmap-lib@0.16.1(markmap-common@0.16.0): dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.2 highlight.js: 11.11.1 js-yaml: 4.1.0 katex: 0.16.22 @@ -8320,13 +3951,13 @@ snapshots: markmap-toolbar@0.17.2(markmap-common@0.16.0): dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.2 '@gera2ld/jsx-dom': 2.2.2 markmap-common: 0.16.0 markmap-view@0.16.0(markmap-common@0.16.0): dependencies: - '@babel/runtime': 7.27.1 + '@babel/runtime': 7.28.2 '@gera2ld/jsx-dom': 2.2.2 '@types/d3': 7.4.3 d3: 7.9.0 @@ -8342,43 +3973,27 @@ snapshots: mj-context-menu: 0.6.1 speech-rule-engine: 4.1.2 - md-editor-v3@2.11.3(vue@3.5.14): + md-editor-v3@2.11.3(vue@3.5.18): dependencies: - vue: 3.5.14 - - mdn-data@2.0.14: {} + vue: 3.5.18 mdurl@1.0.1: {} - media-typer@0.3.0: {} - - memfs@3.5.3: + memfs@4.23.0: dependencies: - fs-monkey: 1.0.6 - - memfs@4.17.2: - dependencies: - '@jsonjoy.com/json-pack': 1.2.0(tslib@2.8.1) - '@jsonjoy.com/util': 1.6.0(tslib@2.8.1) - tree-dump: 1.0.2(tslib@2.8.1) + '@jsonjoy.com/json-pack': 1.4.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.8.0(tslib@2.8.1) + tree-dump: 1.0.3(tslib@2.8.1) tslib: 2.8.1 memoize-one@6.0.0: {} mensch@0.3.4: {} - merge-descriptors@1.0.3: {} - - merge-source-map@1.1.0: - dependencies: - source-map: 0.6.1 - merge-stream@2.0.0: {} merge2@1.4.1: {} - methods@1.1.2: {} - mhchemparser@4.2.1: {} micromatch@4.0.8: @@ -8388,59 +4003,33 @@ snapshots: mime-db@1.52.0: {} - mime-db@1.54.0: {} - mime-types@2.1.35: dependencies: mime-db: 1.52.0 - mime@1.6.0: {} - mime@2.6.0: {} - mimic-fn@1.2.0: {} - - mimic-fn@2.1.0: {} - - mini-css-extract-plugin@2.9.2(webpack@5.99.8): - dependencies: - schema-utils: 4.3.2 - tapable: 2.2.1 - webpack: 5.99.8 - - minimalistic-assert@1.0.1: {} - minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 - - minimist@1.2.8: {} - - minipass@3.3.6: - dependencies: - yallist: 4.0.0 + brace-expansion: 2.0.2 minipass@7.1.2: {} mj-context-menu@0.6.1: {} - module-alias@2.2.3: {} - - mrmime@2.0.1: {} - - ms@2.0.0: {} + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 ms@2.1.3: {} - multicast-dns@7.2.5: - dependencies: - dns-packet: 5.6.1 - thunky: 1.1.0 - mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -8449,54 +4038,20 @@ snapshots: nanoid@3.3.11: {} - natural-compare@1.4.0: {} - - negotiator@0.6.3: {} - - negotiator@0.6.4: {} - neo-async@2.6.2: {} - nice-try@1.0.5: {} - - no-case@3.0.4: - dependencies: - lower-case: 2.0.2 - tslib: 2.8.1 - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - node-forge@1.3.1: {} - node-releases@2.0.19: {} - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.10 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - - normalize-path@1.0.0: {} - normalize-path@3.0.0: {} normalize-range@0.1.2: {} - normalize-url@6.1.0: {} - normalize-wheel-es@1.2.0: {} - npm-run-path@2.0.2: - dependencies: - path-key: 2.0.1 - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - npm2url@0.2.4: {} nth-check@2.1.1: @@ -8509,68 +4064,10 @@ snapshots: object-inspect@1.13.4: {} - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - obuf@1.1.2: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - on-headers@1.0.2: {} - once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@2.0.1: - dependencies: - mimic-fn: 1.2.0 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - - opener@1.5.2: {} - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - - p-finally@1.0.0: {} - p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -8579,31 +4076,10 @@ snapshots: dependencies: p-limit: 2.3.0 - p-retry@4.6.2: - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - p-try@2.2.0: {} package-json-from-dist@1.0.1: {} - param-case@3.0.4: - dependencies: - dot-case: 3.0.4 - tslib: 2.8.1 - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse5-htmlparser2-tree-adapter@6.0.1: dependencies: parse5: 6.0.1 @@ -8613,27 +4089,16 @@ snapshots: domhandler: 5.0.3 parse5: 7.3.0 - parse5@5.1.1: {} - parse5@6.0.1: {} parse5@7.3.0: dependencies: - entities: 6.0.0 - - parseurl@1.3.3: {} - - pascal-case@3.1.2: - dependencies: - no-case: 3.0.4 - tslib: 2.8.1 + entities: 6.0.1 path-exists@4.0.0: {} path-is-absolute@1.0.1: {} - path-key@2.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} @@ -8643,393 +4108,105 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@0.1.12: {} - - path-type@4.0.0: {} - - picocolors@0.2.1: {} + pathe@2.0.3: {} picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.3: {} + pify@2.3.0: {} - pinia@2.3.1(vue@3.5.14): + pinia@2.3.1(vue@3.5.18): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.14 - vue-demi: 0.14.10(vue@3.5.14) + vue: 3.5.18 + vue-demi: 0.14.10(vue@3.5.18) transitivePeerDependencies: - '@vue/composition-api' pirates@4.0.7: {} - pkg-dir@4.2.0: + pkg-types@1.3.1: dependencies: - find-up: 4.1.0 + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + pkg-types@2.2.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 pngjs@5.0.0: {} - portfinder@1.0.37: + postcss-import@15.1.0(postcss@8.5.6): dependencies: - async: 3.2.6 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - - postcss-calc@8.2.4(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 - postcss-value-parser: 4.2.0 - - postcss-colormin@5.3.1(postcss@8.5.3): - dependencies: - browserslist: 4.24.5 - caniuse-api: 3.0.0 - colord: 2.9.3 - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-convert-values@5.1.3(postcss@8.5.3): - dependencies: - browserslist: 4.24.5 - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-discard-comments@5.1.2(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - postcss-discard-duplicates@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - postcss-discard-empty@5.1.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - postcss-discard-overridden@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - postcss-import@15.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.10 - postcss-js@4.0.1(postcss@8.5.3): + postcss-js@4.0.1(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.3 + postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.3): + postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 yaml: 2.8.0 optionalDependencies: - postcss: 8.5.3 + postcss: 8.5.6 - postcss-loader@6.2.1(postcss@8.5.3)(webpack@5.99.8): + postcss-nested@6.2.0(postcss@8.5.6): dependencies: - cosmiconfig: 7.1.0 - klona: 2.0.6 - postcss: 8.5.3 - semver: 7.7.2 - webpack: 5.99.8 - - postcss-merge-longhand@5.1.7(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.5.3) - - postcss-merge-rules@5.1.4(postcss@8.5.3): - dependencies: - browserslist: 4.24.5 - caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.5.3) - postcss: 8.5.3 + postcss: 8.5.6 postcss-selector-parser: 6.1.2 - postcss-minify-font-values@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-minify-gradients@5.1.1(postcss@8.5.3): - dependencies: - colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-minify-params@5.1.4(postcss@8.5.3): - dependencies: - browserslist: 4.24.5 - cssnano-utils: 3.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-minify-selectors@5.2.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 - - postcss-modules-extract-imports@3.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - postcss-modules-local-by-default@4.2.0(postcss@8.5.3): - dependencies: - icss-utils: 5.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-selector-parser: 7.1.0 - postcss-value-parser: 4.2.0 - - postcss-modules-scope@3.2.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-selector-parser: 7.1.0 - - postcss-modules-values@4.0.0(postcss@8.5.3): - dependencies: - icss-utils: 5.1.0(postcss@8.5.3) - postcss: 8.5.3 - - postcss-nested@6.2.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 - - postcss-normalize-charset@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - - postcss-normalize-display-values@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-positions@5.1.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-repeat-style@5.1.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-string@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-timing-functions@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-unicode@5.1.1(postcss@8.5.3): - dependencies: - browserslist: 4.24.5 - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-url@5.1.0(postcss@8.5.3): - dependencies: - normalize-url: 6.1.0 - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-normalize-whitespace@5.1.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-ordered-values@5.1.3(postcss@8.5.3): - dependencies: - cssnano-utils: 3.1.0(postcss@8.5.3) - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - - postcss-reduce-initial@5.1.2(postcss@8.5.3): - dependencies: - browserslist: 4.24.5 - caniuse-api: 3.0.0 - postcss: 8.5.3 - - postcss-reduce-transforms@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.1.0: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-svgo@5.1.0(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-value-parser: 4.2.0 - svgo: 2.8.0 - - postcss-unique-selectors@5.1.1(postcss@8.5.3): - dependencies: - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 - postcss-value-parser@4.2.0: {} - postcss@7.0.39: - dependencies: - picocolors: 0.2.1 - source-map: 0.6.1 - - postcss@8.5.3: + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - prelude-ls@1.2.1: {} - - prettier@2.8.8: - optional: true - - pretty-error@4.0.0: - dependencies: - lodash: 4.17.21 - renderkid: 3.0.0 - prismjs@1.30.0: {} - process-nextick-args@2.0.1: {} - - progress-webpack-plugin@1.0.16(webpack@5.99.8): - dependencies: - chalk: 2.4.2 - figures: 2.0.0 - log-update: 2.3.0 - webpack: 5.99.8 - - progress@2.0.3: {} - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - pseudomap@1.0.2: {} - - pump@3.0.2: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - - punycode@2.3.1: {} - qrcode@1.5.4: dependencies: dijkstrajs: 1.0.3 pngjs: 5.0.0 yargs: 15.4.1 - qs@6.13.0: - dependencies: - side-channel: 1.1.0 - qs@6.14.0: dependencies: side-channel: 1.1.0 + quansync@0.2.10: {} + queue-microtask@1.2.3: {} randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - range-parser@1.2.1: {} - - raw-body@2.5.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - read-cache@1.0.0: dependencies: pify: 2.3.0 - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 - regenerate-unicode-properties@10.2.0: - dependencies: - regenerate: 1.4.2 - - regenerate@1.4.2: {} - - regexpp@3.2.0: {} - - regexpu-core@6.2.0: - dependencies: - regenerate: 1.4.2 - regenerate-unicode-properties: 10.2.0 - regjsgen: 0.8.0 - regjsparser: 0.12.0 - unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.2.0 - - regjsgen@0.8.0: {} - - regjsparser@0.12.0: - dependencies: - jsesc: 3.0.2 - - relateurl@0.2.7: {} - remarkable-katex@1.2.1: {} remarkable@2.0.1: @@ -9037,76 +4214,140 @@ snapshots: argparse: 1.0.10 autolinker: 3.16.2 - renderkid@3.0.0: - dependencies: - css-select: 4.3.0 - dom-converter: 0.2.0 - htmlparser2: 6.1.0 - lodash: 4.17.21 - strip-ansi: 6.0.1 - require-directory@2.1.1: {} require-from-string@2.0.2: {} require-main-filename@2.0.0: {} - requires-port@1.0.0: {} - - resolve-from@4.0.0: {} - resolve@1.22.10: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@2.0.0: - dependencies: - onetime: 2.0.1 - signal-exit: 3.0.7 - - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - - retry@0.13.1: {} - reusify@1.1.0: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - robust-predicates@3.0.2: {} + rollup@4.46.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.46.1 + '@rollup/rollup-android-arm64': 4.46.1 + '@rollup/rollup-darwin-arm64': 4.46.1 + '@rollup/rollup-darwin-x64': 4.46.1 + '@rollup/rollup-freebsd-arm64': 4.46.1 + '@rollup/rollup-freebsd-x64': 4.46.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.1 + '@rollup/rollup-linux-arm-musleabihf': 4.46.1 + '@rollup/rollup-linux-arm64-gnu': 4.46.1 + '@rollup/rollup-linux-arm64-musl': 4.46.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.1 + '@rollup/rollup-linux-ppc64-gnu': 4.46.1 + '@rollup/rollup-linux-riscv64-gnu': 4.46.1 + '@rollup/rollup-linux-riscv64-musl': 4.46.1 + '@rollup/rollup-linux-s390x-gnu': 4.46.1 + '@rollup/rollup-linux-x64-gnu': 4.46.1 + '@rollup/rollup-linux-x64-musl': 4.46.1 + '@rollup/rollup-win32-arm64-msvc': 4.46.1 + '@rollup/rollup-win32-ia32-msvc': 4.46.1 + '@rollup/rollup-win32-x64-msvc': 4.46.1 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 rw@1.3.3: {} - safe-buffer@5.1.2: {} + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} + sass-embedded-android-arm64@1.89.2: + optional: true + + sass-embedded-android-arm@1.89.2: + optional: true + + sass-embedded-android-riscv64@1.89.2: + optional: true + + sass-embedded-android-x64@1.89.2: + optional: true + + sass-embedded-darwin-arm64@1.89.2: + optional: true + + sass-embedded-darwin-x64@1.89.2: + optional: true + + sass-embedded-linux-arm64@1.89.2: + optional: true + + sass-embedded-linux-arm@1.89.2: + optional: true + + sass-embedded-linux-musl-arm64@1.89.2: + optional: true + + sass-embedded-linux-musl-arm@1.89.2: + optional: true + + sass-embedded-linux-musl-riscv64@1.89.2: + optional: true + + sass-embedded-linux-musl-x64@1.89.2: + optional: true + + sass-embedded-linux-riscv64@1.89.2: + optional: true + + sass-embedded-linux-x64@1.89.2: + optional: true + + sass-embedded-win32-arm64@1.89.2: + optional: true + + sass-embedded-win32-x64@1.89.2: + optional: true + + sass-embedded@1.89.2: + dependencies: + '@bufbuild/protobuf': 2.6.2 + buffer-builder: 0.2.0 + colorjs.io: 0.5.2 + immutable: 5.1.3 + rxjs: 7.8.2 + supports-color: 8.1.1 + sync-child-process: 1.0.2 + varint: 6.0.0 + optionalDependencies: + sass-embedded-android-arm: 1.89.2 + sass-embedded-android-arm64: 1.89.2 + sass-embedded-android-riscv64: 1.89.2 + sass-embedded-android-x64: 1.89.2 + sass-embedded-darwin-arm64: 1.89.2 + sass-embedded-darwin-x64: 1.89.2 + sass-embedded-linux-arm: 1.89.2 + sass-embedded-linux-arm64: 1.89.2 + sass-embedded-linux-musl-arm: 1.89.2 + sass-embedded-linux-musl-arm64: 1.89.2 + sass-embedded-linux-musl-riscv64: 1.89.2 + sass-embedded-linux-musl-x64: 1.89.2 + sass-embedded-linux-riscv64: 1.89.2 + sass-embedded-linux-x64: 1.89.2 + sass-embedded-win32-arm64: 1.89.2 + sass-embedded-win32-x64: 1.89.2 + sax@1.2.4: {} - schema-utils@2.7.1: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.3.2: dependencies: '@types/json-schema': 7.0.15 @@ -9114,97 +4355,22 @@ snapshots: ajv-formats: 2.1.1(ajv@8.17.1) ajv-keywords: 5.1.0(ajv@8.17.1) - select-hose@2.0.0: {} + scule@1.3.0: {} select@1.1.2: {} - selfsigned@2.4.1: - dependencies: - '@types/node-forge': 1.3.11 - node-forge: 1.3.1 - - semver@5.7.2: {} - - semver@6.3.1: {} - - semver@7.7.2: {} - - send@0.19.0: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 - serve-index@1.9.1: - dependencies: - accepts: 1.3.8 - batch: 0.6.1 - debug: 2.6.9 - escape-html: 1.0.3 - http-errors: 1.6.3 - mime-types: 2.1.35 - parseurl: 1.3.3 - transitivePeerDependencies: - - supports-color - - serve-static@1.16.2: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.0 - transitivePeerDependencies: - - supports-color - set-blocking@2.0.0: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - setprototypeof@1.1.0: {} - - setprototypeof@1.2.0: {} - - shallow-clone@3.0.1: - dependencies: - kind-of: 6.0.3 - - shebang-command@1.2.0: - dependencies: - shebang-regex: 1.0.0 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@1.0.0: {} - shebang-regex@3.0.0: {} - shell-quote@1.8.2: {} - side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -9233,32 +4399,10 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} - sirv@2.0.4: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - - slash@3.0.0: {} - - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - slick@1.12.2: {} - sockjs@0.3.24: - dependencies: - faye-websocket: 0.11.4 - uuid: 8.3.2 - websocket-driver: 0.7.4 - sortablejs@1.15.6: {} source-map-js@1.2.1: {} @@ -9275,42 +4419,7 @@ snapshots: source-map@0.6.1: {} - source-map@0.7.4: {} - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.21 - - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.21 - - spdx-license-ids@3.0.21: {} - - spdy-transport@3.0.0: - dependencies: - debug: 4.4.1 - detect-node: 2.1.0 - hpack.js: 2.1.6 - obuf: 1.1.2 - readable-stream: 3.6.2 - wbuf: 1.7.3 - transitivePeerDependencies: - - supports-color - - spdy@4.0.2: - dependencies: - debug: 4.4.1 - handle-thing: 2.0.1 - http-deceiver: 1.2.7 - select-hose: 2.0.0 - spdy-transport: 3.0.0 - transitivePeerDependencies: - - supports-color + source-map@0.7.6: {} speech-rule-engine@4.1.2: dependencies: @@ -9320,23 +4429,6 @@ snapshots: sprintf-js@1.0.3: {} - ssri@8.0.1: - dependencies: - minipass: 3.3.6 - - stable@0.1.8: {} - - stackframe@1.3.4: {} - - statuses@1.5.0: {} - - statuses@2.0.1: {} - - string-width@2.1.1: - dependencies: - is-fullwidth-code-point: 2.0.0 - strip-ansi: 4.0.0 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -9349,18 +4441,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-ansi@4.0.0: - dependencies: - ansi-regex: 3.0.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -9369,26 +4449,16 @@ snapshots: dependencies: ansi-regex: 6.1.0 - strip-eof@1.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-indent@2.0.0: {} - - strip-json-comments@3.1.1: {} - - stylehacks@5.1.1(postcss@8.5.3): + strip-literal@2.1.1: dependencies: - browserslist: 4.24.5 - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 + js-tokens: 9.0.1 - stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.99.8): + stylus-loader@7.1.3(stylus@0.58.1)(webpack@5.101.0): dependencies: fast-glob: 3.3.3 normalize-path: 3.0.0 stylus: 0.58.1 - webpack: 5.99.8 + webpack: 5.101.0 stylus@0.58.1: dependencies: @@ -9396,13 +4466,13 @@ snapshots: debug: 4.4.1 glob: 7.2.3 sax: 1.2.4 - source-map: 0.7.4 + source-map: 0.7.6 transitivePeerDependencies: - supports-color sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.12 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -9410,39 +4480,17 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - supports-color@8.1.1: dependencies: has-flag: 4.0.0 supports-preserve-symlinks-flag@1.0.0: {} - svg-tags@1.0.0: {} - - svgo@2.8.0: + sync-child-process@1.0.2: dependencies: - '@trysound/sax': 0.2.0 - commander: 7.2.0 - css-select: 4.3.0 - css-tree: 1.1.3 - csso: 4.2.0 - picocolors: 1.1.1 - stable: 0.1.8 + sync-message-port: 1.1.3 - table@6.9.0: - dependencies: - ajv: 8.17.1 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + sync-message-port@1.1.3: {} tailwindcss@3.4.17: dependencies: @@ -9460,37 +4508,35 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.3 - postcss-import: 15.1.0(postcss@8.5.3) - postcss-js: 4.0.1(postcss@8.5.3) - postcss-load-config: 4.0.2(postcss@8.5.3) - postcss-nested: 6.2.0(postcss@8.5.3) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.0.1(postcss@8.5.6) + postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.10 sucrase: 3.35.0 transitivePeerDependencies: - ts-node - tapable@2.2.1: {} + tapable@2.2.2: {} - terser-webpack-plugin@5.3.14(webpack@5.99.8): + terser-webpack-plugin@5.3.14(webpack@5.101.0): dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - terser: 5.39.2 - webpack: 5.99.8 + terser: 5.43.1 + webpack: 5.101.0 - terser@5.39.2: + terser@5.43.1: dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.14.1 + '@jridgewell/source-map': 0.3.10 + acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 - text-table@0.2.0: {} - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -9503,18 +4549,7 @@ snapshots: dependencies: tslib: 2.8.1 - thread-loader@3.0.4(webpack@5.99.8): - dependencies: - json-parse-better-errors: 1.0.2 - loader-runner: 4.3.0 - loader-utils: 2.0.4 - neo-async: 2.6.2 - schema-utils: 3.3.0 - webpack: 5.99.8 - - three@0.128.0: {} - - thunky@1.1.0: {} + three@0.160.1: {} tiny-emitter@2.1.0: {} @@ -9522,13 +4557,9 @@ snapshots: dependencies: is-number: 7.0.0 - toidentifier@1.0.1: {} - - totalist@3.0.1: {} - tr46@0.0.3: {} - tree-dump@1.0.2(tslib@2.8.1): + tree-dump@1.0.3(tslib@2.8.1): dependencies: tslib: 2.8.1 @@ -9538,206 +4569,106 @@ snapshots: tslib@2.8.1: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.20.2: {} - - type-fest@0.6.0: {} - - type-fest@0.8.1: {} - - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - uc.micro@1.0.6: {} - undici-types@6.21.0: {} + ufo@1.6.1: {} - unicode-canonical-property-names-ecmascript@2.0.1: {} + undici-types@7.8.0: {} - unicode-match-property-ecmascript@2.0.0: + unimport@3.14.6(rollup@4.46.1): dependencies: - unicode-canonical-property-names-ecmascript: 2.0.1 - unicode-property-aliases-ecmascript: 2.1.0 + '@rollup/pluginutils': 5.2.0(rollup@4.46.1) + acorn: 8.15.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + fast-glob: 3.3.3 + local-pkg: 1.1.1 + magic-string: 0.30.17 + mlly: 1.7.4 + pathe: 2.0.3 + picomatch: 4.0.3 + pkg-types: 1.3.1 + scule: 1.3.0 + strip-literal: 2.1.1 + unplugin: 1.16.1 + transitivePeerDependencies: + - rollup - unicode-match-property-value-ecmascript@2.2.0: {} - - unicode-property-aliases-ecmascript@2.1.0: {} - - universalify@2.0.1: {} - - unpipe@1.0.0: {} - - update-browserslist-db@1.1.3(browserslist@4.24.5): + unplugin-auto-import@0.18.6(@vueuse/core@9.13.0(vue@3.5.18))(rollup@4.46.1): dependencies: - browserslist: 4.24.5 + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.2.0(rollup@4.46.1) + fast-glob: 3.3.3 + local-pkg: 0.5.1 + magic-string: 0.30.17 + minimatch: 9.0.5 + unimport: 3.14.6(rollup@4.46.1) + unplugin: 1.16.1 + optionalDependencies: + '@vueuse/core': 9.13.0(vue@3.5.18) + transitivePeerDependencies: + - rollup + + unplugin@1.16.1: + dependencies: + acorn: 8.15.0 + webpack-virtual-modules: 0.6.2 + + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - util-deprecate@1.0.2: {} - utila@0.4.0: {} - - utils-merge@1.0.1: {} - - uuid@8.3.2: {} - - v3-waterfall@1.3.3: {} - - v8-compile-cache@2.4.0: {} - valid-data-url@3.0.1: {} - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - vant@4.9.19(vue@3.5.14): + vant@4.9.21(vue@3.5.18): dependencies: '@vant/popperjs': 1.3.0 - '@vant/use': 1.6.0(vue@3.5.14) - '@vue/shared': 3.5.14 - vue: 3.5.14 + '@vant/use': 1.6.0(vue@3.5.18) + '@vue/shared': 3.5.18 + vue: 3.5.18 - vary@1.1.2: {} + varint@6.0.0: {} - vue-demi@0.14.10(vue@3.5.14): + vite@5.4.19(@types/node@24.1.0)(sass-embedded@1.89.2)(stylus@0.58.1)(terser@5.43.1): dependencies: - vue: 3.5.14 - - vue-eslint-parser@8.3.0(eslint@7.32.0): - dependencies: - debug: 4.4.1 - eslint: 7.32.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - lodash: 4.17.21 - semver: 7.7.2 - transitivePeerDependencies: - - supports-color - - vue-hot-reload-api@2.3.4: {} - - vue-loader@15.11.1(@vue/compiler-sfc@3.5.14)(css-loader@6.11.0(webpack@5.99.8))(lodash@4.17.21)(webpack@5.99.8): - dependencies: - '@vue/component-compiler-utils': 3.3.0(lodash@4.17.21) - css-loader: 6.11.0(webpack@5.99.8) - hash-sum: 1.0.2 - loader-utils: 1.4.2 - vue-hot-reload-api: 2.3.4 - vue-style-loader: 4.1.3 - webpack: 5.99.8 + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.46.1 optionalDependencies: - '@vue/compiler-sfc': 3.5.14 - transitivePeerDependencies: - - arc-templates - - atpl - - babel-core - - bracket-template - - coffee-script - - dot - - dust - - dustjs-helpers - - dustjs-linkedin - - eco - - ect - - ejs - - haml-coffee - - hamlet - - hamljs - - handlebars - - hogan.js - - htmling - - jade - - jazz - - jqtpl - - just - - liquid-node - - liquor - - lodash - - marko - - mote - - mustache - - nunjucks - - plates - - pug - - qejs - - ractive - - razor-tmpl - - react - - react-dom - - slm - - squirrelly - - swig - - swig-templates - - teacup - - templayed - - then-jade - - then-pug - - tinyliquid - - toffee - - twig - - twing - - underscore - - vash - - velocityjs - - walrus - - whiskers + '@types/node': 24.1.0 + fsevents: 2.3.3 + sass-embedded: 1.89.2 + stylus: 0.58.1 + terser: 5.43.1 - vue-loader@17.4.2(@vue/compiler-sfc@3.5.14)(vue@3.5.14)(webpack@5.99.8): + vue-demi@0.14.10(vue@3.5.18): dependencies: - chalk: 4.1.2 - hash-sum: 2.0.0 - watchpack: 2.4.2 - webpack: 5.99.8 - optionalDependencies: - '@vue/compiler-sfc': 3.5.14 - vue: 3.5.14 + vue: 3.5.18 - vue-router@4.5.1(vue@3.5.14): + vue-router@4.5.1(vue@3.5.18): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.14 + vue: 3.5.18 - vue-style-loader@4.1.3: + vue-waterfall-plugin-next@2.6.7: {} + + vue@3.5.18: dependencies: - hash-sum: 1.0.2 - loader-utils: 1.4.2 + '@vue/compiler-dom': 3.5.18 + '@vue/compiler-sfc': 3.5.18 + '@vue/runtime-dom': 3.5.18 + '@vue/server-renderer': 3.5.18(vue@3.5.18) + '@vue/shared': 3.5.18 - vue-template-es2015-compiler@1.9.1: {} - - vue@3.5.14: - dependencies: - '@vue/compiler-dom': 3.5.14 - '@vue/compiler-sfc': 3.5.14 - '@vue/runtime-dom': 3.5.14 - '@vue/server-renderer': 3.5.14(vue@3.5.14) - '@vue/shared': 3.5.14 - - watchpack@2.4.2: + watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - wbuf@1.7.3: - dependencies: - minimalistic-assert: 1.0.1 - - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - web-resource-inliner@6.0.1: dependencies: ansi-colors: 4.1.3 @@ -9751,100 +4682,23 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-bundle-analyzer@4.10.2: - dependencies: - '@discoveryjs/json-ext': 0.5.7 - acorn: 8.14.1 - acorn-walk: 8.3.4 - commander: 7.2.0 - debounce: 1.2.1 - escape-string-regexp: 4.0.0 - gzip-size: 6.0.0 - html-escaper: 2.0.2 - opener: 1.5.2 - picocolors: 1.1.1 - sirv: 2.0.4 - ws: 7.5.10 - transitivePeerDependencies: - - bufferutil - - utf-8-validate + webpack-sources@3.3.3: {} - webpack-chain@6.5.1: - dependencies: - deepmerge: 1.5.2 - javascript-stringify: 2.1.0 + webpack-virtual-modules@0.6.2: {} - webpack-dev-middleware@5.3.4(webpack@5.99.8): - dependencies: - colorette: 2.0.20 - memfs: 3.5.3 - mime-types: 2.1.35 - range-parser: 1.2.1 - schema-utils: 4.3.2 - webpack: 5.99.8 - - webpack-dev-server@4.15.2(debug@4.4.1)(webpack@5.99.8): - dependencies: - '@types/bonjour': 3.5.13 - '@types/connect-history-api-fallback': 1.5.4 - '@types/express': 4.17.21 - '@types/serve-index': 1.9.4 - '@types/serve-static': 1.15.7 - '@types/sockjs': 0.3.36 - '@types/ws': 8.18.1 - ansi-html-community: 0.0.8 - bonjour-service: 1.3.0 - chokidar: 3.6.0 - colorette: 2.0.20 - compression: 1.8.0 - connect-history-api-fallback: 2.0.0 - default-gateway: 6.0.3 - express: 4.21.2 - graceful-fs: 4.2.11 - html-entities: 2.6.0 - http-proxy-middleware: 2.0.9(@types/express@4.17.21)(debug@4.4.1) - ipaddr.js: 2.2.0 - launch-editor: 2.10.0 - open: 8.4.2 - p-retry: 4.6.2 - rimraf: 3.0.2 - schema-utils: 4.3.2 - selfsigned: 2.4.1 - serve-index: 1.9.1 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.99.8) - ws: 8.18.2 - optionalDependencies: - webpack: 5.99.8 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - - webpack-merge@5.10.0: - dependencies: - clone-deep: 4.0.1 - flat: 5.0.2 - wildcard: 2.0.1 - - webpack-sources@3.2.3: {} - - webpack-virtual-modules@0.4.6: {} - - webpack@5.99.8: + webpack@5.101.0: dependencies: '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.14.1 - browserslist: 4.24.5 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.25.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -9855,25 +4709,15 @@ snapshots: mime-types: 2.1.35 neo-async: 2.6.2 schema-utils: 4.3.2 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.14(webpack@5.99.8) - watchpack: 2.4.2 - webpack-sources: 3.2.3 + tapable: 2.2.2 + terser-webpack-plugin: 5.3.14(webpack@5.101.0) + watchpack: 2.4.4 + webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - websocket-driver@0.7.4: - dependencies: - http-parser-js: 0.5.10 - safe-buffer: 5.2.1 - websocket-extensions: 0.1.4 - - websocket-extensions@0.1.4: {} - - whatwg-fetch@3.6.20: {} - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -9881,25 +4725,12 @@ snapshots: which-module@2.0.1: {} - which@1.3.1: - dependencies: - isexe: 2.0.0 - which@2.0.2: dependencies: isexe: 2.0.0 wicked-good-xpath@1.3.0: {} - wildcard@2.0.1: {} - - word-wrap@1.2.5: {} - - wrap-ansi@3.0.1: - dependencies: - string-width: 2.1.1 - strip-ansi: 4.0.0 - wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -9920,22 +4751,8 @@ snapshots: wrappy@1.0.2: {} - ws@7.5.10: {} - - ws@8.18.2: {} - y18n@4.0.3: {} - y18n@5.0.8: {} - - yallist@2.1.2: {} - - yallist@3.1.1: {} - - yallist@4.0.0: {} - - yaml@1.10.2: {} - yaml@2.8.0: {} yargs-parser@18.1.3: @@ -9943,8 +4760,6 @@ snapshots: camelcase: 5.3.1 decamelize: 1.2.0 - yargs-parser@20.2.9: {} - yargs@15.4.1: dependencies: cliui: 6.0.0 @@ -9959,23 +4774,6 @@ snapshots: y18n: 4.0.3 yargs-parser: 18.1.3 - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - - yorkie@2.0.0: - dependencies: - execa: 0.8.0 - is-ci: 1.2.1 - normalize-path: 1.0.0 - strip-indent: 2.0.0 - zrender@5.6.1: dependencies: tslib: 2.3.0 diff --git a/web/public/images/ga_beian.png b/web/public/images/ga_beian.png new file mode 100644 index 00000000..55116af3 Binary files /dev/null and b/web/public/images/ga_beian.png differ diff --git a/web/public/images/jimeng/templates/acrylic_ornaments.png b/web/public/images/jimeng/templates/acrylic_ornaments.png new file mode 100644 index 00000000..dcc907fe Binary files /dev/null and b/web/public/images/jimeng/templates/acrylic_ornaments.png differ diff --git a/web/public/images/jimeng/templates/angel_figurine.png b/web/public/images/jimeng/templates/angel_figurine.png new file mode 100644 index 00000000..2692a321 Binary files /dev/null and b/web/public/images/jimeng/templates/angel_figurine.png differ diff --git a/web/public/images/jimeng/templates/felt_3d_polaroid.png b/web/public/images/jimeng/templates/felt_3d_polaroid.png new file mode 100644 index 00000000..9765c802 Binary files /dev/null and b/web/public/images/jimeng/templates/felt_3d_polaroid.png differ diff --git a/web/public/images/jimeng/templates/felt_keychain.png b/web/public/images/jimeng/templates/felt_keychain.png new file mode 100644 index 00000000..d5b29acf Binary files /dev/null and b/web/public/images/jimeng/templates/felt_keychain.png differ diff --git a/web/public/images/jimeng/templates/furry_dream_doll.png b/web/public/images/jimeng/templates/furry_dream_doll.png new file mode 100644 index 00000000..2d8bba46 Binary files /dev/null and b/web/public/images/jimeng/templates/furry_dream_doll.png differ diff --git a/web/public/images/jimeng/templates/glass_ball.png b/web/public/images/jimeng/templates/glass_ball.png new file mode 100644 index 00000000..5dd534c5 Binary files /dev/null and b/web/public/images/jimeng/templates/glass_ball.png differ diff --git a/web/public/images/jimeng/templates/lofi_pixel_character_mini_card.png b/web/public/images/jimeng/templates/lofi_pixel_character_mini_card.png new file mode 100644 index 00000000..abe6d4c7 Binary files /dev/null and b/web/public/images/jimeng/templates/lofi_pixel_character_mini_card.png differ diff --git a/web/public/images/jimeng/templates/lying_in_fluffy_belly.png b/web/public/images/jimeng/templates/lying_in_fluffy_belly.png new file mode 100644 index 00000000..c73b8128 Binary files /dev/null and b/web/public/images/jimeng/templates/lying_in_fluffy_belly.png differ diff --git a/web/public/images/jimeng/templates/micro_landscape_mini_world.png b/web/public/images/jimeng/templates/micro_landscape_mini_world.png new file mode 100644 index 00000000..e7c61468 Binary files /dev/null and b/web/public/images/jimeng/templates/micro_landscape_mini_world.png differ diff --git a/web/public/images/jimeng/templates/micro_landscape_mini_world_professional.png b/web/public/images/jimeng/templates/micro_landscape_mini_world_professional.png new file mode 100644 index 00000000..2bf25b68 Binary files /dev/null and b/web/public/images/jimeng/templates/micro_landscape_mini_world_professional.png differ diff --git a/web/public/images/jimeng/templates/my_world.png b/web/public/images/jimeng/templates/my_world.png new file mode 100644 index 00000000..3e30a6bf Binary files /dev/null and b/web/public/images/jimeng/templates/my_world.png differ diff --git a/web/public/images/jimeng/templates/my_world_universal.png b/web/public/images/jimeng/templates/my_world_universal.png new file mode 100644 index 00000000..10b27fe0 Binary files /dev/null and b/web/public/images/jimeng/templates/my_world_universal.png differ diff --git a/web/public/images/jimeng/templates/plastic_bubble_figure.png b/web/public/images/jimeng/templates/plastic_bubble_figure.png new file mode 100644 index 00000000..ed960937 Binary files /dev/null and b/web/public/images/jimeng/templates/plastic_bubble_figure.png differ diff --git a/web/public/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png b/web/public/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png new file mode 100644 index 00000000..09fd8138 Binary files /dev/null and b/web/public/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png differ diff --git a/web/src/App.vue b/web/src/App.vue index 89974207..6576e1e8 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -78,8 +78,9 @@ console.log( ) - diff --git a/web/src/assets/css/admin/form.scss b/web/src/assets/css/admin/form.scss new file mode 100644 index 00000000..f20caa7c --- /dev/null +++ b/web/src/assets/css/admin/form.scss @@ -0,0 +1,73 @@ +.form { + .el-form-item__label { + .label-title { + display: flex; + align-items: center; + + .el-icon { + margin-left: 5px; + cursor: pointer; + } + } + } + + .el-form-item__content { + width: 100%; + + .uploader-icon { + font-size: 24px; + position: relative; + top: 3px; + } + + .tip-input-line { + .tip { + margin-top: 10px; + color: #c1c1c1; + font-size: 12px; + line-height: 1.5; + } + } + } + + .el-input { + width: 100%; + } + + .text { + font-size: 14px; + } + + .active-info { + line-height: 1.5; + padding: 10px 0 30px 0; + } + + .el-descriptions { + margin-bottom: 20px; + + .el-icon { + font-size: 18px; + } + + .selected { + color: #0bc15f; + } + + .closed { + color: #da0d54; + } + + .text { + margin-left: 10px; + font-size: 12px; + color: #999999; + position: relative; + top: -5px; + } + } + + .el-alert { + margin-bottom: 15px; + } +} \ No newline at end of file diff --git a/web/src/assets/css/admin/form.styl b/web/src/assets/css/admin/form.styl deleted file mode 100644 index d42bcb83..00000000 --- a/web/src/assets/css/admin/form.styl +++ /dev/null @@ -1,74 +0,0 @@ -.form { - .el-form-item__label { - .label-title { - display flex - align-items center - - .el-icon { - margin-left 5px - cursor pointer - } - } - } - - .el-form-item__content { - width 100% - - .uploader-icon { - font-size 24px - position relative - top 3px - } - - .tip-input-line { - .tip { - margin-top 10px - color #c1c1c1 - font-size 12px; - line-height 1.5; - } - } - } - - .el-input { - width 100% - } - - - .text { - font-size 14px - } - - .active-info { - line-height 1.5 - padding 10px 0 30px 0 - } - - .el-descriptions { - margin-bottom 20px - - .el-icon { - font-size 18px - } - - .selected { - color #0bc15f - } - - .closed { - color #da0d54 - } - - .text { - margin-left 10px - font-size 12px - color #999999 - position: relative; - top -5px - } - } - - .el-alert { - margin-bottom 15px; - } -} diff --git a/web/src/assets/css/chat-app.scss b/web/src/assets/css/chat-app.scss new file mode 100644 index 00000000..e7d86026 --- /dev/null +++ b/web/src/assets/css/chat-app.scss @@ -0,0 +1,137 @@ +.page-apps { + // background-color: #282c34; + height: 100%; + + .apps-type-nav { + height: 50px; + padding: 8px 0; + margin: 10px auto; + } + + .scrollbar-type-nav { + display: flex; + align-items: center; + + padding: 2px; + background-color: #f4f1f7; + width: fit-content; + border: 1px solid rgba(79, 89, 102, 0.078); + border-radius: 20px; + margin: 0 auto; + // background: var(--chat-bg); + // width: 100%; + li { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + margin: 5px 8px; + height: 26px; + border-radius: 4px; + // border: 1px solid rgb(80, 80, 80); + padding: 2px 12px; + // background: rgba(60, 60, 60, 0.9); + color: var(--theme-text-tertiary); + font-weight: bold; + font-size: 14px; + cursor: pointer; + + .image { + width: 22px; + height: 22px; + overflow: hidden; + margin-right: 5px; + border-radius: 50%; + } + &.active { + background: #fff; + color: var(--el-color-primary); + border-radius: 20px; + } + } + } + + .app-list-container { + display: flex; + color: #ffffff; + padding: 2px 15px; + overflow-y: visible; + overflow-x: hidden; + + .item__list-box { + .item { + display: flex; + flex-flow: row; + // border: 1px solid rgb(80, 80, 80); + padding: 10px; + background: var(--chat-bg); + border-radius: 8px; + + .image { + width: 80px; + height: 80px; + min-width: 80px; + border-radius: 50%; + overflow: hidden; + object-fit: contain; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border: 2px solid #f5f7fd; + background: #fff; + } + + .inner { + display: flex; + flex-flow: column; + padding: 0 0 0 10px; + width: 100%; + + .info { + text-align: left; + + .info-title { + color: var(--text-theme-color); + font-size: 1.25rem; + line-height: 1.75rem; + letter-spacing: 0.025em; + font-weight: 600; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + } + + .info-text { + padding: 8px 0; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + word-break: break-all; + height: 50px; + font-size: 0.875rem; + color: var(--text-fb); + } + } + + .btn { + margin-top: 10px; + display: flex; + justify-content: right; + + .el-button { + margin-left: 10px; + + .el-icon { + margin-right: 5px; + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/web/src/assets/css/chat-app.styl b/web/src/assets/css/chat-app.styl deleted file mode 100644 index 1781316b..00000000 --- a/web/src/assets/css/chat-app.styl +++ /dev/null @@ -1,142 +0,0 @@ -.page-apps { - // background-color: #282c34; - height 100% - - .apps-type-nav{ - height 50px - padding 8px 0; - margin 10px auto - } - - .scrollbar-type-nav{ - display flex - align-items center - - padding 2px - background-color #f4f1f7 - width fit-content - border 1px solid rgba(79,89,102,.078) - border-radius: 20px - margin: 0 auto - // background: var(--chat-bg); - // width 100% - li{ - flex-shrink 0 - display flex - align-items center - justify-content center - margin 5px 8px - height 26px - border-radius 4px - // border 1px solid rgb(80,80,80) - padding 2px 12px - // background rgba(60,60,60 0.9) - color var(--theme-text-tertiary) - font-weight: bold - font-size 14px - cursor pointer - - .image { - width 22px - height 22px - overflow hidden - margin-right 5px - border-radius 50% - - } - &.active{ - background #fff; - color: var(--el-color-primary); - border-radius 20px - } - } - } - - - .app-list-container { - display flex - color #ffffff - padding 2px 15px; - overflow-y visible - overflow-x hidden - - .item__list-box { - .item { - display flex - flex-flow row - // border 1px solid rgb(80,80,80) - padding 10px - background: var(--chat-bg); - border-radius 8px - - .image { - width 80px - height 80px - min-width 80px - border-radius 50% - overflow hidden - object-fit: contain - display: flex - align-items center - justify-content center - flex-shrink 0 - border: 2px solid #f5f7fd - background: #fff - } - - .inner { - display flex - flex-flow column - padding 0 0 0 10px - width 100% - - .info { - text-align left - - .info-title { - color: var(--text-theme-color) - font-size 1.25rem - line-height 1.75rem - letter-spacing: .025em; - font-weight: 600; - word-break: break-all; - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - } - - .info-text { - padding 8px 0 - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - word-break: break-all; - height 50px - font-size: .875rem; - color var(--text-fb) - - } - } - - .btn { - margin-top 10px - display flex - justify-content right - - .el-button { - margin-left 10px - - .el-icon { - margin-right 5px - } - } - } - } - } - - } - } - -} \ No newline at end of file diff --git a/web/src/assets/css/chat-plus.styl b/web/src/assets/css/chat-plus.scss similarity index 50% rename from web/src/assets/css/chat-plus.styl rename to web/src/assets/css/chat-plus.scss index 3335718d..dc993443 100644 --- a/web/src/assets/css/chat-plus.styl +++ b/web/src/assets/css/chat-plus.scss @@ -1,449 +1,438 @@ - -#app { - - height: 100%; - - .chat-page { - height: 100%; - :deep(.el-message-box__message){ - font-size: 18px !important - } - .newChat{ - margin-bottom: 10px - } - // left side - .el-container{ - height: 100%; - } - .el-aside { - padding 10px - width var(--el-aside-width, 320px) - - .chat-list-container { - display: flex - flex-flow: column - border-radius 10px - padding 10px 0 - - .search-box { - flex-wrap: wrap - margin-bottom: 10px - } - - .content { - width: 100% - overflow-y: scroll - - .chat-list-item { - display: flex - width: 100% - justify-content: flex-start - padding: 8px 12px - //border: 1px solid #3c3c3c - cursor: pointer - border: 1px solid var(--theme-bg-color) - margin-bottom 6px - border-radius 5px - - &:hover { - border: 1px solid var(--border-active); - } - - .avatar { - width: 32px; - height: 32px; - border-radius: 50%; - } - - .chat-title-input { - font-size: 14px; - margin-top: 4px; - margin-left: 10px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - width: 190px; - } - - .chat-title { - color: var(--el-text-color-regular); - padding: 5px 10px; - max-width 220px; - font-size 14px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - - .chat-opt { - position: absolute; - right: 2px; - top: 16px; - color var(--text-fb) - - - .el-dropdown-link { - color var(--text-fb) - } - - .el-icon { - margin-right 8px; - } - } - } - - .chat-list-item.active { - background-color :var(--theme-bg); - box-shadow: 0 3px 9px rgba(112, 144, 176, 0.12); - border: 1px solid var(--border-active); - } - } - } - - - .tool-box { - display: flex; - justify-content: center; - padding-top 12px - // border-top 0.5px solid var(--el-border-color); - - .iconfont { - margin-right 5px - } - } - } - - .el-main { - overflow: hidden; - --el-main-padding: 0; - margin: 0; - - .chat-container { - min-width: 0; - flex: 1; - background-color: var(--chat-bg) - color var(--text-fb) - - .chat-config { - height 50px - padding 10px 30px - display flex - justify-content center - justify-items center - // border-bottom 1px solid var(--el-border-color); - - .role-select-label { - color #ffffff - } - - .el-select { - max-width 150px; - margin-right 10px; - } - - .role-select { - max-width 130px; - } - - .setting { - // padding 5px - border-radius 5px - cursor pointer - width: 26px; - height: 26px; - text-align: center; - line-height: 26px; - // background-color #f2f2f2 - // margin-right 10px - .iconfont { - font-size 16px - color var(--el-color-primary) - } - - &:hover { - background-color var(--text--hover) - } - } - - .el-button { - .el-icon { - margin-right 5px; - } - } - } - - #container { - overflow: hidden; - width: 100%; - position relative - background: var(--chat-bg) - display: flex; - justify-content: center; - padding 0 20px - max-width: 960px; - - .chat-box { - overflow-y: auto; - // 变量定义 - --content-font-size: 16px; - --content-color: #c1c1c1; - - font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; - //padding: 0 0 50px 0; - width: 100%; - - .chat-line { - font-size: 14px; - display: flex; - align-items: flex-start; - } - } - - .input-box { - position absolute - bottom 0 - width 100% - max-width: 800px; - - .input-box-inner { - display flex - background-color:var(--chat-bg); - - justify-content: center; - align-items: center; - // box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1); - padding 0 15px; - - .tool-item { - margin-right 15px - border-radius: 6px; - color: #19c37d; - display flex - justify-content center - justify-items center - padding 6px - cursor pointer - // background #F2F2F2 - - &:hover { - background #D5FAD3 - } - - .iconfont { - font-size: 24px; - } - } - - .input-body { - width 100% - margin: 0; - border: none; - padding: 10px 0; - display flex - justify-content center - position relative - - .hide-div { - white-space: pre-wrap; /* 保持文本换行 */ - visibility: hidden; /* 隐藏 div */ - position: absolute; /* 脱离文档流 */ - line-height: 24px - font-size 14px - word-wrap: break-word; /* 允许单词换行 */ - overflow-wrap: break-word; /* 允许长单词换行,适用于现代浏览器 */ - } - - .input-border { - // display flex - width 100% - overflow hidden - border: 2px solid var( --theme-border-primary) - border-radius 10px - padding 10px - // background-color #F4F4F4 - - &:hover{ - border-color var(--theme-border-hover) - } - - .input-inner { - display flex - flex-flow column - width 100% - - .file-list { - padding-bottom 10px - } - .prompt-input::-webkit-scrollbar { - width: 0; - height: 0; - } - - .prompt-input { - min-height: 58px; - width 100% - line-height: 24px - border none - font-size 14px - background none - resize: none - white-space: pre-wrap; /* 保持文本换行 */ - word-wrap: break-word; /* 允许单词换行 */ - overflow-wrap: break-word; /* 允许长单词换行,适用于现代浏览器 */ - } - } - - - .send-btn { - width 32px - margin-left 10px - - .el-button { - padding 8px 5px; - border-radius 6px; - font-size 20px; - } - } - .little-btns{ - display: flex; - justify-content: flex-end; - align-items: center; - gap: 8px; - - .iconfont{ - font-size: 19px; - cursor pointer - background-color: var(--chat-content-bg); - padding: 5px; - border-radius: 6px; - } - } - - .add-new{ - .el-icon{ - font-size: 20px; - color: #754ff6; - } - cursor:pointer - } - - } - } - } - - } - } - } - } - } - - .el-message-box { - width: 90%; - max-width: 420px; - } - - .el-message { - min-width: 100px; - max-width: 600px; - } -} - -.el-select-dropdown__wrap { - .el-select-dropdown__item { - .role-option { - display flex - flex-flow row - margin-top 8px; - - .el-image { - width 20px - height 20px - border-radius 50% - } - - span { - margin-left 5px; - height 20px; - line-height 20px; - } - } - } -} - -.account { - display flex - background-color #90FFC2 - color #000000 - width 100% - border-radius 10px - padding 10px - - .vip-logo { - .el-image { - width 40px - height 40px - border-radius 100% - background-color:var(--chat-content-bg); - color:var(--theme-text-color-primary); - } - } - - .vip-info { - padding: 0 10px 0 10px - - h4, p { - margin 0 - } - - h4 { - font-weight bold - font-size 16px; - } - - p { - color #333333 - } - } - - .pay-btn { - width 100% - display flex - justify-content right - align-items center - - } -} - -.el-overlay-dialog { - .el-dialog { - .el-dialog__body { - .notice { - line-height 1.8 - font-size 16px - overflow auto - height: 70vh - } - - } - .dialog-footer{ - margin-right: 22px; - } - } -} - -.dialog-service { - text-align center - - .el-image { - width 360px; - } -} - -.tools-dropdown { - width auto - .el-icon { - margin-left 5px; - } -} - +#app { + height: 100%; + + .chat-page { + height: 100%; + :deep(.el-message-box__message) { + font-size: 18px !important; + } + .newChat { + margin-bottom: 10px; + } + // left side + .el-container { + height: 100%; + } + .el-aside { + padding: 10px; + width: var(--el-aside-width, 320px); + + .chat-list-container { + display: flex; + flex-flow: column; + border-radius: 10px; + padding: 10px 0; + + .search-box { + flex-wrap: wrap; + margin-bottom: 10px; + } + + .content { + width: 100%; + overflow-y: scroll; + + .chat-list-item { + display: flex; + width: 100%; + justify-content: flex-start; + padding: 8px 12px; + // border: 1px solid #3c3c3c; + cursor: pointer; + border: 1px solid var(--theme-bg-color); + margin-bottom: 6px; + border-radius: 5px; + + &:hover { + border: 1px solid var(--border-active); + } + + .avatar { + width: 32px; + height: 32px; + border-radius: 50%; + } + + .chat-title-input { + font-size: 14px; + margin-top: 4px; + margin-left: 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + width: 190px; + } + + .chat-title { + color: var(--el-text-color-regular); + padding: 5px 10px; + max-width: 220px; + font-size: 14px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .chat-opt { + position: absolute; + right: 2px; + top: 16px; + color: var(--text-fb); + + .el-dropdown-link { + color: var(--text-fb); + } + + .el-icon { + margin-right: 8px; + } + } + } + + .chat-list-item.active { + background-color: var(--theme-bg); + box-shadow: 0 3px 9px rgba(112, 144, 176, 0.12); + border: 1px solid var(--border-active); + } + } + } + + .tool-box { + display: flex; + justify-content: center; + padding-top: 12px; + // border-top: 0.5px solid var(--el-border-color); + + .iconfont { + margin-right: 5px; + } + } + } + + .el-main { + overflow: hidden; + --el-main-padding: 0; + margin: 0; + + .chat-container { + min-width: 0; + flex: 1; + background-color: var(--chat-bg); + color: var(--text-fb); + + .chat-config { + height: 50px; + padding: 10px 30px; + display: flex; + justify-content: center; + justify-items: center; + // border-bottom: 1px solid var(--el-border-color); + + .role-select-label { + color: #ffffff; + } + + .el-select { + max-width: 150px; + margin-right: 10px; + } + + .role-select { + max-width: 130px; + } + + .setting { + // padding: 5px; + border-radius: 5px; + cursor: pointer; + width: 26px; + height: 26px; + text-align: center; + line-height: 26px; + // background-color: #f2f2f2; + // margin-right: 10px; + .iconfont { + font-size: 16px; + color: var(--el-color-primary); + } + + &:hover { + background-color: var(--text--hover); + } + } + + .el-button { + .el-icon { + margin-right: 5px; + } + } + } + + #container { + overflow: hidden; + width: 100%; + position: relative; + background: var(--chat-bg); + display: flex; + justify-content: center; + padding: 0 20px; + max-width: 960px; + + .chat-box { + overflow-y: auto; + // 变量定义 + --content-font-size: 16px; + --content-color: #c1c1c1; + + font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; + // padding: 0 0 50px 0; + width: 100%; + + .chat-line { + font-size: 14px; + display: flex; + align-items: flex-start; + } + } + + .input-box { + position: absolute; + bottom: 0; + width: 100%; + max-width: 800px; + + .input-box-inner { + display: flex; + background-color: var(--chat-bg); + + justify-content: center; + align-items: center; + // box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1); + padding: 0 15px; + + .tool-item { + margin-right: 15px; + border-radius: 6px; + color: #19c37d; + display: flex; + justify-content: center; + justify-items: center; + padding: 6px; + cursor: pointer; + // background: #f2f2f2; + + &:hover { + background: #d5fad3; + } + + .iconfont { + font-size: 24px; + } + } + + .input-body { + width: 100%; + margin: 0; + border: none; + padding: 10px 0; + display: flex; + justify-content: center; + position: relative; + + .hide-div { + white-space: pre-wrap; /* 保持文本换行 */ + visibility: hidden; /* 隐藏 div */ + position: absolute; /* 脱离文档流 */ + line-height: 24px; + font-size: 14px; + word-wrap: break-word; /* 允许单词换行 */ + overflow-wrap: break-word; /* 允许长单词换行,适用于现代浏览器 */ + } + + .input-border { + // display: flex; + width: 100%; + overflow: hidden; + border: 2px solid var(--theme-border-primary); + border-radius: 10px; + padding: 10px; + // background-color: #f4f4f4; + + &:hover { + border-color: var(--theme-border-hover); + } + + .input-inner { + display: flex; + flex-flow: column; + width: 100%; + + .file-list { + padding-bottom: 10px; + } + .prompt-input::-webkit-scrollbar { + width: 0; + height: 0; + } + + .prompt-input { + min-height: 58px; + width: 100%; + line-height: 24px; + border: none; + font-size: 14px; + background: none; + resize: none; + white-space: pre-wrap; /* 保持文本换行 */ + word-wrap: break-word; /* 允许单词换行 */ + overflow-wrap: break-word; /* 允许长单词换行,适用于现代浏览器 */ + } + } + + .send-btn { + width: 32px; + margin-left: 10px; + + .el-button { + padding: 8px 5px; + border-radius: 6px; + font-size: 20px; + } + } + .little-btns { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 8px; + + .iconfont { + font-size: 19px; + cursor: pointer; + background-color: var(--chat-content-bg); + padding: 5px; + border-radius: 6px; + } + } + + .add-new { + .el-icon { + font-size: 20px; + color: #754ff6; + } + cursor: pointer; + } + } + } + } + } + } + } + } + } + + .el-message-box { + width: 90%; + max-width: 420px; + } + + .el-message { + min-width: 100px; + max-width: 600px; + } +} + +.el-select-dropdown__wrap { + .el-select-dropdown__item { + .role-option { + display: flex; + flex-flow: row; + margin-top: 8px; + + .el-image { + width: 20px; + height: 20px; + border-radius: 50%; + } + + span { + margin-left: 5px; + height: 20px; + line-height: 20px; + } + } + } +} + +.account { + display: flex; + background-color: #90ffc2; + color: #000000; + width: 100%; + border-radius: 10px; + padding: 10px; + + .vip-logo { + .el-image { + width: 40px; + height: 40px; + border-radius: 100%; + background-color: var(--chat-content-bg); + color: var(--theme-text-color-primary); + } + } + + .vip-info { + padding: 0 10px 0 10px; + + h4, p { + margin: 0; + } + + h4 { + font-weight: bold; + font-size: 16px; + } + + p { + color: #333333; + } + } + + .pay-btn { + width: 100%; + display: flex; + justify-content: right; + align-items: center; + } +} + +.el-overlay-dialog { + .el-dialog { + .el-dialog__body { + .notice { + line-height: 1.8; + font-size: 16px; + overflow: auto; + height: 70vh; + } + } + .dialog-footer { + margin-right: 22px; + } + } +} + +.dialog-service { + text-align: center; + + .el-image { + width: 360px; + } +} + +.tools-dropdown { + width: auto; + .el-icon { + margin-left: 5px; + } +} \ No newline at end of file diff --git a/web/src/assets/css/color-dark.styl b/web/src/assets/css/color-dark.scss similarity index 78% rename from web/src/assets/css/color-dark.styl rename to web/src/assets/css/color-dark.scss index ab18ff52..e5b19b04 100644 --- a/web/src/assets/css/color-dark.styl +++ b/web/src/assets/css/color-dark.scss @@ -1,95 +1,95 @@ -.dark { - color-scheme: dark; - --el-color-primary: #409eff; - --el-color-primary-light-3: #3375b9; - --el-color-primary-light-5: #2a598a; - --el-color-primary-light-7: #213d5b; - --el-color-primary-light-8: #1d3043; - --el-color-primary-light-9: #18222c; - --el-color-primary-dark-2: #66b1ff; - --el-color-success: #67c23a; - --el-color-success-light-3: #4e8e2f; - --el-color-success-light-5: #3e6b27; - --el-color-success-light-7: #2d481f; - --el-color-success-light-8: #25371c; - --el-color-success-light-9: #1c2518; - --el-color-success-dark-2: #85ce61; - --el-color-warning: #e6a23c; - --el-color-warning-light-3: #a77730; - --el-color-warning-light-5: #7d5b28; - --el-color-warning-light-7: #533f20; - --el-color-warning-light-8: #3e301c; - --el-color-warning-light-9: #292218; - --el-color-warning-dark-2: #ebb563; - --el-color-danger: #f56c6c; - --el-color-danger-light-3: #b25252; - --el-color-danger-light-5: #854040; - --el-color-danger-light-7: #582e2e; - --el-color-danger-light-8: #412626; - --el-color-danger-light-9: #2b1d1d; - --el-color-danger-dark-2: #f78989; - --el-color-error: #f56c6c; - --el-color-error-light-3: #b25252; - --el-color-error-light-5: #854040; - --el-color-error-light-7: #582e2e; - --el-color-error-light-8: #412626; - --el-color-error-light-9: #2b1d1d; - --el-color-error-dark-2: #f78989; - --el-color-info: #909399; - --el-color-info-light-3: #6b6d71; - --el-color-info-light-5: #525457; - --el-color-info-light-7: #393a3c; - --el-color-info-light-8: #2d2d2f; - --el-color-info-light-9: #202121; - --el-color-info-dark-2: #a6a9ad; - --el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.36), 0px 8px 20px rgba(0, 0, 0, 0.72); - --el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.72); - --el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.72); - --el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, 0px 8px 16px -8px #000000; - --el-bg-color-page: #0a0a0a; - --el-bg-color: #141414; - --el-bg-color-overlay: #1d1e1f; - --el-text-color-primary: #E5EAF3; - --el-text-color-regular: #CFD3DC; - --el-text-color-secondary: #A3A6AD; - --el-text-color-placeholder: #8D9095; - --el-text-color-disabled: #6C6E72; - --el-border-color-darker: #636466; - --el-border-color-dark: #58585B; - --el-border-color: #4C4D4F; - --el-border-color-light: #414243; - --el-border-color-lighter: #363637; - --el-border-color-extra-light: #2B2B2C; - --el-fill-color-darker: #424243; - --el-fill-color-dark: #39393A; - --el-fill-color: #303030; - --el-fill-color-light: #262727; - --el-fill-color-lighter: #1D1D1D; - --el-fill-color-extra-light: #191919; - --el-fill-color-blank: transparent; - --el-mask-color: rgba(0, 0, 0, 0.8); - --el-mask-color-extra-light: rgba(0, 0, 0, 0.3) - --el-menu-bg-color-dark: #39393A - --el-menu-bg-color-darker: #424243 -} - -.dark .el-button { - --el-button-disabled-text-color: rgba(255, 255, 255, 0.5) -} - -.dark .el-card { - --el-card-bg-color: var(--el-bg-color-overlay) -} - -.dark .el-empty { - --el-empty-fill-color-0: var(--el-color-black); - --el-empty-fill-color-1: #4b4b52; - --el-empty-fill-color-2: #36383d; - --el-empty-fill-color-3: #1e1e20; - --el-empty-fill-color-4: #262629; - --el-empty-fill-color-5: #202124; - --el-empty-fill-color-6: #212224; - --el-empty-fill-color-7: #1b1c1f; - --el-empty-fill-color-8: #1c1d1f; - --el-empty-fill-color-9: #18181a +.dark { + color-scheme: dark; + --el-color-primary: #409eff; + --el-color-primary-light-3: #3375b9; + --el-color-primary-light-5: #2a598a; + --el-color-primary-light-7: #213d5b; + --el-color-primary-light-8: #1d3043; + --el-color-primary-light-9: #18222c; + --el-color-primary-dark-2: #66b1ff; + --el-color-success: #67c23a; + --el-color-success-light-3: #4e8e2f; + --el-color-success-light-5: #3e6b27; + --el-color-success-light-7: #2d481f; + --el-color-success-light-8: #25371c; + --el-color-success-light-9: #1c2518; + --el-color-success-dark-2: #85ce61; + --el-color-warning: #e6a23c; + --el-color-warning-light-3: #a77730; + --el-color-warning-light-5: #7d5b28; + --el-color-warning-light-7: #533f20; + --el-color-warning-light-8: #3e301c; + --el-color-warning-light-9: #292218; + --el-color-warning-dark-2: #ebb563; + --el-color-danger: #f56c6c; + --el-color-danger-light-3: #b25252; + --el-color-danger-light-5: #854040; + --el-color-danger-light-7: #582e2e; + --el-color-danger-light-8: #412626; + --el-color-danger-light-9: #2b1d1d; + --el-color-danger-dark-2: #f78989; + --el-color-error: #f56c6c; + --el-color-error-light-3: #b25252; + --el-color-error-light-5: #854040; + --el-color-error-light-7: #582e2e; + --el-color-error-light-8: #412626; + --el-color-error-light-9: #2b1d1d; + --el-color-error-dark-2: #f78989; + --el-color-info: #909399; + --el-color-info-light-3: #6b6d71; + --el-color-info-light-5: #525457; + --el-color-info-light-7: #393a3c; + --el-color-info-light-8: #2d2d2f; + --el-color-info-light-9: #202121; + --el-color-info-dark-2: #a6a9ad; + --el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.36), 0px 8px 20px rgba(0, 0, 0, 0.72); + --el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.72); + --el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.72); + --el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, 0px 8px 16px -8px #000000; + --el-bg-color-page: #0a0a0a; + --el-bg-color: #141414; + --el-bg-color-overlay: #1d1e1f; + --el-text-color-primary: #e5eaf3; + --el-text-color-regular: #cfd3dc; + --el-text-color-secondary: #a3a6ad; + --el-text-color-placeholder: #8d9095; + --el-text-color-disabled: #6c6e72; + --el-border-color-darker: #636466; + --el-border-color-dark: #58585b; + --el-border-color: #4c4d4f; + --el-border-color-light: #414243; + --el-border-color-lighter: #363637; + --el-border-color-extra-light: #2b2b2c; + --el-fill-color-darker: #424243; + --el-fill-color-dark: #39393a; + --el-fill-color: #303030; + --el-fill-color-light: #262727; + --el-fill-color-lighter: #1d1d1d; + --el-fill-color-extra-light: #191919; + --el-fill-color-blank: transparent; + --el-mask-color: rgba(0, 0, 0, 0.8); + --el-mask-color-extra-light: rgba(0, 0, 0, 0.3); + --el-menu-bg-color-dark: #39393a; + --el-menu-bg-color-darker: #424243; +} + +.dark .el-button { + --el-button-disabled-text-color: rgba(255, 255, 255, 0.5); +} + +.dark .el-card { + --el-card-bg-color: var(--el-bg-color-overlay); +} + +.dark .el-empty { + --el-empty-fill-color-0: var(--el-color-black); + --el-empty-fill-color-1: #4b4b52; + --el-empty-fill-color-2: #36383d; + --el-empty-fill-color-3: #1e1e20; + --el-empty-fill-color-4: #262629; + --el-empty-fill-color-5: #202124; + --el-empty-fill-color-6: #212224; + --el-empty-fill-color-7: #1b1c1f; + --el-empty-fill-color-8: #1c1d1f; + --el-empty-fill-color-9: #18181a; } \ No newline at end of file diff --git a/web/src/assets/css/common.scss b/web/src/assets/css/common.scss new file mode 100644 index 00000000..8b46283e --- /dev/null +++ b/web/src/assets/css/common.scss @@ -0,0 +1,151 @@ +:root { + --sm-txt: rgba(163, 174, 208, 1); + --text-secondary: #8a939d; + --el-color-primary: rgb(107, 80, 225); + --van-primary-color: rgb(107, 80, 225); + --theme-textcolor-normal: #b0a0f8; + --el-border-radius-base: 5px; + --el-color-primary-light-5: rgb(107, 85, 255); + --el-color-primary-light-3: rgb(78, 51, 254); + --theme-btn-color: rgba(117, 81, 255, 1); + --common-text-color: #6e4ef9; + --el-component-size: 36px; + --el-color-primary-dark-2: rgb(169 152 247); + --el-button-active-border-color: rgb(169 152 247); + --el-color-success-light-9: #eafffc; + --el-color-success-light-8: #a7f0d9; + --el-message-text-color: #0ecd8b; + --el-color-success: #0ecd8b; + + --text-fff: #fff; + --theme-border-primary: rgba(86, 86, 95, 0.322); //主题边框颜色 + --theme-border-hover: rgb(107, 85, 255); //主题边框hover颜色 + --text--hover: rgba(215, 211, 240, 0.581); //主题色hover色系 + --el-input-focus-border-color: #b0a0f8; + --little-btn-bg: #e9d3f6; + --gray-btn-bg: #ededf591; + // --a-link-color: #3561ff + --a-link-color: #6e8eff; + --shadow-color: rgba(223, 71, 255, 0.6); + --sm-btn-bg: #6052ed; + --theme-text-tertiary: #595959; + --theme-btn-fill-tertiary: #f0ebff; + --theme-text-btn-tertiary: #6841ea; + + // #e7e7e8 +} +.el-dialog { + //--el-border-radius-base: calc(var(--el-component-size) / 2); + --el-dialog-border-radius: 10px; +} +.login-box { + --el-component-size: 48px; +} +.btn-go { + background: var(--btnColor); + color: #fff; + border-radius: 5px; + padding: 5px 10px; + &:hover { + color: #fff; + } +} +.btn-normal { + background: var(--theme-btn-color); + color: #fff; + border-radius: 5px; + padding: 5px 10px; + &:hover { + color: #fff; + } +} +.flex { + display: flex; + align-items: center; +} +.flex-center { + display: flex; + align-items: center; + justify-content: center; +} +.flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} +.theme-color-primary { + color: var(--el-color-primary); +} +.text-color-primary { + color: var(--text-color-primary); +} +.w100 { + width: 100%; +} +.el-input__wrapper { + background: var(--card-bg); +} +.el-dialog__title { + font-weight: bold; + line-height: 28px; +} +.el-button--primary { + border-radius: 5px; +} + +.el-button { + height: auto; +} +/* 设置滚动条的宽度 */ +::-webkit-scrollbar { + width: 12px; /* 垂直滚动条宽度 */ + height: 12px; /* 水平滚动条高度 */ +} + +/* 滚动条的轨道背景颜色 */ + +::-webkit-scrollbar-track { + background: #f1f1f1 !important; /* 滚动条轨道颜色 */ + border-radius: 6px; /* 可选:轨道圆角 */ +} + +/* 滚动条滑块的颜色 */ +::-webkit-scrollbar-thumb { + background: #888 !important; /* 滑块颜色 */ + border-radius: 6px; /* 可选:滑块圆角 */ +} + +/* 鼠标悬停在滑块上的颜色 */ +::-webkit-scrollbar-thumb:hover { + background: #555; /* 悬停时的滑块颜色 */ +} +//.el-message-box +.el-message-box { + --el-messagebox-border-radius: 10px; + --el-messagebox-padding-primary: 24px; +} +.el-message-box__container { + //border-top: 1px solid #dbd3f4; + padding-top: 7px; + .el-message-box__message { + --text-color: var(--theme-text-color-primary); + font-size: 16px; + } +} +.sm-btn-theme { + background-color: var(--theme-btn-fill-tertiary) !important; + color: var(--theme-text-btn-tertiary) !important; + border: none; +} + +.el-tag, .el-tag.el-tag--primary { + --el-tag-bg-color: #f0ebff; +} +.box-card { + padding: 20px; + background-color: var(--chat-bg); + border-radius: 8px; +} +.el-table th.el-table__cell { + background-color: var(--chat-bg); +} \ No newline at end of file diff --git a/web/src/assets/css/common.styl b/web/src/assets/css/common.styl deleted file mode 100644 index 7faea9eb..00000000 --- a/web/src/assets/css/common.styl +++ /dev/null @@ -1,154 +0,0 @@ -:root{ - --sm-txt:rgba(163, 174, 208, 1); - --text-secondary: #8a939d; - --el-color-primary: rgb(107, 80, 225); - --van-primary-color:rgb(107, 80, 225); - --theme-textcolor-normal:#b0a0f8; - --el-border-radius-base: 5px; - --el-color-primary-light-5:rgb(107, 85, 255); - --el-color-primary-light-3:rgb(78, 51, 254); - --theme-btn-color:rgba(117, 81, 255, 1) - --common-text-color:#6e4ef9; - --el-component-size: 36px; ---el-color-primary-dark-2:rgb(169 152 247); ---el-button-active-border-color:rgb(169 152 247); ---el-color-success-light-9:#EAFFFC; ---el-color-success-light-8:#A7F0D9; - --el-message-text-color:#0ECD8B; - --el-color-success:#0ECD8B; - - --text-fff:#fff - --theme-border-primary: rgba(86, 86, 95, .322); //主题边框颜色 - --theme-border-hover: rgb(107, 85, 255);//主题边框hover颜色 - --text--hover:rgba(215, 211, 240, 0.581) //主题色hover色系 - --el-input-focus-border-color: #b0a0f8; - --little-btn-bg:#e9d3f6; - --gray-btn-bg:#ededf591; - // --a-link-color: #3561ff - --a-link-color: #6e8eff - --shadow-color:rgba(223,71,255,0.6) - --sm-btn-bg:#6052ed; - --theme-text-tertiary: #595959; - --theme-btn-fill-tertiary: #f0ebff; - --theme-text-btn-tertiary: #6841ea; - - - -// #e7e7e8 -} -.el-dialog{ - //--el-border-radius-base: calc(var(--el-component-size) / 2); - --el-dialog-border-radius: 10px -} -.login-box{ - --el-component-size: 48px; - -} -.btn-go{ - background: var(--btnColor); - color: #fff; - border-radius: 5px; - padding: 5px 10px; - &:hover{ - color: #fff; - } -} -.btn-normal{ - background: var(--theme-btn-color); - color: #fff; - border-radius: 5px; - padding: 5px 10px; - &:hover{ - color: #fff; - } -} -.flex{ - display: flex; - align-items: center; - } -.flex-center{ - display: flex; - align-items: center; - justify-content: center; -} -.flex-between{ - display: flex; - align-items: center; - justify-content: space-between; -} -.theme-color-primary{ - color: var(--el-color-primary); -} -.text-color-primary{ - color:var(--text-color-primary) -} -.w100{ - width: 100%; -} -.el-input__wrapper{ - background: var( --card-bg) -} -.el-dialog__title{ - font-weight: bold; - line-height: 28px; -} -.el-button--primary{ - border-radius: 5px; -} - -.el-button { - height auto -} -/* 设置滚动条的宽度 */ -::-webkit-scrollbar { - width: 12px; /* 垂直滚动条宽度 */ - height: 12px; /* 水平滚动条高度 */ -} - -/* 滚动条的轨道背景颜色 */ - -::-webkit-scrollbar-track { - background: #f1f1f1 !important; /* 滚动条轨道颜色 */ - border-radius: 6px; /* 可选:轨道圆角 */ -} - -/* 滚动条滑块的颜色 */ -::-webkit-scrollbar-thumb { - background: #888 !important; /* 滑块颜色 */ - border-radius: 6px; /* 可选:滑块圆角 */ -} - -/* 鼠标悬停在滑块上的颜色 */ -::-webkit-scrollbar-thumb:hover { - background: #555; /* 悬停时的滑块颜色 */ -} -//.el-message-box -.el-message-box{ - --el-messagebox-border-radius: 10px - --el-messagebox-padding-primary: 24px -} -.el-message-box__container{ - //border-top: 1px solid #dbd3f4; - padding-top: 7px; - .el-message-box__message{ - --text-color:var(--theme-text-color-primary) - font-size: 16px - } -} -.sm-btn-theme{ - background-color: var(--theme-btn-fill-tertiary) !important; - color: var(--theme-text-btn-tertiary) !important; - border: none; -} - -.el-tag, .el-tag.el-tag--primary{ - --el-tag-bg-color:#f0ebff -} -.box-card{ - padding: 20px; - background-color: var(--chat-bg); - border-radius: 8px; -} -.el-table th.el-table__cell { - background-color: var(--chat-bg) -} \ No newline at end of file diff --git a/web/src/assets/css/custom-scroll.styl b/web/src/assets/css/custom-scroll.scss similarity index 90% rename from web/src/assets/css/custom-scroll.styl rename to web/src/assets/css/custom-scroll.scss index bd427a56..fc198317 100644 --- a/web/src/assets/css/custom-scroll.styl +++ b/web/src/assets/css/custom-scroll.scss @@ -1,37 +1,37 @@ -.custom-scroll { - /* 修改滚动条的颜色 */ - - ::-webkit-scrollbar { - width: 8px; /* 滚动条宽度 */ - } - - /* 修改滚动条轨道的背景颜色 */ - - ::-webkit-scrollbar-track { - background-color: #282C34; - } - - /* 修改滚动条的滑块颜色 */ - - ::-webkit-scrollbar-thumb { - background-color: #444444; - border-radius 8px - } - - /* 修改滚动条的滑块的悬停颜色 */ - - ::-webkit-scrollbar-thumb:hover { - background-color: #666666; - } - overflow: auto; /* 保持滚动功能 */ - scrollbar-width: none; /* 隐藏滚动条(Firefox) */ - -ms-overflow-style: none; /* 隐藏滚动条(IE、Edge) */ - ::-webkit-scrollbar { - display: none; /* 隐藏滚动条(Webkit 浏览器) */ - } - &.showScrollbar { - ::-webkit-scrollbar { - display: none; /* 隐藏滚动条(Webkit 浏览器) */ - } - } +.custom-scroll { + /* 修改滚动条的颜色 */ + + ::-webkit-scrollbar { + width: 8px; /* 滚动条宽度 */ + } + + /* 修改滚动条轨道的背景颜色 */ + + ::-webkit-scrollbar-track { + background-color: #282c34; + } + + /* 修改滚动条的滑块颜色 */ + + ::-webkit-scrollbar-thumb { + background-color: #444444; + border-radius: 8px; + } + + /* 修改滚动条的滑块的悬停颜色 */ + + ::-webkit-scrollbar-thumb:hover { + background-color: #666666; + } + overflow: auto; /* 保持滚动功能 */ + scrollbar-width: none; /* 隐藏滚动条(Firefox) */ + -ms-overflow-style: none; /* 隐藏滚动条(IE、Edge) */ + ::-webkit-scrollbar { + display: none; /* 隐藏滚动条(Webkit 浏览器) */ + } + &.showScrollbar { + ::-webkit-scrollbar { + display: none; /* 隐藏滚动条(Webkit 浏览器) */ + } + } } \ No newline at end of file diff --git a/web/src/assets/css/font.scss b/web/src/assets/css/font.scss new file mode 100644 index 00000000..b032ebcf --- /dev/null +++ b/web/src/assets/css/font.scss @@ -0,0 +1,16 @@ +$font-regular: 'PingFangSC-Regular', 'Roboto', 'sans-serif', 'ui-sans-serif', '-apple-system', + 'system-ui', 'Segoe UI', 'Helvetica', 'Apple Color Emoji', 'Arial', 'sans-serif', 'Segoe UI Emoji', + 'Segoe UI Symbol'; +$font-medium: 'PingFangSC-Medium', 'Roboto', 'sans-serif', 'ui-sans-serif', '-apple-system', + 'system-ui', 'Segoe UI', 'Helvetica', 'Apple Color Emoji', 'Arial', 'sans-serif', 'Segoe UI Emoji', + 'Segoe UI Symbol'; + +.font-regular { + font-family: $font-regular; + font-weight: normal; +} + +.font-medium { + font-family: $font-medium; + font-weight: 500; +} diff --git a/web/src/assets/css/font.styl b/web/src/assets/css/font.styl deleted file mode 100644 index 0831c2a0..00000000 --- a/web/src/assets/css/font.styl +++ /dev/null @@ -1,24 +0,0 @@ -@font-face { - font-family: "OPlusSans3-Regular"; - src: url("../fonts/OPlusSans3-Regular.ttf") format("truetype"); - font-weight: normal; - font-style: normal; -} -@font-face { - font-family: "OPlusSans3-Medium"; - src: url("../fonts/OPlusSans3-Medium.ttf") format("truetype"); - font-weight: normal; - font-style: normal; -} -$font-regular = "OPlusSans3-Regular", "PingFangSC-Regular", "Roboto", "sans-serif"; -$font-medium = "OPlusSans3-Medium", "PingFangSC-Medium", "Roboto", "sans-serif"; - -.font-regular { - font-family: $font-regular; - font-weight: normal; -} - -.font-medium { - font-family: $font-medium; - font-weight: 500; -} \ No newline at end of file diff --git a/web/src/assets/css/home.styl b/web/src/assets/css/home.scss similarity index 73% rename from web/src/assets/css/home.styl rename to web/src/assets/css/home.scss index 533e2e5e..47206dd9 100644 --- a/web/src/assets/css/home.styl +++ b/web/src/assets/css/home.scss @@ -1,283 +1,266 @@ -.layout { - display: flex; - position: relative; - height: 100vh; - - .big-top-title { - padding-top: 10px; - } - - .top-collapse { - padding-top: 10px - - img { - width 24px !important - height: 24px !important - } - } - - .tab-box { - align-items: center - background-color: var(--card-bg) - border-right: 1px solid var(--line-box); - // height: 100% - // position: fixed; - height: 100vh; - - .title { - font-size: 28px - height: 40px - width 120px - text-align: center; - word-wrap break-all; - overflow hidden - font-weight: 700 - color: var(--text-theme-color) - } - - img { - height: 44px - object-fit: cover - border-radius: 50% - border: 2px solid #754ff6; - background: #fff - } - - .marr { - margin-right: 4px; - } - - } - -} - -.flex-center-col { - display flex - align-items center - flex-direction column - - .iconfont { - font-size 24px - cursor pointer - color var(--text-color) - } - - .icon-new-chat { - color #ffffff - } - -} - -.menu-list-collapse { - .flex-center-col { - flex-direction: row; - } - - .menu-list-item { - - height: 38px; - line-height: 38px; - - .iconfont { - font-size 16px - } - - } - - .menu-list-item:hover, - .active { - background: rgba(79, 89, 102, .122); - - border-radius: 8px; - - .el-icon { - background: transparent !important; - } - } - - .menu-title { - font-size: 15px !important; - margin-bottom: 0 !important; - } -} - -.menu-list { - width: 65px; - - .svg-icon { - svg { - width: 30px; - height: 30px; - } - } - - .menu-list-item { - // margin-bottom: 10px; - margin: 0 8px 8px; - cursor: pointer; - font-weight: 550; - color: var(--text-theme-color); - - &:hover { - .el-icon { - background: rgba(79, 89, 102, .122); - } - - } - - .el-icon { - width: 24px; - height: 24px; - padding: 4px; - overflow: hidden; - border-radius: 50%; - font-size: 20px; - } - - &.active { - color: var(--text-theme-color); - font-weight: 700; - } - - - } - - .bot { - position: absolute; - bottom: 6px; - width 65px; - - } - - .bot-line { - - width: 100%; - height: 1px; - background: var(--line-box) - margin: 20px 0 10px 0; - } - - .menu-title { - font-size: 12px; - margin-bottom: 6px; - - - } - - .icon-house, - .icon-github { - font-size: 20px; - color: #754ff6; - cursor pointer - } - - .menu-bot-item { - display: flex; - align-items: center; - justify-content: space-around; - } -} - -.right-main { - height: 100%; - // background: #f5f7fd; - background: var(--theme-bg-all); - // background-image: linear-gradient(180deg, rgba(247, 232, 255, .54), rgba(191, 223, 255, .35)); - width: 100%; - - .loginMask { - position: absolute; - top: 0; - width: 100%; - height: 100%; - z-index: 999; - } - - .topheader { - display: flex; - position: fixed; - right: 8px; - z-index: 999; - top: 0; - // width 100%; - align-items: center; - justify-content: flex-end; - } - - .btn-go { - background: #754ff6; - margin: 10px 10px 0; - } -} - - -.el-popper { - .more-menus { - li { - padding: 0px 15px; - cursor: pointer; - border-radius: 5px; - margin: 5px 0; - height: 38px; - line-height: 38px; - - .el-image { - position: relative - top 5px - right 5px - } - - &:hover { - background: rgba(183, 176, 255, 0.5); - } - } - - li.active { - background: rgba(183, 176, 255, 0.5); - } - } - - .setting-menus { - .title { - color: var(--text-theme-color); - } - - .el-icon, - .iconfont { - font-size: 18px - margin-right: 6px - } - color: var(--text-theme-color); - } - - .username { - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - -webkit-line-clamp: 1; - } - - -} - -.rightHeightMax { - height: 100vh; - max-height: 100vh; - overflow: hidden; - -} - -.rightHeight { - height: calc(100vh - 42px); - max-height: calc(100vh - 42px); - overflow: hidden; - - .content { - padding-top: 42px; - } -} - -.content { - height: 100%; - overflow: scroll; +.layout { + display: flex; + position: relative; + height: 100vh; + + .big-top-title { + padding-top: 10px; + } + + .top-collapse { + padding-top: 10px; + + img { + width: 24px !important; + height: 24px !important; + } + } + + .tab-box { + align-items: center; + background-color: var(--card-bg); + border-right: 1px solid var(--line-box); + // height: 100%; + // position: fixed; + height: 100vh; + + .title { + font-size: 28px; + height: 40px; + width: 120px; + text-align: center; + word-wrap: break-all; + overflow: hidden; + font-weight: 700; + color: var(--text-theme-color); + } + + img { + height: 44px; + object-fit: cover; + border-radius: 50%; + border: 2px solid #754ff6; + background: #fff; + } + + .marr { + margin-right: 4px; + } + } +} + +.flex-center-col { + display: flex; + align-items: center; + flex-direction: column; + + .iconfont { + font-size: 24px; + cursor: pointer; + color: var(--text-color); + } + + .icon-new-chat { + color: #ffffff; + } +} + +.menu-list-collapse { + .flex-center-col { + flex-direction: row; + } + + .menu-list-item { + height: 38px; + line-height: 38px; + + .iconfont { + font-size: 16px; + } + } + + .menu-list-item:hover, + .active { + background: rgba(79, 89, 102, 0.122); + border-radius: 8px; + + .el-icon { + background: transparent !important; + } + } + + .menu-title { + font-size: 15px !important; + margin-bottom: 0 !important; + } +} + +.menu-list { + width: 65px; + + .svg-icon { + svg { + width: 30px; + height: 30px; + } + } + + .menu-list-item { + // margin-bottom: 10px; + margin: 0 8px 8px; + cursor: pointer; + font-weight: 550; + color: var(--text-theme-color); + + &:hover { + .el-icon { + background: rgba(79, 89, 102, 0.122); + } + } + + .el-icon { + width: 24px; + height: 24px; + padding: 4px; + overflow: hidden; + border-radius: 50%; + font-size: 20px; + } + + &.active { + color: var(--text-theme-color); + font-weight: 700; + } + } + + .bot { + position: absolute; + bottom: 6px; + width: 65px; + } + + .bot-line { + width: 100%; + height: 1px; + background: var(--line-box); + margin: 20px 0 10px 0; + } + + .menu-title { + font-size: 12px; + margin-bottom: 6px; + } + + .icon-house, + .icon-github { + font-size: 20px; + color: #754ff6; + cursor: pointer; + } + + .menu-bot-item { + display: flex; + align-items: center; + justify-content: space-around; + } +} + +.right-main { + height: 100%; + // background: #f5f7fd; + background: var(--theme-bg-all); + // background-image: linear-gradient(180deg, rgba(247, 232, 255, .54), rgba(191, 223, 255, .35)); + width: 100%; + + .loginMask { + position: absolute; + top: 0; + width: 100%; + height: 100%; + z-index: 999; + } + + .topheader { + display: flex; + position: fixed; + right: 8px; + z-index: 999; + top: 0; + // width: 100%; + align-items: center; + justify-content: flex-end; + } + + .btn-go { + background: #754ff6; + margin: 10px 10px 0; + } +} + +.el-popper { + .more-menus { + li { + padding: 0px 15px; + cursor: pointer; + border-radius: 5px; + margin: 5px 0; + height: 38px; + line-height: 38px; + + .el-image { + position: relative; + top: 5px; + right: 5px; + } + + &:hover { + background: rgba(183, 176, 255, 0.5); + } + } + + li.active { + background: rgba(183, 176, 255, 0.5); + } + } + + .setting-menus { + .title { + color: var(--text-theme-color); + } + + .el-icon, + .iconfont { + font-size: 18px; + margin-right: 6px; + } + color: var(--text-theme-color); + } + + .username { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + -webkit-line-clamp: 1; + } +} + +.rightHeightMax { + height: 100vh; + max-height: 100vh; + overflow: hidden; +} + +.rightHeight { + height: calc(100vh - 42px); + max-height: calc(100vh - 42px); + overflow: hidden; + + .content { + padding-top: 42px; + } +} + +.content { + height: 100%; + overflow: scroll; } \ No newline at end of file diff --git a/web/src/assets/css/image-dall.styl b/web/src/assets/css/image-dall.scss similarity index 53% rename from web/src/assets/css/image-dall.styl rename to web/src/assets/css/image-dall.scss index 363f6d15..c15676eb 100644 --- a/web/src/assets/css/image-dall.styl +++ b/web/src/assets/css/image-dall.scss @@ -1,233 +1,224 @@ -.page-dall { - // background-color: #282c34; - - .inner { - display: flex; - - .sd-box { - margin 10px - // background-color #262626 - // border 1px solid #454545 - min-width 300px - max-width 300px - padding 10px - border-radius 10px - color var(--text-theme-color); - font-size 14px - - h2 { - font-weight: bold; - font-size 20px - text-align center - color#b0a0f8 - } - - // 隐藏滚动条 - - ::-webkit-scrollbar { - width: 0; - height: 0; - background-color: transparent; - } - - .sd-params { - margin-top 10px - overflow auto - - - .param-line { - padding 0 10px - - .grid-content - .form-item-inner { - display flex - - .info-icon { - margin-left 10px - position relative - top 8px - } - } - - } - - .param-line.pt { - padding-top 5px - padding-bottom 5px - } - - .text-info { - padding 10px - } - } - - .submit-btn { - padding 10px 15px 0 15px - text-align center - - .el-button { - width 200px - - - } - } - } - - .el-form { - .el-form-item__label { - color var(--text-theme-color) - } - } - - .task-list-box { - background: var(--chat-bg); - width 100% - color var(--text-theme-color) - overflow-x hidden - - .task-list-inner { - .el-tabs { - --el-tabs-header-height: 55px; - } - - .el-tabs__item { - color: var(--text-theme-color); - font-size: 18px; - } - - .title-tabs .el-tabs__item.is-active { - color:#b0a0f8; - font-size: 18px; - } - - .title-tabs .el-tabs__active-bar { - background-color:#b0a0f8; - } - - .el-textarea { - // --el-input-focus-border-color:#b0a0f8; - } - - .el-textarea__inner { - background: transparent; - color: var(--text-theme-color); - } - - .el-input__wrapper { - background: transparent; - padding 5px - } - - .text { - margin-bottom: 10px; - color: #6b778c; - font-size: 15px - } - - .param-line.pt { - padding-top 5px - padding-bottom 5px - } - - .form-item-inner { - display flex - align-items: center - - .el-icon { - margin-left 10px - } - } - - .el-form-item__label { - color var(--text-theme-color) - } - - // 图片上传样式 - - .img-inline { - display flex - - .img-uploader { - .el-upload { - border: 1px dashed var(--el-border-color); - border-radius: 6px; - cursor: pointer; - position: relative; - overflow: hidden; - width 120px; - transition: var(--el-transition-duration-fast); - margin-bottom: 20px; - - &:hover { - border-color: var(--el-color-primary); - } - - .el-icon.uploader-icon { - font-size: 28px - color: #8c939d - width 100% - height: 120px - text-align: center - } - } - } - - .img-list-box { - display flex - - .img-item { - width 120px - position relative - margin-right 10px - - .el-image { - width 120px - height 120px - border-radius 5px - } - - .el-button { - position absolute - right 5px - top 5px - width 20px - height 20px - } - } - } - } - - .el-row.text-info { - width 100% - padding 10px 0 - - .el-tag { - margin-right 10px - } - } - - // 提交按钮 - - .submit-btn { - display flex - margin: 20px 0 - - .el-button { - width 200px - } - } - - - // 任务列表 - @import "waterfall-list.styl" - } - - .no-more-data { - text-align center - padding 30px - } - } - } - -} - +@use 'waterfall-list.scss' as *; + +.page-dall { + // background-color: #282c34; + + .inner { + display: flex; + + .sd-box { + margin: 10px; + // background-color: #262626; + // border: 1px solid #454545; + min-width: 300px; + max-width: 300px; + padding: 10px; + border-radius: 10px; + color: var(--text-theme-color); + font-size: 14px; + + h2 { + font-weight: bold; + font-size: 20px; + text-align: center; + color: #b0a0f8; + } + + // 隐藏滚动条 + ::-webkit-scrollbar { + width: 0; + height: 0; + background-color: transparent; + } + + .sd-params { + margin-top: 10px; + overflow: auto; + + .param-line { + padding: 0 10px; + + .grid-content, + .form-item-inner { + display: flex; + + .info-icon { + margin-left: 10px; + position: relative; + top: 8px; + } + } + } + + .param-line.pt { + padding-top: 5px; + padding-bottom: 5px; + } + + .text-info { + padding: 10px; + } + } + + .submit-btn { + padding: 10px 15px 0 15px; + text-align: center; + + .el-button { + width: 200px; + } + } + } + + .el-form { + .el-form-item__label { + color: var(--text-theme-color); + } + } + + .task-list-box { + background: var(--chat-bg); + width: 100%; + color: var(--text-theme-color); + overflow-x: hidden; + + .task-list-inner { + .el-tabs { + --el-tabs-header-height: 55px; + } + + .el-tabs__item { + color: var(--text-theme-color); + font-size: 18px; + } + + .title-tabs .el-tabs__item.is-active { + color: #b0a0f8; + font-size: 18px; + } + + .title-tabs .el-tabs__active-bar { + background-color: #b0a0f8; + } + + .el-textarea { + // --el-input-focus-border-color: #b0a0f8; + } + + .el-textarea__inner { + background: transparent; + color: var(--text-theme-color); + } + + .el-input__wrapper { + background: transparent; + padding: 5px; + } + + .text { + margin-bottom: 10px; + color: #6b778c; + font-size: 15px; + } + + .param-line.pt { + padding-top: 5px; + padding-bottom: 5px; + } + + .form-item-inner { + display: flex; + align-items: center; + + .el-icon { + margin-left: 10px; + } + } + + .el-form-item__label { + color: var(--text-theme-color); + } + + // 图片上传样式 + .img-inline { + display: flex; + + .img-uploader { + .el-upload { + border: 1px dashed var(--el-border-color); + border-radius: 6px; + cursor: pointer; + position: relative; + overflow: hidden; + width: 120px; + transition: var(--el-transition-duration-fast); + margin-bottom: 20px; + + &:hover { + border-color: var(--el-color-primary); + } + + .el-icon.uploader-icon { + font-size: 28px; + color: #8c939d; + width: 100%; + height: 120px; + text-align: center; + } + } + } + + .img-list-box { + display: flex; + + .img-item { + width: 120px; + position: relative; + margin-right: 10px; + + .el-image { + width: 120px; + height: 120px; + border-radius: 5px; + } + + .el-button { + position: absolute; + right: 5px; + top: 5px; + width: 20px; + height: 20px; + } + } + } + } + + .el-row.text-info { + width: 100%; + padding: 10px 0; + + .el-tag { + margin-right: 10px; + } + } + + // 提交按钮 + .submit-btn { + display: flex; + margin: 20px 0; + + .el-button { + width: 200px; + } + } + + // 任务列表 + } + + .no-more-data { + text-align: center; + padding: 30px; + } + } + } +} diff --git a/web/src/assets/css/image-mj.scss b/web/src/assets/css/image-mj.scss new file mode 100644 index 00000000..4050aad7 --- /dev/null +++ b/web/src/assets/css/image-mj.scss @@ -0,0 +1,489 @@ +@use 'running-job-list.scss' as *; + +.page-mj { + // background-color: #282c34; + height: 100%; + + .inner { + display: flex; + // height: 100%; + + .mj-box { + margin: 10px; + // background-color: #262626; + // border: 1px solid #454545; + // height: calc(100vh - 50px); + // overflow: scroll; + min-width: 300px; + max-width: 300px; + padding: 10px; + border-radius: 10px; + color: var(--text-theme-color); + font-size: 14px; + overflow: auto; + + h2 { + font-weight: bold; + font-size: 20px; + text-align: center; + color: var(--theme-textcolor-normal); + } + + // 隐藏滚动条 + ::-webkit-scrollbar { + width: 0; + height: 0; + background-color: transparent; + } + + .mj-params { + margin-top: 10px; + overflow: auto; + + .param-line { + padding: 0 10px; + + .el-icon { + position: relative; + } + + .grid-content { + // background-color: #383838; + background: var(--card-bg); + border-radius: 8px; + padding: 8px 14px; + display: flex; + cursor: pointer; + margin-bottom: 10px; + // border: 1px solid #383838; + border: 1px solid var(--chat-bg); + + &:hover { + border: 1px solid var(--theme-border-hover); + } + + .icon { + width: 20px; + height: 20px; + margin-bottom: 5px; + } + + .text { + margin-left: 5px; + margin-top: 2px; + } + } + + .grid-content.active { + // color: #47fff1; + // background-color: #585858; + border: 1px solid var(--theme-border-hover); + } + + .model { + background: var(--card-bg); + // border: 1px solid #454545; + border-radius: 8px; + padding: 5px; + margin-bottom: 10px; + display: flex; + flex-flow: column; + align-items: center; + cursor: pointer; + border: 1px solid var(--chat-bg); + + &:hover { + border: 1px solid var(--theme-border-hover); + } + + .el-image { + height: 40px; + width: 100%; + } + + .text { + margin-top: 4px; + font-size: 12px; + } + } + + .model.active { + // color: #47fff1; + // background-color: #585858; + border: 1px solid var(--theme-border-hover); + } + + .form-item-inner { + display: flex; + align-items: center; + + .el-select { + --el-select-input-focus-border-color: var(--el-color-primary); + --el-input-focus-border-color: var(--el-color-primary); + } + + .el-input__wrapper { + background: var(--chat-bg); + } + + .el-input__inner { + color: var(--text-theme-color); + } + + .el-icon { + margin-left: 10px; + } + } + + .img-uploader { + .el-upload { + border: 1px dashed var(--el-border-color); + border-radius: 6px; + cursor: pointer; + position: relative; + overflow: hidden; + width: 100%; + transition: var(--el-transition-duration-fast); + + &:hover { + border-color: var(--el-color-primary); + } + + .el-icon.uploader-icon { + font-size: 28px; + color: #8c939d; + width: 100%; + height: 120px; + text-align: center; + } + } + } + } + + .param-line.pt { + display: flex; + align-items: center; + padding-top: 5px; + padding-bottom: 5px; + } + } + } + + .el-form { + .el-form-item__label { + color: var(--text-theme-color); + } + + .el-input, + .el-slider { + width: 180px; + } + + .uploader-icon { + font-size: 24px; + position: relative; + top: 3px; + } + } + + .task-list-box { + background: var(--chat-bg); + width: 100%; + // padding: 0 10px 10px 10px; + color: var(--text-theme-color); + overflow-x: hidden; + + .task-list-inner { + .el-tabs { + --el-tabs-header-height: 55px; + } + + .el-tabs__item { + color: var(--text-theme-color); + font-size: 18px; + } + + .title-tabs .el-tabs__item.is-active { + color: var(--theme-textcolor-normal); + font-size: 18px; + } + + .title-tabs .el-tabs__active-bar { + background-color: var(--theme-textcolor-normal); + } + + .el-textarea { + --el-input-focus-border-color: var(--el-color-primary); + } + + .el-textarea__inner { + background: transparent; + color: var(--text-theme-color); + } + + .el-input__wrapper { + background: transparent; + padding: 5px; + } + + .text { + margin-bottom: 10px; + color: #6b778c; + font-size: 15px; + } + + .param-line.pt { + padding-top: 5px; + padding-bottom: 5px; + } + + .form-item-inner { + display: flex; + align-items: center; + + .el-icon { + margin-left: 10px; + } + } + + .el-form-item__label { + color: var(--text-theme-color); + } + + // 图片上传样式 + .img-inline { + display: flex; + + .img-uploader { + .el-upload { + border: 1px dashed var(--el-border-color); + border-radius: 6px; + cursor: pointer; + position: relative; + overflow: hidden; + width: 120px; + transition: var(--el-transition-duration-fast); + margin-bottom: 20px; + + &:hover { + border-color: var(--el-color-primary); + } + + .el-icon.uploader-icon { + font-size: 28px; + color: #8c939d; + width: 100%; + height: 120px; + text-align: center; + } + } + } + + .img-list-box { + display: flex; + + .img-item { + width: 120px; + position: relative; + margin-right: 10px; + + .el-image { + width: 120px; + height: 120px; + border-radius: 5px; + } + + .el-button { + position: absolute; + right: 5px; + top: 5px; + width: 20px; + height: 20px; + } + } + } + } + + .el-row.text-info { + width: 100%; + padding: 10px 0; + + .el-tag { + margin-right: 10px; + } + } + + // 提交按钮 + .submit-btn { + display: flex; + margin: 20px 0; + + .el-button { + width: 200px; + } + } + + .job-list-box { + // 任务列表 + + .finish-job-list { + #waterfall { + display: flex; + justify-content: center; + padding-top: 20px; + flex-flow: column; + + .waterfall-item { + overflow: visible; + + .job-item { + width: 100%; + height: 100%; + border: 1px solid #666666; + padding: 6px; + border-radius: 6px; + // position: relative; + + .el-image { + overflow: auto; + } + + .opt { + padding-top: 5px; + + .opt-line { + margin: 6px 0; + + ul { + display: flex; + flex-flow: row; + + li { + margin-right: 6px; + + a { + padding: 3px 0; + width: 40px; + text-align: center; + border-radius: 5px; + display: block; + cursor: pointer; + background-color: #4e5058; + color: #fff; + + &:hover { + background-color: #6d6f78; + } + } + } + + .show-prompt { + font-size: 20px; + cursor: pointer; + } + } + } + } + + .remove { + display: none; + position: absolute; + right: 10px; + top: 10px; + } + + &:hover { + .remove { + display: block; + } + } + } + } + } + } + + .el-image { + width: 100%; + height: 100%; + overflow: visible; + + .el-image-viewer__wrapper { + img { + width: auto; + height: auto; + } + } + + .image-slot { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + min-height: 220px; + color: var(--text-theme-color); + overflow: hidden; + + .err-msg-container { + overflow: hidden; + word-break: break-all; + padding: 15px; + .title { + font-size: 20px; + text-align: center; + font-weight: bold; + color: #f56c6c; + margin-bottom: 30px; + } + + .opt { + display: flex; + justify-content: center; + } + } + .iconfont { + font-size: 50px; + margin-bottom: 10px; + } + } + } + + .el-image.upscale { + img { + // height: 310px; + } + + .image-slot { + min-height: 310px; + } + + .el-image-viewer__wrapper { + img { + width: auto; + height: auto; + } + } + } + } + } + + .no-more-data { + text-align: center; + padding: 30px; + } + } + + .generate-btn { + .iconfont { + margin-right: 5px; + } + } + } +} + +.mj-list-item-prompt { + .el-icon { + margin-left: 10px; + cursor: pointer; + position: relative; + } +} diff --git a/web/src/assets/css/image-mj.styl b/web/src/assets/css/image-mj.styl deleted file mode 100644 index 3c66bc33..00000000 --- a/web/src/assets/css/image-mj.styl +++ /dev/null @@ -1,503 +0,0 @@ -.page-mj { - // background-color: #282c34; - height 100% - - .inner { - display: flex; - // height: 100% - - .mj-box { - margin 10px - // background-color #262626 - // border 1px solid #454545 - // height: calc(100vh - 50px) - // overflow: scroll - min-width 300px - max-width 300px - padding 10px - border-radius 10px - color var(--text-theme-color); - font-size 14px - overflow auto - - h2 { - font-weight: bold; - font-size 20px - text-align center - color var( --theme-textcolor-normal) - } - - // 隐藏滚动条 - - ::-webkit-scrollbar { - width: 0; - height: 0; - background-color: transparent; - } - - .mj-params { - margin-top 10px - overflow auto - - - .param-line { - padding 0 10px - - .el-icon { - position relative - } - - .grid-content { - // background-color #383838 - background: var(--card-bg); - border-radius: 8px; - padding 8px 14px - display flex - cursor pointer - margin-bottom: 10px; - // border 1px solid #383838 - border 1px solid var(--chat-bg) - - - &:hover { - border 1px solid var(--theme-border-hover) - - } - - .icon { - width 20px - height 20px - margin-bottom 5px - } - - .text { - margin-left 5px - margin-top 2px - } - } - - - .grid-content.active { - // color #47fff1 - // background-color #585858 - border 1px solid var(--theme-border-hover) - } - - .model { - background: var(--card-bg); - // border 1px solid #454545 - border-radius 8px - padding 5px - margin-bottom 10px - display flex - flex-flow column - align-items center - cursor pointer - border 1px solid var(--chat-bg) - - &:hover { - border 1px solid var(--theme-border-hover) - - } - - .el-image { - height 40px - width 100% - } - - .text { - margin-top 4px - font-size 12px - } - - } - - .model.active { - // color #47fff1 - // background-color #585858 - border 1px solid var(--theme-border-hover) - - } - - .form-item-inner { - display flex - align-items: center - - .el-select { - --el-select-input-focus-border-color: var(--el-color-primary) - --el-input-focus-border-color: var(--el-color-primary) - } - - .el-input__wrapper { - background: var(--chat-bg) - } - - .el-input__inner { - color: var(--text-theme-color) - } - - .el-icon { - margin-left 10px - } - } - - .img-uploader { - .el-upload { - border: 1px dashed var(--el-border-color); - border-radius: 6px; - cursor: pointer; - position: relative; - overflow: hidden; - width 100% - transition: var(--el-transition-duration-fast); - - &:hover { - border-color: var(--el-color-primary); - } - - .el-icon.uploader-icon { - font-size: 28px - color: #8c939d - width 100% - height: 120px - text-align: center - } - } - } - - } - - .param-line.pt { - display: flex - align-items: center - padding-top 5px - padding-bottom 5px - } - } - } - - .el-form { - .el-form-item__label { - color var(--text-theme-color) - } - - .el-input, .el-slider { - width 180px - } - - .uploader-icon { - font-size 24px - position relative - top 3px - } - } - - .task-list-box { - background: var(--chat-bg); - width 100% - //padding 0 10px 10px 10px - color var(--text-theme-color) - overflow-x hidden - - .task-list-inner { - .el-tabs { - --el-tabs-header-height: 55px; - } - - .el-tabs__item { - color: var(--text-theme-color); - font-size: 18px; - } - - .title-tabs .el-tabs__item.is-active { - color: var( --theme-textcolor-normal); - font-size: 18px; - } - - .title-tabs .el-tabs__active-bar { - background-color: var( --theme-textcolor-normal); - } - - .el-textarea { - --el-input-focus-border-color: var(--el-color-primary) - } - - .el-textarea__inner { - background: transparent; - color: var(--text-theme-color); - } - - .el-input__wrapper { - background: transparent; - padding 5px - } - - .text { - margin-bottom: 10px; - color: #6b778c; - font-size: 15px - } - - .param-line.pt { - padding-top 5px - padding-bottom 5px - } - - .form-item-inner { - display flex - align-items: center - - .el-icon { - margin-left 10px - } - } - - .el-form-item__label { - color var(--text-theme-color) - } - - // 图片上传样式 - - .img-inline { - display flex - - .img-uploader { - .el-upload { - border: 1px dashed var(--el-border-color); - border-radius: 6px; - cursor: pointer; - position: relative; - overflow: hidden; - width 120px; - transition: var(--el-transition-duration-fast); - margin-bottom: 20px; - - &:hover { - border-color: var(--el-color-primary); - } - - .el-icon.uploader-icon { - font-size: 28px - color: #8c939d - width 100% - height: 120px - text-align: center - } - } - } - - .img-list-box { - display flex - - .img-item { - width 120px - position relative - margin-right 10px - - .el-image { - width 120px - height 120px - border-radius 5px - } - - .el-button { - position absolute - right 5px - top 5px - width 20px - height 20px - } - } - } - } - - .el-row.text-info { - width 100% - padding 10px 0 - - .el-tag { - margin-right 10px - } - } - - // 提交按钮 - - .submit-btn { - display flex - margin: 20px 0 - - .el-button { - width 200px - } - } - - - .job-list-box { - // 任务列表 - @import "running-job-list.styl" - - .finish-job-list { - #waterfall { - display flex - justify-content center - padding-top 20px - flex-flow column - - .waterfall-item { - overflow visible - - .job-item { - width 100% - height 100% - border 1px solid #666666 - padding 6px - border-radius 6px - //position relative - - .el-image { - overflow auto - } - - .opt { - padding-top 5px - - .opt-line { - margin 6px 0 - - ul { - display flex - flex-flow row - - li { - margin-right 6px - - a { - padding 3px 0 - width 40px - text-align center - border-radius 5px - display block - cursor pointer - background-color #4E5058 - color #fff - - &:hover { - background-color #6D6F78 - } - } - } - - .show-prompt { - font-size 20px - cursor pointer - } - } - } - } - - - .remove { - display none - position absolute - right 10px - top 10px - } - - &:hover { - .remove { - display block - } - } - } - - } - - } - } - - - .el-image { - width 100% - height 100% - overflow visible - - .el-image-viewer__wrapper { - img { - width auto - height auto - } - } - - .image-slot { - display flex - flex-flow column - justify-content center - align-items center - min-height 220px - color var(--text-theme-color) - overflow hidden - - .err-msg-container { - overflow hidden - word-break break-all - padding 15px - .title { - font-size 20px - text-align center - font-weight bold - color #f56c6c - margin-bottom 30px - } - - .opt { - display flex - justify-content center - } - } - .iconfont { - font-size 50px - margin-bottom 10px - } - } - } - - .el-image.upscale { - img { - //height 310px - } - - .image-slot { - min-height 310px - } - - .el-image-viewer__wrapper { - img { - width auto - height auto - } - } - } - } - } - - .no-more-data { - text-align center - padding 30px - } - } - - .generate-btn { - .iconfont { - margin-right 5px - } - } - } -} - -.mj-list-item-prompt { - .el-icon { - margin-left 10px - cursor pointer - position relative - } -} \ No newline at end of file diff --git a/web/src/assets/css/image-sd.styl b/web/src/assets/css/image-sd.scss similarity index 52% rename from web/src/assets/css/image-sd.styl rename to web/src/assets/css/image-sd.scss index fb2fded5..e101b3f0 100644 --- a/web/src/assets/css/image-sd.styl +++ b/web/src/assets/css/image-sd.scss @@ -1,246 +1,235 @@ -.page-sd { - // background-color: #282c34; - - .inner { - display: flex; - - .sd-box { - margin 10px - // background-color #262626 - // border 1px solid #454545 - min-width 300px - max-width 300px - padding 10px 10px 20px 10px - border-radius 10px - color var(--text-theme-color); - font-size 14px - overflow auto - - h2 { - font-weight: bold; - font-size 20px - text-align center - color var( --theme-textcolor-normal) - - } - - // 隐藏滚动条 - - ::-webkit-scrollbar { - width: 0; - height: 0; - background-color: transparent; - } - - .sd-params { - margin-top 10px - overflow auto - - - .param-line { - padding 0 10px - - .grid-content - .form-item-inner { - display flex - - .info-icon { - margin-left 10px - position relative - top 8px - } - } - - } - - .param-line.pt { - padding-top 5px - padding-bottom 5px - } - - .text-info { - padding 10px - } - } - - .submit-btn { - padding 10px 15px 0 15px - text-align center - - .el-button { - width 200px - - - } - } - } - - .el-form { - .el-form-item__label { - color: var(--text-theme-color) - } - } - - .task-list-box { - background: var(--chat-bg); - width 100% - color: var(--text-theme-color) - overflow-x hidden - - .task-list-inner { - .el-tabs { - --el-tabs-header-height: 55px; - } - - .el-tabs__item { - color: #fff; - font-size: 18px; - } - - .title-tabs .el-tabs__item.is-active { - color: #47FFF1; - font-size: 18px; - } - - .title-tabs .el-tabs__active-bar { - background-color: #47FFF1; - } - - .el-textarea { - // --el-input-focus-border-color: #47FFF1; - } - - .el-textarea__inner { - background: transparent; - color: #fff; - } - - .el-input__wrapper { - background: transparent; - padding 5px - } - - .text { - margin-bottom: 10px; - color: #6b778c; - font-size: 15px - } - - .param-line.pt { - padding-top 5px - padding-bottom 5px - } - - .form-item-inner { - display flex - align-items: center - - .el-icon { - margin-left 10px - } - } - - .el-form-item__label { - color: var(--text-theme-color) - } - - // 图片上传样式 - - .img-inline { - display flex - - .img-uploader { - .el-upload { - border: 1px dashed var(--el-border-color); - border-radius: 6px; - cursor: pointer; - position: relative; - overflow: hidden; - width 120px; - transition: var(--el-transition-duration-fast); - margin-bottom: 20px; - - &:hover { - border-color: var(--el-color-primary); - } - - .el-icon.uploader-icon { - font-size: 28px - color: #8c939d - width 100% - height: 120px - text-align: center - } - } - } - - .img-list-box { - display flex - - .img-item { - width 120px - position relative - margin-right 10px - - .el-image { - width 120px - height 120px - border-radius 5px - } - - .el-button { - position absolute - right 5px - top 5px - width 20px - height 20px - } - } - } - } - - .el-row.text-info { - width 100% - padding 10px 0 - - .el-tag { - margin-right 10px - } - } - - // 提交按钮 - - .submit-btn { - display flex - margin: 20px 0 - - .el-button { - width 200px - } - } - - // 任务列表 - @import "waterfall-list.styl" - } - - .no-more-data { - text-align center - padding 30px - } - } - } - - @import "sd-task-dialog.styl" - - - .mj-list-item-prompt { - .el-icon { - margin-left 10px - cursor pointer - position relative - top 2px - } - } - -} - +@use 'waterfall-list.scss' as *; +@use 'sd-task-dialog.scss' as *; + +.page-sd { + // background-color: #282c34; + + .inner { + display: flex; + + .sd-box { + margin: 10px; + // background-color: #262626; + // border: 1px solid #454545; + min-width: 300px; + max-width: 300px; + padding: 10px 10px 20px 10px; + border-radius: 10px; + color: var(--text-theme-color); + font-size: 14px; + overflow: auto; + + h2 { + font-weight: bold; + font-size: 20px; + text-align: center; + color: var(--theme-textcolor-normal); + } + + // 隐藏滚动条 + ::-webkit-scrollbar { + width: 0; + height: 0; + background-color: transparent; + } + + .sd-params { + margin-top: 10px; + overflow: auto; + + .param-line { + padding: 0 10px; + + .grid-content, + .form-item-inner { + display: flex; + + .info-icon { + margin-left: 10px; + position: relative; + top: 8px; + } + } + } + + .param-line.pt { + padding-top: 5px; + padding-bottom: 5px; + } + + .text-info { + padding: 10px; + } + } + + .submit-btn { + padding: 10px 15px 0 15px; + text-align: center; + + .el-button { + width: 200px; + } + } + } + + .el-form { + .el-form-item__label { + color: var(--text-theme-color); + } + } + + .task-list-box { + background: var(--chat-bg); + width: 100%; + color: var(--text-theme-color); + overflow-x: hidden; + + .task-list-inner { + .el-tabs { + --el-tabs-header-height: 55px; + } + + .el-tabs__item { + color: #fff; + font-size: 18px; + } + + .title-tabs .el-tabs__item.is-active { + color: #47fff1; + font-size: 18px; + } + + .title-tabs .el-tabs__active-bar { + background-color: #47fff1; + } + + .el-textarea { + // --el-input-focus-border-color: #47FFF1; + } + + .el-textarea__inner { + background: transparent; + color: #fff; + } + + .el-input__wrapper { + background: transparent; + padding: 5px; + } + + .text { + margin-bottom: 10px; + color: #6b778c; + font-size: 15px; + } + + .param-line.pt { + padding-top: 5px; + padding-bottom: 5px; + } + + .form-item-inner { + display: flex; + align-items: center; + + .el-icon { + margin-left: 10px; + } + } + + .el-form-item__label { + color: var(--text-theme-color); + } + + // 图片上传样式 + .img-inline { + display: flex; + + .img-uploader { + .el-upload { + border: 1px dashed var(--el-border-color); + border-radius: 6px; + cursor: pointer; + position: relative; + overflow: hidden; + width: 120px; + transition: var(--el-transition-duration-fast); + margin-bottom: 20px; + + &:hover { + border-color: var(--el-color-primary); + } + + .el-icon.uploader-icon { + font-size: 28px; + color: #8c939d; + width: 100%; + height: 120px; + text-align: center; + } + } + } + + .img-list-box { + display: flex; + + .img-item { + width: 120px; + position: relative; + margin-right: 10px; + + .el-image { + width: 120px; + height: 120px; + border-radius: 5px; + } + + .el-button { + position: absolute; + right: 5px; + top: 5px; + width: 20px; + height: 20px; + } + } + } + } + + .el-row.text-info { + width: 100%; + padding: 10px 0; + + .el-tag { + margin-right: 10px; + } + } + + // 提交按钮 + .submit-btn { + display: flex; + margin: 20px 0; + + .el-button { + width: 200px; + } + } + + // 任务列表 + } + + .no-more-data { + text-align: center; + padding: 30px; + } + } + } + + .mj-list-item-prompt { + .el-icon { + margin-left: 10px; + cursor: pointer; + position: relative; + top: 2px; + } + } +} diff --git a/web/src/assets/css/images-wall.scss b/web/src/assets/css/images-wall.scss new file mode 100644 index 00000000..47bb9cba --- /dev/null +++ b/web/src/assets/css/images-wall.scss @@ -0,0 +1,129 @@ +@use 'sd-task-dialog.scss' as *; + +@keyframes expandUp { + 0% { + transform: scaleY(0); + } + 100% { + transform: scaleY(1); + } +} + +.page-images-wall { + display: flex; + // background-color: #282c34; + + .inner { + width: 100%; + color: var(--text-theme-color); + overflow: hidden; + + .header { + display: flex; + padding: 0 40px; + + h2 { + width: 300px; + } + + .settings { + width: 100%; + display: flex; + justify-content: right; + + .el-radio-group { + font-size: 16px; + + .el-radio { + color: var(--text-theme-color); + } + } + } + } + + .waterfall { + position: relative; + margin: 0 auto; + overflow-y: auto; + overflow-x: hidden; + + .waterfall-over-message { + display: none; + } + + .list-item { + display: flex; + + .image { + overflow: hidden; + + .el-image { + width: 100%; + transition: transform 0.3s; + cursor: pointer; + } + } + + .opt { + display: none; + position: absolute; + width: 100%; + bottom: 0; + left: 0; + color: var(--text-theme-color); + padding: 8px 10px; + line-height: 1.2; + border-top-right-radius: 10px; + background-color: rgba(10, 10, 10, 0.7); + + span { + word-break: break-all; + } + .iconfont { + } + .el-icon, + .iconfont { + top: 2px; + cursor: pointer; + color: #fff !important; + border: 1px solid #fff !important; + border-radius: 5px; + padding: 2px; + font-size: 16px; + margin-right: 10px; + + &:hover { + background-color: #444444; + } + } + } + + &:hover { + .opt { + display: block; + animation: expandUp 0.3s ease-in-out forwards; + transform-origin: bottom center; + transform: scaleY(0); /* 初始状态,元素高度为0 */ + } + + .image { + .el-image { + transform: scale(1.2); /* 放大图像到1.2倍大小 */ + } + } + } + } + } + + .footer { + display: flex; + padding: 20px; + align-items: center; + justify-content: center; + + .iconfont { + margin-left: 6px; + } + } + } +} diff --git a/web/src/assets/css/images-wall.styl b/web/src/assets/css/images-wall.styl deleted file mode 100644 index 27fd595b..00000000 --- a/web/src/assets/css/images-wall.styl +++ /dev/null @@ -1,134 +0,0 @@ -@keyframes expandUp { - 0% { - transform: scaleY(0); - } - 100% { - transform: scaleY(1); - } -} - -.page-images-wall { - display: flex; - // background-color: #282c34; - - .inner { - width 100% - color var(--text-theme-color); - overflow hidden - - .header { - display flex - padding 0 40px - - h2 { - width 300px - } - - .settings { - width 100% - display flex - justify-content right - - .el-radio-group { - font-size 16px - - .el-radio { - color var(--text-theme-color); - } - - } - } - } - - .waterfall { - position: relative; - margin: 0 auto; - overflow-y auto - overflow-x hidden - - .waterfall-over-message { - display none - } - - .list-item { - - display flex - - .image { - overflow hidden - - .el-image { - width 100% - transition: transform 0.3s; - cursor pointer - } - } - - .opt { - display none - position absolute - width 100% - bottom 0 - left 0 - color var(--text-theme-color); - padding 8px 10px - line-height 1.2 - border-top-right-radius 10px - background-color rgba(10, 10, 10, 0.7) - - span { - word-break break-all - } - .iconfont{ - - - } - .el-icon, .iconfont { - top 2px - cursor pointer - color: #fff !important; - border 1px solid #fff !important; - border-radius 5px - padding 2px - font-size 16px; - margin-right 10px - - &:hover { - background-color #444444 - } - } - } - - &:hover { - .opt { - display block - animation: expandUp 0.3s ease-in-out forwards; - transform-origin: bottom center; - transform: scaleY(0); /* 初始状态,元素高度为0 */ - } - - .image { - .el-image { - transform: scale(1.2); /* 放大图像到1.2倍大小 */ - } - } - } - } - - } - - - .footer { - display flex - padding 20px - align-items center - justify-content center - - .iconfont { - margin-left 6px - } - } - } - - @import "sd-task-dialog.styl" -} \ No newline at end of file diff --git a/web/src/assets/css/index.scss b/web/src/assets/css/index.scss new file mode 100644 index 00000000..5c538042 --- /dev/null +++ b/web/src/assets/css/index.scss @@ -0,0 +1,207 @@ +.index-page { + margin: 0; + overflow: hidden; + color: var(--text-color); + display: flex; + align-items: center; + background: var(--theme-bg) !important; + flex-flow: column; + height: 100vh; + + .color-bg { + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + } + + .image-bg { + filter: blur(8px); + background-size: cover; + background-position: center; + } + + .shadow { + box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 3px; + + &:hover { + box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 8px; + } + } + + .menu-box { + width: 100%; + display: flex; + height: 80px; + align-items: center; + + .el-menu { + padding: 0 30px; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + background: none; + border: none; + + .menu-item { + display: flex; + // padding: 20px 0; + height: 40px; + align-items: center; + + color: var(--text-color); + .iconfont { + color: var(--text-color); + font-size: 28px; + } + .title { + color: var(--text-color); + font-size: 24px; + padding: 10px 10px 0 10px; + font-weight: 700; + } + + .logo { + height: 60px; + border-radius: 50%; + background: #fff; + border: 2px solid #754ff6; + } + + .el-button { + margin-left: 10px; + + span { + margin-left: 5px; + } + } + } + } + } + + .content { + text-align: center; + position: relative; + display: flex; + flex-flow: column; + align-items: center; + + h1 { + font-size: 5rem; + margin-bottom: 1rem; + } + + p { + font-size: 1.5rem; + margin-bottom: 2rem; + } + + .navs { + display: flex; + max-width: 900px; + padding: 20px; + + .el-space--horizontal { + justify-content: center; + } + + .nav-item { + width: 200px; + .el-button { + width: 100%; + padding: 25px 20px; + font-size: 1.3rem; + transition: all 0.3s ease; + + .iconfont { + font-size: 24px; + margin-right: 10px; + position: relative; + top: -2px; + } + } + } + .nav-item-box { + width: 100%; + border-radius: 8px; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); + aspect-ratio: 1.1028 / 1; + background: var(--card-bg); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 160px; + // min-height: 190px; + i { + display: inline-block; + min-width: 48px; + width: 48px; + height: 48px; + font-size: 38px; + border-radius: 24px; + color: var(--normal-color); + } + + &:hover { + box-shadow: 0 4px 14px 0 rgba(17, 13, 83, 0.18); + transform: translateY(-8px); + } + } + } + } + + .footer { + .el-link__inner { + color: #ffffff; + } + } +} + +.cursor-ani { + position: relative; +} + +.cursor-ani::after { + content: ''; + position: absolute; + width: 1px; + height: 28px; + background: #333; + transform: translateX(3px) translateY(3px); + animation: cursor-blinks 0.8s infinite forwards; +} + +@keyframes cursor-blinks { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} +.cursor-ani { + display: inline-block; + min-height: 34px; + font-size: 1.5em; +} + +.msg-text span { + transition: color 0.3s ease; /* 平滑的颜色过渡 */ + font-weight: bold; +} +.logo-box { + width: 60px; + height: 60px; + background: #fff; + border-radius: 50%; + img { + width: 100%; + object-fit: cover; + height: 100%; + } +} \ No newline at end of file diff --git a/web/src/assets/css/index.styl b/web/src/assets/css/index.styl deleted file mode 100644 index 6f951feb..00000000 --- a/web/src/assets/css/index.styl +++ /dev/null @@ -1,211 +0,0 @@ -.index-page { - margin: 0 - overflow hidden - color var(--text-color) - display flex - align-items center - background: var(--theme-bg) !important - flex-flow column - height: 100vh - - - - .color-bg { - position absolute - top 0 - left 0 - width 100vw - height 100vh - } - - .image-bg { - filter: blur(8px); - background-size: cover; - background-position: center; - } - - .shadow { - box-shadow rgba(0, 0, 0, 0.3) 0px 0px 3px - - &:hover { - box-shadow rgba(0, 0, 0, 0.3) 0px 0px 8px - } - } - - .menu-box { - width 100% - display flex - height 80px - align-items center - - .el-menu { - padding 0 30px - width 100% - display flex - justify-content space-between - align-items center - background none - border none - - .menu-item { - display flex - // padding 20px 0 - height 40px - align-items center - - color var(--text-color); - .iconfont{ - color var(--text-color); - font-size: 28px; - - } - .title { - color var(--text-color); - font-size: 24px; - padding 10px 10px 0 10px - font-weight: 700; - } - - .logo { - height 60px - border-radius 50% - background: #fff - border: 2px solid #754ff6 - } - - .el-button { - margin-left 10px - - span { - margin-left 5px - } - } - } - } - } - - .content { - text-align: center; - position relative - display flex - flex-flow: column; - align-items: center; - - h1 { - font-size: 5rem; - margin-bottom: 1rem; - } - - p { - font-size: 1.5rem; - margin-bottom: 2rem; - } - - .navs { - display flex - max-width 900px - padding 20px - - .el-space--horizontal { - justify-content center - } - - .nav-item { - width 200px - .el-button { - width 100% - padding: 25px 20px; - font-size: 1.3rem; - transition: all 0.3s ease; - - .iconfont { - font-size 24px - margin-right 10px - position relative - top -2px - } - } - } - .nav-item-box{ - width: 100%; - border-radius: 8px; - cursor: pointer; - transition: all 0.2s cubic-bezier(0.645,0.045,0.355,1); - aspect-ratio: 1.1028 / 1; - background: var( --card-bg) - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-width: 160px - // min-height: 190px; - i{ - display: inline-block - min-width: 48px; - width: 48px; - height: 48px; - font-size: 38px - border-radius: 24px; - color: var(--normal-color) - } - - &:hover{ - box-shadow: 0 4px 14px 0 rgba(17, 13, 83, .18); - transform: translateY(-8px);} - - } - } - } - - .footer { - .el-link__inner { - color #ffffff - } - } - -} - -.cursor-ani { - position: relative; -} - -.cursor-ani::after { - content: ''; - position: absolute; - width: 1px; - height: 28px; - background: #333; - transform: translateX(3px) translateY(3px); - animation: cursor-blinks 0.8s infinite forwards; -} - -@keyframes cursor-blinks { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -.cursor-ani { - display: inline-block; - min-height: 34px; - font-size: 1.5em; -} - -.msg-text span { - transition: color 0.3s ease; /* 平滑的颜色过渡 */ - font-weight: bold; -} -.logo-box{ - width: 60px - height: 60px - background: #fff - border-radius: 50% - img{ - width: 100%; - object-fit: cover; - height: 100%; - } -} \ No newline at end of file diff --git a/web/src/assets/css/jimeng.styl b/web/src/assets/css/jimeng.scss similarity index 97% rename from web/src/assets/css/jimeng.styl rename to web/src/assets/css/jimeng.scss index 45a4823b..b5439284 100644 --- a/web/src/assets/css/jimeng.styl +++ b/web/src/assets/css/jimeng.scss @@ -11,7 +11,7 @@ padding: 20px; border-radius: 12px; background: var(--card-bg); - box-shadow: var(--card-shadow, 0 8px 24px rgba(0,0,0,0.12)); + box-shadow: var(--card-shadow, 0 8px 24px rgba(0, 0, 0, 0.12)); color: var(--text-theme-color); font-size: 14px; overflow: auto; @@ -81,7 +81,7 @@ color: var(--primary-text-on-primary-dark, #fff); } transform: translateY(-2px); - box-shadow: var(--primary-shadow, 0 4px 12px rgba(88,101,242,0.3)); + box-shadow: var(--primary-shadow, 0 4px 12px rgba(88, 101, 242, 0.3)); } .category-icon { @@ -233,11 +233,11 @@ flex-direction: column; background: var(--card-bg); border-radius: 12px; - box-shadow: var(--card-shadow, 0 2px 8px rgba(0,0,0,0.1)); + box-shadow: var(--card-shadow, 0 2px 8px rgba(0, 0, 0, 0.1)); overflow: hidden; transition: box-shadow 0.2s; &:hover { - box-shadow: 0 4px 24px rgba(88,101,242,0.12); + box-shadow: 0 4px 24px rgba(88, 101, 242, 0.12); } .task-left { width: 100%; @@ -346,4 +346,4 @@ max-height: 220px; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/web/src/assets/css/login.scss b/web/src/assets/css/login.scss new file mode 100644 index 00000000..ccfcf458 --- /dev/null +++ b/web/src/assets/css/login.scss @@ -0,0 +1,57 @@ +.loginPage { + background: var(--card-bg) !important; + background-color: var(---card-bg) !important; + + .form-title { + color: var(--text-theme-color); + } +} + +.left { + width: 50%; + + .login-box { + width: 410px; + margin: 0 auto; + min-height: calc(100vh - 48px); + } + + .wechatLog { + width: 410px; + height: 50px; + line-height: 50px; + text-align: center; + background: var(--sign-bg); + a { + color: var(--text-theme-color); + } + font-size: 14px; + margin-bottom: 26px; + border-radius: 16px; + .icon-wechat { + color: #0bc15f; + margin-right: 9px; + font-size: 20px; + } + } + + .text-color-primary { + cursor: pointer; + } +} +.login-btn { + width: 100%; + height: 40px; + border-radius: 16px; +} +.code-input { + width: 306px; + margin-right: 9px; +} +:deep(.el-tabs__item.is-active), :deep(.el-tabs__item:hover) { + color: var(--common-text-color) !important; +} + +:deep(.el-tabs__item) { + color: var(--text-theme-color); +} \ No newline at end of file diff --git a/web/src/assets/css/login.styl b/web/src/assets/css/login.styl deleted file mode 100644 index ded9ebef..00000000 --- a/web/src/assets/css/login.styl +++ /dev/null @@ -1,62 +0,0 @@ -.loginPage{ - background: var(--card-bg) !important - background-color: var(---card-bg) !important - -.form-title{ - color:var( --text-theme-color) -} -} - -.left{ - width: 50%; - - .login-box{ - width: 410px; - margin: 0 auto; - min-height: calc(100vh - 48px); - - } - - .wechatLog{ - width: 410px; - height: 50px; - line-height: 50px; - text-align: center - background: var( --sign-bg) - a{ - color: var(--text-theme-color) - - } - font-size: 14px; - margin-bottom: 26px - border-radius: 16px; - .icon-wechat{ - color: #0bc15f - margin-right: 9px - font-size: 20px; - } - } - - .text-color-primary{ - cursor :pointer - } - - - -} -.login-btn { - width :100% - height: 40px; - border-radius: 16px; -} - .code-input{ - width: 306px; - margin-right: 9px; - } -:deep(.el-tabs__item.is-active), :deep(.el-tabs__item:hover) { - color: var(--common-text-color) !important -} - -:deep(.el-tabs__item) { - color: var(--text-theme-color) -} \ No newline at end of file diff --git a/web/src/assets/css/main.styl b/web/src/assets/css/main.scss similarity index 82% rename from web/src/assets/css/main.styl rename to web/src/assets/css/main.scss index 1434c405..f7ec3ba6 100644 --- a/web/src/assets/css/main.styl +++ b/web/src/assets/css/main.scss @@ -1,206 +1,203 @@ -//* { -// margin: 0; -// padding: 0; -//} - -html, -body, -#app, -.wrapper { - width: 100%; - height: 100%; -} - -body { - font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; - -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; -} - -.admin-home { - overflow hidden - position relative - height 100vh - - a { - text-decoration: none - } - - .content-box { - position: absolute; - left: 250px; - right: 0; - top: 0; - bottom: 0; - padding-bottom: 30px; - -webkit-transition: left .3s ease-in-out; - transition: left .3s ease-in-out; - background: #f0f0f0; - - ::-webkit-scrollbar { - width: 8px; /* 滚动条宽度 */ - } - - ::-webkit-scrollbar-thumb { - background-color: #666666; - border-radius 8px - } - - ::-webkit-scrollbar-thumb:hover { - background-color: #999999; - } - - .content { - width: auto; - overflow-y: scroll; - box-sizing: border-box; - - .container { - padding: 15px 20px 30px 20px; - background: #ffffff; - margin-bottom 20px - max-width 100% - } - - .crumbs { - margin: 10px 0; - } - - .el-table th { - background-color: #f5f7fa; - } - - .pagination { - margin: 20px 0; - display: flex; - justify-content: center; - width: 100%; - } - - .plugins-tips { - padding: 20px 10px; - margin-bottom: 20px; - } - - .el-button + .el-tooltip { - margin-left: 10px; - } - - .el-table tr:hover { - background: #f6faff; - } - - .mgb20 { - margin-bottom: 20px; - } - - .move-enter-active, - .move-leave-active { - transition: opacity .1s ease; - } - - .move-enter-from, - .move-leave-to { - opacity: 0; - } - - /*BaseForm*/ - - .form-box { - width: 600px; - } - - .form-box .line { - text-align: center; - } - - .el-time-panel__content::after, - .el-time-panel__content::before { - margin-top: -7px; - } - - .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) { - padding-bottom: 0; - } - - - [class*=" el-icon-"], [class^=el-icon-] { - speak: none; - font-style: normal; - font-weight: 400; - font-variant: normal; - text-transform: none; - line-height: 1; - vertical-align: baseline; - display: inline-block; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - } - - .el-sub-menu [class^=el-icon-] { - vertical-align: middle; - margin-right: 5px; - width: 24px; - text-align: center; - font-size: 18px; - } - - [hidden] { - display: none !important; - } - } - - .dark { - background: var(--el-bg-color); - - .container { - background: var(--el-bg-color); - } - - .crumbs { - margin: 10px 0; - } - - .el-table th { - background-color: var(--el-fill-color-darker); - } - - } - } - - .content-collapse { - left: 65px; - } - - .el-table { - width: 100%; - - .el-table__body-header { - height 40px - } - } -} - -.w-100 { - width 100% -} - - -.d-flex { - display flex !important -} - -.justify-center { - justify-content center -} -.justify-between { - justify-content space-between -} - -.justify-end { - justify-content flex-end -} - -.align-center { - align-items center -} +//* { +// margin: 0; +// padding: 0; +//} + +html, +body, +#app, +.wrapper { + width: 100%; + height: 100%; +} + +body { + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +.admin-home { + overflow: hidden; + position: relative; + height: 100vh; + + a { + text-decoration: none; + } + + .content-box { + position: absolute; + left: 250px; + right: 0; + top: 0; + bottom: 0; + padding-bottom: 30px; + -webkit-transition: left 0.3s ease-in-out; + transition: left 0.3s ease-in-out; + background: #f0f0f0; + + ::-webkit-scrollbar { + width: 8px; /* 滚动条宽度 */ + } + + ::-webkit-scrollbar-thumb { + background-color: #666666; + border-radius: 8px; + } + + ::-webkit-scrollbar-thumb:hover { + background-color: #999999; + } + + .content { + width: auto; + overflow-y: scroll; + box-sizing: border-box; + + .container { + padding: 15px 20px 30px 20px; + background: #ffffff; + margin-bottom: 20px; + max-width: 100%; + } + + .crumbs { + margin: 10px 0; + } + + .el-table th { + background-color: #f5f7fa; + } + + .pagination { + margin: 20px 0; + display: flex; + justify-content: center; + width: 100%; + } + + .plugins-tips { + padding: 20px 10px; + margin-bottom: 20px; + } + + .el-button + .el-tooltip { + margin-left: 10px; + } + + .el-table tr:hover { + background: #f6faff; + } + + .mgb20 { + margin-bottom: 20px; + } + + .move-enter-active, + .move-leave-active { + transition: opacity 0.1s ease; + } + + .move-enter-from, + .move-leave-to { + opacity: 0; + } + + /*BaseForm*/ + + .form-box { + width: 600px; + } + + .form-box .line { + text-align: center; + } + + .el-time-panel__content::after, + .el-time-panel__content::before { + margin-top: -7px; + } + + .el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) { + padding-bottom: 0; + } + + [class*=" el-icon-"], [class^=el-icon-] { + speak: none; + font-style: normal; + font-weight: 400; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: baseline; + display: inline-block; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + .el-sub-menu [class^=el-icon-] { + vertical-align: middle; + margin-right: 5px; + width: 24px; + text-align: center; + font-size: 18px; + } + + [hidden] { + display: none !important; + } + } + + .dark { + background: var(--el-bg-color); + + .container { + background: var(--el-bg-color); + } + + .crumbs { + margin: 10px 0; + } + + .el-table th { + background-color: var(--el-fill-color-darker); + } + } + } + + .content-collapse { + left: 65px; + } + + .el-table { + width: 100%; + + .el-table__body-header { + height: 40px; + } + } +} + +.w-100 { + width: 100%; +} + +.d-flex { + display: flex !important; +} + +.justify-center { + justify-content: center; +} +.justify-between { + justify-content: space-between; +} + +.justify-end { + justify-content: flex-end; +} + +.align-center { + align-items: center; +} \ No newline at end of file diff --git a/web/src/assets/css/mark-map.scss b/web/src/assets/css/mark-map.scss new file mode 100644 index 00000000..deac3b99 --- /dev/null +++ b/web/src/assets/css/mark-map.scss @@ -0,0 +1,149 @@ +.page-mark-map { + // background-color: #282c34; + height: 100%; + + .inner { + display: flex; + + .mark-map-box { + margin: 10px; + // background-color: #262626; + // border: 1px solid #454545; + min-width: 300px; + max-width: 300px; + padding: 10px; + border-radius: 10px; + color: var(--text-theme-color); + font-size: 14px; + + h2 { + font-weight: bold; + font-size: 20px; + text-align: center; + color: var(--theme-textcolor-normal); + } + + // 隐藏滚动条 + ::-webkit-scrollbar { + width: 0; + height: 0; + background-color: transparent; + } + + .mark-map-params { + margin-top: 10px; + overflow: auto; + + .param-line { + padding: 10px; + + .el-button { + width: 100%; + + span { + color: #fff; + } + } + } + + .text-info { + padding: 10px; + + .el-tag { + margin-right: 10px; + } + } + } + } + + .el-form { + .el-form-item__label { + color: var(--text-theme-color); + } + } + + .chat-box { + width: 100%; + background: var(--chat-bg); + + .top-bar { + display: flex; + justify-content: right; + align-items: center; + padding: 10px 20px 10px 10px; + } + + .markdown { + color: var(--text-theme-color); + display: flex; + justify-content: center; + align-items: center; + + h1 { + color: var(--theme-textcolor-normal); + } + + h2 { + color: #ffcc00; + } + + ul { + list-style-type: disc; + margin-left: 20px; + + li { + line-height: 1.5; + } + } + + strong { + font-weight: bold; + } + + em { + font-style: italic; + } + } + + .body { + display: flex; + justify-content: center; + align-items: center; + position: relative; + + .markmap { + width: 100%; + color: var(--text-theme-color); + font-size: 12px; + + .markmap-foreign { + // height: 30px; + } + } + + #toolbar { + position: absolute; + bottom: 10px; + right: 20px; + display: flex; + + .mm-toolbar { + line-height: 36px; + display: flex; + flex-flow: row; + margin-left: 10px; + + .mm-toolbar-brand { + display: none; + } + + .mm-toolbar-item { + cursor: pointer; + color: var(--text-fb); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/web/src/assets/css/mark-map.styl b/web/src/assets/css/mark-map.styl deleted file mode 100644 index 7f19a0ad..00000000 --- a/web/src/assets/css/mark-map.styl +++ /dev/null @@ -1,156 +0,0 @@ -.page-mark-map { - // background-color: #282c34; - height 100% - - .inner { - display: flex; - - .mark-map-box { - margin 10px - // background-color #262626 - // border 1px solid #454545 - min-width 300px - max-width 300px - padding 10px - border-radius 10px - color var(--text-theme-color); - font-size 14px - - h2 { - font-weight: bold; - font-size 20px - text-align center - color var( --theme-textcolor-normal) - } - - // 隐藏滚动条 - - ::-webkit-scrollbar { - width: 0; - height: 0; - background-color: transparent; - } - - .mark-map-params { - margin-top 10px - overflow auto - - - .param-line { - padding 10px - - .el-button { - width 100% - - span { - color #fff - } - } - - } - - .text-info { - padding 10px - - .el-tag { - margin-right 10px - } - } - } - } - - .el-form { - .el-form-item__label { - color var(--text-theme-color) - } - } - - - .chat-box { - width 100% - background: var(--chat-bg); - - - .top-bar { - display flex - justify-content right - align-items center - padding 10px 20px 10px 10px - } - - .markdown { - color var(--text-theme-color) - display flex - justify-content center - align-items center - - h1 { - color: var( --theme-textcolor-normal); - } - - h2 { - color: #ffcc00; - } - - ul { - list-style-type: disc; - margin-left: 20px; - - li { - line-height 1.5 - } - } - - strong { - font-weight: bold; - } - - em { - font-style: italic; - } - } - - .body { - display flex - justify-content center - align-items center - position relative - - .markmap { - width 100% - color var(--text-theme-color) - font-size 12px - - .markmap-foreign { - //height 30px - } - } - - #toolbar { - position: absolute - bottom: 10px - right: 20px - display: flex; - - .mm-toolbar { - line-height: 36px; - display flex - flex-flow row - margin-left: 10px; - - .mm-toolbar-brand { - display none - } - - .mm-toolbar-item { - cursor pointer - color var( --text-fb) - } - } - - } - } - } - } -} - diff --git a/web/src/assets/css/member.scss b/web/src/assets/css/member.scss new file mode 100644 index 00000000..318befec --- /dev/null +++ b/web/src/assets/css/member.scss @@ -0,0 +1,546 @@ +.member { + height: 100%; + + .title { + text-align: center; + background-color: #25272d; + font-size: 24px; + color: var(--text-theme-color); + padding: 10px; + border-bottom: 1px solid #3c3c3c; + } + + .inner { + color: var(--text-theme-color); + padding: 15px 0 15px 15px; + overflow-x: hidden; + overflow-y: visible; + display: flex; + flex-flow: row; + + .profile-card { + max-width: 300px; + border-radius: 18px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08); + padding: 24px 16px; + background: var(--panel-bg); + position: relative; + z-index: 1; + margin-bottom: 24px; + } + .profile-title { + font-size: 18px; + font-weight: bold; + margin-bottom: 18px; + color: #2d8cf0; + letter-spacing: 2px; + text-align: center; + } + .profile-btn { + width: 100%; + margin-bottom: 12px; + font-size: 16px; + font-weight: 500; + display: flex; + align-items: center; + justify-content: center; + border: none; + border-radius: 8px; + background: linear-gradient(90deg, #6dd5ed 0%, #2193b0 100%); + color: #fff; + transition: all 0.3s; + i { + margin-right: 8px; + font-size: 20px; + } + &:hover { + box-shadow: 0 2px 12px #2193b0aa; + transform: translateY(-2px) scale(1.03); + background: linear-gradient(90deg, #2193b0 0%, #6dd5ed 100%); + } + } + .profile-btn.email { + background: linear-gradient(90deg, #f7971e 0%, #ffd200 100%); + } + .profile-btn.mobile { + background: linear-gradient(90deg, #43cea2 0%, #185a9d 100%); + } + .profile-btn.third { + background: linear-gradient(90deg, #ff512f 0%, #dd2476 100%); + } + .profile-btn.password { + background: linear-gradient(90deg, #1d4350 0%, #a43931 100%); + } + .profile-btn.redeem { + background: linear-gradient(90deg, #00c6ff 0%, #0072ff 100%); + } + .profile-bg { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 0; + background: url('data:image/svg+xml;utf8,') + no-repeat center/cover; + opacity: 0.08; + pointer-events: none; + } + + .product-box { + padding: 0 20px; + width: 100%; + + .info { + .el-alert__description { + font-size: 14px !important; + margin: 0; + } + padding: 0 0 20px 0; + } + + .list-box { + .product-col { + animation: fadeInUp 0.6s ease-out; + animation-fill-mode: both; + + &:nth-child(1) { + animation-delay: 0.1s; + } + &:nth-child(2) { + animation-delay: 0.2s; + } + &:nth-child(3) { + animation-delay: 0.3s; + } + &:nth-child(4) { + animation-delay: 0.4s; + } + &:nth-child(5) { + animation-delay: 0.5s; + } + &:nth-child(6) { + animation-delay: 0.6s; + } + } + + .product-item { + background: linear-gradient(135deg, var(--panel-bg) 0%, var(--chat-bg) 100%); + border-radius: 16px; + overflow: hidden; + cursor: pointer; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + margin-bottom: 24px; + border: 1px solid var(--theme-border-primary); + position: relative; + + .product-header { + position: relative; + + .image-container { + position: relative; + display: flex; + justify-content: center; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + + .el-image { + width: 80px; + height: 80px; + border-radius: 50%; + overflow: hidden; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + transition: transform 0.3s ease; + + &:hover { + transform: scale(1.1); + } + } + + .image-overlay { + position: absolute; + top: 10px; + right: 10px; + + .vip-badge { + background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); + color: #333; + padding: 4px 12px; + border-radius: 20px; + font-size: 12px; + font-weight: bold; + box-shadow: 0 4px 12px rgba(255, 215, 0, 0.4); + animation: pulse 2s infinite; + } + } + } + + .product-title { + padding: 20px 20px 0; + text-align: center; + + .name { + font-size: 20px; + font-weight: 700; + color: var(--text-theme-color); + margin: 0 0 8px 0; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + .description { + font-size: 14px; + color: var(--text-secondary-color, #666); + margin: 0; + line-height: 1.4; + } + } + } + + .product-content { + padding: 20px; + + .price-section { + text-align: center; + margin-bottom: 20px; + + .price-info { + display: flex; + align-items: baseline; + justify-content: center; + margin-bottom: 8px; + + .currency { + font-size: 18px; + color: #f56c6c; + font-weight: 600; + margin-right: 2px; + } + + .price-value { + font-size: 32px; + font-weight: 800; + color: #f56c6c; + line-height: 1; + } + + .price-unit { + font-size: 14px; + color: var(--text-secondary-color, #666); + margin-left: 4px; + } + } + + .original-price { + font-size: 12px; + color: #999; + text-decoration: line-through; + } + } + + .features-list { + .feature-item { + display: flex; + align-items: center; + margin-bottom: 8px; + font-size: 14px; + color: var(--text-secondary-color, #666); + + i { + color: #67c23a; + margin-right: 8px; + font-size: 16px; + } + } + } + } + + .product-actions { + padding: 0 20px 20px; + + .payment-buttons { + display: flex; + gap: 12px; + + .payment-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 16px; + border: none; + border-radius: 12px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; + transform: translateZ(0); + + &::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.2), + transparent + ); + transition: left 0.5s; + } + + &:hover::before { + left: 100%; + } + + &:active { + transform: translateY(1px) scale(0.98); + } + + i { + font-size: 18px; + transition: transform 0.3s ease; + } + + span { + font-weight: 600; + transition: transform 0.3s ease; + } + + &:hover { + i, + span { + transform: scale(1.05); + } + } + + &.wechat-btn { + background: linear-gradient(135deg, #07c160 0%, #06ad56 100%); + color: white; + box-shadow: 0 4px 16px rgba(7, 193, 96, 0.3); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(7, 193, 96, 0.4); + background: linear-gradient(135deg, #06ad56 0%, #07c160 100%); + } + } + + &.alipay-btn { + background: linear-gradient(135deg, #1677ff 0%, #0e5fd8 100%); + color: white; + box-shadow: 0 4px 16px rgba(22, 119, 255, 0.3); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px rgba(22, 119, 255, 0.4); + background: linear-gradient(135deg, #0e5fd8 0%, #1677ff 100%); + } + } + } + } + } + + &:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); + border-color: rgba(102, 126, 234, 0.3); + } + } + } + + .headline { + padding: 0 20px 20px 0; + } + + .user-order { + padding: 0 20px 20px 0; + } + } + } +} + +.pay-dialog { + .product-info { + text-align: center; + color: #333333; + font-size: 16px; + + .price { + color: #f56c6c; + font-weight: 700; + } + } +} + +// 添加动画效果 +@keyframes pulse { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.05); + opacity: 0.8; + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +// 响应式优化 +@media (max-width: 768px) { + .member { + .inner { + .product-box { + .list-box { + .el-col { + width: 100% !important; + margin-bottom: 16px; + } + + .product-item { + .product-header { + .image-container { + padding: 16px; + + .el-image { + width: 60px; + height: 60px; + } + } + + .product-title { + padding: 16px 16px 0; + + .name { + font-size: 18px; + } + } + } + + .product-content { + padding: 16px; + + .price-section { + .price-info { + .price-value { + font-size: 28px; + } + } + } + } + + .product-actions { + padding: 0 16px 16px; + + .payment-buttons { + flex-direction: column; + gap: 8px; + + .payment-btn { + padding: 10px 14px; + } + } + } + } + } + } + } + } +} + +@media (max-width: 480px) { + .member { + .inner { + padding: 10px 0 10px 10px; + + .product-box { + padding: 0 10px; + + .list-box { + .product-item { + margin-bottom: 16px; + + .product-header { + .image-container { + padding: 12px; + + .el-image { + width: 50px; + height: 50px; + } + } + + .product-title { + padding: 12px 12px 0; + + .name { + font-size: 16px; + } + + .description { + font-size: 12px; + } + } + } + + .product-content { + padding: 12px; + + .price-section { + .price-info { + .price-value { + font-size: 24px; + } + + .currency { + font-size: 16px; + } + } + } + + .features-list { + .feature-item { + font-size: 12px; + } + } + } + + .product-actions { + padding: 0 12px 12px; + + .payment-buttons { + .payment-btn { + padding: 8px 12px; + font-size: 12px; + + i { + font-size: 16px; + } + } + } + } + } + } + } + } + } +} diff --git a/web/src/assets/css/member.styl b/web/src/assets/css/member.styl deleted file mode 100644 index ec584a86..00000000 --- a/web/src/assets/css/member.styl +++ /dev/null @@ -1,239 +0,0 @@ -.member { - height 100% - - .title { - text-align center - background-color #25272d - font-size 24px - color var(--text-theme-color) - padding 10px - border-bottom 1px solid #3c3c3c - } - - .inner { - color var(--text-theme-color) - padding 15px 0 15px 15px - overflow-x hidden - overflow-y visible - display flex - flex-flow row - - .profile-card { - max-width 300px - border-radius 18px - box-shadow 0 4px 8px rgba(0,0,0,0.08) - padding 24px 16px - background var(--panel-bg) - position relative - z-index 1 - margin-bottom 24px - } - .profile-title { - font-size 18px - font-weight bold - margin-bottom 18px - color #2d8cf0 - letter-spacing 2px - text-align center - } - .profile-btn { - width 100% - margin-bottom 12px - font-size 16px - font-weight 500 - display flex - align-items center - justify-content center - border none - border-radius 8px - background linear-gradient(90deg, #6dd5ed 0%, #2193b0 100%) - color #fff - transition all 0.3s - i { - margin-right 8px - font-size 20px - } - &:hover { - box-shadow 0 2px 12px #2193b0aa - transform translateY(-2px) scale(1.03) - background linear-gradient(90deg, #2193b0 0%, #6dd5ed 100%) - } - } - .profile-btn.email { - background linear-gradient(90deg, #f7971e 0%, #ffd200 100%) - } - .profile-btn.mobile { - background linear-gradient(90deg, #43cea2 0%, #185a9d 100%) - } - .profile-btn.third { - background linear-gradient(90deg, #ff512f 0%, #dd2476 100%) - } - .profile-btn.password { - background linear-gradient(90deg, #1d4350 0%, #a43931 100%) - } - .profile-btn.redeem { - background linear-gradient(90deg, #00c6ff 0%, #0072ff 100%) - } - .profile-bg { - position absolute - left 0 - top 0 - width 100% - height 100% - z-index 0 - background url('data:image/svg+xml;utf8,') no-repeat center/cover - opacity 0.08 - pointer-events none - } - - .product-box { - padding 0 20px - width 100% - - .info { - .el-alert__description { - font-size 14px !important - margin 0 - } - padding 0 0 20px 0 - } - - .list-box { - .product-item { - // border 1px solid #666666 - background-color var(--chat-bg) - border-radius 6px - overflow hidden - cursor pointer - transition: all 0.3s ease; /* 添加过渡效果 */ - margin-bottom 20px - - .image-container { - display flex - justify-content center - - .el-image { - padding 6px - - .el-image__inner { - border-radius 10px - } - } - } - - .product-title { - display flex - padding 10px - - .name { - width 100% - text-align center - font-size 16px - font-weight bold - color var( --el-color-primary) - } - } - - .product-info { - padding 10px 20px - font-size 14px - color #999999 - - .info-line { - display flex - width 100% - padding 5px 0 - - .label { - display flex - width 100% - } - - .price, .expire, calls { - display flex - width 90px - justify-content right - } - - .discount { - color #f56c6c - font-size 20px - } - - .expire { - color #409eff - } - - .power { - color #F2CB51 - } - } - - - .pay-way { - padding 10px 0 - display flex - justify-content: center - flex-wrap wrap - - .el-button { - margin 10px 5px 0 5px - height 32px - filter: none; - - .icon-alipay,.icon-wechat-pay { - color #ffffff - } - .icon-qq { - color #15A6E8 - font-size 24px - } - .icon-jd-pay { - color var(--text-theme-color) - font-size 24px - } - .icon-douyin { - color #0a0a0a - font-size 22px - } - .icon-paypal { - font-size 14px - color #009CDE - } - } - } - } - - &:hover { - // box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */ - transform: translateY(-10px); /* 向上移动10像素 */ - box-shadow: 0 0 10px var(--shadow-color); - background-color: var(--hover-deep-color) - } - } - } - - .headline { - padding 0 20px 20px 0 - } - - .user-order { - padding 0 20px 20px 0 - } - } - } - -} - -.pay-dialog { - .product-info { - text-align center - color #333333 - font-size 16px - - .price { - color #f56c6c - font-weight 700 - } - } -} \ No newline at end of file diff --git a/web/src/assets/css/mobile/chat-list.scss b/web/src/assets/css/mobile/chat-list.scss new file mode 100644 index 00000000..b9a208c3 --- /dev/null +++ b/web/src/assets/css/mobile/chat-list.scss @@ -0,0 +1,33 @@ +@use 'model-select.scss' as *; + +.mobile-chat-list { + .content { + .van-list { + .van-cell__value { + .chat-list-item { + display: flex; + font-size: 14px; + + .van-image { + min-width: 32px; + width: 32px; + height: 32px; + } + + .van-ellipsis { + margin-top: 5px; + margin-left: 10px; + } + } + } + } + } + + .van-nav-bar { + .van-nav-bar__right { + .van-icon { + font-size: 20px; + } + } + } +} diff --git a/web/src/assets/css/mobile/chat-list.styl b/web/src/assets/css/mobile/chat-list.styl deleted file mode 100644 index c1cbd386..00000000 --- a/web/src/assets/css/mobile/chat-list.styl +++ /dev/null @@ -1,37 +0,0 @@ -.mobile-chat-list { - .content { - padding-top 46px - padding-bottom 60px - - .van-list { - .van-cell__value { - .chat-list-item { - display flex - font-size 14px - - .van-image { - min-width 32px - width 32px - height 32px - } - - .van-ellipsis { - margin-top 5px; - margin-left 10px; - } - } - } - } - } - - - .van-nav-bar { - .van-nav-bar__right { - .van-icon { - font-size 20px; - } - } - } -} - -@import "model-select.styl" \ No newline at end of file diff --git a/web/src/assets/css/mobile/chat-session.scss b/web/src/assets/css/mobile/chat-session.scss new file mode 100644 index 00000000..41773c0d --- /dev/null +++ b/web/src/assets/css/mobile/chat-session.scss @@ -0,0 +1,159 @@ +@use 'model-select.scss' as *; + +.mobile-chat { + .van-nav-bar { + position: static; + + .setting { + font-size: 18px; + } + } + + .chat-list-wrapper { + padding: 10px 0 10px 0; + background: var(--van-background); + overflow: hidden; + + .message-list-box { + overflow: auto; + + .van-cell { + background: none; + font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; + } + } + } + + .chat-box-wrapper { + .van-sticky { + .van-cell-group--inset { + margin: 0; + + .van-cell { + padding: 10px; + + .icon-box { + .van-icon { + font-size: 24px; + margin-left: 10px; + } + } + + .button-voice { + padding: 0 2px; + + .el-icon { + font-size: 24px; + } + height: 30px; + } + } + } + + // .file-preview-list { + // display: flex; + // flex-wrap: wrap; + // padding: 6px 10px 0 10px; + // gap: 8px; + + // .file-preview-item { + // position: relative; + // display: inline-flex; + // align-items: center; + // border: 1px solid #e8e8e8; + // background: var(--van-cell-background); + // border-radius: 8px; + // padding: 6px 26px 6px 6px; + // overflow: hidden; + + // .thumb { + // width: 56px; + // height: 56px; + // border-radius: 6px; + // overflow: hidden; + + // .img { + // width: 56px; + // height: 56px; + // } + + // .size { + // position: absolute; + // left: 6px; + // bottom: 6px; + // background: rgba(0, 0, 0, 0.5); + // color: #fff; + // font-size: 10px; + // padding: 1px 4px; + // border-radius: 3px; + // } + // } + + // .doc { + // display: inline-flex; + // align-items: center; + // gap: 6px; + // max-width: 220px; + // .icon { + // width: 24px; + // height: 24px; + // } + // .name { + // white-space: nowrap; + // text-overflow: ellipsis; + // overflow: hidden; + // max-width: 180px; + // } + // .size { + // color: #8c8c8c; + // font-size: 12px; + // margin-left: 6px; + // } + // } + + // .remove { + // position: absolute; + // right: 6px; + // top: 6px; + // font-size: 14px; + // color: #999; + // } + // } + // } + } + } + + .van-nav-bar__title { + .van-dropdown-menu__title { + margin-right: 10px; + } + + .van-cell__title { + text-align: left; + } + } + + .van-nav-bar__right { + .van-icon { + font-size: 20px; + } + } +} + +.van-overlay { + .mic-wrapper { + display: flex; + height: 100vh; + justify-content: center; + align-items: center; + flex-flow: column; + } +} + +// .van-theme-dark { +// .mobile-chat { +// .chat-list-wrapper { +// background: #232425; +// } +// } +// } diff --git a/web/src/assets/css/mobile/chat-session.styl b/web/src/assets/css/mobile/chat-session.styl deleted file mode 100644 index c4d8dbd3..00000000 --- a/web/src/assets/css/mobile/chat-session.styl +++ /dev/null @@ -1,90 +0,0 @@ -.mobile-chat { - .van-nav-bar { - position static - - .setting { - font-size 18px - } - } - - .chat-list-wrapper { - padding 10px 0 10px 0 - background var(--van-background); - overflow hidden - - - .message-list-box { - overflow auto - - .van-cell { - background none - font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; - } - } - - } - - .chat-box-wrapper { - .van-sticky { - .van-cell-group--inset { - margin 0 - - .van-cell { - padding 10px - - .icon-box { - .van-icon { - font-size 24px - margin-left 10px - } - } - - .button-voice { - padding 0 2px - - .el-icon { - font-size 24px - } - height 30px - } - } - } - } - } - - .van-nav-bar__title { - .van-dropdown-menu__title { - margin-right 10px - } - - .van-cell__title { - text-align left - } - } - - .van-nav-bar__right { - .van-icon { - font-size 20px - } - } -} - -.van-overlay { - .mic-wrapper { - display flex - height 100vh - justify-content center - align-items center - flex-flow column - } -} - -// .van-theme-dark { -// .mobile-chat { -// .chat-list-wrapper { -// background #232425; -// } -// } -// } - -@import "model-select.styl" \ No newline at end of file diff --git a/web/src/assets/css/mobile/image-mj.scss b/web/src/assets/css/mobile/image-mj.scss new file mode 100644 index 00000000..243319a7 --- /dev/null +++ b/web/src/assets/css/mobile/image-mj.scss @@ -0,0 +1,217 @@ +.mobile-mj { + .text-line { + padding: 6px 0; + font-size: 14px; + + .van-row { + .van-col { + .rate { + display: flex; + justify-content: center; + background-color: var(--van-background-3); + padding: 5px 10px; + margin: 5px 0; + border-radius: 5px; + flex-flow: column; + + .icon { + text-align: center; + + .van-image { + max-width: 20px; + } + } + + .text { + text-align: center; + color: var(--van-text-color); + } + } + + .model { + display: flex; + justify-content: center; + background-color: var(--van-background-3); + padding: 6px; + margin: 5px 0; + border-radius: 5px; + flex-flow: column; + + .icon { + text-align: center; + + .van-image { + width: 100%; + height: 50px; + } + } + + .text { + text-align: center; + color: var(--van-text-color); + } + } + + .active { + background-color: var(--van-text-color-3); + } + } + } + + .van-button { + position: relative; + + .van-tag { + position: absolute; + right: 20px; + } + } + + .align-right { + display: flex; + justify-content: right; + } + } + + color: var(--van-text-color); + .pt-6 { + padding: 15px 10px; + } + + .tip-text { + padding: 10px; + line-height: 1.5; + color: #c1c1c1; + } + + .running-job-list { + .van-grid { + .van-grid-item { + .van-grid-item__content { + padding: 0; + position: relative; + + .van-image, + .task-in-queue { + min-height: 100px; + } + + .progress { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background: rgba(50, 50, 50, 0.5); + position: absolute; + left: 0; + top: 0; + + .van-circle__text { + color: #ffffff; + } + } + + // end progress + + .task-in-queue { + display: flex; + flex-flow: column; + justify-content: center; + color: #c1c1c1; + + .icon { + text-align: center; + + .iconfont { + font-size: 24px; + } + } + + .text { + font-size: 14px; + margin-top: 5px; + } + } + } + } + } + } + + // end running jobs + + .finish-job-list { + .van-grid { + .van-grid-item { + .van-grid-item__content { + padding: 0; + + .job-item { + overflow: hidden; + border-radius: 6px; + position: relative; + height: 100%; + width: 100%; + + .opt { + .opt-btn { + padding: 2px 0; + text-align: center; + border-radius: 5px; + margin: 3px 0; + display: block; + cursor: pointer; + background-color: #4e5058; + color: #ffffff; + font-size: 14px; + width: 100%; + } + } + + .van-image { + width: 100%; + height: 200px; + } + + .upscale { + height: 260px; + width: 100%; + } + + .remove { + position: absolute; + right: 5px; + top: 5px; + + .el-button { + margin-left: 5px; + height: auto; + padding: 5px; + } + } + } + + .failed { + display: flex; + flex-flow: column; + justify-content: center; + + .title { + margin-bottom: 20px; + text-align: center; + color: #ee0a24; + font-size: 18px; + } + .opt { + display: flex; + justify-content: center; + .van-button { + margin: 0 5px; + } + } + } + } + } + } + } +} diff --git a/web/src/assets/css/mobile/image-mj.styl b/web/src/assets/css/mobile/image-mj.styl deleted file mode 100644 index e482b931..00000000 --- a/web/src/assets/css/mobile/image-mj.styl +++ /dev/null @@ -1,217 +0,0 @@ -.mobile-mj { - .text-line { - padding 6px - font-size 14px - - .van-row { - .van-col { - .rate { - display: flex; - justify-content center - background-color var(--van-background-3) - padding 5px 10px - margin 5px 0 - border-radius 5px - flex-flow column - - .icon { - text-align center - - .van-image { - max-width 20px - } - } - - .text { - text-align center - color var(--van-text-color) - } - } - - .model { - display: flex; - justify-content center - background-color var(--van-background-3) - padding 6px - margin 5px 0 - border-radius 5px - flex-flow column - - .icon { - text-align center - - .van-image { - width 100% - height 50px - } - } - - .text { - text-align center - color var(--van-text-color) - } - } - - .active { - background-color var(--van-text-color-3) - } - } - } - - .van-button { - position relative - - .van-tag { - position absolute - right 20px - } - } - - .align-right { - display flex - justify-content right - } - } - - - color var(--van-text-color) - .pt-6 { - padding 15px 10px - } - - .tip-text { - padding 10px - line-height 1.5 - color #c1c1c1 - } - - .running-job-list { - .van-grid { - .van-grid-item { - .van-grid-item__content { - padding 0 - position relative - - .van-image, .task-in-queue { - min-height 100px - } - - .progress { - display flex - justify-content center - align-items center - width 100% - height 100% - background rgba(50, 50, 50, 0.5) - position absolute - left 0 - top 0 - - .van-circle__text { - color #ffffff - } - } - - // end progress - - .task-in-queue { - display flex - flex-flow column - justify-content center - color #c1c1c1 - - .icon { - text-align center - - .iconfont { - font-size 24px - } - } - - .text { - font-size 14px - margin-top 5px - } - } - } - } - } - } - - //end running jobs - - .finish-job-list { - .van-grid { - .van-grid-item { - .van-grid-item__content { - padding 0 - - .job-item { - overflow hidden - border-radius 6px - position relative - height 100% - width 100% - - .opt { - .opt-btn { - padding 2px 0 - text-align center - border-radius 5px - margin 3px 0 - display block - cursor pointer - background-color #4E5058 - color #ffffff - font-size 14px - width 100% - } - } - - .van-image { - width 100% - height 200px - } - - .upscale { - height 260px - width 100% - } - - .remove { - position absolute - right 5px - top 5px - - .el-button { - margin-left 5px - height auto - padding 5px - } - } - } - - .failed { - display flex - flex-flow column - justify-content center - - .title { - margin-bottom 20px - text-align center - color #ee0a24 - font-size 18px - } - .opt { - display flex - justify-content center - .van-button { - margin 0 5px - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/web/src/assets/css/mobile/image-sd.scss b/web/src/assets/css/mobile/image-sd.scss new file mode 100644 index 00000000..ce94c502 --- /dev/null +++ b/web/src/assets/css/mobile/image-sd.scss @@ -0,0 +1,205 @@ +.mobile-sd { + .text-line { + padding: 0; + font-size: 14px; + + .van-row { + width: 100%; + + .van-col { + .rate { + display: flex; + justify-content: center; + background-color: #f5f5f5; + padding: 5px 10px; + margin: 5px 0; + border-radius: 5px; + flex-flow: column; + + .icon { + text-align: center; + + .van-image { + max-width: 20px; + } + } + + .text { + text-align: center; + color: #555555; + } + } + + .el-input__inner { + text-align: center; + } + + .model { + display: flex; + justify-content: center; + background-color: #f5f5f5; + padding: 6px; + margin: 5px 0; + border-radius: 5px; + flex-flow: column; + + .icon { + text-align: center; + + .van-image { + width: 100%; + height: 50px; + } + } + + .text { + text-align: center; + color: #555555; + } + } + + .active { + background-color: #e5e5e5; + } + } + } + + .van-button { + position: relative; + + .van-tag { + position: absolute; + right: 20px; + } + } + + .align-right { + display: flex; + justify-content: right; + } + } + + color: var(--van-text-color); + + .pt-6 { + padding: 15px 10px; + } + + .tip-text { + padding: 10px; + line-height: 1.5; + color: #c1c1c1; + } + + .running-job-list { + .van-grid { + .van-grid-item { + .van-grid-item__content { + padding: 0; + position: relative; + + .van-image, + .task-in-queue { + min-height: 100px; + } + + .progress { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background: rgba(50, 50, 50, 0.5); + position: absolute; + left: 0; + top: 0; + + .van-circle__text { + color: #ffffff; + } + } + + // end progress + + .task-in-queue { + display: flex; + flex-flow: column; + justify-content: center; + color: #c1c1c1; + + .icon { + text-align: center; + + .iconfont { + font-size: 24px; + } + } + + .text { + font-size: 14px; + margin-top: 5px; + } + } + } + } + } + } + + // end running jobs + + .finish-job-list { + .van-grid { + .van-grid-item { + .van-grid-item__content { + padding: 0; + + .job-item { + overflow: hidden; + border-radius: 6px; + position: relative; + height: 100%; + width: 100%; + + .van-image { + width: 100%; + height: 200px; + } + + .remove { + position: absolute; + right: 5px; + top: 5px; + + .el-button { + margin-left: 5px; + height: auto; + padding: 5px; + } + } + } + + .failed { + display: flex; + flex-flow: column; + justify-content: center; + height: 200px; + + .title { + margin-bottom: 20px; + text-align: center; + color: #ee0a24; + font-size: 18px; + } + .opt { + display: flex; + justify-content: center; + .van-button { + margin: 0 5px; + } + } + } + } + } + } + } +} diff --git a/web/src/assets/css/mobile/image-sd.styl b/web/src/assets/css/mobile/image-sd.styl deleted file mode 100644 index 31c036f5..00000000 --- a/web/src/assets/css/mobile/image-sd.styl +++ /dev/null @@ -1,207 +0,0 @@ -.mobile-sd { - .text-line { - padding 0 6px - font-size 14px - - .van-row { - width 100% - - .van-col { - .rate { - display: flex; - justify-content center - background-color #f5f5f5 - padding 5px 10px - margin 5px 0 - border-radius 5px - flex-flow column - - .icon { - text-align center - - .van-image { - max-width 20px - } - } - - .text { - text-align center - color #555555 - } - } - - .el-input__inner { - text-align center - } - - .model { - display: flex; - justify-content center - background-color #f5f5f5 - padding 6px - margin 5px 0 - border-radius 5px - flex-flow column - - .icon { - text-align center - - .van-image { - width 100% - height 50px - } - } - - .text { - text-align center - color #555555 - } - } - - .active { - background-color #e5e5e5 - } - } - } - - .van-button { - position relative - - .van-tag { - position absolute - right 20px - } - } - - .align-right { - display flex - justify-content right - } - } - - - color var(--van-text-color) - - .pt-6 { - padding 15px 10px - } - - .tip-text { - padding 10px - line-height 1.5 - color #c1c1c1 - } - - .running-job-list { - .van-grid { - .van-grid-item { - .van-grid-item__content { - padding 0 - position relative - - .van-image, .task-in-queue { - min-height 100px - } - - .progress { - display flex - justify-content center - align-items center - width 100% - height 100% - background rgba(50, 50, 50, 0.5) - position absolute - left 0 - top 0 - - .van-circle__text { - color #ffffff - } - } - - // end progress - - .task-in-queue { - display flex - flex-flow column - justify-content center - color #c1c1c1 - - .icon { - text-align center - - .iconfont { - font-size 24px - } - } - - .text { - font-size 14px - margin-top 5px - } - } - } - } - } - } - - //end running jobs - - .finish-job-list { - .van-grid { - .van-grid-item { - .van-grid-item__content { - padding 0 - - .job-item { - overflow hidden - border-radius 6px - position relative - height 100% - width 100% - - .van-image { - width 100% - height 200px - } - - .remove { - position absolute - right 5px - top 5px - - .el-button { - margin-left 5px - height auto - padding 5px - } - } - } - - - .failed { - display flex - flex-flow column - justify-content center - height 200px - - .title { - margin-bottom 20px - text-align center - color #ee0a24 - font-size 18px - } - .opt { - display flex - justify-content center - .van-button { - margin 0 5px - } - } - } - - } - } - } - } -} \ No newline at end of file diff --git a/web/src/assets/css/mobile/jimeng.scss b/web/src/assets/css/mobile/jimeng.scss new file mode 100644 index 00000000..382cdff4 --- /dev/null +++ b/web/src/assets/css/mobile/jimeng.scss @@ -0,0 +1,889 @@ +/* JimengCreate Mobile Styles */ + +/* 自定义动画 */ +@keyframes fade-in { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fade-out { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-10px); + } +} + +@keyframes scale-up { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} + +.animate-fade-in { + animation: fade-in 0.3s ease-out; +} + +.animate-fade-out { + animation: fade-out 0.3s ease-out; +} + +.animate-scale-up { + animation: scale-up 0.3s ease-out; +} + +/* 文本截断 */ +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* 深色模式适配 */ +@media (prefers-color-scheme: dark) { + .bg-gray-50 { + background-color: #1f2937; + } + + .bg-white { + background-color: #374151; + } + + .text-gray-900 { + color: #f9fafb; + } + + .text-gray-700 { + color: #d1d5db; + } + + .text-gray-600 { + color: #9ca3af; + } + + .text-gray-500 { + color: #6b7280; + } + + .border-gray-200 { + border-color: #4b5563; + } + + .bg-gray-100:hover { + background-color: #4b5563; + } +} + +/* 即梦创作页面专用样式 */ +.jimeng-create { + min-height: 100vh; + background-color: #f9fafb; + + /* 页面头部样式 */ + &__header { + position: sticky; + top: 0; + z-index: 40; + background-color: white; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + &-content { + display: flex; + align-items: center; + padding: 0 1rem; + height: 3.5rem; + } + + &-back-btn { + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + border-radius: 50%; + transition: background-color 0.2s; + + &:hover { + background-color: #f3f4f6; + } + + .iconfont { + color: #6b7280; + } + } + + &-title { + flex: 1; + text-align: center; + font-size: 1.125rem; + font-weight: 600; + color: #111827; + } + + &-spacer { + width: 2rem; + } + } + + /* 主要内容区域 */ + &__content { + padding: 1rem; + + .space-y-6 > * + * { + margin-top: 1.5rem; + } + } + + /* 功能分类选择 */ + &__category-section { + background-color: white; + border-radius: 0.75rem; + padding: 1rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + &-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 0.75rem; + } + + &-button { + display: flex; + flex-direction: column; + align-items: center; + padding: 0.75rem; + border-radius: 0.5rem; + border: 2px solid; + transition: all 0.2s; + + &--active { + border-color: #3b82f6; + background-color: #eff6ff; + color: #1d4ed8; + } + + &--inactive { + border-color: #e5e7eb; + background-color: #f9fafb; + color: #6b7280; + + &:hover { + border-color: #d1d5db; + background-color: #f3f4f6; + } + } + + .iconfont { + font-size: 1.5rem; + margin-bottom: 0.5rem; + } + + span { + font-size: 0.875rem; + font-weight: 500; + } + } + } + + /* 生成模式切换 */ + &__mode-section { + background-color: white; + border-radius: 0.75rem; + padding: 1rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + &-content { + display: flex; + align-items: center; + justify-content: space-between; + } + + &-title { + color: #111827; + font-weight: 500; + } + + &-description { + font-size: 0.875rem; + color: #6b7280; + margin-top: 0.25rem; + } + } + + /* 表单组件样式 */ + &__form-section { + background-color: white; + border-radius: 0.75rem; + padding: 1rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + &-label { + display: block; + color: #374151; + font-weight: 500; + margin-bottom: 0.75rem; + } + + &-textarea { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid #e5e7eb; + border-radius: 0.5rem; + resize: none; + transition: all 0.2s; + + &:focus { + outline: none; + ring: 2px solid #3b82f6; + border-color: transparent; + } + } + + &-counter { + text-align: right; + margin-top: 0.5rem; + font-size: 0.875rem; + color: #6b7280; + } + } + + /* 图片上传样式 */ + &__upload { + border: 2px dashed #d1d5db; + border-radius: 0.5rem; + padding: 1.5rem; + text-align: center; + cursor: pointer; + transition: all 0.2s; + + &:hover { + border-color: #60a5fa; + background-color: #eff6ff; + } + + &-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + } + + &-icon { + color: #3b82f6; + font-size: 1.5rem; + } + + &-text { + color: #374151; + font-weight: 500; + } + + &-preview { + position: relative; + + .el-image { + width: 8rem; + height: 8rem; + border-radius: 0.5rem; + } + } + + &-remove-btn { + position: absolute; + top: -0.5rem; + right: -0.5rem; + width: 1.5rem; + height: 1.5rem; + background-color: #ef4444; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: #dc2626; + } + } + + /* 多图片上传样式 */ + &-multiple { + display: flex; + gap: 0.75rem; + + &-item { + position: relative; + + .el-image { + width: 6rem; + height: 6rem; + border-radius: 0.5rem; + } + } + + &-add { + width: 6rem; + height: 6rem; + border: 2px dashed #d1d5db; + border-radius: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: border-color 0.2s; + + &:hover { + border-color: #60a5fa; + } + + .iconfont { + color: #9ca3af; + font-size: 1.25rem; + } + } + } + } + + /* 滑块样式 */ + &__slider-section { + .space-y-4 > * + * { + margin-top: 1rem; + } + + &-header { + display: flex; + align-items: center; + gap: 0.5rem; + + label { + display: block; + color: #374151; + font-weight: 500; + } + + .el-tooltip { + .iconfont { + color: #9ca3af; + cursor: pointer; + } + } + } + } + + /* 开关样式 */ + &__switch-section { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #111827; + font-weight: 500; + } + } + + /* 生成按钮 */ + &__submit-btn { + position: sticky; + bottom: 1rem; + background-color: white; + border-radius: 0.75rem; + padding: 1rem; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); + + button { + width: 100%; + padding: 1rem; + background: linear-gradient(to right, #3b82f6, #8b5cf6); + color: white; + font-weight: 600; + border-radius: 0.75rem; + border: none; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + + &:hover:not(:disabled) { + background: linear-gradient(to right, #2563eb, #7c3aed); + } + + &:disabled { + background: linear-gradient(to right, #9ca3af, #9ca3af); + cursor: not-allowed; + } + + .iconfont { + &.animate-spin { + animation: spin 1s linear infinite; + } + } + } + } + + /* 作品列表样式 */ + &__works { + padding: 1rem; + + &-title { + font-size: 1.125rem; + font-weight: 600; + color: #111827; + margin-bottom: 1rem; + } + + &-list { + .space-y-4 > * + * { + margin-top: 1rem; + } + } + + &-item { + background-color: white; + border-radius: 0.75rem; + padding: 1rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + + &-content { + display: flex; + gap: 1rem; + } + + &-thumb { + flex-shrink: 0; + + &-container { + position: relative; + width: 4rem; + height: 4rem; + border-radius: 0.5rem; + overflow: hidden; + background-color: #f3f4f6; + } + + .el-image { + width: 100%; + height: 100%; + } + + &-placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: #f3f4f6; + + .iconfont { + color: #9ca3af; + font-size: 1.25rem; + } + } + + &-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 0.2s; + + &:hover { + opacity: 1; + } + + .iconfont { + color: white; + font-size: 1.25rem; + } + } + + &-status { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + + &--loading { + background-color: rgba(59, 130, 246, 0.2); + + .iconfont { + color: #3b82f6; + font-size: 1.25rem; + animation: spin 1s linear infinite; + } + } + + &--failed { + background-color: rgba(239, 68, 68, 0.2); + + .iconfont { + color: #ef4444; + font-size: 1.25rem; + } + } + } + } + + &-info { + flex: 1; + min-width: 0; + + &-header { + display: flex; + align-items: start; + justify-content: space-between; + } + + &-title { + color: #111827; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &-prompt { + color: #6b7280; + font-size: 0.875rem; + margin-top: 0.25rem; + } + + &-status { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + + &--failed { + color: #dc2626; + } + + &--processing { + color: #2563eb; + + .loading-spinner { + width: 0.75rem; + height: 0.75rem; + border: 1px solid #2563eb; + border-top-color: transparent; + border-radius: 50%; + animation: spin 1s linear infinite; + } + } + } + + &-tags { + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.5rem; + + &-item { + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + border-radius: 9999px; + + &--primary { + background-color: #dbeafe; + color: #2563eb; + } + + &--warning { + background-color: #fef3c7; + color: #d97706; + } + + &--power { + background-color: #fed7aa; + color: #ea580c; + } + } + } + } + + &-actions { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 1rem; + + &-left { + display: flex; + gap: 0.5rem; + } + + &-btn { + padding: 0.375rem 0.75rem; + font-size: 0.875rem; + border-radius: 0.5rem; + border: none; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 0.25rem; + + .iconfont { + font-size: 0.75rem !important; + } + + &--primary { + background-color: #2563eb; + color: white; + + &:hover { + background-color: #1d4ed8; + } + } + + &--success { + background-color: #16a34a; + color: white; + + &:hover { + background-color: #15803d; + } + } + + &--warning { + background-color: #ea580c; + color: white; + + &:hover { + background-color: #dc2626; + } + } + + &--danger { + background-color: #fef2f2; + color: #dc2626; + + &:hover { + background-color: #fecaca; + } + } + + &:disabled { + background-color: #9ca3af; + cursor: not-allowed; + } + } + } + } + + &-loading { + display: flex; + justify-content: center; + padding: 1rem; + + .iconfont { + color: #3b82f6; + font-size: 1.25rem; + animation: spin 1s linear infinite; + } + } + + &-finished { + text-align: center; + padding: 1rem; + color: #6b7280; + } + } + + /* 媒体预览弹窗 */ + &__media-dialog { + position: fixed; + inset: 0; + z-index: 50; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(0, 0, 0, 0.5); + + &-content { + background-color: white; + border-radius: 1rem; + width: 100%; + max-width: 56rem; + max-height: 80vh; + } + + &-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + border-bottom: 1px solid #e5e7eb; + + h3 { + font-size: 1.125rem; + font-weight: 600; + color: #111827; + } + + button { + padding: 0.5rem; + border: none; + background: none; + border-radius: 50%; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: #f3f4f6; + } + + .iconfont { + color: #6b7280; + } + } + } + + &-body { + padding: 1.5rem; + + img, + video { + width: 100%; + max-height: 60vh; + object-fit: contain; + border-radius: 0.5rem; + } + } + } + + /* 快捷操作按钮样式 */ + &__works-item-quick-actions { + display: flex; + gap: 8px; + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid #f3f4f6; + } + + &__works-item-quick-action-btn { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border: none; + border-radius: 8px; + background: #f9fafb; + color: #6b7280; + cursor: pointer; + transition: all 0.2s ease; + font-size: 14px; + + &:hover { + background: #e5e7eb; + color: #374151; + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + } + + &--danger { + color: #ef4444; + + &:hover { + background: #fef2f2; + color: #dc2626; + } + } + + i { + font-size: 16px; + } + } + + /* 错误信息样式 */ + &__works-item-error { + margin-top: 8px; + padding: 8px 12px; + background: #fef2f2; + border: 1px solid #fecaca; + border-radius: 8px; + } + + &__works-item-error-content { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + } + + &__works-item-error-text { + flex: 1; + font-size: 12px; + color: #dc2626; + line-height: 1.4; + word-break: break-all; + } + + &__works-item-error-copy-btn { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border: none; + border-radius: 4px; + background: #fee2e2; + color: #dc2626; + cursor: pointer; + transition: all 0.2s ease; + font-size: 12px; + + &:hover { + background: #fecaca; + color: #b91c1c; + } + + i { + font-size: 12px; + } + } +} + +/* 旋转动画 */ +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/web/src/assets/css/mobile/model-select.scss b/web/src/assets/css/mobile/model-select.scss new file mode 100644 index 00000000..19ec42e1 --- /dev/null +++ b/web/src/assets/css/mobile/model-select.scss @@ -0,0 +1,16 @@ +.van-popup { + .picker-option { + display: flex; + width: 100%; + padding: 0 10px; + overflow: hidden; + height: 20px; + text-overflow: ellipsis; + + .van-image { + width: 20px; + height: 20px; + margin-right: 5px; + } + } +} \ No newline at end of file diff --git a/web/src/assets/css/mobile/model-select.styl b/web/src/assets/css/mobile/model-select.styl deleted file mode 100644 index 8b3de473..00000000 --- a/web/src/assets/css/mobile/model-select.styl +++ /dev/null @@ -1,16 +0,0 @@ -.van-popup { - .picker-option { - display flex - width 100% - padding 0 10px - overflow hidden - height 20px - text-overflow ellipsis - - .van-image { - width 20px; - height 20px; - margin-right 5px - } - } -} \ No newline at end of file diff --git a/web/src/assets/css/mobile/suno.scss b/web/src/assets/css/mobile/suno.scss new file mode 100644 index 00000000..3a5d670e --- /dev/null +++ b/web/src/assets/css/mobile/suno.scss @@ -0,0 +1,126 @@ +/* 来自 SunoCreate.vue 的样式,已迁移至此,供移动端页面使用 */ + +/* 自定义动画 */ +@keyframes fade-in { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fade-out { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-10px); + } +} + +@keyframes slide-up { + from { + opacity: 0; + transform: translateY(100%); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes scale-up { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} + +.animate-fade-in { + animation: fade-in 0.3s ease-out; +} + +.animate-fade-out { + animation: fade-out 0.3s ease-out; +} + +.animate-slide-up { + animation: slide-up 0.3s ease-out; +} + +.animate-scale-up { + animation: scale-up 0.3s ease-out; +} + +/* 文本截断 */ +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* 滚动监听自动加载更多 */ +.scroll-container { + height: 100vh; + overflow-y: auto; +} + +/* 深色模式适配 */ +@media (prefers-color-scheme: dark) { + .bg-gray-50 { + background-color: #1f2937; + } + + .bg-white { + background-color: #374151; + } + + .text-gray-900 { + color: #f9fafb; + } + + .text-gray-700 { + color: #d1d5db; + } + + .text-gray-600 { + color: #9ca3af; + } + + .text-gray-500 { + color: #6b7280; + } + + .border-gray-200 { + border-color: #4b5563; + } + + .bg-gray-100:hover { + background-color: #4b5563; + } +} + +/* el-upload 组件样式定制 */ +.upload-area { + width: 100%; + + :deep(.el-upload) { + width: 100%; + display: block; + } + + :deep(.el-button) { + width: 100%; + display: block; + } +} diff --git a/web/src/assets/css/mobile/video.scss b/web/src/assets/css/mobile/video.scss new file mode 100644 index 00000000..4473e66c --- /dev/null +++ b/web/src/assets/css/mobile/video.scss @@ -0,0 +1,83 @@ +/* 来自 VideoCreate.vue 的样式,已迁移至此,供移动端页面使用 */ + +// 自定义动画 +@keyframes fade-in { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fade-out { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-10px); + } +} + +@keyframes scale-up { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} + +.animate-fade-in { + animation: fade-in 0.3s ease-out; +} + +.animate-fade-out { + animation: fade-out 0.3s ease-out; +} + +.animate-scale-up { + animation: scale-up 0.3s ease-out; +} + +// 文本截断 +.line-clamp-2 { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +// 深色模式适配 +@media (prefers-color-scheme: dark) { + .bg-gray-50 { + background-color: #1f2937; + } + .bg-white { + background-color: #374151; + } + .text-gray-900 { + color: #f9fafb; + } + .text-gray-700 { + color: #d1d5db; + } + .text-gray-600 { + color: #9ca3af; + } + .text-gray-500 { + color: #6b7280; + } + .border-gray-200 { + border-color: #4b5563; + } + .bg-gray-100:hover { + background-color: #4b5563; + } +} diff --git a/web/src/assets/css/realtime.styl b/web/src/assets/css/realtime.scss similarity index 92% rename from web/src/assets/css/realtime.styl rename to web/src/assets/css/realtime.scss index 18f5337a..70a80d33 100644 --- a/web/src/assets/css/realtime.styl +++ b/web/src/assets/css/realtime.scss @@ -1,189 +1,188 @@ -.realtime-conversation { - /********************** connection ****************************/ - .connection-container { - background-color: #000; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin: 0; - overflow: hidden; - font-family: Arial, sans-serif; - width 100% - - .phone-container { - position: relative; - width: 200px; - height: 200px; - } - - .phone { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 60px; - height: 60px; - background-color: #00ffcc; - mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.074 15.074 0 0 1-6.59-6.59l2.2-2.2c.27-.27.35-.68.24-1.02a11.36 11.36 0 0 1-.57-3.57c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM5.03 5h1.5c.07.89.22 1.76.46 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79zM19 18.97c-1.32-.09-2.59-.35-3.8-.75l1.2-1.2c.85.24 1.72.39 2.6.45v1.5z'/%3E%3C/svg%3E") no-repeat 50% 50%; - mask-size: cover; - -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.074 15.074 0 0 1-6.59-6.59l2.2-2.2c.27-.27.35-.68.24-1.02a11.36 11.36 0 0 1-.57-3.57c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM5.03 5h1.5c.07.89.22 1.76.46 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79zM19 18.97c-1.32-.09-2.59-.35-3.8-.75l1.2-1.2c.85.24 1.72.39 2.6.45v1.5z'/%3E%3C/svg%3E") no-repeat 50% 50%; - -webkit-mask-size: cover; - animation: shake 0.5s ease-in-out infinite; - } - - .signal { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 100px; - height: 100px; - border: 2px dashed #00ffcc; - border-radius: 50%; - opacity: 0; - animation: signal 2s linear infinite; - } - - .signal:nth-child(2) { - animation-delay: 0.5s; - } - - .signal:nth-child(3) { - animation-delay: 1s; - } - - .status-text { - color: #00ffcc; - font-size: 18px; - margin-top: 20px; - height: 1.2em; - overflow: hidden; - } - - @keyframes shake { - 0%, 100% { transform: translate(-50%, -50%) rotate(0deg); } - 25% { transform: translate(-52%, -48%) rotate(-5deg); } - 75% { transform: translate(-48%, -52%) rotate(5deg); } - } - - @keyframes signal { - 0% { - width: 60px; - height: 60px; - opacity: 1; - } - 100% { - width: 200px; - height: 200px; - opacity: 0; - } - } - } - /*********** end of connection ************/ - - .conversation-container { - background: linear-gradient(to right, #2c3e50, #4a5568, #6b46c1); - display: flex; - height 100% - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 0; - width 100% - - .wave-container { - padding 3rem - .wave-animation { - display: flex; - justify-content: center; - align-items: center; - gap: 10px; - - .wave-ellipse { - width: 40px; - height: 40px; - background-color: white; - border-radius: 20px; - animation: wave 0.8s infinite ease-in-out; - } - - .wave-ellipse:nth-child(odd) { - height: 60px; - } - - .wave-ellipse:nth-child(even) { - height: 80px; - } - } - } - - @keyframes wave { - 0%, 100% { - transform: scaleY(0.8); - } - 50% { - transform: scaleY(1.2); - } - } - - .wave-ellipse:nth-child(2) { - animation-delay: 0.1s; - } - - .wave-ellipse:nth-child(3) { - animation-delay: 0.2s; - } - - .wave-ellipse:nth-child(4) { - animation-delay: 0.3s; - } - - .wave-ellipse:nth-child(5) { - animation-delay: 0.4s; - } - - .voice-indicators { - display flex - flex-flow row - justify-content: space-between; - width 100% - } - - .call-controls { - display: flex; - justify-content: center; - gap: 3rem; - padding 3rem - - .call-button { - width: 60px; - height: 60px; - border-radius: 50%; - border: none; - display: flex; - justify-content: center; - align-items: center; - font-size: 24px; - color: white; - cursor: pointer; - - .iconfont { - font-size 24px - } - } - .hangup { - background-color: #e74c3c; - } - - .answer { - background-color: #2ecc71; - } - - .icon { - font-size: 28px; - } - } - - } +.realtime-conversation { + /********************** connection ****************************/ + .connection-container { + background-color: #000; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: 0; + overflow: hidden; + font-family: Arial, sans-serif; + width: 100%; + + .phone-container { + position: relative; + width: 200px; + height: 200px; + } + + .phone { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 60px; + height: 60px; + background-color: #00ffcc; + mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.074 15.074 0 0 1-6.59-6.59l2.2-2.2c.27-.27.35-.68.24-1.02a11.36 11.36 0 0 1-.57-3.57c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM5.03 5h1.5c.07.89.22 1.76.46 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79zM19 18.97c-1.32-.09-2.59-.35-3.8-.75l1.2-1.2c.85.24 1.72.39 2.6.45v1.5z'/%3E%3C/svg%3E") no-repeat 50% 50%; + mask-size: cover; + -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 0 0-1.02.24l-2.2 2.2a15.074 15.074 0 0 1-6.59-6.59l2.2-2.2c.27-.27.35-.68.24-1.02a11.36 11.36 0 0 1-.57-3.57c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM5.03 5h1.5c.07.89.22 1.76.46 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79zM19 18.97c-1.32-.09-2.59-.35-3.8-.75l1.2-1.2c.85.24 1.72.39 2.6.45v1.5z'/%3E%3C/svg%3E") no-repeat 50% 50%; + -webkit-mask-size: cover; + animation: shake 0.5s ease-in-out infinite; + } + + .signal { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100px; + height: 100px; + border: 2px dashed #00ffcc; + border-radius: 50%; + opacity: 0; + animation: signal 2s linear infinite; + } + + .signal:nth-child(2) { + animation-delay: 0.5s; + } + + .signal:nth-child(3) { + animation-delay: 1s; + } + + .status-text { + color: #00ffcc; + font-size: 18px; + margin-top: 20px; + height: 1.2em; + overflow: hidden; + } + + @keyframes shake { + 0%, 100% { transform: translate(-50%, -50%) rotate(0deg); } + 25% { transform: translate(-52%, -48%) rotate(-5deg); } + 75% { transform: translate(-48%, -52%) rotate(5deg); } + } + + @keyframes signal { + 0% { + width: 60px; + height: 60px; + opacity: 1; + } + 100% { + width: 200px; + height: 200px; + opacity: 0; + } + } + } + /*********** end of connection ************/ + + .conversation-container { + background: linear-gradient(to right, #2c3e50, #4a5568, #6b46c1); + display: flex; + height: 100%; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 0; + width: 100%; + + .wave-container { + padding: 3rem; + .wave-animation { + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + + .wave-ellipse { + width: 40px; + height: 40px; + background-color: white; + border-radius: 20px; + animation: wave 0.8s infinite ease-in-out; + } + + .wave-ellipse:nth-child(odd) { + height: 60px; + } + + .wave-ellipse:nth-child(even) { + height: 80px; + } + } + } + + @keyframes wave { + 0%, 100% { + transform: scaleY(0.8); + } + 50% { + transform: scaleY(1.2); + } + } + + .wave-ellipse:nth-child(2) { + animation-delay: 0.1s; + } + + .wave-ellipse:nth-child(3) { + animation-delay: 0.2s; + } + + .wave-ellipse:nth-child(4) { + animation-delay: 0.3s; + } + + .wave-ellipse:nth-child(5) { + animation-delay: 0.4s; + } + + .voice-indicators { + display: flex; + flex-flow: row; + justify-content: space-between; + width: 100%; + } + + .call-controls { + display: flex; + justify-content: center; + gap: 3rem; + padding: 3rem; + + .call-button { + width: 60px; + height: 60px; + border-radius: 50%; + border: none; + display: flex; + justify-content: center; + align-items: center; + font-size: 24px; + color: white; + cursor: pointer; + + .iconfont { + font-size: 24px; + } + } + .hangup { + background-color: #e74c3c; + } + + .answer { + background-color: #2ecc71; + } + + .icon { + font-size: 28px; + } + } + } } \ No newline at end of file diff --git a/web/src/assets/css/running-job-list.scss b/web/src/assets/css/running-job-list.scss new file mode 100644 index 00000000..b2c4978d --- /dev/null +++ b/web/src/assets/css/running-job-list.scss @@ -0,0 +1,43 @@ +.running-job-list { + + .running-job-box { + width: 100%; + display: flex; + flex-flow: row; + + .image-slot { + color: var(--theme-text-color-primary); + } + } + + .job-item { + margin-right: 10px; + width: 200px; + height: 200px; + overflow: hidden; + padding: 2px; + background-color: var(--gray-btn-bg); + + .job-item-inner { + position: relative; + height: 100%; + overflow: hidden; + + .progress { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + + span { + font-size: 20px; + color: var(--theme-text-color-primary); + } + } + } + } +} \ No newline at end of file diff --git a/web/src/assets/css/running-job-list.styl b/web/src/assets/css/running-job-list.styl deleted file mode 100644 index e8b9df99..00000000 --- a/web/src/assets/css/running-job-list.styl +++ /dev/null @@ -1,44 +0,0 @@ -.running-job-list { - - .running-job-box { - width 100% - display flex - flex-flow row - - .image-slot { - color var(--theme-text-color-primary) - } - } - - - .job-item { - margin-right 10px - width 200px - height 200px - overflow hidden - padding 2px - background-color var( --gray-btn-bg) - - .job-item-inner { - position relative - height 100% - overflow hidden - - .progress { - position absolute - width 100% - height 100% - top 0 - left 0 - display flex - justify-content center - align-items center - - span { - font-size 20px - color var(--theme-text-color-primary) - } - } - } - } -} \ No newline at end of file diff --git a/web/src/assets/css/sd-task-dialog.scss b/web/src/assets/css/sd-task-dialog.scss new file mode 100644 index 00000000..1cf6c951 --- /dev/null +++ b/web/src/assets/css/sd-task-dialog.scss @@ -0,0 +1,94 @@ +.el-overlay-dialog { + .el-dialog { + // background-color: #1a1b1e; + + .el-dialog__header { + .el-dialog__title { + color: var(--text-color-primary); + } + } + + .el-dialog__body { + padding: 0 0 0 15px !important; + display: flex; + height: 100%; + + .el-row { + width: 100%; + + .img-container { + display: flex; + justify-content: center; + + .image-slot { + display: flex; + height: 100vh; + align-items: center; + justify-content: center; + + .el-icon { + font-size: 60px; + } + } + } + + .task-info { + // background-color: #25262b; + padding: 1rem 1.5rem; + + .info-line { + width: 100%; + + .prompt { + // background-color: #35363b; + padding: 10px; + color: #999999; + overflow: auto; + max-height: 100px; + min-height: 50px; + + position: relative; + + .el-icon { + position: absolute; + right: 10px; + bottom: 10px; + cursor: pointer; + } + } + + .wrapper { + margin-top: 10px; + display: flex; + + label { + display: flex; + width: 100px; + color: var(--text-fb); + } + + .item-value { + display: flex; + width: 100%; + // background-color: #35363b; + padding: 2px 5px; + border-radius: 5px; + color: var(--text-theme-color); + } + } + } + + .copy-params { + padding: 20px 0 10px 0; + + .el-button { + width: 100%; + } + } + } + } + + // end el-row + } + } +} diff --git a/web/src/assets/css/sd-task-dialog.styl b/web/src/assets/css/sd-task-dialog.styl deleted file mode 100644 index ada3061f..00000000 --- a/web/src/assets/css/sd-task-dialog.styl +++ /dev/null @@ -1,97 +0,0 @@ -.el-overlay-dialog { - .el-dialog { - // background-color #1a1b1e - - .el-dialog__header { - .el-dialog__title { - color #F5F5F5 - } - } - - .el-dialog__body { - padding 0 0 0 15px !important - display flex - height 100% - - .el-row { - width 100% - - .img-container { - display flex - justify-content center - - .image-slot { - display flex - height 100vh - align-items center - justify-content center - - .el-icon { - font-size 60px - } - } - } - - .task-info { - // background-color #25262b - padding 1rem 1.5rem - - - .info-line { - width 100% - - .prompt { - // background-color #35363b - padding 10px - color #999999 - overflow auto - max-height 100px - min-height 50px - - position relative - - .el-icon { - position absolute - right 10px - bottom 10px - cursor pointer - } - } - - .wrapper { - margin-top 10px - display flex - - label { - display flex - width 100px - color :var(--text-fb) - } - - .item-value { - display flex - width 100% - // background-color #35363b - padding 2px 5px - border-radius 5px - color: var(--text-theme-color); - } - } - - } - - .copy-params { - padding 20px 0 10px 0 - - .el-button { - width 100% - } - } - } - } - - // end el-row - - } - } -} \ No newline at end of file diff --git a/web/src/assets/css/song.scss b/web/src/assets/css/song.scss new file mode 100644 index 00000000..302ce15b --- /dev/null +++ b/web/src/assets/css/song.scss @@ -0,0 +1,86 @@ +.page-song { + display: flex; + justify-content: center; + background-color: #0e0808; + height: 100vh; + + .inner { + text-align: left; + color: rgb(250 247 245); + padding: 20px; + max-width: 600px; + width: 100%; + font-family: "Neue Montreal, ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji"; + + .title { + font-size: 40px; + font-weight: 500; + line-height: 1rem; + white-space: nowrap; + text-overflow: ellipsis; + } + + .row { + padding: 8px 0; + } + + .author { + display: flex; + align-items: center; + .nickname { + margin: 0 10px; + } + + .btn { + margin-right: 10px; + background-color: #363030; + border: none; + border-radius: 5px; + padding: 5px 10px; + cursor: pointer; + + &:hover { + background-color: #5f5958; + } + } + } + + .date { + color: #999999; + display: flex; + align-items: center; + + .version { + background-color: #1c1616; + border: 1px solid #8f8f8f; + font-weight: normal; + font-size: 14px; + padding: 1px 3px; + border-radius: 5px; + margin-left: 10px; + } + } + + .prompt { + width: 100%; + background-color: transparent; + white-space: pre-wrap; + overflow-y: auto; + resize: none; + position: relative; + outline: 2px solid transparent; + outline-offset: 2px; + border: none; + font-size: 100%; + line-height: 2rem; + } + } + + .music-player { + width: 100%; + position: fixed; + bottom: 0; + left: 50px; + padding: 20px 0; + } +} \ No newline at end of file diff --git a/web/src/assets/css/song.styl b/web/src/assets/css/song.styl deleted file mode 100644 index 4a7a6ee3..00000000 --- a/web/src/assets/css/song.styl +++ /dev/null @@ -1,88 +0,0 @@ -.page-song { - display: flex; - justify-content: center; - background-color: #0E0808; - height: 100vh; - - .inner { - text-align left - color rgb(250 247 245) - padding 20px - max-width 600px - width 100% - font-family "Neue Montreal,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji" - - .title { - font-size 40px - font-weight: 500 - line-height 1rem - white-space nowrap - text-overflow ellipsis - } - - .row { - padding 8px 0 - } - - .author { - display flex - align-items center - .nickname { - margin 0 10px - } - - .btn { - margin-right 10px - background-color #363030 - border none - border-radius 5px - padding 5px 10px - cursor pointer - - &:hover { - background-color #5F5958 - } - } - } - - .date { - color #999999 - display flex - align-items center - - .version { - background-color #1C1616 - border 1px solid #8f8f8f - font-weight normal - font-size 14px - padding 1px 3px - border-radius 5px - margin-left 10px - } - } - - .prompt { - width 100% - background-color transparent - white-space pre-wrap - overflow-y auto - resize none - position relative - outline 2px solid transparent - outline-offset 2px - border none - font-size 100% - line-height 2rem - } - } - - - .music-player { - width 100% - position: fixed; - bottom: 0; - left: 50px; - padding 20px 0 - } - -} \ No newline at end of file diff --git a/web/src/assets/css/suno.scss b/web/src/assets/css/suno.scss new file mode 100644 index 00000000..749c28d6 --- /dev/null +++ b/web/src/assets/css/suno.scss @@ -0,0 +1,585 @@ +.page-suno { + display: flex; + height: 100%; + background-color: var(--theme-bg-color); + overflow: auto; + + .left-bar { + max-width: 400px; + min-width: 400px; + padding: 20px; + background-color: var(--theme-bg-color); + overflow-y: auto; + + .space-y-6 { + > * + * { + margin-top: 1.5rem; + } + } + + .setting-card { + background: var(--card-bg); + border-radius: 12px; + padding: 16px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid var(--el-border-color-light); + + .card-title { + color: var(--theme-text-color-primary); + font-weight: 600; + font-size: 14px; + } + + .card-description { + color: var(--theme-text-color-secondary); + font-size: 12px; + margin-top: 4px; + } + + .card-label { + color: var(--theme-text-color-primary); + font-weight: 500; + font-size: 14px; + } + + .help-icon { + color: var(--theme-text-color-secondary); + cursor: help; + font-size: 16px; + } + } + + .extend-song-card { + border-left: 4px solid #f97316; + } + + .lyric-btn { + padding: 6px 12px; + background: var(--el-color-primary); + color: white; + font-size: 12px; + border-radius: 8px; + border: none; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 4px; + + &:hover:not(:disabled) { + background: var(--el-color-primary-dark-2); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } + } + + .tag-btn { + padding: 6px 12px; + font-size: 12px; + border: 1px solid var(--el-color-primary-light-5); + color: var(--el-color-primary); + border-radius: 20px; + background: transparent; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background: var(--el-color-primary-light-9); + border-color: var(--el-color-primary); + } + } + + .remove-btn { + padding: 6px 12px; + font-size: 12px; + background: #fef2f2; + color: #dc2626; + border-radius: 8px; + border: none; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background: #fee2e2; + } + } + + .extend-input { + width: 100%; + padding: 12px; + border: 1px solid var(--el-border-color); + border-radius: 8px; + background: var(--el-bg-color); + color: var(--theme-text-color-primary); + outline: none; + transition: border-color 0.2s; + + &:focus { + border-color: var(--el-color-primary); + } + + &::placeholder { + color: var(--theme-text-color-secondary); + } + } + + .create-btn { + width: 100%; + padding: 12px; + background: linear-gradient(135deg, var(--el-color-primary) 0%, #8b5cf6 100%); + color: white; + font-weight: 600; + border-radius: 12px; + border: none; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + + &:hover:not(:disabled) { + background: linear-gradient(135deg, var(--el-color-primary-dark-2) 0%, #7c3aed 100%); + transform: translateY(-1px); + } + + &:disabled { + background: var(--el-fill-color); + color: var(--el-text-color-disabled); + cursor: not-allowed; + transform: none; + } + } + + .upload-tips { + margin-top: 12px; + font-size: 12px; + color: var(--theme-text-color-secondary); + line-height: 1.5; + + p { + margin: 4px 0; + } + } + } + + .right-box { + flex: 1; + color: var(--theme-text-color-primary); + overflow: auto; + background: var(--theme-bg-color); + padding: 20px; + + .list-box { + .song-card { + background: var(--card-bg); + border-radius: 12px; + padding: 16px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid var(--el-border-color-light); + margin-bottom: 16px; + + .song-cover { + position: relative; + width: 64px; + height: 64px; + border-radius: 8px; + overflow: hidden; + background: var(--el-fill-color-light); + + .cover-placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: var(--el-fill-color-light); + } + + .play-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 0.2s; + border: none; + cursor: pointer; + + &:hover { + opacity: 1; + } + } + + .progress-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(59, 130, 246, 0.2); + } + + .error-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(239, 68, 68, 0.2); + } + } + + .song-title { + color: var(--theme-text-color-primary); + font-weight: 600; + font-size: 16px; + margin-bottom: 4px; + + .song-link { + color: var(--el-color-primary); + text-decoration: none; + transition: color 0.2s; + + &:hover { + color: var(--el-color-primary-dark-2); + } + } + } + + .song-description { + color: var(--theme-text-color-secondary); + font-size: 14px; + line-height: 1.4; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .task-status { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + + .status-error { + color: #dc2626; + display: flex; + align-items: center; + gap: 4px; + } + + .status-loading { + color: var(--el-color-primary); + display: flex; + align-items: center; + gap: 4px; + + .loading-spinner { + width: 12px; + height: 12px; + border: 1px solid var(--el-color-primary); + border-top: 1px solid transparent; + border-radius: 50%; + animation: spin 1s linear infinite; + } + } + } + + .song-tags { + display: flex; + align-items: center; + gap: 8px; + margin-top: 8px; + + .model-tag, + .upload-tag, + .full-song-tag, + .extend-tag { + padding: 2px 8px; + font-size: 10px; + border-radius: 12px; + font-weight: 500; + } + + .model-tag { + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; + } + + .upload-tag { + background: rgba(34, 197, 94, 0.1); + color: #22c55e; + } + + .full-song-tag { + background: rgba(245, 158, 11, 0.1); + color: #f59e0b; + } + + .extend-tag { + background: rgba(168, 85, 247, 0.1); + color: #a855f7; + } + } + + .song-actions { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 16px; + + .action-buttons { + display: flex; + gap: 8px; + } + + .action-btn { + padding: 6px 12px; + font-size: 12px; + border-radius: 8px; + border: none; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + gap: 4px; + + &.play-btn { + background: var(--el-color-primary); + color: white; + + &:hover { + background: var(--el-color-primary-dark-2); + } + } + + &.download-btn { + background: #22c55e; + color: white; + + &:hover:not(:disabled) { + background: #16a34a; + } + + &:disabled { + background: var(--el-fill-color); + color: var(--el-text-color-disabled); + cursor: not-allowed; + } + } + + &.merge-btn { + background: #a855f7; + color: white; + + &:hover { + background: #9333ea; + } + } + + &.extend-btn { + background: #f59e0b; + color: white; + + &:hover { + background: #d97706; + } + } + + &.edit-btn { + background: #6b7280; + color: white; + + &:hover { + background: #4b5563; + } + } + + &.delete-btn { + background: #fef2f2; + color: #dc2626; + + &:hover { + background: #fee2e2; + } + } + } + } + + .progress-bar { + margin-top: 16px; + + .progress-info { + display: flex; + justify-content: space-between; + font-size: 12px; + color: var(--theme-text-color-secondary); + margin-bottom: 4px; + } + + .progress-track { + width: 100%; + height: 8px; + background: var(--el-fill-color-light); + border-radius: 4px; + overflow: hidden; + + .progress-fill { + height: 100%; + background: var(--el-color-primary); + border-radius: 4px; + transition: width 0.3s ease; + } + } + } + + .error-message { + margin-top: 16px; + padding: 12px; + background: #fef2f2; + border: 1px solid #fecaca; + border-radius: 8px; + + .error-text { + color: #dc2626; + font-size: 12px; + } + } + } + } + + .pagination { + margin-top: 20px; + display: flex; + justify-content: center; + } + + .music-player { + width: 100%; + position: fixed; + bottom: 0; + left: 50px; + padding: 20px 0; + } + } +} + +// 文本颜色变量 +.text-primary { + color: var(--theme-text-color-primary); +} + +.text-secondary { + color: var(--theme-text-color-secondary); +} + +// 动画 +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.animate-spin { + animation: spin 1s linear infinite; +} + +// 自定义上传组件样式 +.custom-upload { + width: 100%; + + :deep(.el-upload) { + width: 100%; + height: auto; + cursor: pointer; + } +} + +// 表单样式 +.form { + .form-item { + margin-bottom: 20px; + + .label { + margin-bottom: 8px; + font-weight: 500; + color: var(--theme-text-color-primary); + } + } +} + +.dialog-footer { + display: flex; + justify-content: flex-end; + gap: 10px; +} + +// 深色主题适配 +:root[data-theme='dark'] { + .page-suno { + .setting-card, + .song-card { + background: var(--card-bg); + border-color: var(--el-border-color-light); + } + + .remove-btn { + background: rgba(239, 68, 68, 0.1); + color: #fca5a5; + + &:hover { + background: rgba(239, 68, 68, 0.2); + } + } + + .error-message { + background: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.3); + + .error-text { + color: #fca5a5; + } + } + + .action-btn.delete-btn { + background: rgba(239, 68, 68, 0.1); + color: #fca5a5; + + &:hover { + background: rgba(239, 68, 68, 0.2); + } + } + } +} + +// 响应式设计 +@media (max-width: 768px) { + .page-suno { + flex-direction: column; + + .left-bar { + max-width: none; + min-width: auto; + padding: 16px; + } + + .right-box { + padding: 16px; + + .music-player { + left: 0; + padding: 16px; + } + } + } +} diff --git a/web/src/assets/css/suno.styl b/web/src/assets/css/suno.styl deleted file mode 100644 index aa95d9dc..00000000 --- a/web/src/assets/css/suno.styl +++ /dev/null @@ -1,430 +0,0 @@ -.page-suno { - display flex - height 100% - // background-color #0E0808 - overflow auto - .item-group{ - scrollbar-width: auto !important; /* 恢复滚动条(Firefox) */ - -ms-overflow-style: auto !important; /* 恢复滚动条(IE、Edge) */ - ::-webkit-scrollbar { - display: block !important; - } - } - - .left-bar { - max-width 340px - min-width 340px - padding 20px 30px - - .bar-top { - display flex - flex-flow row - justify-content: space-between; - align-items center - - .upload-music { - .iconfont { - margin-right 5px - font-size 14px - } - } - } - - .params { - padding 20px 0 - color: var(--text-theme-color); - position relative - - .pure-music { - position absolute - right 0 - top 24px - display flex - - .text { - margin-top 5px - margin-left 5px - } - } - - .label { - padding 10px 0 - - .text { - margin-right 10px - } - .el-icon { - top 2px - } - } - .item { - margin-bottom: 20px - position relative - - .create-btn { - margin 20px 0 - background-image url("~@/assets/img/suno-create-bg.svg") - background-size: cover; - background-position: 50% 50%; - transition: background 1s ease-in-out; - overflow: hidden; - font-size 16px - width 100% - padding 16px - border-radius 25px - border none - cursor pointer - - img { - position relative - top 3px - margin-right 5px - } - - &:hover { - opacity: 0.9; - } - } - - - .song { - display flex - padding 10px - background-color var(--el-bg-color) - border-radius 10px - margin-bottom 10px - font-size 14px - position relative - - .el-image { - width 50px - height 50px - border-radius 10px - } - .icon-mp3 { - font-size 42px - color #A85295 - } - .title { - display flex - margin-left 10px - align-items center - color var(--el-color-primary) - } - - .el-button--info { - position absolute - right 20px - top 20px - } - } - - .extend-secs { - padding 10px 0 - font-size 14px - - input { - width 50px - text-align center - padding 8px 10px - font-size 14px - background none - border 1px solid #8f8f8f - margin 0 10px - border-radius 10px - outline: none; - transition: border-color 0.5s ease, box-shadow 0.5s ease; - &:focus { - border-color: #0F7A71; - box-shadow: 0 0 5px #0F7A71; - } - } - } - - .btn-lyric { - position absolute - left 10px - bottom 10px - font-size 12px - padding 2px 5px - background-color var(--sm-btn-bg) - color: #fff - } - } - - .tag-select { - position relative - overflow-x auto - overflow-y hidden - scrollbar-width: auto !important; /* 恢复滚动条(Firefox) */ - -ms-overflow-style: auto !important; /* 恢复滚动条(IE、Edge) */ - width 100% - - ::-webkit-scrollbar { - display: block !important; - } - - .inner { - display flex - flex-flow row - padding-bottom 10px - - .tag { - margin-right 10px - word-break keep-all - background: var(--card-bg); - color:var(--theme-text-color-primary); - opacity 0.7 - border-radius 8px - padding 3px 6px - cursor pointer - font-size 13px - &:hover{ - color:var( --el-color-primary) - } - } - } - } - } - } - .right-box { - width 100% - color rgb(250 247 245) - overflow auto - background: var(--chat-bg) - - - .list-box { - padding 20px - .item { - display flex - flex-flow row - padding 5px 0 - cursor pointer - margin-bottom 10px - - &:hover { - background: rgba(188,149,236,0.08) - } - - .left { - .container { - width 60px - height 90px - position relative - - .el-image { - height 90px - border-radius 5px - width 100% - } - - .duration { - position absolute - bottom 0 - right 0 - background-color rgba(14,8,8,.7) - padding 0 3px - font-family 'Input Sans' - font-size 14px - font-weight 700 - border-radius .125rem - } - - .play { - position absolute - width: 56px; - height 100% - top: 0; - left: 50%; - border none - border-radius 5px - background rgba(100, 100, 100, 0.3) - cursor pointer - color #ffffff - opacity 0 - transform: translate(-50%, 0px); - transition opacity 0.3s ease 0s - display flex - justify-content center - align-items center - } - - &:hover { - .play { - opacity 1 - //display block - } - } - } - } - - .center { - width 100% - //border 1px solid saddlebrown - display flex - justify-content center - align-items flex-start - flex-flow column - height 90px - padding 0 20px - - .title { - padding 6px 0 - font-size 16px - font-weight 700 - - a { - color var( --a-link-color) - &:hover { - text-decoration underline - } - } - - .model { - color #8f8f8f - background-color var(--el-bg-color) - border 1px solid var(--el-border-color-light) - font-weight normal - font-size 12px - padding 1px 3px - border-radius 5px - margin-left 10px - - .iconfont { - font-size 12px - } - } - } - - .tags { - font-size 14px - color var(--text-fb) - padding 3px 0 - } - } - - .right { - min-width 350px; - font-size 14px - padding 0 0 0 15px - display flex - justify-content right - - .tools { - display flex - justify-content right - align-items center - flex-flow row - height 90px - - .btn-publish { - padding 2px 10px - - // .text { - // margin-right 10px - // } - } - - .btn-icon { - background none - padding 6px - transition background 0.6s ease 0s - color #919191 - - &:hover { - // background #5f5958 - // color #e1e1e1 - color:var(--el-color-primary) - } - - .downloading { - width 16px - } - } - } - } - } - - - .task { - height 100px - background-color var(--el-bg-color) - border: 1px solid var(--el-border-color-light); - border-radius 5px - display flex - margin-bottom 10px - .left { - display flex - justify-content left - align-items center - padding 20px - width 320px - .title { - font-size 14px - color var(--el-text-color-primary) - white-space: nowrap; /* 防止文字换行 */ - overflow: hidden; /* 隐藏溢出的内容 */ - text-overflow: ellipsis; /* 用省略号表示溢出的内容 */ - } - } - .center { - display flex - width 100% - justify-content center - .failed { - display flex - align-items center - color #E4696B - font-size 14px - } - } - .right { - display flex - width 100px - justify-content center - align-items center - } - } - } - - .pagination { - margin-top 20px - display flex - justify-content center - } - .music-player { - width 100% - position: fixed; - bottom: 0; - left: 50px; - padding 20px 0 - } - } - - - .btn { - margin-right 10px - color: var((--theme-text-color-primary)) - border none - border-radius 5px - padding 5px 10px - cursor pointer - background: var(--btn-bg) - - - &:hover { - opacity :0.8 - } - } -} -.submit-btn { - display flex - align-items: center - margin: 20px 0 - justify-content: center; - .el-button { - width 200px - } - -} \ No newline at end of file diff --git a/web/src/assets/css/theme-dark.styl b/web/src/assets/css/theme-dark.scss similarity index 50% rename from web/src/assets/css/theme-dark.styl rename to web/src/assets/css/theme-dark.scss index b8a14600..ebdd86c9 100644 --- a/web/src/assets/css/theme-dark.styl +++ b/web/src/assets/css/theme-dark.scss @@ -1,77 +1,82 @@ - -@import 'font.styl' -:root[data-theme="dark"]{ - --text-fb:#fff; +@use 'font.scss' as *; +:root[data-theme='dark'] { + --text-fb: #fff; --text-color: rgba(255, 255, 255, 1) !important; // 主要的文本颜色 --normal-color: rgba(163, 174, 208, 1); // 普通颜色 --el-text-color-primary: #fff; - p, h1, h2, h3, h4, h5, h6, article { + p, + h1, + h2, + h3, + h4, + h5, + h6, + article { // color: var(--text-color) !important; - font-family: $font-regular; - + font-family: $font-regular; } html, body, #app, .wrapper { - background: rgb(13, 20, 53) - background-color: rgb(13, 20, 53) + background: rgb(13, 20, 53); + background-color: rgb(13, 20, 53); font-family: $font-regular; } --btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce); - --border-active:rgba(255, 255, 255, 0.1); - --card-bg:#252d58; - --chat-bg:#1f243f - --chat-wel-bg:#2d2f38; + --border-active: rgba(255, 255, 255, 0.1); + --card-bg: #252d58; + --chat-bg: #1f243f; + --chat-wel-bg: #2d2f38; --card-bg-table: rgba(17, 28, 68, 1); - --theme-bg:rgb(13, 20, 53); + --theme-bg: rgb(13, 20, 53); --theme-bg-color: rgb(13, 20, 53); - --theme-bg-all:rgb(13, 20, 53); + --theme-bg-all: rgb(13, 20, 53); --sign-bg: rgba(27, 37, 75, 1); --text-theme-color: #fff; --text-color-primary: #d1c7ff; --theme-text-color-secondary: #a3aed0; --theme-text-color-primary: #fff; --theme-text-primary: #f3f3f3; - --line-box:rgba(255, 255, 255, 0.1); - --el-bg-color:#141a36; + --line-box: rgba(255, 255, 255, 0.1); + --el-bg-color: #141a36; --el-fill-color-blank: rgba(17, 28, 68, 1); - --el-fill-color-light: rgba(86, 86, 95, .2); - --el-color-primary-light-9:rgba(86, 86, 95, .2); - --el-text-color-regular: rgba(163, 174, 208, 1) - --el-border-color:rgb(79, 80, 85);//黑白切换 + --el-fill-color-light: rgba(86, 86, 95, 0.2); + --el-color-primary-light-9: rgba(86, 86, 95, 0.2); + --el-text-color-regular: rgba(163, 174, 208, 1); + --el-border-color: rgb(79, 80, 85); //黑白切换 --el-bg-color-overlay: rgba(17, 28, 68, 1); --el-border-color-light: rgba(255, 255, 255, 0.2); - --chat-content-bg:rgba(86, 86, 95, .2); - --chat-user-content-bg: #762AA4; - --hover-deep-color:#30323c; - --tab-title-bg:#525777;//顶部tab栏背景切换 - --tab-title-color:#fff;//顶部tab栏文字切换 + --chat-content-bg: rgba(86, 86, 95, 0.2); + --chat-user-content-bg: #762aa4; + --hover-deep-color: #30323c; + --tab-title-bg: #525777; //顶部tab栏背景切换 + --tab-title-color: #fff; //顶部tab栏文字切换 //深黑色 - --bg-deep-color:rgba(255,255,255,0.8); - //layout + --bg-deep-color: rgba(255, 255, 255, 0.8); + //layout .more-menus li.moreTitle, .twoTittle .title, .setting-menus span.title, .setting-menus li .el-icon, .setting-menus li .iconfont, - .layout .tab-box .menu-list-item{ + .layout .tab-box .menu-list-item { //filter: invert(100%); } - .more-menus span.title{ + .more-menus span.title { color: var(--text-theme-color); } // 操作按钮 - --btn-bg: rgba(86, 86, 95, .5); + --btn-bg: rgba(86, 86, 95, 0.5); .el-table { // 表格表头背景 - --el-fill-color-darker: rgba(100, 100, 100, .5); + --el-fill-color-darker: rgba(100, 100, 100, 0.5); --el-border-color-darker: #73767a; - --el-table-border-color: rgba(100, 100, 100, .5); - --el-table-row-hover-bg-color: rgba(16, 21, 43, .8); - --el-table-current-row-bg-color: rgba(16, 21, 43, .8); + --el-table-border-color: rgba(100, 100, 100, 0.5); + --el-table-row-hover-bg-color: rgba(16, 21, 43, 0.8); + --el-table-current-row-bg-color: rgba(16, 21, 43, 0.8); } // 加载动画 @@ -87,11 +92,11 @@ --van-button-default-background: #141a36; --van-background: #141a36; --van-tabbar-background: #141a36; - --van-nav-bar-background: #1B244A; + --van-nav-bar-background: #1b244a; --van-dropdown-menu-background: #141a36; // 引用快样式 - --quote-bg-color: #1F243F; + --quote-bg-color: #1f243f; --quote-text-color: #fff; // el-dialog 阴影 @@ -99,4 +104,14 @@ // 面板背景 --panel-bg: linear-gradient(135deg, #252d58 0%, #1f243f 100%); + + // 登录注册页面专用变量 + --login-bg: linear-gradient(135deg, #252d58 0%, #1f243f 100%); + --login-card-bg: #252d58; + --login-card-border: rgba(255, 255, 255, 0.1); + --login-title-color: #fff; + --login-subtitle-color: #a3aed0; + --login-text-color: #a3aed0; + --login-link-color: #d1c7ff; + --login-link-hover-bg: rgba(209, 199, 255, 0.1); } diff --git a/web/src/assets/css/theme-light.scss b/web/src/assets/css/theme-light.scss new file mode 100644 index 00000000..6357d882 --- /dev/null +++ b/web/src/assets/css/theme-light.scss @@ -0,0 +1,75 @@ +@use 'font.scss' as *; +:root[data-theme='light'] { + --text-fb: #000; + --text-color: #5b62ce; // 主要的文本颜色 + --normal-color: rgba(43, 54, 116, 1); // 普通颜色 + --theme-textcolor-normal: #5b62ce; + p, + h1, + h2, + h3, + h4, + h5, + h6, + article { + font-family: $font-regular; + } + html, + body, + #app, + .wrapper { + font-family: $font-regular; + } + + --btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce); + --border-active: rgba(134, 140, 255, 1); + --code-btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce); + --card-bg: #fff; + --chat-bg: #fff; + --theme-bg: linear-gradient(88deg, #fff3f3 1.44%, #e7e8ff); + --theme-bg-all: #f5f7fd; + --theme-bg-color: #f5f7fd; + --sign-bg: rgba(244, 247, 254, 1); + --text-theme-color: rgba(43, 54, 116, 1); + --text-color-primary: rgba(67, 24, 255, 1); + --line-box: rgba(79, 89, 102, 0.122); + --theme-text-color-primary: #000; + --theme-text-primary: #000; + --theme-text-color-secondary: #666; + --chat-content-bg: #f5f7fc; + --chat-user-content-bg: #e0dfff; + --chat-list-bg: #0302020a; + --chat-wel-bg: rgba(247, 247, 248, 1); + --hover-deep-color: #fff; + --el-bg-color-overlay: #fff; + --el-bg-color: #fff; + --el-fill-color-blank: #fff; + --el-pagination-button-bg-color: rgba(86, 86, 95, 0.2); + --tab-title-bg: #fff; //顶部tab栏背景切换 + --tab-title-color: #595959; //顶部tab栏文字切换 + + // 操作按钮 + --btn-bg: rgba(100, 100, 100, 0.1); + + // 加载动画 + --el-mask-color: rgba(100, 100, 100, 0.2); + // code 标签背景 + --code-bg-color: #ececec; + --code-text-color: var(--el-color-primary); + + // 引用快样式 + --quote-bg-color: #e0dfff; + --quote-text-color: #333; + // 面板背景 + --panel-bg: linear-gradient(135deg, #f5eafe 0%, #e9e6fc 100%); + + // 登录注册页面专用变量 + --login-bg: linear-gradient(135deg, #f5eafe 0%, #e9e6fc 100%); + --login-card-bg: #fff; + --login-card-border: rgba(79, 89, 102, 0.122); + --login-title-color: #000; + --login-subtitle-color: #666; + --login-text-color: #666; + --login-link-color: #5b62ce; + --login-link-hover-bg: rgba(91, 98, 206, 0.1); +} diff --git a/web/src/assets/css/theme-light.styl b/web/src/assets/css/theme-light.styl deleted file mode 100644 index 2ec52562..00000000 --- a/web/src/assets/css/theme-light.styl +++ /dev/null @@ -1,64 +0,0 @@ - - -@import 'font.styl' -:root[data-theme="light"] { - --text-fb:#000; - --text-color: #5b62ce; // 主要的文本颜色 - --normal-color: rgba(43, 54, 116, 1); // 普通颜色 - --theme-textcolor-normal:#5b62ce;; - p, h1, h2, h3, h4, h5, h6, article { - font-family: $font-regular; - } - html, - body, - #app, - .wrapper { - font-family: $font-regular; - } - - --btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce); - --border-active:rgba(134, 140, 255, 1); - --code-btnColor: linear-gradient(88deg, #af61f0 1.44%, #5b62ce); - --card-bg:#fff; - --chat-bg:#fff; - --theme-bg:linear-gradient(88deg, #fff3f3 1.44%, #e7e8ff); - --theme-bg-all:#f5f7fd; - --theme-bg-color: #f5f7fd; - --sign-bg: rgba(244, 247, 254, 1); - --text-theme-color: rgba(43, 54, 116, 1) - --text-color-primary: rgba(67, 24, 255, 1); - --line-box:rgba(79, 89, 102, 0.122); - --theme-text-color-primary: #000; - --theme-text-primary: #000; - --theme-text-color-secondary: #666; - --chat-content-bg:#f5f7fc; - --chat-user-content-bg: #e0dfff; - --chat-list-bg: #0302020a; - --chat-wel-bg:rgba(247, 247, 248, 1); - --hover-deep-color:#fff; - --el-bg-color-overlay: #fff; - --el-bg-color:#fff; - --el-fill-color-blank: #fff; - --el-pagination-button-bg-color: rgba(86,86,95,0.2); - --tab-title-bg:#fff;//顶部tab栏背景切换 - --tab-title-color:#595959;//顶部tab栏文字切换 - - - - // 操作按钮 - --btn-bg: rgba(100, 100, 100, .1); - - // 加载动画 - --el-mask-color: rgba(100, 100, 100, 0.2); - // code 标签背景 - --code-bg-color: #ececec; - --code-text-color: var(--el-color-primary); - - // 引用快样式 - --quote-bg-color: #e0dfff; - --quote-text-color: #333; - // 面板背景 - --panel-bg: linear-gradient(135deg, #f5eafe 0%, #e9e6fc 100%); -} - - diff --git a/web/src/assets/css/video.styl b/web/src/assets/css/video.scss similarity index 89% rename from web/src/assets/css/video.styl rename to web/src/assets/css/video.scss index 21d11a40..2e89b1d4 100644 --- a/web/src/assets/css/video.styl +++ b/web/src/assets/css/video.scss @@ -1,567 +1,565 @@ -// 视频生成页面统一样式 -.page-video { - display: flex; - min-height: 100vh; - background: var(--chat-bg); - - // Element Plus 样式覆盖 - :deep(.el-form-item__label) { - color: var(--text-theme-color); - } - - :deep(.el-textarea) { - --el-input-focus-border-color: var(--el-color-primary); - } - - :deep(.el-textarea__inner) { - background: transparent; - color: var(--text-theme-color); - } - - .el-input__wrapper { - background: transparent; - padding: 5px; - } - - // 左侧参数面板 - .params-panel { - min-width: 320px; - max-width: 320px; - margin: 10px; - padding: 0 15px 20px 15px; - border-radius: 10px; - color: var(--text-theme-color); - font-size: 14px; - overflow: auto; - background: var(--card-bg); - - h2 { - font-weight: bold; - font-size: 20px; - text-align: center; - color: var(--theme-textcolor-normal); - margin-bottom: 20px; - } - - // 隐藏滚动条 - ::-webkit-scrollbar { - width: 0; - height: 0; - background-color: transparent; - } - - // 标签页样式 - .video-type-tabs { - margin-bottom: 20px; - - :deep(.el-tabs__item.is-active) { - color: var(--theme-textcolor-normal); - font-size: 16px; - } - - :deep(.el-tabs__item) { - color: var(--text-theme-color); - font-size: 16px; - } - - :deep(.el-tabs__active-bar) { - background-color: var(--theme-textcolor-normal); - } - - .el-tabs { - --el-tabs-header-height: 45px; - } - } - - // 参数行 - .param-line { - padding: 5px 0; - - &.pt { - padding-top: 10px; - padding-bottom: 10px; - } - - .label { - margin-right: 5px; - position: relative; - top: 1px; - } - - .form-item-inner { - display: flex; - align-items: center; - - .el-icon { - margin-left: 10px; - } - } - } - - // 表单项样式 - .el-form { - .el-form-item__label { - color: var(--text-theme-color); - } - - .el-input, .el-slider { - width: 100%; - } - - .el-select { - width: 100%; - --el-select-input-focus-border-color: var(--el-color-primary); - --el-input-focus-border-color: var(--el-color-primary); - - .el-input__wrapper { - background: var(--chat-bg); - } - - .el-input__inner { - color: var(--text-theme-color); - } - } - } - - // 网格内容项 - .grid-content { - background: var(--card-bg); - border-radius: 8px; - padding: 8px 14px; - display: flex; - cursor: pointer; - margin-bottom: 10px; - border: 1px solid var(--chat-bg); - - &:hover { - border: 1px solid var(--theme-border-hover); - } - - &.active { - border: 1px solid var(--theme-border-hover); - } - - .icon { - width: 20px; - height: 20px; - margin-bottom: 5px; - - &.proportion { - width: 20px; - height: 20px; - } - } - - .texts { - margin-left: 5px; - margin-top: 2px; - color: var(--text-theme-color); - } - } - - // 运镜控制 - .camera-control { - padding: 10px; - border-radius: 4px; - background: var(--card-bg); - - :deep(.el-form-item:last-child) { - margin-bottom: 0 !important; - } - } - - // 生成按钮 - .generate-btn { - .iconfont { - margin-right: 5px; - } - } - - // 项目组样式 - .item-group { - display: flex; - align-items: center; - margin-bottom: 15px; - - .label { - margin-right: 10px; - } - } - - // 图片模式切换样式 - .image-mode-toggle { - display: flex; - align-items: center; - justify-content: space-between; - - .label { - margin-right: 10px; - color: var(--text-theme-color); - } - } - - // 过渡动画 - .slide-fade-enter-active { - transition: all 0.3s ease-out; - } - - .slide-fade-leave-active { - transition: all 0.3s ease-in; - } - - .slide-fade-enter-from { - transform: translateY(-10px); - opacity: 0; - } - - .slide-fade-leave-to { - transform: translateY(-10px); - opacity: 0; - } - } - - - // KeLing 参数面板特有样式 - .params-container { - // 任务类型标签页 - .task-type-tabs { - margin-bottom: 20px; - - .text { - margin-bottom: 10px; - color: #6b778c; - font-size: 15px; - } - } - - // 图片上传样式 - .img-inline { - display: flex; - flex-wrap: wrap; - - .img-uploader { - text-align: center; - position: relative; - - :deep(.el-upload) { - border: 1px dashed var(--el-border-color); - border-radius: 6px; - cursor: pointer; - position: relative; - overflow: hidden; - width: 120px; - height: 120px; - transition: var(--el-transition-duration-fast); - margin-bottom: 20px; - - &:hover { - border-color: var(--el-color-primary); - } - } - - .removeimg { - position: absolute; - right: -5px; - top: -5px; - z-index: 10; - cursor: pointer; - color: #f56c6c; - font-size: 20px; - } - - } - - .btn-swap { - .icon-exchange { - color: var(--text-theme-color); - cursor: pointer; - font-size: 20px; - } - } - } - - // 提交按钮 - .submit-btn { - display: flex; - margin: 20px 0; - - .el-button { - width: 100%; - } - } - - // 算力信息 - .text-info { - width: 100%; - padding: 10px 0; - - .el-tag { - margin-right: 10px; - } - } - } - - // 右侧主要内容区域 - .main-content { - padding: 1.5rem; - flex: 1; - background: var(--chat-bg); - color: var(--text-theme-color); - overflow-x: hidden; - - // 作品标题栏 - .works-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - - .h-title { - color: var(--text-theme-color); - margin: 0; - } - - // .filter-buttons { - // .el-button-group { - // .el-button { - // --el-button-bg-color: var(--card-bg); - // --el-button-border-color: var(--chat-bg); - // --el-button-text-color: var(--text-theme-color); - // --el-button-hover-bg-color: var(--theme-border-hover); - // --el-button-hover-border-color: var(--theme-border-hover); - // --el-button-active-bg-color: var(--el-color-primary); - // --el-button-active-border-color: var(--el-color-primary); - - // &.is-type-primary { - // --el-button-bg-color: var(--el-color-primary); - // --el-button-border-color: var(--el-color-primary); - // --el-button-text-color: #ffffff; - // } - // } - // } - // } - } - - // 任务列表 - .video-list { - .list-box { - padding: 0; - - .item { - display: flex; - flex-flow: row; - align-items: center; - min-height: 100px; - padding: 10px 15px; - border-radius: 10px; - cursor: pointer; - margin-bottom: 20px; - background: var(--card-bg); - - .left { - .container { - width: 160px; - position: relative; - overflow: hidden; - display: flex; - justify-content: center; - align-items: center; - - .video { - width: 160px; - height: 120px; - border-radius: 5px; - background-color: var(--el-fill-color-light); - } - - .el-image { - width: 160px; - height: 90px; - border-radius: 5px; - } - - .duration { - position: absolute; - bottom: 0; - right: 0; - background-color: rgba(14, 8, 8, 0.7); - padding: 0 3px; - font-family: 'Input Sans'; - font-size: 14px; - font-weight: 700; - border-radius: 0.125rem; - } - - .play { - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 50%; - border: none; - border-radius: 5px; - background: rgba(100, 100, 100, 0.3); - cursor: pointer; - color: var(--text-theme-color); - opacity: 0; - transform: translate(-50%, 0px); - transition: opacity 0.3s ease 0s; - } - - &:hover { - .play { - opacity: 1; - } - } - } - } - - .center { - width: 100%; - display: flex; - justify-content: center; - align-items: flex-start; - flex-flow: column; - padding: 0 20px; - - .prompt, .failed { - padding: 0; - font-size: 16px; - max-height: 80px; - line-height: 28px; - overflow: hidden; - text-overflow: ellipsis; - } - - .prompt { - color: var(--text-fb); - cursor: text; - } - - .failed { - color: #E4696B; - } - - .pb-2 { - padding-bottom: 8px; - - .el-tag { - margin-right: 4px; - margin-bottom: 4px; - } - } - } - - .right { - display: flex; - justify-content: right; - min-width: 200px; - font-size: 14px; - padding: 0; - - .tools { - display: flex; - justify-content: left; - align-items: center; - flex-flow: row; - height: 90px; - - .btn-publish { - padding: 2px 10px; - - .text { - margin-right: 10px; - } - } - - .btn-icon { - background: none; - padding: 6px; - transition: background 0.6s ease 0s; - color: #919191; - - &:hover { - color: var(--el-color-primary); - } - - .downloading { - width: 16px; - } - } - } - } - - .right-error { - display: flex; - justify-content: center; - align-items: center; - } - } - } - - // 分页 - .pagination { - margin-top: 20px; - display: flex; - justify-content: center; - } - } - } - - // 通用按钮样式 - .btn { - margin-right: 10px; - border: none; - border-radius: 5px; - padding: 5px 10px; - cursor: pointer; - color: var(--theme-text-color-primary); - background-color: var(--btn-bg); - - &:hover { - opacity: 0.7; - } - } - - // 无数据样式 - .no-data { - text-align: center; - padding: 40px; - color: var(--text-theme-color); - } -} - -// 响应式设计 -@media (max-width: 768px) { - .page-video { - flex-direction: column; - - .params-panel { - min-width: 100%; - max-width: 100%; - margin: 10px 0; - } - - .main-content { - padding: 1rem; - - .video-list .list-box .item { - .left .container { - width: 120px; - - .video, .el-image { - width: 120px; - } - } - - .center { - padding: 0 10px; - } - - .right { - min-width: 120px; - } - } - } - } +// 视频生成页面统一样式 +.page-video { + display: flex; + min-height: 100vh; + background: var(--chat-bg); + + // Element Plus 样式覆盖 + :deep(.el-form-item__label) { + color: var(--text-theme-color); + } + + :deep(.el-textarea) { + --el-input-focus-border-color: var(--el-color-primary); + } + + :deep(.el-textarea__inner) { + background: transparent; + color: var(--text-theme-color); + } + + .el-input__wrapper { + background: transparent; + padding: 5px; + } + + // 左侧参数面板 + .params-panel { + min-width: 320px; + max-width: 320px; + margin: 10px; + padding: 0 15px 20px 15px; + border-radius: 10px; + color: var(--text-theme-color); + font-size: 14px; + overflow: auto; + background: var(--card-bg); + + h2 { + font-weight: bold; + font-size: 20px; + text-align: center; + color: var(--theme-textcolor-normal); + margin-bottom: 20px; + } + + // 隐藏滚动条 + ::-webkit-scrollbar { + width: 0; + height: 0; + background-color: transparent; + } + + // 标签页样式 + .video-type-tabs { + margin-bottom: 20px; + + :deep(.el-tabs__item.is-active) { + color: var(--theme-textcolor-normal); + font-size: 16px; + } + + :deep(.el-tabs__item) { + color: var(--text-theme-color); + font-size: 16px; + } + + :deep(.el-tabs__active-bar) { + background-color: var(--theme-textcolor-normal); + } + + .el-tabs { + --el-tabs-header-height: 45px; + } + } + + // 参数行 + .param-line { + padding: 5px 0; + + &.pt { + padding-top: 10px; + padding-bottom: 10px; + } + + .label { + margin-right: 5px; + position: relative; + top: 1px; + } + + .form-item-inner { + display: flex; + align-items: center; + + .el-icon { + margin-left: 10px; + } + } + } + + // 表单项样式 + .el-form { + .el-form-item__label { + color: var(--text-theme-color); + } + + .el-input, .el-slider { + width: 100%; + } + + .el-select { + width: 100%; + --el-select-input-focus-border-color: var(--el-color-primary); + --el-input-focus-border-color: var(--el-color-primary); + + .el-input__wrapper { + background: var(--chat-bg); + } + + .el-input__inner { + color: var(--text-theme-color); + } + } + } + + // 网格内容项 + .grid-content { + background: var(--card-bg); + border-radius: 8px; + padding: 8px 14px; + display: flex; + cursor: pointer; + margin-bottom: 10px; + border: 1px solid var(--chat-bg); + + &:hover { + border: 1px solid var(--theme-border-hover); + } + + &.active { + border: 1px solid var(--theme-border-hover); + } + + .icon { + width: 20px; + height: 20px; + margin-bottom: 5px; + + &.proportion { + width: 20px; + height: 20px; + } + } + + .texts { + margin-left: 5px; + margin-top: 2px; + color: var(--text-theme-color); + } + } + + // 运镜控制 + .camera-control { + padding: 10px; + border-radius: 4px; + background: var(--card-bg); + + :deep(.el-form-item:last-child) { + margin-bottom: 0 !important; + } + } + + // 生成按钮 + .generate-btn { + .iconfont { + margin-right: 5px; + } + } + + // 项目组样式 + .item-group { + display: flex; + align-items: center; + margin-bottom: 15px; + + .label { + margin-right: 10px; + } + } + + // 图片模式切换样式 + .image-mode-toggle { + display: flex; + align-items: center; + justify-content: space-between; + + .label { + margin-right: 10px; + color: var(--text-theme-color); + } + } + + // 过渡动画 + .slide-fade-enter-active { + transition: all 0.3s ease-out; + } + + .slide-fade-leave-active { + transition: all 0.3s ease-in; + } + + .slide-fade-enter-from { + transform: translateY(-10px); + opacity: 0; + } + + .slide-fade-leave-to { + transform: translateY(-10px); + opacity: 0; + } + } + + // KeLing 参数面板特有样式 + .params-container { + // 任务类型标签页 + .task-type-tabs { + margin-bottom: 20px; + + .text { + margin-bottom: 10px; + color: #6b778c; + font-size: 15px; + } + } + + // 图片上传样式 + .img-inline { + display: flex; + flex-wrap: wrap; + + .img-uploader { + text-align: center; + position: relative; + + :deep(.el-upload) { + border: 1px dashed var(--el-border-color); + border-radius: 6px; + cursor: pointer; + position: relative; + overflow: hidden; + width: 120px; + height: 120px; + transition: var(--el-transition-duration-fast); + margin-bottom: 20px; + + &:hover { + border-color: var(--el-color-primary); + } + } + + .removeimg { + position: absolute; + right: -5px; + top: -5px; + z-index: 10; + cursor: pointer; + color: #f56c6c; + font-size: 20px; + } + } + + .btn-swap { + .icon-exchange { + color: var(--text-theme-color); + cursor: pointer; + font-size: 20px; + } + } + } + + // 提交按钮 + .submit-btn { + display: flex; + margin: 20px 0; + + .el-button { + width: 100%; + } + } + + // 算力信息 + .text-info { + width: 100%; + padding: 10px 0; + + .el-tag { + margin-right: 10px; + } + } + } + + // 右侧主要内容区域 + .main-content { + padding: 1.5rem; + flex: 1; + background: var(--chat-bg); + color: var(--text-theme-color); + overflow-x: hidden; + + // 作品标题栏 + .works-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + + .h-title { + color: var(--text-theme-color); + margin: 0; + } + + // .filter-buttons { + // .el-button-group { + // .el-button { + // --el-button-bg-color: var(--card-bg); + // --el-button-border-color: var(--chat-bg); + // --el-button-text-color: var(--text-theme-color); + // --el-button-hover-bg-color: var(--theme-border-hover); + // --el-button-hover-border-color: var(--theme-border-hover); + // --el-button-active-bg-color: var(--el-color-primary); + // --el-button-active-border-color: var(--el-color-primary); + + // &.is-type-primary { + // --el-button-bg-color: var(--el-color-primary); + // --el-button-border-color: var(--el-color-primary); + // --el-button-text-color: #ffffff; + // } + // } + // } + // } + } + + // 任务列表 + .video-list { + .list-box { + padding: 0; + + .item { + display: flex; + flex-flow: row; + align-items: center; + min-height: 100px; + padding: 10px 15px; + border-radius: 10px; + cursor: pointer; + margin-bottom: 20px; + background: var(--card-bg); + + .left { + .container { + width: 160px; + position: relative; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + + .video { + width: 160px; + height: 120px; + border-radius: 5px; + background-color: var(--el-fill-color-light); + } + + .el-image { + width: 160px; + height: 90px; + border-radius: 5px; + } + + .duration { + position: absolute; + bottom: 0; + right: 0; + background-color: rgba(14, 8, 8, 0.7); + padding: 0 3px; + font-family: 'Input Sans'; + font-size: 14px; + font-weight: 700; + border-radius: 0.125rem; + } + + .play { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 50%; + border: none; + border-radius: 5px; + background: rgba(100, 100, 100, 0.3); + cursor: pointer; + color: var(--text-theme-color); + opacity: 0; + transform: translate(-50%, 0px); + transition: opacity 0.3s ease 0s; + } + + &:hover { + .play { + opacity: 1; + } + } + } + } + + .center { + width: 100%; + display: flex; + justify-content: center; + align-items: flex-start; + flex-flow: column; + padding: 0 20px; + + .prompt, .failed { + padding: 0; + font-size: 16px; + max-height: 80px; + line-height: 28px; + overflow: hidden; + text-overflow: ellipsis; + } + + .prompt { + color: var(--text-fb); + cursor: text; + } + + .failed { + color: #e4696b; + } + + .pb-2 { + padding-bottom: 8px; + + .el-tag { + margin-right: 4px; + margin-bottom: 4px; + } + } + } + + .right { + display: flex; + justify-content: right; + min-width: 200px; + font-size: 14px; + padding: 0; + + .tools { + display: flex; + justify-content: left; + align-items: center; + flex-flow: row; + height: 90px; + + .btn-publish { + padding: 2px 10px; + + .text { + margin-right: 10px; + } + } + + .btn-icon { + background: none; + padding: 6px; + transition: background 0.6s ease 0s; + color: #919191; + + &:hover { + color: var(--el-color-primary); + } + + .downloading { + width: 16px; + } + } + } + } + + .right-error { + display: flex; + justify-content: center; + align-items: center; + } + } + } + + // 分页 + .pagination { + margin-top: 20px; + display: flex; + justify-content: center; + } + } + } + + // 通用按钮样式 + .btn { + margin-right: 10px; + border: none; + border-radius: 5px; + padding: 5px 10px; + cursor: pointer; + color: var(--theme-text-color-primary); + background-color: var(--btn-bg); + + &:hover { + opacity: 0.7; + } + } + + // 无数据样式 + .no-data { + text-align: center; + padding: 40px; + color: var(--text-theme-color); + } +} + +// 响应式设计 +@media (max-width: 768px) { + .page-video { + flex-direction: column; + + .params-panel { + min-width: 100%; + max-width: 100%; + margin: 10px 0; + } + + .main-content { + padding: 1rem; + + .video-list .list-box .item { + .left .container { + width: 120px; + + .video, .el-image { + width: 120px; + } + } + + .center { + padding: 0 10px; + } + + .right { + min-width: 120px; + } + } + } + } } \ No newline at end of file diff --git a/web/src/assets/css/waterfall-list.scss b/web/src/assets/css/waterfall-list.scss new file mode 100644 index 00000000..c3739756 --- /dev/null +++ b/web/src/assets/css/waterfall-list.scss @@ -0,0 +1,139 @@ +@use 'running-job-list.scss' as *; + +.job-list-box { + .finish-job-list { + #waterfall { + display: flex; + justify-content: center; + padding-top: 20px; + flex-flow: column; + + .job-item { + width: 100%; + height: 100%; + border: 1px solid #666666; + padding: 6px; + overflow: hidden; + border-radius: 6px; + transition: all 0.3s ease; /* 添加过渡效果 */ + position: relative; + + .opt { + .opt-line { + margin: 6px 0; + + ul { + display: flex; + flex-flow: row; + + li { + margin-right: 6px; + + a { + padding: 3px 0; + width: 40px; + text-align: center; + border-radius: 5px; + display: block; + cursor: pointer; + background-color: #4e5058; + color: #ffffff; + + &:hover { + background-color: #6d6f78; + } + } + } + + .show-prompt { + font-size: 20px; + cursor: pointer; + } + } + } + } + + .remove { + display: none; + position: absolute; + right: 10px; + top: 10px; + } + + &:hover { + .remove { + display: block; + } + } + } + + .animate { + &:hover { + box-shadow: 0 0 10px var(--shadow-color); /* 添加阴影效果 */ + transform: translateY(-10px); /* 向上移动10像素 */ + } + } + } + } + + .el-image { + width: 100%; + height: 100%; + overflow: visible; + + .el-image-viewer__wrapper { + img { + width: auto; + height: auto; + } + } + + .image-slot { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + min-height: 220px; + color: #ffffff; + + .err-msg-container { + overflow: hidden; + word-break: break-all; + padding: 15px; + .title { + font-size: 20px; + text-align: center; + font-weight: bold; + color: #f56c6c; + margin-bottom: 30px; + } + + .opt { + display: flex; + justify-content: center; + } + } + .iconfont { + font-size: 50px; + margin-bottom: 10px; + } + } + } + + .el-image.upscale { + img { + height: 310px; + } + + .image-slot { + height: 310px; + } + + .el-image-viewer__wrapper { + img { + width: auto; + height: auto; + } + } + } +} diff --git a/web/src/assets/css/waterfall-list.styl b/web/src/assets/css/waterfall-list.styl deleted file mode 100644 index 4c9ccd0d..00000000 --- a/web/src/assets/css/waterfall-list.styl +++ /dev/null @@ -1,146 +0,0 @@ -.job-list-box { - - @import "running-job-list.styl" - - .finish-job-list { - #waterfall { - display flex - justify-content center - padding-top 20px - flex-flow column - - - .job-item { - width 100% - height 100% - border 1px solid #666666 - padding 6px - overflow hidden - border-radius 6px - transition: all 0.3s ease; /* 添加过渡效果 */ - position relative - - .opt { - .opt-line { - margin 6px 0 - - ul { - display flex - flex-flow row - - li { - margin-right 6px - - a { - padding 3px 0 - width 40px - text-align center - border-radius 5px - display block - cursor pointer - background-color #4E5058 - color #ffffff - - &:hover { - background-color #6D6F78 - } - } - } - - .show-prompt { - font-size 20px - cursor pointer - } - } - } - } - - - .remove { - display none - position absolute - right 10px - top 10px - } - - &:hover { - .remove { - display block - } - } - } - - - .animate { - &:hover { - box-shadow: 0 0 10px var(--shadow-color); /* 添加阴影效果 */ - transform: translateY(-10px); /* 向上移动10像素 */ - } - } - } - } - - - .el-image { - width 100% - height 100% - overflow visible - - .el-image-viewer__wrapper { - img { - width auto - height auto - } - } - - .image-slot { - display flex - flex-flow column - justify-content center - align-items center - min-height 220px - color #ffffff - - .err-msg-container { - overflow hidden - word-break break-all - padding 15px - .title { - font-size 20px - text-align center - font-weight bold - color #f56c6c - margin-bottom 30px - } - - .opt { - display flex - justify-content center - } - } - .iconfont { - font-size 50px - margin-bottom 10px - } - - - } - } - - .el-image.upscale { - img { - height 310px - } - - .image-slot { - height 310px - } - - .el-image-viewer__wrapper { - img { - width auto - height auto - } - } - } -} \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.css b/web/src/assets/iconfont/iconfont.css index 73489687..f7f73129 100644 --- a/web/src/assets/iconfont/iconfont.css +++ b/web/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4125778 */ - src: url('iconfont.woff2?t=1752831319382') format('woff2'), - url('iconfont.woff?t=1752831319382') format('woff'), - url('iconfont.ttf?t=1752831319382') format('truetype'); + src: url('iconfont.woff2?t=1756954977612') format('woff2'), + url('iconfont.woff?t=1756954977612') format('woff'), + url('iconfont.ttf?t=1756954977612') format('truetype'); } .iconfont { @@ -13,6 +13,46 @@ -moz-osx-font-smoothing: grayscale; } +.icon-cube:before { + content: "\e72c"; +} + +.icon-tag:before { + content: "\e657"; +} + +.icon-tencent:before { + content: "\e655"; +} + +.icon-baidu:before { + content: "\e656"; +} + +.icon-moderation:before { + content: "\e6c6"; +} + +.icon-back-bold:before { + content: "\e654"; +} + +.icon-back-circle:before { + content: "\e653"; +} + +.icon-back:before { + content: "\e6c8"; +} + +.icon-openai:before { + content: "\e652"; +} + +.icon-power:before { + content: "\e651"; +} + .icon-jimeng2:before { content: "\eabc"; } diff --git a/web/src/assets/iconfont/iconfont.js b/web/src/assets/iconfont/iconfont.js index ffd1ad96..f93ed5a1 100644 --- a/web/src/assets/iconfont/iconfont.js +++ b/web/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,v(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function v(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}p()}})(window); \ No newline at end of file +window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,v(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function v(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}p()}})(window); \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.json b/web/src/assets/iconfont/iconfont.json index 4085727c..d3660737 100644 --- a/web/src/assets/iconfont/iconfont.json +++ b/web/src/assets/iconfont/iconfont.json @@ -5,6 +5,76 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "544492", + "name": "cube", + "font_class": "cube", + "unicode": "e72c", + "unicode_decimal": 59180 + }, + { + "icon_id": "5072110", + "name": "tag", + "font_class": "tag", + "unicode": "e657", + "unicode_decimal": 58967 + }, + { + "icon_id": "3547761", + "name": "tencent", + "font_class": "tencent", + "unicode": "e655", + "unicode_decimal": 58965 + }, + { + "icon_id": "26267540", + "name": "baidu", + "font_class": "baidu", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "31975090", + "name": "Content Moderation", + "font_class": "moderation", + "unicode": "e6c6", + "unicode_decimal": 59078 + }, + { + "icon_id": "27025075", + "name": "返回", + "font_class": "back-bold", + "unicode": "e654", + "unicode_decimal": 58964 + }, + { + "icon_id": "6237605", + "name": "返回", + "font_class": "back-circle", + "unicode": "e653", + "unicode_decimal": 58963 + }, + { + "icon_id": "18952076", + "name": "返回", + "font_class": "back", + "unicode": "e6c8", + "unicode_decimal": 59080 + }, + { + "icon_id": "33483666", + "name": "openai", + "font_class": "openai", + "unicode": "e652", + "unicode_decimal": 58962 + }, + { + "icon_id": "25677845", + "name": "算力", + "font_class": "power", + "unicode": "e651", + "unicode_decimal": 58961 + }, { "icon_id": "42693930", "name": "即梦AI-02", @@ -434,7 +504,7 @@ }, { "icon_id": "39584617", - "name": "MidJourney-copy", + "name": "MidJourney", "font_class": "MidJourney", "unicode": "e60e", "unicode_decimal": 58894 diff --git a/web/src/assets/iconfont/iconfont.ttf b/web/src/assets/iconfont/iconfont.ttf index e76c0a11..2be98cba 100644 Binary files a/web/src/assets/iconfont/iconfont.ttf and b/web/src/assets/iconfont/iconfont.ttf differ diff --git a/web/src/assets/iconfont/iconfont.woff b/web/src/assets/iconfont/iconfont.woff index 5511edbd..ff153c0d 100644 Binary files a/web/src/assets/iconfont/iconfont.woff and b/web/src/assets/iconfont/iconfont.woff differ diff --git a/web/src/assets/iconfont/iconfont.woff2 b/web/src/assets/iconfont/iconfont.woff2 index 9ca0b337..474ebe33 100644 Binary files a/web/src/assets/iconfont/iconfont.woff2 and b/web/src/assets/iconfont/iconfont.woff2 differ diff --git a/web/src/components/AccountBg.vue b/web/src/components/AccountBg.vue index 312a4c30..282a14e5 100644 --- a/web/src/components/AccountBg.vue +++ b/web/src/components/AccountBg.vue @@ -1,7 +1,12 @@ - diff --git a/web/src/components/BackTop.vue b/web/src/components/BackTop.vue index dd63466d..accef7b8 100644 --- a/web/src/components/BackTop.vue +++ b/web/src/components/BackTop.vue @@ -6,7 +6,7 @@ :style="{ bottom: bottom + 'px', right: right + 'px', - backgroundColor: bgColor + backgroundColor: bgColor, }" > @@ -14,56 +14,56 @@ - \ No newline at end of file + diff --git a/web/src/components/BindMobile.vue b/web/src/components/BindMobile.vue index 3fe45fd5..18aedd9f 100644 --- a/web/src/components/BindMobile.vue +++ b/web/src/components/BindMobile.vue @@ -1,110 +1,79 @@ - \ No newline at end of file + diff --git a/web/src/components/Calling.vue b/web/src/components/Calling.vue index e7b6c724..3197a584 100644 --- a/web/src/components/Calling.vue +++ b/web/src/components/Calling.vue @@ -1,6 +1,6 @@ - \ No newline at end of file + diff --git a/web/src/components/Captcha.vue b/web/src/components/Captcha.vue index 0d769a23..aeb59438 100644 --- a/web/src/components/Captcha.vue +++ b/web/src/components/Captcha.vue @@ -1,13 +1,19 @@ @@ -25,13 +25,13 @@ defineProps({ type: String, default: '任务正在执行', }, -}); +}) - \ No newline at end of file + diff --git a/web/src/components/ui/ItemsInput.vue b/web/src/components/ui/ItemsInput.vue index 42339645..9a298996 100644 --- a/web/src/components/ui/ItemsInput.vue +++ b/web/src/components/ui/ItemsInput.vue @@ -2,48 +2,48 @@
{{ tag }} - - + 新增 - + + 新增
- \ No newline at end of file + diff --git a/web/src/components/ui/RippleButton.vue b/web/src/components/ui/RippleButton.vue index c21b5985..84ff20da 100644 --- a/web/src/components/ui/RippleButton.vue +++ b/web/src/components/ui/RippleButton.vue @@ -1,35 +1,35 @@ - \ No newline at end of file + diff --git a/web/src/main.js b/web/src/main.js index 10015992..78839f63 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -5,21 +5,26 @@ // * @Author yangjian102621@163.com // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -import { createApp } from "vue"; -import ElementPlus from "element-plus"; -import "element-plus/dist/index.css"; -import "@/assets/iconfont/iconfont.css"; -import "vant/lib/index.css"; -import App from "./App.vue"; -import { useThemeStore } from "@/store/theme"; -import { createPinia } from "pinia"; -import "animate.css/animate.min.css"; -import "@/assets/css/tailwind.css"; +import '@/assets/css/tailwind.css' +import '@/assets/iconfont/iconfont.css' +import { useThemeStore } from '@/store/theme' +import 'animate.css/animate.min.css' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import { createPinia } from 'pinia' +import 'vant/lib/index.css' +import { createApp } from 'vue' +import App from './App.vue' +import '@/assets/css/common.scss' +import '@/assets/css/theme-dark.scss' +import '@/assets/css/theme-light.scss' +import { router } from '@/router' import { ActionSheet, Badge, Button, + Calendar, Cell, CellGroup, Circle, @@ -48,12 +53,17 @@ import { Overlay, Picker, Popup, + PullRefresh, + Radio, + RadioGroup, Row, Search, ShareSheet, Slider, Sticky, + Swipe, SwipeCell, + SwipeItem, Switch, Tab, Tabbar, @@ -61,64 +71,75 @@ import { Tabs, Tag, TextEllipsis, + Toast, Uploader, -} from "vant"; -import { router } from "@/router"; -import "@/assets/css/theme-dark.styl"; -import "@/assets/css/theme-light.styl"; -import "@/assets/css/common.styl"; +} from 'vant' -const pinia = createPinia(); -const themeStore = useThemeStore(pinia); // 使用 theme store +const pinia = createPinia() +const themeStore = useThemeStore(pinia) // 使用 theme store // 设置初始主题 -document.documentElement.setAttribute("data-theme", themeStore.theme); +document.documentElement.setAttribute('data-theme', themeStore.theme) -const app = createApp(App); -app.use(createPinia()); -app.use(ConfigProvider); -app.use(Tabbar); -app.use(TabbarItem); -app.use(NavBar); -app.use(Search); -app.use(Cell); -app.use(Image); -app.use(TextEllipsis); -app.use(Notify); -app.use(Picker); -app.use(Popup); -app.use(List); -app.use(Form); -app.use(Field); -app.use(CellGroup); -app.use(Button); -app.use(DropdownMenu); -app.use(Icon); -app.use(DropdownItem); -app.use(Sticky); -app.use(SwipeCell); -app.use(Dialog); -app.use(ShareSheet); -app.use(Switch); -app.use(Uploader); -app.use(Tag); -app.use(Overlay); -app.use(Col); -app.use(Row); -app.use(Slider); -app.use(Badge); -app.use(Collapse); -app.use(CollapseItem); -app.use(Grid); -app.use(GridItem); -app.use(Empty); -app.use(Circle); -app.use(Loading); -app.use(Lazyload); -app.use(ImagePreview); -app.use(Tab); -app.use(Tabs); -app.use(Divider); -app.use(NoticeBar); -app.use(ActionSheet); -app.use(router).use(ElementPlus).mount("#app"); +// 同时设置初始 dark 类 +if (themeStore.theme === 'dark') { + document.documentElement.classList.add('dark') +} else { + document.documentElement.classList.remove('dark') +} + +const app = createApp(App) +app.use(createPinia()) +app.use(ConfigProvider) +app.use(Tabbar) +app.use(TabbarItem) +app.use(NavBar) +app.use(Search) +app.use(Cell) +app.use(Image) +app.use(TextEllipsis) +app.use(Notify) +app.use(Picker) +app.use(Popup) +app.use(Radio) +app.use(RadioGroup) +app.use(List) +app.use(Form) +app.use(Field) +app.use(CellGroup) +app.use(Button) +app.use(DropdownMenu) +app.use(Icon) +app.use(DropdownItem) +app.use(Sticky) +app.use(Swipe) +app.use(SwipeItem) +app.use(SwipeCell) +app.use(Dialog) +app.use(ShareSheet) +app.use(Switch) +app.use(Uploader) +app.use(Tag) +app.use(Overlay) +app.use(Col) +app.use(Row) +app.use(Slider) +app.use(Badge) +app.use(Collapse) +app.use(CollapseItem) +app.use(Grid) +app.use(GridItem) +app.use(Empty) +app.use(Circle) +app.use(Loading) +app.use(Lazyload) +app.use(ImagePreview) +app.use(Tab) +app.use(Tabs) +app.use(Divider) +app.use(NoticeBar) +app.use(ActionSheet) +app.use(PullRefresh) +app.use(Calendar) +app.use(Toast) +app.use(router).use(ElementPlus).mount('#app') diff --git a/web/src/router.js b/web/src/router.js index a4396afd..f8f2bebf 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -123,25 +123,18 @@ const routes = [ meta: { title: '导出会话记录' }, component: () => import('@/views/ChatExport.vue'), }, + { name: 'login', path: '/login', meta: { title: '用户登录' }, component: () => import('@/views/Login.vue'), }, - - { - name: 'login-callback', - path: '/login/callback', - meta: { title: '用户登录' }, - component: () => import('@/views/LoginCallback.vue'), - }, { name: 'register', path: '/register', - meta: { title: '用户注册' }, - component: () => import('@/views/Register.vue'), + component: () => import('@/views/Login.vue'), }, { name: 'resetpassword', @@ -155,12 +148,7 @@ const routes = [ meta: { title: '控制台登录' }, component: () => import('@/views/admin/Login.vue'), }, - { - path: '/payReturn', - name: 'pay-return', - meta: { title: '支付回调' }, - component: () => import('@/views/PayReturn.vue'), - }, + { name: 'admin', path: '/admin', @@ -175,10 +163,94 @@ const routes = [ component: () => import('@/views/admin/Dashboard.vue'), }, { - path: '/admin/system', - name: 'admin-system', - meta: { title: '系统设置' }, - component: () => import('@/views/admin/SysConfig.vue'), + path: '/admin/config/basic', + name: 'admin-config-basic', + meta: { title: '基础配置' }, + component: () => import('@/views/admin/settings/BasicConfig.vue'), + }, + { + path: '/admin/config/power', + name: 'admin-config-power', + meta: { title: '算力配置' }, + component: () => import('@/views/admin/settings/PowerConfig.vue'), + }, + { + path: '/admin/config/payment', + name: 'admin-config-payment', + meta: { title: '支付配置' }, + component: () => import('@/views/admin/settings/PaymentConfig.vue'), + }, + { + path: '/admin/config/storage', + name: 'admin-config-storage', + meta: { title: '存储配置' }, + component: () => import('@/views/admin/settings/StorageConfig.vue'), + }, + { + path: '/admin/config/sms', + name: 'admin-config-sms', + meta: { title: '短信配置' }, + component: () => import('@/views/admin/settings/SmsConfig.vue'), + }, + { + path: '/admin/config/smtp', + name: 'admin-config-smtp', + meta: { title: '邮件配置' }, + component: () => import('@/views/admin/settings/SmtpConfig.vue'), + }, + { + path: '/admin/config/plugin', + name: 'admin-config-plugin', + meta: { title: '插件配置' }, + component: () => import('@/views/admin/settings/PluginConfig.vue'), + }, + { + path: '/admin/moderation/config', + name: 'admin-config-moderation', + meta: { title: '文本审查配置' }, + component: () => import('@/views/admin/moderation/ModerationConfig.vue'), + }, + { + path: '/admin/moderation/list', + name: 'admin-moderation-list', + meta: { title: '文本审核记录' }, + component: () => import('@/views/admin/moderation/ModerationList.vue'), + }, + { + path: '/admin/config/markmap', + name: 'admin-config-markmap', + meta: { title: '思维导图配置' }, + component: () => import('@/views/admin/settings/MarkMapConfig.vue'), + }, + { + path: '/admin/config/notice', + name: 'admin-config-notice', + meta: { title: '公告配置' }, + component: () => import('@/views/admin/settings/NoticeConfig.vue'), + }, + { + path: '/admin/config/agreement', + name: 'admin-config-agreement', + meta: { title: '用户协议' }, + component: () => import('@/views/admin/settings/AgreementConfig.vue'), + }, + { + path: '/admin/config/privacy', + name: 'admin-config-privacy', + meta: { title: '隐私声明' }, + component: () => import('@/views/admin/settings/PrivacyConfig.vue'), + }, + { + path: '/admin/config/menu', + name: 'admin-config-menu', + meta: { title: '菜单配置' }, + component: () => import('@/views/admin/settings/MenuConfig.vue'), + }, + { + path: '/admin/config/license', + name: 'admin-config-license', + meta: { title: '授权激活' }, + component: () => import('@/views/admin/settings/LicenseConfig.vue'), }, { path: '/admin/user', @@ -268,7 +340,7 @@ const routes = [ path: '/admin/jimeng/config', name: 'admin-jimeng-config', meta: { title: '即梦设置' }, - component: () => import('@/views/admin/jimeng/JimengSetting.vue'), + component: () => import('@/views/admin/jimeng/JimengConfig.vue'), }, { path: '/admin/powerLog', @@ -285,12 +357,6 @@ const routes = [ ], }, - { - name: 'mobile-login', - path: '/mobile/login', - meta: { title: '用户登录' }, - component: () => import('@/views/mobile/Login.vue'), - }, { name: 'mobile', path: '/mobile', @@ -304,21 +370,37 @@ const routes = [ component: () => import('@/views/mobile/Index.vue'), }, { + meta: { title: 'AI对话' }, path: '/mobile/chat', name: 'mobile-chat', component: () => import('@/views/mobile/ChatList.vue'), }, { - path: '/mobile/image', - name: 'mobile-image', - component: () => import('@/views/mobile/Image.vue'), + meta: { title: '创作中心' }, + path: '/mobile/create', + name: 'mobile-create', + component: () => import('@/views/mobile/Create.vue'), }, { + meta: { title: '发现' }, + path: '/mobile/discover', + name: 'mobile-discover', + component: () => import('@/views/mobile/Discover.vue'), + }, + { + meta: { title: '个人中心' }, path: '/mobile/profile', name: 'mobile-profile', component: () => import('@/views/mobile/Profile.vue'), }, { + meta: { title: '会员充值' }, + path: '/mobile/member', + name: 'mobile-member', + component: () => import('@/views/mobile/Member.vue'), + }, + { + meta: { title: '作品展示' }, path: '/mobile/imgWall', name: 'mobile-img-wall', component: () => import('@/views/mobile/pages/ImgWall.vue'), @@ -328,16 +410,50 @@ const routes = [ name: 'mobile-chat-session', component: () => import('@/views/mobile/ChatSession.vue'), }, + { - path: '/mobile/chat/export', - name: 'mobile-chat-export', - component: () => import('@/views/mobile/ChatExport.vue'), - }, - { + meta: { title: '应用中心' }, path: '/mobile/apps', name: 'mobile-apps', component: () => import('@/views/mobile/Apps.vue'), }, + // 新增的功能页面路由 + { + meta: { title: '消费日志' }, + path: '/mobile/power-log', + name: 'mobile-power-log', + component: () => import('@/views/mobile/PowerLog.vue'), + }, + { + meta: { title: '推广计划' }, + path: '/mobile/invite', + name: 'mobile-invite', + component: () => import('@/views/mobile/Invite.vue'), + }, + { + meta: { title: '设置' }, + path: '/mobile/settings', + name: 'mobile-settings', + component: () => import('@/views/mobile/Settings.vue'), + }, + { + meta: { title: 'Suno音乐创作' }, + path: '/mobile/suno', + name: 'mobile-suno', + component: () => import('@/views/mobile/SunoCreate.vue'), + }, + { + meta: { title: '视频生成' }, + path: '/mobile/video', + name: 'mobile-video', + component: () => import('@/views/mobile/VideoCreate.vue'), + }, + { + meta: { title: '即梦AI' }, + path: '/mobile/jimeng', + name: 'mobile-jimeng', + component: () => import('@/views/mobile/JimengCreate.vue'), + }, ], }, @@ -345,7 +461,7 @@ const routes = [ name: 'test', path: '/test', meta: { title: '测试页面' }, - component: () => import('@/views/Test.vue'), + component: () => import('@/views/test/Test.vue'), }, { diff --git a/web/src/store/jimeng.js b/web/src/store/jimeng.js index 8536990f..0a9ca489 100644 --- a/web/src/store/jimeng.js +++ b/web/src/store/jimeng.js @@ -12,7 +12,7 @@ import { httpDownload, httpGet, httpPost } from '@/utils/http' import { replaceImg, substr } from '@/utils/libs' import { ElMessageBox } from 'element-plus' import { defineStore } from 'pinia' -import { computed, nextTick, reactive, ref } from 'vue' +import { computed, reactive, ref } from 'vue' export const useJimengStore = defineStore('jimeng', () => { // 当前激活的功能分类和具体功能 @@ -137,13 +137,13 @@ export const useJimengStore = defineStore('jimeng', () => { }) const imageEditParams = reactive({ - image_urls: '', + image_input: '', scale: 0.5, seed: -1, }) const imageEffectsParams = reactive({ - image_input1: '', + image_input: '', template_id: '', size: '1328x1328', }) @@ -154,7 +154,7 @@ export const useJimengStore = defineStore('jimeng', () => { }) const imageToVideoParams = reactive({ - image_urls: [], + image_input: [], aspect_ratio: '16:9', seed: -1, }) @@ -374,7 +374,7 @@ export const useJimengStore = defineStore('jimeng', () => { break case 'image_to_image': Object.assign(requestData, { - image_input: imageToImageParams.image_input, + image_input: imageToImageParams.image_input[0], width: parseInt(imageToImageParams.size.split('x')[0]), height: parseInt(imageToImageParams.size.split('x')[1]), gpen: imageToImageParams.gpen, @@ -386,17 +386,18 @@ export const useJimengStore = defineStore('jimeng', () => { break case 'image_edit': Object.assign(requestData, { - image_urls: imageEditParams.image_urls, + image_input: imageEditParams.image_input[0], scale: imageEditParams.scale, seed: imageEditParams.seed, }) break case 'image_effects': Object.assign(requestData, { - image_input: imageEffectsParams.image_input1, + image_input: imageEffectsParams.image_input[0], template_id: imageEffectsParams.template_id, width: parseInt(imageEffectsParams.size.split('x')[0]), height: parseInt(imageEffectsParams.size.split('x')[1]), + prompt: imageEffectsParams.prompt, }) break case 'text_to_video': @@ -407,7 +408,7 @@ export const useJimengStore = defineStore('jimeng', () => { break case 'image_to_video': Object.assign(requestData, { - image_urls: imageToVideoParams.image_urls, + image_urls: imageToVideoParams.image_input, aspect_ratio: imageToVideoParams.aspect_ratio, seed: imageToVideoParams.seed, }) @@ -430,7 +431,7 @@ export const useJimengStore = defineStore('jimeng', () => { const downloadFile = async (item) => { const url = replaceImg(item.video_url || item.img_url) - const downloadURL = `${import.meta.env.VITE_API_HOST}/api/download?url=${url}` + const downloadURL = `/api/download?url=${url}` const urlObj = new URL(url) const fileName = urlObj.pathname.split('/').pop() @@ -497,62 +498,6 @@ export const useJimengStore = defineStore('jimeng', () => { showDialog.value = true } - // 画同款功能 - const drawSame = (item) => { - // 联动功能开关 - if (item.type === 'text_to_image' || item.type === 'image_to_image') { - activeCategory.value = 'image_generation' - useImageInput.value = item.type === 'image_to_image' - } else if (item.type === 'text_to_video' || item.type === 'image_to_video') { - activeCategory.value = 'video_generation' - useImageInput.value = item.type === 'image_to_video' - } else if (item.type === 'image_edit') { - activeCategory.value = 'image_editing' - } else if (item.type === 'image_effects') { - activeCategory.value = 'image_effects' - } - switchFunction(item.type) - nextTick(() => { - currentPrompt.value = item.prompt - }) - if (item.type === 'text_to_image') { - if (item.width && item.height) { - textToImageParams.size = `${item.width}x${item.height}` - } - if (item.scale) textToImageParams.scale = item.scale - if (item.seed) textToImageParams.seed = item.seed - if (item.use_pre_llm !== undefined) textToImageParams.use_pre_llm = item.use_pre_llm - } else if (item.type === 'image_to_image') { - if (item.image_input) imageToImageParams.image_input = item.image_input - if (item.width && item.height) { - imageToImageParams.size = `${item.width}x${item.height}` - } - if (item.gpen) imageToImageParams.gpen = item.gpen - if (item.skin) imageToImageParams.skin = item.skin - if (item.skin_unifi) imageToImageParams.skin_unifi = item.skin_unifi - if (item.gen_mode) imageToImageParams.gen_mode = item.gen_mode - if (item.seed) imageToImageParams.seed = item.seed - } else if (item.type === 'image_edit') { - if (item.image_urls) imageEditParams.image_urls = item.image_urls - if (item.scale) imageEditParams.scale = item.scale - if (item.seed) imageEditParams.seed = item.seed - } else if (item.type === 'image_effects') { - if (item.image_input1) imageEffectsParams.image_input1 = item.image_input1 - if (item.template_id) imageEffectsParams.template_id = item.template_id - if (item.width && item.height) { - imageEffectsParams.size = `${item.width}x${item.height}` - } - } else if (item.type === 'text_to_video') { - if (item.aspect_ratio) textToVideoParams.aspect_ratio = item.aspect_ratio - if (item.seed) textToVideoParams.seed = item.seed - } else if (item.type === 'image_to_video') { - if (item.image_urls) imageToVideoParams.image_urls = item.image_urls - if (item.aspect_ratio) imageToVideoParams.aspect_ratio = item.aspect_ratio - if (item.seed) imageToVideoParams.seed = item.seed - } - showMessageOK('已填入全部参数,可直接生成同款') - } - // 页面卸载时清理轮询 const cleanup = () => { stopPolling() @@ -615,14 +560,12 @@ export const useJimengStore = defineStore('jimeng', () => { removeJob, playVideo, cleanup, - drawSame, // 工具函数 substr, replaceImg, } }) - export const imageSizeOptions = [ { label: '1:1 (1328x1328)', value: '1328x1328' }, { label: '3:2 (1584x1056)', value: '1584x1056' }, @@ -640,3 +583,68 @@ export const videoAspectRatioOptions = [ { label: '16:9 (横版)', value: '16:9' }, { label: '9:16 (竖版)', value: '9:16' }, ] + +export const imageEffectsTemplateOptions = [ + { + label: '毛毡3D拍立得风格', + value: 'felt_3d_polaroid', + preview: '/images/jimeng/templates/felt_3d_polaroid.png', + }, + { label: '像素世界风', value: 'my_world', preview: '/images/jimeng/templates/my_world.png' }, + { + label: '像素世界-万物通用版', + value: 'my_world_universal', + preview: '/images/jimeng/templates/my_world_universal.png', + }, + { + label: '盲盒玩偶风', + value: 'plastic_bubble_figure', + preview: '/images/jimeng/templates/plastic_bubble_figure.png', + }, + { + label: '塑料泡罩人偶-文字卡头版', + value: 'plastic_bubble_figure_cartoon_text', + preview: '/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png', + }, + { + label: '毛绒玩偶风', + value: 'furry_dream_doll', + preview: '/images/jimeng/templates/furry_dream_doll.png', + }, + { + label: '迷你世界玩偶风', + value: 'micro_landscape_mini_world', + preview: '/images/jimeng/templates/micro_landscape_mini_world.png', + }, + { + label: '微型景观小世界-职业版', + value: 'micro_landscape_mini_world_professional', + preview: '/images/jimeng/templates/micro_landscape_mini_world_professional.png', + }, + { + label: '亚克力挂饰', + value: 'acrylic_ornaments', + preview: '/images/jimeng/templates/acrylic_ornaments.png', + }, + { + label: '毛毡钥匙扣', + value: 'felt_keychain', + preview: '/images/jimeng/templates/felt_keychain.png', + }, + { + label: 'Lofi 像素人物小卡', + value: 'lofi_pixel_character_mini_card', + preview: '/images/jimeng/templates/lofi_pixel_character_mini_card.png', + }, + { + label: '天使形象手办', + value: 'angel_figurine', + preview: '/images/jimeng/templates/angel_figurine.png', + }, + { + label: '躺在毛茸茸肚皮里', + value: 'lying_in_fluffy_belly', + preview: '/images/jimeng/templates/lying_in_fluffy_belly.png', + }, + { label: '玻璃球', value: 'glass_ball', preview: '/images/jimeng/templates/glass_ball.png' }, +] diff --git a/web/src/store/mobile/jimeng.js b/web/src/store/mobile/jimeng.js new file mode 100644 index 00000000..300ce5cf --- /dev/null +++ b/web/src/store/mobile/jimeng.js @@ -0,0 +1,525 @@ +import { showMessageError, showMessageOK } from '@/utils/dialog' +import { httpDownload, httpGet, httpPost } from '@/utils/http' +import { replaceImg } from '@/utils/libs' +import { defineStore } from 'pinia' +import { showConfirmDialog } from 'vant' +import { computed, reactive, ref, watch } from 'vue' + +export const useJimengStore = defineStore('mobile-jimeng', () => { + // 响应式数据 + const activeCategory = ref('image_generation') + const useImageInput = ref(false) + const submitting = ref(false) + const listLoading = ref(false) + const listFinished = ref(false) + const currentList = ref([]) + const showMediaDialog = ref(false) + const currentMediaUrl = ref('') + const currentPrompt = ref('') + const page = ref(1) + const pageSize = ref(10) + const total = ref(0) + const currentPowerCost = ref(0) + const taskPulling = ref(true) + const tastPullHandler = ref(null) + + // 新增:算力配置 + const powerConfig = ref({ + text_to_image: 20, + image_to_image: 30, + image_edit: 25, + image_effects: 15, + text_to_video: 100, + image_to_video: 120, + }) + + // 功能分类 + const categories = ref([ + { key: 'image_generation', name: '图像生成' }, + { key: 'image_editing', name: '图像编辑' }, + { key: 'image_effects', name: '图像特效' }, + { key: 'video_generation', name: '视频生成' }, + ]) + + // 选项数据 + const imageSizeOptions = [ + { label: '1:1 (1328x1328)', value: '1328x1328' }, + { label: '3:2 (1584x1056)', value: '1584x1056' }, + { label: '2:3 (1056x1584)', value: '1056x1584' }, + { label: '4:3 (1472x1104)', value: '1472x1104' }, + { label: '3:4 (1104x1472)', value: '1104x1472' }, + { label: '16:9 (1664x936)', value: '1664x936' }, + { label: '9:16 (936x1664)', value: '936x1664' }, + { label: '21:9 (2016x864)', value: '2016x864' }, + { label: '9:21 (864x2016)', value: '864x2016' }, + ] + + const videoAspectRatioOptions = [ + { label: '1:1 (正方形)', value: '1:1' }, + { label: '16:9 (横版)', value: '16:9' }, + { label: '9:16 (竖版)', value: '9:16' }, + ] + + const imageEffectsTemplateOptions = [ + { + label: '毛毡3D拍立得风格', + value: 'felt_3d_polaroid', + preview: '/images/jimeng/templates/felt_3d_polaroid.png', + }, + { label: '像素世界风', value: 'my_world', preview: '/images/jimeng/templates/my_world.png' }, + { + label: '像素世界-万物通用版', + value: 'my_world_universal', + preview: '/images/jimeng/templates/my_world_universal.png', + }, + { + label: '盲盒玩偶风', + value: 'plastic_bubble_figure', + preview: '/images/jimeng/templates/plastic_bubble_figure.png', + }, + { + label: '塑料泡罩人偶-文字卡头版', + value: 'plastic_bubble_figure_cartoon_text', + preview: '/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png', + }, + { + label: '毛绒玩偶风', + value: 'furry_dream_doll', + preview: '/images/jimeng/templates/furry_dream_doll.png', + }, + { + label: '迷你世界玩偶风', + value: 'micro_landscape_mini_world', + preview: '/images/jimeng/templates/micro_landscape_mini_world.png', + }, + { + label: '微型景观小世界-职业版', + value: 'micro_landscape_mini_world_professional', + preview: '/images/jimeng/templates/micro_landscape_mini_world_professional.png', + }, + { + label: '亚克力挂饰', + value: 'acrylic_ornaments', + preview: '/images/jimeng/templates/acrylic_ornaments.png', + }, + { + label: '毛毡钥匙扣', + value: 'felt_keychain', + preview: '/images/jimeng/templates/felt_keychain.png', + }, + { + label: 'Lofi 像素人物小卡', + value: 'lofi_pixel_character_mini_card', + preview: '/images/jimeng/templates/lofi_pixel_character_mini_card.png', + }, + { + label: '天使形象手办', + value: 'angel_figurine', + preview: '/images/jimeng/templates/angel_figurine.png', + }, + { + label: '躺在毛茸茸肚皮里', + value: 'lying_in_fluffy_belly', + preview: '/images/jimeng/templates/lying_in_fluffy_belly.png', + }, + { label: '玻璃球', value: 'glass_ball', preview: '/images/jimeng/templates/glass_ball.png' }, + ] + + // 功能参数 + // 各功能的参数 + const textToImageParams = reactive({ + size: '1328x1328', + scale: 2.5, + seed: -1, + use_pre_llm: true, + }) + + const imageToImageParams = reactive({ + image_input: '', + size: '1328x1328', + gpen: 0.4, + skin: 0.3, + skin_unifi: 0, + gen_mode: 'creative', + seed: -1, + }) + + const imageEditParams = reactive({ + image_input: '', + scale: 0.5, + seed: -1, + }) + + const imageEffectsParams = reactive({ + image_input: '', + template_id: '', + size: '1328x1328', + }) + + const textToVideoParams = reactive({ + aspect_ratio: '16:9', + seed: -1, + }) + + const imageToVideoParams = reactive({ + image_urls: [], + aspect_ratio: '16:9', + seed: -1, + }) + + // 计算属性 + const activeFunction = computed(() => { + if (activeCategory.value === 'image_generation') { + return useImageInput.value ? 'image_to_image' : 'text_to_image' + } else if (activeCategory.value === 'image_editing') { + return 'image_edit' + } else if (activeCategory.value === 'image_effects') { + return 'image_effects' + } else if (activeCategory.value === 'video_generation') { + return useImageInput.value ? 'image_to_video' : 'text_to_video' + } + return 'text_to_image' + }) + + // 新增:动态计算当前算力消耗 + const updateCurrentPowerCost = () => { + const functionKey = activeFunction.value + currentPowerCost.value = powerConfig.value[functionKey] || 10 + } + + // 监听任务类型变化,自动更新算力 + watch( + [activeCategory, useImageInput], + () => { + updateCurrentPowerCost() + }, + { immediate: true } + ) + + // Actions + const getCategoryIcon = (category) => { + const iconMap = { + image_generation: 'iconfont icon-image', + image_editing: 'iconfont icon-edit', + image_effects: 'iconfont icon-chuangzuo', + video_generation: 'iconfont icon-video', + } + return iconMap[category] || 'iconfont icon-image' + } + + const switchCategory = (key) => { + activeCategory.value = key + useImageInput.value = false + } + + // 新增:获取算力配置 + const fetchPowerConfig = async () => { + try { + const res = await httpGet('/api/jimeng/power-config') + if (res.data) { + powerConfig.value = res.data + updateCurrentPowerCost() // 更新当前算力消耗 + } + } catch (error) { + console.error('获取算力配置失败:', error) + } + } + + const submitTask = () => { + if (!currentPrompt.value.trim()) { + showMessageError('请输入提示词') + return + } + + submitting.value = true + let requestData = { task_type: activeFunction.value, prompt: currentPrompt.value } + // 根据功能类型添加相应参数 + switch (activeFunction.value) { + case 'text_to_image': + Object.assign(requestData, { + width: parseInt(textToImageParams.size.split('x')[0]), + height: parseInt(textToImageParams.size.split('x')[1]), + scale: textToImageParams.scale, + seed: textToImageParams.seed, + use_pre_llm: textToImageParams.use_pre_llm, + }) + break + case 'image_to_image': + Object.assign(requestData, { + image_input: imageToImageParams.image_input[0], + width: parseInt(imageToImageParams.size.split('x')[0]), + height: parseInt(imageToImageParams.size.split('x')[1]), + gpen: imageToImageParams.gpen, + skin: imageToImageParams.skin, + skin_unifi: imageToImageParams.skin_unifi, + gen_mode: imageToImageParams.gen_mode, + seed: imageToImageParams.seed, + }) + break + case 'image_edit': + Object.assign(requestData, { + image_input: imageEditParams.image_input[0], + scale: imageEditParams.scale, + seed: imageEditParams.seed, + }) + break + case 'image_effects': + Object.assign(requestData, { + image_input: imageEffectsParams.image_input[0], + template_id: imageEffectsParams.template_id, + width: parseInt(imageEffectsParams.size.split('x')[0]), + height: parseInt(imageEffectsParams.size.split('x')[1]), + prompt: imageEffectsParams.prompt, + }) + break + case 'text_to_video': + Object.assign(requestData, { + aspect_ratio: textToVideoParams.aspect_ratio, + seed: textToVideoParams.seed, + }) + break + case 'image_to_video': + Object.assign(requestData, { + image_urls: imageToVideoParams.image_input, + aspect_ratio: imageToVideoParams.aspect_ratio, + seed: imageToVideoParams.seed, + }) + break + } + + return httpPost('/api/jimeng/task', requestData) + .then(() => { + fetchData(1) + taskPulling.value = true + showMessageOK('创建任务成功') + currentPrompt.value = '' + }) + .catch((e) => { + showMessageError('创建任务失败:' + e.message) + }) + .finally(() => { + submitting.value = false + }) + } + + const fetchData = (_page) => { + if (_page) { + page.value = _page + } + listLoading.value = true + + return httpPost('/api/jimeng/jobs', { page: page.value, page_size: pageSize.value }) + .then((res) => { + total.value = res.data.total + let needPull = false + const items = [] + if (res.data.items) { + for (let v of res.data.items) { + if (v.status === 'in_queue' || v.status === 'generating') { + needPull = true + } + items.push(v) + } + } + listLoading.value = false + taskPulling.value = needPull + + if (page.value === 1) { + currentList.value = items + } else { + currentList.value.push(...items) + } + + if (items.length < pageSize.value) { + listFinished.value = true + } + }) + .catch((e) => { + listLoading.value = false + showMessageError('获取作品列表失败:' + e.message) + }) + } + + const loadMore = () => { + page.value++ + fetchData() + } + + const playMedia = (item) => { + currentMediaUrl.value = item.img_url || item.video_url + showMediaDialog.value = true + } + + const downloadFile = async (item) => { + const url = replaceImg(item.video_url || item.img_url) + const downloadURL = `/api/download?url=${url}` + const urlObj = new URL(url) + const fileName = urlObj.pathname.split('/').pop() + + item.downloading = true + + try { + const response = await httpDownload(downloadURL) + const blob = new Blob([response.data]) + const link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(link.href) + item.downloading = false + } catch (error) { + showMessageError('下载失败') + item.downloading = false + } + } + + const retryTask = (id) => { + return httpGet('/api/jimeng/retry', { id }) + .then(() => { + showMessageOK('重试任务成功') + fetchData(1) + }) + .catch((e) => { + showMessageError('重试任务失败:' + e.message) + }) + } + + const removeJob = async (item) => { + return showConfirmDialog({ + title: '确认删除', + message: '此操作将会删除任务相关文件,继续操作吗?', + confirmButtonText: '确认删除', + cancelButtonText: '取消', + }) + .then(() => { + return httpGet('/api/jimeng/remove', { id: item.id }) + .then(() => { + showMessageOK('任务删除成功') + fetchData(1) + }) + .catch((e) => { + showMessageError('任务删除失败:' + e.message) + }) + }) + .catch(() => {}) + } + + const getFunctionName = (type) => { + const nameMap = { + text_to_image: '文生图', + image_to_image: '图生图', + image_edit: '图像编辑', + image_effects: '图像特效', + text_to_video: '文生视频', + image_to_video: '图生视频', + } + return nameMap[type] || type + } + + const getTaskType = (type) => { + return type.includes('video') ? 'warning' : 'primary' + } + + const startTaskPolling = () => { + tastPullHandler.value = setInterval(() => { + if (taskPulling.value) { + fetchData(1) + } + }, 5000) + } + + const stopTaskPolling = () => { + if (tastPullHandler.value) { + clearInterval(tastPullHandler.value) + } + } + + const closeMediaDialog = () => { + showMediaDialog.value = false + currentMediaUrl.value = '' + } + + // 新增:复制提示词功能 + const copyPrompt = (prompt) => { + navigator.clipboard + .writeText(prompt) + .then(() => { + showMessageOK('提示词已复制') + }) + .catch(() => { + showMessageError('复制失败') + }) + } + + // 新增:复制错误信息功能 + const copyErrorMsg = (msg) => { + navigator.clipboard + .writeText(msg) + .then(() => { + showMessageOK('错误信息已复制') + }) + .catch(() => { + showMessageError('复制失败') + }) + } + + // 新增:初始化方法 + const init = async () => { + await fetchPowerConfig() + } + + return { + // State + activeCategory, + useImageInput, + submitting, + listLoading, + listFinished, + currentList, + showMediaDialog, + currentMediaUrl, + currentPrompt, + page, + pageSize, + total, + currentPowerCost, + taskPulling, + tastPullHandler, + categories, + imageSizeOptions, + videoAspectRatioOptions, + imageEffectsTemplateOptions, + textToImageParams, + imageToImageParams, + imageEditParams, + imageEffectsParams, + textToVideoParams, + imageToVideoParams, + powerConfig, + + // Computed + activeFunction, + + // Actions + getCategoryIcon, + switchCategory, + submitTask, + fetchData, + loadMore, + playMedia, + downloadFile, + retryTask, + removeJob, + getFunctionName, + getTaskType, + startTaskPolling, + stopTaskPolling, + closeMediaDialog, + fetchPowerConfig, + copyPrompt, + copyErrorMsg, + init, + } +}) diff --git a/web/src/store/mobile/suno.js b/web/src/store/mobile/suno.js new file mode 100644 index 00000000..35352a95 --- /dev/null +++ b/web/src/store/mobile/suno.js @@ -0,0 +1,366 @@ +import { getSystemInfo } from '@/store/cache' +import { closeLoading, showLoading, showMessageError, showMessageOK } from '@/utils/dialog' +import { httpDownload, httpGet, httpPost } from '@/utils/http' +import { replaceImg } from '@/utils/libs' +import { defineStore } from 'pinia' +import { showConfirmDialog } from 'vant' +import { reactive, ref } from 'vue' + +export const useSunoStore = defineStore('suno', () => { + // 状态 + const custom = ref(false) + const data = reactive({ + model: 'chirp-auk', + tags: '', + lyrics: '', + prompt: '', + title: '', + instrumental: false, + ref_task_id: '', + extend_secs: 0, + ref_song_id: '', + type: 1, + }) + const loading = ref(false) + const list = ref([]) + const listLoading = ref(false) + const listFinished = ref(false) + const btnText = ref('开始创作') + const refSong = ref(null) + const showModelPicker = ref(false) + const showPlayer = ref(false) + const showDeleteModal = ref(false) + const currentAudio = ref('') + const uploadFiles = ref([]) + const uploadRef = ref(null) + const isGenerating = ref(false) + const deleting = ref(false) + const models = ref([ + { label: 'v3.0', value: 'chirp-v3-0' }, + { label: 'v3.5', value: 'chirp-v3-5' }, + { label: 'v4.0', value: 'chirp-v4' }, + { label: 'v4.5', value: 'chirp-auk' }, + ]) + const tags = ref([ + { label: '女声', value: 'female vocals' }, + { label: '男声', value: 'male vocals' }, + { label: '流行', value: 'pop' }, + { label: '摇滚', value: 'rock' }, + { label: '电音', value: 'electronic' }, + { label: '钢琴', value: 'piano' }, + { label: '吉他', value: 'guitar' }, + { label: '嘻哈', value: 'hip hop' }, + ]) + const page = ref(1) + const pageSize = ref(10) + const total = ref(0) + const taskPulling = ref(true) + const tastPullHandler = ref(null) + const sunoPowerCost = ref(0) + + onMounted(() => { + getSystemInfo().then((res) => { + sunoPowerCost.value = res.data.suno_power + }) + }) + + // 方法 + const onModelSelect = (selectedModel) => { + data.model = selectedModel.value + } + const selectTag = (tag) => { + if (data.tags.length + tag.value.length >= 119) { + showMessageError('标签长度超出限制') + return + } + const currentTags = data.tags.split(',').filter((t) => t.trim()) + if (!currentTags.includes(tag.value)) { + currentTags.push(tag.value) + data.tags = currentTags.join(',') + } + } + const createLyric = () => { + if (data.lyrics === '') { + showMessageError('请输入歌词描述') + return + } + isGenerating.value = true + httpPost('/api/prompt/lyric', { prompt: data.lyrics }) + .then((res) => { + const lines = res.data.split('\n') + data.title = lines.shift().replace(/\*/g, '') + lines.shift() + data.lyrics = lines.join('\n') + showMessageOK('歌词生成成功') + }) + .catch((e) => { + showMessageError('歌词生成失败:' + e.message) + }) + .finally(() => { + isGenerating.value = false + }) + } + const handleFileChange = (file) => { + uploadFiles.value = [file] + if (file.status === 'ready') { + uploadAudio(file) + } + } + const beforeUpload = (file) => { + const isLt10M = file.size / 1024 / 1024 < 10 + if (!isLt10M) { + showMessageError('文件大小不能超过 10MB!') + return false + } + return true + } + const uploadAudio = (file) => { + const formData = new FormData() + formData.append('file', file.raw, file.name) + showLoading('正在上传文件...') + httpPost('/api/upload', formData) + .then((res) => { + httpPost('/api/suno/create', { + audio_url: res.data.url, + title: res.data.name, + type: 4, + }) + .then(() => { + fetchData(1) + showMessageOK('歌曲上传成功') + removeRefSong() + uploadFiles.value = [] + if (uploadRef.value) { + uploadRef.value.clearFiles() + } + }) + .catch((e) => { + showMessageError('歌曲上传失败:' + e.message) + }) + .finally(() => { + closeLoading() + }) + }) + .catch((e) => { + showMessageError('文件上传失败:' + e.message) + }) + .finally(() => { + closeLoading() + }) + } + const create = () => { + data.type = custom.value ? 2 : 1 + data.ref_task_id = refSong.value ? refSong.value.task_id : '' + data.ref_song_id = refSong.value ? refSong.value.song_id : '' + data.extend_secs = refSong.value ? refSong.value.extend_secs : 0 + if (refSong.value) { + if (data.extend_secs > refSong.value.duration) { + showMessageError('续写开始时间不能超过原歌曲长度') + return + } + } else if (custom.value) { + if (data.lyrics === '') { + showMessageError('请输入歌词') + return + } + if (data.title === '') { + showMessageError('请输入歌曲标题') + return + } + } else { + if (data.prompt === '') { + showMessageError('请输入歌曲描述') + return + } + } + loading.value = true + httpPost('/api/suno/create', data) + .then(() => { + fetchData(1) + taskPulling.value = true + showMessageOK('创建任务成功') + }) + .catch((e) => { + showMessageError('创建任务失败:' + e.message) + }) + .finally(() => { + loading.value = false + }) + } + const fetchData = (_page) => { + if (_page) { + page.value = _page + } + listLoading.value = true + httpGet('/api/suno/list', { page: page.value, page_size: pageSize.value }) + .then((res) => { + total.value = res.data.total + let needPull = false + const items = [] + for (let v of res.data.items) { + if (v.progress === 100) { + v.major_model_version = v['raw_data']['major_model_version'] + } + if (v.progress === 0 || v.progress === 102) { + needPull = true + } + items.push(v) + } + listLoading.value = false + taskPulling.value = needPull + if (page.value === 1) { + list.value = items + } else { + list.value.push(...items) + } + if (items.length < pageSize.value) { + listFinished.value = true + } + }) + .catch((e) => { + listLoading.value = false + showMessageError('获取作品列表失败:' + e.message) + }) + } + const loadMore = () => { + if (!listFinished.value && !listLoading.value) { + page.value++ + fetchData() + } + } + const refreshFirstPage = () => { + const currentPage = page.value + const currentList = [...list.value] + httpGet('/api/suno/list', { page: 1, page_size: pageSize.value }) + .then((res) => { + let needPull = false + const firstPageItems = [] + for (let v of res.data.items) { + if (v.progress === 100) { + v.major_model_version = v['raw_data']['major_model_version'] + } + if (v.progress === 0 || v.progress === 102) { + needPull = true + } + firstPageItems.push(v) + } + taskPulling.value = needPull + if (currentPage === 1) { + list.value = firstPageItems + } else { + const otherPagesData = currentList.slice(pageSize.value) + list.value = [...firstPageItems, ...otherPagesData] + } + }) + .catch((e) => { + console.error('刷新第一页数据失败:', e) + }) + } + const play = (item) => { + currentAudio.value = item.audio_url + showPlayer.value = true + } + const download = (item) => { + const url = replaceImg(item.audio_url) + const downloadURL = `/api/download?url=${url}` + const urlObj = new URL(url) + const fileName = urlObj.pathname.split('/').pop() + item.downloading = true + httpDownload(downloadURL) + .then((response) => { + const blob = new Blob([response.data]) + const link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(link.href) + item.downloading = false + }) + .catch(() => { + showMessageError('下载失败') + item.downloading = false + }) + .finally(() => { + item.downloading = false + }) + } + + const removeJob = (item) => { + showConfirmDialog({ + title: '确认删除', + message: '此操作将会删除任务相关文件,继续操作吗?', + confirmButtonText: '确认删除', + cancelButtonText: '取消', + }).then(() => { + httpGet('/api/suno/remove', { id: item.id }) + .then(() => { + showMessageOK('任务删除成功') + fetchData(1) + }) + .catch(() => { + showMessageError('任务删除失败') + }) + }) + } + + const extend = (item) => { + refSong.value = item + refSong.value.extend_secs = item.duration + data.title = item.title + custom.value = true + btnText.value = '续写歌曲' + window.scrollTo({ top: 0, behavior: 'smooth' }) + } + const removeRefSong = () => { + refSong.value = null + btnText.value = '开始创作' + } + + // 副作用(定时轮询、滚动监听)建议在页面层处理,store 只暴露方法 + + return { + // 状态 + custom, + data, + loading, + list, + listLoading, + listFinished, + btnText, + refSong, + showModelPicker, + showPlayer, + showDeleteModal, + currentAudio, + uploadFiles, + uploadRef, + isGenerating, + deleting, + models, + tags, + page, + pageSize, + total, + taskPulling, + tastPullHandler, + sunoPowerCost, + // 方法 + onModelSelect, + selectTag, + createLyric, + handleFileChange, + beforeUpload, + uploadAudio, + create, + fetchData, + loadMore, + refreshFirstPage, + play, + download, + removeJob, + extend, + removeRefSong, + } +}) diff --git a/web/src/store/mobile/video.js b/web/src/store/mobile/video.js new file mode 100644 index 00000000..4e3e13c9 --- /dev/null +++ b/web/src/store/mobile/video.js @@ -0,0 +1,397 @@ +import { defineStore } from 'pinia' +import { ref, reactive, watch } from 'vue' +import { httpGet, httpPost } from '@/utils/http' +import { showMessageOK, showMessageError, showLoading, closeLoading } from '@/utils/dialog' +import { getSystemInfo } from '@/store/cache' + +export const useVideoStore = defineStore('video', () => { + // 状态 + const activeVideoType = ref('luma') + const loading = ref(false) + const generating = ref(false) + const isGenerating = ref(false) + const listLoading = ref(false) + const listFinished = ref(false) + const currentList = ref([]) + const showVideoDialog = ref(false) + const currentVideoUrl = ref('') + + // Luma 参数 + const lumaParams = reactive({ + prompt: '', + image: '', + image_tail: '', + loop: false, + expand_prompt: false, + }) + const lumaUseImageMode = ref(false) + const lumaStartImage = ref([]) + const lumaEndImage = ref([]) + + // KeLing 参数 + const kelingParams = reactive({ + aspect_ratio: '16:9', + model: 'kling-v1-6', + duration: '5', + mode: 'std', + cfg_scale: 0.5, + prompt: '', + negative_prompt: '', + image: '', + image_tail: '', + camera_control: { + type: '', + config: { + horizontal: 0, + vertical: 0, + pan: 0, + tilt: 0, + roll: 0, + zoom: 0, + }, + }, + }) + const kelingUseImageMode = ref(false) + const kelingStartImage = ref([]) + const kelingEndImage = ref([]) + + // 选项数据 + const aspectRatioOptions = ['16:9', '9:16', '1:1', '4:3'] + const modelOptions = [ + { label: '可灵 1.6', value: 'kling-v1-6' }, + { label: '可灵 1.5', value: 'kling-v1-5' }, + { label: '可灵 1.0', value: 'kling-v1' }, + ] + const durationOptions = ['5', '10'] + const modeOptions = ['std', 'pro'] + const cameraControlOptions = [ + '', + 'simple', + 'down_back', + 'forward_up', + 'right_turn_forward', + 'left_turn_forward', + ] + const getCameraControlLabel = (option) => { + const labelMap = { + '': '请选择', + simple: '简单运镜', + down_back: '下移拉远', + forward_up: '推进上移', + right_turn_forward: '右旋推进', + left_turn_forward: '左旋推进', + } + return labelMap[option] || option + } + + // 页面数据 + const page = ref(1) + const pageSize = ref(10) + const total = ref(0) + const lumaPowerCost = ref(0) + const kelingPowerCost = ref(0) + const taskPulling = ref(true) + const keLingPowers = ref({}) + + // 监听器:当可灵参数变化时更新算力 + watch( + () => [kelingParams.model, kelingParams.mode, kelingParams.duration], + () => { + updateModelPower() + }, + { deep: true } + ) + + // 方法 + const updateModelPower = () => { + // 根据模型、模式、时长计算算力消耗 + const key = `${kelingParams.model}_${kelingParams.mode}_${kelingParams.duration}` + kelingPowerCost.value = keLingPowers.value[key] || 10 + } + watch( + () => [kelingParams.model, kelingParams.mode, kelingParams.duration], + () => { + updateModelPower() + }, + { deep: true } + ) + + // 监听器:当可灵参数变化时更新算力 + watch( + () => [kelingParams.model, kelingParams.mode, kelingParams.duration], + () => { + updateModelPower() + }, + { deep: true } + ) + + const switchVideoType = (type) => { + activeVideoType.value = type + } + const handleLumaStartImageUpload = (e) => { + if (e.target.files[0]) { + uploadLumaStartImage({ file: e.target.files[0], name: e.target.files[0].name }) + } + } + const handleLumaEndImageUpload = (e) => { + if (e.target.files[0]) { + uploadLumaEndImage({ file: e.target.files[0], name: e.target.files[0].name }) + } + } + const handleKelingStartImageUpload = (e) => { + if (e.target.files[0]) { + uploadKelingStartImage({ file: e.target.files[0], name: e.target.files[0].name }) + } + } + const handleKelingEndImageUpload = (e) => { + if (e.target.files[0]) { + uploadKelingEndImage({ file: e.target.files[0], name: e.target.files[0].name }) + } + } + + const generatePrompt = async () => { + if (isGenerating.value) return + + const prompt = activeVideoType.value === 'luma' ? lumaParams.prompt : kelingParams.prompt + if (!prompt) { + return showMessageError('请输入原始提示词') + } + + isGenerating.value = true + try { + const res = await httpPost('/api/prompt/video', { prompt }) + if (activeVideoType.value === 'luma') { + lumaParams.prompt = res.data + } else { + kelingParams.prompt = res.data + } + } catch (error) { + showMessageError('生成提示词失败:' + error.message) + } finally { + isGenerating.value = false + } + } + const toggleLumaImageMode = () => { + if (!lumaUseImageMode.value) { + lumaParams.image = '' + lumaParams.image_tail = '' + lumaStartImage.value = [] + lumaEndImage.value = [] + } + } + const toggleKelingImageMode = () => { + if (!kelingUseImageMode.value) { + kelingParams.image = '' + kelingParams.image_tail = '' + kelingStartImage.value = [] + kelingEndImage.value = [] + } + } + const uploadLumaStartImage = (file) => { + uploadImage(file, (url) => { + lumaParams.image = url + }) + } + const uploadLumaEndImage = (file) => { + uploadImage(file, (url) => { + lumaParams.image_tail = url + }) + } + const uploadKelingStartImage = (file) => { + uploadImage(file, (url) => { + kelingParams.image = url + }) + } + const uploadKelingEndImage = (file) => { + uploadImage(file, (url) => { + kelingParams.image_tail = url + }) + } + const uploadImage = (file, callback) => { + const formData = new FormData() + formData.append('file', file.file, file.name) + showLoading('正在上传图片...') + httpPost('/api/upload', formData) + .then((res) => { + callback(res.data.url) + showMessageOK('图片上传成功') + }) + .catch((e) => { + showMessageError('图片上传失败:' + e.message) + }) + .finally(() => { + closeLoading() + }) + } + const createLumaVideo = () => { + if (!lumaParams.prompt.trim()) { + showMessageError('请输入视频提示词') + return + } + generating.value = true + const params = { + ...lumaParams, + task_type: 'luma', + } + httpPost('/api/video/create', params) + .then(() => { + fetchData(1) + taskPulling.value = true + showMessageOK('创建任务成功') + }) + .catch((e) => { + showMessageError('创建任务失败:' + e.message) + }) + .finally(() => { + generating.value = false + }) + } + const createKelingVideo = () => { + if (!kelingParams.prompt.trim()) { + showMessageError('请输入视频提示词') + return + } + generating.value = true + const params = { + ...kelingParams, + task_type: 'keling', + } + httpPost('/api/video/create', params) + .then(() => { + fetchData(1) + taskPulling.value = true + showMessageOK('创建任务成功') + }) + .catch((e) => { + showMessageError('创建任务失败:' + e.message) + }) + .finally(() => { + generating.value = false + }) + } + const fetchData = (_page) => { + if (_page) { + page.value = _page + } + listLoading.value = true + httpGet('/api/video/list', { page: page.value, page_size: pageSize.value }) + .then((res) => { + total.value = res.data.total + let needPull = false + const items = [] + for (let v of res.data.items) { + if (v.progress === 0 || v.progress === 102) { + needPull = true + } + items.push(v) + } + listLoading.value = false + taskPulling.value = needPull + if (page.value === 1) { + currentList.value = items + } else { + currentList.value.push(...items) + } + if (items.length < pageSize.value) { + listFinished.value = true + } + }) + .catch((e) => { + listLoading.value = false + showMessageError('获取作品列表失败:' + e.message) + }) + } + const fetchUserPower = async () => { + try { + // 获取系统信息,更新算力配置 + const sysInfo = await getSystemInfo() + lumaPowerCost.value = sysInfo.data.luma_power || 10 + keLingPowers.value = sysInfo.data.keling_powers || {} + updateModelPower() + } catch (error) { + console.error('获取用户算力失败:', error) + // 设置默认值 + lumaPowerCost.value = 10 + kelingPowerCost.value = 15 + } + } + const loadMore = () => { + page.value++ + fetchData() + } + const playVideo = (item) => { + currentVideoUrl.value = item.video_url + showVideoDialog.value = true + } + const downloadVideo = (item) => { + item.downloading = true + const link = document.createElement('a') + link.href = item.video_url + link.download = item.title || 'video.mp4' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + item.downloading = false + showMessageOK('开始下载') + } + const removeJob = (item) => { + // 建议在页面层处理弹窗,store 只负责数据和业务 + } + + return { + // 状态 + activeVideoType, + loading, + generating, + isGenerating, + listLoading, + listFinished, + currentList, + showVideoDialog, + currentVideoUrl, + lumaParams, + lumaUseImageMode, + lumaStartImage, + lumaEndImage, + kelingParams, + kelingUseImageMode, + kelingStartImage, + kelingEndImage, + aspectRatioOptions, + modelOptions, + durationOptions, + modeOptions, + cameraControlOptions, + getCameraControlLabel, + page, + pageSize, + total, + lumaPowerCost, + kelingPowerCost, + taskPulling, + keLingPowers, + // 方法 + updateModelPower, + switchVideoType, + handleLumaStartImageUpload, + handleLumaEndImageUpload, + handleKelingStartImageUpload, + handleKelingEndImageUpload, + generatePrompt, + toggleLumaImageMode, + toggleKelingImageMode, + uploadLumaStartImage, + uploadLumaEndImage, + uploadKelingStartImage, + uploadKelingEndImage, + uploadImage, + createLumaVideo, + createKelingVideo, + fetchData, + fetchUserPower, + loadMore, + playVideo, + downloadVideo, + removeJob, + } +}) diff --git a/web/src/store/sharedata.js b/web/src/store/sharedata.js index f2662c3b..c795cca1 100644 --- a/web/src/store/sharedata.js +++ b/web/src/store/sharedata.js @@ -109,6 +109,14 @@ export const useSharedStore = defineStore('shared', { setTheme(theme) { this.theme = theme document.documentElement.setAttribute('data-theme', theme) // 设置 HTML 的 data-theme 属性 + + // 同时设置 dark 类,以便 Tailwind 的 dark: 前缀能够工作 + if (theme === 'dark') { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + Storage.set('theme', theme) }, setIsLogin(value) { diff --git a/web/src/store/suno.js b/web/src/store/suno.js new file mode 100644 index 00000000..a9ddd99f --- /dev/null +++ b/web/src/store/suno.js @@ -0,0 +1,440 @@ +import { closeLoading, showLoading, showMessageError, showMessageOK } from '@/utils/dialog' +import { httpDownload, httpGet, httpPost } from '@/utils/http' +import { replaceImg } from '@/utils/libs' +import Compressor from 'compressorjs' +import { ElMessage, ElMessageBox } from 'element-plus' +import { compact } from 'lodash' +import { defineStore } from 'pinia' +import { computed, onMounted, ref } from 'vue' +import { checkSession, getSystemInfo } from './cache' +import { useSharedStore } from './sharedata' + +export const useSunoStore = defineStore('suno', () => { + // 响应式数据 + const custom = ref(false) + const models = ref([ + { label: 'v3.0', value: 'chirp-v3-0' }, + { label: 'v3.5', value: 'chirp-v3-5' }, + { label: 'v4.0', value: 'chirp-v4' }, + { label: 'v4.5', value: 'chirp-auk' }, + ]) + + const tags = ref([ + { label: '女声', value: 'female vocals' }, + { label: '男声', value: 'male vocals' }, + { label: '流行', value: 'pop' }, + { label: '摇滚', value: 'rock' }, + { label: '硬摇滚', value: 'hard rock' }, + { label: '电音', value: 'electronic' }, + { label: '金属', value: 'metal' }, + { label: '重金属', value: 'heavy metal' }, + { label: '节拍', value: 'beat' }, + { label: '弱拍', value: 'upbeat' }, + { label: '合成器', value: 'synth' }, + { label: '吉他', value: 'guitar' }, + { label: '钢琴', value: 'piano' }, + { label: '小提琴', value: 'violin' }, + { label: '贝斯', value: 'bass' }, + { label: '嘻哈', value: 'hip hop' }, + ]) + + const data = ref({ + model: 'chirp-auk', + tags: '', + lyrics: '', + prompt: '', + title: '', + instrumental: false, + ref_task_id: '', + extend_secs: 0, + ref_song_id: '', + }) + + const loading = ref(false) + const noData = ref(true) + const playList = ref([]) + const showPlayer = ref(false) + const list = ref([]) + const taskPulling = ref(true) + const btnText = ref('开始创作') + const refSong = ref(null) + const showDialog = ref(false) + const editData = ref({ title: '', cover: '', id: 0 }) + const promptPlaceholder = ref('请在这里输入你自己写的歌词...') + const isGenerating = ref(false) + const sunoPower = ref(0) + const isLogin = ref(false) + const shareStore = useSharedStore() + + // 分页相关 + const page = ref(1) + const pageSize = ref(10) + const total = ref(0) + + // 定时器引用 + let tastPullHandler = null + + // 计算属性 + const hasRefSong = computed(() => refSong.value !== null) + + onMounted(() => { + getSystemInfo().then((res) => { + sunoPower.value = res.data.suno_power + }) + checkSession().then((res) => { + isLogin.value = true + }) + }) + + // 方法 + const fetchData = async (_page) => { + if (_page) { + page.value = _page + } + loading.value = true + + try { + const res = await httpGet('/api/suno/list', { + page: page.value, + page_size: pageSize.value, + }) + + total.value = res.data.total + let needPull = false + const items = [] + + for (let v of res.data.items) { + if (v.progress === 100) { + v.major_model_version = v['raw_data']['major_model_version'] + } + if (v.progress === 0 || v.progress === 102) { + needPull = true + } + items.push(v) + } + + loading.value = false + taskPulling.value = needPull + + // 如果任务有变化,则刷新任务列表 + if (JSON.stringify(list.value) !== JSON.stringify(items)) { + list.value = items + } + noData.value = list.value.length === 0 + } catch (e) { + loading.value = false + noData.value = true + showMessageError('获取作品列表失败:' + e.message) + } + } + + const create = async () => { + if (!isLogin.value) { + return shareStore.setShowLoginDialog(true) + } + + data.value.type = custom.value ? 2 : 1 + data.value.ref_task_id = refSong.value ? refSong.value.task_id : '' + data.value.ref_song_id = refSong.value ? refSong.value.song_id : '' + data.value.extend_secs = refSong.value ? refSong.value.extend_secs : 0 + + // 验证输入 + if (refSong.value) { + if (data.value.extend_secs > refSong.value.duration) { + return showMessageError('续写开始时间不能超过原歌曲长度') + } + } else if (custom.value) { + if (data.value.lyrics === '') { + return showMessageError('请输入歌词') + } + if (data.value.title === '') { + return showMessageError('请输入歌曲标题') + } + } else { + if (data.value.prompt === '') { + return showMessageError('请输入歌曲描述') + } + } + + try { + await httpPost('/api/suno/create', data.value) + await fetchData(1) + taskPulling.value = true + showMessageOK('创建任务成功') + } catch (e) { + showMessageError('创建任务失败:' + e.message) + } + } + + const merge = async (item) => { + try { + await httpPost('/api/suno/create', { song_id: item.song_id, type: 3 }) + await fetchData(1) + taskPulling.value = true + showMessageOK('创建任务成功') + } catch (e) { + showMessageError('合并歌曲失败:' + e.message) + } + } + + const download = async (item) => { + const url = replaceImg(item.audio_url) + const downloadURL = `/api/download?url=${url}` + const urlObj = new URL(url) + const fileName = urlObj.pathname.split('/').pop() + + item.downloading = true + + try { + const response = await httpDownload(downloadURL) + const blob = new Blob([response.data]) + const link = document.createElement('a') + link.href = URL.createObjectURL(blob) + link.download = fileName + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(link.href) + item.downloading = false + } catch (error) { + showMessageError('下载失败') + item.downloading = false + } + } + + const uploadAudio = async (file) => { + // 判断是否登录 + if (!isLogin.value) { + return shareStore.setShowLoginDialog(true) + } + + const formData = new FormData() + formData.append('file', file.file, file.name) + showLoading('正在上传文件...') + + try { + const res = await httpPost('/api/upload', formData) + await httpPost('/api/suno/create', { + audio_url: res.data.url, + title: res.data.name, + type: 4, + }) + await fetchData(1) + showMessageOK('歌曲上传成功') + closeLoading() + removeRefSong() + ElMessage.success({ message: '上传成功', duration: 500 }) + } catch (e) { + showMessageError('歌曲上传失败:' + e.message) + closeLoading() + } + } + + const extend = (item) => { + refSong.value = item + refSong.value.extend_secs = item.duration + data.value.title = item.title + custom.value = true + btnText.value = '续写歌曲' + promptPlaceholder.value = '输入额外的歌词,根据您之前的歌词来扩展歌曲...' + } + + const update = (item) => { + showDialog.value = true + editData.value.title = item.title + editData.value.cover = item.cover_url + editData.value.id = item.id + } + + const updateSong = async () => { + if (editData.value.title === '' || editData.value.cover === '') { + return showMessageError('歌曲标题和封面不能为空') + } + + try { + await httpPost('/api/suno/update', editData.value) + showMessageOK('更新歌曲成功') + showDialog.value = false + await fetchData() + } catch (e) { + showMessageError('更新歌曲失败:' + e.message) + } + } + + const removeRefSong = () => { + refSong.value = null + btnText.value = '开始创作' + promptPlaceholder.value = '请在这里输入你自己写的歌词...' + } + + const selectTag = (tag) => { + const currentTags = data.value.tags.trim() + const newTagLength = tag.value.length + + if (currentTags.length + newTagLength >= 119) { + return + } + + const tagArray = currentTags + ? currentTags + .split(',') + .map((t) => t.trim()) + .filter((t) => t) + : [] + const newTags = compact([...tagArray, tag.value]) + data.value.tags = newTags.join(',') + } + + const removeJob = async (item) => { + try { + await ElMessageBox.confirm('此操作将会删除任务相关文件,继续操作码?', '删除提示', { + confirmButtonText: '确认', + cancelButtonText: '取消', + type: 'warning', + }) + + await httpGet('/api/suno/remove', { id: item.id }) + ElMessage.success('任务删除成功') + await fetchData() + } catch (e) { + if (e !== 'cancel') { + ElMessage.error('任务删除失败:' + e.message) + } + } + } + + const publishJob = async (item) => { + try { + await httpGet('/api/suno/publish', { id: item.id, publish: item.publish }) + ElMessage.success('操作成功') + } catch (e) { + ElMessage.error('操作失败:' + e.message) + } + } + + const getShareURL = (item) => { + return `${location.protocol}//${location.host}/song/${item.song_id}` + } + + const uploadCover = (file) => { + new Compressor(file.file, { + quality: 0.6, + success(result) { + const formData = new FormData() + formData.append('file', result, result.name) + showLoading('图片上传中...') + + httpPost('/api/upload', formData) + .then((res) => { + editData.value.cover = res.data.url + ElMessage.success({ message: '上传成功', duration: 500 }) + closeLoading() + }) + .catch((e) => { + ElMessage.error('图片上传失败:' + e.message) + closeLoading() + }) + }, + error(err) { + console.log(err.message) + }, + }) + } + + const createLyric = async () => { + if (data.value.lyrics === '') { + return showMessageError('请输入歌词描述') + } + + isGenerating.value = true + + try { + const res = await httpPost('/api/prompt/lyric', { prompt: data.value.lyrics }) + const lines = res.data.split('\n') + data.value.title = lines.shift().replace(/\*/g, '') + lines.shift() + data.value.lyrics = lines.join('\n') + isGenerating.value = false + } catch (e) { + showMessageError('歌词生成失败:' + e.message) + isGenerating.value = false + } + } + + const startTaskPolling = () => { + tastPullHandler = setInterval(() => { + if (taskPulling.value) { + fetchData(1) + } + }, 5000) + } + + const stopTaskPolling = () => { + if (tastPullHandler) { + clearInterval(tastPullHandler) + tastPullHandler = null + } + } + + const resetData = () => { + data.value = { + model: 'chirp-auk', + tags: '', + lyrics: '', + prompt: '', + title: '', + instrumental: false, + ref_task_id: '', + extend_secs: 0, + ref_song_id: '', + } + custom.value = false + refSong.value = null + btnText.value = '开始创作' + promptPlaceholder.value = '请在这里输入你自己写的歌词...' + } + + return { + // 状态 + custom, + models, + tags, + data, + loading, + noData, + playList, + showPlayer, + list, + taskPulling, + btnText, + refSong, + showDialog, + editData, + promptPlaceholder, + isGenerating, + page, + pageSize, + total, + hasRefSong, + sunoPower, + // 方法 + fetchData, + create, + merge, + download, + uploadAudio, + extend, + update, + updateSong, + removeRefSong, + selectTag, + removeJob, + publishJob, + getShareURL, + uploadCover, + createLyric, + startTaskPolling, + stopTaskPolling, + resetData, + } +}) diff --git a/web/src/store/theme.js b/web/src/store/theme.js index 1c4964f9..fd40a2b9 100644 --- a/web/src/store/theme.js +++ b/web/src/store/theme.js @@ -1,15 +1,23 @@ // src/store/index.js -import { defineStore } from "pinia"; +import { defineStore } from 'pinia' -export const useThemeStore = defineStore("theme", { +export const useThemeStore = defineStore('theme', { state: () => ({ - theme: localStorage.getItem("theme") || "light" // 默认从 localStorage 获取主题 + theme: localStorage.getItem('theme') || 'light', // 默认从 localStorage 获取主题 }), actions: { setTheme(theme) { - this.theme = theme; - document.documentElement.setAttribute("data-theme", theme); - localStorage.setItem("theme", theme); // 保存到 localStorage - } - } -}); + this.theme = theme + document.documentElement.setAttribute('data-theme', theme) + + // 同时设置 dark 类,以便 Tailwind 的 dark: 前缀能够工作 + if (theme === 'dark') { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + + localStorage.setItem('theme', theme) // 保存到 localStorage + }, + }, +}) diff --git a/web/src/store/video.js b/web/src/store/video.js index 87382323..c025892e 100644 --- a/web/src/store/video.js +++ b/web/src/store/video.js @@ -441,8 +441,6 @@ export const useVideoStore = defineStore('video', () => { } isGenerating.value = true - showLoading('正在生成视频脚本...') - try { const res = await httpPost('/api/prompt/video', { prompt }) if (activeVideoType.value === 'luma') { @@ -450,10 +448,8 @@ export const useVideoStore = defineStore('video', () => { } else { kelingParams.prompt = res.data } - closeLoading() } catch (error) { showMessageError('生成提示词失败:' + error.message) - closeLoading() } finally { isGenerating.value = false } @@ -468,7 +464,7 @@ export const useVideoStore = defineStore('video', () => { // 视频下载 const downloadVideo = async (item) => { const url = replaceImg(item.video_url) - const downloadURL = `${import.meta.env.VITE_API_HOST}/api/download?url=${url}` + const downloadURL = `/api/download?url=${url}` const urlObj = new URL(url) const fileName = urlObj.pathname.split('/').pop() diff --git a/web/src/utils/dialog.js b/web/src/utils/dialog.js index b738ded9..a4bf3d95 100644 --- a/web/src/utils/dialog.js +++ b/web/src/utils/dialog.js @@ -1,51 +1,62 @@ /** * Util lib functions */ -import {closeToast, showConfirmDialog, showFailToast, showLoadingToast, showSuccessToast, showToast} from "vant"; -import {isMobile} from "@/utils/libs"; -import {ElMessage} from "element-plus"; +import { isMobile } from '@/utils/libs' +import { ElMessage } from 'element-plus' +import { + closeToast, + showConfirmDialog, + showFailToast, + showLoadingToast, + showSuccessToast, + showToast, +} from 'vant' export function showLoginDialog(router) { showConfirmDialog({ - title: "登录", - message: "此操作需要登录才能进行,前往登录?", + title: '登录', + message: '此操作需要登录才能进行,前往登录?', }) - .then(() => { - router.push("/login"); - }) - .catch(() => { - // on cancel - }); + .then(() => { + router.push('/login') + }) + .catch(() => { + // on cancel + }) } export function showMessageOK(message) { if (isMobile()) { - showSuccessToast(message); + showSuccessToast(message) } else { - ElMessage.success(message); + ElMessage.success(message) } } +export function showMessageSuccess(message) { + showMessageOK(message) +} + export function showMessageInfo(message) { if (isMobile()) { - showToast(message); + showToast(message) } else { - ElMessage.info(message); + ElMessage.info(message) } } export function showMessageError(message) { if (isMobile()) { - showFailToast({message: message, duration: 0}); + showFailToast({ message: message }) } else { - ElMessage.error(message); + ElMessage.error(message) } } -export function showLoading(message = "正在处理...") { - showLoadingToast({message: message, forbidClick: true, duration: 0}); +export function showLoading(message = '正在处理...') { + showLoadingToast({ message: message, forbidClick: true, duration: 0 }) } export function closeLoading() { - closeToast(); + closeToast() } diff --git a/web/src/utils/http.js b/web/src/utils/http.js index 01620446..c1ddf351 100644 --- a/web/src/utils/http.js +++ b/web/src/utils/http.js @@ -5,8 +5,32 @@ // * @Author yangjian102621@163.com // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -import axios from 'axios' import { getAdminToken, getUserToken, removeAdminToken, removeUserToken } from '@/store/session' +import axios from 'axios' + +// Blob 数据读取和解析的辅助函数 +export async function parseBlobResponse(blob) { + try { + // 检查 Blob 的类型 + if (blob.type && blob.type.includes('application/json')) { + // 如果是 JSON 类型,直接解析 + const text = await blob.text() + return JSON.parse(text) + } else { + // 如果不是 JSON 类型,尝试解析为文本 + const text = await blob.text() + try { + return JSON.parse(text) + } catch (e) { + // 如果解析 JSON 失败,返回文本内容 + return { message: text, rawData: text } + } + } + } catch (error) { + console.error('解析 Blob 响应失败:', error) + return { message: '解析响应数据失败', error: error.message } + } +} axios.defaults.timeout = 180000 // axios.defaults.baseURL = process.env.VUE_APP_API_HOST @@ -29,7 +53,7 @@ axios.interceptors.response.use( (response) => { return response }, - (error) => { + async (error) => { if (error.response.status === 401) { if (error.response.request.responseURL.indexOf('/api/admin') !== -1) { removeAdminToken() @@ -39,8 +63,14 @@ axios.interceptors.response.use( error.response.data.message = '请先登录' return Promise.reject(error.response.data) } + if (error.response.status === 400) { - return Promise.reject(new Error(error.response.data.message)) + let errorMessage = error.response.data.message + if (!errorMessage) { + const parsedData = await parseBlobResponse(error.response.data) + errorMessage = parsedData.message + } + return Promise.reject(new Error(errorMessage)) } else { return Promise.reject(error) } diff --git a/web/src/utils/libs.js b/web/src/utils/libs.js index 1c151507..88be8fe1 100644 --- a/web/src/utils/libs.js +++ b/web/src/utils/libs.js @@ -236,7 +236,7 @@ export function showLoginDialog(router) { message: '此操作需要登录才能进行,前往登录?', }) .then(() => { - router.push('/mobile/login') + router.push('/login') }) .catch(() => { // on cancel diff --git a/web/src/views/404.vue b/web/src/views/404.vue index 6b9d2043..1602cb3a 100644 --- a/web/src/views/404.vue +++ b/web/src/views/404.vue @@ -8,19 +8,19 @@ - diff --git a/web/src/views/ChatApps.vue b/web/src/views/ChatApps.vue index 573bed5e..0497ec45 100644 --- a/web/src/views/ChatApps.vue +++ b/web/src/views/ChatApps.vue @@ -167,7 +167,7 @@ const useRole = (role) => { } - diff --git a/web/src/views/ChatExport.vue b/web/src/views/ChatExport.vue index e373888e..2fbe53bd 100644 --- a/web/src/views/ChatExport.vue +++ b/web/src/views/ChatExport.vue @@ -1,119 +1,144 @@ - - - + + + diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index 06213c4d..41bdfe5d 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -110,7 +110,7 @@
- + @@ -329,18 +329,17 @@
- - + - + @@ -361,32 +360,8 @@ - -
-
-
- - -
- - -