Merge branch 'dev' into aller-lock

This commit is contained in:
yanle
2024-03-04 23:45:53 +08:00
17 changed files with 187 additions and 251 deletions
+6 -58
View File
@@ -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()
+63
View File
@@ -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')
+10 -24
View File
@@ -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"}
+2 -2
View File
@@ -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)
+10 -1
View File
@@ -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)
+10
View File
@@ -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
View File
@@ -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";
+27 -71
View File
@@ -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,
}),
+8 -29
View File
@@ -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,
}),
});
+1 -4
View File
@@ -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);
+4 -7
View 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;
+6 -6
View File
@@ -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 () => {
+2
View File
@@ -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
View File
@@ -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;
}
+4 -14
View File
@@ -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$/, ""),