Merge branch 'dev' into aller-lock
This commit is contained in:
+6
-58
@@ -14,9 +14,9 @@ from .service.model_manager.model_installer import download_url_with_wget
|
||||
from .service.model_manager.model_list import get_model_list
|
||||
from .service.media_service import *
|
||||
from .service.file_sync_service import *
|
||||
from .service.db_service import get_my_workflows_dir
|
||||
|
||||
WEB_DIRECTORY = "entry"
|
||||
DEFAULT_USER = "guest"
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
__all__ = ['NODE_CLASS_MAPPINGS']
|
||||
version = "V1.0.0"
|
||||
@@ -36,43 +36,6 @@ if os.path.exists(dist_path):
|
||||
|
||||
server.PromptServer.instance.app.add_subapp("/workspace_web/", workspace_app)
|
||||
|
||||
@server.PromptServer.instance.routes.post("/workspace/save_db")
|
||||
async def save_db(request):
|
||||
# Extract parameters from the request
|
||||
data = await request.json()
|
||||
table = data['table']
|
||||
json_data = data['json']
|
||||
|
||||
file_name = f'{db_dir_path}/{table}.json'
|
||||
# Offload file writing to a separate thread
|
||||
def write_json_string_to_db(file_name, json_data):
|
||||
if not os.path.exists(db_dir_path):
|
||||
os.makedirs(db_dir_path)
|
||||
# Write the JSON data to the specified file
|
||||
with open(file_name, 'w') as file:
|
||||
file.write(json.dumps(json_data, indent=4))
|
||||
await asyncio.to_thread(write_json_string_to_db, file_name, json_data)
|
||||
return web.Response(text=f"JSON saved to {file_name}")
|
||||
|
||||
def read_table(table):
|
||||
if not table:
|
||||
return None
|
||||
file_name = f'{db_dir_path}/{table}.json'
|
||||
if not os.path.exists(file_name):
|
||||
return None
|
||||
|
||||
with open(file_name, 'r') as file:
|
||||
data = json.load(file)
|
||||
return data
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/workspace/get_db")
|
||||
async def get_workspace(request):
|
||||
# Extract the table parameter from the query string
|
||||
table = request.query.get('table')
|
||||
data = await asyncio.to_thread(read_table, table)
|
||||
return web.json_response(data)
|
||||
|
||||
BACKUP_DIR = os.path.join(workspace_path, "backup")
|
||||
MAX_BACKUP_FILES = 20
|
||||
|
||||
@@ -121,33 +84,18 @@ async def get_system_dir(request):
|
||||
|
||||
return web.Response(text=json.dumps({"dir_path": dir_path, "dir_contents": dir_contents}), content_type='application/json')
|
||||
except Exception as e:
|
||||
print('🔴🔴Error',e)
|
||||
return web.Response(text=json.dumps({"error": str(e)}), status=500)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/workspace/get_default_my_workflows_dir")
|
||||
async def get_default_my_workflows_dir(request):
|
||||
@server.PromptServer.instance.routes.get("/workspace/get_my_workflows_dir")
|
||||
async def fetch_my_workflows_dir(request):
|
||||
try:
|
||||
dir = os.path.join(comfy_path, 'my_workflows')
|
||||
return web.Response(text=json.dumps({"path": dir}), content_type='application/json')
|
||||
dir = get_my_workflows_dir()
|
||||
return web.Response(text=json.dumps({"path": dir, "os": sys.platform}), content_type='application/json')
|
||||
except Exception as e:
|
||||
return web.Response(text=json.dumps({"error": str(e)}), status=500)
|
||||
|
||||
def get_my_workflows_dir():
|
||||
data = read_table('userSettings')
|
||||
if (data):
|
||||
records = json.loads(data)
|
||||
|
||||
if DEFAULT_USER in records and 'myWorkflowsDir' in records[DEFAULT_USER]:
|
||||
curDir = records[DEFAULT_USER]['myWorkflowsDir']
|
||||
elif 'myWorkflowsDir' in records:
|
||||
curDir = records['myWorkflowsDir']
|
||||
|
||||
if curDir:
|
||||
return curDir
|
||||
|
||||
return os.path.join(comfy_path, 'my_workflows')
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/workspace/update_file")
|
||||
async def update_file(request):
|
||||
data = await request.json()
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
import server
|
||||
import os
|
||||
import folder_paths
|
||||
import json
|
||||
import asyncio
|
||||
from aiohttp import web
|
||||
from .model_manager.model_preview import get_thumbnail_for_image_file
|
||||
|
||||
workspace_path = os.path.join(os.path.dirname(os.path.dirname(__file__)))
|
||||
comfy_path = os.path.dirname(folder_paths.__file__)
|
||||
db_dir_path = os.path.join(workspace_path, "db")
|
||||
DEFAULT_USER = "guest"
|
||||
|
||||
@server.PromptServer.instance.routes.post("/workspace/save_db")
|
||||
async def save_db(request):
|
||||
# Extract parameters from the request
|
||||
data = await request.json()
|
||||
table = data['table']
|
||||
json_data = data['json']
|
||||
|
||||
file_name = f'{db_dir_path}/{table}.json'
|
||||
# Offload file writing to a separate thread
|
||||
def write_json_string_to_db(file_name, json_data):
|
||||
if not os.path.exists(db_dir_path):
|
||||
os.makedirs(db_dir_path)
|
||||
# Write the JSON data to the specified file
|
||||
with open(file_name, 'w') as file:
|
||||
file.write(json.dumps(json_data, indent=4))
|
||||
await asyncio.to_thread(write_json_string_to_db, file_name, json_data)
|
||||
return web.Response(text=f"JSON saved to {file_name}")
|
||||
|
||||
def read_table(table):
|
||||
if not table:
|
||||
return None
|
||||
file_name = f'{db_dir_path}/{table}.json'
|
||||
if not os.path.exists(file_name):
|
||||
return None
|
||||
|
||||
with open(file_name, 'r') as file:
|
||||
data = json.load(file)
|
||||
return data
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/workspace/get_db")
|
||||
async def get_workspace(request):
|
||||
# Extract the table parameter from the query string
|
||||
table = request.query.get('table')
|
||||
data = await asyncio.to_thread(read_table, table)
|
||||
return web.json_response(data)
|
||||
|
||||
def get_my_workflows_dir():
|
||||
data = read_table('userSettings')
|
||||
if (data):
|
||||
records = json.loads(data)
|
||||
|
||||
if DEFAULT_USER in records and 'myWorkflowsDir' in records[DEFAULT_USER]:
|
||||
curDir = records[DEFAULT_USER]['myWorkflowsDir']
|
||||
elif 'myWorkflowsDir' in records:
|
||||
curDir = records['myWorkflowsDir']
|
||||
if os.path.exists(curDir):
|
||||
return curDir
|
||||
return os.path.join(comfy_path, 'my_workflows')
|
||||
@@ -1,6 +1,5 @@
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
import platform
|
||||
import tempfile
|
||||
import server
|
||||
@@ -22,7 +21,7 @@ async def save_file(request):
|
||||
def save_file_sync(reqJson):
|
||||
current_path = reqJson.get('path')
|
||||
new_json_data_str = reqJson.get('json')
|
||||
|
||||
current_path = os.path.join(get_my_workflows_dir(), current_path)
|
||||
try:
|
||||
new_json_data = json.loads(new_json_data_str)
|
||||
except json.JSONDecodeError:
|
||||
@@ -57,12 +56,10 @@ async def rename_file(request):
|
||||
return web.json_response(data, content_type='application/json')
|
||||
|
||||
def rename_file_sync(reqJson):
|
||||
current_path = reqJson.get('path')
|
||||
current_path = Path(get_my_workflows_dir()) / reqJson['path']
|
||||
new_name = reqJson.get('newName')
|
||||
try:
|
||||
new_path = Path(current_path).parent / new_name
|
||||
# if new_path.suffix != ".json":
|
||||
# raise ValueError("New file name must have a .json extension")
|
||||
new_path = current_path.parent / new_name
|
||||
Path(current_path).rename(new_path)
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
@@ -77,10 +74,9 @@ async def move_file(request):
|
||||
def move_file_sync(reqJson):
|
||||
current_path = reqJson.get('path')
|
||||
new_path = reqJson.get('newParentPath')
|
||||
current_path = os.path.join(get_my_workflows_dir(), current_path)
|
||||
new_path = os.path.join(get_my_workflows_dir(), new_path)
|
||||
try:
|
||||
target_path = Path(new_path) / Path(current_path).name
|
||||
# if target_path.suffix != ".json":
|
||||
# raise ValueError("Target file must be a .json file")
|
||||
shutil.move(current_path, new_path)
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
@@ -97,10 +93,10 @@ def create_workflow_file(reqJson):
|
||||
name = reqJson.get('name')
|
||||
workflow_json = reqJson.get('json')
|
||||
|
||||
if not parentFolderPath or not name or workflow_json is None:
|
||||
if not name or workflow_json is None:
|
||||
print('🔴Create workflow missing necessary data')
|
||||
return {} # Missing necessary data
|
||||
|
||||
abs_path = Path(parentFolderPath)
|
||||
abs_path = Path(get_my_workflows_dir()) / parentFolderPath
|
||||
# Create the directory if it does not exist
|
||||
abs_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -115,7 +111,6 @@ def create_workflow_file(reqJson):
|
||||
counter += 1
|
||||
|
||||
new_file_path = abs_path / new_file_name
|
||||
|
||||
# Writing JSON data to the file
|
||||
with open(new_file_path, 'w', encoding='utf-8') as file:
|
||||
file.write(workflow_json)
|
||||
@@ -126,7 +121,7 @@ def create_workflow_file(reqJson):
|
||||
return {} # In case of any error
|
||||
|
||||
def read_workflow_file(path, id):
|
||||
abs_path = Path(path)
|
||||
abs_path = os.path.join(get_my_workflows_dir(), path)
|
||||
create_time, update_time = getFileCreateTime(abs_path)
|
||||
with open(abs_path, 'r', encoding='utf-8') as f:
|
||||
json_data = json.load(f)
|
||||
@@ -200,15 +195,6 @@ def file_handle(name, file, fileList, file_path):
|
||||
}
|
||||
fileList.append(fileInfo)
|
||||
|
||||
def atomic_json_update(filepath, data):
|
||||
# Generate a temporary file
|
||||
dir_name, file_name = os.path.split(filepath)
|
||||
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', dir=dir_name, delete=False) as tmp_file:
|
||||
json.dump(data, tmp_file, indent=4)
|
||||
temp_name = tmp_file.name
|
||||
# Replace the old file with the new file atomically
|
||||
os.replace(temp_name, filepath)
|
||||
|
||||
@server.PromptServer.instance.routes.post('/workspace/file/gen_unique_name')
|
||||
async def generate_unique_file_name(request):
|
||||
reqJson = await request.json()
|
||||
@@ -220,7 +206,7 @@ def generate_unique_file_name_sync(reqJson):
|
||||
if not file_path:
|
||||
return {"success": False, "error": "File path is required"}
|
||||
|
||||
path = Path(file_path)
|
||||
path = Path(get_my_workflows_dir()) / file_path
|
||||
if not path.parent.is_dir():
|
||||
return {"success": False, "error": "Directory of the provided path does not exist"}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ import glob
|
||||
from threading import Lock
|
||||
import server
|
||||
import uuid
|
||||
from .twoway_sync_folder_service import get_my_workflows_abs_path
|
||||
|
||||
@server.PromptServer.instance.routes.post('/workspace/file/scan_my_workflows_folder')
|
||||
async def scan_my_workflows_files(request):
|
||||
reqJson = await request.json()
|
||||
path = reqJson['path']
|
||||
path = get_my_workflows_abs_path(path)
|
||||
recursive = reqJson.get('recursive', False)
|
||||
metaInfoOnly = reqJson.get('metaInfoOnly', False)
|
||||
|
||||
@@ -68,8 +70,6 @@ def file_handle(name, fileList, file_path, metaInfoOnly):
|
||||
|
||||
fileList.append(fileInfo)
|
||||
|
||||
# Note: Implement getFileCreateTime function as per your existing logic
|
||||
|
||||
def getFileCreateTime(path):
|
||||
# Cross-platform compatibility for creation time
|
||||
file_stats = os.stat(path)
|
||||
|
||||
@@ -4,6 +4,7 @@ from aiohttp import web
|
||||
import os
|
||||
from pathlib import Path
|
||||
import server
|
||||
from .db_service import get_my_workflows_dir
|
||||
|
||||
@server.PromptServer.instance.routes.post('/workspace/folder/create')
|
||||
async def create_folder(request):
|
||||
@@ -13,6 +14,7 @@ async def create_folder(request):
|
||||
|
||||
def create_folder_sync(reqJson):
|
||||
folder_path = reqJson.get('path')
|
||||
folder_path = os.path.join(get_my_workflows_dir(), folder_path)
|
||||
try:
|
||||
Path(folder_path).mkdir(parents=True, exist_ok=True)
|
||||
return {"success": True}
|
||||
@@ -27,6 +29,7 @@ async def delete_folder(request):
|
||||
|
||||
def delete_folder_sync(reqJson):
|
||||
folder_path = reqJson.get('path')
|
||||
folder_path = os.path.join(get_my_workflows_dir(), folder_path)
|
||||
try:
|
||||
shutil.rmtree(folder_path)
|
||||
return {"success": True}
|
||||
@@ -41,6 +44,7 @@ async def rename_folder(request):
|
||||
|
||||
def rename_folder_sync(reqJson):
|
||||
current_path = reqJson.get('absPath')
|
||||
current_path = os.path.join(get_my_workflows_dir(), current_path)
|
||||
new_name = reqJson.get('newName')
|
||||
try:
|
||||
new_path = Path(current_path).parent / new_name
|
||||
@@ -57,9 +61,14 @@ async def move_folder(request):
|
||||
|
||||
def move_folder_sync(reqJson):
|
||||
current_path = reqJson.get('folder')
|
||||
new_path = reqJson.get('newParentPath')
|
||||
current_path = os.path.join(get_my_workflows_dir(), current_path)
|
||||
new_path = os.path.join(get_my_workflows_dir(), reqJson.get('newParentPath',""))
|
||||
try:
|
||||
shutil.move(current_path, new_path)
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def get_my_workflows_abs_path(rel_path: str):
|
||||
myworkflows = get_my_workflows_dir()
|
||||
return os.path.join(myworkflows, rel_path)
|
||||
|
||||
@@ -122,6 +122,16 @@ export async function openWorkflowsFolder() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchMyWorkflowsDir(){
|
||||
const resp = await fetch("/workspace/get_my_workflows_dir");
|
||||
const res = (await resp.json()) as { path?: string; error?: string; os:string };
|
||||
if (res.error) {
|
||||
alert(`Failed to fetch my workflows path: ${res.error}`)
|
||||
|
||||
}
|
||||
return(res.path)
|
||||
}
|
||||
|
||||
export async function getAllModelsList() {
|
||||
try {
|
||||
const response = await fetch("/model_manager/get_model_list", {
|
||||
|
||||
+13
-2
@@ -27,6 +27,8 @@ import {
|
||||
getWorkflowIdInUrlHash,
|
||||
generateUrlHashWithFlowId,
|
||||
openWorkflowInNewTab,
|
||||
validateOrSaveAllJsonFileMyWorkflows,
|
||||
rewriteAllLocalFiles,
|
||||
} from "./utils";
|
||||
import { Topbar } from "./topbar/Topbar";
|
||||
import { EShortcutKeys, Workflow, WorkflowVersion } from "./types/dbTypes";
|
||||
@@ -151,10 +153,10 @@ export default function App() {
|
||||
if (getWorkflowIdInUrlHash()) {
|
||||
const newUrlHash = generateUrlHashWithFlowId(id);
|
||||
window.location.hash = newUrlHash;
|
||||
document.title = name + " - ComfyUI";
|
||||
document.title = workflow!.name + " - ComfyUI";
|
||||
} else {
|
||||
localStorage.setItem("curFlowID", id);
|
||||
document.title = "ComfyUI - " + name;
|
||||
document.title = "ComfyUI - " + workflow!.name;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -177,6 +179,15 @@ export default function App() {
|
||||
if (latestWfID) {
|
||||
loadWorkflowIDImpl(latestWfID);
|
||||
}
|
||||
/**
|
||||
* For two-way sync, one-time rewrite all /my_workflows files to the database
|
||||
*/
|
||||
if (localStorage.getItem("REWRITTEN_ALL_LOCAL_DISK_FILE") === "true") {
|
||||
await validateOrSaveAllJsonFileMyWorkflows();
|
||||
} else {
|
||||
await rewriteAllLocalFiles();
|
||||
localStorage.setItem("REWRITTEN_ALL_LOCAL_DISK_FILE", "true");
|
||||
}
|
||||
};
|
||||
|
||||
const subsribeToWsToStopWarning = () => {
|
||||
|
||||
@@ -223,7 +223,7 @@ export default function RecentFilesDrawer({ onClose, onClickNewFlow }: Props) {
|
||||
<HStack gap={4}>
|
||||
<HStack gap={2}>
|
||||
<Text fontSize={19} fontWeight={600}>
|
||||
☕️Workspace
|
||||
🦄Workspace
|
||||
</Text>
|
||||
{/* <Tooltip label="Login to share and sync your workflows to cloud">
|
||||
<Link onClick={openCognitoPopup}>Login</Link>
|
||||
|
||||
@@ -6,24 +6,9 @@ import {
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
Text,
|
||||
StackDivider,
|
||||
VStack,
|
||||
Box,
|
||||
Tag,
|
||||
IconButton,
|
||||
Button,
|
||||
Flex,
|
||||
Input,
|
||||
Stack,
|
||||
useToast,
|
||||
SimpleGrid,
|
||||
Tooltip,
|
||||
} from "@chakra-ui/react";
|
||||
import { IconEdit } from "@tabler/icons-react";
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
import { getSystemDir } from "../Api";
|
||||
import { userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
import { ShortcutSettings } from "../settings/ShortcutSettings";
|
||||
import FolderOnTopSettings from "../settings/FolderOnTopSettings";
|
||||
import TwoWaySyncSettings from "../settings/TwoWaySyncSettings";
|
||||
|
||||
@@ -1,29 +1,17 @@
|
||||
import { COMFYSPACE_TRACKING_FIELD_NAME } from "../const";
|
||||
import { userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
import { indexdb } from "../db-tables/indexdb";
|
||||
import { Workflow } from "../types/dbTypes";
|
||||
import { genAbsPathByRelPath, sanitizeAbsPath } from "../utils/OsPathUtils";
|
||||
import {
|
||||
getWorkflowRelPath,
|
||||
joinRelPath,
|
||||
sanitizeRelPath,
|
||||
} from "../utils/OsPathUtils";
|
||||
|
||||
export namespace TwowaySyncAPI {
|
||||
async function genWorkflowAbsPath({
|
||||
parentFolderID,
|
||||
name,
|
||||
}: Workflow): Promise<string> {
|
||||
const myWorkflowsDir =
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(
|
||||
`${myWorkflowsDir}/${parentFolderID ?? ""}/${name}.json`,
|
||||
);
|
||||
return absPath;
|
||||
}
|
||||
export async function moveWorkflow(
|
||||
workflow: Workflow,
|
||||
newParentFolderRelPath: string,
|
||||
) {
|
||||
const absPath = await genWorkflowAbsPath(workflow);
|
||||
const newParentFolderAbs = await genAbsPathByRelPath(
|
||||
newParentFolderRelPath,
|
||||
);
|
||||
try {
|
||||
const response = await fetch("/workspace/file/move", {
|
||||
method: "POST",
|
||||
@@ -31,8 +19,8 @@ export namespace TwowaySyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
newParentPath: newParentFolderAbs,
|
||||
path: getWorkflowRelPath(workflow),
|
||||
newParentPath: newParentFolderRelPath,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
@@ -50,7 +38,6 @@ export namespace TwowaySyncAPI {
|
||||
fileName: string,
|
||||
parentFolderID: string | null,
|
||||
): Promise<string | null> {
|
||||
const absPath = await genAbsPathByRelPath(`${parentFolderID}/${fileName}`);
|
||||
try {
|
||||
const response = await fetch("/workspace/file/gen_unique_name", {
|
||||
method: "POST",
|
||||
@@ -58,7 +45,7 @@ export namespace TwowaySyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
path: joinRelPath(parentFolderID ?? "", fileName),
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
@@ -77,11 +64,6 @@ export namespace TwowaySyncAPI {
|
||||
{ parentFolderID, name }: Pick<Workflow, "name" | "parentFolderID">,
|
||||
newName: string,
|
||||
) {
|
||||
const myWorkflowsDir =
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(
|
||||
`${myWorkflowsDir}/${parentFolderID ?? ""}/${name}.json`,
|
||||
);
|
||||
try {
|
||||
const response = await fetch("/workspace/file/rename", {
|
||||
method: "POST",
|
||||
@@ -89,13 +71,13 @@ export namespace TwowaySyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
path: joinRelPath(parentFolderID ?? "", name + ".json"),
|
||||
newName: newName,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.error) {
|
||||
alert("Error moving file: " + result.error);
|
||||
alert("Error rename file: " + result.error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -112,29 +94,8 @@ export namespace TwowaySyncAPI {
|
||||
}>;
|
||||
};
|
||||
}
|
||||
export async function scanMyWorkflowsDupId() {
|
||||
const myWorkflowsDir =
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(myWorkflowsDir!);
|
||||
try {
|
||||
const response = await fetch("/workspace/file/scan_dup_id", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
}),
|
||||
});
|
||||
const result = (await response.json()) as DuplicatesResponse;
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error deleting file:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveWorkflow(workflow: Workflow) {
|
||||
const absPath = await genWorkflowAbsPath(workflow);
|
||||
const json = workflow.json;
|
||||
const flow = JSON.parse(json);
|
||||
flow.extra[COMFYSPACE_TRACKING_FIELD_NAME] = {
|
||||
@@ -147,7 +108,7 @@ export namespace TwowaySyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
path: getWorkflowRelPath(workflow),
|
||||
json: JSON.stringify(flow),
|
||||
}),
|
||||
});
|
||||
@@ -163,7 +124,6 @@ export namespace TwowaySyncAPI {
|
||||
return result;
|
||||
}
|
||||
export async function deleteWorkflow(workflow: Workflow) {
|
||||
const absPath = await genWorkflowAbsPath(workflow);
|
||||
try {
|
||||
const response = await fetch("/workspace/delete_file", {
|
||||
method: "POST",
|
||||
@@ -171,7 +131,7 @@ export namespace TwowaySyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
file_path: absPath,
|
||||
file_path: getWorkflowRelPath(workflow),
|
||||
deleteEmptyFolder: false,
|
||||
}),
|
||||
});
|
||||
@@ -188,11 +148,6 @@ export namespace TwowaySyncAPI {
|
||||
json,
|
||||
id,
|
||||
}: Workflow) {
|
||||
const myWorkflowsDir =
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(
|
||||
`${myWorkflowsDir}/${parentFolderID ?? ""}`,
|
||||
);
|
||||
let jsonObj: any = JSON.parse(json);
|
||||
|
||||
jsonObj = {
|
||||
@@ -204,10 +159,11 @@ export namespace TwowaySyncAPI {
|
||||
},
|
||||
};
|
||||
const input: { parentFolderPath: string; name: string; json: string } = {
|
||||
parentFolderPath: absPath,
|
||||
parentFolderPath: sanitizeRelPath(parentFolderID ?? ""),
|
||||
name: name,
|
||||
json: JSON.stringify(jsonObj),
|
||||
};
|
||||
console.log("createWorkflow", input);
|
||||
try {
|
||||
const response = await fetch("/workspace/create_workflow_file", {
|
||||
method: "POST",
|
||||
@@ -220,13 +176,15 @@ export namespace TwowaySyncAPI {
|
||||
|
||||
result.name &&
|
||||
(await indexdb.workflows?.update(id, {
|
||||
filePath: `${absPath}/${name}.json`,
|
||||
name: result.name,
|
||||
}));
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`Error creating file <${id}> at "${absPath}"`, error);
|
||||
alert(`Error creating file <${id}> at "${absPath}"`);
|
||||
console.error(
|
||||
`Error creating file <${id}> at "${parentFolderID}"`,
|
||||
error,
|
||||
);
|
||||
alert(`Error creating file <${id}> at "${parentFolderID}"`);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -242,11 +200,7 @@ export namespace TwowaySyncAPI {
|
||||
json?: Object;
|
||||
error?: string;
|
||||
}> {
|
||||
const myWorkflowsDir =
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(
|
||||
`${myWorkflowsDir}/${parentFolderID ?? ""}/${name}.json`,
|
||||
);
|
||||
const relPath = joinRelPath(parentFolderID ?? "", name + ".json");
|
||||
try {
|
||||
const response = await fetch("/workspace/get_workflow_file", {
|
||||
method: "POST",
|
||||
@@ -255,24 +209,24 @@ export namespace TwowaySyncAPI {
|
||||
},
|
||||
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
path: relPath,
|
||||
id: id,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.error) {
|
||||
console.error(
|
||||
`Error getting file <${id}> at "${absPath}"`,
|
||||
`Error getting file <${id}> at "${relPath}"`,
|
||||
result.error,
|
||||
);
|
||||
alert({
|
||||
message: `Error getting file <${id}> at "${absPath}"`,
|
||||
message: `Error getting file <${id}> at "${relPath}"`,
|
||||
level: "error",
|
||||
});
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`Erro finding file <${id}> at "${absPath}"`, error);
|
||||
console.error(`Erro finding file <${id}> at "${relPath}"`, error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -287,8 +241,10 @@ export type ScanLocalFile = {
|
||||
updateTime: number;
|
||||
};
|
||||
export type ScanLocalFolder = {
|
||||
id: string;
|
||||
type: "folder";
|
||||
name: string;
|
||||
parentFolderID: string;
|
||||
};
|
||||
export async function scanLocalFiles(
|
||||
path: string,
|
||||
@@ -302,7 +258,7 @@ export async function scanLocalFiles(
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: sanitizeAbsPath(path),
|
||||
path: sanitizeRelPath(path),
|
||||
recursive,
|
||||
metaInfoOnly,
|
||||
}),
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
import { Folder } from "../types/dbTypes";
|
||||
import {
|
||||
genAbsPathByRelPath,
|
||||
sanitizeAbsPath,
|
||||
sanitizeRelPath,
|
||||
} from "../utils/OsPathUtils";
|
||||
import { sanitizeRelPath } from "../utils/OsPathUtils";
|
||||
|
||||
export namespace TwowayFolderSyncAPI {
|
||||
export async function genFolderRelPath({
|
||||
@@ -16,26 +11,15 @@ export namespace TwowayFolderSyncAPI {
|
||||
}): Promise<string> {
|
||||
return sanitizeRelPath(`${parentFolderID ?? ""}/${name}`);
|
||||
}
|
||||
export async function genFolderAbsPath({
|
||||
parentFolderID,
|
||||
name,
|
||||
}: Pick<Folder, "name" | "parentFolderID">): Promise<string> {
|
||||
const myWorkflowsDir =
|
||||
await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(
|
||||
`${myWorkflowsDir}/${parentFolderID ?? ""}/${name}`,
|
||||
);
|
||||
return absPath;
|
||||
}
|
||||
|
||||
export async function createFolder(folder: Folder) {
|
||||
const abs = await genFolderAbsPath(folder);
|
||||
await fetch("/workspace/folder/create", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: abs,
|
||||
path: sanitizeRelPath(folder.id),
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -43,16 +27,14 @@ export namespace TwowayFolderSyncAPI {
|
||||
folderToBeMoved: string,
|
||||
newParentPath: string,
|
||||
) {
|
||||
const absPath = await genAbsPathByRelPath(folderToBeMoved);
|
||||
const absNewParentPath = await genAbsPathByRelPath(newParentPath);
|
||||
const resp = await fetch("/workspace/folder/move", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
folder: absPath,
|
||||
newParentPath: absNewParentPath,
|
||||
folder: folderToBeMoved,
|
||||
newParentPath: newParentPath,
|
||||
}),
|
||||
});
|
||||
const result = await resp.json();
|
||||
@@ -62,7 +44,6 @@ export namespace TwowayFolderSyncAPI {
|
||||
return;
|
||||
}
|
||||
export async function deleteFolder(relPath: string) {
|
||||
const absPath = await genAbsPathByRelPath(relPath);
|
||||
try {
|
||||
const response = await fetch("/workspace/folder/delete", {
|
||||
method: "POST",
|
||||
@@ -70,7 +51,7 @@ export namespace TwowayFolderSyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
path: relPath,
|
||||
}),
|
||||
});
|
||||
const result = await response.text();
|
||||
@@ -83,7 +64,6 @@ export namespace TwowayFolderSyncAPI {
|
||||
export async function genFilesCountInFolder(
|
||||
folderRelPath: string,
|
||||
): Promise<number | null> {
|
||||
const absPath = await genAbsPathByRelPath(folderRelPath);
|
||||
try {
|
||||
const response = await fetch("/workspace/file/count_files", {
|
||||
method: "POST",
|
||||
@@ -91,7 +71,7 @@ export namespace TwowayFolderSyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path: absPath,
|
||||
path: folderRelPath,
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
@@ -109,7 +89,6 @@ export namespace TwowayFolderSyncAPI {
|
||||
folderRelPath: string,
|
||||
newName: string,
|
||||
): Promise<void> {
|
||||
const oldAbsPath = await genAbsPathByRelPath(folderRelPath);
|
||||
try {
|
||||
const response = await fetch("/workspace/folder/rename", {
|
||||
method: "POST",
|
||||
@@ -117,7 +96,7 @@ export namespace TwowayFolderSyncAPI {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
absPath: oldAbsPath,
|
||||
absPath: folderRelPath,
|
||||
newName: newName,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -6,7 +6,6 @@ import { TableBase } from "./TableBase";
|
||||
import { indexdb } from "./indexdb";
|
||||
import { TwowayFolderSyncAPI } from "../apis/TwowaySyncFolderApi";
|
||||
import { TwowaySyncAPI, scanLocalFiles } from "../apis/TwowaySyncApi";
|
||||
import { genAbsPathByRelPath } from "../utils/OsPathUtils";
|
||||
|
||||
export class FoldersTable extends TableBase<Folder> {
|
||||
static readonly TABLE_NAME = "folders";
|
||||
@@ -98,9 +97,7 @@ export class FoldersTable extends TableBase<Folder> {
|
||||
) {
|
||||
const twoWaySyncEnabled = await userSettingsTable?.getSetting("twoWaySync");
|
||||
if (twoWaySyncEnabled) {
|
||||
const absPath = await genAbsPathByRelPath(id);
|
||||
// delete from indexdb
|
||||
await scanLocalFiles(absPath, true, true).then((files) => {
|
||||
await scanLocalFiles(id, true, true).then((files) => {
|
||||
files.forEach((file) => {
|
||||
if (file.type === "workflow") {
|
||||
console.log("delete workflow", file);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { UserSettings } from "../types/dbTypes";
|
||||
import { TableBase } from "./TableBase";
|
||||
import { MODEL_TYPE_TO_FOLDER_MAPPING } from "../model-manager/install-models/util/modelTypes";
|
||||
import { fetchMyWorkflowsDir } from "../Api";
|
||||
|
||||
export class UserSettingsTable extends TableBase<UserSettings> {
|
||||
public defaultSettings: UserSettings;
|
||||
@@ -91,13 +92,9 @@ export class UserSettingsTable extends TableBase<UserSettings> {
|
||||
|
||||
static async load(): Promise<UserSettingsTable> {
|
||||
const instance = new UserSettingsTable();
|
||||
const resp = await fetch("/workspace/get_default_my_workflows_dir");
|
||||
const res = (await resp.json()) as { path?: string; error?: string };
|
||||
if (res.error) {
|
||||
console.error("Failed to get myWorkflowsDir");
|
||||
} else if (res.path) {
|
||||
instance.defaultSettings.myWorkflowsDir = res.path;
|
||||
}
|
||||
const myWorkflowsDir = await fetchMyWorkflowsDir();
|
||||
|
||||
instance.defaultSettings.myWorkflowsDir = myWorkflowsDir!;
|
||||
|
||||
await instance.getSetting("autoSave").then((res) => {
|
||||
instance._autoSave = res ?? true;
|
||||
|
||||
@@ -20,8 +20,9 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { IconEdit } from "@tabler/icons-react";
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
import { getSystemDir } from "../Api";
|
||||
import { fetchMyWorkflowsDir, getSystemDir } from "../Api";
|
||||
import { userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
import { validateOrSaveAllJsonFileMyWorkflows } from "../utils";
|
||||
|
||||
export default function SelectMyWorkflowsDir() {
|
||||
const [isManualEntry, setIsManualEntry] = useState(false);
|
||||
@@ -35,9 +36,7 @@ export default function SelectMyWorkflowsDir() {
|
||||
const toast = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
userSettingsTable?.getSetting("myWorkflowsDir").then((res) => {
|
||||
setCurrentDirectory(res);
|
||||
});
|
||||
fetchMyWorkflowsDir().then((dir) => setCurrentDirectory(dir ?? ""));
|
||||
userSettingsTable?.getSetting("twoWaySync").then((res) => {
|
||||
setTwoWaySync(res ?? false);
|
||||
});
|
||||
@@ -69,7 +68,8 @@ export default function SelectMyWorkflowsDir() {
|
||||
setSubdirectoryList(dirList);
|
||||
setIsEditDirectory(true);
|
||||
setDirPathList(
|
||||
(currentDirectory && currentDirectory.split("/").filter((p) => !!p)) ||
|
||||
(currentDirectory &&
|
||||
currentDirectory.split(/[\\|\/]/).filter((p) => !!p)) ||
|
||||
[],
|
||||
);
|
||||
}
|
||||
@@ -106,7 +106,7 @@ export default function SelectMyWorkflowsDir() {
|
||||
manualEntry && setNoPermission(false);
|
||||
onCloseEditDirectory();
|
||||
// to update /my_workflows files in disk to new location
|
||||
await userSettingsTable?.getSetting("twoWaySync");
|
||||
validateOrSaveAllJsonFileMyWorkflows();
|
||||
};
|
||||
|
||||
const onReset = async () => {
|
||||
|
||||
@@ -151,6 +151,8 @@ export function sortFlows(
|
||||
export async function validateOrSaveAllJsonFileMyWorkflows(
|
||||
deleteEmptyFolder = false,
|
||||
) {
|
||||
const twoWaySync = await userSettingsTable?.getSetting("twoWaySync");
|
||||
if (twoWaySync) return; // disable this function in two way sync mode
|
||||
const flowList = (await workflowsTable?.listAll()) ?? [];
|
||||
for (const workflow of flowList) {
|
||||
const fullPath = await generateFilePathAbsolute(workflow);
|
||||
|
||||
+20
-17
@@ -1,24 +1,27 @@
|
||||
import { userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
import type { Workflow } from "../types/dbTypes";
|
||||
|
||||
export function osPathJoin(...args: string[]) {
|
||||
const joined = args.filter((segment) => segment !== "").join("/");
|
||||
if (joined.endsWith("/")) {
|
||||
return joined.slice(0, -1);
|
||||
}
|
||||
return sanitizeRelPath(joined);
|
||||
export function joinRelPath(...segments: string[]) {
|
||||
const rel = segments.filter((segment) => segment !== "").join("/");
|
||||
return sanitizeRelPath(rel);
|
||||
}
|
||||
|
||||
export function sanitizeAbsPath(path: string) {
|
||||
const segments = path.split("/").filter((segment) => segment !== "");
|
||||
return "/" + segments.join("/");
|
||||
}
|
||||
export function sanitizeRelPath(path: string) {
|
||||
const segments = path.split("/").filter((segment) => segment !== "");
|
||||
return segments.join("/");
|
||||
const segments = path
|
||||
.split("/")
|
||||
.filter((segment) => segment !== "")
|
||||
.join("/");
|
||||
// Replace backslashes with forward slashes to handle Windows paths
|
||||
let sanitizedPath = segments.replace(/\\/g, "/");
|
||||
// Remove leading slashes to ensure the path is treated as relative
|
||||
sanitizedPath = sanitizedPath.replace(/^\/+/, "");
|
||||
|
||||
return sanitizedPath;
|
||||
}
|
||||
|
||||
export async function genAbsPathByRelPath(relPath: string): Promise<string> {
|
||||
const myWorkflowsDir = await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
const absPath = sanitizeAbsPath(`${myWorkflowsDir}/${relPath}`);
|
||||
return absPath;
|
||||
export function getWorkflowRelPath(workflow: Workflow) {
|
||||
return joinRelPath(workflow.parentFolderID ?? "", workflow.name + ".json");
|
||||
}
|
||||
|
||||
export function getFolderRelPath(folder: { id: string }) {
|
||||
return folder.id;
|
||||
}
|
||||
|
||||
@@ -8,32 +8,23 @@ import { isFolder, userSettingsTable } from "../db-tables/WorkspaceDB";
|
||||
import { indexdb } from "../db-tables/indexdb";
|
||||
import { Folder, Workflow } from "../types/dbTypes";
|
||||
import { sortFileItem } from "../utils";
|
||||
import { sanitizeAbsPath } from "./OsPathUtils";
|
||||
import { sanitizeRelPath } from "./OsPathUtils";
|
||||
|
||||
export async function scanMyWorkflowsDir(
|
||||
parentFolderID: string | null,
|
||||
sortBy?: ESortTypes,
|
||||
): Promise<(Workflow | Folder)[]> {
|
||||
const myWorkflowsDir = await userSettingsTable?.getSetting("myWorkflowsDir");
|
||||
if (myWorkflowsDir == null || myWorkflowsDir === "") {
|
||||
alert("No Workspace saving directory found, please go to Settings.");
|
||||
return [];
|
||||
}
|
||||
const parentRelPath = parentFolderID ?? "";
|
||||
const parentRelPath = sanitizeRelPath(parentFolderID ?? "");
|
||||
// folderID is the relative path of the folder in two way sync mode
|
||||
const scanDir = `${myWorkflowsDir}/${parentRelPath}`;
|
||||
const fileList = await scanLocalFiles(scanDir);
|
||||
const fileList = await scanLocalFiles(parentRelPath);
|
||||
|
||||
const allFilesPromises = fileList.map(async (file) => {
|
||||
const fileName = file.name;
|
||||
const absPath = sanitizeAbsPath([scanDir, fileName].join("/"));
|
||||
// const absPath = osPathJoin(scanDir, fileName);
|
||||
const relPath = [parentRelPath, fileName].join("/");
|
||||
|
||||
if (scanFileIsFolder(file)) {
|
||||
// is folder
|
||||
const folder: Folder = {
|
||||
id: relPath,
|
||||
id: sanitizeRelPath(relPath),
|
||||
name: file.name,
|
||||
parentFolderID: parentRelPath,
|
||||
type: "folder",
|
||||
@@ -51,7 +42,6 @@ export async function scanMyWorkflowsDir(
|
||||
const newWorkflow: Workflow = {
|
||||
...existingWorkflow,
|
||||
id: file.id,
|
||||
filePath: absPath,
|
||||
lastSavedJson: file.json ?? "{}",
|
||||
json: file.json ?? "{}",
|
||||
name: fileName.replace(/\.json$/, ""),
|
||||
|
||||
Reference in New Issue
Block a user