mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-06 13:29:06 -05:00
Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f0d4ad5e4 | ||
|
|
cd3530f598 | ||
|
|
53aea914ac | ||
|
|
dc0c1decee | ||
|
|
32d56f2274 | ||
|
|
ef86c1d189 | ||
|
|
e264c50427 | ||
|
|
f05ad62301 | ||
|
|
0a6228bf16 | ||
|
|
fa3a0b57f9 | ||
|
|
4390c02117 | ||
|
|
77011176af | ||
|
|
759fc503d3 | ||
|
|
0cb633e479 | ||
|
|
81ceb981e8 | ||
|
|
4dae1a7955 | ||
|
|
d119f4cab2 | ||
|
|
7e1eb90d29 | ||
|
|
bf97ea1659 | ||
|
|
749ca968ec | ||
|
|
0c54b481fb | ||
|
|
4943bad8ec | ||
|
|
450dbd0053 | ||
|
|
236c8fa656 | ||
|
|
1dfc2ee602 | ||
|
|
1d158082f6 | ||
|
|
f3e44c53d7 | ||
|
|
c8d5e7c97b | ||
|
|
9bde6bbd0a | ||
|
|
df5be218a5 | ||
|
|
2deb870bb6 | ||
|
|
0f9975339c | ||
|
|
6ad4e7bbb5 | ||
|
|
2bcf67aaa6 | ||
|
|
c01b8ce4ca | ||
|
|
f7bb649b16 | ||
|
|
e3e67c8df7 | ||
|
|
c9698c0f23 | ||
|
|
2cdd1d8136 | ||
|
|
8d8e5c0317 | ||
|
|
4e66a73677 | ||
|
|
08f1bc4e65 | ||
|
|
c6d9cb9c9e | ||
|
|
efbb90dd60 | ||
|
|
7a7940d365 | ||
|
|
8a6f80a181 | ||
|
|
e8e0097e2d | ||
|
|
f475b05c51 | ||
|
|
7e5f9004e2 | ||
|
|
660771b48c | ||
|
|
030e8b837e | ||
|
|
a42cba567c | ||
|
|
484b5b2fd8 | ||
|
|
a71fb8ed6c | ||
|
|
5b8114f6f3 | ||
|
|
68637d24c7 | ||
|
|
c097afe657 | ||
|
|
78bc7d7909 | ||
|
|
b68ce44d52 | ||
|
|
632344d166 | ||
|
|
f3814b7d2b | ||
|
|
618a544dbd | ||
|
|
9a55426236 | ||
|
|
b7ad490c9b | ||
|
|
2095cb88c2 | ||
|
|
a9e05ae988 | ||
|
|
99a6c38632 | ||
|
|
b2766509e3 | ||
|
|
3f5b5a397c | ||
|
|
923b1ac830 | ||
|
|
17dbe7c9a7 | ||
|
|
df80cdfe33 | ||
|
|
eb1916b773 | ||
|
|
a3df0489b1 | ||
|
|
b19e036a61 | ||
|
|
b51e37f221 | ||
|
|
cf9882b5b9 | ||
|
|
bbf85c953d | ||
|
|
17ddc76223 | ||
|
|
754ec0ba86 | ||
|
|
1198aa7d87 | ||
|
|
43437abae7 | ||
|
|
9439cfa2ba | ||
|
|
a731ccc8bd | ||
|
|
451c8b9dde | ||
|
|
b7682db9a3 | ||
|
|
7e2d72c4e3 | ||
|
|
28bb460409 | ||
|
|
56d635166b | ||
|
|
f6a7257104 | ||
|
|
1fce060ef7 | ||
|
|
5c966e5a95 | ||
|
|
0520ef5d43 | ||
|
|
25b110778a | ||
|
|
327bf84e57 | ||
|
|
1c48b309b5 | ||
|
|
7c5dec821d | ||
|
|
dcd8f6c08a | ||
|
|
31f9a63c3b | ||
|
|
e902b67a63 | ||
|
|
b11c72fde4 | ||
|
|
07b90c6ae3 | ||
|
|
ba6163b6d8 | ||
|
|
8055b625d0 | ||
|
|
3a61ffbbb0 | ||
|
|
f8478677c5 | ||
|
|
f5094c5a94 | ||
|
|
8300187566 | ||
|
|
cd8ab3616e | ||
|
|
be0c92b755 | ||
|
|
c34ea20406 | ||
|
|
6e9b1db196 | ||
|
|
d83aabd2be | ||
|
|
d46479cd22 | ||
|
|
19cae33382 | ||
|
|
267cd079ad | ||
|
|
19c1efc73e | ||
|
|
dfa9a22861 | ||
|
|
533f9bacc4 | ||
|
|
0358748729 | ||
|
|
1540d0a5a5 | ||
|
|
d177e164f1 | ||
|
|
f1355c9d15 | ||
|
|
485a9ea47c | ||
|
|
dbc606fb53 | ||
|
|
a00b4ae232 | ||
|
|
998b5cf78a | ||
|
|
b4deae6e8d | ||
|
|
87fdf17010 | ||
|
|
c6975a9e8b | ||
|
|
b44ac55bc2 | ||
|
|
9c65c95ba9 | ||
|
|
7beb9f4e69 | ||
|
|
dbecd74f46 | ||
|
|
6826ee1672 | ||
|
|
a12ae7ef56 | ||
|
|
dbc100409d | ||
|
|
6b87cd9655 | ||
|
|
7ce2cdc9cc | ||
|
|
1f4e38b7a7 | ||
|
|
0013a0797b | ||
|
|
5e9b14dc0b | ||
|
|
b7cfb0db13 | ||
|
|
8948bfbf45 | ||
|
|
4218e90bf4 | ||
|
|
2172d7ac60 | ||
|
|
5e45cb4908 | ||
|
|
d662883fdd | ||
|
|
f83f3d4682 | ||
|
|
e03c745093 | ||
|
|
73b9d699ed | ||
|
|
5a7b9aba2f | ||
|
|
cf433b26a5 | ||
|
|
573035b17d | ||
|
|
a267c0c53f | ||
|
|
328563f4e6 | ||
|
|
3844fec968 | ||
|
|
8557a2477b | ||
|
|
d02519ab74 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -114,4 +114,4 @@ jobs:
|
||||
releaseBody: '[Changelog __VERSION__](https://yaak.app/blog/__VERSION__)'
|
||||
releaseDraft: true
|
||||
prerelease: true
|
||||
args: '${{ matrix.args }} --config ./src-tauri/tauri.commercial.conf.json'
|
||||
args: '${{ matrix.args }} --config ./src-tauri/tauri.release.conf.json'
|
||||
|
||||
44
.github/workflows/sponsors.yml
vendored
Normal file
44
.github/workflows/sponsors.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Generate Sponsors README
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 30 15 * * 0-6
|
||||
permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Generate Sponsors
|
||||
uses: JamesIves/github-sponsors-readme-action@v1
|
||||
with:
|
||||
token: ${{ secrets.SPONSORS_PAT }}
|
||||
file: 'README.md'
|
||||
maximum: 1999
|
||||
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="50px" alt="User avatar: {{{ login }}}" /></a> '
|
||||
active-only: false
|
||||
include-private: true
|
||||
marker: 'sponsors-base'
|
||||
|
||||
- name: Generate Sponsors
|
||||
uses: JamesIves/github-sponsors-readme-action@v1
|
||||
with:
|
||||
token: ${{ secrets.SPONSORS_PAT }}
|
||||
file: 'README.md'
|
||||
minimum: 2000
|
||||
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="80px" alt="User avatar: {{{ login }}}" /></a> '
|
||||
active-only: false
|
||||
include-private: true
|
||||
marker: 'sponsors-premium'
|
||||
|
||||
# ⚠️ Note: You can use any deployment step here to automatically push the README
|
||||
# changes back to your branch.
|
||||
- name: Commit Changes
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
branch: main
|
||||
force: false
|
||||
folder: '.'
|
||||
77
README.md
77
README.md
@@ -1,32 +1,66 @@
|
||||
# Yaak API Client
|
||||
<p align="center">
|
||||
<a href="https://github.com/JamesIves/github-sponsors-readme-action">
|
||||
<img width="200px" src="https://github.com/mountain-loop/yaak/raw/main/src-tauri/icons/icon.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Yaak is a desktop API client for interacting with REST, GraphQL, Server Sent Events (SSE), WebSocket, and gRPC
|
||||
APIs. It's built using [Tauri](https://tauri.app), Rust, and ReactJS.
|
||||
<h1 align="center">
|
||||
💫 Yaak ➟ Desktop API Client 💫
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
A fast, privacy-first API client for REST, GraphQL, SSE, WebSocket, and gRPC – built with Tauri, Rust, and React.
|
||||
</p>
|
||||
<p align="center">
|
||||
Development is funded by community-purchased <a href="https://yaak.app/pricing">licenses</a>. You can also <a href="https://github.com/sponsors/gschier">become a sponsor</a> to have your logo appear below. 💖
|
||||
</p>
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
<p align="center">
|
||||
<!-- sponsors-premium --><a href="https://github.com/MVST-Solutions"><img src="https://github.com/MVST-Solutions.png" width="80px" alt="User avatar: MVST-Solutions" /></a> <a href="https://github.com/dharsanb"><img src="https://github.com/dharsanb.png" width="80px" alt="User avatar: dharsanb" /></a> <a href="https://github.com/railwayapp"><img src="https://github.com/railwayapp.png" width="80px" alt="User avatar: railwayapp" /></a> <a href="https://github.com/caseyamcl"><img src="https://github.com/caseyamcl.png" width="80px" alt="User avatar: caseyamcl" /></a> <a href="https://github.com/andriyor"><img src="https://github.com/andriyor.png" width="80px" alt="User avatar: andriyor" /></a> <a href="https://github.com/"><img src="https://raw.githubusercontent.com/JamesIves/github-sponsors-readme-action/dev/.github/assets/placeholder.png" width="80px" alt="User avatar: " /></a> <!-- sponsors-premium -->
|
||||
</p>
|
||||
<p align="center">
|
||||
<!-- sponsors-base --><a href="https://github.com/seanwash"><img src="https://github.com/seanwash.png" width="50px" alt="User avatar: seanwash" /></a> <a href="https://github.com/jerath"><img src="https://github.com/jerath.png" width="50px" alt="User avatar: jerath" /></a> <a href="https://github.com/itsa-sh"><img src="https://github.com/itsa-sh.png" width="50px" alt="User avatar: itsa-sh" /></a> <a href="https://github.com/dmmulroy"><img src="https://github.com/dmmulroy.png" width="50px" alt="User avatar: dmmulroy" /></a> <a href="https://github.com/timcole"><img src="https://github.com/timcole.png" width="50px" alt="User avatar: timcole" /></a> <a href="https://github.com/VLZH"><img src="https://github.com/VLZH.png" width="50px" alt="User avatar: VLZH" /></a> <a href="https://github.com/terasaka2k"><img src="https://github.com/terasaka2k.png" width="50px" alt="User avatar: terasaka2k" /></a> <a href="https://github.com/majudhu"><img src="https://github.com/majudhu.png" width="50px" alt="User avatar: majudhu" /></a> <a href="https://github.com/axelrindle"><img src="https://github.com/axelrindle.png" width="50px" alt="User avatar: axelrindle" /></a> <a href="https://github.com/jirizverina"><img src="https://github.com/jirizverina.png" width="50px" alt="User avatar: jirizverina" /></a> <a href="https://github.com/chip-well"><img src="https://github.com/chip-well.png" width="50px" alt="User avatar: chip-well" /></a> <!-- sponsors-base -->
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
|
||||
## Features
|
||||
|
||||
Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it.
|
||||
Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in.
|
||||
|
||||
|
||||
### 🌐 Work with any API
|
||||
|
||||
- Import collections from Postman, Insomnia, OpenAPI, Swagger, or Curl.
|
||||
- Send requests via REST, GraphQL, gRPC, WebSocket, or Server-Sent Events.
|
||||
- Filter and inspect responses with JSONPath or XPath.
|
||||
|
||||
### 🔐 Stay secure
|
||||
- Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication.
|
||||
- Secure sensitive values with encrypted secrets.
|
||||
- Store secrets in your OS keychain.
|
||||
|
||||
### ☁️ Organize & collaborate
|
||||
- Group requests into workspaces and nested folders.
|
||||
- Use environment variables to switch between dev, staging, and prod.
|
||||
- Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox.
|
||||
|
||||
### 🧩 Extend & customize
|
||||
- Insert dynamic values like UUIDs or timestamps with template tags.
|
||||
- Pick from built-in themes or build your own.
|
||||
- Create plugins to extend authentication, template tags, or the UI.
|
||||
|
||||
|
||||
## Contribution Policy
|
||||
|
||||
Yaak is open source, but only accepting contributions for bug fixes. To get started,
|
||||
Yaak is open source but only accepting contributions for bug fixes. To get started,
|
||||
visit [`DEVELOPMENT.md`](DEVELOPMENT.md) for tips on setting up your environment.
|
||||
|
||||
## Feature Overview
|
||||
|
||||
- 🪂 Import data from Postman, Insomnia, OpenAPI, Swagger, or Curl.<br/>
|
||||
- 📤 Send requests via REST, GraphQL, Server Sent Events (SSE), WebSockets, or gRPC.<br/>
|
||||
- 🔐 Automatically authorize requests with OAuth 2.0, JWT tokens, Basic Auth, and more.<br/>
|
||||
- 🔎 Filter response bodies using JSONPath or XPath queries.<br/>
|
||||
- ⛓️ Chain together multiple requests to dynamically reference values.<br/>
|
||||
- 📂 Organize requests into workspaces and nested folders.<br/>
|
||||
- 🧮 Use environment variables to easily switch between Prod and Dev.<br/>
|
||||
- 🛡️ Secure arbitrary text values with end-to-end encryption<br/>
|
||||
- 🏷️ Send dynamic values like UUIDs or timestamps using template tags.<br/>
|
||||
- 🎨 Choose from many of the included themes, or make your own.<br/>
|
||||
- 💽 Mirror workspace data to a directory for integration with Git or Dropbox.<br/>
|
||||
- 📜 View response history for each request.<br/>
|
||||
- 🔌 Create your own plugins for authentication, template tags, and more!<br/>
|
||||
- 🛜 Configure a proxy to access firewall-blocked APIs
|
||||
|
||||
## Useful Resources
|
||||
|
||||
- [Feedback and Bug Reports](https://feedback.yaak.app)
|
||||
@@ -34,4 +68,3 @@ visit [`DEVELOPMENT.md`](DEVELOPMENT.md) for tips on setting up your environment
|
||||
- [Yaak vs Postman](https://yaak.app/alternatives/postman)
|
||||
- [Yaak vs Bruno](https://yaak.app/alternatives/bruno)
|
||||
- [Yaak vs Insomnia](https://yaak.app/alternatives/insomnia)
|
||||
|
||||
|
||||
405
package-lock.json
generated
405
package-lock.json
generated
@@ -8,40 +8,44 @@
|
||||
"name": "yaak-app",
|
||||
"version": "0.0.0",
|
||||
"workspaces": [
|
||||
"packages/common-lib",
|
||||
"packages/plugin-runtime",
|
||||
"packages/plugin-runtime-types",
|
||||
"packages/common-lib",
|
||||
"plugins/action-copy-curl",
|
||||
"plugins/action-copy-grpcurl",
|
||||
"plugins/auth-apikey",
|
||||
"plugins/auth-aws",
|
||||
"plugins/auth-basic",
|
||||
"plugins/auth-bearer",
|
||||
"plugins/auth-jwt",
|
||||
"plugins/auth-oauth1",
|
||||
"plugins/auth-oauth2",
|
||||
"plugins/action-copy-curl",
|
||||
"plugins/action-copy-grpcurl",
|
||||
"plugins/filter-jsonpath",
|
||||
"plugins/filter-xpath",
|
||||
"plugins/importer-curl",
|
||||
"plugins/importer-insomnia",
|
||||
"plugins/importer-openapi",
|
||||
"plugins/importer-postman",
|
||||
"plugins/importer-postman-environment",
|
||||
"plugins/importer-yaak",
|
||||
"plugins/template-function-cookie",
|
||||
"plugins/template-function-timestamp",
|
||||
"plugins/template-function-encode",
|
||||
"plugins/template-function-fs",
|
||||
"plugins/template-function-hash",
|
||||
"plugins/template-function-json",
|
||||
"plugins/template-function-prompt",
|
||||
"plugins/template-function-random",
|
||||
"plugins/template-function-regex",
|
||||
"plugins/template-function-request",
|
||||
"plugins/template-function-response",
|
||||
"plugins/template-function-timestamp",
|
||||
"plugins/template-function-uuid",
|
||||
"plugins/template-function-xml",
|
||||
"plugins/themes-yaak",
|
||||
"src-tauri",
|
||||
"src-tauri/yaak-crypto",
|
||||
"src-tauri/yaak-git",
|
||||
"src-tauri/yaak-fonts",
|
||||
"src-tauri/yaak-git",
|
||||
"src-tauri/yaak-license",
|
||||
"src-tauri/yaak-mac-window",
|
||||
"src-tauri/yaak-models",
|
||||
@@ -59,7 +63,7 @@
|
||||
"@eslint/compat": "^1.3.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@tauri-apps/cli": "^2.8.4",
|
||||
"@tauri-apps/cli": "^2.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||
"@typescript-eslint/parser": "^8.27.0",
|
||||
"@yaakapp/cli": "^0.2.7",
|
||||
@@ -819,6 +823,45 @@
|
||||
"integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/utilities": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
|
||||
@@ -1929,24 +1972,6 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||
"integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-dnd/invariant": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
|
||||
"integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@react-dnd/shallowequal": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
|
||||
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@replit/codemirror-emacs": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
|
||||
@@ -2814,6 +2839,7 @@
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz",
|
||||
"integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.2.0"
|
||||
@@ -2833,9 +2859,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/history": {
|
||||
"version": "1.121.34",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.121.34.tgz",
|
||||
"integrity": "sha512-YL8dGi5ZU+xvtav2boRlw4zrRghkY6hvdcmHhA0RGSJ/CBgzv+cbADW9eYJLx74XMZvIQ1pp6VMbrpXnnM5gHA==",
|
||||
"version": "1.133.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.133.3.tgz",
|
||||
"integrity": "sha512-zFQnGdX0S4g5xRuS+95iiEXM+qlGvYG7ksmOKx7LaMv60lDWa0imR8/24WwXXvBWJT1KnwVdZcjvhCwz9IiJCw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -2846,9 +2872,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz",
|
||||
"integrity": "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==",
|
||||
"version": "5.90.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.5.tgz",
|
||||
"integrity": "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2856,12 +2882,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.83.0.tgz",
|
||||
"integrity": "sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ==",
|
||||
"version": "5.90.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.5.tgz",
|
||||
"integrity": "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.83.0"
|
||||
"@tanstack/query-core": "5.90.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2872,14 +2898,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-router": {
|
||||
"version": "1.127.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.127.3.tgz",
|
||||
"integrity": "sha512-QprmWHJrGbEKXJiP7WZ+dilTJRc7nMbsFCUnfAUw8PsOYanhgvBkBwAU05YEo8WTIZ9atCc1R90hyzqbiBFkdA==",
|
||||
"version": "1.133.13",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.133.13.tgz",
|
||||
"integrity": "sha512-mVAj70mPOH/a60Hjlha3gHEWLFuE4kHeKau/AL5Xp6e5GtNk1JTRwN4sJ9QlSyLcClOUUtGfED1FoLj0D2W0Eg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.121.34",
|
||||
"@tanstack/history": "1.133.3",
|
||||
"@tanstack/react-store": "^0.7.0",
|
||||
"@tanstack/router-core": "1.127.3",
|
||||
"@tanstack/router-core": "1.133.13",
|
||||
"isbot": "^5.1.22",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"tiny-warning": "^1.0.3"
|
||||
@@ -2932,14 +2958,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/router-core": {
|
||||
"version": "1.127.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.127.3.tgz",
|
||||
"integrity": "sha512-08JlfwsMIDkMyCQsRviMVBn0cVUzlNzkll4pZgf6QRSO1RASBsci5hMojcsdH0d/yXLH0FBJ6fINbj0ctBm63Q==",
|
||||
"version": "1.133.13",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.133.13.tgz",
|
||||
"integrity": "sha512-zZptdlS/wSkqozb07Y3zX5gas2OapJdjEG6/Id0e/twNefVdR4EY2TK/mgvyhHtKIpCxIcnZz/3opypgeQi9bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.121.34",
|
||||
"@tanstack/history": "1.133.3",
|
||||
"@tanstack/store": "^0.7.0",
|
||||
"cookie-es": "^1.2.2",
|
||||
"cookie-es": "^2.0.0",
|
||||
"seroval": "^1.3.2",
|
||||
"seroval-plugins": "^1.3.2",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
@@ -3088,9 +3114,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.8.0.tgz",
|
||||
"integrity": "sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==",
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.0.tgz",
|
||||
"integrity": "sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw==",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -3098,9 +3124,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.8.4.tgz",
|
||||
"integrity": "sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.9.1.tgz",
|
||||
"integrity": "sha512-kKi2/WWsNXKoMdatBl4xrT7e1Ce27JvsetBVfWuIb6D3ep/Y0WO5SIr70yarXOSWam8NyDur4ipzjZkg6m7VDg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
"bin": {
|
||||
@@ -3114,23 +3140,23 @@
|
||||
"url": "https://opencollective.com/tauri"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tauri-apps/cli-darwin-arm64": "2.8.4",
|
||||
"@tauri-apps/cli-darwin-x64": "2.8.4",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.8.4",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.8.4",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.8.4",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.8.4",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.8.4",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.8.4",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.8.4",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.8.4",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.8.4"
|
||||
"@tauri-apps/cli-darwin-arm64": "2.9.1",
|
||||
"@tauri-apps/cli-darwin-x64": "2.9.1",
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.9.1",
|
||||
"@tauri-apps/cli-linux-arm64-gnu": "2.9.1",
|
||||
"@tauri-apps/cli-linux-arm64-musl": "2.9.1",
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": "2.9.1",
|
||||
"@tauri-apps/cli-linux-x64-gnu": "2.9.1",
|
||||
"@tauri-apps/cli-linux-x64-musl": "2.9.1",
|
||||
"@tauri-apps/cli-win32-arm64-msvc": "2.9.1",
|
||||
"@tauri-apps/cli-win32-ia32-msvc": "2.9.1",
|
||||
"@tauri-apps/cli-win32-x64-msvc": "2.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.8.4.tgz",
|
||||
"integrity": "sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.1.tgz",
|
||||
"integrity": "sha512-sdwhtsE/6njD0AjgfYEj1JyxZH4SBmCJSXpRm6Ph5fQeuZD6MyjzjdVOrrtFguyREVQ7xn0Ujkwvbo01ULthNg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3145,9 +3171,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-darwin-x64": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.8.4.tgz",
|
||||
"integrity": "sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.1.tgz",
|
||||
"integrity": "sha512-c86g+67wTdI4TUCD7CaSd/13+oYuLQxVST4ZNJ5C+6i1kdnU3Us1L68N9MvbDLDQGJc9eo0pvuK6sCWkee+BzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3162,9 +3188,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.8.4.tgz",
|
||||
"integrity": "sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.1.tgz",
|
||||
"integrity": "sha512-IrB3gFQmueQKJjjisOcMktW/Gh6gxgqYO419doA3YZ7yIV5rbE8ZW52Q3I4AO+SlFEyVYer5kpi066p0JBlLGw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -3179,9 +3205,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.8.4.tgz",
|
||||
"integrity": "sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.1.tgz",
|
||||
"integrity": "sha512-Ke7TyXvu6HbWSkmVkFbbH19D3cLsd117YtXP/u9NIvSpYwKeFtnbpirrIUfPm44Q+PZFZ2Hvg8X9qoUiAK0zKw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3196,9 +3222,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.8.4.tgz",
|
||||
"integrity": "sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.1.tgz",
|
||||
"integrity": "sha512-sGvy75sv55oeMulR5ArwPD28DsDQxqTzLhXCrpU9/nbFg/JImmI7k994YE9fr3V0qE3Cjk5gjLldRNv7I9sjwQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3213,9 +3239,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.8.4.tgz",
|
||||
"integrity": "sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.1.tgz",
|
||||
"integrity": "sha512-tEKbJydV3BdIxpAx8aGHW6VDg1xW4LlQuRD/QeFZdZNTreHJpMbJEcdvAcI+Hg6vgQpVpaoEldR9W4F6dYSLqQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -3230,9 +3256,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.8.4.tgz",
|
||||
"integrity": "sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.1.tgz",
|
||||
"integrity": "sha512-mg5msXHagtHpyCVWgI01M26JeSrgE/otWyGdYcuTwyRYZYEJRTbcNt7hscOkdNlPBe7isScW7PVKbxmAjJJl4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3247,9 +3273,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.8.4.tgz",
|
||||
"integrity": "sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.1.tgz",
|
||||
"integrity": "sha512-lFZEXkpDreUe3zKilvnMsrnKP9gwQudaEjDnOz/GMzbzNceIuPfFZz0cR/ky1Aoq4eSvZonPKHhROq4owz4fzg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3264,9 +3290,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.8.4.tgz",
|
||||
"integrity": "sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.1.tgz",
|
||||
"integrity": "sha512-ejc5RAp/Lm1Aj0EQHaT+Wdt5PHfdgQV5hIDV00MV6HNbIb5W4ZUFxMDaRkAg65gl9MvY2fH396riePW3RoKXDw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3281,9 +3307,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.8.4.tgz",
|
||||
"integrity": "sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.1.tgz",
|
||||
"integrity": "sha512-fSATtJDc0fNjVB6ystyi8NbwhNFk8i8E05h6KrsC8Fio5eaJIJvPCbC9pdrPl6kkxN1X7fj25ErBbgfqgcK8Fg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -3298,9 +3324,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.8.4.tgz",
|
||||
"integrity": "sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==",
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.1.tgz",
|
||||
"integrity": "sha512-/JHlOzpUDhjBOO9w167bcYxfJbcMQv7ykS/Y07xjtcga8np0rzUzVGWYmLMH7orKcDMC7wjhheEW1x8cbGma/Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3377,6 +3403,16 @@
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/aws4": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/aws4/-/aws4-1.11.6.tgz",
|
||||
"integrity": "sha512-5CnVUkHNyLGpD9AnOcK66YyP0qvIh6nhJJoeK8zSl5YKikUcUbdB7SlHevUYVqicgeh6j5AJa1qa/h08dSZHoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@@ -4112,6 +4148,10 @@
|
||||
"resolved": "plugins/auth-apikey",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/auth-aws": {
|
||||
"resolved": "plugins/auth-aws",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/auth-basic": {
|
||||
"resolved": "plugins/auth-basic",
|
||||
"link": true
|
||||
@@ -4124,6 +4164,10 @@
|
||||
"resolved": "plugins/auth-jwt",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/auth-oauth1": {
|
||||
"resolved": "plugins/auth-oauth1",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/auth-oauth2": {
|
||||
"resolved": "plugins/auth-oauth2",
|
||||
"link": true
|
||||
@@ -4152,6 +4196,10 @@
|
||||
"resolved": "plugins/importer-postman",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/importer-postman-environment": {
|
||||
"resolved": "plugins/importer-postman-environment",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/importer-yaak": {
|
||||
"resolved": "plugins/importer-yaak",
|
||||
"link": true
|
||||
@@ -4180,6 +4228,10 @@
|
||||
"resolved": "plugins/template-function-prompt",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/template-function-random": {
|
||||
"resolved": "plugins/template-function-random",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@yaak/template-function-regex": {
|
||||
"resolved": "plugins/template-function-regex",
|
||||
"link": true
|
||||
@@ -4887,6 +4939,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/aws4": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
|
||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axe-core": {
|
||||
"version": "4.10.3",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
|
||||
@@ -5855,9 +5913,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cookie-es": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz",
|
||||
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz",
|
||||
"integrity": "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/copy-descriptor": {
|
||||
@@ -6647,17 +6705,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dnd-core": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
|
||||
"integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-dnd/asap": "^5.0.1",
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"redux": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
|
||||
@@ -9213,15 +9260,6 @@
|
||||
"@babel/runtime": "^7.7.6"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
@@ -13049,6 +13087,12 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/oauth-1.0a": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/oauth-1.0a/-/oauth-1.0a-2.2.6.tgz",
|
||||
"integrity": "sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -14361,45 +14405,6 @@
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"@react-dnd/shallowequal": "^4.0.1",
|
||||
"dnd-core": "^16.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"hoist-non-react-statics": "^3.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/hoist-non-react-statics": ">= 3.3.1",
|
||||
"@types/node": ">= 12",
|
||||
"@types/react": ">= 16",
|
||||
"react": ">= 16.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/hoist-non-react-statics": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd-html5-backend": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
|
||||
"integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dnd-core": "^16.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
@@ -14418,6 +14423,7 @@
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-markdown": {
|
||||
@@ -14786,15 +14792,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||
@@ -16846,6 +16843,7 @@
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
|
||||
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
@@ -17875,9 +17873,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.7.tgz",
|
||||
"integrity": "sha512-hc6LujN/EkJHmxeiDJMs0qBontZ1cdBvvoCbWhVjzUFTU329VRyOC46gHNSA8NcOC5yzCeXpwI40tieI3DEZqg==",
|
||||
"version": "7.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.8.tgz",
|
||||
"integrity": "sha512-cJBdq0/u+8rgstg9t7UkBilf8ipLmeXJO30NxD5HAHOivnj10ocV8YtR/XBvd2wQpN3TmcaxNKaHX3tN7o5F5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -18149,6 +18147,12 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/vkbeautify": {
|
||||
"version": "0.99.3",
|
||||
"resolved": "https://registry.npmjs.org/vkbeautify/-/vkbeautify-0.99.3.tgz",
|
||||
"integrity": "sha512-2ozZEFfmVvQcHWoHLNuiKlUfDKlhh4KGsy54U0UrlLMR1SO+XKAIDqBxtBwHgNrekurlJwE8A9K6L49T78ZQ9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vscode-languageserver-types": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||
@@ -18543,27 +18547,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml-formatter": {
|
||||
"version": "3.6.6",
|
||||
"resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-3.6.6.tgz",
|
||||
"integrity": "sha512-yfofQht42x2sN1YThT6Er6GFXiQinfDAsMTNvMPi2uZw5/Vtc2PYHfvALR8U+b2oN2ekBxLd2tGWV06rAM8nQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xml-parser-xo": "^4.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-parser-xo": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-4.1.4.tgz",
|
||||
"integrity": "sha512-wo+yWDNeMwd1ctzH4CsiGXaAappDsxuR+VnmPewOzHk/zvefksT2ZlcWpAePl11THOWgnIZM4GjvumevurNWZw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/xpath": {
|
||||
"version": "0.0.34",
|
||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz",
|
||||
@@ -18803,7 +18786,7 @@
|
||||
},
|
||||
"packages/plugin-runtime-types": {
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.6.6",
|
||||
"version": "0.7.0",
|
||||
"dependencies": {
|
||||
"@types/node": "^24.0.13"
|
||||
},
|
||||
@@ -18823,6 +18806,16 @@
|
||||
"name": "@yaak/auth-apikey",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"plugins/auth-aws": {
|
||||
"name": "@yaak/auth-aws",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"aws4": "^1.13.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws4": "^1.11.6"
|
||||
}
|
||||
},
|
||||
"plugins/auth-basic": {
|
||||
"name": "@yaak/auth-basic",
|
||||
"version": "0.1.0"
|
||||
@@ -18841,6 +18834,13 @@
|
||||
"@types/jsonwebtoken": "^9.0.7"
|
||||
}
|
||||
},
|
||||
"plugins/auth-oauth1": {
|
||||
"name": "@yaak/auth-oauth1",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"oauth-1.0a": "^2.2.6"
|
||||
}
|
||||
},
|
||||
"plugins/auth-oauth2": {
|
||||
"name": "@yaak/auth-oauth2",
|
||||
"version": "0.1.0"
|
||||
@@ -18895,6 +18895,10 @@
|
||||
"name": "@yaak/importer-postman",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"plugins/importer-postman-environment": {
|
||||
"name": "@yaak/importer-postman-environment",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"plugins/importer-yaak": {
|
||||
"name": "@yaak/importer-yaak",
|
||||
"version": "0.1.0"
|
||||
@@ -18936,6 +18940,10 @@
|
||||
"name": "@yaak/template-function-prompt",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"plugins/template-function-random": {
|
||||
"name": "@yaak/template-function-random",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"plugins/template-function-regex": {
|
||||
"name": "@yaak/template-function-regex",
|
||||
"version": "0.1.0"
|
||||
@@ -19058,17 +19066,17 @@
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/language": "^6.11.0",
|
||||
"@codemirror/search": "^6.5.11",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@gilbarbara/deep-equal": "^0.3.1",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
"@lezer/lr": "^1.3.3",
|
||||
"@replit/codemirror-emacs": "^6.1.0",
|
||||
"@replit/codemirror-vim": "^6.3.0",
|
||||
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tanstack/react-query": "^5.76.1",
|
||||
"@tanstack/react-router": "^1.120.3",
|
||||
"@tanstack/react-virtual": "^3.13.8",
|
||||
"@tauri-apps/api": "^2.8.0",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"@tanstack/react-router": "^1.133.13",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tauri-apps/api": "^2.9.0",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
||||
@@ -19097,8 +19105,6 @@
|
||||
"parse-color": "^1.0.0",
|
||||
"react": "^19.1.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-pdf": "^10.0.1",
|
||||
@@ -19109,12 +19115,13 @@
|
||||
"remark-gfm": "^4.0.1",
|
||||
"slugify": "^1.6.6",
|
||||
"uuid": "^11.1.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"whatwg-mimetype": "^4.0.0",
|
||||
"xml-formatter": "^3.6.3",
|
||||
"yaml": "^2.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "^1.8.0",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
|
||||
"@tanstack/router-plugin": "^1.127.5",
|
||||
"@types/node": "^24.0.13",
|
||||
@@ -19133,7 +19140,7 @@
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-nesting": "^13.0.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"vite": "^7.0.7",
|
||||
"vite": "^7.0.8",
|
||||
"vite-plugin-static-copy": "^3.1.2",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vite-plugin-top-level-await": "^1.5.0",
|
||||
|
||||
20
package.json
20
package.json
@@ -7,40 +7,44 @@
|
||||
"url": "git+https://github.com/mountain-loop/yaak.git"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/common-lib",
|
||||
"packages/plugin-runtime",
|
||||
"packages/plugin-runtime-types",
|
||||
"packages/common-lib",
|
||||
"plugins/action-copy-curl",
|
||||
"plugins/action-copy-grpcurl",
|
||||
"plugins/auth-apikey",
|
||||
"plugins/auth-aws",
|
||||
"plugins/auth-basic",
|
||||
"plugins/auth-bearer",
|
||||
"plugins/auth-jwt",
|
||||
"plugins/auth-oauth2",
|
||||
"plugins/action-copy-curl",
|
||||
"plugins/action-copy-grpcurl",
|
||||
"plugins/auth-oauth1",
|
||||
"plugins/filter-jsonpath",
|
||||
"plugins/filter-xpath",
|
||||
"plugins/importer-curl",
|
||||
"plugins/importer-insomnia",
|
||||
"plugins/importer-openapi",
|
||||
"plugins/importer-postman",
|
||||
"plugins/importer-postman-environment",
|
||||
"plugins/importer-yaak",
|
||||
"plugins/template-function-cookie",
|
||||
"plugins/template-function-timestamp",
|
||||
"plugins/template-function-encode",
|
||||
"plugins/template-function-fs",
|
||||
"plugins/template-function-hash",
|
||||
"plugins/template-function-json",
|
||||
"plugins/template-function-prompt",
|
||||
"plugins/template-function-random",
|
||||
"plugins/template-function-regex",
|
||||
"plugins/template-function-request",
|
||||
"plugins/template-function-response",
|
||||
"plugins/template-function-timestamp",
|
||||
"plugins/template-function-uuid",
|
||||
"plugins/template-function-xml",
|
||||
"plugins/themes-yaak",
|
||||
"src-tauri",
|
||||
"src-tauri/yaak-crypto",
|
||||
"src-tauri/yaak-git",
|
||||
"src-tauri/yaak-fonts",
|
||||
"src-tauri/yaak-git",
|
||||
"src-tauri/yaak-license",
|
||||
"src-tauri/yaak-mac-window",
|
||||
"src-tauri/yaak-models",
|
||||
@@ -60,8 +64,8 @@
|
||||
"build-plugins": "npm run --workspaces --if-present build",
|
||||
"test": "npm run --workspaces --if-present test",
|
||||
"icons": "run-p icons:*",
|
||||
"icons:dev": "tauri icon src-tauri/icons/icon.png --output src-tauri/icons/release",
|
||||
"icons:release": "tauri icon src-tauri/icons/icon-dev.png --output src-tauri/icons/dev",
|
||||
"icons:dev": "tauri icon src-tauri/icons/icon-dev.png --output src-tauri/icons/dev",
|
||||
"icons:release": "tauri icon src-tauri/icons/icon.png --output src-tauri/icons/release",
|
||||
"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",
|
||||
@@ -79,7 +83,7 @@
|
||||
"@eslint/compat": "^1.3.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@tauri-apps/cli": "^2.8.4",
|
||||
"@tauri-apps/cli": "^2.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||
"@typescript-eslint/parser": "^8.27.0",
|
||||
"@yaakapp/cli": "^0.2.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.6.6",
|
||||
"version": "0.7.0",
|
||||
"keywords": [
|
||||
"api-client",
|
||||
"insomnia-alternative",
|
||||
|
||||
@@ -361,7 +361,11 @@ export type GetKeyValueRequest = { key: string, };
|
||||
|
||||
export type GetKeyValueResponse = { value?: string, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
export type GetTemplateFunctionConfigRequest = { contextId: string, name: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type GetTemplateFunctionConfigResponse = { function: TemplateFunction, pluginRefId: string, };
|
||||
|
||||
export type GetTemplateFunctionSummaryResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type GetThemesRequest = Record<string, never>;
|
||||
|
||||
@@ -385,7 +389,7 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: PluginWindowContext, payload: InternalEventPayload, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type JsonPrimitive = string | number | boolean | null;
|
||||
|
||||
@@ -437,7 +441,7 @@ export type SetKeyValueRequest = { key: string, value: string, };
|
||||
|
||||
export type SetKeyValueResponse = {};
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, timeout?: number, };
|
||||
|
||||
export type TemplateFunction = { name: string, description?: string,
|
||||
/**
|
||||
|
||||
@@ -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 Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import {
|
||||
CallTemplateFunctionArgs,
|
||||
FormInput,
|
||||
GetHttpAuthenticationConfigRequest,
|
||||
TemplateFunction,
|
||||
} from "../bindings/gen_events";
|
||||
import { Context } from "./Context";
|
||||
TemplateFunctionArg,
|
||||
} from '../bindings/gen_events';
|
||||
import { MaybePromise } from '../helpers';
|
||||
import { Context } from './Context';
|
||||
|
||||
export type DynamicTemplateFunctionArg = FormInput & {
|
||||
dynamic(
|
||||
ctx: Context,
|
||||
args: GetHttpAuthenticationConfigRequest,
|
||||
): MaybePromise<Partial<FormInput> | undefined | null>;
|
||||
};
|
||||
|
||||
export type TemplateFunctionPlugin = TemplateFunction & {
|
||||
onRender(
|
||||
ctx: Context,
|
||||
args: CallTemplateFunctionArgs,
|
||||
): Promise<string | null>;
|
||||
args: (TemplateFunctionArg | DynamicTemplateFunctionArg)[];
|
||||
onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null>;
|
||||
};
|
||||
|
||||
@@ -186,32 +186,65 @@ export class PluginInstance {
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_template_functions_request' &&
|
||||
payload.type === 'get_template_function_summary_request' &&
|
||||
Array.isArray(this.#mod?.templateFunctions)
|
||||
) {
|
||||
const reply: TemplateFunction[] = this.#mod.templateFunctions.map((templateFunction) => {
|
||||
return {
|
||||
...migrateTemplateFunctionSelectOptions(templateFunction),
|
||||
// Add everything except render
|
||||
onRender: undefined,
|
||||
};
|
||||
});
|
||||
const functions: TemplateFunction[] = this.#mod.templateFunctions.map(
|
||||
(templateFunction) => {
|
||||
return {
|
||||
...migrateTemplateFunctionSelectOptions(templateFunction),
|
||||
// Add everything except render
|
||||
onRender: undefined,
|
||||
};
|
||||
},
|
||||
);
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_template_functions_response',
|
||||
type: 'get_template_function_summary_response',
|
||||
pluginRefId: this.#workerData.pluginRefId,
|
||||
functions: reply,
|
||||
functions,
|
||||
};
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_template_function_config_request' &&
|
||||
Array.isArray(this.#mod?.templateFunctions)
|
||||
) {
|
||||
let templateFunction = this.#mod.templateFunctions.find((f) => f.name === payload.name);
|
||||
if (templateFunction == null) {
|
||||
this.#sendEmpty(windowContext, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
templateFunction = migrateTemplateFunctionSelectOptions(templateFunction);
|
||||
// @ts-ignore
|
||||
delete templateFunction.onRender;
|
||||
const resolvedArgs: TemplateFunctionArg[] = [];
|
||||
for (const arg of templateFunction.args) {
|
||||
if (arg && 'dynamic' in arg) {
|
||||
const dynamicAttrs = await arg.dynamic(ctx, payload);
|
||||
const { dynamic, ...other } = arg;
|
||||
resolvedArgs.push({ ...other, ...dynamicAttrs } as TemplateFunctionArg);
|
||||
} else if (arg) {
|
||||
resolvedArgs.push(arg);
|
||||
}
|
||||
templateFunction.args = resolvedArgs;
|
||||
}
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_template_function_config_response',
|
||||
pluginRefId: this.#workerData.pluginRefId,
|
||||
function: templateFunction,
|
||||
};
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.type === 'get_http_authentication_summary_request' && this.#mod?.authentication) {
|
||||
const { name, shortLabel, label } = this.#mod.authentication;
|
||||
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_http_authentication_summary_response',
|
||||
name,
|
||||
label,
|
||||
shortLabel,
|
||||
...this.#mod.authentication,
|
||||
};
|
||||
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
@@ -454,6 +487,8 @@ export class PluginInstance {
|
||||
show: async (args) => {
|
||||
await this.#sendAndWaitForReply(windowContext, {
|
||||
type: 'show_toast_request',
|
||||
// Handle default here because null/undefined both convert to None in Rust translation
|
||||
timeout: args.timeout === undefined ? 5000 : args.timeout,
|
||||
...args,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -8,10 +8,15 @@ if (!port) {
|
||||
throw new Error('Plugin runtime missing PORT')
|
||||
}
|
||||
|
||||
const host = process.env.HOST;
|
||||
if (!host) {
|
||||
throw new Error('Plugin runtime missing HOST')
|
||||
}
|
||||
|
||||
const pluginToAppEvents = new EventChannel();
|
||||
const plugins: Record<string, PluginHandle> = {};
|
||||
|
||||
const ws = new WebSocket(`ws://localhost:${port}`);
|
||||
const ws = new WebSocket(`ws://${host}:${port}`);
|
||||
|
||||
ws.on('message', async (e: Buffer) => {
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { TemplateFunction } from '@yaakapp/api';
|
||||
import { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||
|
||||
export function migrateTemplateFunctionSelectOptions(f: TemplateFunction): TemplateFunction {
|
||||
export function migrateTemplateFunctionSelectOptions(
|
||||
f: TemplateFunctionPlugin,
|
||||
): TemplateFunctionPlugin {
|
||||
const migratedArgs = f.args.map((a) => {
|
||||
if (a.type === 'select') {
|
||||
a.options = a.options.map((o) => ({
|
||||
|
||||
@@ -43,6 +43,26 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
finalUrl = base + separator + queryString + (hash ? `#${hash}` : '');
|
||||
}
|
||||
|
||||
// Add API key authentication
|
||||
if (request.authenticationType === 'apikey') {
|
||||
if (request.authentication?.location === 'query') {
|
||||
const sep = finalUrl.includes('?') ? '&' : '?';
|
||||
finalUrl = [
|
||||
finalUrl,
|
||||
sep,
|
||||
encodeURIComponent(request.authentication?.key ?? 'token'),
|
||||
'=',
|
||||
encodeURIComponent(request.authentication?.value ?? ''),
|
||||
].join('');
|
||||
} else {
|
||||
request.headers = request.headers ?? [];
|
||||
request.headers.push({
|
||||
name: request.authentication?.key ?? 'X-Api-Key',
|
||||
value: request.authentication?.value ?? '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
xs.push(quote(finalUrl));
|
||||
xs.push(NEWLINE);
|
||||
|
||||
@@ -82,21 +102,49 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
}
|
||||
|
||||
// Add basic/digest authentication
|
||||
if (request.authenticationType === 'basic' || request.authenticationType === 'digest') {
|
||||
if (request.authenticationType === 'digest') xs.push('--digest');
|
||||
xs.push(
|
||||
'--user',
|
||||
quote(`${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`),
|
||||
);
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
if (request.authentication?.disabled !== true) {
|
||||
if (request.authenticationType === 'basic' || request.authenticationType === 'digest') {
|
||||
if (request.authenticationType === 'digest') xs.push('--digest');
|
||||
xs.push(
|
||||
'--user',
|
||||
quote(
|
||||
`${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`,
|
||||
),
|
||||
);
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
// Add bearer authentication
|
||||
if (request.authenticationType === 'bearer') {
|
||||
const value =
|
||||
`${request.authentication?.prefix ?? 'Bearer'} ${request.authentication?.token ?? ''}`.trim();
|
||||
xs.push('--header', quote(`Authorization: ${value}`));
|
||||
xs.push(NEWLINE);
|
||||
// Add bearer authentication
|
||||
if (request.authenticationType === 'bearer') {
|
||||
const value =
|
||||
`${request.authentication?.prefix ?? 'Bearer'} ${request.authentication?.token ?? ''}`.trim();
|
||||
xs.push('--header', quote(`Authorization: ${value}`));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
if (request.authenticationType === 'auth-aws-sig-v4') {
|
||||
xs.push(
|
||||
'--aws-sigv4',
|
||||
[
|
||||
'aws',
|
||||
'amz',
|
||||
request.authentication?.region ?? '',
|
||||
request.authentication?.service ?? '',
|
||||
].join(':'),
|
||||
);
|
||||
xs.push(NEWLINE);
|
||||
xs.push(
|
||||
'--user',
|
||||
quote(
|
||||
`${request.authentication?.accessKeyId ?? ''}:${request.authentication?.secretAccessKey ?? ''}`,
|
||||
),
|
||||
);
|
||||
if (request.authentication?.sessionToken) {
|
||||
xs.push(NEWLINE);
|
||||
xs.push('--header', quote(`X-Amz-Security-Token: ${request.authentication.sessionToken}`));
|
||||
}
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove trailing newline
|
||||
|
||||
@@ -27,6 +27,7 @@ describe('exporter-curl', () => {
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app/path?a=aaa&b=bbb#section'`].join(` \\n `));
|
||||
});
|
||||
|
||||
test('Exports POST with url form data', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
@@ -170,6 +171,20 @@ describe('exporter-curl', () => {
|
||||
).toEqual([`curl 'https://yaak.app'`, `--user 'user:pass'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Basic auth disabled', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'basic',
|
||||
authentication: {
|
||||
disabled: true,
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Broken basic auth', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
@@ -246,6 +261,145 @@ describe('exporter-curl', () => {
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('AWS v4 auth', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'auth-aws-sig-v4',
|
||||
authentication: {
|
||||
accessKeyId: 'ak',
|
||||
secretAccessKey: 'sk',
|
||||
sessionToken: '',
|
||||
region: 'us-east-1',
|
||||
service: 's3',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[`curl 'https://yaak.app'`, `--aws-sigv4 aws:amz:us-east-1:s3`, `--user 'ak:sk'`].join(
|
||||
` \\\n `,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('AWS v4 auth with session', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'auth-aws-sig-v4',
|
||||
authentication: {
|
||||
accessKeyId: 'ak',
|
||||
secretAccessKey: 'sk',
|
||||
sessionToken: 'st',
|
||||
region: 'us-east-1',
|
||||
service: 's3',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
[
|
||||
`curl 'https://yaak.app'`,
|
||||
`--aws-sigv4 aws:amz:us-east-1:s3`,
|
||||
`--user 'ak:sk'`,
|
||||
`--header 'X-Amz-Security-Token: st'`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
|
||||
test('API key auth header', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'header',
|
||||
key: 'X-Header',
|
||||
value: 'my-token',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'X-Header: my-token'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth header query', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app?hi=there',
|
||||
urlParameters: [{ name: 'param', value: 'hi' }],
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'foo',
|
||||
value: 'bar',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?hi=there¶m=hi&foo=bar'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth header query with params', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
urlParameters: [{ name: 'param', value: 'hi' }],
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'foo',
|
||||
value: 'bar',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?param=hi&foo=bar'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth header default', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'header',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app'`, `--header 'X-Api-Key: '`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth query', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'foo',
|
||||
value: 'bar-baz',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?foo=bar-baz'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth query with existing', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app?foo=bar&baz=qux',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'hi',
|
||||
value: 'there',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&hi=there'`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth query default', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
url: 'https://yaak.app?foo=bar&baz=qux',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
},
|
||||
}),
|
||||
).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&token='`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Stale body data', async () => {
|
||||
expect(
|
||||
await convertToCurl({
|
||||
|
||||
@@ -68,16 +68,37 @@ export async function convert(request: Partial<GrpcRequest>, allProtoFiles: stri
|
||||
}
|
||||
|
||||
// Add basic authentication
|
||||
if (request.authenticationType === 'basic') {
|
||||
const user = request.authentication?.username ?? '';
|
||||
const pass = request.authentication?.password ?? '';
|
||||
const encoded = btoa(`${user}:${pass}`);
|
||||
xs.push('-H', quote(`Authorization: Basic ${encoded}`));
|
||||
xs.push(NEWLINE);
|
||||
} else if (request.authenticationType === 'bearer') {
|
||||
// Add bearer authentication
|
||||
xs.push('-H', quote(`Authorization: Bearer ${request.authentication?.token ?? ''}`));
|
||||
xs.push(NEWLINE);
|
||||
if (request.authentication?.disabled !== true) {
|
||||
if (request.authenticationType === 'basic') {
|
||||
const user = request.authentication?.username ?? '';
|
||||
const pass = request.authentication?.password ?? '';
|
||||
const encoded = btoa(`${user}:${pass}`);
|
||||
xs.push('-H', quote(`Authorization: Basic ${encoded}`));
|
||||
xs.push(NEWLINE);
|
||||
} else if (request.authenticationType === 'bearer') {
|
||||
// Add bearer authentication
|
||||
xs.push('-H', quote(`Authorization: Bearer ${request.authentication?.token ?? ''}`));
|
||||
xs.push(NEWLINE);
|
||||
} else if (request.authenticationType === 'apikey') {
|
||||
if (request.authentication?.location === 'query') {
|
||||
const sep = request.url?.includes('?') ? '&' : '?';
|
||||
request.url = [
|
||||
request.url,
|
||||
sep,
|
||||
encodeURIComponent(request.authentication?.key ?? 'token'),
|
||||
'=',
|
||||
encodeURIComponent(request.authentication?.value ?? ''),
|
||||
].join('');
|
||||
} else {
|
||||
xs.push(
|
||||
'-H',
|
||||
quote(
|
||||
`${request.authentication?.key ?? 'X-Api-Key'}: ${request.authentication?.value ?? ''}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
// Add form params
|
||||
|
||||
@@ -27,6 +27,55 @@ describe('exporter-curl', () => {
|
||||
),
|
||||
).toEqual([`grpcurl -H 'aaa: AAA'`, `-H 'bbb: BBB'`, `yaak.app`].join(` \\\n `));
|
||||
});
|
||||
test('Basic auth', async () => {
|
||||
expect(
|
||||
await convert(
|
||||
{
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'basic',
|
||||
authentication: {
|
||||
username: 'user',
|
||||
password: 'pass',
|
||||
},
|
||||
},
|
||||
[],
|
||||
),
|
||||
).toEqual([`grpcurl -H 'Authorization: Basic dXNlcjpwYXNz'`, `yaak.app`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth', async () => {
|
||||
expect(
|
||||
await convert(
|
||||
{
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
key: 'X-Token',
|
||||
value: 'tok',
|
||||
},
|
||||
},
|
||||
[],
|
||||
),
|
||||
).toEqual([`grpcurl -H 'X-Token: tok'`, `yaak.app`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('API key auth', async () => {
|
||||
expect(
|
||||
await convert(
|
||||
{
|
||||
url: 'https://yaak.app',
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: 'query',
|
||||
key: 'token',
|
||||
value: 'tok 1',
|
||||
},
|
||||
},
|
||||
[],
|
||||
),
|
||||
).toEqual([`grpcurl`, `yaak.app?token=tok%201`].join(` \\\n `));
|
||||
});
|
||||
|
||||
test('Single proto file', async () => {
|
||||
expect(await convert({ url: 'https://yaak.app' }, ['/foo/bar/baz.proto'])).toEqual(
|
||||
[
|
||||
|
||||
49
plugins/auth-aws/README.md
Normal file
49
plugins/auth-aws/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# AWS Signature Version 4 Auth
|
||||
|
||||
A plugin for authenticating AWS-compatible requests using the
|
||||
[AWS Signature Version 4 signing process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html).
|
||||
This enables secure, signed requests to AWS services (or any S3-compatible APIs like
|
||||
Cloudflare R2).
|
||||
|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
This plugin provides AWS Signature authentication for API requests in Yaak. SigV4 is used
|
||||
by nearly all AWS APIs to verify the authenticity and integrity of requests using
|
||||
cryptographic signatures.
|
||||
|
||||
With this plugin, you can securely sign requests to AWS services such as S3, STS, Lambda,
|
||||
API Gateway, DynamoDB, and more. You can also authenticate against S3-compatible services
|
||||
like **Cloudflare R2**, **MinIO**, or **Wasabi**.
|
||||
|
||||
## How AWS Signature Version 4 Works
|
||||
|
||||
SigV4 signs requests by creating a hash of key request components (method, URL, headers,
|
||||
and optionally the payload) using your AWS credentials. The resulting HMAC signature is
|
||||
added in the `Authorization` header along with credential scope metadata.
|
||||
|
||||
Example header:
|
||||
|
||||
```
|
||||
Authorization: AWS4-HMAC-SHA256 Credential=AKIA…/20251011/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=abcdef123456…
|
||||
```
|
||||
|
||||
Each request must include a timestamp (`X-Amz-Date`) and may include a session token if
|
||||
using temporary credentials.
|
||||
|
||||
## Configuration
|
||||
|
||||
The plugin presents the following fields:
|
||||
|
||||
- **Access Key ID** – Your AWS access key identifier
|
||||
- **Secret Access Key** – The secret associated with the access key
|
||||
- **Session Token** *(optional)* – Used for temporary or assumed-role credentials (treated as secret)
|
||||
- **Region** – AWS region (e.g., `us-east-1`)
|
||||
- **Service** – AWS service identifier (e.g., `sts`, `s3`, `execute-api`)
|
||||
|
||||
## Usage
|
||||
|
||||
1. Configure a request, folder, or workspace to use **AWS SigV4 Authentication**
|
||||
2. Enter your AWS credentials and target service/region
|
||||
3. The plugin will automatically sign outgoing requests with valid SigV4 headers
|
||||
23
plugins/auth-aws/package.json
Normal file
23
plugins/auth-aws/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@yaak/auth-aws",
|
||||
"displayName": "AWS SigV4",
|
||||
"description": "Authenticate requests using AWS SigV4 signing",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mountain-loop/yaak.git",
|
||||
"directory": "plugins/auth-aws"
|
||||
},
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"aws4": "^1.13.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws4": "^1.11.6"
|
||||
}
|
||||
}
|
||||
BIN
plugins/auth-aws/screenshot.png
Normal file
BIN
plugins/auth-aws/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 790 KiB |
96
plugins/auth-aws/src/index.ts
Normal file
96
plugins/auth-aws/src/index.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { CallHttpAuthenticationResponse } from '@yaakapp-internal/plugins';
|
||||
import type { PluginDefinition } from '@yaakapp/api';
|
||||
import aws4 from 'aws4';
|
||||
import type { Request } from 'aws4';
|
||||
import { URL } from 'node:url';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
authentication: {
|
||||
name: 'awsv4',
|
||||
label: 'AWS Signature',
|
||||
shortLabel: 'AWS v4',
|
||||
args: [
|
||||
{ name: 'accessKeyId', label: 'Access Key ID', type: 'text', password: true },
|
||||
{
|
||||
name: 'secretAccessKey',
|
||||
label: 'Secret Access Key',
|
||||
type: 'text',
|
||||
password: true,
|
||||
},
|
||||
{
|
||||
name: 'service',
|
||||
label: 'Service Name',
|
||||
type: 'text',
|
||||
defaultValue: 'sts',
|
||||
placeholder: 'sts',
|
||||
description: 'The service that is receiving the request (sts, s3, sqs, ...)',
|
||||
},
|
||||
{
|
||||
name: 'region',
|
||||
label: 'Region',
|
||||
type: 'text',
|
||||
placeholder: 'us-east-1',
|
||||
description: 'The region that is receiving the request (defaults to us-east-1)',
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
name: 'sessionToken',
|
||||
label: 'Session Token',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
description: 'Only required if you are using temporary credentials',
|
||||
},
|
||||
],
|
||||
onApply(_ctx, { values, ...args }): CallHttpAuthenticationResponse {
|
||||
const accessKeyId = String(values.accessKeyId || '');
|
||||
const secretAccessKey = String(values.secretAccessKey || '');
|
||||
const sessionToken = String(values.sessionToken || '') || undefined;
|
||||
|
||||
const url = new URL(args.url);
|
||||
|
||||
const headers: NonNullable<Request['headers']> = {};
|
||||
for (const headerName of ['content-type', 'host', 'x-amz-date', 'x-amz-security-token']) {
|
||||
const v = args.headers.find((h) => h.name.toLowerCase() === headerName);
|
||||
if (v != null) {
|
||||
headers[headerName] = v.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.method !== 'GET') {
|
||||
headers['x-amz-content-sha256'] = 'UNSIGNED-PAYLOAD';
|
||||
}
|
||||
|
||||
const signature = aws4.sign(
|
||||
{
|
||||
host: url.host,
|
||||
method: args.method,
|
||||
path: url.pathname + (url.search || ''),
|
||||
service: String(values.service || 'sts'),
|
||||
region: values.region ? String(values.region) : undefined,
|
||||
headers,
|
||||
},
|
||||
{
|
||||
accessKeyId,
|
||||
secretAccessKey,
|
||||
sessionToken,
|
||||
},
|
||||
);
|
||||
|
||||
// After signing, aws4 will set:
|
||||
// - opts.headers["Authorization"]
|
||||
// - opts.headers["X-Amz-Date"]
|
||||
// - optionally content sha256 header etc
|
||||
|
||||
if (signature.headers == null) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
setHeaders: Object.entries(signature.headers)
|
||||
.filter(([name]) => name !== 'content-type') // Don't add this because we already have it
|
||||
.map(([name, value]) => ({ name, value: String(value || '') })),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
3
plugins/auth-aws/tsconfig.json
Normal file
3
plugins/auth-aws/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -46,6 +46,50 @@ export const plugin: PluginDefinition = {
|
||||
name: 'secretBase64',
|
||||
label: 'Secret is base64 encoded',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'location',
|
||||
label: 'Behavior',
|
||||
defaultValue: 'header',
|
||||
options: [
|
||||
{ label: 'Insert Header', value: 'header' },
|
||||
{ label: 'Append Query Parameter', value: 'query' },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
label: 'Header Name',
|
||||
defaultValue: 'Authorization',
|
||||
optional: true,
|
||||
dynamic(_ctx, args) {
|
||||
if (args.values.location === 'query') {
|
||||
return {
|
||||
label: 'Parameter Name',
|
||||
description: 'The name of the query parameter to add to the request',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
label: 'Header Name',
|
||||
description: 'The name of the header to add to the request',
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'headerPrefix',
|
||||
label: 'Header Prefix',
|
||||
optional: true,
|
||||
defaultValue: 'Bearer',
|
||||
dynamic(_ctx, args) {
|
||||
if (args.values.location === 'query') {
|
||||
return {
|
||||
hidden: true,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'editor',
|
||||
name: 'payload',
|
||||
@@ -61,8 +105,17 @@ export const plugin: PluginDefinition = {
|
||||
const token = jwt.sign(`${payload}`, secret, {
|
||||
algorithm: algorithm as (typeof algorithms)[number],
|
||||
});
|
||||
const value = `Bearer ${token}`;
|
||||
return { setHeaders: [{ name: 'Authorization', value }] };
|
||||
|
||||
if (values.location === 'query') {
|
||||
const paramName = String(values.name || 'token');
|
||||
const paramValue = String(values.value || '');
|
||||
return { setQueryParameters: [{ name: paramName, value: paramValue }] };
|
||||
} else {
|
||||
const headerPrefix = values.headerPrefix != null ? values.headerPrefix : 'Bearer';
|
||||
const headerName = String(values.name || 'Authorization');
|
||||
const headerValue = `${headerPrefix} ${token}`.trim();
|
||||
return { setHeaders: [{ name: headerName, value: headerValue }] };
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
20
plugins/auth-oauth1/package.json
Normal file
20
plugins/auth-oauth1/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@yaak/auth-oauth1",
|
||||
"displayName": "OAuth 1.0",
|
||||
"description": "Authenticate requests using OAuth 1.0a",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mountain-loop/yaak.git",
|
||||
"directory": "plugins/auth-oauth1"
|
||||
},
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"oauth-1.0a": "^2.2.6"
|
||||
}
|
||||
}
|
||||
197
plugins/auth-oauth1/src/index.ts
Normal file
197
plugins/auth-oauth1/src/index.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import type { Context, GetHttpAuthenticationConfigRequest, PluginDefinition } from '@yaakapp/api';
|
||||
import crypto from 'node:crypto';
|
||||
import OAuth from 'oauth-1.0a';
|
||||
|
||||
const signatures = {
|
||||
HMAC_SHA1: 'HMAC-SHA1',
|
||||
HMAC_SHA256: 'HMAC-SHA256',
|
||||
HMAC_SHA512: 'HMAC-SHA512',
|
||||
RSA_SHA1: 'RSA-SHA1',
|
||||
RSA_SHA256: 'RSA-SHA256',
|
||||
RSA_SHA512: 'RSA-SHA512',
|
||||
PLAINTEXT: 'PLAINTEXT',
|
||||
} as const;
|
||||
const defaultSig = signatures.HMAC_SHA1;
|
||||
|
||||
const pkSigs = Object.values(signatures).filter((k) => k.startsWith('RSA-'));
|
||||
const nonPkSigs = Object.values(signatures).filter((k) => !pkSigs.includes(k));
|
||||
|
||||
type SigMethod = (typeof signatures)[keyof typeof signatures];
|
||||
|
||||
function hiddenIfNot(
|
||||
sigMethod: SigMethod[],
|
||||
...other: ((values: GetHttpAuthenticationConfigRequest['values']) => boolean)[]
|
||||
) {
|
||||
return (_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) => {
|
||||
const hasGrantType = sigMethod.find((t) => t === String(values.signatureMethod ?? defaultSig));
|
||||
const hasOtherBools = other.every((t) => t(values));
|
||||
const show = hasGrantType && hasOtherBools;
|
||||
return { hidden: !show };
|
||||
};
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
authentication: {
|
||||
name: 'oauth1',
|
||||
label: 'OAuth 1.0',
|
||||
shortLabel: 'OAuth 1',
|
||||
args: [
|
||||
{
|
||||
name: 'signatureMethod',
|
||||
label: 'Signature Method',
|
||||
type: 'select',
|
||||
defaultValue: defaultSig,
|
||||
options: Object.values(signatures).map((v) => ({ label: v, value: v })),
|
||||
},
|
||||
{ name: 'consumerKey', label: 'Consumer Key', type: 'text', password: true, optional: true },
|
||||
{
|
||||
name: 'consumerSecret',
|
||||
label: 'Consumer Secret',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
name: 'tokenKey',
|
||||
label: 'Access Token',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
},
|
||||
{
|
||||
name: 'tokenSecret',
|
||||
label: 'Token Secret',
|
||||
type: 'text',
|
||||
password: true,
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(nonPkSigs),
|
||||
},
|
||||
{
|
||||
name: 'privateKey',
|
||||
label: 'Private Key (RSA-SHA1)',
|
||||
type: 'text',
|
||||
multiLine: true,
|
||||
optional: true,
|
||||
password: true,
|
||||
placeholder:
|
||||
'-----BEGIN RSA PRIVATE KEY-----\nPrivate key in PEM format\n-----END RSA PRIVATE KEY-----',
|
||||
dynamic: hiddenIfNot(pkSigs),
|
||||
},
|
||||
{
|
||||
type: 'accordion',
|
||||
label: 'Advanced',
|
||||
inputs: [
|
||||
{ name: 'callback', label: 'Callback Url', type: 'text', optional: true },
|
||||
{ name: 'verifier', label: 'Verifier', type: 'text', optional: true, password: true },
|
||||
{ name: 'timestamp', label: 'Timestamp', type: 'text', optional: true },
|
||||
{ name: 'nonce', label: 'Nonce', type: 'text', optional: true },
|
||||
{
|
||||
name: 'version',
|
||||
label: 'OAuth Version',
|
||||
type: 'text',
|
||||
optional: true,
|
||||
defaultValue: '1.0',
|
||||
},
|
||||
{ name: 'realm', label: 'Realm', type: 'text', optional: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
onApply(
|
||||
_ctx,
|
||||
{ values, method, url },
|
||||
): {
|
||||
setHeaders?: { name: string; value: string }[];
|
||||
setQueryParameters?: { name: string; value: string }[];
|
||||
} {
|
||||
const consumerKey = String(values.consumerKey || '');
|
||||
const consumerSecret = String(values.consumerSecret || '');
|
||||
|
||||
const signatureMethod = String(values.signatureMethod || signatures.HMAC_SHA1) as SigMethod;
|
||||
const version = String(values.version || '1.0');
|
||||
const realm = String(values.realm || '') || undefined;
|
||||
|
||||
const oauth = new OAuth({
|
||||
consumer: { key: consumerKey, secret: consumerSecret },
|
||||
signature_method: signatureMethod,
|
||||
version,
|
||||
hash_function: hashFunction(signatureMethod),
|
||||
realm,
|
||||
});
|
||||
|
||||
if (pkSigs.includes(signatureMethod)) {
|
||||
oauth.getSigningKey = (tokenSecret?: string) => tokenSecret || '';
|
||||
}
|
||||
|
||||
const requestUrl = new URL(url);
|
||||
|
||||
// Base request options passed to oauth-1.0a
|
||||
const requestData: Omit<OAuth.RequestOptions, 'data'> & {
|
||||
data: Record<string, string | string[]>;
|
||||
} = {
|
||||
method,
|
||||
url: requestUrl.toString(),
|
||||
includeBodyHash: false,
|
||||
data: {},
|
||||
};
|
||||
|
||||
// (1) Include existing query params in signature base string
|
||||
for (const key of requestUrl.searchParams.keys()) {
|
||||
if (key.startsWith('oauth_')) continue;
|
||||
const all = requestUrl.searchParams.getAll(key);
|
||||
requestData.data[key] = all.length > 1 ? all : all[0]!;
|
||||
}
|
||||
|
||||
// (2) Manual oauth_* overrides
|
||||
if (values.callback) requestData.data.oauth_callback = String(values.callback);
|
||||
if (values.nonce) requestData.data.oauth_nonce = String(values.nonce);
|
||||
if (values.timestamp) requestData.data.oauth_timestamp = String(values.timestamp);
|
||||
if (values.verifier) requestData.data.oauth_verifier = String(values.verifier);
|
||||
|
||||
let token: OAuth.Token | { key: string } | undefined;
|
||||
|
||||
if (pkSigs.includes(signatureMethod)) {
|
||||
token = {
|
||||
key: String(values.tokenKey || ''),
|
||||
secret: String(values.privateKey || ''),
|
||||
};
|
||||
} else if (values.tokenKey && values.tokenSecret) {
|
||||
token = { key: String(values.tokenKey), secret: String(values.tokenSecret) };
|
||||
} else if (values.tokenKey) {
|
||||
token = { key: String(values.tokenKey) };
|
||||
}
|
||||
|
||||
const authParams = oauth.authorize(requestData, token as OAuth.Token | undefined);
|
||||
const { Authorization } = oauth.toHeader(authParams);
|
||||
return { setHeaders: [{ name: 'Authorization', value: Authorization }] };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function hashFunction(signatureMethod: SigMethod) {
|
||||
switch (signatureMethod) {
|
||||
case signatures.HMAC_SHA1:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha1', key).update(base).digest('base64');
|
||||
case signatures.HMAC_SHA256:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha256', key).update(base).digest('base64');
|
||||
case signatures.HMAC_SHA512:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha512', key).update(base).digest('base64');
|
||||
case signatures.RSA_SHA1:
|
||||
return (base: string, privateKey: string) =>
|
||||
crypto.createSign('RSA-SHA1').update(base).sign(privateKey, 'base64');
|
||||
case signatures.RSA_SHA256:
|
||||
return (base: string, privateKey: string) =>
|
||||
crypto.createSign('RSA-SHA256').update(base).sign(privateKey, 'base64');
|
||||
case signatures.RSA_SHA512:
|
||||
return (base: string, privateKey: string) =>
|
||||
crypto.createSign('RSA-SHA512').update(base).sign(privateKey, 'base64');
|
||||
case signatures.PLAINTEXT:
|
||||
return (base: string) => base;
|
||||
default:
|
||||
return (base: string, key: string) =>
|
||||
crypto.createHmac('sha1', key).update(base).digest('base64');
|
||||
}
|
||||
}
|
||||
3
plugins/auth-oauth1/tsconfig.json
Normal file
3
plugins/auth-oauth1/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint":"tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
"lint":"tsc --noEmit && eslint . --ext .ts,.tsx",
|
||||
"test": "vitest --run tests"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { fetchAccessToken } from '../fetchAccessToken';
|
||||
import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken';
|
||||
import type { AccessToken, TokenStoreArgs } from '../store';
|
||||
import { getDataDirKey, storeToken } from '../store';
|
||||
import { extractCode } from '../util';
|
||||
|
||||
export const PKCE_SHA256 = 'S256';
|
||||
export const PKCE_PLAIN = 'plain';
|
||||
@@ -79,7 +80,6 @@ export async function getAuthorizationCode(
|
||||
authorizationUrl.searchParams.set('code_challenge_method', pkce.challengeMethod);
|
||||
}
|
||||
|
||||
const logsEnabled = (await ctx.store.get('enable_logs')) ?? false;
|
||||
const dataDirKey = await getDataDirKey(ctx, contextId);
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
console.log('[oauth2] Authorizing', authorizationUrlStr);
|
||||
@@ -97,18 +97,17 @@ export async function getAuthorizationCode(
|
||||
}
|
||||
},
|
||||
async onNavigate({ url: urlStr }) {
|
||||
const url = new URL(urlStr);
|
||||
if (logsEnabled) console.log('[oauth2] Navigated to', urlStr);
|
||||
|
||||
if (url.searchParams.has('error')) {
|
||||
let code;
|
||||
try {
|
||||
code = extractCode(urlStr, redirectUri);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
close();
|
||||
return reject(new Error(`Failed to authorize: ${url.searchParams.get('error')}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const code = url.searchParams.get('code');
|
||||
if (!code) {
|
||||
console.log('[oauth2] Code not found');
|
||||
return; // Could be one of many redirects in a chain, so skip it
|
||||
return;
|
||||
}
|
||||
|
||||
// Close the window here, because we don't need it anymore!
|
||||
|
||||
@@ -6,8 +6,8 @@ import type {
|
||||
PluginDefinition,
|
||||
} from '@yaakapp/api';
|
||||
import {
|
||||
genPkceCodeVerifier,
|
||||
DEFAULT_PKCE_METHOD,
|
||||
genPkceCodeVerifier,
|
||||
getAuthorizationCode,
|
||||
PKCE_PLAIN,
|
||||
PKCE_SHA256,
|
||||
@@ -125,17 +125,6 @@ export const plugin: PluginDefinition = {
|
||||
await resetDataDirKey(ctx, contextId);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Toggle Debug Logs',
|
||||
async onSelect(ctx) {
|
||||
const enableLogs = !(await ctx.store.get('enable_logs'));
|
||||
await ctx.store.set('enable_logs', enableLogs);
|
||||
await ctx.toast.show({
|
||||
message: `Debug logs ${enableLogs ? 'enabled' : 'disabled'}`,
|
||||
color: 'info',
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
args: [
|
||||
{
|
||||
@@ -271,6 +260,12 @@ export const plugin: PluginDefinition = {
|
||||
label: 'Advanced',
|
||||
inputs: [
|
||||
{ type: 'text', name: 'scope', label: 'Scope', optional: true },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'headerName',
|
||||
label: 'Header Name',
|
||||
defaultValue: 'Authorization',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'headerPrefix',
|
||||
@@ -397,15 +392,9 @@ export const plugin: PluginDefinition = {
|
||||
throw new Error('Invalid grant type ' + grantType);
|
||||
}
|
||||
|
||||
const headerName = stringArg(values, 'headerName') || 'Authorization';
|
||||
const headerValue = `${headerPrefix} ${token.response[tokenName]}`.trim();
|
||||
return {
|
||||
setHeaders: [
|
||||
{
|
||||
name: 'Authorization',
|
||||
value: headerValue,
|
||||
},
|
||||
],
|
||||
};
|
||||
return { setHeaders: [{ name: headerName, value: headerValue }] };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,3 +3,83 @@ import type { AccessToken } from './store';
|
||||
export function isTokenExpired(token: AccessToken) {
|
||||
return token.expiresAt && Date.now() > token.expiresAt;
|
||||
}
|
||||
|
||||
export function extractCode(urlStr: string, redirectUri: string | null): string | null {
|
||||
const url = new URL(urlStr);
|
||||
|
||||
if (!urlMatchesRedirect(url, redirectUri)) {
|
||||
console.log('[oauth2] URL does not match redirect origin/path; skipping.');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prefer query param; fall back to fragment if query lacks it
|
||||
|
||||
const query = url.searchParams;
|
||||
const queryError = query.get('error');
|
||||
const queryDesc = query.get('error_description');
|
||||
const queryUri = query.get('error_uri');
|
||||
|
||||
let hashParams: URLSearchParams | null = null;
|
||||
if (url.hash && url.hash.length > 1) {
|
||||
hashParams = new URLSearchParams(url.hash.slice(1));
|
||||
}
|
||||
const hashError = hashParams?.get('error');
|
||||
const hashDesc = hashParams?.get('error_description');
|
||||
const hashUri = hashParams?.get('error_uri');
|
||||
|
||||
const error = queryError || hashError;
|
||||
if (error) {
|
||||
const desc = queryDesc || hashDesc;
|
||||
const uri = queryUri || hashUri;
|
||||
let message = `Failed to authorize: ${error}`;
|
||||
if (desc) message += ` (${desc})`;
|
||||
if (uri) message += ` [${uri}]`;
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const queryCode = query.get('code');
|
||||
if (queryCode) return queryCode;
|
||||
|
||||
const hashCode = hashParams?.get('code');
|
||||
if (hashCode) return hashCode;
|
||||
|
||||
console.log('[oauth2] Code not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
export function urlMatchesRedirect(url: URL, redirectUrl: string | null): boolean {
|
||||
if (!redirectUrl) return true;
|
||||
|
||||
let redirect;
|
||||
try {
|
||||
redirect = new URL(redirectUrl);
|
||||
} catch {
|
||||
console.log('[oauth2] Invalid redirect URI; skipping.');
|
||||
return false;
|
||||
}
|
||||
|
||||
const sameProtocol = url.protocol === redirect.protocol;
|
||||
|
||||
const sameHost = url.hostname.toLowerCase() === redirect.hostname.toLowerCase();
|
||||
|
||||
const normalizePort = (u: URL) =>
|
||||
(u.protocol === 'https:' && (!u.port || u.port === '443')) ||
|
||||
(u.protocol === 'http:' && (!u.port || u.port === '80'))
|
||||
? ''
|
||||
: u.port;
|
||||
|
||||
const samePort = normalizePort(url) === normalizePort(redirect);
|
||||
|
||||
const normPath = (p: string) => {
|
||||
const withLeading = p.startsWith('/') ? p : `/${p}`;
|
||||
// strip trailing slashes, keep root as "/"
|
||||
return withLeading.replace(/\/+$/g, '') || '/';
|
||||
};
|
||||
|
||||
// Require redirect path to be a prefix of the navigated URL path
|
||||
const urlPath = normPath(url.pathname);
|
||||
const redirectPath = normPath(redirect.pathname);
|
||||
const pathMatches = urlPath === redirectPath || urlPath.startsWith(`${redirectPath}/`);
|
||||
|
||||
return sameProtocol && sameHost && samePort && pathMatches;
|
||||
}
|
||||
|
||||
109
plugins/auth-oauth2/tests/util.test.ts
Normal file
109
plugins/auth-oauth2/tests/util.test.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { extractCode } from '../src/util';
|
||||
|
||||
describe('extractCode', () => {
|
||||
test('extracts code from query when same origin + path', () => {
|
||||
const url = 'https://app.example.com/cb?code=abc123&state=xyz';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBe('abc123');
|
||||
});
|
||||
|
||||
test('extracts code from query with weird path', () => {
|
||||
const url = 'https://app.example.com/cbwithextra?code=abc123&state=xyz';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBeNull();
|
||||
});
|
||||
|
||||
test('allows trailing slash differences', () => {
|
||||
expect(extractCode('https://app.example.com/cb/?code=abc', 'https://app.example.com/cb')).toBe(
|
||||
'abc',
|
||||
);
|
||||
expect(extractCode('https://app.example.com/cb?code=abc', 'https://app.example.com/cb/')).toBe(
|
||||
'abc',
|
||||
);
|
||||
});
|
||||
|
||||
test('treats default ports as equal (https:443, http:80)', () => {
|
||||
expect(
|
||||
extractCode('https://app.example.com/cb?code=abc', 'https://app.example.com:443/cb'),
|
||||
).toBe('abc');
|
||||
expect(extractCode('http://app.example.com/cb?code=abc', 'http://app.example.com:80/cb')).toBe(
|
||||
'abc',
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects different port', () => {
|
||||
expect(
|
||||
extractCode('https://app.example.com/cb?code=abc', 'https://app.example.com:8443/cb'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test('rejects different hostname (including subdomain changes)', () => {
|
||||
expect(
|
||||
extractCode('https://evil.example.com/cb?code=abc', 'https://app.example.com/cb'),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test('requires path to start with redirect path (ignoring query/hash)', () => {
|
||||
// same origin but wrong path -> null
|
||||
expect(
|
||||
extractCode('https://app.example.com/other?code=abc', 'https://app.example.com/cb'),
|
||||
).toBeNull();
|
||||
|
||||
// deeper subpath under the redirect path -> allowed (prefix match)
|
||||
expect(
|
||||
extractCode('https://app.example.com/cb/deep?code=abc', 'https://app.example.com/cb'),
|
||||
).toBe('abc');
|
||||
});
|
||||
|
||||
test('works with custom schemes', () => {
|
||||
expect(extractCode('myapp://cb?code=abc', 'myapp://cb')).toBe('abc');
|
||||
});
|
||||
|
||||
test('prefers query over fragment when both present', () => {
|
||||
const url = 'https://app.example.com/cb?code=queryCode#code=hashCode';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBe('queryCode');
|
||||
});
|
||||
|
||||
test('extracts code from fragment when query lacks code', () => {
|
||||
const url = 'https://app.example.com/cb#code=fromHash&state=xyz';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBe('fromHash');
|
||||
});
|
||||
|
||||
test('returns null if no code present (query or fragment)', () => {
|
||||
const url = 'https://app.example.com/cb?state=only';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBeNull();
|
||||
});
|
||||
|
||||
test('returns null when provider reports an error', () => {
|
||||
const url = 'https://app.example.com/cb?error=access_denied&error_description=oopsy';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(() => extractCode(url, redirect)).toThrow('Failed to authorize: access_denied');
|
||||
});
|
||||
|
||||
test('when redirectUri is null, extracts code from any URL', () => {
|
||||
expect(extractCode('https://random.example.com/whatever?code=abc', null)).toBe('abc');
|
||||
});
|
||||
|
||||
test('handles extra params gracefully', () => {
|
||||
const url = 'https://app.example.com/cb?foo=1&bar=2&code=abc&baz=3';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBe('abc');
|
||||
});
|
||||
|
||||
test('ignores fragment noise when code is in query', () => {
|
||||
const url = 'https://app.example.com/cb?code=abc#some=thing';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBe('abc');
|
||||
});
|
||||
|
||||
// If you decide NOT to support fragment-based codes, flip these to expect null or mark as .skip
|
||||
test('supports fragment-only code for response_mode=fragment providers', () => {
|
||||
const url = 'https://app.example.com/cb#state=xyz&code=abc';
|
||||
const redirect = 'https://app.example.com/cb';
|
||||
expect(extractCode(url, redirect)).toBe('abc');
|
||||
});
|
||||
});
|
||||
@@ -122,6 +122,12 @@ function importHttpRequest(r: any, workspaceId: string): PartialImportResources[
|
||||
name: r.name,
|
||||
description: r.description || undefined,
|
||||
url: convertSyntax(r.url),
|
||||
urlParameters: (r.parameters ?? [])
|
||||
.map((p: any) => ({
|
||||
enabled: !p.disabled,
|
||||
name: p.name ?? '',
|
||||
value: p.value ?? '',
|
||||
})),
|
||||
body,
|
||||
bodyType,
|
||||
authentication,
|
||||
@@ -184,15 +190,15 @@ function importEnvironment(
|
||||
workspaceId: string,
|
||||
isParent?: boolean,
|
||||
): PartialImportResources['environments'][0] {
|
||||
isParent ??= e.parentId === workspaceId;
|
||||
return {
|
||||
id: convertId(e._id),
|
||||
createdAt: e.created ? new Date(e.created).toISOString().replace('Z', '') : undefined,
|
||||
updatedAt: e.modified ? new Date(e.modified).toISOString().replace('Z', '') : undefined,
|
||||
workspaceId: convertId(workspaceId),
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
sortPriority: e.metaSortKey, // Will be added to Yaak later
|
||||
base: isParent ?? e.parentId === workspaceId,
|
||||
sortPriority: e.metaSortKey,
|
||||
parentModel: isParent ? 'workspace' : 'environment',
|
||||
parentId: null,
|
||||
model: 'environment',
|
||||
name: e.name,
|
||||
variables: Object.entries(e.data).map(([name, value]) => ({
|
||||
|
||||
@@ -125,6 +125,12 @@ function importHttpRequest(
|
||||
name: r.name,
|
||||
description: r.meta?.description || undefined,
|
||||
url: convertSyntax(r.url),
|
||||
urlParameters: (r.parameters ?? [])
|
||||
.map((p: any) => ({
|
||||
enabled: !p.disabled,
|
||||
name: p.name ?? '',
|
||||
value: p.value ?? '',
|
||||
})),
|
||||
body,
|
||||
bodyType,
|
||||
method: r.method,
|
||||
@@ -295,9 +301,7 @@ function importEnvironment(
|
||||
updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined,
|
||||
workspaceId: convertId(workspaceId),
|
||||
public: !e.isPrivate,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
sortPriority: sortKey, // Will be added to Yaak later
|
||||
sortPriority: sortKey,
|
||||
parentModel: isParent ? 'workspace' : 'environment',
|
||||
parentId: null,
|
||||
model: 'environment',
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"createdAt": "2025-01-13T15:15:43.767",
|
||||
"updatedAt": "2025-01-13T15:15:55.209",
|
||||
"sortPriority": 1736781343767,
|
||||
"base": true,
|
||||
"parentId": null,
|
||||
"parentModel": "workspace",
|
||||
"id": "GENERATE_ID::env_16c0dec5b77c414ae0e419b8f10c3701300c5900",
|
||||
"model": "environment",
|
||||
"name": "Base Environment",
|
||||
@@ -22,7 +23,8 @@
|
||||
"createdAt": "2025-01-13T15:15:58.515",
|
||||
"updatedAt": "2025-01-13T15:16:34.705",
|
||||
"sortPriority": 1736781358515,
|
||||
"base": false,
|
||||
"parentId": null,
|
||||
"parentModel": "environment",
|
||||
"id": "GENERATE_ID::env_799ae3d723ef44af91b4817e5d057e6d",
|
||||
"model": "environment",
|
||||
"name": "Production",
|
||||
@@ -39,7 +41,8 @@
|
||||
"createdAt": "2025-01-13T15:16:14.707",
|
||||
"updatedAt": "2025-01-13T15:16:31.078",
|
||||
"sortPriority": 1736781358565,
|
||||
"base": false,
|
||||
"parentId": null,
|
||||
"parentModel": "environment",
|
||||
"id": "GENERATE_ID::env_030fbfdbb274426ebd78e2e6518f8553",
|
||||
"model": "environment",
|
||||
"name": "Staging",
|
||||
@@ -110,6 +113,13 @@
|
||||
"model": "http_request",
|
||||
"name": "New Request",
|
||||
"url": "${[BASE_URL ]}/foo/:id",
|
||||
"urlParameters": [
|
||||
{
|
||||
"name": "query",
|
||||
"value": "qqq",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"workspaceId": "GENERATE_ID::wrk_d4d92f7c0ee947b89159243506687019"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
"sortPriority": -1747414129276,
|
||||
"updatedAt": "2025-05-16T16:48:49.313",
|
||||
"url": "https://httpbin.org/post",
|
||||
"urlParameters": [],
|
||||
"workspaceId": "GENERATE_ID::wrk_9717dd1c9e0c4b2e9ed6d2abcf3bd45c"
|
||||
},
|
||||
{
|
||||
@@ -98,6 +99,7 @@
|
||||
"name": "New Request",
|
||||
"sortPriority": -1747414160498,
|
||||
"updatedAt": "2025-05-16T16:49:20.497",
|
||||
"urlParameters": [],
|
||||
"workspaceId": "GENERATE_ID::wrk_9717dd1c9e0c4b2e9ed6d2abcf3bd45c"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -135,6 +135,13 @@
|
||||
"name": "New Request",
|
||||
"sortPriority": -1736781406672,
|
||||
"url": "${[BASE_URL ]}/foo/:id",
|
||||
"urlParameters": [
|
||||
{
|
||||
"name": "query",
|
||||
"value": "qqq",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"workspaceId": "GENERATE_ID::wrk_c1eacfa750a04f3ea9985ef28043fa53"
|
||||
}
|
||||
],
|
||||
|
||||
14
plugins/importer-postman-environment/package.json
Normal file
14
plugins/importer-postman-environment/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@yaak/importer-postman-environment",
|
||||
"displayName": "Postman Environment Importer",
|
||||
"description": "Import environments from Postman",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"main": "./build/index.js",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "tsc --noEmit && eslint . --ext .ts,.tsx",
|
||||
"test": "vitest --run tests"
|
||||
}
|
||||
}
|
||||
138
plugins/importer-postman-environment/src/index.ts
Normal file
138
plugins/importer-postman-environment/src/index.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type {
|
||||
Context,
|
||||
Environment,
|
||||
PartialImportResources,
|
||||
PluginDefinition,
|
||||
Workspace,
|
||||
} from '@yaakapp/api';
|
||||
import type { ImportPluginResponse } from '@yaakapp/api/lib/plugins/ImporterPlugin';
|
||||
|
||||
type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||
|
||||
interface ExportResources {
|
||||
workspaces: AtLeast<Workspace, 'name' | 'id' | 'model'>[];
|
||||
environments: AtLeast<Environment, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
importer: {
|
||||
name: 'Postman Environment',
|
||||
description: 'Import postman environment exports',
|
||||
onImport(_ctx: Context, args: { text: string }) {
|
||||
return convertPostmanEnvironment(args.text);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function convertPostmanEnvironment(contents: string): ImportPluginResponse | undefined {
|
||||
const root = parseJSONToRecord(contents);
|
||||
if (root == null) return;
|
||||
|
||||
// Validate that it looks like a Postman Environment export
|
||||
const values = toArray<{
|
||||
key?: string;
|
||||
value?: unknown;
|
||||
enabled?: boolean;
|
||||
description?: string;
|
||||
type?: string;
|
||||
}>(root.values);
|
||||
const scope = root._postman_variable_scope;
|
||||
const hasEnvMarkers = typeof scope === 'string';
|
||||
|
||||
if (values.length === 0 || (!hasEnvMarkers && typeof root.name !== 'string')) {
|
||||
// Not a Postman environment file, skip
|
||||
return;
|
||||
}
|
||||
|
||||
const exportResources: ExportResources = {
|
||||
workspaces: [],
|
||||
environments: [],
|
||||
};
|
||||
|
||||
const envVariables = values
|
||||
.map((v) => ({
|
||||
enabled: v.enabled ?? true,
|
||||
name: String(v.key ?? ''),
|
||||
value: String(v.value),
|
||||
description: v.description ? String(v.description) : null,
|
||||
}))
|
||||
.filter((v) => v.name.length > 0);
|
||||
|
||||
const environment: ExportResources['environments'][0] = {
|
||||
model: 'environment',
|
||||
id: generateId('environment'),
|
||||
name: root.name ? String(root.name) : 'Environment',
|
||||
workspaceId: 'CURRENT_WORKSPACE',
|
||||
parentModel: 'environment',
|
||||
parentId: null,
|
||||
variables: envVariables,
|
||||
};
|
||||
exportResources.environments.push(environment);
|
||||
|
||||
const resources = deleteUndefinedAttrs(
|
||||
convertTemplateSyntax(exportResources),
|
||||
) as PartialImportResources;
|
||||
|
||||
return { resources };
|
||||
}
|
||||
|
||||
function parseJSONToRecord<T>(jsonStr: string): Record<string, T> | null {
|
||||
try {
|
||||
return toRecord(JSON.parse(jsonStr));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function toRecord<T>(value: Record<string, T> | unknown): Record<string, T> {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
return value as Record<string, T>;
|
||||
}
|
||||
return {} as Record<string, T>;
|
||||
}
|
||||
|
||||
function toArray<T>(value: unknown): T[] {
|
||||
if (Object.prototype.toString.call(value) === '[object Array]') return value as T[];
|
||||
else return [] as T[];
|
||||
}
|
||||
|
||||
/** Recursively render all nested object properties */
|
||||
function convertTemplateSyntax<T>(obj: T): T {
|
||||
if (typeof obj === 'string') {
|
||||
return obj.replace(
|
||||
/{{\s*(_\.)?([^}]*)\s*}}/g,
|
||||
(_m, _dot, expr) => '${[' + expr.trim() + ']}',
|
||||
) as T;
|
||||
} else if (Array.isArray(obj) && obj != null) {
|
||||
return obj.map(convertTemplateSyntax) as T;
|
||||
} else if (typeof obj === 'object' && obj != null) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj as Record<string, unknown>).map(([k, v]) => [k, convertTemplateSyntax(v)]),
|
||||
) as T;
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
function deleteUndefinedAttrs<T>(obj: T): T {
|
||||
if (Array.isArray(obj) && obj != null) {
|
||||
return obj.map(deleteUndefinedAttrs) as T;
|
||||
} else if (typeof obj === 'object' && obj != null) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj as Record<string, unknown>)
|
||||
.filter(([, v]) => v !== undefined)
|
||||
.map(([k, v]) => [k, deleteUndefinedAttrs(v)]),
|
||||
) as T;
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
const idCount: Partial<Record<string, number>> = {};
|
||||
|
||||
function generateId(model: string): string {
|
||||
idCount[model] = (idCount[model] ?? -1) + 1;
|
||||
return `GENERATE_ID::${model.toUpperCase()}_${idCount[model]}`;
|
||||
}
|
||||
|
||||
export default plugin;
|
||||
27
plugins/importer-postman-environment/tests/fixtures/environment.input.json
vendored
Normal file
27
plugins/importer-postman-environment/tests/fixtures/environment.input.json
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"id": "123",
|
||||
"name": "My Environment",
|
||||
"values": [
|
||||
{
|
||||
"key": "baseUrl",
|
||||
"value": "https://api.example.com",
|
||||
"type": "default",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{ access_token }}",
|
||||
"type": "default",
|
||||
"description": "Access token for the API.",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "disabled",
|
||||
"type": "secret",
|
||||
"value": "hello",
|
||||
"enabled": false
|
||||
}
|
||||
],
|
||||
"_postman_variable_scope": "environment",
|
||||
"_postman_exported_using": "PostmanRuntime/1.0.0"
|
||||
}
|
||||
35
plugins/importer-postman-environment/tests/fixtures/environment.output.json
vendored
Normal file
35
plugins/importer-postman-environment/tests/fixtures/environment.output.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"resources": {
|
||||
"workspaces": [],
|
||||
"environments": [
|
||||
{
|
||||
"id": "GENERATE_ID::ENVIRONMENT_0",
|
||||
"model": "environment",
|
||||
"name": "My Environment",
|
||||
"variables": [
|
||||
{
|
||||
"enabled": true,
|
||||
"description": null,
|
||||
"name": "baseUrl",
|
||||
"value": "https://api.example.com"
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"description": "Access token for the API.",
|
||||
"name": "token",
|
||||
"value": "${[access_token]}"
|
||||
},
|
||||
{
|
||||
"enabled": false,
|
||||
"description": null,
|
||||
"name": "disabled",
|
||||
"value": "hello"
|
||||
}
|
||||
],
|
||||
"workspaceId": "CURRENT_WORKSPACE",
|
||||
"parentId": null,
|
||||
"parentModel": "environment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
22
plugins/importer-postman-environment/tests/index.test.ts
Normal file
22
plugins/importer-postman-environment/tests/index.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { convertPostmanEnvironment } from '../src';
|
||||
|
||||
describe('importer-postman-environment', () => {
|
||||
const p = path.join(__dirname, 'fixtures');
|
||||
const fixtures = fs.readdirSync(p);
|
||||
|
||||
for (const fixture of fixtures) {
|
||||
if (fixture.includes('.output')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
test('Imports ' + fixture, () => {
|
||||
const contents = fs.readFileSync(path.join(p, fixture), 'utf-8');
|
||||
const expected = fs.readFileSync(path.join(p, fixture.replace('.input', '.output')), 'utf-8');
|
||||
const result = convertPostmanEnvironment(contents);
|
||||
expect(result).toEqual(JSON.parse(expected));
|
||||
});
|
||||
}
|
||||
});
|
||||
3
plugins/importer-postman-environment/tsconfig.json
Normal file
3
plugins/importer-postman-environment/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -68,6 +68,7 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin
|
||||
id: generateId('workspace'),
|
||||
name: info.name ? String(info.name) : 'Postman Import',
|
||||
description,
|
||||
...globalAuth,
|
||||
};
|
||||
exportResources.workspaces.push(workspace);
|
||||
|
||||
@@ -105,8 +106,7 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin
|
||||
} else if (typeof v.name === 'string' && 'request' in v) {
|
||||
const r = toRecord(v.request);
|
||||
const bodyPatch = importBody(r.body);
|
||||
const requestAuthPath = importAuth(r.auth);
|
||||
const authPatch = requestAuthPath.authenticationType == null ? globalAuth : requestAuthPath;
|
||||
const requestAuth = importAuth(r.auth);
|
||||
|
||||
const headers: HttpRequestHeader[] = toArray<{
|
||||
key: string;
|
||||
@@ -145,10 +145,9 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin
|
||||
urlParameters,
|
||||
body: bodyPatch.body,
|
||||
bodyType: bodyPatch.bodyType,
|
||||
authentication: authPatch.authentication,
|
||||
authenticationType: authPatch.authenticationType,
|
||||
sortPriority: sortPriorityIndex++,
|
||||
headers,
|
||||
...requestAuth,
|
||||
};
|
||||
exportResources.httpRequests.push(request);
|
||||
} else {
|
||||
@@ -223,25 +222,159 @@ function convertUrl(rawUrl: string | unknown): Pick<HttpRequest, 'url' | 'urlPar
|
||||
}
|
||||
|
||||
function importAuth(rawAuth: unknown): Pick<HttpRequest, 'authentication' | 'authenticationType'> {
|
||||
const auth = toRecord<{ username?: string; password?: string; token?: string }>(rawAuth);
|
||||
if ('basic' in auth) {
|
||||
const auth = toRecord<Record<string, string>>(rawAuth);
|
||||
|
||||
// Helper: Postman stores auth params as an array of { key, value, ... }
|
||||
const pmArrayToObj = (v: unknown): Record<string, unknown> => {
|
||||
if (!Array.isArray(v)) return toRecord(v);
|
||||
const o: Record<string, unknown> = {};
|
||||
for (const i of v) {
|
||||
const ii = toRecord(i);
|
||||
if (typeof ii.key === 'string') {
|
||||
o[ii.key] = ii.value;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
const authType: string | undefined = auth.type ? String(auth.type) : undefined;
|
||||
|
||||
if (authType === 'noauth') {
|
||||
return {
|
||||
authenticationType: 'none',
|
||||
authentication: {},
|
||||
};
|
||||
}
|
||||
|
||||
if ('basic' in auth && authType === 'basic') {
|
||||
const b = pmArrayToObj(auth.basic);
|
||||
return {
|
||||
authenticationType: 'basic',
|
||||
authentication: {
|
||||
username: auth.basic.username || '',
|
||||
password: auth.basic.password || '',
|
||||
username: String(b.username ?? ''),
|
||||
password: String(b.password ?? ''),
|
||||
},
|
||||
};
|
||||
} else if ('bearer' in auth) {
|
||||
}
|
||||
|
||||
if ('bearer' in auth && authType === 'bearer') {
|
||||
const b = pmArrayToObj(auth.bearer);
|
||||
// Postman uses key "token"
|
||||
return {
|
||||
authenticationType: 'bearer',
|
||||
authentication: {
|
||||
token: auth.bearer.token || '',
|
||||
token: String(b.token ?? ''),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return { authenticationType: null, authentication: {} };
|
||||
}
|
||||
|
||||
if ('awsv4' in auth && authType === 'awsv4') {
|
||||
const a = pmArrayToObj(auth.awsv4);
|
||||
return {
|
||||
authenticationType: 'awsv4',
|
||||
authentication: {
|
||||
accessKeyId: a.accessKey != null ? String(a.accessKey) : undefined,
|
||||
secretAccessKey: a.secretKey != null ? String(a.secretKey) : undefined,
|
||||
sessionToken: a.sessionToken != null ? String(a.sessionToken) : undefined,
|
||||
region: a.region != null ? String(a.region) : undefined,
|
||||
service: a.service != null ? String(a.service) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ('apikey' in auth && authType === 'apikey') {
|
||||
const a = pmArrayToObj(auth.apikey);
|
||||
return {
|
||||
authenticationType: 'apikey',
|
||||
authentication: {
|
||||
location: a.in === 'query' ? 'query' : 'header',
|
||||
key: a.value != null ? String(a.value) : undefined,
|
||||
value: a.key != null ? String(a.key) : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ('jwt' in auth && authType === 'jwt') {
|
||||
const a = pmArrayToObj(auth.jwt);
|
||||
return {
|
||||
authenticationType: 'jwt',
|
||||
authentication: {
|
||||
algorithm: a.algorithm != null ? String(a.algorithm).toUpperCase() : undefined,
|
||||
secret: a.secret != null ? String(a.secret) : undefined,
|
||||
secretBase64: !!a.isSecretBase64Encoded,
|
||||
payload: a.payload != null ? String(a.payload) : undefined,
|
||||
headerPrefix: a.headerPrefix != null ? String(a.headerPrefix) : undefined,
|
||||
location: a.addTokenTo === 'header' ? 'header' : 'query',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ('oauth2' in auth && authType === 'oauth2') {
|
||||
const o = pmArrayToObj(auth.oauth2);
|
||||
|
||||
let grantType = o.grant_type ? String(o.grant_type) : 'authorization_code';
|
||||
let pkcePatch: Record<string, unknown> = {};
|
||||
|
||||
if (grantType === 'authorization_code_with_pkce') {
|
||||
grantType = 'authorization_code';
|
||||
pkcePatch =
|
||||
o.grant_type === 'authorization_code_with_pkce'
|
||||
? {
|
||||
usePkce: true,
|
||||
pkceChallengeMethod: o.challengeAlgorithm ?? undefined,
|
||||
pkceCodeVerifier: o.code_verifier != null ? String(o.code_verifier) : undefined,
|
||||
}
|
||||
: {};
|
||||
} else if (grantType === 'password_credentials') {
|
||||
grantType = 'password';
|
||||
}
|
||||
|
||||
const accessTokenUrl = o.accessTokenUrl != null ? String(o.accessTokenUrl) : undefined;
|
||||
const audience = o.audience != null ? String(o.audience) : undefined;
|
||||
const authorizationUrl = o.authUrl != null ? String(o.authUrl) : undefined;
|
||||
const clientId = o.clientId != null ? String(o.clientId) : undefined;
|
||||
const clientSecret = o.clientSecret != null ? String(o.clientSecret) : undefined;
|
||||
const credentials = o.client_authentication === 'body' ? 'body' : undefined;
|
||||
const headerPrefix = o.headerPrefix ?? 'Bearer';
|
||||
const password = o.password != null ? String(o.password) : undefined;
|
||||
const redirectUri = o.redirect_uri != null ? String(o.redirect_uri) : undefined;
|
||||
const scope = o.scope != null ? String(o.scope) : undefined;
|
||||
const state = o.state != null ? String(o.state) : undefined;
|
||||
const username = o.username != null ? String(o.username) : undefined;
|
||||
|
||||
let grantPatch: Record<string, unknown> = {};
|
||||
if (grantType === 'authorization_code') {
|
||||
grantPatch = {
|
||||
clientSecret,
|
||||
authorizationUrl,
|
||||
accessTokenUrl,
|
||||
redirectUri,
|
||||
state,
|
||||
...pkcePatch,
|
||||
};
|
||||
} else if (grantType === 'implicit') {
|
||||
grantPatch = { authorizationUrl, redirectUri, state };
|
||||
} else if (grantType === 'password') {
|
||||
grantPatch = { clientSecret, accessTokenUrl, username, password };
|
||||
} else if (grantType === 'client_credentials') {
|
||||
grantPatch = { clientSecret, accessTokenUrl };
|
||||
}
|
||||
|
||||
const authentication = {
|
||||
name: 'oauth2',
|
||||
grantType,
|
||||
audience,
|
||||
clientId,
|
||||
credentials,
|
||||
headerPrefix,
|
||||
scope,
|
||||
...grantPatch,
|
||||
} as Record<string, unknown>;
|
||||
|
||||
return { authenticationType: 'oauth2', authentication };
|
||||
}
|
||||
|
||||
return { authenticationType: null, authentication: {} };
|
||||
}
|
||||
|
||||
function importBody(rawBody: unknown): Pick<HttpRequest, 'body' | 'bodyType' | 'headers'> {
|
||||
@@ -376,7 +509,10 @@ function toArray<T>(value: unknown): T[] {
|
||||
/** Recursively render all nested object properties */
|
||||
function convertTemplateSyntax<T>(obj: T): T {
|
||||
if (typeof obj === 'string') {
|
||||
return obj.replace(/{{\s*(_\.)?([^}]+)\s*}}/g, '${[$2]}') as T;
|
||||
return obj.replace(
|
||||
/{{\s*(_\.)?([^}]*)\s*}}/g,
|
||||
(_m, _dot, expr) => '${[' + expr.trim().replace(/^vault:/, '') + ']}',
|
||||
) as T;
|
||||
} else if (Array.isArray(obj) && obj != null) {
|
||||
return obj.map(convertTemplateSyntax) as T;
|
||||
} else if (typeof obj === 'object' && obj != null) {
|
||||
|
||||
828
plugins/importer-postman/tests/fixtures/auth.input.json
vendored
Normal file
828
plugins/importer-postman/tests/fixtures/auth.input.json
vendored
Normal file
@@ -0,0 +1,828 @@
|
||||
{
|
||||
"info": {
|
||||
"_postman_id": "9e6dfada-256c-49ea-a38f-7d1b05b7ca2d",
|
||||
"name": "Authentication",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
|
||||
"_exporter_id": "18798"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "No Auth",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "noauth"
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://yaak.app/x/echo",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"yaak",
|
||||
"app"
|
||||
],
|
||||
"path": [
|
||||
"x",
|
||||
"echo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Inherit",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://yaak.app/x/echo",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"yaak",
|
||||
"app"
|
||||
],
|
||||
"path": [
|
||||
"x",
|
||||
"echo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "OAuth 2 Auth Code",
|
||||
"protocolProfileBehavior": {
|
||||
"disableBodyPruning": true
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"oauth2": [
|
||||
{
|
||||
"key": "grant_type",
|
||||
"value": "authorization_code",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "headerPrefix",
|
||||
"value": "Bearer",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "client_authentication",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "challengeAlgorithm",
|
||||
"value": "S256",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "refreshTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "state",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "scope",
|
||||
"value": "scope",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "code_verifier",
|
||||
"value": "verifier",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientSecret",
|
||||
"value": "clientsecet",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientId",
|
||||
"value": "cliend id",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "authUrl",
|
||||
"value": "https://github.com/login/oauth/authorize",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "accessTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "useBrowser",
|
||||
"value": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"key": "redirect_uri",
|
||||
"value": "https://callback",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "tokenName",
|
||||
"value": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"hello\": \"world\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{vault:hello}}",
|
||||
"host": [
|
||||
"{{vault:hello}}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "OAuth 2 Auth Code (PKCE)",
|
||||
"protocolProfileBehavior": {
|
||||
"disableBodyPruning": true
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"oauth2": [
|
||||
{
|
||||
"key": "headerPrefix",
|
||||
"value": "Bearer",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "client_authentication",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "challengeAlgorithm",
|
||||
"value": "S256",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "refreshTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "state",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "scope",
|
||||
"value": "scope",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "code_verifier",
|
||||
"value": "verifier",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "grant_type",
|
||||
"value": "authorization_code_with_pkce",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientSecret",
|
||||
"value": "clientsecet",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientId",
|
||||
"value": "cliend id",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "authUrl",
|
||||
"value": "https://github.com/login/oauth/authorize",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "accessTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "useBrowser",
|
||||
"value": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"key": "redirect_uri",
|
||||
"value": "https://callback",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "tokenName",
|
||||
"value": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"hello\": \"world\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{vault:hello}}",
|
||||
"host": [
|
||||
"{{vault:hello}}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "OAuth 2 Implicit",
|
||||
"protocolProfileBehavior": {
|
||||
"disableBodyPruning": true
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"oauth2": [
|
||||
{
|
||||
"key": "client_authentication",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "redirect_uri",
|
||||
"value": "https://yaak.app/x/echo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "useBrowser",
|
||||
"value": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"key": "grant_type",
|
||||
"value": "implicit",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "headerPrefix",
|
||||
"value": "Bearer",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "challengeAlgorithm",
|
||||
"value": "S256",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "refreshTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "state",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "scope",
|
||||
"value": "scope",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "code_verifier",
|
||||
"value": "verifier",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientSecret",
|
||||
"value": "clientsecet",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientId",
|
||||
"value": "cliend id",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "authUrl",
|
||||
"value": "https://github.com/login/oauth/authorize",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "accessTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "tokenName",
|
||||
"value": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"hello\": \"world\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{vault:hello}}",
|
||||
"host": [
|
||||
"{{vault:hello}}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "OAuth 2 Password",
|
||||
"protocolProfileBehavior": {
|
||||
"disableBodyPruning": true
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"oauth2": [
|
||||
{
|
||||
"key": "password",
|
||||
"value": "password",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "username",
|
||||
"value": "username",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientSecret",
|
||||
"value": "clientsecret",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientId",
|
||||
"value": "clientid",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "grant_type",
|
||||
"value": "password_credentials",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "client_authentication",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "redirect_uri",
|
||||
"value": "https://yaak.app/x/echo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "useBrowser",
|
||||
"value": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"key": "headerPrefix",
|
||||
"value": "Bearer",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "challengeAlgorithm",
|
||||
"value": "S256",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "refreshTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "state",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "scope",
|
||||
"value": "scope",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "code_verifier",
|
||||
"value": "verifier",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "authUrl",
|
||||
"value": "https://github.com/login/oauth/authorize",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "accessTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "tokenName",
|
||||
"value": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"hello\": \"world\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{vault:hello}}",
|
||||
"host": [
|
||||
"{{vault:hello}}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "OAuth 2 Client Credentials",
|
||||
"protocolProfileBehavior": {
|
||||
"disableBodyPruning": true
|
||||
},
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"oauth2": [
|
||||
{
|
||||
"key": "grant_type",
|
||||
"value": "client_credentials",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "password",
|
||||
"value": "password",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "username",
|
||||
"value": "username",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientSecret",
|
||||
"value": "clientsecret",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "clientId",
|
||||
"value": "clientid",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "client_authentication",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "redirect_uri",
|
||||
"value": "https://yaak.app/x/echo",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "useBrowser",
|
||||
"value": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"key": "headerPrefix",
|
||||
"value": "Bearer",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "challengeAlgorithm",
|
||||
"value": "S256",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "refreshTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"value": "state",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "scope",
|
||||
"value": "scope",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "code_verifier",
|
||||
"value": "verifier",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "authUrl",
|
||||
"value": "https://github.com/login/oauth/authorize",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "accessTokenUrl",
|
||||
"value": "https://github.com/login/oauth/access_token",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "tokenName",
|
||||
"value": "name",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"hello\": \"world\"\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{vault:hello}}",
|
||||
"host": [
|
||||
"{{vault:hello}}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "AWS V4",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "awsv4",
|
||||
"awsv4": [
|
||||
{
|
||||
"key": "sessionToken",
|
||||
"value": "session",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "service",
|
||||
"value": "s3",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "region",
|
||||
"value": "us-west-1",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "secretKey",
|
||||
"value": "secret",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "accessKey",
|
||||
"value": "access",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://yaak.app/x/echo",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"yaak",
|
||||
"app"
|
||||
],
|
||||
"path": [
|
||||
"x",
|
||||
"echo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "API Key",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "apikey",
|
||||
"apikey": [
|
||||
{
|
||||
"key": "in",
|
||||
"value": "query",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "value",
|
||||
"value": "value",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "key",
|
||||
"value": "key",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://yaak.app/x/echo",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"yaak",
|
||||
"app"
|
||||
],
|
||||
"path": [
|
||||
"x",
|
||||
"echo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "JWT",
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "jwt",
|
||||
"jwt": [
|
||||
{
|
||||
"key": "header",
|
||||
"value": "{\n \"header\": \"foo\"\n}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "headerPrefix",
|
||||
"value": "Bearer",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "payload",
|
||||
"value": "{\n \"my\": \"payload\"\n}",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "isSecretBase64Encoded",
|
||||
"value": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"key": "secret",
|
||||
"value": "mysecret",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "algorithm",
|
||||
"value": "HS384",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "addTokenTo",
|
||||
"value": "header",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "queryParamKey",
|
||||
"value": "token",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [],
|
||||
"url": {
|
||||
"raw": "https://yaak.app/x/echo",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"yaak",
|
||||
"app"
|
||||
],
|
||||
"path": [
|
||||
"x",
|
||||
"echo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": []
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
"type": "basic",
|
||||
"basic": [
|
||||
{
|
||||
"key": "password",
|
||||
"value": "workspace_secret",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"key": "username",
|
||||
"value": "workspace",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"event": [
|
||||
{
|
||||
"listen": "prerequest",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"packages": {},
|
||||
"requests": {},
|
||||
"exec": [
|
||||
""
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"type": "text/javascript",
|
||||
"packages": {},
|
||||
"requests": {},
|
||||
"exec": [
|
||||
""
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"variable": [
|
||||
{
|
||||
"key": "COLLECTION VARIABLE",
|
||||
"value": "collection variable"
|
||||
}
|
||||
]
|
||||
}
|
||||
304
plugins/importer-postman/tests/fixtures/auth.output.json
vendored
Normal file
304
plugins/importer-postman/tests/fixtures/auth.output.json
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
{
|
||||
"resources": {
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_0",
|
||||
"name": "Authentication",
|
||||
"authenticationType": "basic",
|
||||
"authentication": {
|
||||
"username": "workspace",
|
||||
"password": "workspace_secret"
|
||||
}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_0",
|
||||
"name": "Global Variables",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"variables": [
|
||||
{
|
||||
"name": "COLLECTION VARIABLE",
|
||||
"value": "collection variable"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_0",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "No Auth",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 0,
|
||||
"headers": [],
|
||||
"authenticationType": "none",
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_1",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "Inherit",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 1,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_2",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Auth Code",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 2,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "authorization_code",
|
||||
"clientId": "cliend id",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecet",
|
||||
"authorizationUrl": "https://github.com/login/oauth/authorize",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"redirectUri": "https://callback",
|
||||
"state": "state"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_3",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Auth Code (PKCE)",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 3,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "authorization_code",
|
||||
"clientId": "cliend id",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecet",
|
||||
"authorizationUrl": "https://github.com/login/oauth/authorize",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"redirectUri": "https://callback",
|
||||
"state": "state",
|
||||
"usePkce": true,
|
||||
"pkceChallengeMethod": "S256",
|
||||
"pkceCodeVerifier": "verifier"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_4",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Implicit",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 4,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "implicit",
|
||||
"clientId": "cliend id",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"authorizationUrl": "https://github.com/login/oauth/authorize",
|
||||
"redirectUri": "https://yaak.app/x/echo",
|
||||
"state": "state"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_5",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Password",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 5,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "password",
|
||||
"clientId": "clientid",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecret",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"username": "username",
|
||||
"password": "password"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_6",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "OAuth 2 Client Credentials",
|
||||
"method": "GET",
|
||||
"url": "${[hello]}",
|
||||
"urlParameters": [],
|
||||
"body": {
|
||||
"text": "{\n \"hello\": \"world\"\n}"
|
||||
},
|
||||
"bodyType": "application/json",
|
||||
"sortPriority": 6,
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"authenticationType": "oauth2",
|
||||
"authentication": {
|
||||
"name": "oauth2",
|
||||
"grantType": "client_credentials",
|
||||
"clientId": "clientid",
|
||||
"headerPrefix": "Bearer",
|
||||
"scope": "scope",
|
||||
"clientSecret": "clientsecret",
|
||||
"accessTokenUrl": "https://github.com/login/oauth/access_token"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_7",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "AWS V4",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 7,
|
||||
"headers": [],
|
||||
"authenticationType": "awsv4",
|
||||
"authentication": {
|
||||
"accessKeyId": "access",
|
||||
"secretAccessKey": "secret",
|
||||
"sessionToken": "session",
|
||||
"region": "us-west-1",
|
||||
"service": "s3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_8",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "API Key",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 8,
|
||||
"headers": [],
|
||||
"authenticationType": "apikey",
|
||||
"authentication": {
|
||||
"location": "query",
|
||||
"key": "value",
|
||||
"value": "key"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_9",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"folderId": null,
|
||||
"name": "JWT",
|
||||
"method": "GET",
|
||||
"url": "https://yaak.app/x/echo",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"sortPriority": 9,
|
||||
"headers": [],
|
||||
"authenticationType": "jwt",
|
||||
"authentication": {
|
||||
"algorithm": "HS384",
|
||||
"secret": "mysecret",
|
||||
"secretBase64": false,
|
||||
"payload": "{\n \"my\": \"payload\"\n}",
|
||||
"headerPrefix": "Bearer",
|
||||
"location": "header"
|
||||
}
|
||||
}
|
||||
],
|
||||
"folders": []
|
||||
}
|
||||
}
|
||||
@@ -3,86 +3,88 @@
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_0",
|
||||
"name": "New Collection"
|
||||
"id": "GENERATE_ID::WORKSPACE_1",
|
||||
"name": "New Collection",
|
||||
"authenticationType": null,
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"id": "GENERATE_ID::ENVIRONMENT_0",
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_1",
|
||||
"name": "Global Variables",
|
||||
"variables": [],
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"parentModel": "workspace"
|
||||
"variables": []
|
||||
}
|
||||
],
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_0",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_10",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"folderId": "GENERATE_ID::FOLDER_1",
|
||||
"name": "Request 1",
|
||||
"method": "GET",
|
||||
"url": "",
|
||||
"sortPriority": 2,
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"authentication": {},
|
||||
"sortPriority": 2,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"headers": []
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_1",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_11",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"folderId": "GENERATE_ID::FOLDER_0",
|
||||
"name": "Request 2",
|
||||
"method": "GET",
|
||||
"sortPriority": 3,
|
||||
"url": "",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"authentication": {},
|
||||
"sortPriority": 3,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"headers": []
|
||||
"authentication": {}
|
||||
},
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_2",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_12",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"folderId": null,
|
||||
"sortPriority": 4,
|
||||
"name": "Request 3",
|
||||
"method": "GET",
|
||||
"url": "",
|
||||
"urlParameters": [],
|
||||
"body": {},
|
||||
"bodyType": null,
|
||||
"authentication": {},
|
||||
"sortPriority": 4,
|
||||
"headers": [],
|
||||
"authenticationType": null,
|
||||
"headers": []
|
||||
"authentication": {}
|
||||
}
|
||||
],
|
||||
"folders": [
|
||||
{
|
||||
"model": "folder",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"sortPriority": 0,
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"id": "GENERATE_ID::FOLDER_0",
|
||||
"name": "Top Folder",
|
||||
"sortPriority": 0,
|
||||
"folderId": null
|
||||
},
|
||||
{
|
||||
"model": "folder",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_0",
|
||||
"sortPriority": 1,
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"id": "GENERATE_ID::FOLDER_1",
|
||||
"name": "Nested Folder",
|
||||
"sortPriority": 1,
|
||||
"folderId": "GENERATE_ID::FOLDER_0"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "baeare",
|
||||
"value": "my-token",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,18 +3,23 @@
|
||||
"workspaces": [
|
||||
{
|
||||
"model": "workspace",
|
||||
"id": "GENERATE_ID::WORKSPACE_1",
|
||||
"name": "New Collection"
|
||||
"id": "GENERATE_ID::WORKSPACE_2",
|
||||
"name": "New Collection",
|
||||
"authenticationType": "basic",
|
||||
"authentication": {
|
||||
"username": "globaluser",
|
||||
"password": "globalpass"
|
||||
}
|
||||
}
|
||||
],
|
||||
"environments": [
|
||||
{
|
||||
"id": "GENERATE_ID::ENVIRONMENT_1",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"model": "environment",
|
||||
"id": "GENERATE_ID::ENVIRONMENT_2",
|
||||
"name": "Global Variables",
|
||||
"parentId": null,
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"parentModel": "workspace",
|
||||
"parentId": null,
|
||||
"variables": [
|
||||
{
|
||||
"name": "COLLECTION VARIABLE",
|
||||
@@ -26,11 +31,10 @@
|
||||
"httpRequests": [
|
||||
{
|
||||
"model": "http_request",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_3",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_1",
|
||||
"id": "GENERATE_ID::HTTP_REQUEST_13",
|
||||
"workspaceId": "GENERATE_ID::WORKSPACE_2",
|
||||
"folderId": null,
|
||||
"name": "Form URL",
|
||||
"sortPriority": 0,
|
||||
"method": "POST",
|
||||
"url": "example.com/:foo/:bar",
|
||||
"urlParameters": [
|
||||
@@ -71,10 +75,7 @@
|
||||
]
|
||||
},
|
||||
"bodyType": "multipart/form-data",
|
||||
"authentication": {
|
||||
"token": ""
|
||||
},
|
||||
"authenticationType": "bearer",
|
||||
"sortPriority": 0,
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-foo",
|
||||
@@ -91,7 +92,11 @@
|
||||
"value": "multipart/form-data",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"authenticationType": "bearer",
|
||||
"authentication": {
|
||||
"token": "my-token"
|
||||
}
|
||||
}
|
||||
],
|
||||
"folders": []
|
||||
|
||||
@@ -17,7 +17,9 @@ describe('importer-postman', () => {
|
||||
const expected = fs.readFileSync(path.join(p, fixture.replace('.input', '.output')), 'utf-8');
|
||||
const result = convertPostman(contents);
|
||||
// console.log(JSON.stringify(result, null, 2))
|
||||
expect(result).toEqual(JSON.parse(expected));
|
||||
expect(JSON.stringify(result, null, 2)).toEqual(
|
||||
JSON.stringify(JSON.parse(expected), null, 2),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -68,11 +68,11 @@ export function migrateImport(contents: string) {
|
||||
|
||||
// Migrate v4 to v5
|
||||
for (const environment of parsed.resources.environments ?? []) {
|
||||
if ('base' in environment && environment.base) {
|
||||
if ('base' in environment && environment.base && environment.parentModel == null) {
|
||||
environment.parentModel = 'workspace';
|
||||
environment.parentId = null;
|
||||
delete environment.base;
|
||||
} else if ('base' in environment && !environment.base) {
|
||||
} else if ('base' in environment && !environment.base && environment.parentModel == null) {
|
||||
environment.parentModel = 'environment';
|
||||
environment.parentId = null;
|
||||
delete environment.base;
|
||||
|
||||
@@ -5,9 +5,29 @@ export const plugin: PluginDefinition = {
|
||||
{
|
||||
name: 'base64.encode',
|
||||
description: 'Encode a value to base64',
|
||||
args: [{ label: 'Plain Text', type: 'text', name: 'value', multiLine: true }],
|
||||
args: [
|
||||
{
|
||||
label: 'Encoding',
|
||||
type: 'select',
|
||||
name: 'encoding',
|
||||
defaultValue: 'base64',
|
||||
options: [
|
||||
{
|
||||
label: 'Base64',
|
||||
value: 'base64',
|
||||
},
|
||||
{
|
||||
label: 'Base64 URL-safe',
|
||||
value: 'base64url',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ label: 'Plain Text', type: 'text', name: 'value', multiLine: true },
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
return Buffer.from(String(args.values.value ?? '')).toString('base64');
|
||||
return Buffer.from(String(args.values.value ?? '')).toString(
|
||||
args.values.encoding === 'base64url' ? 'base64url' : 'base64',
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import fs from 'node:fs';
|
||||
|
||||
const options = [
|
||||
{ label: 'ASCII', value: 'ascii' },
|
||||
{ label: 'UTF-8', value: 'utf8' },
|
||||
{ label: 'UTF-16 LE', value: 'utf16le' },
|
||||
{ label: 'Base64', value: 'base64' },
|
||||
{ label: 'Base64 URL-safe', value: 'base64url' },
|
||||
{ label: 'Latin-1', value: 'latin1' },
|
||||
{ label: 'Hexadecimal', value: 'hex' },
|
||||
];
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'fs.readFile',
|
||||
description: 'Read the contents of a file as utf-8',
|
||||
args: [{ title: 'Select File', type: 'file', name: 'path', label: 'File' }],
|
||||
args: [
|
||||
{ title: 'Select File', type: 'file', name: 'path', label: 'File' },
|
||||
{
|
||||
title: 'Select encoding',
|
||||
type: 'select',
|
||||
name: 'encoding',
|
||||
label: 'Encoding',
|
||||
defaultValue: 'utf8',
|
||||
description: 'Specifies how the file’s bytes are decoded into text when read',
|
||||
options,
|
||||
},
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.path) return null;
|
||||
if (!args.values.path || !args.values.encoding) return null;
|
||||
|
||||
try {
|
||||
return fs.promises.readFile(String(args.values.path ?? ''), 'utf-8');
|
||||
return fs.promises.readFile(String(args.values.path ?? ''), {
|
||||
encoding: String(args.values.encoding ?? 'utf-8') as BufferEncoding,
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
12
plugins/template-function-random/package.json
Normal file
12
plugins/template-function-random/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@yaak/template-function-random",
|
||||
"displayName": "Random Template Functions",
|
||||
"description": "Template functions for generating random values",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"lint":"tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
}
|
||||
}
|
||||
43
plugins/template-function-random/src/index.ts
Normal file
43
plugins/template-function-random/src/index.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'random.range',
|
||||
description: 'Generate a random number between two values',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'min',
|
||||
label: 'Minimum',
|
||||
defaultValue: '0',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'max',
|
||||
label: 'Maximum',
|
||||
defaultValue: '1',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'decimals',
|
||||
optional: true,
|
||||
label: 'Decimal Places',
|
||||
},
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
const min = args.values.min ? parseInt(String(args.values.min ?? '0')) : 0;
|
||||
const max = args.values.max ? parseInt(String(args.values.max ?? '1')) : 1;
|
||||
const decimals = args.values.decimals
|
||||
? parseInt(String(args.values.decimals ?? '0'))
|
||||
: null;
|
||||
|
||||
let value = Math.random() * (max - min) + min;
|
||||
if (decimals !== null) {
|
||||
value = Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
|
||||
}
|
||||
return String(value);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
3
plugins/template-function-random/tsconfig.json
Normal file
3
plugins/template-function-random/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { HttpUrlParameter } from '@yaakapp-internal/models';
|
||||
import type { AnyModel, HttpUrlParameter } from '@yaakapp-internal/models';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
@@ -96,5 +96,59 @@ export const plugin: PluginDefinition = {
|
||||
return renderedValue;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'request.name',
|
||||
args: [
|
||||
{
|
||||
name: 'requestId',
|
||||
label: 'Http Request',
|
||||
type: 'http_request',
|
||||
},
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
const requestId = String(args.values.requestId ?? 'n/a');
|
||||
const httpRequest = await ctx.httpRequest.getById({ id: requestId });
|
||||
if (httpRequest == null) return null;
|
||||
|
||||
return resolvedModelName(httpRequest);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// TODO: Use a common function for this, but it fails to build on windows during CI if I try importing it here
|
||||
export function resolvedModelName(r: AnyModel | null): string {
|
||||
if (r == null) return '';
|
||||
|
||||
if (!('url' in r) || r.model === 'plugin') {
|
||||
return 'name' in r ? r.name : '';
|
||||
}
|
||||
|
||||
// Return name if it has one
|
||||
if ('name' in r && r.name) {
|
||||
return r.name;
|
||||
}
|
||||
|
||||
// Replace variable syntax with variable name
|
||||
const withoutVariables = r.url.replace(/\$\{\[\s*([^\]\s]+)\s*]}/g, '$1');
|
||||
if (withoutVariables.trim() === '') {
|
||||
return r.model === 'http_request'
|
||||
? r.bodyType && r.bodyType === 'graphql'
|
||||
? 'GraphQL Request'
|
||||
: 'HTTP Request'
|
||||
: r.model === 'websocket_request'
|
||||
? 'WebSocket Request'
|
||||
: 'gRPC Request';
|
||||
}
|
||||
|
||||
// GRPC gets nice short names
|
||||
if (r.model === 'grpc_request' && r.service != null && r.method != null) {
|
||||
const shortService = r.service.split('.').pop();
|
||||
return `${shortService}/${r.method}`;
|
||||
}
|
||||
|
||||
// Strip unnecessary protocol
|
||||
const withoutProto = withoutVariables.replace(/^(http|https|ws|wss):\/\//, '');
|
||||
|
||||
return withoutProto;
|
||||
}
|
||||
|
||||
@@ -3,25 +3,44 @@ import type {
|
||||
CallTemplateFunctionArgs,
|
||||
Context,
|
||||
FormInput,
|
||||
GetHttpAuthenticationConfigRequest,
|
||||
HttpResponse,
|
||||
PluginDefinition,
|
||||
RenderPurpose,
|
||||
} from '@yaakapp/api';
|
||||
import type { DynamicTemplateFunctionArg } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||
import { JSONPath } from 'jsonpath-plus';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import xpath from 'xpath';
|
||||
|
||||
const BEHAVIOR_TTL = 'ttl';
|
||||
const BEHAVIOR_ALWAYS = 'always';
|
||||
const BEHAVIOR_SMART = 'smart';
|
||||
|
||||
const behaviorArg: FormInput = {
|
||||
type: 'select',
|
||||
name: 'behavior',
|
||||
label: 'Sending Behavior',
|
||||
defaultValue: 'smart',
|
||||
options: [
|
||||
{ label: 'When no responses', value: 'smart' },
|
||||
{ label: 'Always', value: 'always' },
|
||||
{ label: 'When no responses', value: BEHAVIOR_SMART },
|
||||
{ label: 'Always', value: BEHAVIOR_ALWAYS },
|
||||
{ label: 'When expired', value: BEHAVIOR_TTL },
|
||||
],
|
||||
};
|
||||
|
||||
const ttlArg: DynamicTemplateFunctionArg = {
|
||||
type: 'text',
|
||||
name: 'ttl',
|
||||
label: 'Expiration Time (seconds)',
|
||||
placeholder: '0',
|
||||
description: 'Resend the request when the latest response is older than this many seconds, or if there are no responses yet.',
|
||||
dynamic(_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) {
|
||||
const show = values.behavior === BEHAVIOR_TTL;
|
||||
return { hidden: !show };
|
||||
},
|
||||
};
|
||||
|
||||
const requestArg: FormInput = {
|
||||
type: 'http_request',
|
||||
name: 'request',
|
||||
@@ -42,6 +61,7 @@ export const plugin: PluginDefinition = {
|
||||
placeholder: 'Content-Type',
|
||||
},
|
||||
behaviorArg,
|
||||
ttlArg,
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request || !args.values.header) return null;
|
||||
@@ -50,6 +70,7 @@ export const plugin: PluginDefinition = {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -72,6 +93,7 @@ export const plugin: PluginDefinition = {
|
||||
placeholder: '$.books[0].id or /books[0]/id',
|
||||
},
|
||||
behaviorArg,
|
||||
ttlArg,
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request || !args.values.path) return null;
|
||||
@@ -80,6 +102,7 @@ export const plugin: PluginDefinition = {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -113,7 +136,7 @@ export const plugin: PluginDefinition = {
|
||||
name: 'response.body.raw',
|
||||
description: 'Access the entire response body, as text',
|
||||
aliases: ['response'],
|
||||
args: [requestArg, behaviorArg],
|
||||
args: [requestArg, behaviorArg, ttlArg],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request) return null;
|
||||
|
||||
@@ -121,6 +144,7 @@ export const plugin: PluginDefinition = {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -177,9 +201,11 @@ async function getResponse(
|
||||
requestId,
|
||||
behavior,
|
||||
purpose,
|
||||
ttl,
|
||||
}: {
|
||||
requestId: string;
|
||||
behavior: string | null;
|
||||
ttl: string | null;
|
||||
purpose: RenderPurpose;
|
||||
},
|
||||
): Promise<HttpResponse | null> {
|
||||
@@ -203,7 +229,11 @@ async function getResponse(
|
||||
const finalBehavior = behavior === 'always' && purpose === 'preview' ? 'smart' : behavior;
|
||||
|
||||
// Send if no responses and "smart," or "always"
|
||||
if ((finalBehavior === 'smart' && response == null) || finalBehavior === 'always') {
|
||||
if (
|
||||
(finalBehavior === 'smart' && response == null) ||
|
||||
finalBehavior === 'always' ||
|
||||
(finalBehavior === BEHAVIOR_TTL && shouldSendExpired(response, ttl))
|
||||
) {
|
||||
// NOTE: Render inside this conditional, or we'll get infinite recursion (render->render->...)
|
||||
const renderedHttpRequest = await ctx.httpRequest.render({ httpRequest, purpose });
|
||||
response = await ctx.httpRequest.send({ httpRequest: renderedHttpRequest });
|
||||
@@ -211,3 +241,12 @@ async function getResponse(
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
function shouldSendExpired(response: HttpResponse | null, ttl: string | null): boolean {
|
||||
if (response == null) return true;
|
||||
const ttlSeconds = parseInt(ttl || '0');
|
||||
if (isNaN(ttlSeconds)) throw new Error(`Invalid TTL "${ttl}"`);
|
||||
const nowMillis = Date.now();
|
||||
const respMillis = new Date(response.createdAt + 'Z').getTime();
|
||||
return respMillis + ttlSeconds * 1000 < nowMillis;
|
||||
}
|
||||
|
||||
@@ -52,21 +52,30 @@ export const plugin: PluginDefinition = {
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'timestamp.unix',
|
||||
description: 'Get the current timestamp in seconds',
|
||||
args: [],
|
||||
onRender: async () => String(Math.floor(Date.now() / 1000)),
|
||||
description: 'Get the timestamp in seconds',
|
||||
args: [dateArg],
|
||||
onRender: async (_ctx, args) => {
|
||||
const d = parseDateString(String(args.values.date ?? ''));
|
||||
return String(Math.floor(d.getTime() / 1000));
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timestamp.unixMillis',
|
||||
description: 'Get the current timestamp in milliseconds',
|
||||
args: [],
|
||||
onRender: async () => String(Date.now()),
|
||||
description: 'Get the timestamp in milliseconds',
|
||||
args: [dateArg],
|
||||
onRender: async (_ctx, args) => {
|
||||
const d = parseDateString(String(args.values.date ?? ''));
|
||||
return String(d.getTime());
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timestamp.iso8601',
|
||||
description: 'Get the current date in ISO8601 format',
|
||||
args: [],
|
||||
onRender: async () => new Date().toISOString(),
|
||||
description: 'Get the date in ISO8601 format',
|
||||
args: [dateArg],
|
||||
onRender: async (_ctx, args) => {
|
||||
const d = parseDateString(String(args.values.date ?? ''));
|
||||
return d.toISOString();
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timestamp.format',
|
||||
@@ -155,7 +164,7 @@ export function formatDatetime(args: {
|
||||
format?: string;
|
||||
in?: ContextFn<Date>;
|
||||
}): string {
|
||||
const { date, format = 'yyyy-MM-dd HH:mm:ss' } = args;
|
||||
const { date, format } = args;
|
||||
const d = parseDateString(date ?? '');
|
||||
return formatDate(d, String(format), { in: args.in });
|
||||
return formatDate(d, String(format || 'yyyy-MM-dd HH:mm:ss'), { in: args.in });
|
||||
}
|
||||
|
||||
300
src-tauri/Cargo.lock
generated
300
src-tauri/Cargo.lock
generated
@@ -2,15 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
@@ -85,12 +76,6 @@ dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.3.2"
|
||||
@@ -428,21 +413,6 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base32"
|
||||
version = "0.5.1"
|
||||
@@ -717,7 +687,7 @@ dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -816,17 +786,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.41"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1290,7 +1259,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2002,12 +1971,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "gio"
|
||||
version = "0.18.4"
|
||||
@@ -2389,9 +2352,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.14"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
|
||||
checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -2405,7 +2368,7 @@ dependencies = [
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"socket2 0.6.1",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
@@ -3205,7 +3168,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -3628,15 +3591,6 @@ dependencies = [
|
||||
"objc2-security",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
@@ -3782,7 +3736,7 @@ dependencies = [
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4334,8 +4288,8 @@ dependencies = [
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.12",
|
||||
"socket2 0.5.10",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -4356,7 +4310,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -4371,7 +4325,7 @@ dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
@@ -4552,7 +4506,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4766,12 +4720,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
@@ -4815,9 +4763,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.27"
|
||||
version = "0.23.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@@ -4851,9 +4799,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eda84358ed17f1f354cf4b1909ad346e6c7bc2513e8c40eb08e0157aa13a9070"
|
||||
checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
@@ -4878,9 +4826,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.3"
|
||||
version = "0.103.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
|
||||
checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -4981,7 +4929,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5065,9 +5013,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.226"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
@@ -5107,18 +5055,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.226"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.226"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5138,14 +5086,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5392,6 +5341,16 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "softbuffer"
|
||||
version = "0.4.6"
|
||||
@@ -5587,9 +5546,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.34.3"
|
||||
version = "0.34.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7"
|
||||
checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"block2 0.6.1",
|
||||
@@ -5661,9 +5620,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.8.5"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c"
|
||||
checksum = "7f07c6590706b2fc0ab287b041cf5ce9c435b3850bdae5571e19d9d27584e89d"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5701,7 +5660,7 @@ dependencies = [
|
||||
"tauri-runtime",
|
||||
"tauri-runtime-wry",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tray-icon",
|
||||
"url",
|
||||
@@ -5714,9 +5673,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f"
|
||||
checksum = "f71be1f494b683ac439e6d61c16ab5c472c6f9c6ee78995b29556d9067c021a1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -5736,9 +5695,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a"
|
||||
checksum = "6c1fe64c74cc40f90848281a90058a6db931eb400b60205840e09801ee30f190"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@@ -5754,7 +5713,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"syn 2.0.101",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -5763,9 +5722,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e"
|
||||
checksum = "260c5d2eb036b76206b9fca20b7be3614cfd21046c5396f7959e0e64a4b07f2f"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -5777,9 +5736,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f"
|
||||
checksum = "3d7ce9aab979296b2f91e6fbf154207c2e3512b12ddca0b24bfa0e0cde6b2976"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@@ -5804,7 +5763,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5821,7 +5780,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tracing",
|
||||
"url",
|
||||
"windows-registry",
|
||||
@@ -5842,7 +5801,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -5863,7 +5822,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"toml 0.9.5",
|
||||
"url",
|
||||
]
|
||||
@@ -5886,7 +5845,7 @@ dependencies = [
|
||||
"swift-rs",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -5906,7 +5865,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"url",
|
||||
"windows",
|
||||
"zbus",
|
||||
@@ -5927,7 +5886,7 @@ dependencies = [
|
||||
"sys-locale",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5947,7 +5906,7 @@ dependencies = [
|
||||
"shared_child",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -5961,7 +5920,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin-deep-link",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
"zbus",
|
||||
@@ -5991,7 +5950,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
@@ -6011,14 +5970,14 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846"
|
||||
checksum = "3367f0b47df90e9195cd9f04a56b0055a2cba45aa11923c6c253d748778176fc"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
@@ -6032,7 +5991,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
@@ -6041,9 +6000,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.8.1"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807"
|
||||
checksum = "80d91d29ca680c545364cf75ba2f2e3c7ea2ab6376bfa3be26b56fa2463a5b5e"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@@ -6068,9 +6027,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.7.0"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212"
|
||||
checksum = "f6b8bbe426abdbf52d050e52ed693130dbd68375b9ad82a3fb17efb4c8d85673"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli",
|
||||
@@ -6096,7 +6055,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"toml 0.9.5",
|
||||
"url",
|
||||
"urlpattern",
|
||||
@@ -6159,11 +6118,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
"thiserror-impl 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6179,9 +6138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6268,27 +6227,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.1"
|
||||
version = "1.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"socket2 0.6.1",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6478,7 +6436,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project",
|
||||
"prost",
|
||||
"socket2",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower 0.4.13",
|
||||
@@ -6614,7 +6572,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -6645,21 +6603,21 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "ts-rs"
|
||||
version = "11.0.1"
|
||||
version = "11.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ef1b7a6d914a34127ed8e1fa927eb7088903787bcded4fa3eef8f85ee1568be"
|
||||
checksum = "4994acea2522cd2b3b85c1d9529a55991e3ad5e25cdcd3de9d505972c4379424"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ts-rs-macros"
|
||||
version = "11.0.1"
|
||||
version = "11.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9d4ed7b4c18cc150a6a0a1e9ea1ecfa688791220781af6e119f9599a8502a0a"
|
||||
checksum = "ee6ff59666c9cbaec3533964505d39154dc4e0a56151fdea30a09ed0301f62e2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6682,7 +6640,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@@ -7204,7 +7162,7 @@ version = "0.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"windows",
|
||||
"windows-core",
|
||||
]
|
||||
@@ -7270,7 +7228,7 @@ dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
@@ -7291,7 +7249,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
@@ -7303,7 +7261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
@@ -7335,6 +7293,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
@@ -7342,7 +7306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7351,7 +7315,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
@@ -7362,7 +7326,7 @@ version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7371,7 +7335,7 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7410,6 +7374,15 @@ dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
@@ -7478,7 +7451,7 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7487,7 +7460,7 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-link 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7718,7 +7691,7 @@ dependencies = [
|
||||
"os_pipe",
|
||||
"rustix 0.38.44",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tree_magic_mini",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
@@ -7734,9 +7707,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.53.3"
|
||||
version = "0.53.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90"
|
||||
checksum = "6d78ec082b80fa088569a970d043bb3050abaabf4454101d44514ee8d9a8c9f6"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.1",
|
||||
@@ -7766,7 +7739,7 @@ dependencies = [
|
||||
"sha2",
|
||||
"soup3",
|
||||
"tao-macros",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
@@ -7843,6 +7816,7 @@ dependencies = [
|
||||
"cookie",
|
||||
"eventsource-client",
|
||||
"http",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"md5 0.8.0",
|
||||
"mime_guess",
|
||||
@@ -7865,9 +7839,10 @@ dependencies = [
|
||||
"tauri-plugin-single-instance",
|
||||
"tauri-plugin-updater",
|
||||
"tauri-plugin-window-state",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower-service",
|
||||
"ts-rs",
|
||||
"uuid",
|
||||
"yaak-common",
|
||||
@@ -7894,7 +7869,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"tauri",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7909,7 +7884,7 @@ dependencies = [
|
||||
"serde",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"yaak-models",
|
||||
]
|
||||
|
||||
@@ -7921,7 +7896,7 @@ dependencies = [
|
||||
"serde",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
@@ -7937,7 +7912,7 @@ dependencies = [
|
||||
"serde_yaml",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs",
|
||||
"yaak-models",
|
||||
"yaak-sync",
|
||||
@@ -7991,7 +7966,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs",
|
||||
"yaak-common",
|
||||
"yaak-models",
|
||||
@@ -8030,9 +8005,9 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-dialog",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"thiserror 2.0.17",
|
||||
"ts-rs",
|
||||
"yaak-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8057,7 +8032,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-shell",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"ts-rs",
|
||||
@@ -8091,7 +8066,7 @@ dependencies = [
|
||||
"sha1",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"yaak-models",
|
||||
@@ -8102,10 +8077,11 @@ name = "yaak-templates"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
"wasm-bindgen",
|
||||
@@ -8123,7 +8099,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"yaak-http",
|
||||
@@ -8344,7 +8320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aed5f10c571472911e37d8f7601a8dfba52b4f7f73a344015291b82ab292faf6"
|
||||
dependencies = [
|
||||
"log",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror 2.0.17",
|
||||
"zip",
|
||||
]
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ updater = []
|
||||
license = ["yaak-license"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.4.1", features = [] }
|
||||
tauri-build = { version = "2.5.0", features = [] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
openssl-sys = { version = "0.9.105", features = ["vendored"] } # For Ubuntu installation to work
|
||||
@@ -68,6 +68,8 @@ tauri-plugin-shell = { workspace = true }
|
||||
tauri-plugin-single-instance = { version = "2.3.4", features = ["deep-link"] }
|
||||
tauri-plugin-updater = "2.9.0"
|
||||
tauri-plugin-window-state = "2.4.0"
|
||||
hyper-util = { version = "0.1.17", default-features = false, features = ["client-legacy"] }
|
||||
tower-service = "0.3.3"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
tokio-stream = "0.1.17"
|
||||
@@ -89,23 +91,23 @@ yaak-templates = { workspace = true }
|
||||
yaak-ws = { path = "yaak-ws" }
|
||||
|
||||
[workspace.dependencies]
|
||||
chrono = "0.4.41"
|
||||
chrono = "0.4.42"
|
||||
hex = "0.4.3"
|
||||
keyring = "3.6.3"
|
||||
reqwest = "0.12.20"
|
||||
reqwest_cookie_store = "0.8.0"
|
||||
rustls = { version = "0.23.27", default-features = false }
|
||||
rustls-platform-verifier = "0.6.0"
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
rustls = { version = "0.23.33", default-features = false }
|
||||
rustls-platform-verifier = "0.6.1"
|
||||
serde = "1.0.228"
|
||||
serde_json = "1.0.145"
|
||||
sha2 = "0.10.9"
|
||||
tauri = "2.8.5"
|
||||
tauri-plugin = "2.4.0"
|
||||
tauri = "2.9.0"
|
||||
tauri-plugin = "2.5.0"
|
||||
tauri-plugin-dialog = "2.4.0"
|
||||
tauri-plugin-shell = "2.3.1"
|
||||
thiserror = "2.0.12"
|
||||
tokio = "1.45.1"
|
||||
ts-rs = "11.0.1"
|
||||
thiserror = "2.0.17"
|
||||
tokio = "1.48.0"
|
||||
ts-rs = "11.1.0"
|
||||
yaak-common = { path = "yaak-common" }
|
||||
yaak-crypto = { path = "yaak-crypto" }
|
||||
yaak-fonts = { path = "yaak-fonts" }
|
||||
|
||||
54
src-tauri/src/dns.rs
Normal file
54
src-tauri/src/dns.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use hyper_util::client::legacy::connect::dns::{
|
||||
GaiResolver as HyperGaiResolver, Name as HyperName,
|
||||
};
|
||||
use reqwest::dns::{Addrs, Name, Resolve, Resolving};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tower_service::Service;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct LocalhostResolver {
|
||||
fallback: HyperGaiResolver,
|
||||
}
|
||||
|
||||
impl LocalhostResolver {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let resolver = HyperGaiResolver::new();
|
||||
Arc::new(Self { fallback: resolver })
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve for LocalhostResolver {
|
||||
fn resolve(&self, name: Name) -> Resolving {
|
||||
let host = name.as_str().to_lowercase();
|
||||
|
||||
let is_localhost = host.ends_with(".localhost");
|
||||
if is_localhost {
|
||||
// Port 0 is fine; reqwest replaces it with the URL's explicit
|
||||
// port or the scheme’s default (80/443, etc.).
|
||||
// (See docs note below.)
|
||||
let addrs: Vec<SocketAddr> = vec![
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
|
||||
SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
|
||||
];
|
||||
|
||||
return Box::pin(async move {
|
||||
Ok::<Addrs, Box<dyn std::error::Error + Send + Sync>>(Box::new(addrs.into_iter()))
|
||||
});
|
||||
}
|
||||
|
||||
let mut fallback = self.fallback.clone();
|
||||
let name_str = name.as_str().to_string();
|
||||
Box::pin(async move {
|
||||
match HyperName::from_str(&name_str) {
|
||||
Ok(n) => fallback
|
||||
.call(n)
|
||||
.await
|
||||
.map(|addrs| Box::new(addrs) as Addrs)
|
||||
.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync>),
|
||||
Err(e) => Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,9 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] yaak_common::error::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
ClipboardError(#[from] tauri_plugin_clipboard_manager::Error),
|
||||
|
||||
#[error("Updater error: {0}")]
|
||||
UpdaterError(#[from] tauri_plugin_updater::Error),
|
||||
|
||||
|
||||
@@ -1,48 +1,74 @@
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use log::debug;
|
||||
use std::sync::OnceLock;
|
||||
use tauri::{AppHandle, Runtime};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
|
||||
const NAMESPACE: &str = "analytics";
|
||||
const NUM_LAUNCHES_KEY: &str = "num_launches";
|
||||
const LAST_VERSION_KEY: &str = "last_tracked_version";
|
||||
const PREV_VERSION_KEY: &str = "last_tracked_version_prev";
|
||||
const VERSION_SINCE_KEY: &str = "last_tracked_version_since";
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct LaunchEventInfo {
|
||||
pub current_version: String,
|
||||
pub previous_version: String,
|
||||
pub launched_after_update: bool,
|
||||
pub version_since: NaiveDateTime,
|
||||
pub user_since: NaiveDateTime,
|
||||
pub num_launches: i32,
|
||||
}
|
||||
|
||||
pub async fn store_launch_history<R: Runtime>(app_handle: &AppHandle<R>) -> LaunchEventInfo {
|
||||
let last_tracked_version_key = "last_tracked_version";
|
||||
static LAUNCH_INFO: OnceLock<LaunchEventInfo> = OnceLock::new();
|
||||
|
||||
let mut info = LaunchEventInfo::default();
|
||||
pub fn get_or_upsert_launch_info<R: Runtime>(app_handle: &AppHandle<R>) -> &LaunchEventInfo {
|
||||
LAUNCH_INFO.get_or_init(|| {
|
||||
let now = Utc::now().naive_utc();
|
||||
let mut info = LaunchEventInfo {
|
||||
version_since: app_handle.db().get_key_value_dte(NAMESPACE, VERSION_SINCE_KEY, now),
|
||||
current_version: app_handle.package_info().version.to_string(),
|
||||
user_since: app_handle.db().get_settings().created_at,
|
||||
num_launches: app_handle.db().get_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, 0) + 1,
|
||||
|
||||
info.num_launches = get_num_launches(app_handle).await + 1;
|
||||
info.current_version = app_handle.package_info().version.to_string();
|
||||
// The rest will be set below
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
app_handle
|
||||
.with_tx(|tx| {
|
||||
info.previous_version =
|
||||
tx.get_key_value_string(NAMESPACE, last_tracked_version_key, "");
|
||||
app_handle
|
||||
.with_tx(|tx| {
|
||||
// Load the previously tracked version
|
||||
let curr_db = tx.get_key_value_str(NAMESPACE, LAST_VERSION_KEY, "");
|
||||
let prev_db = tx.get_key_value_str(NAMESPACE, PREV_VERSION_KEY, "");
|
||||
|
||||
if !info.previous_version.is_empty() {
|
||||
info.launched_after_update = info.current_version != info.previous_version;
|
||||
};
|
||||
// We just updated if the app version is different from the last tracked version we stored
|
||||
if !curr_db.is_empty() && info.current_version != curr_db {
|
||||
info.launched_after_update = true;
|
||||
}
|
||||
|
||||
// Update key values
|
||||
// If we just updated, track the previous version as the "previous" current version
|
||||
if info.launched_after_update {
|
||||
info.previous_version = curr_db.clone();
|
||||
info.version_since = now;
|
||||
} else {
|
||||
info.previous_version = prev_db.clone();
|
||||
}
|
||||
|
||||
let source = &UpdateSource::Background;
|
||||
let version = info.current_version.as_str();
|
||||
tx.set_key_value_string(NAMESPACE, last_tracked_version_key, version, source);
|
||||
tx.set_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, source);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
// Rotate stored versions: move previous into the "prev" slot before overwriting
|
||||
let source = &UpdateSource::Background;
|
||||
|
||||
info
|
||||
}
|
||||
|
||||
pub async fn get_num_launches<R: Runtime>(app_handle: &AppHandle<R>) -> i32 {
|
||||
app_handle.db().get_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, 0)
|
||||
tx.set_key_value_str(NAMESPACE, PREV_VERSION_KEY, &info.previous_version, source);
|
||||
tx.set_key_value_str(NAMESPACE, LAST_VERSION_KEY, &info.current_version, source);
|
||||
tx.set_key_value_dte(NAMESPACE, VERSION_SINCE_KEY, info.version_since, source);
|
||||
tx.set_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, info.num_launches, source);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
debug!("Initialized launch info");
|
||||
|
||||
info
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ use yaak_plugins::events::{
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_templates::{RenderErrorBehavior, RenderOptions};
|
||||
use crate::dns::LocalhostResolver;
|
||||
|
||||
pub async fn send_http_request<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
@@ -76,7 +78,11 @@ pub async fn send_http_request<R: Runtime>(
|
||||
RenderPurpose::Send,
|
||||
);
|
||||
|
||||
let request = match render_http_request(&resolved_request, environment_chain, &cb).await {
|
||||
let opt = RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
};
|
||||
|
||||
let request = match render_http_request(&resolved_request, environment_chain, &cb, &opt).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
return Ok(response_err(
|
||||
@@ -105,6 +111,7 @@ pub async fn send_http_request<R: Runtime>(
|
||||
.gzip(true)
|
||||
.brotli(true)
|
||||
.deflate(true)
|
||||
.dns_resolver(LocalhostResolver::new())
|
||||
.referer(false)
|
||||
.tls_info(true);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ pub(crate) async fn import_data<R: Runtime>(
|
||||
.workspaces
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Workspace>(v.id.as_str(), &mut id_map);
|
||||
v.id = maybe_gen_id::<Workspace, R>(window, v.id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
@@ -37,11 +37,12 @@ pub(crate) async fn import_data<R: Runtime>(
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Environment>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.id = maybe_gen_id::<Environment, R>(window, v.id.as_str(), &mut id_map);
|
||||
v.workspace_id =
|
||||
maybe_gen_id::<Workspace, R>(window, v.workspace_id.as_str(), &mut id_map);
|
||||
match (v.parent_model.as_str(), v.parent_id.clone().as_deref()) {
|
||||
("folder", Some(parent_id)) => {
|
||||
v.parent_id = Some(maybe_gen_id::<Folder>(&parent_id, &mut id_map));
|
||||
v.parent_id = Some(maybe_gen_id::<Folder, R>(window, &parent_id, &mut id_map));
|
||||
}
|
||||
("", _) => {
|
||||
// Fix any empty ones
|
||||
@@ -60,9 +61,10 @@ pub(crate) async fn import_data<R: Runtime>(
|
||||
.folders
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Folder>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v.id = maybe_gen_id::<Folder, R>(window, v.id.as_str(), &mut id_map);
|
||||
v.workspace_id =
|
||||
maybe_gen_id::<Workspace, R>(window, v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder, R>(window, v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
@@ -71,9 +73,10 @@ pub(crate) async fn import_data<R: Runtime>(
|
||||
.http_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<HttpRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v.id = maybe_gen_id::<HttpRequest, R>(window, v.id.as_str(), &mut id_map);
|
||||
v.workspace_id =
|
||||
maybe_gen_id::<Workspace, R>(window, v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder, R>(window, v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
@@ -82,9 +85,10 @@ pub(crate) async fn import_data<R: Runtime>(
|
||||
.grpc_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<GrpcRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v.id = maybe_gen_id::<GrpcRequest, R>(window, v.id.as_str(), &mut id_map);
|
||||
v.workspace_id =
|
||||
maybe_gen_id::<Workspace, R>(window, v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder, R>(window, v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
@@ -93,9 +97,10 @@ pub(crate) async fn import_data<R: Runtime>(
|
||||
.websocket_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<WebsocketRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v.id = maybe_gen_id::<WebsocketRequest, R>(window, v.id.as_str(), &mut id_map);
|
||||
v.workspace_id =
|
||||
maybe_gen_id::<Workspace, R>(window, v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder, R>(window, v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
extern crate core;
|
||||
use crate::encoding::read_response_body;
|
||||
use crate::error::Error::GenericError;
|
||||
use crate::error::Result;
|
||||
use crate::grpc::{build_metadata, metadata_to_map, resolve_grpc_request};
|
||||
use crate::http_request::{resolve_http_request, send_http_request};
|
||||
use crate::import::import_data;
|
||||
@@ -41,17 +42,19 @@ use yaak_plugins::events::{
|
||||
CallGrpcRequestActionArgs, CallGrpcRequestActionRequest, CallHttpRequestActionArgs,
|
||||
CallHttpRequestActionRequest, Color, FilterResponse, GetGrpcRequestActionsResponse,
|
||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, InternalEvent,
|
||||
InternalEventPayload, JsonPrimitive, PluginWindowContext, RenderPurpose, ShowToastRequest,
|
||||
GetHttpRequestActionsResponse, GetTemplateFunctionConfigResponse,
|
||||
GetTemplateFunctionSummaryResponse, InternalEvent, InternalEventPayload, JsonPrimitive,
|
||||
PluginWindowContext, RenderPurpose, ShowToastRequest,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_meta::PluginMetadata;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_sse::sse::ServerSentEvent;
|
||||
use yaak_templates::format::format_json;
|
||||
use yaak_templates::{Tokens, transform_args};
|
||||
use yaak_templates::format_json::format_json;
|
||||
use yaak_templates::{RenderErrorBehavior, RenderOptions, Tokens, transform_args};
|
||||
|
||||
mod commands;
|
||||
mod dns;
|
||||
mod encoding;
|
||||
mod error;
|
||||
mod grpc;
|
||||
@@ -126,6 +129,9 @@ async fn cmd_render_template<R: Runtime>(
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Preview,
|
||||
),
|
||||
&RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(result)
|
||||
@@ -167,6 +173,9 @@ async fn cmd_grpc_reflect<R: Runtime>(
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Send,
|
||||
),
|
||||
&RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -213,6 +222,9 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Send,
|
||||
),
|
||||
&RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -335,6 +347,9 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Send,
|
||||
),
|
||||
&RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("Failed to render template")
|
||||
@@ -404,6 +419,9 @@ async fn cmd_grpc_go<R: Runtime>(
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Send,
|
||||
),
|
||||
&RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -811,11 +829,40 @@ async fn cmd_grpc_request_actions<R: Runtime>(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_template_functions<R: Runtime>(
|
||||
async fn cmd_template_function_summaries<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> YaakResult<Vec<GetTemplateFunctionsResponse>> {
|
||||
Ok(plugin_manager.get_template_functions(&window).await?)
|
||||
) -> YaakResult<Vec<GetTemplateFunctionSummaryResponse>> {
|
||||
let results = plugin_manager.get_template_function_summaries(&window).await?;
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_template_function_config<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
function_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
model: AnyModel,
|
||||
environment_id: Option<&str>,
|
||||
) -> YaakResult<GetTemplateFunctionConfigResponse> {
|
||||
let (workspace_id, folder_id) = match model.clone() {
|
||||
AnyModel::HttpRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::GrpcRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::WebsocketRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::Folder(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::Workspace(m) => (m.id, None),
|
||||
m => {
|
||||
return Err(GenericError(format!(
|
||||
"Unsupported model to call template functions {m:?}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
let environment_chain =
|
||||
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
Ok(plugin_manager
|
||||
.get_template_function_config(&window, function_name, environment_chain, values, model.id())
|
||||
.await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -833,10 +880,10 @@ async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request: AnyModel,
|
||||
model: AnyModel,
|
||||
environment_id: Option<&str>,
|
||||
) -> YaakResult<GetHttpAuthenticationConfigResponse> {
|
||||
let (workspace_id, folder_id) = match request.clone() {
|
||||
let (workspace_id, folder_id) = match model.clone() {
|
||||
AnyModel::HttpRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::GrpcRequest(m) => (m.workspace_id, m.folder_id),
|
||||
AnyModel::WebsocketRequest(m) => (m.workspace_id, m.folder_id),
|
||||
@@ -851,7 +898,7 @@ async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
|
||||
Ok(plugin_manager
|
||||
.get_http_authentication_config(&window, environment_chain, auth_name, values, request.id())
|
||||
.get_http_authentication_config(&window, environment_chain, auth_name, values, model.id())
|
||||
.await?)
|
||||
}
|
||||
|
||||
@@ -991,6 +1038,35 @@ async fn cmd_save_response<R: Runtime>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_send_folder<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
window: WebviewWindow<R>,
|
||||
environment_id: Option<String>,
|
||||
cookie_jar_id: Option<String>,
|
||||
folder_id: &str,
|
||||
) -> YaakResult<()> {
|
||||
let requests = app_handle.db().list_http_requests_for_folder_recursive(folder_id)?;
|
||||
for request in requests {
|
||||
let app_handle = app_handle.clone();
|
||||
let window = window.clone();
|
||||
let environment_id = environment_id.clone();
|
||||
let cookie_jar_id = cookie_jar_id.clone();
|
||||
tokio::spawn(async move {
|
||||
let _ = cmd_send_http_request(
|
||||
app_handle,
|
||||
window,
|
||||
environment_id.as_deref(),
|
||||
cookie_jar_id.as_deref(),
|
||||
request,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_send_http_request<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
@@ -1102,7 +1178,7 @@ async fn cmd_install_plugin<R: Runtime>(
|
||||
async fn cmd_create_grpc_request<R: Runtime>(
|
||||
workspace_id: &str,
|
||||
name: &str,
|
||||
sort_priority: f32,
|
||||
sort_priority: f64,
|
||||
folder_id: Option<&str>,
|
||||
app_handle: AppHandle<R>,
|
||||
window: WebviewWindow<R>,
|
||||
@@ -1266,7 +1342,6 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_updater::Builder::default().build())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
@@ -1284,6 +1359,11 @@ pub fn run() {
|
||||
builder = builder.plugin(yaak_license::init());
|
||||
}
|
||||
|
||||
#[cfg(feature = "updater")]
|
||||
{
|
||||
builder = builder.plugin(tauri_plugin_updater::Builder::default().build());
|
||||
}
|
||||
|
||||
builder
|
||||
.setup(|app| {
|
||||
{
|
||||
@@ -1366,7 +1446,9 @@ pub fn run() {
|
||||
cmd_save_response,
|
||||
cmd_send_ephemeral_request,
|
||||
cmd_send_http_request,
|
||||
cmd_template_functions,
|
||||
cmd_send_folder,
|
||||
cmd_template_function_config,
|
||||
cmd_template_function_summaries,
|
||||
cmd_template_tokens_to_string,
|
||||
//
|
||||
//
|
||||
@@ -1384,7 +1466,7 @@ pub fn run() {
|
||||
let _ = window::create_main_window(app_handle, "/");
|
||||
let h = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let info = history::store_launch_history(&h).await;
|
||||
let info = history::get_or_upsert_launch_info(&h);
|
||||
debug!("Launched Yaak {:?}", info);
|
||||
});
|
||||
|
||||
@@ -1484,7 +1566,30 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
||||
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
||||
// want to block here
|
||||
tauri::async_runtime::spawn(async move {
|
||||
plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await;
|
||||
let ev = plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await;
|
||||
|
||||
let ev = match ev {
|
||||
Ok(Some(ev)) => ev,
|
||||
Ok(None) => return,
|
||||
Err(e) => {
|
||||
warn!("Failed to handle plugin event: {e:?}");
|
||||
let _ = app_handle.emit(
|
||||
"show_toast",
|
||||
InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: e.to_string(),
|
||||
color: Some(Color::Danger),
|
||||
icon: None,
|
||||
timeout: Some(30000),
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||
if let Err(e) = plugin_manager.reply(&event, &ev).await {
|
||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||
}
|
||||
});
|
||||
}
|
||||
plugin_manager.unsubscribe(rx_id.as_str()).await;
|
||||
@@ -1519,11 +1624,16 @@ async fn call_frontend<R: Runtime>(
|
||||
fn get_window_from_window_context<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
window_context: &PluginWindowContext,
|
||||
) -> Option<WebviewWindow<R>> {
|
||||
) -> Result<WebviewWindow<R>> {
|
||||
let label = match window_context {
|
||||
PluginWindowContext::Label { label, .. } => label,
|
||||
PluginWindowContext::None => {
|
||||
return app_handle.webview_windows().iter().next().map(|(_, w)| w.to_owned());
|
||||
return app_handle
|
||||
.webview_windows()
|
||||
.iter()
|
||||
.next()
|
||||
.map(|(_, w)| w.to_owned())
|
||||
.ok_or(GenericError("No windows open".to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1536,7 +1646,7 @@ fn get_window_from_window_context<R: Runtime>(
|
||||
error!("Failed to find window by {window_context:?}");
|
||||
}
|
||||
|
||||
window
|
||||
Ok(window.ok_or(GenericError(format!("Failed to find window for {}", label)))?)
|
||||
}
|
||||
|
||||
fn workspace_from_window<R: Runtime>(window: &WebviewWindow<R>) -> Option<Workspace> {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::history::get_num_launches;
|
||||
use crate::history::get_or_upsert_launch_info;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::debug;
|
||||
use log::{debug, info};
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow};
|
||||
@@ -77,6 +77,13 @@ impl YaakNotifier {
|
||||
|
||||
self.last_check = SystemTime::now();
|
||||
|
||||
if !app_handle.db().get_settings().check_notifications {
|
||||
info!("Notifications are disabled. Skipping check.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
debug!("Checking for notifications");
|
||||
|
||||
#[cfg(feature = "license")]
|
||||
let license_check = {
|
||||
use yaak_license::{LicenseCheckStatus, check_license};
|
||||
@@ -91,17 +98,17 @@ impl YaakNotifier {
|
||||
#[cfg(not(feature = "license"))]
|
||||
let license_check = "disabled".to_string();
|
||||
|
||||
let settings = window.db().get_settings();
|
||||
let num_launches = get_num_launches(app_handle).await;
|
||||
let info = app_handle.package_info().clone();
|
||||
let launch_info = get_or_upsert_launch_info(app_handle);
|
||||
let req = yaak_api_client(app_handle)?
|
||||
.request(Method::GET, "https://notify.yaak.app/notifications")
|
||||
.query(&[
|
||||
("version", info.version.to_string().as_str()),
|
||||
("launches", num_launches.to_string().as_str()),
|
||||
("installed", settings.created_at.format("%Y-%m-%d").to_string().as_str()),
|
||||
("version", &launch_info.current_version),
|
||||
("version_prev", &launch_info.previous_version),
|
||||
("launches", &launch_info.num_launches.to_string()),
|
||||
("installed", &launch_info.user_since.format("%Y-%m-%d").to_string()),
|
||||
("license", &license_check),
|
||||
("platform", get_os()),
|
||||
("updates", &get_updater_status(app_handle).to_string()),
|
||||
("platform", &get_os().to_string()),
|
||||
]);
|
||||
let resp = req.send().await?;
|
||||
if resp.status() != 200 {
|
||||
@@ -131,3 +138,33 @@ async fn get_kv<R: Runtime>(app_handle: &AppHandle<R>) -> Result<Vec<String>> {
|
||||
Some(v) => Ok(serde_json::from_str(&v.value)?),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn get_updater_status<R: Runtime>(app_handle: &AppHandle<R>) -> &'static str {
|
||||
#[cfg(not(feature = "updater"))]
|
||||
{
|
||||
// Updater is not enabled as a Rust feature
|
||||
return "missing";
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "updater", target_os = "linux"))]
|
||||
{
|
||||
let settings = app_handle.db().get_settings();
|
||||
if !settings.autoupdate {
|
||||
// Updates are explicitly disabled
|
||||
"disabled"
|
||||
} else if std::env::var("APPIMAGE").is_err() {
|
||||
// Updates are enabled, but unsupported
|
||||
"unsupported"
|
||||
} else {
|
||||
// Updates are enabled and supported
|
||||
"enabled"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "updater", not(target_os = "linux")))]
|
||||
{
|
||||
let settings = app_handle.db().get_settings();
|
||||
if settings.autoupdate { "enabled" } else { "disabled" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::error::Result;
|
||||
use crate::http_request::send_http_request;
|
||||
use crate::render::{render_grpc_request, render_http_request, render_json_value};
|
||||
use crate::window::{CreateWindowConfig, create_window};
|
||||
@@ -7,10 +8,12 @@ use crate::{
|
||||
};
|
||||
use chrono::Utc;
|
||||
use cookie::Cookie;
|
||||
use log::{error, warn};
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, State};
|
||||
use log::error;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use yaak_common::window::WorkspaceWindowTrait;
|
||||
use yaak_models::models::{HttpResponse, Plugin};
|
||||
use yaak_models::queries::any_request::AnyRequest;
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
use yaak_plugins::events::{
|
||||
@@ -20,108 +23,119 @@ use yaak_plugins::events::{
|
||||
RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest,
|
||||
TemplateRenderResponse, WindowNavigateEvent,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_handle::PluginHandle;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_templates::{RenderErrorBehavior, RenderOptions};
|
||||
|
||||
pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
event: &InternalEvent,
|
||||
plugin_handle: &PluginHandle,
|
||||
) {
|
||||
) -> Result<Option<InternalEventPayload>> {
|
||||
// debug!("Got event to app {event:?}");
|
||||
let window_context = event.window_context.to_owned();
|
||||
let response_event: Option<InternalEventPayload> = match event.clone().payload {
|
||||
match event.clone().payload {
|
||||
InternalEventPayload::CopyTextRequest(req) => {
|
||||
app_handle
|
||||
.clipboard()
|
||||
.write_text(req.text.as_str())
|
||||
.expect("Failed to write text to clipboard");
|
||||
Some(InternalEventPayload::CopyTextResponse(EmptyPayload {}))
|
||||
app_handle.clipboard().write_text(req.text.as_str())?;
|
||||
Ok(Some(InternalEventPayload::CopyTextResponse(EmptyPayload {})))
|
||||
}
|
||||
InternalEventPayload::ShowToastRequest(req) => {
|
||||
match window_context {
|
||||
PluginWindowContext::Label { label, .. } => app_handle
|
||||
.emit_to(label, "show_toast", req)
|
||||
.expect("Failed to emit show_toast to window"),
|
||||
_ => app_handle.emit("show_toast", req).expect("Failed to emit show_toast"),
|
||||
PluginWindowContext::Label { label, .. } => {
|
||||
app_handle.emit_to(label, "show_toast", req)?
|
||||
}
|
||||
_ => app_handle.emit("show_toast", req)?,
|
||||
};
|
||||
Some(InternalEventPayload::ShowToastResponse(EmptyPayload {}))
|
||||
Ok(Some(InternalEventPayload::ShowToastResponse(EmptyPayload {})))
|
||||
}
|
||||
InternalEventPayload::PromptTextRequest(_) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
call_frontend(&window, event).await
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
Ok(call_frontend(&window, event).await)
|
||||
}
|
||||
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
||||
let http_responses = app_handle
|
||||
.db()
|
||||
.list_http_responses_for_request(&req.request_id, req.limit.map(|l| l as u64))
|
||||
.unwrap_or_default();
|
||||
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
||||
Ok(Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
||||
http_responses,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
|
||||
let http_request = app_handle.db().get_http_request(&req.id).ok();
|
||||
Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
|
||||
Ok(Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
|
||||
http_request,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::RenderGrpcRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render grpc request");
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment_id = environment_from_window(&window).map(|e| e.id);
|
||||
let environment_chain = window
|
||||
.db()
|
||||
.resolve_environments(&workspace.id, None, environment_id.as_deref())
|
||||
.expect("Failed to resolve environments");
|
||||
let environment_chain = window.db().resolve_environments(
|
||||
&workspace.id,
|
||||
req.grpc_request.folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let grpc_request = render_grpc_request(&req.grpc_request, environment_chain, &cb)
|
||||
.await
|
||||
.expect("Failed to render grpc request");
|
||||
Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
|
||||
let opt = RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
};
|
||||
let grpc_request =
|
||||
render_grpc_request(&req.grpc_request, environment_chain, &cb, &opt).await?;
|
||||
Ok(Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
|
||||
grpc_request,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::RenderHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render http request");
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment_id = environment_from_window(&window).map(|e| e.id);
|
||||
let environment_chain = window
|
||||
.db()
|
||||
.resolve_environments(&workspace.id, None, environment_id.as_deref())
|
||||
.expect("Failed to resolve environments");
|
||||
let environment_chain = window.db().resolve_environments(
|
||||
&workspace.id,
|
||||
req.http_request.folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let http_request = render_http_request(&req.http_request, environment_chain, &cb)
|
||||
.await
|
||||
.expect("Failed to render http request");
|
||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||
let opt = &RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
};
|
||||
let http_request =
|
||||
render_http_request(&req.http_request, environment_chain, &cb, &opt).await?;
|
||||
Ok(Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||
http_request,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::TemplateRenderRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render");
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment_id = environment_from_window(&window).map(|e| e.id);
|
||||
let environment_chain = window
|
||||
.db()
|
||||
.resolve_environments(&workspace.id, None, environment_id.as_deref())
|
||||
.expect("Failed to resolve environments");
|
||||
let folder_id = if let Some(id) = window.request_id() {
|
||||
match window.db().get_any_request(&id) {
|
||||
Ok(AnyRequest::HttpRequest(r)) => r.folder_id,
|
||||
Ok(AnyRequest::GrpcRequest(r)) => r.folder_id,
|
||||
Ok(AnyRequest::WebsocketRequest(r)) => r.folder_id,
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let environment_chain = window.db().resolve_environments(
|
||||
&workspace.id,
|
||||
folder_id.as_deref(),
|
||||
environment_id.as_deref(),
|
||||
)?;
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let data = render_json_value(req.data, environment_chain, &cb)
|
||||
.await
|
||||
.expect("Failed to render template");
|
||||
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
||||
let opt = RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
};
|
||||
let data = render_json_value(req.data, environment_chain, &cb, &opt).await?;
|
||||
Ok(Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data })))
|
||||
}
|
||||
InternalEventPayload::ErrorResponse(resp) => {
|
||||
error!("Plugin error: {}: {:?}", resp.error, resp);
|
||||
@@ -134,16 +148,15 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
resp.error
|
||||
),
|
||||
color: Some(Color::Danger),
|
||||
timeout: None,
|
||||
timeout: Some(30000),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await
|
||||
}
|
||||
InternalEventPayload::ReloadResponse(req) => {
|
||||
let plugins = app_handle.db().list_plugins().unwrap();
|
||||
let plugins = app_handle.db().list_plugins()?;
|
||||
for plugin in plugins {
|
||||
if plugin.directory != plugin_handle.dir {
|
||||
continue;
|
||||
@@ -153,7 +166,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead
|
||||
..plugin
|
||||
};
|
||||
app_handle.db().upsert_plugin(&new_plugin, &UpdateSource::Plugin).unwrap();
|
||||
app_handle.db().upsert_plugin(&new_plugin, &UpdateSource::Plugin)?;
|
||||
}
|
||||
|
||||
if !req.silent {
|
||||
@@ -163,17 +176,18 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}@{}", info.name, info.version),
|
||||
icon: Some(Icon::Info),
|
||||
timeout: Some(3000),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
None
|
||||
}
|
||||
InternalEventPayload::SendHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for sending HTTP request");
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
let mut http_request = req.http_request;
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
@@ -187,20 +201,17 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
let http_response = if http_request.id.is_empty() {
|
||||
HttpResponse::default()
|
||||
} else {
|
||||
window
|
||||
.db()
|
||||
.upsert_http_response(
|
||||
&HttpResponse {
|
||||
request_id: http_request.id.clone(),
|
||||
workspace_id: http_request.workspace_id.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Plugin,
|
||||
)
|
||||
.unwrap()
|
||||
window.db().upsert_http_response(
|
||||
&HttpResponse {
|
||||
request_id: http_request.id.clone(),
|
||||
workspace_id: http_request.workspace_id.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
&UpdateSource::Plugin,
|
||||
)?
|
||||
};
|
||||
|
||||
let result = send_http_request(
|
||||
let http_response = send_http_request(
|
||||
&window,
|
||||
&http_request,
|
||||
&http_response,
|
||||
@@ -208,16 +219,11 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
cookie_jar,
|
||||
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
let http_response = match result {
|
||||
Ok(r) => r,
|
||||
Err(_e) => return,
|
||||
};
|
||||
|
||||
Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse {
|
||||
Ok(Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse {
|
||||
http_response,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::OpenWindowRequest(req) => {
|
||||
let (navigation_tx, mut navigation_rx) = tokio::sync::mpsc::channel(128);
|
||||
@@ -240,8 +246,8 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
}),
|
||||
None,
|
||||
);
|
||||
Box::pin(handle_plugin_event(app_handle, &error_event, plugin_handle)).await;
|
||||
return;
|
||||
return Box::pin(handle_plugin_event(app_handle, &error_event, plugin_handle))
|
||||
.await;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -277,32 +283,33 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
InternalEventPayload::CloseWindowRequest(req) => {
|
||||
if let Some(window) = app_handle.webview_windows().get(&req.label) {
|
||||
window.close().expect("Failed to close window");
|
||||
window.close()?;
|
||||
}
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
InternalEventPayload::SetKeyValueRequest(req) => {
|
||||
let name = plugin_handle.info().name;
|
||||
app_handle.db().set_plugin_key_value(&name, &req.key, &req.value);
|
||||
Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {}))
|
||||
Ok(Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {})))
|
||||
}
|
||||
InternalEventPayload::GetKeyValueRequest(req) => {
|
||||
let name = plugin_handle.info().name;
|
||||
let value = app_handle.db().get_plugin_key_value(&name, &req.key).map(|v| v.value);
|
||||
Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value }))
|
||||
Ok(Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value })))
|
||||
}
|
||||
InternalEventPayload::DeleteKeyValueRequest(req) => {
|
||||
let name = plugin_handle.info().name;
|
||||
let deleted = app_handle.db().delete_plugin_key_value(&name, &req.key).unwrap();
|
||||
Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { deleted }))
|
||||
let deleted = app_handle.db().delete_plugin_key_value(&name, &req.key)?;
|
||||
Ok(Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse {
|
||||
deleted,
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::ListCookieNamesRequest(_req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for listing cookies");
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
let names = match cookie_jar_from_window(&window) {
|
||||
None => Vec::new(),
|
||||
Some(j) => j
|
||||
@@ -311,11 +318,12 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
.filter_map(|c| Cookie::parse(c.raw_cookie).ok().map(|c| c.name().to_string()))
|
||||
.collect(),
|
||||
};
|
||||
Some(InternalEventPayload::ListCookieNamesResponse(ListCookieNamesResponse { names }))
|
||||
Ok(Some(InternalEventPayload::ListCookieNamesResponse(ListCookieNamesResponse {
|
||||
names,
|
||||
})))
|
||||
}
|
||||
InternalEventPayload::GetCookieValueRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for listing cookies");
|
||||
let window = get_window_from_window_context(app_handle, &window_context)?;
|
||||
let value = match cookie_jar_from_window(&window) {
|
||||
None => None,
|
||||
Some(j) => j.cookies.into_iter().find_map(|c| match Cookie::parse(c.raw_cookie) {
|
||||
@@ -325,15 +333,8 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
_ => None,
|
||||
}),
|
||||
};
|
||||
Some(InternalEventPayload::GetCookieValueResponse(GetCookieValueResponse { value }))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(e) = response_event {
|
||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||
if let Err(e) = plugin_manager.reply(&event, &e).await {
|
||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||
Ok(Some(InternalEventPayload::GetCookieValueResponse(GetCookieValueResponse { value })))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
use serde_json::Value;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::BTreeMap;
|
||||
use yaak_http::apply_path_placeholders;
|
||||
use yaak_models::models::{
|
||||
Environment, GrpcRequest, HttpRequest, HttpRequestHeader, HttpUrlParameter,
|
||||
};
|
||||
use yaak_models::render::make_vars_hashmap;
|
||||
use yaak_templates::{TemplateCallback, parse_and_render, render_json_value_raw};
|
||||
use yaak_templates::{RenderOptions, TemplateCallback, parse_and_render, render_json_value_raw};
|
||||
|
||||
pub async fn render_template<T: TemplateCallback>(
|
||||
template: &str,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
opt: &RenderOptions,
|
||||
) -> yaak_templates::error::Result<String> {
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
render(template, vars, cb).await
|
||||
parse_and_render(template, vars, cb, &opt).await
|
||||
}
|
||||
|
||||
pub async fn render_json_value<T: TemplateCallback>(
|
||||
value: Value,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
opt: &RenderOptions,
|
||||
) -> yaak_templates::error::Result<Value> {
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
render_json_value_raw(value, vars, cb).await
|
||||
render_json_value_raw(value, vars, cb, opt).await
|
||||
}
|
||||
|
||||
pub async fn render_grpc_request<T: TemplateCallback>(
|
||||
r: &GrpcRequest,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
opt: &RenderOptions,
|
||||
) -> yaak_templates::error::Result<GrpcRequest> {
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
|
||||
@@ -36,18 +39,18 @@ pub async fn render_grpc_request<T: TemplateCallback>(
|
||||
for p in r.metadata.clone() {
|
||||
metadata.push(HttpRequestHeader {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars, cb).await?,
|
||||
value: render(p.value.as_str(), vars, cb).await?,
|
||||
name: parse_and_render(p.name.as_str(), vars, cb, &opt).await?,
|
||||
value: parse_and_render(p.value.as_str(), vars, cb, &opt).await?,
|
||||
id: p.id,
|
||||
})
|
||||
}
|
||||
|
||||
let mut authentication = BTreeMap::new();
|
||||
for (k, v) in r.authentication.clone() {
|
||||
authentication.insert(k, render_json_value_raw(v, vars, cb).await?);
|
||||
authentication.insert(k, render_json_value_raw(v, vars, cb, &opt).await?);
|
||||
}
|
||||
|
||||
let url = render(r.url.as_str(), vars, cb).await?;
|
||||
let url = parse_and_render(r.url.as_str(), vars, cb, &opt).await?;
|
||||
|
||||
Ok(GrpcRequest {
|
||||
url,
|
||||
@@ -61,40 +64,47 @@ pub async fn render_http_request<T: TemplateCallback>(
|
||||
r: &HttpRequest,
|
||||
environment_chain: Vec<Environment>,
|
||||
cb: &T,
|
||||
opt: &RenderOptions,
|
||||
) -> yaak_templates::error::Result<HttpRequest> {
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
|
||||
let mut url_parameters = Vec::new();
|
||||
for p in r.url_parameters.clone() {
|
||||
if !p.enabled {
|
||||
continue;
|
||||
}
|
||||
url_parameters.push(HttpUrlParameter {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars, cb).await?,
|
||||
value: render(p.value.as_str(), vars, cb).await?,
|
||||
name: parse_and_render(p.name.as_str(), vars, cb, &opt).await?,
|
||||
value: parse_and_render(p.value.as_str(), vars, cb, &opt).await?,
|
||||
id: p.id,
|
||||
})
|
||||
}
|
||||
|
||||
let mut headers = Vec::new();
|
||||
for p in r.headers.clone() {
|
||||
if !p.enabled {
|
||||
continue;
|
||||
}
|
||||
headers.push(HttpRequestHeader {
|
||||
enabled: p.enabled,
|
||||
name: render(p.name.as_str(), vars, cb).await?,
|
||||
value: render(p.value.as_str(), vars, cb).await?,
|
||||
name: parse_and_render(p.name.as_str(), vars, cb, &opt).await?,
|
||||
value: parse_and_render(p.value.as_str(), vars, cb, &opt).await?,
|
||||
id: p.id,
|
||||
})
|
||||
}
|
||||
|
||||
let mut body = BTreeMap::new();
|
||||
for (k, v) in r.body.clone() {
|
||||
body.insert(k, render_json_value_raw(v, vars, cb).await?);
|
||||
body.insert(k, render_json_value_raw(v, vars, cb, &opt).await?);
|
||||
}
|
||||
|
||||
let mut authentication = BTreeMap::new();
|
||||
for (k, v) in r.authentication.clone() {
|
||||
authentication.insert(k, render_json_value_raw(v, vars, cb).await?);
|
||||
authentication.insert(k, render_json_value_raw(v, vars, cb, &opt).await?);
|
||||
}
|
||||
|
||||
let url = render(r.url.clone().as_str(), vars, cb).await?;
|
||||
let url = parse_and_render(r.url.clone().as_str(), vars, cb, &opt).await?;
|
||||
|
||||
// This doesn't fit perfectly with the concept of "rendering" but it kind of does
|
||||
let (url, url_parameters) = apply_path_placeholders(&url, url_parameters);
|
||||
@@ -108,11 +118,3 @@ pub async fn render_http_request<T: TemplateCallback>(
|
||||
..r.to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn render<T: TemplateCallback>(
|
||||
template: &str,
|
||||
vars: &HashMap<String, String>,
|
||||
cb: &T,
|
||||
) -> yaak_templates::error::Result<String> {
|
||||
parse_and_render(template, vars, cb).await
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crate::error::Result;
|
||||
use crate::window_menu::app_menu;
|
||||
use log::{info, warn};
|
||||
use rand::random;
|
||||
use tauri::{
|
||||
AppHandle, Emitter, LogicalSize, Manager, Runtime, WebviewUrl, WebviewWindow, WindowEvent,
|
||||
AppHandle, Emitter, LogicalSize, Manager, PhysicalSize, Runtime, WebviewUrl, WebviewWindow, WindowEvent
|
||||
};
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
use tokio::sync::mpsc;
|
||||
use crate::error::Result;
|
||||
|
||||
const DEFAULT_WINDOW_WIDTH: f64 = 1100.0;
|
||||
const DEFAULT_WINDOW_HEIGHT: f64 = 600.0;
|
||||
@@ -49,7 +49,6 @@ pub(crate) fn create_window<R: Runtime>(
|
||||
.resizable(true)
|
||||
.visible(false) // To prevent theme flashing, the frontend code calls show() immediately after configuring the theme
|
||||
.fullscreen(false)
|
||||
.disable_drag_drop_handler() // Required for frontend Dnd on windows
|
||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
|
||||
|
||||
if let Some(key) = config.data_dir_key {
|
||||
@@ -161,6 +160,11 @@ pub(crate) fn create_window<R: Runtime>(
|
||||
"dev.reset_size" => webview_window
|
||||
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
||||
.unwrap(),
|
||||
"dev.reset_size_record" => {
|
||||
let width = webview_window.outer_size().unwrap().width;
|
||||
let height = width * 9 / 16;
|
||||
webview_window.set_size(PhysicalSize::new(width, height)).unwrap()
|
||||
}
|
||||
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
||||
"dev.generate_theme_css" => {
|
||||
w.emit("generate_theme_css", true).unwrap();
|
||||
@@ -216,10 +220,10 @@ pub(crate) fn create_child_window(
|
||||
) -> Result<WebviewWindow> {
|
||||
let app_handle = parent_window.app_handle();
|
||||
let label = format!("{OTHER_WINDOW_PREFIX}_{label}");
|
||||
let scale_factor = parent_window.scale_factor().unwrap();
|
||||
let scale_factor = parent_window.scale_factor()?;
|
||||
|
||||
let current_pos = parent_window.inner_position().unwrap().to_logical::<f64>(scale_factor);
|
||||
let current_size = parent_window.inner_size().unwrap().to_logical::<f64>(scale_factor);
|
||||
let current_pos = parent_window.inner_position()?.to_logical::<f64>(scale_factor);
|
||||
let current_size = parent_window.inner_size()?.to_logical::<f64>(scale_factor);
|
||||
|
||||
// Position the new window in the middle of the parent
|
||||
let position = (
|
||||
|
||||
@@ -143,6 +143,8 @@ pub fn app_menu<R: Runtime>(app_handle: &AppHandle<R>) -> tauri::Result<Menu<R>>
|
||||
.build(app_handle)?,
|
||||
&MenuItemBuilder::with_id("dev.reset_size".to_string(), "Reset Size")
|
||||
.build(app_handle)?,
|
||||
&MenuItemBuilder::with_id("dev.reset_size_record".to_string(), "Reset Size 16x9")
|
||||
.build(app_handle)?,
|
||||
&MenuItemBuilder::with_id(
|
||||
"dev.generate_theme_css".to_string(),
|
||||
"Generate Theme CSS",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"createUpdaterArtifacts": "v1Compatible",
|
||||
"createUpdaterArtifacts": true,
|
||||
"windows": {
|
||||
"signCommand": "trusted-signing-cli -e https://eus.codesigning.azure.net/ -a Yaak -c yaakapp %1"
|
||||
}
|
||||
@@ -5,6 +5,7 @@ pub trait WorkspaceWindowTrait {
|
||||
fn workspace_id(&self) -> Option<String>;
|
||||
fn cookie_jar_id(&self) -> Option<String>;
|
||||
fn environment_id(&self) -> Option<String>;
|
||||
fn request_id(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
impl<R: Runtime> WorkspaceWindowTrait for WebviewWindow<R> {
|
||||
@@ -28,4 +29,10 @@ impl<R: Runtime> WorkspaceWindowTrait for WebviewWindow<R> {
|
||||
let mut query_pairs = url.query_pairs();
|
||||
query_pairs.find(|(k, _v)| k == "environment_id").map(|(_k, v)| v.to_string())
|
||||
}
|
||||
|
||||
fn request_id(&self) -> Option<String> {
|
||||
let url = self.url().unwrap();
|
||||
let mut query_pairs = url.query_pairs();
|
||||
query_pairs.find(|(k, _v)| k == "request_id").map(|(_k, v)| v.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ pub async fn activate_license<R: Runtime>(
|
||||
}
|
||||
|
||||
let body: ActivateLicenseResponsePayload = response.json().await?;
|
||||
window.app_handle().db().set_key_value_string(
|
||||
window.app_handle().db().set_key_value_str(
|
||||
KV_ACTIVATION_ID_KEY,
|
||||
KV_NAMESPACE,
|
||||
body.activation_id.as_str(),
|
||||
@@ -207,5 +207,5 @@ fn build_url(path: &str) -> String {
|
||||
}
|
||||
|
||||
pub async fn get_activation_id<R: Runtime>(app_handle: &AppHandle<R>) -> String {
|
||||
app_handle.db().get_key_value_string(KV_ACTIVATION_ID_KEY, KV_NAMESPACE, "")
|
||||
app_handle.db().get_key_value_str(KV_ACTIVATION_ID_KEY, KV_NAMESPACE, "")
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ sha2 = { workspace = true }
|
||||
tauri = { workspace = true }
|
||||
tauri-plugin-dialog = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||
yaak-common = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { workspace = true, features = ["build"] }
|
||||
|
||||
@@ -14,7 +14,7 @@ export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
|
||||
|
||||
export type EncryptedKey = { encryptedKey: string, };
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
@@ -62,7 +62,7 @@ export type ProxySetting = { "type": "enabled", http: string, https: string, aut
|
||||
|
||||
export type ProxySettingAuth = { user: string, password: string, };
|
||||
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, coloredMethods: boolean, editorFont: string | null, editorFontSize: number, editorKeymap: EditorKeymap, editorSoftWrap: boolean, hideWindowControls: boolean, interfaceFont: string | null, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, hideLicenseBadge: boolean, autoupdate: boolean, autoDownloadUpdates: boolean, };
|
||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, coloredMethods: boolean, editorFont: string | null, editorFontSize: number, editorKeymap: EditorKeymap, editorSoftWrap: boolean, hideWindowControls: boolean, interfaceFont: string | null, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, hideLicenseBadge: boolean, autoupdate: boolean, autoDownloadUpdates: boolean, checkNotifications: boolean, };
|
||||
|
||||
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { newStoreData } from './util';
|
||||
export const modelStoreDataAtom = atom(newStoreData());
|
||||
|
||||
export const cookieJarsAtom = createOrderedModelAtom('cookie_jar', 'name', 'asc');
|
||||
export const environmentsAtom = createOrderedModelAtom('environment', 'name', 'asc');
|
||||
export const environmentsAtom = createOrderedModelAtom('environment', 'sortPriority', 'asc');
|
||||
export const foldersAtom = createModelAtom('folder');
|
||||
export const grpcConnectionsAtom = createOrderedModelAtom('grpc_connection', 'createdAt', 'desc');
|
||||
export const grpcEventsAtom = createOrderedModelAtom('grpc_event', 'createdAt', 'asc');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
|
||||
import { resolvedModelName } from '@yaakapp/app/lib/resolvedModelName';
|
||||
import { AnyModel, ModelPayload } from '../bindings/gen_models';
|
||||
import { modelStoreDataAtom } from './atoms';
|
||||
import { ExtractModel, JotaiStore, ModelStoreData } from './types';
|
||||
@@ -69,23 +70,21 @@ export async function changeModelStoreWorkspace(workspaceId: string | null) {
|
||||
_activeWorkspaceId = workspaceId;
|
||||
}
|
||||
|
||||
export function getAnyModel(id: string): AnyModel | null {
|
||||
export function listModels<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
||||
modelType: M | ReadonlyArray<M>,
|
||||
): T[] {
|
||||
let data = mustStore().get(modelStoreDataAtom);
|
||||
for (const modelData of Object.values(data)) {
|
||||
let model = modelData[id];
|
||||
if (model != null) {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
const types: ReadonlyArray<M> = Array.isArray(modelType) ? modelType : [modelType];
|
||||
return types.flatMap((t) => Object.values(data[t]) as T[]);
|
||||
}
|
||||
|
||||
export function getModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
||||
modelType: M | M[],
|
||||
modelType: M | ReadonlyArray<M>,
|
||||
id: string,
|
||||
): T | null {
|
||||
let data = mustStore().get(modelStoreDataAtom);
|
||||
for (const t of Array.isArray(modelType) ? modelType : [modelType]) {
|
||||
const types: ReadonlyArray<M> = Array.isArray(modelType) ? modelType : [modelType];
|
||||
for (const t of types) {
|
||||
let v = data[t][id];
|
||||
if (v?.model === t) return v as T;
|
||||
}
|
||||
@@ -136,21 +135,43 @@ export async function deleteModel<M extends AnyModel['model'], T extends Extract
|
||||
await invoke<string>('plugin:yaak-models|delete', { model });
|
||||
}
|
||||
|
||||
export function duplicateModelById<
|
||||
M extends AnyModel['model'],
|
||||
T extends ExtractModel<AnyModel, M>,
|
||||
>(modelType: M | M[], id: string) {
|
||||
let model = getModel<M, T>(modelType, id);
|
||||
return duplicateModel(model);
|
||||
}
|
||||
|
||||
export function duplicateModel<M extends AnyModel['model'], T extends ExtractModel<AnyModel, M>>(
|
||||
model: T | null,
|
||||
) {
|
||||
if (model == null) {
|
||||
throw new Error('Failed to delete null model');
|
||||
throw new Error('Failed to duplicate null model');
|
||||
}
|
||||
return invoke<string>('plugin:yaak-models|duplicate', { model });
|
||||
|
||||
// If the model has a name, try to duplicate it with a name that doesn't conflict
|
||||
let name = 'name' in model ? resolvedModelName(model) : undefined;
|
||||
if (name != null) {
|
||||
const existingModels = listModels(model.model);
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const hasConflict = existingModels.some((m) => {
|
||||
if ('folderId' in m && 'folderId' in model && model.folderId !== m.folderId) {
|
||||
return false;
|
||||
} else if (resolvedModelName(m) !== name) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!hasConflict) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Name conflict. Try another one
|
||||
const m: RegExpMatchArray | null = name.match(/ Copy( (?<n>\d+))?$/);
|
||||
if (m != null && m.groups?.n == null) {
|
||||
name = name.substring(0, m.index) + ' Copy 2';
|
||||
} else if (m != null && m.groups?.n != null) {
|
||||
name = name.substring(0, m.index) + ` Copy ${parseInt(m.groups.n) + 1}`;
|
||||
} else {
|
||||
name = `${name} Copy`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return invoke<string>('plugin:yaak-models|duplicate', { model: { ...model, name } });
|
||||
}
|
||||
|
||||
export async function createGlobalModel<T extends Exclude<AnyModel, { workspaceId: string }>>(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE settings ADD COLUMN check_notifications BOOLEAN DEFAULT true NOT NULL;
|
||||
@@ -0,0 +1,11 @@
|
||||
UPDATE http_requests
|
||||
SET authentication_type = 'awsv4'
|
||||
WHERE authentication_type = 'auth-aws-sig-v4';
|
||||
|
||||
UPDATE folders
|
||||
SET authentication_type = 'awsv4'
|
||||
WHERE authentication_type = 'auth-aws-sig-v4';
|
||||
|
||||
UPDATE workspaces
|
||||
SET authentication_type = 'awsv4'
|
||||
WHERE authentication_type = 'auth-aws-sig-v4';
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE environments
|
||||
ADD COLUMN sort_priority REAL DEFAULT 0 NOT NULL;
|
||||
@@ -38,14 +38,12 @@ impl<'a> DbContext<'a> {
|
||||
let mut stmt = self.conn.prepare(sql.as_str()).expect("Failed to prepare query");
|
||||
match stmt.query_row(&*params.as_params(), M::from_row) {
|
||||
Ok(result) => Ok(result),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => {
|
||||
Err(ModelNotFound(format!(
|
||||
r#"table "{}" {} == {}"#,
|
||||
M::table_name().into_iden().to_string(),
|
||||
col.into_iden().to_string(),
|
||||
value_debug
|
||||
)))
|
||||
}
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Err(ModelNotFound(format!(
|
||||
r#"table "{}" {} == {}"#,
|
||||
M::table_name().into_iden().to_string(),
|
||||
col.into_iden().to_string(),
|
||||
value_debug
|
||||
))),
|
||||
Err(e) => Err(crate::error::Error::SqlError(e)),
|
||||
}
|
||||
}
|
||||
@@ -69,7 +67,7 @@ impl<'a> DbContext<'a> {
|
||||
.expect("Failed to run find on DB")
|
||||
}
|
||||
|
||||
pub fn find_all<'s, M>(&self) -> crate::error::Result<Vec<M>>
|
||||
pub fn find_all<'s, M>(&self) -> Result<Vec<M>>
|
||||
where
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
@@ -117,7 +115,7 @@ impl<'a> DbContext<'a> {
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub fn upsert<M>(&self, model: &M, source: &UpdateSource) -> crate::error::Result<M>
|
||||
pub fn upsert<M>(&self, model: &M, source: &UpdateSource) -> Result<M>
|
||||
where
|
||||
M: Into<AnyModel> + From<AnyModel> + UpsertModelInfo + Clone,
|
||||
{
|
||||
@@ -139,7 +137,7 @@ impl<'a> DbContext<'a> {
|
||||
other_values: Vec<(impl IntoIden + Eq, impl Into<SimpleExpr>)>,
|
||||
update_columns: Vec<impl IntoIden>,
|
||||
source: &UpdateSource,
|
||||
) -> crate::error::Result<M>
|
||||
) -> Result<M>
|
||||
where
|
||||
M: Into<AnyModel> + From<AnyModel> + UpsertModelInfo + Clone,
|
||||
{
|
||||
@@ -178,7 +176,7 @@ impl<'a> DbContext<'a> {
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
pub(crate) fn delete<'s, M>(&self, m: &M, source: &UpdateSource) -> crate::error::Result<M>
|
||||
pub(crate) fn delete<'s, M>(&self, m: &M, source: &UpdateSource) -> Result<M>
|
||||
where
|
||||
M: Into<AnyModel> + Clone + UpsertModelInfo,
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::error::Error::MigrationError;
|
||||
use crate::error::Result;
|
||||
use include_dir::{Dir, DirEntry, include_dir};
|
||||
use log::info;
|
||||
use log::{debug, info};
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::{OptionalExtension, TransactionBehavior, params};
|
||||
@@ -86,6 +86,7 @@ fn run_migration(migration_path: &DirEntry, tx: &mut rusqlite::Transaction) -> R
|
||||
.optional()?;
|
||||
|
||||
if row.is_some() {
|
||||
debug!("Skipping already run migration {description}");
|
||||
return Ok(false); // Migration was already run
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ pub struct Settings {
|
||||
pub hide_license_badge: bool,
|
||||
pub autoupdate: bool,
|
||||
pub auto_download_updates: bool,
|
||||
pub check_notifications: bool,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Settings {
|
||||
@@ -175,6 +176,7 @@ impl UpsertModelInfo for Settings {
|
||||
(Autoupdate, self.autoupdate.into()),
|
||||
(AutoDownloadUpdates, self.auto_download_updates.into()),
|
||||
(ColoredMethods, self.colored_methods.into()),
|
||||
(CheckNotifications, self.check_notifications.into()),
|
||||
(Proxy, proxy.into()),
|
||||
])
|
||||
}
|
||||
@@ -200,6 +202,7 @@ impl UpsertModelInfo for Settings {
|
||||
SettingsIden::Autoupdate,
|
||||
SettingsIden::AutoDownloadUpdates,
|
||||
SettingsIden::ColoredMethods,
|
||||
SettingsIden::CheckNotifications,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -232,6 +235,7 @@ impl UpsertModelInfo for Settings {
|
||||
auto_download_updates: row.get("auto_download_updates")?,
|
||||
hide_license_badge: row.get("hide_license_badge")?,
|
||||
colored_methods: row.get("colored_methods")?,
|
||||
check_notifications: row.get("check_notifications")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -550,6 +554,7 @@ pub struct Environment {
|
||||
pub parent_id: Option<String>,
|
||||
pub variables: Vec<EnvironmentVariable>,
|
||||
pub color: Option<String>,
|
||||
pub sort_priority: f64,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Environment {
|
||||
@@ -587,6 +592,7 @@ impl UpsertModelInfo for Environment {
|
||||
(Color, self.color.into()),
|
||||
(Name, self.name.trim().into()),
|
||||
(Public, self.public.into()),
|
||||
(SortPriority, self.sort_priority.into()),
|
||||
(Variables, serde_json::to_string(&self.variables)?.into()),
|
||||
])
|
||||
}
|
||||
@@ -600,6 +606,7 @@ impl UpsertModelInfo for Environment {
|
||||
EnvironmentIden::Name,
|
||||
EnvironmentIden::Public,
|
||||
EnvironmentIden::Variables,
|
||||
EnvironmentIden::SortPriority,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -622,6 +629,7 @@ impl UpsertModelInfo for Environment {
|
||||
name: row.get("name")?,
|
||||
public: row.get("public")?,
|
||||
variables: serde_json::from_str(variables.as_str()).unwrap_or_default(),
|
||||
sort_priority: row.get("sort_priority")?,
|
||||
|
||||
// Deprecated field, but we need to keep it around for a couple of versions
|
||||
// for compatibility because sync/export don't have a schema field
|
||||
@@ -679,7 +687,7 @@ pub struct Folder {
|
||||
pub description: String,
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
pub sort_priority: f64,
|
||||
}
|
||||
|
||||
impl UpsertModelInfo for Folder {
|
||||
@@ -1049,7 +1057,7 @@ pub struct WebsocketRequest {
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
pub message: String,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
pub sort_priority: f64,
|
||||
pub url: String,
|
||||
pub url_parameters: Vec<HttpUrlParameter>,
|
||||
}
|
||||
@@ -1484,7 +1492,7 @@ pub struct GrpcRequest {
|
||||
pub method: Option<String>,
|
||||
pub name: String,
|
||||
pub service: Option<String>,
|
||||
pub sort_priority: f32,
|
||||
pub sort_priority: f64,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
|
||||
23
src-tauri/yaak-models/src/queries/any_request.rs
Normal file
23
src-tauri/yaak-models/src/queries/any_request.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
GrpcRequest, HttpRequest, WebsocketRequest,
|
||||
};
|
||||
|
||||
pub enum AnyRequest {
|
||||
HttpRequest(HttpRequest),
|
||||
GrpcRequest(GrpcRequest),
|
||||
WebsocketRequest(WebsocketRequest),
|
||||
}
|
||||
|
||||
impl<'a> DbContext<'a> {
|
||||
pub fn get_any_request(&self, id: &str) -> Result<AnyRequest> {
|
||||
if let Ok(http_request) = self.get_http_request(id) {
|
||||
Ok(AnyRequest::HttpRequest(http_request))
|
||||
} else if let Ok(grpc_request) = self.get_grpc_request(id) {
|
||||
Ok(AnyRequest::GrpcRequest(grpc_request))
|
||||
} else {
|
||||
Ok(AnyRequest::WebsocketRequest(self.get_websocket_request(id)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,15 +49,7 @@ impl<'a> DbContext<'a> {
|
||||
info!("Upserted {} websocket_requests", imported_resources.websocket_requests.len());
|
||||
}
|
||||
|
||||
if environments.len() > 0 {
|
||||
for x in environments {
|
||||
let x = self.upsert_environment(&x, source)?;
|
||||
imported_resources.environments.push(x.clone());
|
||||
}
|
||||
info!("Upserted {} environments", imported_resources.environments.len());
|
||||
}
|
||||
|
||||
// Do folders last so it doesn't cause the UI to render empty folders before populating
|
||||
// Do folders after their children so the UI doesn't render empty folders before populating
|
||||
// immediately after.
|
||||
if folders.len() > 0 {
|
||||
for v in folders {
|
||||
@@ -67,6 +59,15 @@ impl<'a> DbContext<'a> {
|
||||
info!("Upserted {} folders", imported_resources.folders.len());
|
||||
}
|
||||
|
||||
// Do environments last because they can depend on many models (requests, folders, etc)
|
||||
if environments.len() > 0 {
|
||||
for x in environments {
|
||||
let x = self.upsert_environment(&x, source)?;
|
||||
imported_resources.environments.push(x.clone());
|
||||
}
|
||||
info!("Upserted {} environments", imported_resources.environments.len());
|
||||
}
|
||||
|
||||
Ok(imported_resources)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ impl<'a> DbContext<'a> {
|
||||
|
||||
/// Lists environments and will create a base environment if one doesn't exist
|
||||
pub fn list_environments_ensure_base(&self, workspace_id: &str) -> Result<Vec<Environment>> {
|
||||
let mut environments =
|
||||
self.find_many::<Environment>(EnvironmentIden::WorkspaceId, workspace_id, None)?;
|
||||
let mut environments = self.list_environments_dangerous(workspace_id)?;
|
||||
|
||||
let base_environment = environments.iter().find(|e| e.parent_model == "workspace");
|
||||
|
||||
@@ -59,6 +58,11 @@ impl<'a> DbContext<'a> {
|
||||
Ok(environments)
|
||||
}
|
||||
|
||||
/// List environments for a workspace. Prefer list_environments_ensure_base()
|
||||
fn list_environments_dangerous(&self, workspace_id: &str) -> Result<Vec<Environment>> {
|
||||
Ok(self.find_many::<Environment>(EnvironmentIden::WorkspaceId, workspace_id, None)?)
|
||||
}
|
||||
|
||||
pub fn delete_environment(
|
||||
&self,
|
||||
environment: &Environment,
|
||||
@@ -93,7 +97,7 @@ impl<'a> DbContext<'a> {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
self.list_environments_ensure_base(&environment.workspace_id)
|
||||
self.list_environments_dangerous(&environment.workspace_id)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|e| {
|
||||
@@ -131,8 +135,9 @@ impl<'a> DbContext<'a> {
|
||||
let mut name = environment.name.clone();
|
||||
match (environment.parent_model.as_str(), environment.parent_id.as_deref()) {
|
||||
("folder", Some(folder_id)) => {
|
||||
let folder = self.get_folder(folder_id)?;
|
||||
name = format!("{} Environment", folder.name);
|
||||
if let Ok(folder) = self.get_folder(folder_id) {
|
||||
name = format!("{} Environment", folder.name);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{HttpRequest, HttpRequestHeader, HttpRequestIden};
|
||||
use crate::models::{Folder, FolderIden, HttpRequest, HttpRequestHeader, HttpRequestIden};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -89,4 +89,18 @@ impl<'a> DbContext<'a> {
|
||||
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
pub fn list_http_requests_for_folder_recursive(
|
||||
&self,
|
||||
folder_id: &str,
|
||||
) -> Result<Vec<HttpRequest>> {
|
||||
let mut children = Vec::new();
|
||||
for m in self.find_many::<Folder>(FolderIden::FolderId, folder_id, None)? {
|
||||
children.extend(self.list_http_requests_for_folder_recursive(&m.id)?);
|
||||
}
|
||||
for m in self.find_many::<HttpRequest>(FolderIden::FolderId, folder_id, None)? {
|
||||
children.push(m);
|
||||
}
|
||||
Ok(children)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use chrono::NaiveDateTime;
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{KeyValue, KeyValueIden, UpsertModelInfo};
|
||||
@@ -22,7 +23,7 @@ impl<'a> DbContext<'a> {
|
||||
Ok(items.map(|v| v.unwrap()).collect())
|
||||
}
|
||||
|
||||
pub fn get_key_value_string(&self, namespace: &str, key: &str, default: &str) -> String {
|
||||
pub fn get_key_value_str(&self, namespace: &str, key: &str, default: &str) -> String {
|
||||
match self.get_key_value_raw(namespace, key) {
|
||||
None => default.to_string(),
|
||||
Some(v) => {
|
||||
@@ -38,6 +39,22 @@ impl<'a> DbContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_key_value_dte(&self, namespace: &str, key: &str, default: NaiveDateTime) -> NaiveDateTime {
|
||||
match self.get_key_value_raw(namespace, key) {
|
||||
None => default,
|
||||
Some(v) => {
|
||||
let result = serde_json::from_str(&v.value);
|
||||
match result {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("Failed to parse date key value: {}", e);
|
||||
default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_key_value_int(&self, namespace: &str, key: &str, default: i32) -> i32 {
|
||||
match self.get_key_value_raw(namespace, key) {
|
||||
None => default.clone(),
|
||||
@@ -67,7 +84,18 @@ impl<'a> DbContext<'a> {
|
||||
self.conn.resolve().query_row(sql.as_str(), &*params.as_params(), KeyValue::from_row).ok()
|
||||
}
|
||||
|
||||
pub fn set_key_value_string(
|
||||
pub fn set_key_value_dte(
|
||||
&self,
|
||||
namespace: &str,
|
||||
key: &str,
|
||||
value: NaiveDateTime,
|
||||
source: &UpdateSource,
|
||||
) -> (KeyValue, bool) {
|
||||
let encoded = serde_json::to_string(&value).unwrap();
|
||||
self.set_key_value_raw(namespace, key, &encoded, source)
|
||||
}
|
||||
|
||||
pub fn set_key_value_str(
|
||||
&self,
|
||||
namespace: &str,
|
||||
key: &str,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod any_request;
|
||||
mod batch;
|
||||
mod cookie_jars;
|
||||
mod environments;
|
||||
|
||||
@@ -18,11 +18,11 @@ impl<'a> DbContext<'a> {
|
||||
updated_at: Default::default(),
|
||||
|
||||
appearance: "system".to_string(),
|
||||
editor_font_size: 13,
|
||||
editor_font_size: 12,
|
||||
editor_font: None,
|
||||
editor_keymap: EditorKeymap::Default,
|
||||
editor_soft_wrap: true,
|
||||
interface_font_size: 15,
|
||||
interface_font_size: 14,
|
||||
interface_scale: 1.0,
|
||||
interface_font: None,
|
||||
hide_window_controls: false,
|
||||
@@ -35,6 +35,7 @@ impl<'a> DbContext<'a> {
|
||||
colored_methods: false,
|
||||
hide_license_badge: false,
|
||||
auto_download_updates: true,
|
||||
check_notifications: true,
|
||||
};
|
||||
self.upsert(&settings, &UpdateSource::Background).expect("Failed to upsert settings")
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ fn add_variable_to_map(
|
||||
) -> HashMap<String, String> {
|
||||
let mut map = m.clone();
|
||||
for variable in variables {
|
||||
if !variable.enabled || variable.value.is_empty() {
|
||||
if !variable.enabled {
|
||||
continue;
|
||||
}
|
||||
let name = variable.name.as_str();
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::models::{
|
||||
AnyModel, Environment, Folder, GrpcRequest, HttpRequest, UpsertModelInfo, WebsocketRequest,
|
||||
Workspace, WorkspaceIden,
|
||||
};
|
||||
use yaak_common::window::WorkspaceWindowTrait;
|
||||
use crate::query_manager::QueryManagerExt;
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use log::warn;
|
||||
@@ -158,7 +159,17 @@ pub fn get_workspace_export_resources<R: Runtime>(
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn maybe_gen_id<M: UpsertModelInfo>(id: &str, ids: &mut BTreeMap<String, String>) -> String {
|
||||
pub fn maybe_gen_id<M: UpsertModelInfo, R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
id: &str,
|
||||
ids: &mut BTreeMap<String, String>,
|
||||
) -> String {
|
||||
if id == "CURRENT_WORKSPACE" {
|
||||
if let Some(wid) = window.workspace_id() {
|
||||
return wid.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
if !id.starts_with("GENERATE_ID::") {
|
||||
return id.to_string();
|
||||
}
|
||||
@@ -173,12 +184,13 @@ pub fn maybe_gen_id<M: UpsertModelInfo>(id: &str, ids: &mut BTreeMap<String, Str
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_gen_id_opt<M: UpsertModelInfo>(
|
||||
pub fn maybe_gen_id_opt<M: UpsertModelInfo, R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
id: Option<String>,
|
||||
ids: &mut BTreeMap<String, String>,
|
||||
) -> Option<String> {
|
||||
match id {
|
||||
Some(id) => Some(maybe_gen_id::<M>(id.as_str(), ids)),
|
||||
Some(id) => Some(maybe_gen_id::<M, R>(window, id.as_str(), ids)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +361,11 @@ export type GetKeyValueRequest = { key: string, };
|
||||
|
||||
export type GetKeyValueResponse = { value?: string, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
export type GetTemplateFunctionConfigRequest = { contextId: string, name: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type GetTemplateFunctionConfigResponse = { function: TemplateFunction, pluginRefId: string, };
|
||||
|
||||
export type GetTemplateFunctionSummaryResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type GetThemesRequest = Record<string, never>;
|
||||
|
||||
@@ -385,7 +389,7 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: PluginWindowContext, payload: InternalEventPayload, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type JsonPrimitive = string | number | boolean | null;
|
||||
|
||||
|
||||
@@ -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 Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use reqwest::{Response, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use tauri::{AppHandle, Runtime, is_dev};
|
||||
use tauri::{AppHandle, Runtime};
|
||||
use ts_rs::TS;
|
||||
use yaak_common::api_client::yaak_api_client;
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
@@ -96,11 +96,7 @@ pub async fn search_plugins<R: Runtime>(
|
||||
}
|
||||
|
||||
fn build_url(path: &str) -> Url {
|
||||
let base_url = if is_dev() {
|
||||
"http://localhost:9444/api/v1/plugins"
|
||||
} else {
|
||||
"https://api.yaak.app/api/v1/plugins"
|
||||
};
|
||||
let base_url = "https://api.yaak.app/api/v1/plugins";
|
||||
Url::from_str(&format!("{base_url}{path}")).unwrap()
|
||||
}
|
||||
|
||||
|
||||
@@ -99,8 +99,10 @@ pub enum InternalEventPayload {
|
||||
CallGrpcRequestActionRequest(CallGrpcRequestActionRequest),
|
||||
|
||||
// Template Functions
|
||||
GetTemplateFunctionsRequest,
|
||||
GetTemplateFunctionsResponse(GetTemplateFunctionsResponse),
|
||||
GetTemplateFunctionSummaryRequest(EmptyPayload),
|
||||
GetTemplateFunctionSummaryResponse(GetTemplateFunctionSummaryResponse),
|
||||
GetTemplateFunctionConfigRequest(GetTemplateFunctionConfigRequest),
|
||||
GetTemplateFunctionConfigResponse(GetTemplateFunctionConfigResponse),
|
||||
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
||||
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
||||
|
||||
@@ -123,6 +125,9 @@ pub enum InternalEventPayload {
|
||||
RenderGrpcRequestRequest(RenderGrpcRequestRequest),
|
||||
RenderGrpcRequestResponse(RenderGrpcRequestResponse),
|
||||
|
||||
TemplateRenderRequest(TemplateRenderRequest),
|
||||
TemplateRenderResponse(TemplateRenderResponse),
|
||||
|
||||
GetKeyValueRequest(GetKeyValueRequest),
|
||||
GetKeyValueResponse(GetKeyValueResponse),
|
||||
SetKeyValueRequest(SetKeyValueRequest),
|
||||
@@ -135,9 +140,6 @@ pub enum InternalEventPayload {
|
||||
WindowCloseEvent,
|
||||
CloseWindowRequest(CloseWindowRequest),
|
||||
|
||||
TemplateRenderRequest(TemplateRenderRequest),
|
||||
TemplateRenderResponse(TemplateRenderResponse),
|
||||
|
||||
ShowToastRequest(ShowToastRequest),
|
||||
ShowToastResponse(EmptyPayload),
|
||||
|
||||
@@ -673,11 +675,28 @@ pub struct CallHttpAuthenticationResponse {
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionsResponse {
|
||||
pub struct GetTemplateFunctionSummaryResponse {
|
||||
pub functions: Vec<TemplateFunction>,
|
||||
pub plugin_ref_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionConfigRequest {
|
||||
pub context_id: String,
|
||||
pub name: String,
|
||||
pub values: HashMap<String, JsonPrimitive>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionConfigResponse {
|
||||
pub function: TemplateFunction,
|
||||
pub plugin_ref_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
|
||||
@@ -10,7 +10,8 @@ use crate::events::{
|
||||
FilterRequest, FilterResponse, GetGrpcRequestActionsResponse,
|
||||
GetHttpAuthenticationConfigRequest, GetHttpAuthenticationConfigResponse,
|
||||
GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse,
|
||||
GetTemplateFunctionsResponse, GetThemesRequest, GetThemesResponse, ImportRequest,
|
||||
GetTemplateFunctionConfigRequest, GetTemplateFunctionConfigResponse,
|
||||
GetTemplateFunctionSummaryResponse, GetThemesRequest, GetThemesResponse, ImportRequest,
|
||||
ImportResponse, InternalEvent, InternalEventPayload, JsonPrimitive, PluginWindowContext,
|
||||
RenderPurpose,
|
||||
};
|
||||
@@ -39,7 +40,7 @@ use yaak_models::render::make_vars_hashmap;
|
||||
use yaak_models::util::generate_id;
|
||||
use yaak_templates::error::Error::RenderError;
|
||||
use yaak_templates::error::Result as TemplateResult;
|
||||
use yaak_templates::render_json_value_raw;
|
||||
use yaak_templates::{RenderErrorBehavior, RenderOptions, render_json_value_raw};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PluginManager {
|
||||
@@ -489,35 +490,71 @@ impl PluginManager {
|
||||
Ok(all_actions)
|
||||
}
|
||||
|
||||
pub async fn get_template_functions<R: Runtime>(
|
||||
pub async fn get_template_function_config<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||
self.get_template_functions_with_context(&PluginWindowContext::new(&window)).await
|
||||
}
|
||||
fn_name: &str,
|
||||
environment_chain: Vec<Environment>,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
model_id: &str,
|
||||
) -> Result<GetTemplateFunctionConfigResponse> {
|
||||
let results = self.get_template_function_summaries(window).await?;
|
||||
let r = results
|
||||
.iter()
|
||||
.find(|r| r.functions.iter().any(|f| f.name == fn_name))
|
||||
.ok_or_else(|| PluginNotFoundErr(fn_name.into()))?;
|
||||
|
||||
pub async fn get_template_functions_with_context(
|
||||
&self,
|
||||
window_context: &PluginWindowContext,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||
let reply_events = self
|
||||
.send_and_wait(window_context, &InternalEventPayload::GetTemplateFunctionsRequest)
|
||||
.await?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for event in reply_events {
|
||||
if let InternalEventPayload::GetTemplateFunctionsResponse(resp) = event.payload {
|
||||
result.push(resp.clone());
|
||||
let plugin = match self.get_plugin_by_ref_id(&r.plugin_ref_id).await {
|
||||
None => {
|
||||
// It's probably a native function, so just fallback to the summary
|
||||
let function = r
|
||||
.functions
|
||||
.iter()
|
||||
.find(|f| f.name == fn_name)
|
||||
.ok_or_else(|| PluginNotFoundErr(fn_name.into()))?;
|
||||
return Ok(GetTemplateFunctionConfigResponse {
|
||||
function: function.clone(),
|
||||
plugin_ref_id: r.plugin_ref_id.clone(),
|
||||
});
|
||||
}
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
let window_context = &PluginWindowContext::new(&window);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
let cb = PluginTemplateCallback::new(
|
||||
window.app_handle(),
|
||||
&window_context,
|
||||
RenderPurpose::Preview,
|
||||
);
|
||||
// We don't want to fail for this op because the UI will not be able to list any auth types then
|
||||
let render_opt = RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::ReturnEmpty,
|
||||
};
|
||||
let rendered_values = render_json_value_raw(json!(values), vars, &cb, &render_opt).await?;
|
||||
let context_id = format!("{:x}", md5::compute(model_id.to_string()));
|
||||
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
&PluginWindowContext::new(window),
|
||||
&plugin,
|
||||
&InternalEventPayload::GetTemplateFunctionConfigRequest(
|
||||
GetTemplateFunctionConfigRequest {
|
||||
values: serde_json::from_value(rendered_values)?,
|
||||
name: fn_name.to_string(),
|
||||
context_id,
|
||||
},
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
match event.payload {
|
||||
InternalEventPayload::GetTemplateFunctionConfigResponse(resp) => Ok(resp),
|
||||
InternalEventPayload::EmptyResponse(_) => {
|
||||
Err(PluginErr("Template function plugin returned empty".to_string()))
|
||||
}
|
||||
InternalEventPayload::ErrorResponse(e) => Err(PluginErr(e.error)),
|
||||
e => Err(PluginErr(format!("Template function plugin returned invalid event {:?}", e))),
|
||||
}
|
||||
|
||||
// Add Rust-based functions
|
||||
result.push(GetTemplateFunctionsResponse {
|
||||
plugin_ref_id: "__NATIVE__".to_string(), // Meh
|
||||
functions: vec![template_function_secure(), template_function_keyring()],
|
||||
});
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn call_http_request_action<R: Runtime>(
|
||||
@@ -587,7 +624,7 @@ impl PluginManager {
|
||||
environment_chain: Vec<Environment>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
model_id: &str,
|
||||
) -> Result<GetHttpAuthenticationConfigResponse> {
|
||||
let results = self.get_http_authentication_summaries(window).await?;
|
||||
let plugin = results
|
||||
@@ -601,8 +638,12 @@ impl PluginManager {
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Preview,
|
||||
);
|
||||
let rendered_values = render_json_value_raw(json!(values), vars, &cb).await?;
|
||||
let context_id = format!("{:x}", md5::compute(request_id.to_string()));
|
||||
// We don't want to fail for this op because the UI will not be able to list any auth types then
|
||||
let render_opt = RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::ReturnEmpty,
|
||||
};
|
||||
let rendered_values = render_json_value_raw(json!(values), vars, &cb, &render_opt).await?;
|
||||
let context_id = format!("{:x}", md5::compute(model_id.to_string()));
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
&PluginWindowContext::new(window),
|
||||
@@ -643,6 +684,9 @@ impl PluginManager {
|
||||
&PluginWindowContext::new(&window),
|
||||
RenderPurpose::Preview,
|
||||
),
|
||||
&RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::Throw,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let results = self.get_http_authentication_summaries(window).await?;
|
||||
@@ -713,6 +757,34 @@ impl PluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_template_function_summaries<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
) -> Result<Vec<GetTemplateFunctionSummaryResponse>> {
|
||||
let window_context = PluginWindowContext::new(window);
|
||||
let reply_events = self
|
||||
.send_and_wait(
|
||||
&window_context,
|
||||
&InternalEventPayload::GetTemplateFunctionSummaryRequest(EmptyPayload {}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for event in reply_events {
|
||||
if let InternalEventPayload::GetTemplateFunctionSummaryResponse(resp) = event.payload {
|
||||
results.push(resp.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Add Rust-based functions
|
||||
results.push(GetTemplateFunctionSummaryResponse {
|
||||
plugin_ref_id: "__NATIVE__".to_string(), // Meh
|
||||
functions: vec![template_function_secure(), template_function_keyring()],
|
||||
});
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub async fn call_template_function(
|
||||
&self,
|
||||
window_context: &PluginWindowContext,
|
||||
@@ -786,10 +858,11 @@ impl PluginManager {
|
||||
content: &str,
|
||||
content_type: &str,
|
||||
) -> Result<FilterResponse> {
|
||||
let plugin_name = if content_type.to_lowercase().contains("json") {
|
||||
"@yaak/filter-jsonpath"
|
||||
} else {
|
||||
let ct = content_type.to_lowercase();
|
||||
let plugin_name = if ct.contains("xml") || ct.contains("html") {
|
||||
"@yaak/filter-xpath"
|
||||
} else {
|
||||
"@yaak/filter-jsonpath"
|
||||
};
|
||||
|
||||
let plugin = self
|
||||
|
||||
@@ -78,6 +78,10 @@ pub fn template_function_secure_run<R: Runtime>(
|
||||
_ => return Ok("".to_string()),
|
||||
};
|
||||
|
||||
if value.is_empty() {
|
||||
return Ok("".to_string());
|
||||
}
|
||||
|
||||
let value = match value.strip_prefix("YENC_") {
|
||||
None => {
|
||||
return Err(RenderError("Could not decrypt non-encrypted value".to_string()));
|
||||
|
||||
@@ -24,6 +24,7 @@ pub async fn start_nodejs_plugin_runtime<R: Runtime>(
|
||||
let cmd = app
|
||||
.shell()
|
||||
.sidecar("yaaknode")?
|
||||
.env("HOST", addr.ip().to_string())
|
||||
.env("PORT", addr.port().to_string())
|
||||
.args(&[&plugin_runtime_main]);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user