Compare commits

..

121 Commits

Author SHA1 Message Date
Gregory Schier
fdb4331032 Build plugins 2025-01-14 11:03:15 -08:00
Gregory Schier
8d645eb8c6 Fix sync setting truncation 2025-01-14 06:49:53 -08:00
Gregory Schier
592cf38e38 Fix yaak schema in export 2025-01-14 06:31:48 -08:00
Gregory Schier
ac0ecb342d Fix tab jiggle 2025-01-14 06:15:52 -08:00
Gregory Schier
439a29ab46 Update README 2025-01-14 06:15:46 -08:00
Gregory Schier
db64b54c79 Slight cleanup 2025-01-13 16:59:39 -08:00
Gregory Schier
49f5e980de Clean up filesystem sync setting 2025-01-13 16:46:56 -08:00
Gregory Schier
658e2179ca Add beta feedback badge 2025-01-13 16:16:01 -08:00
Gregory Schier
e7184e4d47 Fix releases lint 2025-01-13 12:32:20 -08:00
Gregory Schier
6719573b2b Preserve dropdown trigger button background when menu is open 2025-01-13 12:28:31 -08:00
Gregory Schier
4479164321 Merge remote-tracking branch 'origin/master' 2025-01-13 12:11:42 -08:00
Gregory Schier
4295a09515 Adjust settings window and fix dynamic dialog 2025-01-13 12:11:38 -08:00
Gregory Schier
bb5da84c82 Fix Windows/Linux CmdCtrl hotkey 2025-01-13 12:10:23 -08:00
Gregory Schier
72ab3f0a3c Better SegmentedControl styles 2025-01-13 11:08:48 -08:00
Gregory Schier
eea87ac02f Better SegmentedControl styles 2025-01-13 11:08:04 -08:00
Gregory Schier
587667fe79 Better markdown editor and SegmentedControl 2025-01-13 10:46:13 -08:00
Gregory Schier
84c3987c34 Fix unused var 2025-01-13 09:58:37 -08:00
Gregory Schier
40a77be556 Update plugins 2025-01-13 08:53:14 -08:00
Gregory Schier
d37cfad862 Fix var underscores 2025-01-13 07:41:13 -08:00
Gregory Schier
34c0449a40 Merge remote-tracking branch 'origin/master' 2025-01-13 07:15:13 -08:00
Gregory Schier
ad4d695b75 Fix environment activation and setting active cookie jar 2025-01-13 07:15:01 -08:00
OTonGitHub
969e1b965d fix grammer in readme (#154) 2025-01-13 06:40:03 -08:00
Gregory Schier
88ff7f4300 Support dashes in template variable/fn names 2025-01-13 06:38:21 -08:00
Gregory Schier
8cd9c031e8 Add back sidebar hover style 2025-01-12 21:17:29 -08:00
Gregory Schier
806ce2f0ba Fix request pane name styling 2025-01-11 14:33:00 -08:00
Gregory Schier
dcb17c3ed4 Comment 2025-01-11 14:18:16 -08:00
Gregory Schier
d2936cb022 Ensure only one dropdown can be open at a time 2025-01-11 14:16:37 -08:00
Gregory Schier
ba330047ca Cargo format 2025-01-11 13:53:30 -08:00
Gregory Schier
295aea4f2e Add single-instance plugin 2025-01-11 13:50:44 -08:00
Gregory Schier
476dbc432b Add FocusTrap to dropdown menu to fix filtering 2025-01-11 12:15:01 -08:00
Gregory Schier
8dff75ad4f Add ability to exclude environments from data export 2025-01-11 11:36:00 -08:00
Gregory Schier
88b410bf99 Fix pairs language not highlighting 2025-01-11 08:54:45 -08:00
Gregory Schier
3d3ff2824f Improve Dropdown selection handling 2025-01-11 08:15:45 -08:00
Gregory Schier
3b56f4e142 Support TLS 1.3 (#153) 2025-01-11 06:51:34 -08:00
Gregory Schier
576340db33 Improve initial sync subscription 2025-01-11 06:47:03 -08:00
Gregory Schier
bcf5b3db84 Fix dropdown not closing when clicking trigger 2025-01-11 06:46:47 -08:00
Gregory Schier
8b5b66acf0 Don't load response when blocking large responses 2025-01-10 06:27:57 -08:00
Gregory Schier
f694456ddc Fix workspace creation, reveal sync dir, and don't update timestamps on sync/import 2025-01-09 07:50:23 -08:00
Gregory Schier
0a7257c55a Fix redirecting to latest workspace 2025-01-08 22:08:59 -08:00
Gregory Schier
328e3db56e Fix UpdateSource for sync upserts 2025-01-08 15:25:03 -08:00
Gregory Schier
cbc443075a Ability to open workspace from directory, WorkspaceMeta, and many sync improvements 2025-01-08 14:57:13 -08:00
Gregory Schier
37671a50f2 Ensure fs_sync always writes to current state's path (even if Yaak changes the default) 2025-01-08 09:20:59 -08:00
Gregory Schier
95266a9177 Tweak workspace settings dialog and Markdown editor 2025-01-08 08:54:40 -08:00
Gregory Schier
eeb66ca28a Fix UrlBar padding 2025-01-08 06:43:45 -08:00
Gregory Schier
d745e91f80 Backspace to delete selected in sidebar 2025-01-08 06:42:32 -08:00
Gregory Schier
7a9c2e2223 Move info tab to last 2025-01-08 06:21:44 -08:00
Gregory Schier
1d51bd642a Menu now uses "click away" instead of backdrop 2025-01-08 06:20:21 -08:00
Gregory Schier
1920f720a9 Some small tweaks for Vim mode 2025-01-08 06:11:05 -08:00
Gregory Schier
81005165f3 Vim/emacs/vscode keybindings 2025-01-07 22:27:43 -08:00
Gregory Schier
3cf372c01e Fix workspace creation dialog 2025-01-07 07:05:23 -08:00
Gregory Schier
2f7b66fc92 A bunch of changes, including moving prompt/confirm out of context 2025-01-07 06:56:51 -08:00
Gregory Schier
4776bbc753 Fix more 2025-01-07 05:37:50 -08:00
Gregory Schier
79f668c863 Fix split layout not always working 2025-01-07 05:36:27 -08:00
Gregory Schier
a164875104 Move saved tab state to jotai with LS 2025-01-06 17:06:56 -08:00
Gregory Schier
bc50891edb Remove useNavigate everywhere, and make request a query param. And convert dialog to Jotai 2025-01-06 16:54:07 -08:00
Gregory Schier
806a8eb801 Remove useToast everywhere 2025-01-06 12:21:21 -08:00
Gregory Schier
ab55c2e0ce Move toast state to Jotai 2025-01-06 12:05:43 -08:00
Gregory Schier
c2ea2a5fe5 Dir sync filesystem watching 2025-01-06 09:24:07 -08:00
Gregory Schier
c72180bb59 Upgrade Tauri and add Tauri devtools 2025-01-05 11:33:39 -08:00
Gregory Schier
17fdd608d1 Optimize directory sync performance 2025-01-05 10:56:40 -08:00
Gregory Schier
40adce921b Move JS monorepo packages to folder 2025-01-04 16:55:18 -08:00
Gregory Schier
75ead9cc8a Fix error handling 2025-01-04 07:44:39 -08:00
Gregory Schier
609bd4cdea Update license message 2025-01-04 07:36:38 -08:00
Gregory Schier
68e1b5d746 Update license message 2025-01-04 07:32:42 -08:00
Gregory Schier
53f5ef3515 Add separate SVG response viewer 2025-01-04 07:14:33 -08:00
Gregory Schier
592c1228f1 Fix tauri event listener hook 2025-01-04 07:01:31 -08:00
Gregory Schier
36cecb2d29 Fix active workspace deletion 2025-01-03 20:50:18 -08:00
Gregory Schier
31440eea76 Filesystem Sync (#142) 2025-01-03 20:41:00 -08:00
Gregory Schier
6ad27c4458 Put delete workspace in settings 2025-01-02 08:41:44 -08:00
Gregory Schier
0dd09062e3 Sort workspaces, envs, jars 2025-01-02 08:33:04 -08:00
Gregory Schier
5ebf7dc499 Tackled remaining perf wins 2025-01-02 06:51:54 -08:00
Gregory Schier
42cd4a5f0f Split out slow pathParameters extension and skip unnecessary model updates 2025-01-01 16:42:53 -08:00
Gregory Schier
add39bda6e Revert to preserving editor state with fromJson due to state callbacks not being preserved 2025-01-01 08:19:41 -08:00
Gregory Schier
be938a81dc Fix settings update bug 2025-01-01 07:10:29 -08:00
Gregory Schier
4b807f221b A bunch of fixes 2025-01-01 07:01:41 -08:00
Gregory Schier
80119f6574 Prevent a bunch more stuff from re-rendering 2024-12-31 23:24:41 -08:00
Gregory Schier
dfca17f9b7 Prevent sidebar re-render on every keypress (#152) 2024-12-31 15:02:10 -08:00
Gregory Schier
135c366e32 Preserve Editor State (#151) 2024-12-31 07:31:43 -08:00
Gregory Schier
31f2bff0f6 Optimize sidebar collapsing 2024-12-23 05:05:04 -08:00
Gregory Schier
61d094d9fd Some fixes around environments 2024-12-21 11:04:49 -08:00
Gregory Schier
c1d5881167 Merge branch 'master' into cleanup
# Conflicts:
#	src-web/components/MarkdownEditor.tsx
#	src-web/components/RequestPane.tsx
#	src-web/hooks/useActiveEnvironment.ts
2024-12-21 05:46:33 -08:00
Gregory Schier
dd8ccfe21f Extract base environment (#149) 2024-12-21 05:44:55 -08:00
Gregory Schier
b4b29babfd Adjust markdown editor 2024-12-21 05:39:11 -08:00
Gregory Schier
ecabe9b6ef Cleanup (#148) 2024-12-20 23:49:48 -08:00
Gregory Schier
ec999015ab Fixed the circular imports and things 2024-12-20 23:49:15 -08:00
Gregory Schier
51a11b6495 Switch to useMutation in some places 2024-12-20 17:38:41 -08:00
Gregory Schier
27134a52ad Performance sweep (#147) 2024-12-20 17:31:15 -08:00
Gregory Schier
42bf016e90 Add the ability to duplicate folders (#144) 2024-12-19 13:06:08 -08:00
Gregory Schier
833dc7d3f7 Markdown documentation for HTTP requests (#145) 2024-12-19 05:57:40 -08:00
Gregory Schier
42d350ef27 Disallow drag-n-drop when editing request name 2024-12-17 06:22:29 -08:00
Gregory Schier
a81f9d07cb Tweak license message 2024-12-17 06:14:12 -08:00
Gregory Schier
cb6e3d4ac8 Better insight into settings updates 2024-12-16 16:27:13 -08:00
Gregory Schier
5ff5d6fb1d Update release info 2024-12-16 13:53:45 -08:00
Gregory Schier
e2253786dc Tweak license flow 2024-12-16 13:46:58 -08:00
Gregory Schier
20140148bf Update gruvbox theme 2024-12-13 06:38:54 -08:00
Albert Hansrisuk
4b9dce26ac feat: add gruvbox dark theme (#140) 2024-12-13 06:30:10 -08:00
Jeffrey Mitchell
3b2c2960a9 Fix: Settings Window Title Bar Draggable Region Deadzone (#139) 2024-12-09 20:58:55 -08:00
Gregory Schier
a79578142d Port some stuff from sync PR 2024-12-05 11:27:49 -08:00
Gregory Schier
2b61257e50 Fix date 2024-12-03 11:48:39 -08:00
Gregory Schier
1f4eea89c5 Better license badge placement 2024-12-03 11:06:50 -08:00
Gregory Schier
1609e46660 Fix CI 2024-12-03 10:58:39 -08:00
Gregory Schier
28d5a2a019 Add copy 2024-12-03 10:09:44 -08:00
Gregory Schier
40f0f5387a Fix lint problems 2024-12-03 09:43:16 -08:00
Gregory Schier
88bcfb9e66 Changes for commercial use (#138) 2024-12-03 09:28:27 -08:00
Gregory Schier
2b076c90e4 Increase gRPC max message sizes 2024-12-03 09:24:14 -08:00
Leandro Otoni
0443fbdfdb Merge pull request #137
* add codeFolding config for json
2024-12-03 07:15:38 -08:00
Gregory Schier
36d24bdac0 Tweaked header size logic 2024-12-03 07:11:25 -08:00
Peiman Nourani
d4dfc1c820 Hide window controls in macOS fullscreen mode (#134) 2024-12-03 07:08:01 -08:00
Bad3r
00178ad197 Feat: Add Support for Nord Theme 🏔️ (#132)
Signed-off-by: Bad3r <bad3r@pm.me>
2024-12-03 07:00:47 -08:00
Bad3r
f8efd1a31a fix: Add Missing Themes Moonlight & Dracula (#133)
Signed-off-by: Bad3r <bad3r@pm.me>
2024-12-03 06:57:33 -08:00
Gregory Schier
e1363cf151 Make protoc writable before signing 2024-11-23 06:18:17 -08:00
Gregory Schier
38e0f5ede7 Remove unnecessary things in Windows signing conf 2024-11-23 05:27:12 -08:00
Gregory Schier
9663018e21 Update tauri.conf.json 2024-11-22 08:51:29 -08:00
Gregory Schier
80a7c2a9c7 Try updating protoc 2024-11-22 07:09:59 -08:00
Gregory Schier
4687723176 Fix Tauri signing account name 2024-11-22 06:17:37 -08:00
Gregory Schier
41ce2df00c Move signing into Tauri 2024-11-22 05:49:24 -08:00
Gregory Schier
794967904a Try signing again 2024-11-21 13:43:05 -08:00
Gregory Schier
74a7a1a21a Merge remote-tracking branch 'origin/master' 2024-11-21 13:13:10 -08:00
Gregory Schier
d9587aa314 First attempt at Windows signing 2024-11-21 13:13:05 -08:00
dependabot[bot]
6b208ef67c Bump cross-spawn (#135)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-20 21:15:30 -08:00
Gregory Schier
0cfec0ada6 Fix GraphQL body handling 2024-11-18 07:17:19 -08:00
404 changed files with 15455 additions and 6583 deletions

View File

@@ -8,14 +8,15 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'eslint-config-prettier',
],
plugins: ['react-refresh'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: ['./tsconfig.json'],
},
ignorePatterns: [
'scripts/**/*',
'plugin-runtime/**/*',
'plugin-runtime-types/**/*',
'packages/plugin-runtime/**/*',
'packages/plugin-runtime-types/**/*',
'src-tauri/**/*',
'src-web/tailwind.config.cjs',
'src-web/vite.config.ts',
@@ -32,6 +33,7 @@ module.exports = {
},
},
rules: {
'react-refresh/only-export-components': 'error',
'jsx-a11y/no-autofocus': 'off',
'react/react-in-jsx-scope': 'off',
'import/no-unresolved': 'off',

View File

@@ -22,7 +22,7 @@ jobs:
- platform: 'macos-latest' # for Intel-based Macs.
args: '--target x86_64-apple-darwin'
yaak_arch: 'x64'
- platform: 'ubuntu-22.04' # for Tauri v1, you could replace this with ubuntu-20.04.
- platform: 'ubuntu-22.04'
args: ''
yaak_arch: 'x64'
- platform: 'windows-latest'
@@ -38,10 +38,6 @@ jobs:
with:
node-version: 22
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
@@ -56,7 +52,7 @@ jobs:
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
src-tauri/target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
@@ -66,6 +62,10 @@ jobs:
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: install dependencies (windows only)
if: matrix.platform == 'windows-latest'
run: cargo install --force trusted-signing-cli
- name: Install NPM Dependencies
run: |
npm ci
@@ -76,6 +76,9 @@ jobs:
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Run JS build
run: npm run build
- name: Run lint
run: npm run lint
@@ -94,20 +97,32 @@ jobs:
env:
YAAK_PLUGINS_DIR: ${{ env.YAAK_PLUGINS_DIR }}
YAAK_TARGET_ARCH: ${{ matrix.yaak_arch }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
# Apple signing stuff
APPLE_CERTIFICATE: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_SIGNING_IDENTITY }}
APPLE_TEAM_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_TEAM_ID }}
# Windows signing stuff
AZURE_CLIENT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ matrix.platform == 'windows-latest' && secrets.AZURE_TENANT_ID }}
with:
tagName: 'v__VERSION__'
releaseName: 'Release __VERSION__'
releaseBody: 'https://yaak.app/blog/__VERSION__'
releaseBody: |
> [!IMPORTANT]
> The Yaak project is open source. However, to fund development, these pre-built binaries require a license for commercial use. Please see the [Pricing Page](https://yaak.app/pricing) for more details.
You can view the full release notes here → https://yaak.app/blog/2024.13.0
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}

View File

@@ -11,10 +11,16 @@ All feedback, bug reports, questions, and feature requests should be reported to
[feedback.yaak.app](https://feedback.yaak.app). Issues will be duplicated
in this repository if applicable.
## Community Projects
- [`yaak2postman`](https://github.com/BiteCraft/yaak2postman) CLI for converting Yaak data
exports to Postman-compatible collections
## Contribution Policy
Yaak open source, but only accepting contributions for bug fixes. See the
Yaak is open source, but only accepting contributions for bug fixes. See the
[`good first issue`](https://github.com/yaakapp/app/labels/good%20first%20issue) label for
issues that are more approachable for contribution.
To get started, visit [`DEVELOPMENT.md`](DEVELOPMENT.md) for tips on setting up your environment.
To get started, visit [`DEVELOPMENT.md`](DEVELOPMENT.md) for tips on setting up your
environment.

3341
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,22 +4,25 @@
"version": "0.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/yaakapp/app.git"
"url": "git+https://github.com/mountain-loop/yaak.git"
},
"workspaces": [
"plugin-runtime",
"plugin-runtime-types",
"src-tauri/yaak_models",
"src-tauri/yaak_plugin_runtime",
"src-tauri/yaak_sync",
"src-tauri/yaak_templates",
"src-tauri/yaak_sse",
"packages/plugin-runtime",
"packages/plugin-runtime-types",
"packages/common-lib",
"src-tauri/yaak-license",
"src-tauri/yaak-models",
"src-tauri/yaak-plugins",
"src-tauri/yaak-sse",
"src-tauri/yaak-sync",
"src-tauri/yaak-templates",
"src-web"
],
"scripts": {
"start": "npm run app-dev",
"app-build": "tauri build",
"app-dev": "tauri dev --no-watch --config ./src-tauri/tauri-dev.conf.json",
"build": "npm run --workspaces --if-present build",
"bootstrap": "run-p bootstrap:* && npm run --workspaces --if-present bootstrap",
"bootstrap:vendor-node": "node scripts/vendor-node.cjs",
"bootstrap:vendor-plugins": "node scripts/vendor-plugins.cjs",
@@ -31,18 +34,18 @@
"tauri-before-dev": "npm run --workspaces --if-present dev"
},
"devDependencies": {
"@tauri-apps/cli": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^8.5.0",
"@tauri-apps/cli": "^2.2.2",
"@typescript-eslint/eslint-plugin": "^8.18.1",
"@typescript-eslint/parser": "^8.18.1",
"eslint": "^8",
"eslint-config-prettier": "^8",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-react": "^7.35.2",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.1.0",
"nodejs-file-downloader": "^4.13.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",
"typescript": "^5.6.2"
"prettier": "^3.4.2",
"typescript": "^5.7.2"
}
}

View File

@@ -0,0 +1 @@
export * from './debounce';

View File

@@ -1,5 +1,5 @@
{
"name": "@yaakapp-internal/plugin",
"name": "@yaakapp-internal/lib",
"private": true,
"version": "1.0.0",
"main": "index.ts"

View File

@@ -1,6 +1,6 @@
{
"name": "@yaakapp/api",
"version": "0.2.16",
"version": "0.2.17",
"main": "lib/index.js",
"typings": "./lib/index.d.ts",
"files": [
@@ -11,8 +11,8 @@
"build": "run-s build:copy-types build:tsc",
"build:tsc": "tsc",
"build:copy-types": "run-p build:copy-types:*",
"build:copy-types:root": "cpy --flat ../src-tauri/yaak_plugin_runtime/bindings/*.ts ./src/bindings",
"build:copy-types:next": "cpy --flat ../src-tauri/yaak_plugin_runtime/bindings/serde_json/*.ts ./src/bindings/serde_json",
"build:copy-types:root": "cpy --flat ../../src-tauri/yaak-plugin-runtime/bindings/*.ts ./src/bindings",
"build:copy-types:next": "cpy --flat ../../src-tauri/yaak-plugin-runtime/bindings/serde_json/*.ts ./src/bindings/serde_json",
"prepublishOnly": "npm run build"
},
"dependencies": {

View File

@@ -0,0 +1,338 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type {
Environment,
Folder,
GrpcRequest,
HttpRequest,
HttpResponse,
Workspace,
} from './models';
import type { JsonValue } from './serde_json/JsonValue';
export type BootRequest = { dir: string; watch: boolean };
export type BootResponse = { name: string; version: string; capabilities: Array<string> };
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest };
export type CallHttpRequestActionRequest = {
key: string;
pluginRefId: string;
args: CallHttpRequestActionArgs;
};
export type CallTemplateFunctionArgs = {
purpose: RenderPurpose;
values: { [key in string]?: string };
};
export type CallTemplateFunctionRequest = { name: string; args: CallTemplateFunctionArgs };
export type CallTemplateFunctionResponse = { value: string | null };
export type Color =
| 'custom'
| 'default'
| 'primary'
| 'secondary'
| 'info'
| 'success'
| 'notice'
| 'warning'
| 'danger';
export type CopyTextRequest = { text: string };
export type ExportHttpRequestRequest = { httpRequest: HttpRequest };
export type ExportHttpRequestResponse = { content: string };
export type FilterRequest = { content: string; filter: string };
export type FilterResponse = { content: string };
export type FindHttpResponsesRequest = { requestId: string; limit?: number };
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse> };
export type GetHttpRequestActionsRequest = Record<string, never>;
export type GetHttpRequestActionsResponse = {
actions: Array<HttpRequestAction>;
pluginRefId: string;
};
export type GetHttpRequestByIdRequest = { id: string };
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null };
export type GetTemplateFunctionsResponse = {
functions: Array<TemplateFunction>;
pluginRefId: string;
};
export type HttpRequestAction = { key: string; label: string; icon?: Icon };
export type Icon = 'copy' | 'info' | 'check_circle' | 'alert_triangle' | '_unknown';
export type ImportRequest = { content: string };
export type ImportResources = {
workspaces: Array<Workspace>;
environments: Array<Environment>;
folders: Array<Folder>;
httpRequests: Array<HttpRequest>;
grpcRequests: Array<GrpcRequest>;
};
export type ImportResponse = { resources: ImportResources };
export type InternalEvent = {
id: string;
pluginRefId: string;
replyId: string | null;
payload: InternalEventPayload;
windowContext: WindowContext;
};
export type InternalEventPayload =
| ({ type: 'boot_request' } & BootRequest)
| ({ type: 'boot_response' } & BootResponse)
| { type: 'reload_request' }
| { type: 'reload_response' }
| { type: 'terminate_request' }
| { type: 'terminate_response' }
| ({ type: 'import_request' } & ImportRequest)
| ({ type: 'import_response' } & ImportResponse)
| ({ type: 'filter_request' } & FilterRequest)
| ({ type: 'filter_response' } & FilterResponse)
| ({ type: 'export_http_request_request' } & ExportHttpRequestRequest)
| ({ type: 'export_http_request_response' } & ExportHttpRequestResponse)
| ({ type: 'send_http_request_request' } & SendHttpRequestRequest)
| ({ type: 'send_http_request_response' } & SendHttpRequestResponse)
| ({ type: 'get_http_request_actions_request' } & GetHttpRequestActionsRequest)
| ({ type: 'get_http_request_actions_response' } & GetHttpRequestActionsResponse)
| ({ type: 'call_http_request_action_request' } & CallHttpRequestActionRequest)
| { type: 'get_template_functions_request' }
| ({ type: 'get_template_functions_response' } & GetTemplateFunctionsResponse)
| ({ type: 'call_template_function_request' } & CallTemplateFunctionRequest)
| ({ type: 'call_template_function_response' } & CallTemplateFunctionResponse)
| ({ type: 'copy_text_request' } & CopyTextRequest)
| ({ type: 'render_http_request_request' } & RenderHttpRequestRequest)
| ({ type: 'render_http_request_response' } & RenderHttpRequestResponse)
| ({ type: 'template_render_request' } & TemplateRenderRequest)
| ({ type: 'template_render_response' } & TemplateRenderResponse)
| ({ type: 'show_toast_request' } & ShowToastRequest)
| ({ type: 'prompt_text_request' } & PromptTextRequest)
| ({ type: 'prompt_text_response' } & PromptTextResponse)
| ({ type: 'get_http_request_by_id_request' } & GetHttpRequestByIdRequest)
| ({ type: 'get_http_request_by_id_response' } & GetHttpRequestByIdResponse)
| ({ type: 'find_http_responses_request' } & FindHttpResponsesRequest)
| ({ type: 'find_http_responses_response' } & FindHttpResponsesResponse)
| { type: 'empty_response' };
export type OpenFileFilter = {
name: string;
/**
* File extensions to require
*/
extensions: Array<string>;
};
export type PromptTextRequest = {
id: string;
title: string;
label: string;
description?: string;
defaultValue?: string;
placeholder?: string;
/**
* Text to add to the confirmation button
*/
confirmText?: string;
/**
* Text to add to the cancel button
*/
cancelText?: string;
/**
* Require the user to enter a non-empty value
*/
require?: boolean;
};
export type PromptTextResponse = { value: string | null };
export type RenderHttpRequestRequest = { httpRequest: HttpRequest; purpose: RenderPurpose };
export type RenderHttpRequestResponse = { httpRequest: HttpRequest };
export type RenderPurpose = 'send' | 'preview';
export type SendHttpRequestRequest = { httpRequest: HttpRequest };
export type SendHttpRequestResponse = { httpResponse: HttpResponse };
export type ShowToastRequest = { message: string; color?: Color; icon?: Icon };
export type TemplateFunction = {
name: string;
description?: string;
/**
* Also support alternative names. This is useful for not breaking existing
* tags when changing the `name` property
*/
aliases?: Array<string>;
args: Array<TemplateFunctionArg>;
};
export type TemplateFunctionArg =
| ({ type: 'text' } & TemplateFunctionTextArg)
| ({
type: 'select';
} & TemplateFunctionSelectArg)
| ({ type: 'checkbox' } & TemplateFunctionCheckboxArg)
| ({
type: 'http_request';
} & TemplateFunctionHttpRequestArg)
| ({ type: 'file' } & TemplateFunctionFileArg);
export type TemplateFunctionBaseArg = {
/**
* The name of the argument. Should be `camelCase` format
*/
name: string;
/**
* Whether the user must fill in the argument
*/
optional?: boolean;
/**
* The label of the input
*/
label?: string;
/**
* The default value
*/
defaultValue?: string;
};
export type TemplateFunctionCheckboxArg = {
/**
* The name of the argument. Should be `camelCase` format
*/
name: string;
/**
* Whether the user must fill in the argument
*/
optional?: boolean;
/**
* The label of the input
*/
label?: string;
/**
* The default value
*/
defaultValue?: string;
};
export type TemplateFunctionFileArg = {
/**
* The title of the file selection window
*/
title: string;
/**
* Allow selecting multiple files
*/
multiple?: boolean;
directory?: boolean;
defaultPath?: string;
filters?: Array<OpenFileFilter>;
/**
* The name of the argument. Should be `camelCase` format
*/
name: string;
/**
* Whether the user must fill in the argument
*/
optional?: boolean;
/**
* The label of the input
*/
label?: string;
/**
* The default value
*/
defaultValue?: string;
};
export type TemplateFunctionHttpRequestArg = {
/**
* The name of the argument. Should be `camelCase` format
*/
name: string;
/**
* Whether the user must fill in the argument
*/
optional?: boolean;
/**
* The label of the input
*/
label?: string;
/**
* The default value
*/
defaultValue?: string;
};
export type TemplateFunctionSelectArg = {
/**
* The options that will be available in the select input
*/
options: Array<TemplateFunctionSelectOption>;
/**
* The name of the argument. Should be `camelCase` format
*/
name: string;
/**
* Whether the user must fill in the argument
*/
optional?: boolean;
/**
* The label of the input
*/
label?: string;
/**
* The default value
*/
defaultValue?: string;
};
export type TemplateFunctionSelectOption = { label: string; value: string };
export type TemplateFunctionTextArg = {
/**
* Placeholder for the text input
*/
placeholder?: string;
/**
* The name of the argument. Should be `camelCase` format
*/
name: string;
/**
* Whether the user must fill in the argument
*/
optional?: boolean;
/**
* The label of the input
*/
label?: string;
/**
* The default value
*/
defaultValue?: string;
};
export type TemplateRenderRequest = { data: JsonValue; purpose: RenderPurpose };
export type TemplateRenderResponse = { data: JsonValue };
export type WindowContext = { type: 'none' } | { type: 'label'; label: string };

View File

@@ -1,16 +1,16 @@
// 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, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
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, };
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, sortPriority: number, };
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, };
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: 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, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
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, };
@@ -22,4 +22,4 @@ export type HttpResponseState = "initialized" | "connected" | "closed";
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, variables: Array<EnvironmentVariable>, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };

View File

@@ -1,4 +1,4 @@
import {
import type {
FindHttpResponsesRequest,
FindHttpResponsesResponse,
GetHttpRequestByIdRequest,

View File

@@ -1,4 +1,4 @@
import { Context } from './Context';
import type { Context } from './Context';
export type FilterPluginResponse = string[];

View File

@@ -1,5 +1,5 @@
import { CallHttpRequestActionArgs, HttpRequestAction } from '..';
import { Context } from './Context';
import type { CallHttpRequestActionArgs, HttpRequestAction } from '..';
import type { Context } from './Context';
export type HttpRequestActionPlugin = HttpRequestAction & {
onSelect(ctx: Context, args: CallHttpRequestActionArgs): Promise<void> | void;

View File

@@ -1,12 +1,13 @@
import { Environment, Folder, HttpRequest, Workspace } from '..';
import { AtLeast } from '../helpers';
import { Context } from './Context';
import type { Environment, Folder, GrpcRequest, HttpRequest, Workspace } from '..';
import type { AtLeast } from '../helpers';
import type { Context } from './Context';
export type ImportPluginResponse = null | {
workspaces: AtLeast<Workspace, 'name' | 'id' | 'model'>[];
environments: AtLeast<Environment, 'name' | 'id' | 'model' | 'workspaceId'>[];
httpRequests: AtLeast<HttpRequest, 'name' | 'id' | 'model' | 'workspaceId'>[];
folders: AtLeast<Folder, 'name' | 'id' | 'model' | 'workspaceId'>[];
httpRequests: AtLeast<HttpRequest, 'name' | 'id' | 'model' | 'workspaceId'>[];
grpcRequests: AtLeast<GrpcRequest, 'name' | 'id' | 'model' | 'workspaceId'>[];
};
export type ImporterPlugin = {

View File

@@ -1,5 +1,5 @@
import { CallTemplateFunctionArgs, TemplateFunction } from '..';
import { Context } from './Context';
import type { CallTemplateFunctionArgs, TemplateFunction } from '..';
import type { Context } from './Context';
export type TemplateFunctionPlugin = TemplateFunction & {
onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null>;

View File

@@ -1,5 +1,5 @@
import { Theme } from '../themes';
import { Context } from './Context';
import type { Theme } from '../themes';
import type { Context } from './Context';
export type ThemePlugin = {
name: string;

View File

@@ -1,8 +1,8 @@
import { FilterPlugin } from './FilterPlugin';
import { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
import { ImporterPlugin } from './ImporterPlugin';
import { TemplateFunctionPlugin } from './TemplateFunctionPlugin';
import { ThemePlugin } from './ThemePlugin';
import type { FilterPlugin } from './FilterPlugin';
import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
import type { ImporterPlugin } from './ImporterPlugin';
import type { TemplateFunctionPlugin } from './TemplateFunctionPlugin';
import type { ThemePlugin } from './ThemePlugin';
export type { Context } from './Context';

View File

@@ -3,9 +3,9 @@
"scripts": {
"bootstrap": "npm run build",
"build": "run-p build:*",
"build:main": "esbuild src/index.ts --bundle --platform=node --outfile=../src-tauri/vendored/plugin-runtime/index.cjs",
"build:worker": "esbuild src/index.worker.ts --bundle --platform=node --outfile=../src-tauri/vendored/plugin-runtime/index.worker.cjs",
"build:proto": "grpc_tools_node_protoc --ts_proto_out=src/gen --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false --proto_path=../proto ../proto/plugins/*.proto"
"build:main": "esbuild src/index.ts --bundle --platform=node --outfile=../../src-tauri/vendored/plugin-runtime/index.cjs",
"build:worker": "esbuild src/index.worker.ts --bundle --platform=node --outfile=../../src-tauri/vendored/plugin-runtime/index.worker.cjs",
"build:proto": "grpc_tools_node_protoc --ts_proto_out=src/gen --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false --proto_path=../../proto ../../proto/plugins/*.proto"
},
"dependencies": {
"intercept-stdout": "^0.1.2",

View File

@@ -1,6 +1,6 @@
import { InternalEvent } from '@yaakapp/api';
import type { InternalEvent } from '@yaakapp/api';
import EventEmitter from 'node:events';
import { EventStreamEvent } from './gen/plugins/runtime';
import type { EventStreamEvent } from './gen/plugins/runtime';
export class EventChannel {
emitter: EventEmitter = new EventEmitter();

View File

@@ -1,8 +1,8 @@
import { BootRequest, InternalEvent } from '@yaakapp-internal/plugin';
import type { BootRequest, InternalEvent } from '@yaakapp-internal/plugins';
import path from 'node:path';
import { Worker } from 'node:worker_threads';
import { EventChannel } from './EventChannel';
import { PluginWorkerData } from './index.worker';
import type { EventChannel } from './EventChannel';
import type { PluginWorkerData } from './index.worker';
export class PluginHandle {
#worker: Worker;

View File

@@ -1,12 +1,16 @@
import { InternalEvent } from '@yaakapp/api';
import type { InternalEvent } from '@yaakapp/api';
import { createChannel, createClient, Status } from 'nice-grpc';
import { EventChannel } from './EventChannel';
import { PluginRuntimeClient, PluginRuntimeDefinition } from './gen/plugins/runtime';
import type { PluginRuntimeClient} from './gen/plugins/runtime';
import { PluginRuntimeDefinition } from './gen/plugins/runtime';
import { PluginHandle } from './PluginHandle';
const port = process.env.PORT || '50051';
const channel = createChannel(`localhost:${port}`);
const channel = createChannel(`localhost:${port}`, undefined, {
'grpc.max_receive_message_length': Number.MAX_SAFE_INTEGER,
'grpc.max_send_message_length': Number.MAX_SAFE_INTEGER,
});
const client: PluginRuntimeClient = createClient(PluginRuntimeDefinition, channel);
const events = new EventChannel();

View File

@@ -1,4 +1,4 @@
import {
import type {
BootRequest,
FindHttpResponsesResponse,
GetHttpRequestByIdResponse,
@@ -12,13 +12,14 @@ import {
TemplateFunction,
TemplateRenderResponse,
WindowContext,
} from '@yaakapp-internal/plugin';
import { Context } from '@yaakapp/api';
import { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/HttpRequestActionPlugin';
import { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
} from '@yaakapp-internal/plugins';
import type { Context } from '@yaakapp/api';
import type { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/HttpRequestActionPlugin';
import type { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
import interceptStdout from 'intercept-stdout';
import * as console from 'node:console';
import { readFileSync, Stats, statSync, watch } from 'node:fs';
import type { Stats} from 'node:fs';
import { readFileSync, statSync, watch } from 'node:fs';
import path from 'node:path';
import * as util from 'node:util';
import { parentPort, workerData } from 'node:worker_threads';
@@ -200,7 +201,7 @@ async function initialize() {
// Message comes into the plugin to be processed
parentPort!.on('message', async (event: InternalEvent) => {
let { windowContext, payload, id: replyId } = event;
const { windowContext, payload, id: replyId } = event;
const ctx = newCtx(event);
try {
if (payload.type === 'boot_request') {

View File

@@ -1,10 +1,10 @@
const decompress = require('decompress');
const Downloader = require('nodejs-file-downloader');
const path = require('node:path');
const { rmSync, mkdirSync, cpSync, existsSync } = require('node:fs');
const { rmSync, mkdirSync, cpSync, existsSync, statSync, chmodSync } = require('node:fs');
const { execSync } = require('node:child_process');
const VERSION = '27.2';
const VERSION = '28.3';
// `${process.platform}_${process.arch}`
const MAC_ARM = 'darwin_arm64';
@@ -67,6 +67,11 @@ mkdirSync(dstDir, { recursive: true });
cpSync(includeSrc, includeDst, { recursive: true });
rmSync(tmpDir, { recursive: true, force: true });
// Make binary writable, so we can sign it during release
const stat = statSync(binDst);
const newMode = stat.mode | 0o200;
chmodSync(binDst, newMode);
console.log('Downloaded protoc to', binDst);
})().catch((err) => console.log('Script failed:', err));

1175
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,20 @@
[workspace]
members = ["yaak_grpc", "yaak_templates", "yaak_plugin_runtime", "yaak_models", "yaak_sse"]
members = [
"yaak-grpc",
"yaak-license",
"yaak-models",
"yaak-plugins",
"yaak-sse",
"yaak-sync",
"yaak-templates",
]
[package]
name = "yaak-app"
version = "0.0.0"
edition = "2021"
authors = ["Gregory Schier"]
publish = false
# Produce a library for mobile support
[lib]
@@ -15,8 +24,11 @@ crate-type = ["staticlib", "cdylib", "lib"]
[profile.release]
strip = true # Automatically strip symbols from the binary.
[features]
cargo-clippy = []
[build-dependencies]
tauri-build = { version = "2.0.3", features = [] }
tauri-build = { version = "2.0.4", features = [] }
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2.7"
@@ -26,46 +38,54 @@ cocoa = "0.26.0"
openssl-sys = { version = "0.9", features = ["vendored"] } # For Ubuntu installation to work
[dependencies]
yaak_grpc = { path = "yaak_grpc" }
yaak_templates = { path = "yaak_templates" }
yaak_plugin_runtime = { workspace = true }
yaak_sse = { workspace = true }
yaak_models = { workspace = true }
base64 = "0.22.0"
chrono = { version = "0.4.31", features = ["serde"] }
datetime = "0.5.2"
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.13.0" }
hex_color = "3.0.0"
http = "1"
log = "0.4.21"
rand = "0.8.5"
regex = "1.10.2"
reqwest = { version = "0.12.4", features = ["multipart", "cookies", "gzip", "brotli", "deflate", "json", "native-tls-alpn"] }
reqwest = { workspace = true, features = ["multipart", "cookies", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider"] }
reqwest_cookie_store = "0.8.0"
rustls = { version = "0.23.21", default-features = false }
rustls-platform-verifier = "0.5.0"
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["raw_value"] }
tauri = { workspace = true, features = ["devtools", "protocol-asset"] }
tauri-plugin-clipboard-manager = "2.2.0"
tauri-plugin-dialog = "2.2.0"
tauri-plugin-fs = "2.2.0"
tauri-plugin-log = { version = "2.2.0", features = ["colored"] }
tauri-plugin-opener = "2.2.2"
tauri-plugin-os = "2.2.0"
tauri-plugin-shell = { workspace = true }
tauri-plugin-clipboard-manager = "2.0.1"
tauri-plugin-dialog = "2.0.3"
tauri-plugin-fs = "2.0.3"
tauri-plugin-log = { version = "2.0.1", features = ["colored"] }
tauri-plugin-os = "2.0.1"
tauri-plugin-updater = "2.0.2"
tauri-plugin-window-state = "2.0.1"
tauri-plugin-single-instance = "2.2.0"
tauri-plugin-updater = "2.3.0"
tauri-plugin-window-state = "2.2.0"
tokio = { version = "1.36.0", features = ["sync"] }
tokio-stream = "0.1.15"
uuid = "1.7.0"
ts-rs = { workspace = true }
mime_guess = "2.0.5"
urlencoding = "2.1.3"
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.13.0" }
uuid = "1.7.0"
yaak-grpc = { path = "yaak-grpc" }
yaak-license = { path = "yaak-license" }
yaak-models = { workspace = true }
yaak-plugins = { workspace = true }
yaak-sse = { workspace = true }
yaak-sync = { path = "yaak-sync" }
yaak-templates = { path = "yaak-templates" }
[workspace.dependencies]
yaak_models = { path = "yaak_models" }
yaak_sse = { path = "yaak_sse" }
yaak_plugin_runtime = { path = "yaak_plugin_runtime" }
yaak-models = { path = "yaak-models" }
yaak-sse = { path = "yaak-sse" }
yaak-plugins = { path = "yaak-plugins" }
serde = "1.0.215"
serde_json = "1.0.132"
tauri-plugin-shell = "2.0.2"
tauri = { version = "2.1.1", features = ["devtools", "protocol-asset"] }
tauri-plugin-shell = "2.2.0"
tauri = "2.2.0"
thiserror = "2.0.3"
ts-rs = "10.0.0"
reqwest = "0.12.12"

View File

@@ -0,0 +1,5 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AnalyticsAction = "cancel" | "click" | "commit" | "create" | "delete" | "delete_many" | "duplicate" | "export" | "hide" | "import" | "launch" | "launch_first" | "launch_update" | "send" | "show" | "toggle" | "update" | "upsert";
export type AnalyticsResource = "app" | "appearance" | "button" | "checkbox" | "cookie_jar" | "dialog" | "environment" | "folder" | "grpc_connection" | "grpc_event" | "grpc_request" | "http_request" | "http_response" | "link" | "key_value" | "plugin" | "select" | "setting" | "sidebar" | "tab" | "theme" | "workspace";

View File

@@ -16,6 +16,7 @@
"clipboard-manager:allow-read-text",
"dialog:allow-open",
"dialog:allow-save",
"fs:allow-read-dir",
"fs:allow-read-file",
"fs:allow-read-text-file",
{
@@ -29,7 +30,10 @@
}
]
},
"shell:allow-open",
"opener:allow-open-url",
"opener:allow-open-path",
"opener:allow-default-urls",
"opener:allow-reveal-item-in-dir",
"core:webview:allow-set-webview-zoom",
"core:window:allow-close",
"core:window:allow-internal-toggle-maximize",
@@ -44,6 +48,8 @@
"core:window:allow-toggle-maximize",
"core:window:allow-unmaximize",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text"
"clipboard-manager:allow-write-text",
"yaak-license:default",
"yaak-sync:default"
]
}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"shell:allow-open","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-toggle-maximize","core:window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text"]}}
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"opener:allow-open-url","opener:allow-open-path","opener:allow-default-urls","opener:allow-reveal-item-in-dir","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-toggle-maximize","core:window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","yaak-license:default","yaak-sync:default"]}}

View File

@@ -140,7 +140,7 @@
"identifier": {
"anyOf": [
{
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "fs:default"
},
@@ -984,6 +984,11 @@
"type": "string",
"const": "fs:allow-seek"
},
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-size"
},
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -1109,6 +1114,11 @@
"type": "string",
"const": "fs:deny-seek"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-size"
},
{
"description": "Denies the stat command without any pre-configured scope.",
"type": "string",
@@ -1581,7 +1591,7 @@
"description": "FS scope entry.",
"anyOf": [
{
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
},
{
@@ -1591,7 +1601,7 @@
],
"properties": {
"path": {
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
@@ -1605,7 +1615,7 @@
"description": "FS scope entry.",
"anyOf": [
{
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
},
{
@@ -1615,7 +1625,167 @@
],
"properties": {
"path": {
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer",
"type": "string",
"const": "opener:default"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls"
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path"
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url"
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir"
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path"
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url"
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir"
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
@@ -2602,6 +2772,11 @@
"type": "string",
"const": "core:webview:allow-reparent"
},
{
"description": "Enables the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:allow-set-webview-background-color"
},
{
"description": "Enables the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2682,6 +2857,11 @@
"type": "string",
"const": "core:webview:deny-reparent"
},
{
"description": "Denies the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:deny-set-webview-background-color"
},
{
"description": "Denies the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2897,6 +3077,21 @@
"type": "string",
"const": "core:window:allow-set-always-on-top"
},
{
"description": "Enables the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-background-color"
},
{
"description": "Enables the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-count"
},
{
"description": "Enables the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-label"
},
{
"description": "Enables the set_closable command without any pre-configured scope.",
"type": "string",
@@ -2982,6 +3177,11 @@
"type": "string",
"const": "core:window:allow-set-minimizable"
},
{
"description": "Enables the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-overlay-icon"
},
{
"description": "Enables the set_position command without any pre-configured scope.",
"type": "string",
@@ -3242,6 +3442,21 @@
"type": "string",
"const": "core:window:deny-set-always-on-top"
},
{
"description": "Denies the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-background-color"
},
{
"description": "Denies the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-count"
},
{
"description": "Denies the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-label"
},
{
"description": "Denies the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3327,6 +3542,11 @@
"type": "string",
"const": "core:window:deny-set-minimizable"
},
{
"description": "Denies the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-overlay-icon"
},
{
"description": "Denies the set_position command without any pre-configured scope.",
"type": "string",
@@ -3478,7 +3698,7 @@
"const": "dialog:deny-save"
},
{
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "fs:default"
},
@@ -4322,6 +4542,11 @@
"type": "string",
"const": "fs:allow-seek"
},
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-size"
},
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -4447,6 +4672,11 @@
"type": "string",
"const": "fs:deny-seek"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-size"
},
{
"description": "Denies the stat command without any pre-configured scope.",
"type": "string",
@@ -4922,6 +5152,46 @@
"type": "string",
"const": "log:deny-log"
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer",
"type": "string",
"const": "opener:default"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls"
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path"
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url"
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir"
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path"
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url"
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir"
},
{
"description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n",
"type": "string",
@@ -5141,6 +5411,76 @@
"description": "Denies the save_window_state command without any pre-configured scope.",
"type": "string",
"const": "window-state:deny-save-window-state"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "yaak-license:default"
},
{
"description": "Enables the activate command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:allow-activate"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:allow-check"
},
{
"description": "Denies the activate command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:deny-activate"
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:deny-check"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "yaak-sync:default"
},
{
"description": "Enables the apply command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-apply"
},
{
"description": "Enables the calculate command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-calculate"
},
{
"description": "Enables the calculate_fs command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-calculate-fs"
},
{
"description": "Enables the watch command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-watch"
},
{
"description": "Denies the apply command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-apply"
},
{
"description": "Denies the calculate command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-calculate"
},
{
"description": "Denies the calculate_fs command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-calculate-fs"
},
{
"description": "Denies the watch command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-watch"
}
]
},
@@ -5238,6 +5578,23 @@
}
]
},
"Application": {
"description": "Opener scope application.",
"anyOf": [
{
"description": "Open in default application.",
"type": "null"
},
{
"description": "If true, allow open with any application.",
"type": "boolean"
},
{
"description": "Allow specific application to open with.",
"type": "string"
}
]
},
"ShellScopeEntryAllowedArg": {
"description": "A command argument allowed to be executed by the webview API.",
"anyOf": [

View File

@@ -37,7 +37,7 @@
],
"definitions": {
"Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```",
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
"type": "object",
"required": [
"identifier",
@@ -84,7 +84,7 @@
}
},
"permissions": {
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
@@ -140,7 +140,7 @@
"identifier": {
"anyOf": [
{
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "fs:default"
},
@@ -984,6 +984,11 @@
"type": "string",
"const": "fs:allow-seek"
},
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-size"
},
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -1109,6 +1114,11 @@
"type": "string",
"const": "fs:deny-seek"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-size"
},
{
"description": "Denies the stat command without any pre-configured scope.",
"type": "string",
@@ -1581,7 +1591,7 @@
"description": "FS scope entry.",
"anyOf": [
{
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
},
{
@@ -1591,7 +1601,7 @@
],
"properties": {
"path": {
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
@@ -1605,7 +1615,7 @@
"description": "FS scope entry.",
"anyOf": [
{
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
},
{
@@ -1615,7 +1625,167 @@
],
"properties": {
"path": {
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer",
"type": "string",
"const": "opener:default"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls"
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path"
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url"
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir"
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path"
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url"
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir"
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
@@ -2602,6 +2772,11 @@
"type": "string",
"const": "core:webview:allow-reparent"
},
{
"description": "Enables the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:allow-set-webview-background-color"
},
{
"description": "Enables the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2682,6 +2857,11 @@
"type": "string",
"const": "core:webview:deny-reparent"
},
{
"description": "Denies the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:deny-set-webview-background-color"
},
{
"description": "Denies the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2897,6 +3077,21 @@
"type": "string",
"const": "core:window:allow-set-always-on-top"
},
{
"description": "Enables the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-background-color"
},
{
"description": "Enables the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-count"
},
{
"description": "Enables the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-label"
},
{
"description": "Enables the set_closable command without any pre-configured scope.",
"type": "string",
@@ -2982,6 +3177,11 @@
"type": "string",
"const": "core:window:allow-set-minimizable"
},
{
"description": "Enables the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-overlay-icon"
},
{
"description": "Enables the set_position command without any pre-configured scope.",
"type": "string",
@@ -3242,6 +3442,21 @@
"type": "string",
"const": "core:window:deny-set-always-on-top"
},
{
"description": "Denies the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-background-color"
},
{
"description": "Denies the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-count"
},
{
"description": "Denies the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-label"
},
{
"description": "Denies the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3327,6 +3542,11 @@
"type": "string",
"const": "core:window:deny-set-minimizable"
},
{
"description": "Denies the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-overlay-icon"
},
{
"description": "Denies the set_position command without any pre-configured scope.",
"type": "string",
@@ -3478,7 +3698,7 @@
"const": "dialog:deny-save"
},
{
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "fs:default"
},
@@ -4322,6 +4542,11 @@
"type": "string",
"const": "fs:allow-seek"
},
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-size"
},
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -4447,6 +4672,11 @@
"type": "string",
"const": "fs:deny-seek"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-size"
},
{
"description": "Denies the stat command without any pre-configured scope.",
"type": "string",
@@ -4922,6 +5152,46 @@
"type": "string",
"const": "log:deny-log"
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer",
"type": "string",
"const": "opener:default"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls"
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path"
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url"
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir"
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path"
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url"
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir"
},
{
"description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n",
"type": "string",
@@ -5141,6 +5411,76 @@
"description": "Denies the save_window_state command without any pre-configured scope.",
"type": "string",
"const": "window-state:deny-save-window-state"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "yaak-license:default"
},
{
"description": "Enables the activate command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:allow-activate"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:allow-check"
},
{
"description": "Denies the activate command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:deny-activate"
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:deny-check"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "yaak-sync:default"
},
{
"description": "Enables the apply command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-apply"
},
{
"description": "Enables the calculate command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-calculate"
},
{
"description": "Enables the calculate_fs command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-calculate-fs"
},
{
"description": "Enables the watch command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-watch"
},
{
"description": "Denies the apply command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-apply"
},
{
"description": "Denies the calculate command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-calculate"
},
{
"description": "Denies the calculate_fs command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-calculate-fs"
},
{
"description": "Denies the watch command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-watch"
}
]
},
@@ -5238,6 +5578,23 @@
}
]
},
"Application": {
"description": "Opener scope application.",
"anyOf": [
{
"description": "Open in default application.",
"type": "null"
},
{
"description": "If true, allow open with any application.",
"type": "boolean"
},
{
"description": "Allow specific application to open with.",
"type": "string"
}
]
},
"ShellScopeEntryAllowedArg": {
"description": "A command argument allowed to be executed by the webview API.",
"anyOf": [

View File

@@ -140,7 +140,7 @@
"identifier": {
"anyOf": [
{
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "fs:default"
},
@@ -984,6 +984,11 @@
"type": "string",
"const": "fs:allow-seek"
},
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-size"
},
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -1109,6 +1114,11 @@
"type": "string",
"const": "fs:deny-seek"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-size"
},
{
"description": "Denies the stat command without any pre-configured scope.",
"type": "string",
@@ -1581,7 +1591,7 @@
"description": "FS scope entry.",
"anyOf": [
{
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
},
{
@@ -1591,7 +1601,7 @@
],
"properties": {
"path": {
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
@@ -1605,7 +1615,7 @@
"description": "FS scope entry.",
"anyOf": [
{
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
},
{
@@ -1615,7 +1625,167 @@
],
"properties": {
"path": {
"description": "FS scope path.",
"description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer",
"type": "string",
"const": "opener:default"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls"
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path"
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url"
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir"
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path"
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url"
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir"
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
@@ -2602,6 +2772,11 @@
"type": "string",
"const": "core:webview:allow-reparent"
},
{
"description": "Enables the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:allow-set-webview-background-color"
},
{
"description": "Enables the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2682,6 +2857,11 @@
"type": "string",
"const": "core:webview:deny-reparent"
},
{
"description": "Denies the set_webview_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:webview:deny-set-webview-background-color"
},
{
"description": "Denies the set_webview_focus command without any pre-configured scope.",
"type": "string",
@@ -2897,6 +3077,21 @@
"type": "string",
"const": "core:window:allow-set-always-on-top"
},
{
"description": "Enables the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-background-color"
},
{
"description": "Enables the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-count"
},
{
"description": "Enables the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-badge-label"
},
{
"description": "Enables the set_closable command without any pre-configured scope.",
"type": "string",
@@ -2982,6 +3177,11 @@
"type": "string",
"const": "core:window:allow-set-minimizable"
},
{
"description": "Enables the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-set-overlay-icon"
},
{
"description": "Enables the set_position command without any pre-configured scope.",
"type": "string",
@@ -3242,6 +3442,21 @@
"type": "string",
"const": "core:window:deny-set-always-on-top"
},
{
"description": "Denies the set_background_color command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-background-color"
},
{
"description": "Denies the set_badge_count command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-count"
},
{
"description": "Denies the set_badge_label command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-badge-label"
},
{
"description": "Denies the set_closable command without any pre-configured scope.",
"type": "string",
@@ -3327,6 +3542,11 @@
"type": "string",
"const": "core:window:deny-set-minimizable"
},
{
"description": "Denies the set_overlay_icon command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-set-overlay-icon"
},
{
"description": "Denies the set_position command without any pre-configured scope.",
"type": "string",
@@ -3478,7 +3698,7 @@
"const": "dialog:deny-save"
},
{
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n",
"description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n",
"type": "string",
"const": "fs:default"
},
@@ -4322,6 +4542,11 @@
"type": "string",
"const": "fs:allow-seek"
},
{
"description": "Enables the size command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-size"
},
{
"description": "Enables the stat command without any pre-configured scope.",
"type": "string",
@@ -4447,6 +4672,11 @@
"type": "string",
"const": "fs:deny-seek"
},
{
"description": "Denies the size command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-size"
},
{
"description": "Denies the stat command without any pre-configured scope.",
"type": "string",
@@ -4922,6 +5152,46 @@
"type": "string",
"const": "log:deny-log"
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer",
"type": "string",
"const": "opener:default"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls"
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path"
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url"
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir"
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path"
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url"
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir"
},
{
"description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n",
"type": "string",
@@ -5141,6 +5411,76 @@
"description": "Denies the save_window_state command without any pre-configured scope.",
"type": "string",
"const": "window-state:deny-save-window-state"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "yaak-license:default"
},
{
"description": "Enables the activate command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:allow-activate"
},
{
"description": "Enables the check command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:allow-check"
},
{
"description": "Denies the activate command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:deny-activate"
},
{
"description": "Denies the check command without any pre-configured scope.",
"type": "string",
"const": "yaak-license:deny-check"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "yaak-sync:default"
},
{
"description": "Enables the apply command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-apply"
},
{
"description": "Enables the calculate command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-calculate"
},
{
"description": "Enables the calculate_fs command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-calculate-fs"
},
{
"description": "Enables the watch command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:allow-watch"
},
{
"description": "Denies the apply command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-apply"
},
{
"description": "Denies the calculate command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-calculate"
},
{
"description": "Denies the calculate_fs command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-calculate-fs"
},
{
"description": "Denies the watch command without any pre-configured scope.",
"type": "string",
"const": "yaak-sync:deny-watch"
}
]
},
@@ -5238,6 +5578,23 @@
}
]
},
"Application": {
"description": "Opener scope application.",
"anyOf": [
{
"description": "Open in default application.",
"type": "null"
},
{
"description": "If true, allow open with any application.",
"type": "boolean"
},
{
"description": "Allow specific application to open with.",
"type": "string"
}
]
},
"ShellScopeEntryAllowedArg": {
"description": "A command argument allowed to be executed by the webview API.",
"anyOf": [

View File

@@ -0,0 +1,8 @@
ALTER TABLE http_requests
ADD COLUMN description TEXT DEFAULT '' NOT NULL;
ALTER TABLE grpc_requests
ADD COLUMN description TEXT DEFAULT '' NOT NULL;
ALTER TABLE folders
ADD COLUMN description TEXT DEFAULT '' NOT NULL;

View File

@@ -0,0 +1,45 @@
-- Add the new field
ALTER TABLE environments
ADD COLUMN environment_id TEXT REFERENCES environments (id) ON DELETE CASCADE;
-- Create temporary column so we know which rows are meant to be base environments. We'll use this to update
-- child environments to point to them.
ALTER TABLE environments
ADD COLUMN migrated_base_env BOOLEAN DEFAULT FALSE NOT NULL;
-- Create a base environment for each workspace
INSERT INTO environments (id, workspace_id, name, variables, migrated_base_env)
SELECT (
-- This is the best way to generate a random string in SQLite, apparently
'ev_' || SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1) ||
SUBSTR('abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23457789', (ABS(RANDOM()) % 57) + 1, 1)
),
workspaces.id,
'Global Variables',
variables,
TRUE
FROM workspaces;
-- Update all non-base environments to point to newly created base environments
UPDATE environments
SET environment_id = ( SELECT base_env.id
FROM environments AS base_env
WHERE base_env.workspace_id = environments.workspace_id
AND base_env.migrated_base_env IS TRUE )
WHERE migrated_base_env IS FALSE;
-- Drop temporary column
ALTER TABLE environments
DROP COLUMN migrated_base_env;
-- Drop the old variables column
-- IMPORTANT: Skip to give the user the option to roll back to a previous app version. We can drop it once the migration working in the real world
-- ALTER TABLE workspaces DROP COLUMN variables;

View File

@@ -0,0 +1,21 @@
ALTER TABLE workspaces
ADD COLUMN setting_sync_dir TEXT;
CREATE TABLE sync_states
(
id TEXT NOT NULL
PRIMARY KEY,
model TEXT DEFAULT 'sync_state' NOT NULL,
workspace_id TEXT NOT NULL
REFERENCES workspaces
ON DELETE CASCADE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
flushed_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
checksum TEXT NOT NULL,
model_id TEXT NOT NULL,
sync_dir TEXT NOT NULL,
rel_path TEXT NOT NULL,
UNIQUE (workspace_id, model_id)
);

View File

@@ -0,0 +1,2 @@
ALTER TABLE settings
ADD COLUMN editor_keymap TEXT DEFAULT 'codemirror' NOT NULL;

View File

@@ -0,0 +1,11 @@
CREATE TABLE workspace_metas
(
id TEXT NOT NULL
PRIMARY KEY,
model TEXT DEFAULT 'workspace_meta' NOT NULL,
workspace_id TEXT NOT NULL
REFERENCES workspaces ON DELETE CASCADE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
setting_sync_dir TEXT
);

View File

@@ -4,8 +4,11 @@ use log::{debug, info};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tauri::{Manager, Runtime, WebviewWindow};
use yaak_models::queries::{generate_id, get_key_value_int, get_key_value_string, get_or_create_settings, set_key_value_int, set_key_value_string};
use ts_rs::TS;
use yaak_models::queries::{
generate_id, get_key_value_int, get_key_value_string, get_or_create_settings,
set_key_value_int, set_key_value_string, UpdateSource,
};
use crate::is_dev;
@@ -13,11 +16,14 @@ const NAMESPACE: &str = "analytics";
const NUM_LAUNCHES_KEY: &str = "num_launches";
// serializable
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export, export_to = "analytics.ts")]
pub enum AnalyticsResource {
App,
Appearance,
Button,
Checkbox,
CookieJar,
Dialog,
Environment,
@@ -27,10 +33,13 @@ pub enum AnalyticsResource {
GrpcRequest,
HttpRequest,
HttpResponse,
Link,
KeyValue,
Plugin,
Select,
Setting,
Sidebar,
Tab,
Theme,
Workspace,
}
@@ -43,18 +52,16 @@ impl AnalyticsResource {
impl Display for AnalyticsResource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
serde_json::to_string(self).unwrap().replace("\"", "")
)
write!(f, "{}", serde_json::to_string(self).unwrap().replace("\"", ""))
}
}
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export, export_to = "analytics.ts")]
pub enum AnalyticsAction {
Cancel,
Click,
Commit,
Create,
Delete,
@@ -81,11 +88,7 @@ impl AnalyticsAction {
impl Display for AnalyticsAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
serde_json::to_string(self).unwrap().replace("\"", "")
)
write!(f, "{}", serde_json::to_string(self).unwrap().replace("\"", ""))
}
}
@@ -107,13 +110,7 @@ pub async fn track_launch_event<R: Runtime>(w: &WebviewWindow<R>) -> LaunchEvent
info.current_version = w.package_info().version.to_string();
if info.previous_version.is_empty() {
track_event(
w,
AnalyticsResource::App,
AnalyticsAction::LaunchFirst,
None,
)
.await;
track_event(w, AnalyticsResource::App, AnalyticsAction::LaunchFirst, None).await;
} else {
info.launched_after_update = info.current_version != info.previous_version;
if info.launched_after_update {
@@ -143,9 +140,11 @@ pub async fn track_launch_event<R: Runtime>(w: &WebviewWindow<R>) -> LaunchEvent
NAMESPACE,
last_tracked_version_key,
info.current_version.as_str(),
&UpdateSource::Background,
)
.await;
set_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches).await;
set_key_value_int(w, NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, &UpdateSource::Background)
.await;
info
}
@@ -156,7 +155,6 @@ pub async fn track_event<R: Runtime>(
action: AnalyticsAction,
attributes: Option<Value>,
) {
let id = get_id(w).await;
let event = format!("{}.{}", resource, action);
let attributes_json = attributes.unwrap_or("{}".to_string().into()).to_string();
@@ -180,16 +178,13 @@ pub async fn track_event<R: Runtime>(
("tz", tz),
("xy", get_window_size(w)),
];
let req = reqwest::Client::builder()
.build()
.unwrap()
.get(format!("{base_url}/t/e"))
.query(&params);
let req =
reqwest::Client::builder().build().unwrap().get(format!("{base_url}/t/e")).query(&params);
let settings = get_or_create_settings(w).await;
if !settings.telemetry {
info!("Track event (disabled): {}", event);
return
return;
}
// Disable analytics actual sending in dev
@@ -226,18 +221,15 @@ fn get_window_size<R: Runtime>(w: &WebviewWindow<R>) -> String {
let width: f64 = size.width as f64 / scale_factor;
let height: f64 = size.height as f64 / scale_factor;
format!(
"{}x{}",
(width / 100.0).round() * 100.0,
(height / 100.0).round() * 100.0
)
format!("{}x{}", (width / 100.0).round() * 100.0, (height / 100.0).round() * 100.0)
}
async fn get_id<R: Runtime>(w: &WebviewWindow<R>) -> String {
let id = get_key_value_string(w, "analytics", "id", "").await;
if id.is_empty() {
let new_id = generate_id();
set_key_value_string(w, "analytics", "id", new_id.as_str()).await;
set_key_value_string(w, "analytics", "id", new_id.as_str(), &UpdateSource::Background)
.await;
new_id
} else {
id

View File

@@ -1,77 +0,0 @@
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use tauri::{Manager, WebviewWindow};
use yaak_models::models::{Environment, Folder, GrpcRequest, HttpRequest, Workspace};
#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct WorkspaceExport {
pub yaak_version: String,
pub yaak_schema: i64,
pub timestamp: NaiveDateTime,
pub resources: WorkspaceExportResources,
}
#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct WorkspaceExportResources {
pub workspaces: Vec<Workspace>,
pub environments: Vec<Environment>,
pub folders: Vec<Folder>,
pub http_requests: Vec<HttpRequest>,
pub grpc_requests: Vec<GrpcRequest>,
}
#[derive(Default, Debug, Deserialize, Serialize)]
pub struct ImportResult {
pub resources: WorkspaceExportResources,
}
pub async fn get_workspace_export_resources(
window: &WebviewWindow,
workspace_ids: Vec<&str>,
) -> WorkspaceExport {
let app_handle = window.app_handle();
let mut data = WorkspaceExport {
yaak_version: app_handle.package_info().version.clone().to_string(),
yaak_schema: 2,
timestamp: chrono::Utc::now().naive_utc(),
resources: WorkspaceExportResources {
workspaces: Vec::new(),
environments: Vec::new(),
folders: Vec::new(),
http_requests: Vec::new(),
grpc_requests: Vec::new(),
},
};
for workspace_id in workspace_ids {
data.resources.workspaces.push(
yaak_models::queries::get_workspace(window, workspace_id)
.await
.expect("Failed to get workspace"),
);
data.resources.environments.append(
&mut yaak_models::queries::list_environments(window, workspace_id)
.await
.expect("Failed to get environments"),
);
data.resources.folders.append(
&mut yaak_models::queries::list_folders(window, workspace_id)
.await
.expect("Failed to get folders"),
);
data.resources.http_requests.append(
&mut yaak_models::queries::list_http_requests(window, workspace_id)
.await
.expect("Failed to get http requests"),
);
data.resources.grpc_requests.append(
&mut yaak_models::queries::list_grpc_requests(window, workspace_id)
.await
.expect("Failed to get grpc requests"),
);
}
return data;
}

View File

@@ -1,9 +1,3 @@
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use crate::render::render_http_request;
use crate::response_err;
use crate::template_callback::PluginTemplateCallback;
@@ -16,7 +10,14 @@ use mime_guess::Mime;
use reqwest::redirect::Policy;
use reqwest::{multipart, Proxy, Url};
use reqwest::{Method, Response};
use rustls::ClientConfig;
use rustls_platform_verifier::ConfigVerifierExt;
use serde_json::Value;
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tauri::{Manager, Runtime, WebviewWindow};
use tokio::fs;
use tokio::fs::{create_dir_all, File};
@@ -28,10 +29,10 @@ use yaak_models::models::{
HttpResponseState, ProxySetting, ProxySettingAuth,
};
use yaak_models::queries::{
get_http_response, get_or_create_settings, get_workspace, update_response_if_id,
upsert_cookie_jar,
get_base_environment, get_http_response, get_or_create_settings, get_workspace,
update_response_if_id, upsert_cookie_jar, UpdateSource,
};
use yaak_plugin_runtime::events::{RenderPurpose, WindowContext};
use yaak_plugins::events::{RenderPurpose, WindowContext};
pub async fn send_http_request<R: Runtime>(
window: &WebviewWindow<R>,
@@ -43,6 +44,9 @@ pub async fn send_http_request<R: Runtime>(
) -> Result<HttpResponse, String> {
let workspace =
get_workspace(window, &request.workspace_id).await.expect("Failed to get Workspace");
let base_environment = get_base_environment(window, &request.workspace_id)
.await
.expect("Failed to get base environment");
let settings = get_or_create_settings(window).await;
let cb = PluginTemplateCallback::new(
window.app_handle(),
@@ -54,7 +58,7 @@ pub async fn send_http_request<R: Runtime>(
let response = Arc::new(Mutex::new(og_response.clone()));
let rendered_request =
render_http_request(&request, &workspace, environment.as_ref(), &cb).await;
render_http_request(&request, &base_environment, environment.as_ref(), &cb).await;
let mut url_string = rendered_request.url;
@@ -74,9 +78,21 @@ pub async fn send_http_request<R: Runtime>(
.brotli(true)
.deflate(true)
.referer(false)
.danger_accept_invalid_certs(!workspace.setting_validate_certificates)
.tls_info(true);
if workspace.setting_validate_certificates {
// Use platform-native verifier to validate certificates
client_builder =
client_builder.use_preconfigured_tls(ClientConfig::with_platform_verifier())
} else {
// Use rustls to skip validation because rustls_platform_verifier does not have this
// ability
client_builder = client_builder
.use_rustls_tls()
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true);
}
match settings.proxy {
Some(ProxySetting::Disabled) => client_builder = client_builder.no_proxy(),
Some(ProxySetting::Enabled { http, https, auth }) => {
@@ -243,7 +259,7 @@ pub async fn send_http_request<R: Runtime>(
let request_body = rendered_request.body;
if let Some(body_type) = &rendered_request.body_type {
if request_body.contains_key("query") && request_body.contains_key("variables") {
if body_type == "graphql" {
let query = get_str_h(&request_body, "query");
let variables = get_str_h(&request_body, "variables");
let body = if variables.trim().is_empty() {
@@ -255,9 +271,6 @@ pub async fn send_http_request<R: Runtime>(
)
};
request_builder = request_builder.body(body.to_owned());
} else if request_body.contains_key("text") {
let body = get_str_h(&request_body, "text");
request_builder = request_builder.body(body.to_owned());
} else if body_type == "application/x-www-form-urlencoded"
&& request_body.contains_key("form")
{
@@ -359,6 +372,9 @@ pub async fn send_http_request<R: Runtime>(
}
headers.remove("Content-Type"); // reqwest will add this automatically
request_builder = request_builder.multipart(multipart_form);
} else if request_body.contains_key("text") {
let body = get_str_h(&request_body, "text");
request_builder = request_builder.body(body.to_owned());
} else {
warn!("Unsupported body type: {}", body_type);
}
@@ -436,7 +452,7 @@ pub async fn send_http_request<R: Runtime>(
};
r.state = HttpResponseState::Connected;
update_response_if_id(&window, &r)
update_response_if_id(&window, &r, &UpdateSource::Window)
.await
.expect("Failed to update response after connected");
}
@@ -465,7 +481,7 @@ pub async fn send_http_request<R: Runtime>(
f.flush().await.expect("Failed to flush file");
written_bytes += bytes.len();
r.content_length = Some(written_bytes as i32);
update_response_if_id(&window, &r)
update_response_if_id(&window, &r, &UpdateSource::Window)
.await
.expect("Failed to update response");
}
@@ -487,7 +503,7 @@ pub async fn send_http_request<R: Runtime>(
None => Some(written_bytes as i32),
};
r.state = HttpResponseState::Closed;
update_response_if_id(&window, &r)
update_response_if_id(&window, &r, &UpdateSource::Window)
.await
.expect("Failed to update response");
};
@@ -513,7 +529,9 @@ pub async fn send_http_request<R: Runtime>(
})
.collect::<Vec<_>>();
cookie_jar.cookies = json_cookies;
if let Err(e) = upsert_cookie_jar(&window, &cookie_jar).await {
if let Err(e) =
upsert_cookie_jar(&window, &cookie_jar, &UpdateSource::Window).await
{
error!("Failed to update cookie jar: {}", e);
};
}
@@ -535,7 +553,7 @@ pub async fn send_http_request<R: Runtime>(
match get_http_response(window, response_id.as_str()).await {
Ok(mut r) => {
r.state = HttpResponseState::Closed;
update_response_if_id(&window, &r).await.expect("Failed to update response")
update_response_if_id(&window, &r, &UpdateSource::Window).await.expect("Failed to update response")
},
_ => {
response_err(&*response.lock().await, "Ephemeral request was cancelled".to_string(), &window).await

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ use log::debug;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use tauri::{Emitter, Manager, Runtime, WebviewWindow};
use yaak_models::queries::{get_key_value_raw, set_key_value_raw};
use yaak_models::queries::{get_key_value_raw, set_key_value_raw, UpdateSource};
// Check for updates every hour
const MAX_UPDATE_CHECK_SECONDS: u64 = 60 * 60;
@@ -47,7 +47,7 @@ impl YaakNotifier {
seen.push(id.to_string());
debug!("Marked notification as seen {}", id);
let seen_json = serde_json::to_string(&seen).map_err(|e| e.to_string())?;
set_key_value_raw(w, KV_NAMESPACE, KV_KEY, seen_json.as_str()).await;
set_key_value_raw(w, KV_NAMESPACE, KV_KEY, seen_json.as_str(), &UpdateSource::Window).await;
Ok(())
}
@@ -74,10 +74,7 @@ impl YaakNotifier {
return Ok(());
}
let notification = resp
.json::<YaakNotification>()
.await
.map_err(|e| e.to_string())?;
let notification = resp.json::<YaakNotification>().await.map_err(|e| e.to_string())?;
let age = notification.timestamp.signed_duration_since(Utc::now());
let seen = get_kv(window).await?;

View File

@@ -3,37 +3,37 @@ use serde_json::{json, Map, Value};
use std::collections::{BTreeMap, HashMap};
use yaak_models::models::{
Environment, EnvironmentVariable, GrpcMetadataEntry, GrpcRequest, HttpRequest,
HttpRequestHeader, HttpUrlParameter, Workspace,
HttpRequestHeader, HttpUrlParameter,
};
use yaak_templates::{parse_and_render, TemplateCallback};
pub async fn render_template<T: TemplateCallback>(
template: &str,
w: &Workspace,
e: Option<&Environment>,
base_environment: &Environment,
environment: Option<&Environment>,
cb: &T,
) -> String {
let vars = &make_vars_hashmap(w, e);
let vars = &make_vars_hashmap(base_environment, environment);
render(template, vars, cb).await
}
pub async fn render_json_value<T: TemplateCallback>(
value: Value,
w: &Workspace,
e: Option<&Environment>,
base_environment: &Environment,
environment: Option<&Environment>,
cb: &T,
) -> Value {
let vars = &make_vars_hashmap(w, e);
let vars = &make_vars_hashmap(base_environment, environment);
render_json_value_raw(value, vars, cb).await
}
pub async fn render_grpc_request<T: TemplateCallback>(
r: &GrpcRequest,
w: &Workspace,
e: Option<&Environment>,
base_environment: &Environment,
environment: Option<&Environment>,
cb: &T,
) -> GrpcRequest {
let vars = &make_vars_hashmap(w, e);
let vars = &make_vars_hashmap(base_environment, environment);
let mut metadata = Vec::new();
for p in r.metadata.clone() {
@@ -41,6 +41,7 @@ pub async fn render_grpc_request<T: TemplateCallback>(
enabled: p.enabled,
name: render(p.name.as_str(), vars, cb).await,
value: render(p.value.as_str(), vars, cb).await,
id: p.id,
})
}
@@ -61,11 +62,11 @@ pub async fn render_grpc_request<T: TemplateCallback>(
pub async fn render_http_request(
r: &HttpRequest,
w: &Workspace,
e: Option<&Environment>,
base_environment: &Environment,
environment: Option<&Environment>,
cb: &PluginTemplateCallback,
) -> HttpRequest {
let vars = &make_vars_hashmap(w, e);
let vars = &make_vars_hashmap(base_environment, environment);
let mut url_parameters = Vec::new();
for p in r.url_parameters.clone() {
@@ -73,6 +74,7 @@ pub async fn render_http_request(
enabled: p.enabled,
name: render(p.name.as_str(), vars, cb).await,
value: render(p.value.as_str(), vars, cb).await,
id: p.id,
})
}
@@ -82,6 +84,7 @@ pub async fn render_http_request(
enabled: p.enabled,
name: render(p.name.as_str(), vars, cb).await,
value: render(p.value.as_str(), vars, cb).await,
id: p.id,
})
}
@@ -110,11 +113,11 @@ pub async fn render_http_request(
}
pub fn make_vars_hashmap(
workspace: &Workspace,
base_environment: &Environment,
environment: Option<&Environment>,
) -> HashMap<String, String> {
let mut variables = HashMap::new();
variables = add_variable_to_map(variables, &workspace.variables);
variables = add_variable_to_map(variables, &base_environment.variables);
if let Some(e) = environment {
variables = add_variable_to_map(variables, &e.variables);
@@ -309,6 +312,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo/bar"),
@@ -322,6 +326,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo"),
@@ -335,6 +340,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo?:foo"),
@@ -348,6 +354,7 @@ mod placeholder_tests {
enabled: true,
name: "".to_string(),
value: "".to_string(),
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:missing"),
@@ -361,6 +368,7 @@ mod placeholder_tests {
enabled: false,
name: ":foo".to_string(),
value: "xxx".to_string(),
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo"),
@@ -374,6 +382,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "xxx".into(),
enabled: true,
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foooo"),
@@ -387,6 +396,7 @@ mod placeholder_tests {
name: ":foo".into(),
value: "Hello World".into(),
enabled: true,
id: "p1".into(),
};
assert_eq!(
replace_path_placeholder(&p, "https://example.com/:foo"),
@@ -403,17 +413,17 @@ mod placeholder_tests {
name: "b".to_string(),
value: "bbb".to_string(),
enabled: true,
id: "p1".into(),
},
HttpUrlParameter {
name: ":a".to_string(),
value: "aaa".to_string(),
enabled: true,
id: "p2".into(),
},
],
..Default::default()
});
println!("HELLO?: {result:?}");
assert_eq!(result.url, "example.com/aaa/bar");
assert_eq!(result.url_parameters.len(), 1);
assert_eq!(result.url_parameters[0].name, "b");

View File

@@ -1,3 +1,4 @@
use crate::MAIN_WINDOW_PREFIX;
use hex_color::HexColor;
use log::warn;
use objc::{msg_send, sel, sel_impl};
@@ -6,7 +7,6 @@ use tauri::{
plugin::{Builder, TauriPlugin},
Emitter, Listener, Manager, Runtime, Window, WindowEvent,
};
use crate::MAIN_WINDOW_PREFIX;
const WINDOW_CONTROL_PAD_X: f64 = 13.0;
const WINDOW_CONTROL_PAD_Y: f64 = 18.0;
@@ -202,10 +202,9 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: &Window<R>) {
}
unsafe {
let ns_win = window
.ns_window()
.expect("NS Window should exist to mount traffic light delegate.")
as id;
let ns_win =
window.ns_window().expect("NS Window should exist to mount traffic light delegate.")
as id;
let current_delegate: id = ns_win.delegate();
@@ -322,10 +321,7 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: &Window<R>) {
) {
unsafe {
with_window_state(&*this, |state: &mut WindowState<R>| {
state
.window
.emit("did-enter-fullscreen", ())
.expect("Failed to emit event");
state.window.emit("did-enter-fullscreen", ()).expect("Failed to emit event");
});
let super_del: id = *this.get_ivar("super_delegate");
@@ -339,10 +335,7 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: &Window<R>) {
) {
unsafe {
with_window_state(&*this, |state: &mut WindowState<R>| {
state
.window
.emit("will-enter-fullscreen", ())
.expect("Failed to emit event");
state.window.emit("will-enter-fullscreen", ()).expect("Failed to emit event");
});
let super_del: id = *this.get_ivar("super_delegate");
@@ -356,10 +349,7 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: &Window<R>) {
) {
unsafe {
with_window_state(&*this, |state: &mut WindowState<R>| {
state
.window
.emit("did-exit-fullscreen", ())
.expect("Failed to emit event");
state.window.emit("did-exit-fullscreen", ()).expect("Failed to emit event");
let id = state.window.ns_window().expect("Failed to emit event") as id;
position_traffic_lights(
@@ -381,10 +371,7 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: &Window<R>) {
) {
unsafe {
with_window_state(&*this, |state: &mut WindowState<R>| {
state
.window
.emit("will-exit-fullscreen", ())
.expect("Failed to emit event");
state.window.emit("will-exit-fullscreen", ()).expect("Failed to emit event");
});
let super_del: id = *this.get_ivar("super_delegate");
@@ -432,11 +419,8 @@ pub fn setup_traffic_light_positioner<R: Runtime>(window: &Window<R>) {
window: window.clone(),
};
let app_box = Box::into_raw(Box::new(app_state)) as *mut c_void;
let random_str: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(20)
.map(char::from)
.collect();
let random_str: String =
rand::thread_rng().sample_iter(&Alphanumeric).take(20).map(char::from).collect();
// We need to ensure we have a unique delegate name, otherwise we will panic while trying to create a duplicate
// delegate with the same name.

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap;
use tauri::{AppHandle, Manager, Runtime};
use yaak_plugin_runtime::events::{RenderPurpose, TemplateFunctionArg, WindowContext};
use yaak_plugin_runtime::manager::PluginManager;
use yaak_plugins::events::{RenderPurpose, TemplateFunctionArg, WindowContext};
use yaak_plugins::manager::PluginManager;
use yaak_templates::TemplateCallback;
#[derive(Clone)]
@@ -31,11 +31,7 @@ impl TemplateCallback for PluginTemplateCallback {
let window_context = self.window_context.to_owned();
// The beta named the function `Response` but was changed in stable.
// Keep this here for a while because there's no easy way to migrate
let fn_name = if fn_name == "Response" {
"response"
} else {
fn_name
};
let fn_name = if fn_name == "Response" { "response" } else { fn_name };
let function = self
.plugin_manager

View File

@@ -6,7 +6,7 @@ use tauri::{AppHandle, Manager};
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons};
use tauri_plugin_updater::UpdaterExt;
use tokio::task::block_in_place;
use yaak_plugin_runtime::manager::PluginManager;
use yaak_plugins::manager::PluginManager;
use crate::is_dev;

View File

@@ -45,8 +45,8 @@
"active": true,
"category": "DeveloperTool",
"externalBin": [
"vendored/protoc/yaakprotoc",
"vendored/node/yaaknode"
"vendored/node/yaaknode",
"vendored/protoc/yaakprotoc"
],
"icon": [
"icons/release/32x32.png",
@@ -79,8 +79,7 @@
"frameworks": []
},
"windows": {
"digestAlgorithm": "sha256",
"timestampUrl": ""
"signCommand": "trusted-signing-cli -e https://eus.codesigning.azure.net/ -a Yaak -c yaakapp %1"
}
}
}

View File

@@ -265,30 +265,31 @@ __export(src_exports, {
module.exports = __toCommonJS(src_exports);
var import_shell_quote = __toESM(require_shell_quote());
var DATA_FLAGS = ["d", "data", "data-raw", "data-urlencode", "data-binary", "data-ascii"];
var SUPPORTED_ARGS = [
["url"],
// Specify the URL explicitly
["user", "u"],
// Authentication
["digest"],
// Apply auth as digest
["header", "H"],
var SUPPORTED_FLAGS = [
["cookie", "b"],
["get", "G"],
// Put the post data in the URL
["d", "data"],
// Add url encoded data
["data-ascii"],
["data-binary"],
["data-raw"],
["data-urlencode"],
["data-binary"],
["data-ascii"],
["digest"],
// Apply auth as digest
["form", "F"],
// Add multipart data
["get", "G"],
// Put the post data in the URL
["header", "H"],
["request", "X"],
// Request method
["url"],
// Specify the URL explicitly
["url-query"],
["user", "u"],
// Authentication
DATA_FLAGS
].flatMap((v) => v);
var BOOL_FLAGS = ["G", "get", "digest"];
var BOOLEAN_FLAGS = ["G", "get", "digest"];
function pluginHookImport(_ctx, rawData) {
if (!rawData.match(/^\s*curl /)) {
return null;
@@ -345,7 +346,7 @@ function pluginHookImport(_ctx, rawData) {
};
}
function importCommand(parseEntries, workspaceId) {
const pairsByName = {};
const flagsByName = {};
const singletons = [];
for (let i = 1; i < parseEntries.length; i++) {
let parseEntry = parseEntries[i];
@@ -355,12 +356,12 @@ function importCommand(parseEntries, workspaceId) {
if (typeof parseEntry === "string" && parseEntry.match(/^-{1,2}[\w-]+/)) {
const isSingleDash = parseEntry[0] === "-" && parseEntry[1] !== "-";
let name = parseEntry.replace(/^-{1,2}/, "");
if (!SUPPORTED_ARGS.includes(name)) {
if (!SUPPORTED_FLAGS.includes(name)) {
continue;
}
let value;
const nextEntry = parseEntries[i + 1];
const hasValue = !BOOL_FLAGS.includes(name);
const hasValue = !BOOLEAN_FLAGS.includes(name);
if (isSingleDash && name.length > 1) {
value = name.slice(1);
name = name.slice(0, 1);
@@ -370,31 +371,42 @@ function importCommand(parseEntries, workspaceId) {
} else {
value = true;
}
pairsByName[name] = pairsByName[name] || [];
pairsByName[name].push(value);
flagsByName[name] = flagsByName[name] || [];
flagsByName[name].push(value);
} else if (parseEntry) {
singletons.push(parseEntry);
}
}
let urlParameters;
let url;
const urlArg = getPairValue(pairsByName, singletons[0] || "", ["url"]);
const urlArg = getPairValue(flagsByName, singletons[0] || "", ["url"]);
const [baseUrl, search] = splitOnce(urlArg, "?");
urlParameters = search?.split("&").map((p) => {
const v = splitOnce(p, "=");
return { name: decodeURIComponent(v[0] ?? ""), value: decodeURIComponent(v[1] ?? ""), enabled: true };
}) ?? [];
url = baseUrl ?? urlArg;
const [username, password] = getPairValue(pairsByName, "", ["u", "user"]).split(/:(.*)$/);
const isDigest = getPairValue(pairsByName, false, ["digest"]);
for (const p of flagsByName["url-query"] ?? []) {
if (typeof p !== "string") {
continue;
}
const [name, value] = p.split("=");
urlParameters.push({
name: name ?? "",
value: value ?? "",
enabled: true
});
}
const [username, password] = getPairValue(flagsByName, "", ["u", "user"]).split(/:(.*)$/);
const isDigest = getPairValue(flagsByName, false, ["digest"]);
const authenticationType = username ? isDigest ? "digest" : "basic" : null;
const authentication = username ? {
username: username.trim(),
password: (password ?? "").trim()
} : {};
const headers = [
...pairsByName["header"] || [],
...pairsByName["H"] || []
...flagsByName["header"] || [],
...flagsByName["H"] || []
].map((header) => {
const [name, value] = header.split(/:(.*)$/);
if (!value) {
@@ -411,8 +423,8 @@ function importCommand(parseEntries, workspaceId) {
};
});
const cookieHeaderValue = [
...pairsByName["cookie"] || [],
...pairsByName["b"] || []
...flagsByName["cookie"] || [],
...flagsByName["b"] || []
].map((str) => {
const name = str.split("=", 1)[0];
const value = str.replace(`${name}=`, "");
@@ -428,12 +440,12 @@ function importCommand(parseEntries, workspaceId) {
enabled: true
});
}
const dataParameters = pairsToDataParameters(pairsByName);
const dataParameters = pairsToDataParameters(flagsByName);
const contentTypeHeader = headers.find((header) => header.name.toLowerCase() === "content-type");
const mimeType = contentTypeHeader ? contentTypeHeader.value.split(";")[0] : null;
const formDataParams = [
...pairsByName["form"] || [],
...pairsByName["F"] || []
...flagsByName["form"] || [],
...flagsByName["F"] || []
].map((str) => {
const parts = str.split("=");
const name = parts[0] ?? "";
@@ -451,7 +463,7 @@ function importCommand(parseEntries, workspaceId) {
});
let body = {};
let bodyType = null;
const bodyAsGET = getPairValue(pairsByName, false, ["G", "get"]);
const bodyAsGET = getPairValue(flagsByName, false, ["G", "get"]);
if (dataParameters.length > 0 && bodyAsGET) {
urlParameters.push(...dataParameters);
} else if (dataParameters.length > 0 && (mimeType == null || mimeType === "application/x-www-form-urlencoded")) {
@@ -486,7 +498,7 @@ function importCommand(parseEntries, workspaceId) {
});
}
}
let method = getPairValue(pairsByName, "", ["X", "request"]).toUpperCase();
let method = getPairValue(flagsByName, "", ["X", "request"]).toUpperCase();
if (method === "" && body) {
method = "text" in body || "form" in body ? "POST" : "GET";
}

View File

@@ -7232,56 +7232,54 @@ function pluginHookImport(ctx, contents) {
folders: []
};
const workspacesToImport = parsed.resources.filter(isWorkspace);
for (const workspaceToImport of workspacesToImport) {
const baseEnvironment = parsed.resources.find(
(r) => isEnvironment(r) && r.parentId === workspaceToImport._id
);
for (const w of workspacesToImport) {
resources.workspaces.push({
id: convertId(workspaceToImport._id),
createdAt: new Date(workspacesToImport.created ?? Date.now()).toISOString().replace("Z", ""),
updatedAt: new Date(workspacesToImport.updated ?? Date.now()).toISOString().replace("Z", ""),
id: convertId(w._id),
createdAt: w.created ? new Date(w.created).toISOString().replace("Z", "") : void 0,
updatedAt: w.updated ? new Date(w.updated).toISOString().replace("Z", "") : void 0,
model: "workspace",
name: workspaceToImport.name,
variables: baseEnvironment ? parseVariables(baseEnvironment.data) : []
name: w.name,
description: w.description || void 0
});
const environmentsToImport = parsed.resources.filter(
(r) => isEnvironment(r) && r.parentId === baseEnvironment?._id
(r) => isEnvironment(r)
);
resources.environments.push(
...environmentsToImport.map((r) => importEnvironment(r, workspaceToImport._id))
...environmentsToImport.map((r) => importEnvironment(r, w._id))
);
const nextFolder = (parentId) => {
const children = parsed.resources.filter((r) => r.parentId === parentId);
let sortPriority = 0;
for (const child of children) {
if (isRequestGroup(child)) {
resources.folders.push(importFolder(child, workspaceToImport._id));
resources.folders.push(importFolder(child, w._id));
nextFolder(child._id);
} else if (isHttpRequest(child)) {
resources.httpRequests.push(
importHttpRequest(child, workspaceToImport._id, sortPriority++)
importHttpRequest(child, w._id, sortPriority++)
);
} else if (isGrpcRequest(child)) {
resources.grpcRequests.push(
importGrpcRequest(child, workspaceToImport._id, sortPriority++)
importGrpcRequest(child, w._id, sortPriority++)
);
}
}
};
nextFolder(workspaceToImport._id);
nextFolder(w._id);
}
resources.httpRequests = resources.httpRequests.filter(Boolean);
resources.grpcRequests = resources.grpcRequests.filter(Boolean);
resources.environments = resources.environments.filter(Boolean);
resources.workspaces = resources.workspaces.filter(Boolean);
return { resources };
return { resources: deleteUndefinedAttrs(resources) };
}
function importEnvironment(e, workspaceId) {
return {
id: convertId(e._id),
createdAt: new Date(e.created ?? Date.now()).toISOString().replace("Z", ""),
updatedAt: new Date(e.updated ?? Date.now()).toISOString().replace("Z", ""),
createdAt: e.created ? new Date(e.created).toISOString().replace("Z", "") : void 0,
updatedAt: e.updated ? new Date(e.updated).toISOString().replace("Z", "") : void 0,
workspaceId: convertId(workspaceId),
environmentId: e.parentId === workspaceId ? null : convertId(e.parentId),
model: "environment",
name: e.name,
variables: Object.entries(e.data).map(([name, value]) => ({
@@ -7294,10 +7292,11 @@ function importEnvironment(e, workspaceId) {
function importFolder(f, workspaceId) {
return {
id: convertId(f._id),
createdAt: new Date(f.created ?? Date.now()).toISOString().replace("Z", ""),
updatedAt: new Date(f.updated ?? Date.now()).toISOString().replace("Z", ""),
createdAt: f.created ? new Date(f.created).toISOString().replace("Z", "") : void 0,
updatedAt: f.updated ? new Date(f.updated).toISOString().replace("Z", "") : void 0,
folderId: f.parentId === workspaceId ? null : convertId(f.parentId),
workspaceId: convertId(workspaceId),
description: f.description || void 0,
model: "folder",
name: f.name
};
@@ -7308,13 +7307,14 @@ function importGrpcRequest(r, workspaceId, sortPriority = 0) {
const method = parts[1] ?? null;
return {
id: convertId(r._id),
createdAt: new Date(r.created ?? Date.now()).toISOString().replace("Z", ""),
updatedAt: new Date(r.updated ?? Date.now()).toISOString().replace("Z", ""),
createdAt: r.created ? new Date(r.created).toISOString().replace("Z", "") : void 0,
updatedAt: r.updated ? new Date(r.updated).toISOString().replace("Z", "") : void 0,
workspaceId: convertId(workspaceId),
folderId: r.parentId === workspaceId ? null : convertId(r.parentId),
model: "grpc_request",
sortPriority,
name: r.name,
description: r.description || void 0,
url: convertSyntax(r.url),
service,
method,
@@ -7374,13 +7374,14 @@ function importHttpRequest(r, workspaceId, sortPriority = 0) {
}
return {
id: convertId(r._id),
createdAt: new Date(r.created ?? Date.now()).toISOString().replace("Z", ""),
updatedAt: new Date(r.updated ?? Date.now()).toISOString().replace("Z", ""),
createdAt: r.created ? new Date(r.created).toISOString().replace("Z", "") : void 0,
updatedAt: r.updated ? new Date(r.updated).toISOString().replace("Z", "") : void 0,
workspaceId: convertId(workspaceId),
folderId: r.parentId === workspaceId ? null : convertId(r.parentId),
model: "http_request",
sortPriority,
name: r.name,
description: r.description || void 0,
url: convertSyntax(r.url),
body,
bodyType,
@@ -7394,13 +7395,6 @@ function importHttpRequest(r, workspaceId, sortPriority = 0) {
})).filter(({ name, value }) => name !== "" || value !== "")
};
}
function parseVariables(data) {
return Object.entries(data).map(([name, value]) => ({
enabled: true,
name,
value: `${value}`
}));
}
function convertSyntax(variable) {
if (!isJSString(variable)) return variable;
return variable.replaceAll(/{{\s*(_\.)?([^}]+)\s*}}/g, "${[$2]}");
@@ -7432,6 +7426,17 @@ function convertId(id) {
}
return `GENERATE_ID::${id}`;
}
function deleteUndefinedAttrs(obj) {
if (Array.isArray(obj) && obj != null) {
return obj.map(deleteUndefinedAttrs);
} else if (typeof obj === "object" && obj != null) {
return Object.fromEntries(
Object.entries(obj).filter(([, v]) => v !== void 0).map(([k, v]) => [k, deleteUndefinedAttrs(v)])
);
} else {
return obj;
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
pluginHookImport

View File

@@ -145878,13 +145878,20 @@ function pluginHookImport(_ctx, contents) {
model: "workspace",
id: generateId("workspace"),
name: info.name || "Postman Import",
description: info.description?.content ?? info.description ?? "",
description: info.description?.content ?? info.description
};
exportResources.workspaces.push(workspace);
const environment = {
model: "environment",
id: generateId("environment"),
name: "Global Variables",
workspaceId: workspace.id,
variables: root.variable?.map((v) => ({
name: v.key,
value: v.value
})) ?? []
};
exportResources.workspaces.push(workspace);
exportResources.environments.push(environment);
const importItem = (v, folderId = null) => {
if (typeof v.name === "string" && Array.isArray(v.item)) {
const folder = {
@@ -145924,6 +145931,7 @@ function pluginHookImport(_ctx, contents) {
workspaceId: workspace.id,
folderId,
name: v.name,
description: v.description || void 0,
method: r.method || "GET",
url,
urlParameters,
@@ -145941,7 +145949,8 @@ function pluginHookImport(_ctx, contents) {
for (const item of root.item) {
importItem(item);
}
return { resources: convertTemplateSyntax(exportResources) };
const resources = deleteUndefinedAttrs(convertTemplateSyntax(exportResources));
return { resources };
}
function convertUrl(url) {
if (typeof url === "string") {
@@ -146123,6 +146132,17 @@ function convertTemplateSyntax(obj) {
return obj;
}
}
function deleteUndefinedAttrs(obj) {
if (Array.isArray(obj) && obj != null) {
return obj.map(deleteUndefinedAttrs);
} else if (typeof obj === "object" && obj != null) {
return Object.fromEntries(
Object.entries(obj).filter(([, v]) => v !== void 0).map(([k, v]) => [k, deleteUndefinedAttrs(v)])
);
} else {
return obj;
}
}
var idCount = {};
function generateId(model) {
idCount[model] = (idCount[model] ?? -1) + 1;

View File

@@ -45,13 +45,20 @@ function pluginHookImport(_ctx, contents) {
model: "workspace",
id: generateId("workspace"),
name: info.name || "Postman Import",
description: info.description?.content ?? info.description ?? "",
description: info.description?.content ?? info.description
};
exportResources.workspaces.push(workspace);
const environment = {
model: "environment",
id: generateId("environment"),
name: "Global Variables",
workspaceId: workspace.id,
variables: root.variable?.map((v) => ({
name: v.key,
value: v.value
})) ?? []
};
exportResources.workspaces.push(workspace);
exportResources.environments.push(environment);
const importItem = (v, folderId = null) => {
if (typeof v.name === "string" && Array.isArray(v.item)) {
const folder = {
@@ -91,6 +98,7 @@ function pluginHookImport(_ctx, contents) {
workspaceId: workspace.id,
folderId,
name: v.name,
description: v.description || void 0,
method: r.method || "GET",
url,
urlParameters,
@@ -108,7 +116,8 @@ function pluginHookImport(_ctx, contents) {
for (const item of root.item) {
importItem(item);
}
return { resources: convertTemplateSyntax(exportResources) };
const resources = deleteUndefinedAttrs(convertTemplateSyntax(exportResources));
return { resources };
}
function convertUrl(url) {
if (typeof url === "string") {
@@ -290,6 +299,17 @@ function convertTemplateSyntax(obj) {
return obj;
}
}
function deleteUndefinedAttrs(obj) {
if (Array.isArray(obj) && obj != null) {
return obj.map(deleteUndefinedAttrs);
} else if (typeof obj === "object" && obj != null) {
return Object.fromEntries(
Object.entries(obj).filter(([, v]) => v !== void 0).map(([k, v]) => [k, deleteUndefinedAttrs(v)])
);
} else {
return obj;
}
}
var idCount = {};
function generateId(model) {
idCount[model] = (idCount[model] ?? -1) + 1;

View File

@@ -41,6 +41,24 @@ function pluginHookImport(_ctx, contents) {
parsed.resources.httpRequests = parsed.resources.requests;
delete parsed.resources["requests"];
}
for (const workspace of parsed.resources.workspaces ?? []) {
if ("variables" in workspace) {
const baseEnvironment = {
id: `GENERATE_ID::base_env_${workspace["id"]}`,
name: "Global Variables",
variables: workspace.variables,
workspaceId: workspace.id
};
parsed.resources.environments = parsed.resources.environments ?? [];
parsed.resources.environments.push(baseEnvironment);
delete workspace.variables;
for (const environment of parsed.resources.environments) {
if (environment.workspaceId === workspace.id && environment.id !== baseEnvironment.id) {
environment.environmentId = baseEnvironment.id;
}
}
}
}
return { resources: parsed.resources };
}
function isJSObject(obj) {

View File

@@ -29,6 +29,7 @@ var plugin = {
description: "Prompt the user for input when sending a request",
args: [
{ type: "text", name: "title", label: "Title" },
{ type: "text", name: "label", label: "Label", optional: true },
{ type: "text", name: "defaultValue", label: "Default Value", optional: true },
{ type: "text", name: "placeholder", label: "Placeholder", optional: true }
],

View File

@@ -1,5 +1,5 @@
{
"name": "template-function-response",
"name": "@yaakapp/template-function-response",
"private": true,
"version": "0.0.1",
"scripts": {

View File

@@ -1,7 +1,8 @@
[package]
name = "yaak_grpc"
name = "yaak-grpc"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
tonic = "0.10.2"
@@ -22,4 +23,4 @@ tauri = { workspace = true }
tauri-plugin-shell = { workspace = true }
md5 = "0.7.0"
dunce = "1.0.4"
async-recursion = "1.1.1"
async-recursion = "1.1.1"

View File

@@ -33,8 +33,7 @@ impl Encoder for DynamicCodec {
type Error = Status;
fn encode(&mut self, item: Self::Item, dst: &mut EncodeBuf<'_>) -> Result<(), Self::Error> {
item.encode(dst)
.expect("buffer is too small to decode this message");
item.encode(dst).expect("buffer is too small to decode this message");
Ok(())
}
}
@@ -45,8 +44,7 @@ impl Decoder for DynamicCodec {
fn decode(&mut self, src: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
let mut msg = DynamicMessage::new(self.0.output());
msg.merge(src)
.map_err(|err| Status::internal(err.to_string()))?;
msg.merge(src).map_err(|err| Status::internal(err.to_string()))?;
Ok(Some(msg))
}
}

View File

@@ -30,15 +30,13 @@ pub struct MethodDefinition {
pub server_streaming: bool,
}
static SERIALIZE_OPTIONS: &'static SerializeOptions = &SerializeOptions::new()
.skip_default_fields(false)
.stringify_64_bit_integers(false);
static SERIALIZE_OPTIONS: &'static SerializeOptions =
&SerializeOptions::new().skip_default_fields(false).stringify_64_bit_integers(false);
pub fn serialize_message(msg: &DynamicMessage) -> Result<String, String> {
let mut buf = Vec::new();
let mut se = serde_json::Serializer::pretty(&mut buf);
msg.serialize_with_options(&mut se, SERIALIZE_OPTIONS)
.map_err(|e| e.to_string())?;
msg.serialize_with_options(&mut se, SERIALIZE_OPTIONS).map_err(|e| e.to_string())?;
let s = String::from_utf8(buf).expect("serde_json to emit valid utf8");
Ok(s)
}

View File

@@ -54,19 +54,14 @@ impl From<Status> for StreamError {
impl GrpcConnection {
pub fn service(&self, service: &str) -> Result<ServiceDescriptor, String> {
let service = self
.pool
.get_service_by_name(service)
.ok_or("Failed to find service")?;
let service = self.pool.get_service_by_name(service).ok_or("Failed to find service")?;
Ok(service)
}
pub fn method(&self, service: &str, method: &str) -> Result<MethodDescriptor, String> {
let service = self.service(service)?;
let method = service
.methods()
.find(|m| m.name() == method)
.ok_or("Failed to find method")?;
let method =
service.methods().find(|m| m.name() == method).ok_or("Failed to find method")?;
Ok(method)
}
@@ -132,13 +127,10 @@ impl GrpcConnection {
let path = method_desc_to_path(method);
let codec = DynamicCodec::new(method.clone());
client.ready().await.unwrap();
client
.client_streaming(req, path, codec)
.await
.map_err(|e| StreamError {
message: e.message().to_string(),
status: Some(e),
})
client.client_streaming(req, path, codec).await.map_err(|e| StreamError {
message: e.message().to_string(),
status: Some(e),
})
}
pub async fn server_streaming(
@@ -197,8 +189,7 @@ impl GrpcHandle {
fill_pool_from_files(&self.app_handle, proto_files).await
}?;
self.pools
.insert(make_pool_key(id, uri, proto_files), pool.clone());
self.pools.insert(make_pool_key(id, uri, proto_files), pool.clone());
Ok(())
}
@@ -211,9 +202,7 @@ impl GrpcHandle {
// Ensure reflection is up-to-date
self.reflect(id, uri, proto_files).await?;
let pool = self
.get_pool(id, uri, proto_files)
.ok_or("Failed to get pool".to_string())?;
let pool = self.get_pool(id, uri, proto_files).ok_or("Failed to get pool".to_string())?;
Ok(self.services_from_pool(&pool))
}
@@ -234,7 +223,7 @@ impl GrpcHandle {
&pool,
input_message,
))
.unwrap(),
.unwrap(),
})
}
def
@@ -249,9 +238,7 @@ impl GrpcHandle {
proto_files: &Vec<PathBuf>,
) -> Result<GrpcConnection, String> {
self.reflect(id, uri, proto_files).await?;
let pool = self
.get_pool(id, uri, proto_files)
.ok_or("Failed to get pool")?;
let pool = self.get_pool(id, uri, proto_files).ok_or("Failed to get pool")?;
let uri = uri_from_str(uri)?;
let conn = get_transport();
@@ -301,4 +288,4 @@ fn make_pool_key(id: &str, uri: &str, proto_files: &Vec<PathBuf>) -> String {
);
format!("{:x}", md5::compute(pool_key))
}
}

View File

@@ -104,7 +104,7 @@ pub async fn fill_pool_from_reflection(uri: &Uri) -> Result<DescriptorPool, Stri
if service == "grpc.reflection.v1alpha.ServerReflection" {
continue;
}
if service == "grpc.reflection.v1.ServerReflection"{
if service == "grpc.reflection.v1.ServerReflection" {
// TODO: update reflection client to use v1
continue;
}
@@ -316,8 +316,8 @@ mod topology {
zero_indegree.push(node.clone());
}
}
for (node, deps) in data.out_graph.iter(){
if deps.is_empty(){
for (node, deps) in data.out_graph.iter() {
if deps.is_empty() {
zero_indegree.push(node.clone());
}
}
@@ -344,8 +344,8 @@ mod topology {
}
let node = self.zero_indegree.pop().unwrap();
if let Some(parents) = self.data.in_graph.get(&node){
for parent in parents.iter(){
if let Some(parents) = self.data.in_graph.get(&node) {
for parent in parents.iter() {
let deps = self.data.out_graph.get_mut(parent).unwrap();
deps.remove(&node);
if deps.is_empty() {
@@ -360,7 +360,7 @@ mod topology {
}
#[test]
fn test_sort(){
fn test_sort() {
{
let mut topo_sort = SimpleTopoSort::new();
topo_sort.insert("a", []);
@@ -390,5 +390,4 @@ mod topology {
assert_eq!(iter.next(), None);
}
}
}

View File

@@ -0,0 +1,20 @@
[package]
name = "yaak-license"
links = "yaak-license"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
reqwest = { workspace = true, features = ["json"] }
serde = { version = "1.0.208", features = ["derive"] }
ts-rs = { workspace = true }
thiserror = { workspace = true }
tauri = { workspace = true }
yaak-models = { workspace = true }
chrono = "0.4.38"
log = "0.4.22"
serde_json = "1.0.132"
[build-dependencies]
tauri-plugin = { version = "2.0.3", features = ["build"] }

View File

@@ -0,0 +1,11 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type APIErrorResponsePayload = { error: string, message: string, };
export type ActivateLicenseRequestPayload = { licenseKey: string, appVersion: string, appPlatform: string, };
export type ActivateLicenseResponsePayload = { activationId: string, };
export type CheckActivationResponsePayload = { active: boolean, };
export type LicenseCheckStatus = { "type": "personal_use", trial_ended: string, } | { "type": "commercial_use" } | { "type": "invalid_license" } | { "type": "trialing", end: string, };

View File

@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type CheckActivationRequestPayload = { activationId: string, };

View File

@@ -0,0 +1,5 @@
const COMMANDS: &[&str] = &["activate", "check"];
fn main() {
tauri_plugin::Builder::new(COMMANDS).build();
}

View File

@@ -0,0 +1,37 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { useEffect } from 'react';
import { LicenseCheckStatus } from './bindings/license';
export * from './bindings/license';
export function useLicense() {
const queryClient = useQueryClient();
const activate = useMutation<void, string, { licenseKey: string }>({
mutationKey: ['license.activate'],
mutationFn: (payload) => invoke('plugin:yaak-license|activate', payload),
onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }),
});
// Check the license again after a license is activated
useEffect(() => {
const unlisten = listen('license-activated', async () => {
await queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY });
});
return () => {
unlisten.then((fn) => fn());
};
}, []);
const CHECK_QUERY_KEY = ['license.check'];
const check = useQuery<void, string, LicenseCheckStatus>({
queryKey: CHECK_QUERY_KEY,
queryFn: () => invoke('plugin:yaak-license|check'),
});
return {
activate,
check,
} as const;
}

View File

@@ -1,5 +1,5 @@
{
"name": "@yaakapp-internal/template",
"name": "@yaakapp-internal/license",
"private": true,
"version": "1.0.0",
"main": "index.ts"

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -0,0 +1,68 @@
## Default Permission
Default permissions for the plugin
- `allow-check`
- `allow-activate`
## Permission Table
<table>
<tr>
<th>Identifier</th>
<th>Description</th>
</tr>
<tr>
<td>
`yaak-license:allow-activate`
</td>
<td>
Enables the activate command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`yaak-license:deny-activate`
</td>
<td>
Denies the activate command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`yaak-license:allow-check`
</td>
<td>
Enables the check command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`yaak-license:deny-check`
</td>
<td>
Denies the check command without any pre-configured scope.
</td>
</tr>
</table>

View File

@@ -0,0 +1,3 @@
[default]
description = "Default permissions for the plugin"
permissions = ["allow-check", "allow-activate"]

View File

@@ -0,0 +1,325 @@
{
"$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 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": "Default permissions for the plugin",
"type": "string",
"const": "default"
}
]
}
}
}

View File

@@ -0,0 +1,37 @@
use crate::errors::Result;
use crate::{activate_license, check_license, ActivateLicenseRequestPayload, LicenseCheckStatus};
use log::{debug, info};
use std::string::ToString;
use tauri::{command, AppHandle, Manager, Runtime, WebviewWindow};
#[command]
pub async fn check<R: Runtime>(app_handle: AppHandle<R>) -> Result<LicenseCheckStatus> {
debug!("Checking license");
check_license(&app_handle).await
}
#[command]
pub async fn activate<R: Runtime>(license_key: &str, window: WebviewWindow<R>) -> Result<()> {
info!("Activating license {}", license_key);
activate_license(
&window,
ActivateLicenseRequestPayload {
license_key: license_key.to_string(),
app_platform: get_os().to_string(),
app_version: window.app_handle().package_info().version.to_string(),
},
)
.await
}
fn get_os() -> &'static str {
if cfg!(target_os = "windows") {
"windows"
} else if cfg!(target_os = "macos") {
"macos"
} else if cfg!(target_os = "linux") {
"linux"
} else {
"unknown"
}
}

View File

@@ -0,0 +1,28 @@
use serde::{Serialize, Serializer};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Reqwest error: {0}")]
APIError(#[from] reqwest::Error),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("{message}")]
ClientError { message: String, error: String },
#[error("Internal server error")]
ServerError,
}
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>;

View File

@@ -0,0 +1,16 @@
use tauri::{
generate_handler,
plugin::{Builder, TauriPlugin},
Runtime,
};
mod commands;
mod errors;
mod license;
use crate::commands::{activate, check};
pub use license::*;
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("yaak-license").invoke_handler(generate_handler![check, activate]).build()
}

View File

@@ -0,0 +1,158 @@
use crate::errors::Error::{ClientError, ServerError};
use crate::errors::Result;
use chrono::{NaiveDateTime, Utc};
use log::{debug, info, warn};
use serde::{Deserialize, Serialize};
use std::ops::Add;
use std::time::Duration;
use tauri::{is_dev, AppHandle, Emitter, Runtime, WebviewWindow};
use ts_rs::TS;
use yaak_models::queries::UpdateSource;
const KV_NAMESPACE: &str = "license";
const KV_ACTIVATION_ID_KEY: &str = "activation_id";
const TRIAL_SECONDS: u64 = 3600 * 24 * 14;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "models.ts")]
pub struct CheckActivationRequestPayload {
pub activation_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "license.ts")]
pub struct CheckActivationResponsePayload {
pub active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "license.ts")]
pub struct ActivateLicenseRequestPayload {
pub license_key: String,
pub app_version: String,
pub app_platform: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "license.ts")]
pub struct ActivateLicenseResponsePayload {
pub activation_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "license.ts")]
pub struct APIErrorResponsePayload {
pub error: String,
pub message: String,
}
pub async fn activate_license<R: Runtime>(
window: &WebviewWindow<R>,
p: ActivateLicenseRequestPayload,
) -> Result<()> {
let client = reqwest::Client::new();
let response = client.post(build_url("/activate")).json(&p).send().await?;
if response.status().is_client_error() {
let body: APIErrorResponsePayload = response.json().await?;
return Err(ClientError {
message: body.message,
error: body.error,
});
}
if response.status().is_server_error() {
return Err(ServerError);
}
let body: ActivateLicenseResponsePayload = response.json().await?;
yaak_models::queries::set_key_value_string(
window,
KV_ACTIVATION_ID_KEY,
KV_NAMESPACE,
body.activation_id.as_str(),
&UpdateSource::Window,
)
.await;
if let Err(e) = window.emit("license-activated", true) {
warn!("Failed to emit check-license event: {}", e);
}
Ok(())
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case", tag = "type")]
#[ts(export, export_to = "license.ts")]
pub enum LicenseCheckStatus {
PersonalUse { trial_ended: NaiveDateTime },
CommercialUse,
InvalidLicense,
Trialing { end: NaiveDateTime },
}
pub async fn check_license<R: Runtime>(app_handle: &AppHandle<R>) -> Result<LicenseCheckStatus> {
let activation_id = yaak_models::queries::get_key_value_string(
app_handle,
KV_ACTIVATION_ID_KEY,
KV_NAMESPACE,
"",
)
.await;
let settings = yaak_models::queries::get_or_create_settings(app_handle).await;
let trial_end = settings.created_at.add(Duration::from_secs(TRIAL_SECONDS));
debug!("Trial ending at {trial_end:?}");
let has_activation_id = !activation_id.is_empty();
let trial_period_active = Utc::now().naive_utc() < trial_end;
match (has_activation_id, trial_period_active) {
(false, true) => Ok(LicenseCheckStatus::Trialing { end: trial_end }),
(false, false) => Ok(LicenseCheckStatus::PersonalUse {
trial_ended: trial_end,
}),
(true, _) => {
info!("Checking license activation");
// A license has been activated, so let's check the license server
let client = reqwest::Client::new();
let payload = CheckActivationRequestPayload {
activation_id: activation_id.clone(),
};
let response = client.post(build_url("/check")).json(&payload).send().await?;
if response.status().is_client_error() {
let body: APIErrorResponsePayload = response.json().await?;
return Err(ClientError {
message: body.message,
error: body.error,
});
}
if response.status().is_server_error() {
return Err(ServerError);
}
let body: CheckActivationResponsePayload = response.json().await?;
if !body.active {
return Ok(LicenseCheckStatus::InvalidLicense);
}
Ok(LicenseCheckStatus::CommercialUse)
}
}
}
fn build_url(path: &str) -> String {
if is_dev() {
format!("http://localhost:9444/licenses{path}")
} else {
format!("https://license.yaak.app/licenses{path}")
}
}

View File

@@ -1,20 +1,21 @@
[package]
name = "yaak_models"
name = "yaak-models"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
chrono = { version = "0.4.38", features = ["serde"] }
rusqlite = { version = "0.31.0", features = ["bundled", "chrono"] }
sea-query = { version = "0.31.0", features = ["with-chrono", "attr"] }
sea-query-rusqlite = { version = "0.6.0", features = ["with-chrono"] }
log = "0.4.22"
nanoid = "0.4.0"
r2d2 = "0.8.10"
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"
thiserror = "1.0.63"
ts-rs = { version = "10.0.0", features = ["chrono-impl", "serde-json-impl"] }
tauri = { workspace = true }
sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-rustls"] }
log = "0.4.22"
rand = "0.8.5"
r2d2 = "0.8.10"
r2d2_sqlite = { version = "0.24.0" }
tauri = { workspace = true }
thiserror = "1.0.63"
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }

View File

@@ -1,6 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AnyModel = CookieJar | Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | Plugin | Settings | KeyValue | Workspace;
export type AnyModel = CookieJar | Environment | Folder | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | Plugin | Settings | KeyValue | Workspace | WorkspaceMeta;
export type Cookie = { raw_cookie: string, domain: CookieDomain, expires: CookieExpires, path: [string, boolean], };
@@ -10,11 +10,13 @@ export type CookieExpires = { "AtUtc": string } | "SessionEnd";
export type CookieJar = { model: "cookie_jar", id: string, createdAt: string, updatedAt: string, workspaceId: string, cookies: Array<Cookie>, name: string, };
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, };
export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, sortPriority: number, };
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 GrpcConnection = { model: "grpc_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, method: string, service: string, status: number, state: GrpcConnectionState, trailers: { [key in string]?: string }, url: string, };
@@ -24,13 +26,13 @@ export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, up
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, };
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>, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: 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, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
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, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
@@ -38,16 +40,26 @@ export type HttpResponseHeader = { name: string, value: string, };
export type HttpResponseState = "initialized" | "connected" | "closed";
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, };
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
export type ModelPayload = { model: AnyModel, windowLabel: string, updateSource: UpdateSource, };
export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, };
export type ProxySetting = { "type": "enabled", http: string, https: string, auth: ProxySettingAuth | null, } | { "type": "disabled" };
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, telemetry: boolean, theme: string, themeDark: string, themeLight: string, updateChannel: string, proxy: ProxySetting | null, };
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, telemetry: boolean, theme: string, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, variables: Array<EnvironmentVariable>, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
export type SyncHistory = { model: "sync_history", id: string, workspaceId: string, createdAt: string, states: Array<SyncState>, checksum: string, relPath: string, syncDir: string, };
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 = "sync" | "window" | "plugin" | "background";
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
export type WorkspaceMeta = { model: "workspace_meta", id: string, workspaceId: string, createdAt: string, updatedAt: string, settingSyncDir: string | null, };

View File

@@ -1,5 +1,5 @@
pub mod error;
pub mod models;
pub mod queries;
mod error;
pub mod plugin;
pub mod plugin;

View File

@@ -1,9 +1,11 @@
use chrono::NaiveDateTime;
use rusqlite::Row;
use sea_query::Iden;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::collections::BTreeMap;
use std::fmt::Display;
use std::str::FromStr;
use ts_rs::TS;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
@@ -26,6 +28,48 @@ pub struct ProxySettingAuth {
pub password: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export, export_to = "models.ts")]
pub enum EditorKeymap {
Default,
Vim,
Vscode,
Emacs,
}
impl FromStr for EditorKeymap {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"default" => Ok(Self::Default),
"vscode" => Ok(Self::Vscode),
"vim" => Ok(Self::Vim),
"emacs" => Ok(Self::Emacs),
_ => Ok(Self::default()),
}
}
}
impl Display for EditorKeymap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
EditorKeymap::Default => "default".to_string(),
EditorKeymap::Vscode => "vscode".to_string(),
EditorKeymap::Vim => "vim".to_string(),
EditorKeymap::Emacs => "emacs".to_string(),
};
write!(f, "{}", str)
}
}
impl Default for EditorKeymap {
fn default() -> Self {
Self::Default
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "models.ts")]
@@ -42,12 +86,13 @@ pub struct Settings {
pub interface_font_size: i32,
pub interface_scale: f32,
pub open_workspace_new_window: Option<bool>,
pub proxy: Option<ProxySetting>,
pub telemetry: bool,
pub theme: String,
pub theme_dark: String,
pub theme_light: String,
pub update_channel: String,
pub proxy: Option<ProxySetting>,
pub editor_keymap: EditorKeymap,
}
#[derive(Iden)]
@@ -61,6 +106,7 @@ pub enum SettingsIden {
Appearance,
EditorFontSize,
EditorKeymap,
EditorSoftWrap,
InterfaceFontSize,
InterfaceScale,
@@ -78,6 +124,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
let proxy: Option<String> = r.get("proxy")?;
let editor_keymap: String = r.get("editor_keymap")?;
Ok(Settings {
id: r.get("id")?,
model: r.get("model")?,
@@ -85,6 +132,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
updated_at: r.get("updated_at")?,
appearance: r.get("appearance")?,
editor_font_size: r.get("editor_font_size")?,
editor_keymap: EditorKeymap::from_str(editor_keymap.as_str()).unwrap(),
editor_soft_wrap: r.get("editor_soft_wrap")?,
interface_font_size: r.get("interface_font_size")?,
interface_scale: r.get("interface_scale")?,
@@ -110,7 +158,6 @@ pub struct Workspace {
pub updated_at: NaiveDateTime,
pub name: String,
pub description: String,
pub variables: Vec<EnvironmentVariable>,
// Settings
#[serde(default = "default_true")]
@@ -134,14 +181,12 @@ pub enum WorkspaceIden {
SettingFollowRedirects,
SettingRequestTimeout,
SettingValidateCertificates,
Variables,
}
impl<'s> TryFrom<&Row<'s>> for Workspace {
type Error = rusqlite::Error;
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
let variables: String = r.get("variables")?;
Ok(Workspace {
id: r.get("id")?,
model: r.get("model")?,
@@ -149,10 +194,9 @@ impl<'s> TryFrom<&Row<'s>> for Workspace {
updated_at: r.get("updated_at")?,
name: r.get("name")?,
description: r.get("description")?,
variables: serde_json::from_str(variables.as_str()).unwrap_or_default(),
setting_validate_certificates: r.get("setting_validate_certificates")?,
setting_follow_redirects: r.get("setting_follow_redirects")?,
setting_request_timeout: r.get("setting_request_timeout")?,
setting_validate_certificates: r.get("setting_validate_certificates")?,
})
}
}
@@ -169,6 +213,47 @@ impl Workspace {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "models.ts")]
pub struct WorkspaceMeta {
#[ts(type = "\"workspace_meta\"")]
pub model: String,
pub id: String,
pub workspace_id: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub setting_sync_dir: Option<String>,
}
#[derive(Iden)]
pub enum WorkspaceMetaIden {
#[iden = "workspace_metas"]
Table,
Model,
Id,
WorkspaceId,
CreatedAt,
UpdatedAt,
SettingSyncDir,
}
impl<'s> TryFrom<&Row<'s>> for WorkspaceMeta {
type Error = rusqlite::Error;
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
Ok(WorkspaceMeta {
id: r.get("id")?,
workspace_id: r.get("workspace_id")?,
model: r.get("model")?,
created_at: r.get("created_at")?,
updated_at: r.get("updated_at")?,
setting_sync_dir: r.get("setting_sync_dir")?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export, export_to = "models.ts")]
enum CookieDomain {
@@ -248,6 +333,7 @@ pub struct Environment {
pub model: String,
pub id: String,
pub workspace_id: String,
pub environment_id: Option<String>,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
@@ -263,6 +349,7 @@ pub enum EnvironmentIden {
Id,
CreatedAt,
UpdatedAt,
EnvironmentId,
WorkspaceId,
Name,
@@ -278,6 +365,7 @@ impl<'s> TryFrom<&Row<'s>> for Environment {
id: r.get("id")?,
model: r.get("model")?,
workspace_id: r.get("workspace_id")?,
environment_id: r.get("environment_id")?,
created_at: r.get("created_at")?,
updated_at: r.get("updated_at")?,
name: r.get("name")?,
@@ -295,6 +383,7 @@ pub struct EnvironmentVariable {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
@@ -310,6 +399,7 @@ pub struct Folder {
pub folder_id: Option<String>,
pub name: String,
pub description: String,
pub sort_priority: f32,
}
@@ -325,6 +415,7 @@ pub enum FolderIden {
UpdatedAt,
Name,
Description,
SortPriority,
}
@@ -341,6 +432,7 @@ impl<'s> TryFrom<&Row<'s>> for Folder {
updated_at: r.get("updated_at")?,
folder_id: r.get("folder_id")?,
name: r.get("name")?,
description: r.get("description")?,
})
}
}
@@ -354,6 +446,7 @@ pub struct HttpRequestHeader {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
@@ -365,6 +458,7 @@ pub struct HttpUrlParameter {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
@@ -385,6 +479,7 @@ pub struct HttpRequest {
#[ts(type = "Record<string, any>")]
pub body: BTreeMap<String, Value>,
pub body_type: Option<String>,
pub description: String,
pub headers: Vec<HttpRequestHeader>,
#[serde(default = "default_http_request_method")]
pub method: String,
@@ -409,6 +504,7 @@ pub enum HttpRequestIden {
AuthenticationType,
Body,
BodyType,
Description,
Headers,
Method,
Name,
@@ -437,6 +533,7 @@ impl<'s> TryFrom<&Row<'s>> for HttpRequest {
method: r.get("method")?,
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(),
@@ -567,6 +664,7 @@ pub struct GrpcMetadataEntry {
pub enabled: bool,
pub name: String,
pub value: String,
pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
@@ -584,6 +682,7 @@ pub struct GrpcRequest {
pub authentication_type: Option<String>,
#[ts(type = "Record<string, any>")]
pub authentication: BTreeMap<String, Value>,
pub description: String,
pub message: String,
pub metadata: Vec<GrpcMetadataEntry>,
pub method: Option<String>,
@@ -606,6 +705,7 @@ pub enum GrpcRequestIden {
Authentication,
AuthenticationType,
Description,
Message,
Metadata,
Method,
@@ -629,6 +729,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcRequest {
updated_at: r.get("updated_at")?,
folder_id: r.get("folder_id")?,
name: r.get("name")?,
description: r.get("description")?,
service: r.get("service")?,
method: r.get("method")?,
message: r.get("message")?,
@@ -852,6 +953,76 @@ impl<'s> TryFrom<&Row<'s>> for Plugin {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "models.ts")]
pub struct SyncState {
#[ts(type = "\"sync_state\"")]
pub model: String,
pub id: String,
pub workspace_id: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub flushed_at: NaiveDateTime,
pub model_id: String,
pub checksum: String,
pub rel_path: String,
pub sync_dir: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "models.ts")]
pub struct SyncHistory {
#[ts(type = "\"sync_history\"")]
pub model: String,
pub id: String,
pub workspace_id: String,
pub created_at: NaiveDateTime,
pub states: Vec<SyncState>,
pub checksum: String,
pub rel_path: String,
pub sync_dir: String,
}
#[derive(Iden)]
pub enum SyncStateIden {
#[iden = "sync_states"]
Table,
Model,
Id,
WorkspaceId,
CreatedAt,
UpdatedAt,
Checksum,
FlushedAt,
ModelId,
RelPath,
SyncDir,
}
impl<'s> TryFrom<&Row<'s>> for SyncState {
type Error = rusqlite::Error;
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
Ok(SyncState {
id: r.get("id")?,
workspace_id: r.get("workspace_id")?,
model: r.get("model")?,
created_at: r.get("created_at")?,
updated_at: r.get("updated_at")?,
flushed_at: r.get("flushed_at")?,
checksum: r.get("checksum")?,
model_id: r.get("model_id")?,
sync_dir: r.get("sync_dir")?,
rel_path: r.get("rel_path")?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "models.ts")]
@@ -913,6 +1084,8 @@ pub enum ModelType {
TypeHttpResponse,
TypePlugin,
TypeWorkspace,
TypeWorkspaceMeta,
TypeSyncState,
}
impl ModelType {
@@ -928,12 +1101,14 @@ impl ModelType {
ModelType::TypeHttpResponse => "rs",
ModelType::TypePlugin => "pg",
ModelType::TypeWorkspace => "wk",
ModelType::TypeWorkspaceMeta => "wm",
ModelType::TypeSyncState => "ss",
}
.to_string()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[derive(Debug, Clone, Serialize, TS)]
#[serde(rename_all = "camelCase", untagged)]
#[ts(export, export_to = "models.ts")]
pub enum AnyModel {
@@ -949,4 +1124,52 @@ pub enum AnyModel {
Settings(Settings),
KeyValue(KeyValue),
Workspace(Workspace),
WorkspaceMeta(WorkspaceMeta),
}
impl<'de> Deserialize<'de> for AnyModel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
let model = value.as_object().unwrap();
let model = match model.get("model") {
Some(m) if m == "http_request" => {
AnyModel::HttpRequest(serde_json::from_value(value).unwrap())
}
Some(m) if m == "grpc_request" => {
AnyModel::GrpcRequest(serde_json::from_value(value).unwrap())
}
Some(m) if m == "workspace" => {
AnyModel::Workspace(serde_json::from_value(value).unwrap())
}
Some(m) if m == "environment" => {
AnyModel::Environment(serde_json::from_value(value).unwrap())
}
Some(m) if m == "folder" => AnyModel::Folder(serde_json::from_value(value).unwrap()),
Some(m) if m == "key_value" => {
AnyModel::KeyValue(serde_json::from_value(value).unwrap())
}
Some(m) if m == "grpc_connection" => {
AnyModel::GrpcConnection(serde_json::from_value(value).unwrap())
}
Some(m) if m == "grpc_event" => {
AnyModel::GrpcEvent(serde_json::from_value(value).unwrap())
}
Some(m) if m == "cookie_jar" => {
AnyModel::CookieJar(serde_json::from_value(value).unwrap())
}
Some(m) if m == "plugin" => AnyModel::Plugin(serde_json::from_value(value).unwrap()),
Some(m) => {
return Err(serde::de::Error::custom(format!("Unknown model {}", m)));
}
None => {
return Err(serde::de::Error::custom("Missing or invalid model"));
}
};
Ok(model)
}
}

View File

@@ -4,15 +4,14 @@ use r2d2_sqlite::SqliteConnectionManager;
use serde::Deserialize;
use sqlx::migrate::Migrator;
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::SqlitePool;
use std::fs::{create_dir_all, File};
use sqlx::{ConnectOptions, SqlitePool};
use std::fs::create_dir_all;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
use tauri::async_runtime::Mutex;
use tauri::path::BaseDirectory;
use tauri::plugin::TauriPlugin;
use tauri::{plugin, AppHandle, Manager, Runtime};
use tauri::{plugin, AppHandle, Manager, Runtime, Url};
pub struct SqliteConnection(pub Mutex<Pool<SqliteConnectionManager>>);
@@ -62,25 +61,17 @@ impl Builder {
}
}
async fn must_migrate_db<R: Runtime>(app_handle: &AppHandle<R>, path: &PathBuf) {
let app_data_dir = app_handle.path().app_data_dir().unwrap();
let sqlite_file_path = app_data_dir.join("db.sqlite");
info!("Creating database file at {:?}", sqlite_file_path);
File::options()
.write(true)
.create(true)
.open(&sqlite_file_path)
.expect("Problem creating database file!");
fn db_connection_url(sqlite_file_path: &PathBuf) -> Url {
let p_string = sqlite_file_path.to_string_lossy().replace(' ', "%20");
let url = format!("sqlite://{}?mode=rwc", p_string);
Url::parse(&url).unwrap()
}
async fn must_migrate_db<R: Runtime>(app_handle: &AppHandle<R>, sqlite_file_path: &PathBuf) {
let url = db_connection_url(sqlite_file_path);
info!("Connecting to database at {}", url);
let opts = SqliteConnectOptions::from_str(path.to_string_lossy().to_string().as_str()).unwrap();
let pool = SqlitePool::connect_with(opts)
.await
.expect("Failed to connect to database");
let opts = SqliteConnectOptions::from_url(&url).unwrap();
let pool = SqlitePool::connect_with(opts).await.expect("Failed to connect to database");
let p = app_handle
.path()
.resolve("migrations", BaseDirectory::Resource)

View File

@@ -1,16 +1,14 @@
[package]
name = "yaak_plugin_runtime"
name = "yaak-plugins"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
anyhow = "1.0.86"
command-group = "5.0.1"
dunce = "1.0.4"
log = "0.4.21"
prost = "0.13.1"
rand = "0.8.5"
reqwest = { version = "0.12.5", features = ["stream"] }
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.113"
tauri = { workspace = true }
@@ -18,8 +16,8 @@ tauri-plugin-shell = { workspace = true }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "process"] }
tonic = "0.12.1"
ts-rs = "10.0.0"
thiserror = "1.0.63"
yaak_models = { workspace = true }
thiserror = "2.0.7"
yaak-models = { workspace = true }
regex = "1.10.6"
path-slash = "0.2.1"

View File

@@ -114,7 +114,7 @@ name: string,
*/
optional?: boolean,
/**
* The label of the input
* The label of the input
*/
label?: string,
/**
@@ -132,7 +132,7 @@ name: string,
*/
optional?: boolean,
/**
* The label of the input
* The label of the input
*/
label?: string,
/**
@@ -158,7 +158,7 @@ name: string,
*/
optional?: boolean,
/**
* The label of the input
* The label of the input
*/
label?: string,
/**
@@ -176,7 +176,7 @@ name: string,
*/
optional?: boolean,
/**
* The label of the input
* The label of the input
*/
label?: string,
/**
@@ -198,7 +198,7 @@ name: string,
*/
optional?: boolean,
/**
* The label of the input
* The label of the input
*/
label?: string,
/**
@@ -222,7 +222,7 @@ name: string,
*/
optional?: boolean,
/**
* The label of the input
* The label of the input
*/
label?: string,
/**

View File

@@ -1,18 +1,18 @@
// 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, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
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, };
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, sortPriority: number, };
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, };
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>, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: 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, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
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, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
@@ -20,6 +20,6 @@ export type HttpResponseHeader = { name: string, value: string, };
export type HttpResponseState = "initialized" | "connected" | "closed";
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, };
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, variables: Array<EnvironmentVariable>, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };

Some files were not shown because too many files have changed in this diff Show More