mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-29 13:51:46 +02:00
Filesystem Sync (#142)
This commit is contained in:
22
src-tauri/yaak-sync/Cargo.toml
Normal file
22
src-tauri/yaak-sync/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "yaak-sync"
|
||||
links = "yaak-sync"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
yaak-models = { workspace = true }
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
tauri = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||
serde_yaml = "0.9.34"
|
||||
log = "0.4.22"
|
||||
serde_json = "1.0.132"
|
||||
hex = "0.4.3"
|
||||
sha1 = "0.10.6"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { version = "2.0.3", features = ["build"] }
|
||||
7
src-tauri/yaak-sync/bindings/git.ts
Normal file
7
src-tauri/yaak-sync/bindings/git.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type GitCommit = { author: string, when: string, message: string | null, };
|
||||
|
||||
export type GitStatus = "added" | "conflict" | "current" | "modified" | "removed" | "renamed" | "type_change";
|
||||
|
||||
export type GitStatusEntry = { relaPath: string, status: GitStatus, staged: boolean, prev: string | null, next: string | null, };
|
||||
23
src-tauri/yaak-sync/bindings/models.ts
Normal file
23
src-tauri/yaak-sync/bindings/models.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
||||
|
||||
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||
|
||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||
|
||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
|
||||
|
||||
export type SyncModel = { "type": "workspace" } & Workspace | { "type": "environment" } & Environment | { "type": "folder" } & Folder | { "type": "http_request" } & HttpRequest | { "type": "grpc_request" } & GrpcRequest;
|
||||
|
||||
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
|
||||
|
||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, settingSyncDir: string | null, };
|
||||
7
src-tauri/yaak-sync/bindings/sync.ts
Normal file
7
src-tauri/yaak-sync/bindings/sync.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { SyncModel } from "./models";
|
||||
import type { SyncState } from "./models";
|
||||
|
||||
export type FsCandidate = { "type": "FsCandidate", model: SyncModel, relPath: string, checksum: string, };
|
||||
|
||||
export type SyncOp = { "type": "fsCreate", model: SyncModel, } | { "type": "fsUpdate", model: SyncModel, state: SyncState, } | { "type": "fsDelete", state: SyncState, fs: FsCandidate | null, } | { "type": "dbCreate", fs: FsCandidate, } | { "type": "dbUpdate", state: SyncState, fs: FsCandidate, } | { "type": "dbDelete", model: SyncModel, state: SyncState, };
|
||||
5
src-tauri/yaak-sync/build.rs
Normal file
5
src-tauri/yaak-sync/build.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
const COMMANDS: &[&str] = &["calculate", "apply"];
|
||||
|
||||
fn main() {
|
||||
tauri_plugin::Builder::new(COMMANDS).build();
|
||||
}
|
||||
20
src-tauri/yaak-sync/index.ts
Normal file
20
src-tauri/yaak-sync/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { Workspace } from '@yaakapp-internal/models';
|
||||
import { SyncOp } from './bindings/sync';
|
||||
|
||||
export const calculateSync = async (workspace: Workspace) => {
|
||||
if (!workspace.settingSyncDir) throw new Error("Workspace sync dir not configured");
|
||||
|
||||
return invoke<SyncOp[]>('plugin:yaak-sync|calculate', {
|
||||
workspaceId: workspace.id,
|
||||
dir: workspace.settingSyncDir,
|
||||
});
|
||||
};
|
||||
|
||||
export const applySync = async (workspace: Workspace, ops: SyncOp[]) => {
|
||||
console.log('Applying sync', ops);
|
||||
return invoke<void>('plugin:yaak-sync|apply', {
|
||||
workspaceId: workspace.id,
|
||||
dir: workspace.settingSyncDir,
|
||||
});
|
||||
};
|
||||
6
src-tauri/yaak-sync/package.json
Normal file
6
src-tauri/yaak-sync/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@yaakapp-internal/sync",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "index.ts"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-activate"
|
||||
description = "Enables the activate command without any pre-configured scope."
|
||||
commands.allow = ["activate"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-activate"
|
||||
description = "Denies the activate command without any pre-configured scope."
|
||||
commands.deny = ["activate"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-add"
|
||||
description = "Enables the add command without any pre-configured scope."
|
||||
commands.allow = ["add"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-add"
|
||||
description = "Denies the add command without any pre-configured scope."
|
||||
commands.deny = ["add"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-apply"
|
||||
description = "Enables the apply command without any pre-configured scope."
|
||||
commands.allow = ["apply"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-apply"
|
||||
description = "Denies the apply command without any pre-configured scope."
|
||||
commands.deny = ["apply"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-calculate"
|
||||
description = "Enables the calculate command without any pre-configured scope."
|
||||
commands.allow = ["calculate"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-calculate"
|
||||
description = "Denies the calculate command without any pre-configured scope."
|
||||
commands.deny = ["calculate"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-check"
|
||||
description = "Enables the check command without any pre-configured scope."
|
||||
commands.allow = ["check"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-check"
|
||||
description = "Denies the check command without any pre-configured scope."
|
||||
commands.deny = ["check"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-checkout"
|
||||
description = "Enables the checkout command without any pre-configured scope."
|
||||
commands.allow = ["checkout"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-checkout"
|
||||
description = "Denies the checkout command without any pre-configured scope."
|
||||
commands.deny = ["checkout"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-cmd-add"
|
||||
description = "Enables the cmd_add command without any pre-configured scope."
|
||||
commands.allow = ["cmd_add"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-cmd-add"
|
||||
description = "Denies the cmd_add command without any pre-configured scope."
|
||||
commands.deny = ["cmd_add"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-cmd-checkout"
|
||||
description = "Enables the cmd_checkout command without any pre-configured scope."
|
||||
commands.allow = ["cmd_checkout"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-cmd-checkout"
|
||||
description = "Denies the cmd_checkout command without any pre-configured scope."
|
||||
commands.deny = ["cmd_checkout"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-cmd-commit"
|
||||
description = "Enables the cmd_commit command without any pre-configured scope."
|
||||
commands.allow = ["cmd_commit"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-cmd-commit"
|
||||
description = "Denies the cmd_commit command without any pre-configured scope."
|
||||
commands.deny = ["cmd_commit"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-cmd-init"
|
||||
description = "Enables the cmd_init command without any pre-configured scope."
|
||||
commands.allow = ["cmd_init"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-cmd-init"
|
||||
description = "Denies the cmd_init command without any pre-configured scope."
|
||||
commands.deny = ["cmd_init"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-cmd-log"
|
||||
description = "Enables the cmd_log command without any pre-configured scope."
|
||||
commands.allow = ["cmd_log"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-cmd-log"
|
||||
description = "Denies the cmd_log command without any pre-configured scope."
|
||||
commands.deny = ["cmd_log"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-cmd-status"
|
||||
description = "Enables the cmd_status command without any pre-configured scope."
|
||||
commands.allow = ["cmd_status"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-cmd-status"
|
||||
description = "Denies the cmd_status command without any pre-configured scope."
|
||||
commands.deny = ["cmd_status"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-commit"
|
||||
description = "Enables the commit command without any pre-configured scope."
|
||||
commands.allow = ["commit"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-commit"
|
||||
description = "Denies the commit command without any pre-configured scope."
|
||||
commands.deny = ["commit"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-init"
|
||||
description = "Enables the init command without any pre-configured scope."
|
||||
commands.allow = ["init"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-init"
|
||||
description = "Denies the init command without any pre-configured scope."
|
||||
commands.deny = ["init"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-init-repo"
|
||||
description = "Enables the init_repo command without any pre-configured scope."
|
||||
commands.allow = ["init_repo"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-init-repo"
|
||||
description = "Denies the init_repo command without any pre-configured scope."
|
||||
commands.deny = ["init_repo"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-initialize"
|
||||
description = "Enables the initialize command without any pre-configured scope."
|
||||
commands.allow = ["initialize"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-initialize"
|
||||
description = "Denies the initialize command without any pre-configured scope."
|
||||
commands.deny = ["initialize"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-log"
|
||||
description = "Enables the log command without any pre-configured scope."
|
||||
commands.allow = ["log"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-log"
|
||||
description = "Denies the log command without any pre-configured scope."
|
||||
commands.deny = ["log"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-status"
|
||||
description = "Enables the status command without any pre-configured scope."
|
||||
commands.allow = ["status"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-status"
|
||||
description = "Denies the status command without any pre-configured scope."
|
||||
commands.deny = ["status"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-sync"
|
||||
description = "Enables the sync command without any pre-configured scope."
|
||||
commands.allow = ["sync"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-sync"
|
||||
description = "Denies the sync command without any pre-configured scope."
|
||||
commands.deny = ["sync"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-sync-fs"
|
||||
description = "Enables the sync_fs command without any pre-configured scope."
|
||||
commands.allow = ["sync_fs"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-sync-fs"
|
||||
description = "Denies the sync_fs command without any pre-configured scope."
|
||||
commands.deny = ["sync_fs"]
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-unstage"
|
||||
description = "Enables the unstage command without any pre-configured scope."
|
||||
commands.allow = ["unstage"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-unstage"
|
||||
description = "Denies the unstage command without any pre-configured scope."
|
||||
commands.deny = ["unstage"]
|
||||
562
src-tauri/yaak-sync/permissions/autogenerated/reference.md
Normal file
562
src-tauri/yaak-sync/permissions/autogenerated/reference.md
Normal file
@@ -0,0 +1,562 @@
|
||||
## Default Permission
|
||||
|
||||
Default permissions for the plugin
|
||||
|
||||
- `allow-calculate`
|
||||
- `allow-apply`
|
||||
|
||||
## Permission Table
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Identifier</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-activate`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the activate command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-activate`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the activate command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-add`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the add command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-add`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the add command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-apply`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the apply command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-apply`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the apply command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-calculate`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the calculate command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-calculate`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the calculate command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-check`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the check command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-check`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the check command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-checkout`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the checkout command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-checkout`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the checkout command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-cmd-add`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the cmd_add command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-cmd-add`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the cmd_add command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-cmd-checkout`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the cmd_checkout command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-cmd-checkout`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the cmd_checkout command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-cmd-commit`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the cmd_commit command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-cmd-commit`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the cmd_commit command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-cmd-init`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the cmd_init command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-cmd-init`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the cmd_init command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-cmd-log`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the cmd_log command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-cmd-log`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the cmd_log command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-cmd-status`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the cmd_status command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-cmd-status`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the cmd_status command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-commit`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the commit command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-commit`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the commit command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-init`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the init command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-init`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the init command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-init-repo`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the init_repo command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-init-repo`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the init_repo command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-initialize`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the initialize command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-initialize`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the initialize command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-log`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the log command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-log`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the log command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-status`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the status command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-status`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the status command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-sync`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the sync command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-sync`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the sync command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-sync-fs`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the sync_fs command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-sync-fs`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the sync_fs command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:allow-unstage`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the unstage command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-sync:deny-unstage`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the unstage command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
6
src-tauri/yaak-sync/permissions/default.toml
Normal file
6
src-tauri/yaak-sync/permissions/default.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[default]
|
||||
description = "Default permissions for the plugin"
|
||||
permissions = [
|
||||
"allow-calculate",
|
||||
"allow-apply",
|
||||
]
|
||||
515
src-tauri/yaak-sync/permissions/schemas/schema.json
Normal file
515
src-tauri/yaak-sync/permissions/schemas/schema.json
Normal file
@@ -0,0 +1,515 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PermissionFile",
|
||||
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"description": "The default permission set for the plugin",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/DefaultPermission"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"set": {
|
||||
"description": "A list of permissions sets defined",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PermissionSet"
|
||||
}
|
||||
},
|
||||
"permission": {
|
||||
"description": "A list of inlined permissions",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Permission"
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"DefaultPermission": {
|
||||
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"description": "The version of the permission.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"description": {
|
||||
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"permissions": {
|
||||
"description": "All permissions this set contains.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"PermissionSet": {
|
||||
"description": "A set of direct permissions grouped together under a new name.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"description",
|
||||
"identifier",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"identifier": {
|
||||
"description": "A unique identifier for the permission.",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Human-readable description of what the permission does.",
|
||||
"type": "string"
|
||||
},
|
||||
"permissions": {
|
||||
"description": "All permissions this set contains.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PermissionKind"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Permission": {
|
||||
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"identifier"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"description": "The version of the permission.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"identifier": {
|
||||
"description": "A unique identifier for the permission.",
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"commands": {
|
||||
"description": "Allowed or denied commands when using this permission.",
|
||||
"default": {
|
||||
"allow": [],
|
||||
"deny": []
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Commands"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scope": {
|
||||
"description": "Allowed or denied scoped when using this permission.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Scopes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"platforms": {
|
||||
"description": "Target platforms this permission applies. By default all platforms are affected by this permission.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Target"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Commands": {
|
||||
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow": {
|
||||
"description": "Allowed command.",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"deny": {
|
||||
"description": "Denied command, which takes priority.",
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Scopes": {
|
||||
"description": "An argument for fine grained behavior control of Tauri commands.\n\nIt can be of any serde serializable type and is used to allow or prevent certain actions inside a Tauri command. The configured scope is passed to the command and will be enforced by the command implementation.\n\n## Example\n\n```json { \"allow\": [{ \"path\": \"$HOME/**\" }], \"deny\": [{ \"path\": \"$HOME/secret.txt\" }] } ```",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allow": {
|
||||
"description": "Data that defines what is allowed by the scope.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
},
|
||||
"deny": {
|
||||
"description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Value": {
|
||||
"description": "All supported ACL values.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Represents a null JSON value.",
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"description": "Represents a [`bool`].",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Represents a valid ACL [`Number`].",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Number"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Represents a [`String`].",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Represents a list of other [`Value`]s.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Represents a map of [`String`] keys to [`Value`]s.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/Value"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Number": {
|
||||
"description": "A valid ACL number.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Represents an [`i64`].",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
{
|
||||
"description": "Represents a [`f64`].",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Target": {
|
||||
"description": "Platform target.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "MacOS.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"macOS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Windows.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"windows"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Linux.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Android.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "iOS.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"iOS"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"PermissionKind": {
|
||||
"type": "string",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Enables the activate command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-activate"
|
||||
},
|
||||
{
|
||||
"description": "Denies the activate command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-activate"
|
||||
},
|
||||
{
|
||||
"description": "Enables the add command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-add"
|
||||
},
|
||||
{
|
||||
"description": "Denies the add command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-add"
|
||||
},
|
||||
{
|
||||
"description": "Enables the apply command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-apply"
|
||||
},
|
||||
{
|
||||
"description": "Denies the apply command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-apply"
|
||||
},
|
||||
{
|
||||
"description": "Enables the calculate command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-calculate"
|
||||
},
|
||||
{
|
||||
"description": "Denies the calculate command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-calculate"
|
||||
},
|
||||
{
|
||||
"description": "Enables the check command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-check"
|
||||
},
|
||||
{
|
||||
"description": "Denies the check command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-check"
|
||||
},
|
||||
{
|
||||
"description": "Enables the checkout command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Denies the checkout command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cmd_add command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-cmd-add"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cmd_add command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-cmd-add"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cmd_checkout command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-cmd-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cmd_checkout command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-cmd-checkout"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cmd_commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-cmd-commit"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cmd_commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-cmd-commit"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cmd_init command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-cmd-init"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cmd_init command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-cmd-init"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cmd_log command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-cmd-log"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cmd_log command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-cmd-log"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cmd_status command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-cmd-status"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cmd_status command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-cmd-status"
|
||||
},
|
||||
{
|
||||
"description": "Enables the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-commit"
|
||||
},
|
||||
{
|
||||
"description": "Denies the commit command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-commit"
|
||||
},
|
||||
{
|
||||
"description": "Enables the init command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-init"
|
||||
},
|
||||
{
|
||||
"description": "Denies the init command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-init"
|
||||
},
|
||||
{
|
||||
"description": "Enables the init_repo command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-init-repo"
|
||||
},
|
||||
{
|
||||
"description": "Denies the init_repo command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-init-repo"
|
||||
},
|
||||
{
|
||||
"description": "Enables the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-initialize"
|
||||
},
|
||||
{
|
||||
"description": "Denies the initialize command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-initialize"
|
||||
},
|
||||
{
|
||||
"description": "Enables the log command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-log"
|
||||
},
|
||||
{
|
||||
"description": "Denies the log command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-log"
|
||||
},
|
||||
{
|
||||
"description": "Enables the status command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-status"
|
||||
},
|
||||
{
|
||||
"description": "Denies the status command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-status"
|
||||
},
|
||||
{
|
||||
"description": "Enables the sync command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-sync"
|
||||
},
|
||||
{
|
||||
"description": "Denies the sync command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-sync"
|
||||
},
|
||||
{
|
||||
"description": "Enables the sync_fs command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-sync-fs"
|
||||
},
|
||||
{
|
||||
"description": "Denies the sync_fs command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-sync-fs"
|
||||
},
|
||||
{
|
||||
"description": "Enables the unstage command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-unstage"
|
||||
},
|
||||
{
|
||||
"description": "Denies the unstage command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-unstage"
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin",
|
||||
"type": "string",
|
||||
"const": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src-tauri/yaak-sync/src/commands.rs
Normal file
13
src-tauri/yaak-sync/src/commands.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use crate::error::Result;
|
||||
use crate::sync::{apply_sync, calculate_sync, SyncOp};
|
||||
use tauri::{command, Runtime, WebviewWindow};
|
||||
|
||||
#[command]
|
||||
pub async fn apply<R: Runtime>(window: WebviewWindow<R>, workspace_id: &str) -> Result<()> {
|
||||
apply_sync(&window, workspace_id).await
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn calculate<R: Runtime>(window: WebviewWindow<R>, workspace_id: &str) -> Result<Vec<SyncOp>> {
|
||||
calculate_sync(&window, workspace_id).await
|
||||
}
|
||||
38
src-tauri/yaak-sync/src/error.rs
Normal file
38
src-tauri/yaak-sync/src/error.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Yaml error: {0}")]
|
||||
YamlParseError(#[from] serde_yaml::Error),
|
||||
|
||||
#[error("Yaml error: {0}")]
|
||||
ModelError(#[from] yaak_models::error::Error),
|
||||
|
||||
#[error("Unknown model: {0}")]
|
||||
UnknownModel(String),
|
||||
|
||||
#[error("Workspace not configured for sync: {0}")]
|
||||
WorkspaceSyncNotConfigured(String),
|
||||
|
||||
#[error("I/o error: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
|
||||
#[error("Yaml error: {0}")]
|
||||
JsonParseError(#[from] serde_json::Error),
|
||||
|
||||
#[error("Invalid sync file: {0}")]
|
||||
InvalidSyncFile(String),
|
||||
}
|
||||
|
||||
impl Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
15
src-tauri/yaak-sync/src/lib.rs
Normal file
15
src-tauri/yaak-sync/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::commands::{apply, calculate};
|
||||
use tauri::{
|
||||
generate_handler,
|
||||
plugin::{Builder, TauriPlugin},
|
||||
Runtime,
|
||||
};
|
||||
|
||||
mod commands;
|
||||
mod error;
|
||||
mod models;
|
||||
mod sync;
|
||||
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("yaak-sync").invoke_handler(generate_handler![calculate, apply]).build()
|
||||
}
|
||||
109
src-tauri/yaak-sync/src/models.rs
Normal file
109
src-tauri/yaak-sync/src/models.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use crate::error::Error::{InvalidSyncFile, UnknownModel};
|
||||
use crate::error::Result;
|
||||
use chrono::NaiveDateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha1::{Digest, Sha1};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use ts_rs::TS;
|
||||
use yaak_models::models::{AnyModel, Environment, Folder, GrpcRequest, HttpRequest, Workspace};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case", tag = "type")]
|
||||
#[ts(export, export_to = "models.ts")]
|
||||
pub enum SyncModel {
|
||||
Workspace(Workspace),
|
||||
Environment(Environment),
|
||||
Folder(Folder),
|
||||
HttpRequest(HttpRequest),
|
||||
GrpcRequest(GrpcRequest),
|
||||
}
|
||||
|
||||
impl SyncModel {
|
||||
pub fn from_file(file_path: &Path) -> Result<Option<(SyncModel, Vec<u8>, String)>> {
|
||||
let content = match fs::read(file_path) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(&content);
|
||||
let checksum = hex::encode(hasher.finalize());
|
||||
|
||||
let ext = file_path.extension().unwrap_or_default();
|
||||
if ext == "yml" || ext == "yaml" {
|
||||
Ok(Some((serde_yaml::from_slice(content.as_slice())?, content, checksum)))
|
||||
} else if ext == "json" {
|
||||
Ok(Some((serde_json::from_reader(content.as_slice())?, content, checksum)))
|
||||
} else {
|
||||
Err(InvalidSyncFile(file_path.to_str().unwrap().to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_file_contents(&self, rel_path: &Path) -> Result<(Vec<u8>, String)> {
|
||||
let ext = rel_path.extension().unwrap_or_default();
|
||||
let content = if ext == "yaml" || ext == "yml" {
|
||||
serde_yaml::to_string(self)?
|
||||
} else {
|
||||
serde_json::to_string(self)?
|
||||
};
|
||||
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(&content);
|
||||
let checksum = hex::encode(hasher.finalize());
|
||||
|
||||
Ok((content.into_bytes(), checksum))
|
||||
}
|
||||
|
||||
pub fn id(&self) -> String {
|
||||
match self.clone() {
|
||||
SyncModel::Workspace(m) => m.id,
|
||||
SyncModel::Environment(m) => m.id,
|
||||
SyncModel::Folder(m) => m.id,
|
||||
SyncModel::HttpRequest(m) => m.id,
|
||||
SyncModel::GrpcRequest(m) => m.id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn workspace_id(&self) -> String {
|
||||
match self.clone() {
|
||||
SyncModel::Workspace(m) => m.id,
|
||||
SyncModel::Environment(m) => m.workspace_id,
|
||||
SyncModel::Folder(m) => m.workspace_id,
|
||||
SyncModel::HttpRequest(m) => m.workspace_id,
|
||||
SyncModel::GrpcRequest(m) => m.workspace_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updated_at(&self) -> NaiveDateTime {
|
||||
match self.clone() {
|
||||
SyncModel::Workspace(m) => m.updated_at,
|
||||
SyncModel::Environment(m) => m.updated_at,
|
||||
SyncModel::Folder(m) => m.updated_at,
|
||||
SyncModel::HttpRequest(m) => m.updated_at,
|
||||
SyncModel::GrpcRequest(m) => m.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<AnyModel> for SyncModel {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(value: AnyModel) -> Result<Self> {
|
||||
let m = match value {
|
||||
AnyModel::Environment(m) => SyncModel::Environment(m),
|
||||
AnyModel::Folder(m) => SyncModel::Folder(m),
|
||||
AnyModel::GrpcRequest(m) => SyncModel::GrpcRequest(m),
|
||||
AnyModel::HttpRequest(m) => SyncModel::HttpRequest(m),
|
||||
AnyModel::Workspace(m) => SyncModel::Workspace(m),
|
||||
AnyModel::CookieJar(m) => return Err(UnknownModel(m.model)),
|
||||
AnyModel::GrpcConnection(m) => return Err(UnknownModel(m.model)),
|
||||
AnyModel::GrpcEvent(m) => return Err(UnknownModel(m.model)),
|
||||
AnyModel::HttpResponse(m) => return Err(UnknownModel(m.model)),
|
||||
AnyModel::Plugin(m) => return Err(UnknownModel(m.model)),
|
||||
AnyModel::Settings(m) => return Err(UnknownModel(m.model)),
|
||||
AnyModel::KeyValue(m) => return Err(UnknownModel(m.model)),
|
||||
};
|
||||
Ok(m)
|
||||
}
|
||||
}
|
||||
542
src-tauri/yaak-sync/src/sync.rs
Normal file
542
src-tauri/yaak-sync/src/sync.rs
Normal file
@@ -0,0 +1,542 @@
|
||||
use crate::error::Error::{InvalidSyncFile, WorkspaceSyncNotConfigured};
|
||||
use crate::error::Result;
|
||||
use crate::models::SyncModel;
|
||||
use chrono::Utc;
|
||||
use log::{debug, warn};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs;
|
||||
use std::fs::create_dir_all;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use ts_rs::TS;
|
||||
use yaak_models::models::{SyncState, Workspace};
|
||||
use yaak_models::queries::{
|
||||
delete_environment, delete_folder, delete_grpc_request, delete_http_request, delete_sync_state,
|
||||
delete_workspace, get_workspace, get_workspace_export_resources,
|
||||
list_sync_states_for_workspace, upsert_environment, upsert_folder, upsert_grpc_request,
|
||||
upsert_http_request, upsert_sync_state, upsert_workspace, UpdateSource,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
#[ts(export, export_to = "sync.ts")]
|
||||
pub(crate) enum SyncOp {
|
||||
FsCreate {
|
||||
model: SyncModel,
|
||||
},
|
||||
FsUpdate {
|
||||
model: SyncModel,
|
||||
state: SyncState,
|
||||
},
|
||||
FsDelete {
|
||||
state: SyncState,
|
||||
fs: Option<FsCandidate>,
|
||||
},
|
||||
DbCreate {
|
||||
fs: FsCandidate,
|
||||
},
|
||||
DbUpdate {
|
||||
state: SyncState,
|
||||
fs: FsCandidate,
|
||||
},
|
||||
DbDelete {
|
||||
model: SyncModel,
|
||||
state: SyncState,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for SyncOp {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(
|
||||
match self {
|
||||
SyncOp::FsCreate { model } => format!("fs_create({})", model.id()),
|
||||
SyncOp::FsUpdate { model, .. } => format!("fs_update({})", model.id()),
|
||||
SyncOp::FsDelete { state, .. } => format!("fs_delete({})", state.model_id),
|
||||
SyncOp::DbCreate { fs } => format!("db_create({})", fs.model.id()),
|
||||
SyncOp::DbUpdate { fs, .. } => format!("db_update({})", fs.model.id()),
|
||||
SyncOp::DbDelete { model, .. } => format!("db_delete({})", model.id()),
|
||||
}
|
||||
.as_str(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum DbCandidate {
|
||||
Added(SyncModel),
|
||||
Modified(SyncModel, SyncState),
|
||||
Deleted(SyncState),
|
||||
Unmodified(SyncModel, SyncState),
|
||||
}
|
||||
|
||||
impl DbCandidate {
|
||||
fn model_id(&self) -> String {
|
||||
match &self {
|
||||
DbCandidate::Added(m) => m.id(),
|
||||
DbCandidate::Modified(m, _) => m.id(),
|
||||
DbCandidate::Deleted(s) => s.model_id.clone(),
|
||||
DbCandidate::Unmodified(m, _) => m.id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase", tag = "type")]
|
||||
#[ts(export, export_to = "sync.ts")]
|
||||
pub(crate) struct FsCandidate {
|
||||
model: SyncModel,
|
||||
rel_path: PathBuf,
|
||||
checksum: String,
|
||||
}
|
||||
|
||||
pub(crate) async fn calculate_sync<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace_id: &str,
|
||||
) -> Result<Vec<SyncOp>> {
|
||||
let workspace = get_workspace(window, workspace_id).await?;
|
||||
let db_candidates = get_db_candidates(window, &workspace).await?;
|
||||
let fs_candidates = get_fs_candidates(&workspace)?;
|
||||
let sync_ops = compute_sync_ops(db_candidates, fs_candidates);
|
||||
|
||||
Ok(sync_ops)
|
||||
}
|
||||
|
||||
pub(crate) async fn apply_sync<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace_id: &str,
|
||||
) -> Result<()> {
|
||||
let workspace = get_workspace(window, workspace_id).await?;
|
||||
let sync_ops = calculate_sync(window, workspace_id).await?;
|
||||
|
||||
let sync_state_ops = apply_sync_ops(window, &workspace, sync_ops).await?;
|
||||
let result = apply_sync_state_ops(window, &workspace, sync_state_ops).await;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn get_db_candidates<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
workspace: &Workspace,
|
||||
) -> Result<Vec<DbCandidate>> {
|
||||
let workspace_id = workspace.id.as_str();
|
||||
let models = workspace_models(mgr, workspace).await;
|
||||
let sync_dir = get_workspace_sync_dir(workspace)?;
|
||||
let sync_states = list_sync_states_for_workspace(mgr, workspace_id, sync_dir).await?;
|
||||
|
||||
// 1. Add candidates for models (created/modified/unmodified)
|
||||
let mut candidates: Vec<DbCandidate> = models
|
||||
.iter()
|
||||
.map(|model| {
|
||||
let existing_sync_state = sync_states.iter().find(|ss| ss.model_id == model.id());
|
||||
let existing_sync_state = match existing_sync_state {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
// No sync state yet, so model was just added
|
||||
return DbCandidate::Added(model.to_owned());
|
||||
}
|
||||
};
|
||||
|
||||
let updated_since_flush = model.updated_at() > existing_sync_state.flushed_at;
|
||||
if updated_since_flush {
|
||||
DbCandidate::Modified(model.to_owned(), existing_sync_state.to_owned())
|
||||
} else {
|
||||
DbCandidate::Unmodified(model.to_owned(), existing_sync_state.to_owned())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// 2. Add SyncState-only candidates (deleted)
|
||||
candidates.extend(sync_states.iter().filter_map(|sync_state| {
|
||||
let already_added = models.iter().find(|m| m.id() == sync_state.model_id).is_some();
|
||||
if already_added {
|
||||
return None;
|
||||
}
|
||||
Some(DbCandidate::Deleted(sync_state.to_owned()))
|
||||
}));
|
||||
|
||||
Ok(candidates)
|
||||
}
|
||||
|
||||
fn get_fs_candidates(workspace: &Workspace) -> Result<Vec<FsCandidate>> {
|
||||
let dir = match workspace.setting_sync_dir.clone() {
|
||||
None => return Ok(Vec::new()),
|
||||
Some(d) => d,
|
||||
};
|
||||
|
||||
// Ensure the root directory exists
|
||||
create_dir_all(dir.clone())?;
|
||||
|
||||
let candidates = fs::read_dir(dir)?
|
||||
.filter_map(|dir_entry| {
|
||||
let dir_entry = dir_entry.ok()?;
|
||||
if !dir_entry.file_type().ok()?.is_file() {
|
||||
return None;
|
||||
};
|
||||
|
||||
let path = dir_entry.path();
|
||||
let (model, _, checksum) = match SyncModel::from_file(&path) {
|
||||
Ok(Some(m)) => m,
|
||||
Ok(None) => return None,
|
||||
Err(InvalidSyncFile(_)) => return None,
|
||||
Err(e) => {
|
||||
warn!("Failed to read sync file {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Skip models belonging to different workspace
|
||||
if model.workspace_id() != workspace.id.as_str() {
|
||||
debug!("Skipping non-workspace file");
|
||||
return None;
|
||||
}
|
||||
|
||||
let rel_path = Path::new(&dir_entry.file_name()).to_path_buf();
|
||||
Some(FsCandidate {
|
||||
rel_path,
|
||||
model,
|
||||
checksum,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(candidates)
|
||||
}
|
||||
|
||||
fn compute_sync_ops(
|
||||
db_candidates: Vec<DbCandidate>,
|
||||
fs_candidates: Vec<FsCandidate>,
|
||||
) -> Vec<SyncOp> {
|
||||
let mut db_map: HashMap<String, DbCandidate> = HashMap::new();
|
||||
for c in db_candidates {
|
||||
db_map.insert(c.model_id(), c);
|
||||
}
|
||||
|
||||
let mut fs_map: HashMap<String, FsCandidate> = HashMap::new();
|
||||
for c in fs_candidates {
|
||||
fs_map.insert(c.model.id(), c);
|
||||
}
|
||||
|
||||
// Collect all keys from both maps for the OUTER JOIN
|
||||
let keys: std::collections::HashSet<_> = db_map.keys().chain(fs_map.keys()).collect();
|
||||
|
||||
keys.into_iter()
|
||||
.filter_map(|k| {
|
||||
let op = match (db_map.get(k), fs_map.get(k)) {
|
||||
(None, None) => return None, // Can never happen
|
||||
(None, Some(fs)) => SyncOp::DbCreate { fs: fs.to_owned() },
|
||||
(Some(DbCandidate::Unmodified(model, sync_state)), None) => SyncOp::DbDelete {
|
||||
model: model.to_owned(),
|
||||
state: sync_state.to_owned(),
|
||||
},
|
||||
(Some(DbCandidate::Modified(model, sync_state)), None) => SyncOp::FsUpdate {
|
||||
model: model.to_owned(),
|
||||
state: sync_state.to_owned(),
|
||||
},
|
||||
(Some(DbCandidate::Added(model)), None) => SyncOp::FsCreate {
|
||||
model: model.to_owned(),
|
||||
},
|
||||
(Some(DbCandidate::Deleted(sync_state)), None) => {
|
||||
// Already deleted on FS, but sending it so the SyncState gets dealt with
|
||||
SyncOp::FsDelete {
|
||||
state: sync_state.to_owned(),
|
||||
fs: None,
|
||||
}
|
||||
}
|
||||
(Some(DbCandidate::Unmodified(_, sync_state)), Some(fs_candidate)) => {
|
||||
if sync_state.checksum == fs_candidate.checksum {
|
||||
return None;
|
||||
} else {
|
||||
SyncOp::DbUpdate {
|
||||
state: sync_state.to_owned(),
|
||||
fs: fs_candidate.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(DbCandidate::Modified(model, sync_state)), Some(fs_candidate)) => {
|
||||
if sync_state.checksum == fs_candidate.checksum {
|
||||
SyncOp::FsUpdate {
|
||||
model: model.to_owned(),
|
||||
state: sync_state.to_owned(),
|
||||
}
|
||||
} else if model.updated_at() < fs_candidate.model.updated_at() {
|
||||
// CONFLICT! Write to DB if fs model is newer
|
||||
SyncOp::DbUpdate {
|
||||
state: sync_state.to_owned(),
|
||||
fs: fs_candidate.to_owned(),
|
||||
}
|
||||
} else {
|
||||
// CONFLICT! Write to FS if db model is newer
|
||||
SyncOp::FsUpdate {
|
||||
model: model.to_owned(),
|
||||
state: sync_state.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(DbCandidate::Added(model)), Some(_)) => {
|
||||
// This would be super rare, so let's follow the user's intention
|
||||
SyncOp::FsCreate {
|
||||
model: model.to_owned(),
|
||||
}
|
||||
}
|
||||
(Some(DbCandidate::Deleted(sync_state)), Some(fs_candidate)) => SyncOp::FsDelete {
|
||||
state: sync_state.to_owned(),
|
||||
fs: Some(fs_candidate.to_owned()),
|
||||
},
|
||||
};
|
||||
Some(op)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
async fn workspace_models<R: Runtime>(
|
||||
mgr: &impl Manager<R>,
|
||||
workspace: &Workspace,
|
||||
) -> Vec<SyncModel> {
|
||||
let workspace_id = workspace.id.as_str();
|
||||
let resources = get_workspace_export_resources(mgr, vec![workspace_id]).await.resources;
|
||||
|
||||
let mut sync_models = vec![SyncModel::Workspace(workspace.to_owned())];
|
||||
for m in resources.environments {
|
||||
sync_models.push(SyncModel::Environment(m));
|
||||
}
|
||||
for m in resources.folders {
|
||||
sync_models.push(SyncModel::Folder(m));
|
||||
}
|
||||
for m in resources.http_requests {
|
||||
sync_models.push(SyncModel::HttpRequest(m));
|
||||
}
|
||||
for m in resources.grpc_requests {
|
||||
sync_models.push(SyncModel::GrpcRequest(m));
|
||||
}
|
||||
|
||||
sync_models
|
||||
}
|
||||
|
||||
async fn apply_sync_ops<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace: &Workspace,
|
||||
sync_ops: Vec<SyncOp>,
|
||||
) -> Result<Vec<SyncStateOp>> {
|
||||
if sync_ops.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Sync ops {}",
|
||||
sync_ops.iter().map(|op| op.to_string()).collect::<Vec<String>>().join(", ")
|
||||
);
|
||||
let mut sync_state_ops = Vec::new();
|
||||
for op in sync_ops {
|
||||
let op = apply_sync_op(window, workspace, &op).await?;
|
||||
sync_state_ops.push(op);
|
||||
}
|
||||
Ok(sync_state_ops)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SyncStateOp {
|
||||
Create {
|
||||
model_id: String,
|
||||
checksum: String,
|
||||
rel_path: PathBuf,
|
||||
},
|
||||
Update {
|
||||
state: SyncState,
|
||||
checksum: String,
|
||||
rel_path: PathBuf,
|
||||
},
|
||||
Delete {
|
||||
state: SyncState,
|
||||
},
|
||||
}
|
||||
|
||||
/// Flush a DB model to the filesystem
|
||||
async fn apply_sync_op<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace: &Workspace,
|
||||
op: &SyncOp,
|
||||
) -> Result<SyncStateOp> {
|
||||
let sync_state_op = match op {
|
||||
SyncOp::FsCreate { model } => {
|
||||
let rel_path = derive_model_filename(&model);
|
||||
let abs_path = derive_full_model_path(workspace, &model)?;
|
||||
let (content, checksum) = model.to_file_contents(&rel_path)?;
|
||||
fs::write(&abs_path, content)?;
|
||||
SyncStateOp::Create {
|
||||
model_id: model.id(),
|
||||
checksum,
|
||||
rel_path,
|
||||
}
|
||||
}
|
||||
SyncOp::FsUpdate { model, state } => {
|
||||
let rel_path = derive_model_filename(&model);
|
||||
let abs_path = derive_full_model_path(workspace, &model)?;
|
||||
let (content, checksum) = model.to_file_contents(&rel_path)?;
|
||||
fs::write(&abs_path, content)?;
|
||||
SyncStateOp::Update {
|
||||
state: state.to_owned(),
|
||||
checksum,
|
||||
rel_path,
|
||||
}
|
||||
}
|
||||
SyncOp::FsDelete {
|
||||
state,
|
||||
fs: fs_candidate,
|
||||
} => match fs_candidate {
|
||||
None => SyncStateOp::Delete {
|
||||
state: state.to_owned(),
|
||||
},
|
||||
Some(fs_candidate) => {
|
||||
let abs_path = derive_full_model_path(workspace, &fs_candidate.model)?;
|
||||
fs::remove_file(&abs_path)?;
|
||||
SyncStateOp::Delete {
|
||||
state: state.to_owned(),
|
||||
}
|
||||
}
|
||||
},
|
||||
SyncOp::DbCreate { fs } => {
|
||||
upsert_model(window, &fs.model).await?;
|
||||
SyncStateOp::Create {
|
||||
model_id: fs.model.id(),
|
||||
checksum: fs.checksum.to_owned(),
|
||||
rel_path: fs.rel_path.to_owned(),
|
||||
}
|
||||
}
|
||||
SyncOp::DbUpdate { state, fs } => {
|
||||
upsert_model(window, &fs.model).await?;
|
||||
SyncStateOp::Update {
|
||||
state: state.to_owned(),
|
||||
checksum: fs.checksum.to_owned(),
|
||||
rel_path: fs.rel_path.to_owned(),
|
||||
}
|
||||
}
|
||||
SyncOp::DbDelete { model, state } => {
|
||||
delete_model(window, model).await?;
|
||||
SyncStateOp::Delete {
|
||||
state: state.to_owned(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(sync_state_op)
|
||||
}
|
||||
async fn apply_sync_state_ops<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace: &Workspace,
|
||||
ops: Vec<SyncStateOp>,
|
||||
) -> Result<()> {
|
||||
for op in ops {
|
||||
apply_sync_state_op(window, workspace, op).await?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn apply_sync_state_op<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
workspace: &Workspace,
|
||||
op: SyncStateOp,
|
||||
) -> Result<()> {
|
||||
let dir_path = get_workspace_sync_dir(workspace)?;
|
||||
match op {
|
||||
SyncStateOp::Create {
|
||||
checksum,
|
||||
rel_path,
|
||||
model_id,
|
||||
} => {
|
||||
let sync_state = SyncState {
|
||||
workspace_id: workspace.to_owned().id,
|
||||
model_id,
|
||||
checksum,
|
||||
sync_dir: dir_path.to_str().unwrap().to_string(),
|
||||
rel_path: rel_path.to_str().unwrap().to_string(),
|
||||
flushed_at: Utc::now().naive_utc(),
|
||||
..Default::default()
|
||||
};
|
||||
upsert_sync_state(window, sync_state).await?;
|
||||
}
|
||||
SyncStateOp::Update {
|
||||
state: sync_state,
|
||||
checksum,
|
||||
rel_path,
|
||||
} => {
|
||||
let sync_state = SyncState {
|
||||
checksum,
|
||||
sync_dir: dir_path.to_str().unwrap().to_string(),
|
||||
rel_path: rel_path.to_str().unwrap().to_string(),
|
||||
flushed_at: Utc::now().naive_utc(),
|
||||
..sync_state
|
||||
};
|
||||
upsert_sync_state(window, sync_state).await?;
|
||||
}
|
||||
SyncStateOp::Delete { state } => {
|
||||
delete_sync_state(window, state.id.as_str()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_workspace_sync_dir(workspace: &Workspace) -> Result<PathBuf> {
|
||||
let workspace_id = workspace.to_owned().id;
|
||||
match workspace.setting_sync_dir.to_owned() {
|
||||
Some(d) => Ok(Path::new(&d).to_path_buf()),
|
||||
None => Err(WorkspaceSyncNotConfigured(workspace_id)),
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_full_model_path(workspace: &Workspace, m: &SyncModel) -> Result<PathBuf> {
|
||||
let dir = get_workspace_sync_dir(workspace)?;
|
||||
Ok(dir.join(derive_model_filename(m)))
|
||||
}
|
||||
|
||||
fn derive_model_filename(m: &SyncModel) -> PathBuf {
|
||||
let rel = format!("{}.yaml", m.id());
|
||||
let rel = Path::new(&rel).to_path_buf();
|
||||
|
||||
// Ensure parent dir exists
|
||||
rel
|
||||
}
|
||||
|
||||
async fn upsert_model<R: Runtime>(window: &WebviewWindow<R>, m: &SyncModel) -> Result<()> {
|
||||
match m {
|
||||
SyncModel::Workspace(m) => {
|
||||
upsert_workspace(window, m.to_owned(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::Environment(m) => {
|
||||
upsert_environment(window, m.to_owned(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::Folder(m) => {
|
||||
upsert_folder(window, m.to_owned(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::HttpRequest(m) => {
|
||||
upsert_http_request(window, m.to_owned(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::GrpcRequest(m) => {
|
||||
upsert_grpc_request(window, m.to_owned(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_model<R: Runtime>(window: &WebviewWindow<R>, model: &SyncModel) -> Result<()> {
|
||||
match model {
|
||||
SyncModel::Workspace(m) => {
|
||||
delete_workspace(window, m.id.as_str(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::Environment(m) => {
|
||||
delete_environment(window, m.id.as_str(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::Folder(m) => {
|
||||
delete_folder(window, m.id.as_str(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::HttpRequest(m) => {
|
||||
delete_http_request(window, m.id.as_str(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
SyncModel::GrpcRequest(m) => {
|
||||
delete_grpc_request(window, m.id.as_str(), &UpdateSource::Sync).await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user