diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..225fc6f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/__pycache__ diff --git a/__init__.py b/__init__.py index 68b65d2..581a8c0 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,4 @@ +import logging import platform import random import sys @@ -119,6 +120,7 @@ async def auth_callback(request): auth_query = await request.json() token = auth_query.get("token", "") client_key = auth_query.get("client_key", "") + logging.info(f"### auth_callback: {token} {client_key}") if token and client_key: token = unquote(token) client_key = unquote(client_key) diff --git a/auth_unit.py b/auth_unit.py index 3fe4e9a..66f1c8c 100644 --- a/auth_unit.py +++ b/auth_unit.py @@ -1,3 +1,4 @@ +import logging import os import time import requests @@ -56,6 +57,9 @@ class AuthUnit: self.last_check_time = time.time() return self.token, "", RiceRoundErrorDef.SUCCESS else: + logging.warn( + f"get_user_token failed, {response.status_code} {response.text}" + ) error_message = "登录结果错误" error_code = RiceRoundErrorDef.UNKNOWN_ERROR try: @@ -119,6 +123,7 @@ class AuthUnit: def set_user_token(self, user_token, client_key): if not client_key or self.client_key != client_key: + logging.warn(f"client_key is not match, {self.client_key} != {client_key}") return if not user_token: user_token = "" diff --git a/js/riceconfig.js b/js/riceconfig.js index 48f693f..4edd1dc 100644 --- a/js/riceconfig.js +++ b/js/riceconfig.js @@ -1 +1,159 @@ -import{api}from"../../../scripts/api.js";import{ComfyApp,app}from"../../../scripts/app.js";import{showToast}from"./riceround.js";const UserTokenKey="riceround_user_token";function isValidJWTFormat(e){if("string"!=typeof e)return!1;if(e.length<50)return!1;const t=e.split(".");if(3!==t.length)return!1;const o=/^[A-Za-z0-9_-]+$/;return t.every((e=>e.length>0&&o.test(e)))}async function set_exclusive_user(e){e?localStorage.setItem("RiceRound.Cloud.exclusive",e):localStorage.removeItem("RiceRound.Cloud.exclusive");200!=(await api.fetchApi("/riceround/set_exclusive_user",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({exclusive_user:e})})).status&&showToast("设置专属用户客户ID失败","error")}app.registerExtension({name:"riceround.config",async setup(){app.ui.settings.addSetting({id:"RiceRound.User.logout",name:"登出当前用户",type:()=>{const e=document.createElement("tr"),t=document.createElement("td"),o=document.createElement("input");return o.type="button",o.value="登出",o.style.borderRadius="8px",o.style.padding="8px 16px",o.style.fontSize="14px",o.style.cursor="pointer",o.style.border="1px solid #666",o.style.backgroundColor="#444",o.style.color="#fff",o.onclick=async()=>{localStorage.removeItem("Comfy.Settings.RiceRound.User.long_token"),localStorage.removeItem(UserTokenKey),app.ui.settings.setSettingValue("RiceRound.User.long_token",""),await api.fetchApi("/riceround/logout"),showToast("登出成功")},t.appendChild(o),e.appendChild(t),e}}),app.ui.settings.addSetting({id:"RiceRound.User.long_token",name:"设置长效token",type:"text",textType:"password",defaultValue:"",tooltip:"用于非本机授权登录情况,请勿泄露!提倡使用本机登录授权更安全!",onChange:async function(e){isValidJWTFormat(e)&&await api.fetchApi("/riceround/set_long_token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({long_token:e})})}}),app.ui.settings.addSetting({id:"RiceRound.Setting.wait-time",name:"任务排队等待时间(秒)",tooltip:"不建议设置太短,否则可能等不到运行结果就退出了",type:"slider",attrs:{min:30,max:7200,step:10},defaultValue:600,onChange:e=>{api.fetchApi("/riceround/set_wait_time",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({wait_time:e})})}}),app.ui.settings.addSetting({id:"RiceRound.Publish",name:"自动发布工作流",type:"boolean",defaultValue:!0,tooltip:"设置为true时,会自动发布工作流",onChange:function(e){api.fetchApi("/riceround/set_auto_publish",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({auto_publish:e})}),e||localStorage.setItem("RiceRound.Setting.auto_overwrite",e)}}),app.ui.settings.addSetting({id:"RiceRound.Publish.auto_overwrite",name:"自动覆盖更新同id工作流",type:"boolean",tooltip:"设置为true时,会自动覆盖已有的template_id的数据",onChange:function(e){api.fetchApi("/riceround/set_auto_overwrite",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({auto_overwrite:e})})}}),app.ui.settings.addSetting({id:"RiceRound.Cloud.run_client",name:"自启动云节点客户端",type:"boolean",defaultValue:!0,tooltip:"没有任何云节点客户运行的话,则该用户云节点无法运行",onChange:function(e){api.fetchApi("/riceround/set_run_client",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({run_client:e})})}}),app.ui.settings.addSetting({id:"RiceRound.Advanced.setting",name:"模型列表存放位置,手动清理或安装高级节点",type:()=>{const e=document.createElement("tr"),t=document.createElement("td"),o=document.createElement("input");return o.type="button",o.value="打开文件夹",o.style.borderRadius="8px",o.style.padding="8px 16px",o.style.fontSize="14px",o.style.cursor="pointer",o.style.border="1px solid #666",o.style.backgroundColor="#444",o.style.color="#fff",o.onmouseover=()=>{o.style.backgroundColor="#555"},o.onmouseout=()=>{o.style.backgroundColor="#444"},o.onclick=()=>{api.fetchApi("/riceround/open_selector_list_folder",{method:"GET",headers:{"Content-Type":"application/json"}})},t.appendChild(o),e.appendChild(t),e}})}}); \ No newline at end of file +import { api as e } from "../../../scripts/api.js"; + +import { ComfyApp as t, app as o } from "../../../scripts/app.js"; + +import { showToast as n } from "./riceround.js"; + +const i = "riceround_user_token"; + +function r(e) { + if ("string" != typeof e) return !1; + if (e.length < 50) return !1; + const t = e.split("."); + if (3 !== t.length) return !1; + const o = /^[A-Za-z0-9_-]+$/; + return t.every((e => e.length > 0 && o.test(e))); +} + +async function s(t) { + t ? localStorage.setItem("RiceRound.Cloud.exclusive", t) : localStorage.removeItem("RiceRound.Cloud.exclusive"); + 200 != (await e.fetchApi("/riceround/set_exclusive_user", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + exclusive_user: t + }) + })).status && n("设置专属用户客户ID失败", "error"); +} + +o.registerExtension({ + name: "riceround.config", + async setup() { + o.ui.settings.addSetting({ + id: "RiceRound.User.logout", + name: "登出当前用户", + type: () => { + const t = document.createElement("tr"), r = document.createElement("td"), s = document.createElement("input"); + return s.type = "button", s.value = "登出", s.style.borderRadius = "8px", s.style.padding = "8px 16px", + s.style.fontSize = "14px", s.style.cursor = "pointer", s.style.border = "1px solid #666", + s.style.backgroundColor = "#444", s.style.color = "#fff", s.onclick = async () => { + localStorage.removeItem("Comfy.Settings.RiceRound.User.long_token"), localStorage.removeItem(i), + o.ui.settings.setSettingValue("RiceRound.User.long_token", ""), await e.fetchApi("/riceround/logout"), + n("登出成功"); + }, r.appendChild(s), t.appendChild(r), t; + } + }), o.ui.settings.addSetting({ + id: "RiceRound.User.long_token", + name: "设置长效token", + type: "text", + textType: "password", + defaultValue: "", + tooltip: "用于非本机授权登录情况,请勿泄露!提倡使用本机登录授权更安全!", + onChange: async function(t) { + r(t) && await e.fetchApi("/riceround/set_long_token", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + long_token: t + }) + }); + } + }), o.ui.settings.addSetting({ + id: "RiceRound.Setting.wait-time", + name: "任务排队等待时间(秒)", + tooltip: "不建议设置太短,否则可能等不到运行结果就退出了", + type: "slider", + attrs: { + min: 30, + max: 7200, + step: 10 + }, + defaultValue: 600, + onChange: t => { + e.fetchApi("/riceround/set_wait_time", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + wait_time: t + }) + }); + } + }), o.ui.settings.addSetting({ + id: "RiceRound.Publish", + name: "自动发布工作流", + type: "boolean", + defaultValue: !0, + tooltip: "设置为true时,会自动发布工作流", + onChange: function(t) { + e.fetchApi("/riceround/set_auto_publish", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + auto_publish: t + }) + }), t || localStorage.setItem("RiceRound.Setting.auto_overwrite", t); + } + }), o.ui.settings.addSetting({ + id: "RiceRound.Publish.auto_overwrite", + name: "自动覆盖更新同id工作流", + type: "boolean", + tooltip: "设置为true时,会自动覆盖已有的template_id的数据", + onChange: function(t) { + e.fetchApi("/riceround/set_auto_overwrite", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + auto_overwrite: t + }) + }); + } + }), o.ui.settings.addSetting({ + id: "RiceRound.Cloud.run_client", + name: "自启动云节点客户端", + type: "boolean", + defaultValue: !0, + tooltip: "没有任何云节点客户运行的话,则该用户云节点无法运行", + onChange: function(t) { + e.fetchApi("/riceround/set_run_client", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + run_client: t + }) + }); + } + }), o.ui.settings.addSetting({ + id: "RiceRound.Advanced.setting", + name: "模型列表存放位置,手动清理或安装高级节点", + type: () => { + const t = document.createElement("tr"), o = document.createElement("td"), n = document.createElement("input"); + return n.type = "button", n.value = "打开文件夹", n.style.borderRadius = "8px", n.style.padding = "8px 16px", + n.style.fontSize = "14px", n.style.cursor = "pointer", n.style.border = "1px solid #666", + n.style.backgroundColor = "#444", n.style.color = "#fff", n.onmouseover = () => { + n.style.backgroundColor = "#555"; + }, n.onmouseout = () => { + n.style.backgroundColor = "#444"; + }, n.onclick = () => { + e.fetchApi("/riceround/open_selector_list_folder", { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }); + }, o.appendChild(n), t.appendChild(o), t; + } + }); + } +}); \ No newline at end of file diff --git a/js/riceround.js b/js/riceround.js index b98d2d5..c82fc9f 100644 --- a/js/riceround.js +++ b/js/riceround.js @@ -1 +1,432 @@ -import{api}from"../../../scripts/api.js";import{ComfyApp,app}from"../../../scripts/app.js";const style=document.createElement("style");style.textContent="\n .riceround-swal-top-container {\n z-index: 99999 !important;\n }\n",document.head.appendChild(style);export async function loadResource(e,t=""){if(!document.querySelector(`script[src="${e}"]`)){const t=document.createElement("script");t.src=e,document.head.appendChild(t);try{await new Promise(((o,n)=>{t.onload=o,t.onerror=()=>n(new Error(`Failed to load script: ${e}`))}))}catch(e){}}if(t){if(!document.querySelector(`link[href="${t}"]`)){const e=document.createElement("link");e.rel="stylesheet",e.href=t,document.head.appendChild(e);try{await new Promise(((o,n)=>{e.onload=o,e.onerror=()=>n(new Error(`Failed to load stylesheet: ${t}`))}))}catch(e){}}}}let toastHasLoaded=!1;async function loadToast(){if(!toastHasLoaded){const e="https://cdn.jsdelivr.net/npm/toastify-js",t="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css";await loadResource(e,t),toastHasLoaded=!0}}export async function showToast(e,t="info",o=3e3){await loadToast(),"info"==t?Toastify({text:e,duration:o,close:!1,gravity:"top",position:"center",backgroundColor:"#3498db",stopOnFocus:!1}).showToast():"error"==t?Toastify({text:e,duration:o,close:!0,gravity:"top",position:"center",backgroundColor:"#FF4444",stopOnFocus:!0}).showToast():"warning"==t&&Toastify({text:e,duration:o,close:!0,gravity:"top",position:"center",backgroundColor:"#FFA500",stopOnFocus:!0}).showToast()}let messageBoxHasLoaded=!1;export async function loadMessageBox(){messageBoxHasLoaded||(await loadResource("https://cdn.jsdelivr.net/npm/sweetalert2@11","https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bootstrap-4/bootstrap-4.css"),messageBoxHasLoaded=!0)}async function serverShowMessageBox(e,t){await loadMessageBox();const o={...e,heightAuto:!1};try{const e=await Swal.fire(o);api.fetchApi("/riceround/message",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`id=${t}&message=${e.isConfirmed?"1":"0"}`})}catch(e){api.fetchApi("/riceround/message",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`id=${t}&message=0`})}window.addEventListener("beforeunload",(function(){fetch("/riceround/message",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`id=${t}&message=0`,keepalive:!0})}),{once:!0})}api.addEventListener("riceround_toast",(e=>{showToast(e.detail.content,e.detail.type,e.detail.duration)})),api.addEventListener("riceround_dialog",(e=>{serverShowMessageBox(JSON.parse(e.detail.json_content),e.detail.id)}));let dialogLibHasLoaded=!1;async function waitForObject(e,t,o=5e3){return new Promise(((n,i)=>{const a=Date.now(),s=()=>{Date.now()-a>o?i(new Error(`Timeout waiting for ${t} to load`)):e()?n():setTimeout(s,50)};s()}))}export async function initDialogLib(e=!1){if(!dialogLibHasLoaded)try{const e="https://unpkg.com/vue@3/dist/vue.global.js";await loadResource(e,""),await waitForObject((()=>window.Vue),"vue");const t="https://cdn.jsdelivr.net/npm/element-plus",o="https://cdn.jsdelivr.net/npm/element-plus/dist/index.css";if(await loadResource(t,o),await waitForObject((()=>window.ElementPlus),"element-plus"),null==window.DialogLib){const e="riceround/static/dialog-lib.umd.cjs";await loadResource(e,""),await waitForObject((()=>window.DialogLib),"showLoginDialog")}dialogLibHasLoaded=!0}catch(e){throw e}}async function setNodeAdditionalInfo(e){try{const t={method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)},o=await api.fetchApi("/riceround/set_node_additional_info",t);await o.json()}catch(e){}}function changeWidget(e,t,o,n){e.type=t,e.value=o,e.options=n}function changeWidgets(e,t,o,n){"customtext"===t&&(t="text");const i=n.options;var a=e.widgets[1].value;i?.values?.includes(a)||(a=n.value),"RiceRoundAdvancedChoiceNode"==e.comfyClass||"RiceRoundSimpleChoiceNode"==e.comfyClass?changeWidget(e.widgets[1],t,a,i):"RiceRoundIntNode"!=e.comfyClass&&"RiceRoundFloatNode"!=e.comfyClass||4==e.widgets.length&&(changeWidget(e.widgets[1],"number",a,i),changeWidget(e.widgets[2],"number",i?.min??0,i),changeWidget(e.widgets[3],"number",i?.max??100,i))}function adaptWidgetsBasedOnConnection(e,t,o,n){if(e.outputs[0].type=o.type,"name"===e.widgets[0].label){(["数值","文本","列表","参数","Parameter"].includes(e.widgets[0].value)||""==e.widgets[0].value)&&(e.widgets[0].value=o.label?o.label:o.name)}const i=o.widget?.name?o.widget?.name:o.name,a=t.widgets.find((e=>e.name===i));if(!a)return;changeWidgets(e,a.origType??a.type,t,a)}api.addEventListener("riceround_login_dialog",(e=>{const t=e.detail.client_key,o=e.detail.title;window.DialogLib.showLoginDialog({title:o,spyNetworkError:!0,mainKey:"riceround"}).then((e=>{api.fetchApi("/riceround/auth_callback",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token:e,client_key:t})}),showToast("登录成功")})).catch((e=>{showToast("登录失败","error")}))})),api.addEventListener("riceround_show_workflow_payment_dialog",(e=>{const t=e.detail.title??"支付",o=e.detail.template_id;o?window.DialogLib.showWorkflowQRPaymentDialog({title:t,template_id:o}).then((({success:e,msg:t})=>{showToast(t,e?"info":"error")})).catch((e=>{showToast("取消支付","error")})):showToast("模板ID不能为空","error")})),api.addEventListener("riceround_clear_user_info",(async e=>{const t=e.detail.clear_key;"all"==t?(localStorage.removeItem("Comfy.Settings.RiceRound.User.long_token"),localStorage.removeItem("riceround_user_token"),app.ui.settings.setSettingValue("RiceRound.User.long_token","")):"long_token"==t?(localStorage.removeItem("Comfy.Settings.RiceRound.User.long_token"),app.ui.settings.setSettingValue("RiceRound.User.long_token","")):"user_token"==t&&localStorage.removeItem("riceround_user_token")})),api.addEventListener("execution_start",(async({detail:e})=>{let t="";const o={};for(const e of app.graph.nodes){if("RiceRoundDecryptNode"===e.type)return;if("RiceRoundEncryptNode"===e.type){const o=e.widgets?.find((e=>"template_id"===e.name&&e.value));if(o){if(t)return;t=o.value}}else if("RiceRoundAdvancedChoiceNode"===e.type||"RiceRoundSimpleChoiceNode"===e.type){if(1===!e.outputs?.[0]?.links?.length)continue;const t=e.widgets[1].options?.values??[];if(!t.length)continue;const n=e.graph.links[e.outputs[0].links[0]];if(!n)continue;const i=e.graph.getNodeById(n.target_id);if(!i?.inputs||"RiceRoundDecryptNode"===i.comfyClass||"RiceRoundEncryptNode"===i.comfyClass)continue;const a=i.inputs[n.target_slot];if(!a||!i.widgets)continue;const s=a.widget?.name||a.name;if(!s)continue;const d=`${i.comfyClass}.${s}`;o[e.id]={class_name:d,options_value:t,node_type:e.type}}}t&&Object.keys(o).length>0&&await setNodeAdditionalInfo({choice_node_map:o,template_id:t})}));let applySimpleChoiceNodeExtraLogicTimer=null;function applySimpleChoiceNodeExtraLogic(e,t){applySimpleChoiceNodeExtraLogicTimer&&clearTimeout(applySimpleChoiceNodeExtraLogicTimer),applySimpleChoiceNodeExtraLogicTimer=setTimeout((()=>{applySimpleChoiceNodeExtraLogicTimer=null;const o=t.graph.extra?.choice_node_map;if(e&&e.widgets&&2===e.widgets.length&&o&&o[e.id]){const t={values:o[e.id]};changeWidget(e.widgets[1],"combo",e.widgets[1].value,t)}}),200)}function adaptWidgetsToConnection(e){if(!e.outputs||0===e.outputs.length)return;const t=e.outputs[0].links;if(t&&1===t.length){const o=e.graph.links[t[0]];if(!o)return;const n=e.graph.getNodeById(o.target_id);if(!n||!n.inputs)return;if("RiceRoundDecryptNode"==n.comfyClass&&"RiceRoundSimpleChoiceNode"==e.comfyClass)return void applySimpleChoiceNodeExtraLogic(e,app);if("RiceRoundEncryptNode"==n.comfyClass||"RiceRoundDecryptNode"==n.comfyClass)return;const i=n.inputs[o.target_slot];if(!i||void 0===n.widgets)return;adaptWidgetsBasedOnConnection(e,n,i,app)}else t&&0!==t.length||("RiceRoundAdvancedChoiceNode"==e.comfyClass||"RiceRoundSimpleChoiceNode"==e.comfyClass?(e.widgets[0].value="Parameter",e.outputs[0].type="*"):"RiceRoundIntNode"==e.comfyClass?4==e.widgets.length&&(e.widgets[0].value="数值",e.widgets[1].value=0,e.widgets[2].value=0,e.widgets[3].value=100):"RiceRoundFloatNode"==e.comfyClass&&4==e.widgets.length&&(e.widgets[0].value="数值",e.widgets[1].value=0,e.widgets[2].value=0,e.widgets[3].value=100))}function setupParameterNode(e){const t=e.prototype.onAdded;e.prototype.onAdded=function(){t?.apply(this,arguments),adaptWidgetsToConnection(this)};const o=e.prototype.onAfterGraphConfigured;e.prototype.onAfterGraphConfigured=function(){o?.apply(this,arguments),adaptWidgetsToConnection(this)};const n=e.prototype.onConnectOutput;e.prototype.onConnectOutput=function(e,t,o,i,a){return!(!o.widget&&!(o.type in["STRING","COMBO","combo"]))&&(!n||(result=n.apply(this,arguments),result))};const i=e.prototype.onConnectionsChange;e.prototype.onConnectionsChange=function(e,t,o,n,a){return 2!=e||o||!this?.type||"RiceRoundAdvancedChoiceNode"!=this.type&&"RiceRoundSimpleChoiceNode"!=this.type||(this.widgets[0].value="Parameter"),app.configuringGraph||adaptWidgetsToConnection(this),i?.apply(this,arguments)}}function generateUUID(){let e="";for(let t=0;t<32;t++){e+=Math.floor(16*Math.random()).toString(16)}return e}function rebootAPI(){if("electronAPI"in window)return window.electronAPI.restartApp(),!0;api.fetchApi("/manager/reboot")}app.registerExtension({name:"riceround.custom",setup(){initDialogLib()},async beforeRegisterNodeDef(e,t,o){if(["RiceRoundAdvancedChoiceNode","RiceRoundSimpleChoiceNode","RiceRoundIntNode","RiceRoundFloatNode"].includes(t.name)&&setupParameterNode(e),"RiceRoundEncryptNode"==t.name){const t=400,o=120,n=e.prototype.onNodeCreated;e.prototype.onNodeCreated=function(){const e=n?n.apply(this):void 0;return void 0!==this.size?.[0]&&(this.size[0]=t),void 0!==this.size?.[1]&&(this.size[1]=Math.max(o,this.size[1])),e},e.prototype.onResize=function(e){return void 0!==e?.[0]&&(e[0]=t),void 0!==e?.[1]&&(e[1]=Math.max(o,e[1])),e}}},loadedGraphNode:async e=>{if(!e.title){const t=e.type;if(t&&t.includes("RiceRoundAdvancedChoiceNode")){const e=t.match(/RiceRoundAdvancedChoiceNode_([^_]+)_/);if(!e)return;const o=e[1];await loadMessageBox();const n=await Swal.fire({title:"高级选择节点安装确认",html:'\n
检测到高级选择节点,是否需要安装相关组件?
\n检测到高级选择节点,是否需要安装相关组件?
\n