mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-06-11 17:12:47 +02:00
[WIP] Encryption for secure values (#183)
This commit is contained in:
@@ -14,12 +14,12 @@ r2d2_sqlite = { version = "0.25.0" }
|
||||
rusqlite = { version = "0.32.1", features = ["bundled", "chrono"] }
|
||||
sea-query = { version = "0.32.1", features = ["with-chrono", "attr"] }
|
||||
sea-query-rusqlite = { version = "0.7.0", features = ["with-chrono"] }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sqlx = { version = "0.8.0", default-features = false, features = ["migrate", "sqlite", "runtime-tokio-rustls"] }
|
||||
tauri = { workspace = true }
|
||||
thiserror = "2.0.11"
|
||||
tokio = "1.43.0"
|
||||
tokio = { workspace = true }
|
||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -12,6 +12,8 @@ export type CookieJar = { model: "cookie_jar", id: string, createdAt: string, up
|
||||
|
||||
export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
|
||||
|
||||
export type EncryptedKey = { encryptedKey: string, };
|
||||
|
||||
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, };
|
||||
@@ -56,11 +58,11 @@ export type ProxySetting = { "type": "enabled", http: string, https: string, aut
|
||||
|
||||
export type ProxySettingAuth = { user: string, password: string, };
|
||||
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, theme: string, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, };
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, };
|
||||
|
||||
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 UpdateSource = { "type": "sync" } | { "type": "window", label: string, } | { "type": "plugin" } | { "type": "background" } | { "type": "import" };
|
||||
export type UpdateSource = { "type": "background" } | { "type": "import" } | { "type": "plugin" } | { "type": "sync" } | { "type": "window", label: string, };
|
||||
|
||||
export type WebsocketConnection = { model: "websocket_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, headers: Array<HttpResponseHeader>, state: WebsocketConnectionState, status: number, url: string, };
|
||||
|
||||
@@ -74,6 +76,6 @@ export type WebsocketMessageType = "text" | "binary";
|
||||
|
||||
export type WebsocketRequest = { model: "websocket_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, message: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||
|
||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
|
||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, encryptionKeyChallenge: string | null, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
|
||||
|
||||
export type WorkspaceMeta = { model: "workspace_meta", id: string, workspaceId: string, createdAt: string, updatedAt: string, settingSyncDir: string | null, };
|
||||
export type WorkspaceMeta = { model: "workspace_meta", id: string, workspaceId: string, createdAt: string, updatedAt: string, encryptionKey: EncryptedKey | null, settingSyncDir: string | null, };
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Environment } from "./gen_models.js";
|
||||
import type { Folder } from "./gen_models.js";
|
||||
import type { GrpcRequest } from "./gen_models.js";
|
||||
import type { HttpRequest } from "./gen_models.js";
|
||||
import type { WebsocketRequest } from "./gen_models.js";
|
||||
import type { Workspace } from "./gen_models.js";
|
||||
|
||||
export type BatchUpsertResult = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, websocketRequests: Array<WebsocketRequest>, };
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AnyModel } from '../bindings/gen_models';
|
||||
|
||||
export * from '../bindings/gen_models';
|
||||
export * from '../bindings/gen_util';
|
||||
export * from './store';
|
||||
export * from './atoms';
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ export function patchModelById<M extends AnyModel['model'], T extends ExtractMod
|
||||
}
|
||||
|
||||
const newModel = typeof patch === 'function' ? patch(prev) : { ...prev, ...patch };
|
||||
return invoke<string>('plugin:yaak-models|upsert', { model: newModel });
|
||||
return updateModel(newModel);
|
||||
}
|
||||
|
||||
export async function patchModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
||||
@@ -112,6 +112,12 @@ export async function patchModel<M extends AnyModel['model'], T extends ExtractM
|
||||
return patchModelById<M, T>(base.model, base.id, patch);
|
||||
}
|
||||
|
||||
export async function updateModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
||||
model: T,
|
||||
): Promise<string> {
|
||||
return invoke<string>('plugin:yaak-models|upsert', { model });
|
||||
}
|
||||
|
||||
export async function deleteModelById<
|
||||
M extends AnyModel['model'],
|
||||
T extends ExtractModel<AnyModel, M>,
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-delete"
|
||||
description = "Enables the delete command without any pre-configured scope."
|
||||
commands.allow = ["delete"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-delete"
|
||||
description = "Denies the delete command without any pre-configured scope."
|
||||
commands.deny = ["delete"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-duplicate"
|
||||
description = "Enables the duplicate command without any pre-configured scope."
|
||||
commands.allow = ["duplicate"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-duplicate"
|
||||
description = "Denies the duplicate command without any pre-configured scope."
|
||||
commands.deny = ["duplicate"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-get-settings"
|
||||
description = "Enables the get_settings command without any pre-configured scope."
|
||||
commands.allow = ["get_settings"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-get-settings"
|
||||
description = "Denies the get_settings command without any pre-configured scope."
|
||||
commands.deny = ["get_settings"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-grpc-events"
|
||||
description = "Enables the grpc_events command without any pre-configured scope."
|
||||
commands.allow = ["grpc_events"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-grpc-events"
|
||||
description = "Denies the grpc_events command without any pre-configured scope."
|
||||
commands.deny = ["grpc_events"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-upsert"
|
||||
description = "Enables the upsert command without any pre-configured scope."
|
||||
commands.allow = ["upsert"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-upsert"
|
||||
description = "Denies the upsert command without any pre-configured scope."
|
||||
commands.deny = ["upsert"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-websocket-events"
|
||||
description = "Enables the websocket_events command without any pre-configured scope."
|
||||
commands.allow = ["websocket_events"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-websocket-events"
|
||||
description = "Denies the websocket_events command without any pre-configured scope."
|
||||
commands.deny = ["websocket_events"]
|
||||
@@ -1,13 +0,0 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-workspace-models"
|
||||
description = "Enables the workspace_models command without any pre-configured scope."
|
||||
commands.allow = ["workspace_models"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-workspace-models"
|
||||
description = "Denies the workspace_models command without any pre-configured scope."
|
||||
commands.deny = ["workspace_models"]
|
||||
@@ -1,203 +0,0 @@
|
||||
## Default Permission
|
||||
|
||||
Default permissions for the plugin
|
||||
|
||||
- `allow-delete`
|
||||
- `allow-duplicate`
|
||||
- `allow-get-settings`
|
||||
- `allow-grpc-events`
|
||||
- `allow-upsert`
|
||||
- `allow-websocket-events`
|
||||
- `allow-workspace-models`
|
||||
|
||||
## Permission Table
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Identifier</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-delete`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the delete command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-delete`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the delete command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-duplicate`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the duplicate command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-duplicate`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the duplicate command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-get-settings`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the get_settings command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-get-settings`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the get_settings command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-grpc-events`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the grpc_events command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-grpc-events`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the grpc_events command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-upsert`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the upsert command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-upsert`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the upsert command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-websocket-events`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the websocket_events command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-websocket-events`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the websocket_events command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:allow-workspace-models`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the workspace_models command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`yaak-models:deny-workspace-models`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the workspace_models command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1,375 +0,0 @@
|
||||
{
|
||||
"$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 delete command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-delete"
|
||||
},
|
||||
{
|
||||
"description": "Denies the delete command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-delete"
|
||||
},
|
||||
{
|
||||
"description": "Enables the duplicate command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-duplicate"
|
||||
},
|
||||
{
|
||||
"description": "Denies the duplicate command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-duplicate"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_settings command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-get-settings"
|
||||
},
|
||||
{
|
||||
"description": "Denies the get_settings command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-get-settings"
|
||||
},
|
||||
{
|
||||
"description": "Enables the grpc_events command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-grpc-events"
|
||||
},
|
||||
{
|
||||
"description": "Denies the grpc_events command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-grpc-events"
|
||||
},
|
||||
{
|
||||
"description": "Enables the upsert command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-upsert"
|
||||
},
|
||||
{
|
||||
"description": "Denies the upsert command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-upsert"
|
||||
},
|
||||
{
|
||||
"description": "Enables the websocket_events command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-websocket-events"
|
||||
},
|
||||
{
|
||||
"description": "Denies the websocket_events command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-websocket-events"
|
||||
},
|
||||
{
|
||||
"description": "Enables the workspace_models command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-workspace-models"
|
||||
},
|
||||
{
|
||||
"description": "Denies the workspace_models command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-workspace-models"
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin",
|
||||
"type": "string",
|
||||
"const": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +53,11 @@ impl<'a> DbContext<'a> {
|
||||
where
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
let (order_by_col, order_by_dir) = M::order_by();
|
||||
let (sql, params) = Query::select()
|
||||
.from(M::table_name())
|
||||
.column(Asterisk)
|
||||
.order_by(order_by_col, order_by_dir)
|
||||
.build_rusqlite(SqliteQueryBuilder);
|
||||
let mut stmt = self.conn.resolve().prepare(sql.as_str())?;
|
||||
let items = stmt.query_map(&*params.as_params(), M::from_row)?;
|
||||
@@ -72,18 +74,21 @@ impl<'a> DbContext<'a> {
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
// TODO: Figure out how to do this conditional builder better
|
||||
let (order_by_col, order_by_dir) = M::order_by();
|
||||
let (sql, params) = if let Some(limit) = limit {
|
||||
Query::select()
|
||||
.from(M::table_name())
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(col).eq(value))
|
||||
.limit(limit)
|
||||
.order_by(order_by_col, order_by_dir)
|
||||
.build_rusqlite(SqliteQueryBuilder)
|
||||
} else {
|
||||
Query::select()
|
||||
.from(M::table_name())
|
||||
.column(Asterisk)
|
||||
.cond_where(Expr::col(col).eq(value))
|
||||
.order_by(order_by_col, order_by_dir)
|
||||
.build_rusqlite(SqliteQueryBuilder)
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pub enum Error {
|
||||
|
||||
#[error("Model error: {0}")]
|
||||
GenericError(String),
|
||||
|
||||
|
||||
#[error("Row not found")]
|
||||
RowNotFound,
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use tokio::sync::mpsc;
|
||||
mod commands;
|
||||
|
||||
mod connection_or_tx;
|
||||
mod db_context;
|
||||
pub mod db_context;
|
||||
pub mod error;
|
||||
pub mod models;
|
||||
pub mod queries;
|
||||
|
||||
@@ -3,10 +3,11 @@ use crate::models::HttpRequestIden::{
|
||||
Authentication, AuthenticationType, Body, BodyType, CreatedAt, Description, FolderId, Headers,
|
||||
Method, Name, SortPriority, UpdatedAt, Url, UrlParameters, WorkspaceId,
|
||||
};
|
||||
use crate::util::{generate_prefixed_id, UpdateSource};
|
||||
use crate::util::{UpdateSource, generate_prefixed_id};
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use rusqlite::Row;
|
||||
use sea_query::{enum_def, IntoIden, IntoTableRef, SimpleExpr};
|
||||
use sea_query::Order::Desc;
|
||||
use sea_query::{IntoColumnRef, IntoIden, IntoTableRef, Order, SimpleExpr, enum_def};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -124,6 +125,10 @@ impl UpsertModelInfo for Settings {
|
||||
panic!("Settings does not have unique IDs")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(SettingsIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -209,6 +214,7 @@ pub struct Workspace {
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub encryption_key_challenge: Option<String>,
|
||||
|
||||
// Settings
|
||||
#[serde(default = "default_true")]
|
||||
@@ -231,6 +237,10 @@ impl UpsertModelInfo for Workspace {
|
||||
generate_prefixed_id("wk")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WorkspaceIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -245,6 +255,7 @@ impl UpsertModelInfo for Workspace {
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(Name, self.name.trim().into()),
|
||||
(Description, self.description.into()),
|
||||
(EncryptionKeyChallenge, self.encryption_key_challenge.into()),
|
||||
(SettingFollowRedirects, self.setting_follow_redirects.into()),
|
||||
(SettingRequestTimeout, self.setting_request_timeout.into()),
|
||||
(SettingValidateCertificates, self.setting_validate_certificates.into()),
|
||||
@@ -256,6 +267,7 @@ impl UpsertModelInfo for Workspace {
|
||||
WorkspaceIden::UpdatedAt,
|
||||
WorkspaceIden::Name,
|
||||
WorkspaceIden::Description,
|
||||
WorkspaceIden::EncryptionKeyChallenge,
|
||||
WorkspaceIden::SettingRequestTimeout,
|
||||
WorkspaceIden::SettingFollowRedirects,
|
||||
WorkspaceIden::SettingRequestTimeout,
|
||||
@@ -274,6 +286,7 @@ impl UpsertModelInfo for Workspace {
|
||||
updated_at: row.get("updated_at")?,
|
||||
name: row.get("name")?,
|
||||
description: row.get("description")?,
|
||||
encryption_key_challenge: row.get("encryption_key_challenge")?,
|
||||
setting_follow_redirects: row.get("setting_follow_redirects")?,
|
||||
setting_request_timeout: row.get("setting_request_timeout")?,
|
||||
setting_validate_certificates: row.get("setting_validate_certificates")?,
|
||||
@@ -281,16 +294,11 @@ impl UpsertModelInfo for Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
model: "workspace".to_string(),
|
||||
setting_validate_certificates: true,
|
||||
setting_follow_redirects: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct EncryptedKey {
|
||||
pub encrypted_key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||
@@ -304,6 +312,7 @@ pub struct WorkspaceMeta {
|
||||
pub workspace_id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub encryption_key: Option<EncryptedKey>,
|
||||
pub setting_sync_dir: Option<String>,
|
||||
}
|
||||
|
||||
@@ -320,6 +329,10 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
generate_prefixed_id("wm")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WorkspaceMetaIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -333,6 +346,7 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
(CreatedAt, upsert_date(source, self.created_at)),
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(WorkspaceId, self.workspace_id.into()),
|
||||
(EncryptionKey, self.encryption_key.map(|e| serde_json::to_string(&e).unwrap()).into()),
|
||||
(SettingSyncDir, self.setting_sync_dir.into()),
|
||||
])
|
||||
}
|
||||
@@ -340,6 +354,7 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
fn update_columns() -> Vec<impl IntoIden> {
|
||||
vec![
|
||||
WorkspaceMetaIden::UpdatedAt,
|
||||
WorkspaceMetaIden::EncryptionKey,
|
||||
WorkspaceMetaIden::SettingSyncDir,
|
||||
]
|
||||
}
|
||||
@@ -348,12 +363,14 @@ impl UpsertModelInfo for WorkspaceMeta {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let encryption_key: Option<String> = row.get("encryption_key")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
workspace_id: row.get("workspace_id")?,
|
||||
model: row.get("model")?,
|
||||
created_at: row.get("created_at")?,
|
||||
updated_at: row.get("updated_at")?,
|
||||
encryption_key: encryption_key.map(|e| serde_json::from_str(&e).unwrap()),
|
||||
setting_sync_dir: row.get("setting_sync_dir")?,
|
||||
})
|
||||
}
|
||||
@@ -413,6 +430,10 @@ impl UpsertModelInfo for CookieJar {
|
||||
generate_prefixed_id("cj")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(CookieJarIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -486,6 +507,10 @@ impl UpsertModelInfo for Environment {
|
||||
generate_prefixed_id("ev")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(EnvironmentIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -575,6 +600,10 @@ impl UpsertModelInfo for Folder {
|
||||
generate_prefixed_id("fl")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(FolderIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -673,7 +702,7 @@ pub struct HttpRequest {
|
||||
#[serde(default = "default_http_method")]
|
||||
pub method: String,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
pub sort_priority: f64,
|
||||
pub url: String,
|
||||
pub url_parameters: Vec<HttpUrlParameter>,
|
||||
}
|
||||
@@ -691,6 +720,10 @@ impl UpsertModelInfo for HttpRequest {
|
||||
generate_prefixed_id("rq")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(HttpResponseIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
@@ -745,21 +778,21 @@ impl UpsertModelInfo for HttpRequest {
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
url: r.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
method: r.get("method")?,
|
||||
authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(),
|
||||
authentication_type: r.get("authentication_type")?,
|
||||
body: serde_json::from_str(body.as_str()).unwrap_or_default(),
|
||||
body_type: r.get("body_type")?,
|
||||
description: r.get("description")?,
|
||||
authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(),
|
||||
authentication_type: r.get("authentication_type")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
folder_id: r.get("folder_id")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
method: r.get("method")?,
|
||||
name: r.get("name")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
url: r.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -814,6 +847,10 @@ impl UpsertModelInfo for WebsocketConnection {
|
||||
generate_prefixed_id("wc")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WebsocketConnectionIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -924,6 +961,10 @@ impl UpsertModelInfo for WebsocketRequest {
|
||||
generate_prefixed_id("wr")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WebsocketRequestIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1045,6 +1086,10 @@ impl UpsertModelInfo for WebsocketEvent {
|
||||
generate_prefixed_id("we")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(WebsocketEventIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1158,6 +1203,10 @@ impl UpsertModelInfo for HttpResponse {
|
||||
generate_prefixed_id("rs")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(HttpResponseIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1286,6 +1335,10 @@ impl UpsertModelInfo for GrpcRequest {
|
||||
generate_prefixed_id("gr")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(GrpcRequestIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1409,6 +1462,10 @@ impl UpsertModelInfo for GrpcConnection {
|
||||
generate_prefixed_id("gc")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(GrpcConnectionIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1525,6 +1582,10 @@ impl UpsertModelInfo for GrpcEvent {
|
||||
generate_prefixed_id("ge")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(GrpcEventIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1612,6 +1673,10 @@ impl UpsertModelInfo for Plugin {
|
||||
generate_prefixed_id("pg")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(PluginIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1690,6 +1755,10 @@ impl UpsertModelInfo for SyncState {
|
||||
generate_prefixed_id("ss")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(SyncStateIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1769,6 +1838,10 @@ impl UpsertModelInfo for KeyValue {
|
||||
generate_prefixed_id("kv")
|
||||
}
|
||||
|
||||
fn order_by() -> (impl IntoColumnRef, Order) {
|
||||
(KeyValueIden::CreatedAt, Desc)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
@@ -1919,13 +1992,18 @@ impl<'de> Deserialize<'de> for AnyModel {
|
||||
Some(m) if m == "key_value" => AnyModel::KeyValue(fv(value).unwrap()),
|
||||
Some(m) if m == "plugin" => AnyModel::Plugin(fv(value).unwrap()),
|
||||
Some(m) if m == "settings" => AnyModel::Settings(fv(value).unwrap()),
|
||||
Some(m) if m == "websocket_connection" => AnyModel::WebsocketConnection(fv(value).unwrap()),
|
||||
Some(m) if m == "websocket_connection" => {
|
||||
AnyModel::WebsocketConnection(fv(value).unwrap())
|
||||
}
|
||||
Some(m) if m == "websocket_event" => AnyModel::WebsocketEvent(fv(value).unwrap()),
|
||||
Some(m) if m == "websocket_request" => AnyModel::WebsocketRequest(fv(value).unwrap()),
|
||||
Some(m) if m == "workspace" => AnyModel::Workspace(fv(value).unwrap()),
|
||||
Some(m) if m == "workspace_meta" => AnyModel::WorkspaceMeta(fv(value).unwrap()),
|
||||
Some(m) => {
|
||||
return Err(serde::de::Error::custom(format!("Failed to deserialize AnyModel {}", m)));
|
||||
return Err(serde::de::Error::custom(format!(
|
||||
"Failed to deserialize AnyModel {}",
|
||||
m
|
||||
)));
|
||||
}
|
||||
None => {
|
||||
return Err(serde::de::Error::custom("Missing or invalid model"));
|
||||
@@ -1963,6 +2041,7 @@ pub trait UpsertModelInfo {
|
||||
fn table_name() -> impl IntoTableRef;
|
||||
fn id_column() -> impl IntoIden + Eq + Clone;
|
||||
fn generate_id() -> String;
|
||||
fn order_by() -> (impl IntoColumnRef, Order);
|
||||
fn get_id(&self) -> String;
|
||||
fn insert_values(
|
||||
self,
|
||||
|
||||
@@ -26,7 +26,6 @@ impl<'a> DbContext<'a> {
|
||||
pub fn get_or_create_workspace_meta(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
source: &UpdateSource,
|
||||
) -> Result<WorkspaceMeta> {
|
||||
let workspace_meta = self.get_workspace_meta(workspace_id);
|
||||
if let Some(workspace_meta) = workspace_meta {
|
||||
@@ -38,7 +37,7 @@ impl<'a> DbContext<'a> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.upsert_workspace_meta(&workspace_meta, source)
|
||||
self.upsert_workspace_meta(&workspace_meta, &UpdateSource::Background)
|
||||
}
|
||||
|
||||
pub fn upsert_workspace_meta(
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use crate::connection_or_tx::ConnectionOrTx;
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Error::GenericError;
|
||||
use crate::error::Result;
|
||||
use crate::util::ModelPayload;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::TransactionBehavior;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tauri::{Manager, Runtime};
|
||||
use tauri::{Manager, Runtime, State};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub trait QueryManagerExt<'a, R> {
|
||||
fn db_manager(&'a self) -> State<'a, QueryManager>;
|
||||
fn db(&'a self) -> DbContext<'a>;
|
||||
fn with_db<F, T>(&'a self, func: F) -> T
|
||||
where
|
||||
@@ -20,6 +22,10 @@ pub trait QueryManagerExt<'a, R> {
|
||||
}
|
||||
|
||||
impl<'a, R: Runtime, M: Manager<R>> QueryManagerExt<'a, R> for M {
|
||||
fn db_manager(&'a self) -> State<'a, QueryManager> {
|
||||
self.state::<QueryManager>()
|
||||
}
|
||||
|
||||
fn db(&'a self) -> DbContext<'a> {
|
||||
let qm = self.state::<QueryManager>();
|
||||
qm.inner().connect()
|
||||
@@ -42,7 +48,7 @@ impl<'a, R: Runtime, M: Manager<R>> QueryManagerExt<'a, R> for M {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct QueryManager {
|
||||
pool: Arc<Mutex<Pool<SqliteConnectionManager>>>,
|
||||
events_tx: mpsc::Sender<ModelPayload>,
|
||||
@@ -91,9 +97,12 @@ impl QueryManager {
|
||||
func(&db_context)
|
||||
}
|
||||
|
||||
pub fn with_tx<F, T>(&self, func: F) -> Result<T>
|
||||
pub fn with_tx<T, E>(
|
||||
&self,
|
||||
func: impl FnOnce(&DbContext) -> std::result::Result<T, E>,
|
||||
) -> std::result::Result<T, E>
|
||||
where
|
||||
F: FnOnce(&DbContext) -> Result<T>,
|
||||
E: From<crate::error::Error>,
|
||||
{
|
||||
let mut conn = self
|
||||
.pool
|
||||
@@ -112,11 +121,13 @@ impl QueryManager {
|
||||
|
||||
match func(&db_context) {
|
||||
Ok(val) => {
|
||||
tx.commit()?;
|
||||
tx.commit()
|
||||
.map_err(|e| GenericError(format!("Failed to commit transaction {e:?}")))?;
|
||||
Ok(val)
|
||||
}
|
||||
Err(e) => {
|
||||
tx.rollback()?;
|
||||
tx.rollback()
|
||||
.map_err(|e| GenericError(format!("Failed to rollback transaction {e:?}")))?;
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::collections::BTreeMap;
|
||||
use crate::error::Result;
|
||||
use crate::models::{AnyModel, Environment, Folder, GrpcRequest, HttpRequest, UpsertModelInfo, WebsocketRequest, Workspace, WorkspaceIden};
|
||||
use crate::query_manager::QueryManagerExt;
|
||||
@@ -6,6 +5,7 @@ use chrono::{NaiveDateTime, Utc};
|
||||
use log::warn;
|
||||
use nanoid::nanoid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use tauri::{AppHandle, Listener, Runtime, WebviewWindow};
|
||||
use ts_rs::TS;
|
||||
|
||||
@@ -14,6 +14,10 @@ pub fn generate_prefixed_id(prefix: &str) -> String {
|
||||
}
|
||||
|
||||
pub fn generate_id() -> String {
|
||||
generate_id_of_length(10)
|
||||
}
|
||||
|
||||
pub fn generate_id_of_length(n: usize) -> String {
|
||||
let alphabet: [char; 57] = [
|
||||
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C',
|
||||
@@ -21,7 +25,7 @@ pub fn generate_id() -> String {
|
||||
'X', 'Y', 'Z',
|
||||
];
|
||||
|
||||
nanoid!(10, &alphabet)
|
||||
nanoid!(n, &alphabet)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
@@ -101,8 +105,9 @@ pub struct WorkspaceExport {
|
||||
pub resources: BatchUpsertResult,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||
#[derive(Default, Debug, Deserialize, Serialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_util.ts")]
|
||||
pub struct BatchUpsertResult {
|
||||
pub workspaces: Vec<Workspace>,
|
||||
pub environments: Vec<Environment>,
|
||||
|
||||
Reference in New Issue
Block a user