Next (#1085)
This commit is contained in:
commit
42c50dfa79
6
AI.md
6
AI.md
|
|
@ -5,6 +5,10 @@
|
|||
- [x] The project does not include any AI functionality at runtime.
|
||||
- [x] No data, usage, or similar is sent to, or analyized by AI.
|
||||
|
||||
## Development - Autonomy level
|
||||
|
||||
OliveTin development uses up to **Level 2 of 5 (pair programmer)** on the [AI Levels of Autonomy in Software Engineering](https://blog.jread.com/posts/ai-levels-of-autonomy-in-software-engineering/) scale: AI may generate suggestions and non-trivial changes, but humans review and decide what lands in the project.
|
||||
|
||||
## Development - Contributions
|
||||
|
||||
- [x] The project **does accept** contributions that were written with AI help. **However**:
|
||||
|
|
@ -12,7 +16,7 @@
|
|||
- AI often generates very unmaintainable code as it gets longer - loads of duplication, very little function re-use amd very poor at following style guides / idiomatic design. All code contributions (AI or not) are scrutinized hard for **maintainability** and **clean merging**. Please follow the CONTRIBUTORS guide.
|
||||
- AI that helps with short tab completion is generally fine.
|
||||
- AI that writes lots of new code across lots of files, or makes lots of superfluous changes is generally less likely to be accepted.
|
||||
- Vibe coding is not a suitable way to contribute to this project.
|
||||
- Vibe coding is generally not a suitable way to contribute to this project.
|
||||
- [x] Contributors should declare when AI has been used to help write contributions in the pull request body message.
|
||||
- [x] The project uses AI as an **optional** part of the PR process (coderabbitai). Please raise any concerns about usage within the PR.
|
||||
- [x] Suggestions from coderabbitai can be accepted verbaitem, but ideally it should be the PR author that uses coderabbitai as a guide, who then re-writes the contribution.
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -10,7 +10,7 @@
|
|||
[](https://bestpractices.coreinfrastructure.org/projects/5050)
|
||||
|
||||
[](https://goreportcard.com/report/github.com/OliveTin/OliveTin)
|
||||
[-blue)](https://blog.jread.com/posts/ai-levels-of-autonomy-in-software-engineering/)
|
||||
[-blue)](https://blog.jread.com/posts/ai-levels-of-autonomy-in-software-engineering/)
|
||||
|
||||
[OliveTin 2k to 3k upgrade guide](https://docs.olivetin.app/upgrade/2k3k.html)
|
||||
</div>
|
||||
|
|
@ -87,4 +87,10 @@ OliveTin follows these principles:
|
|||
|
||||
All documentation can be found at [docs.olivetin.app](https://docs.olivetin.app). This includes installation and usage guide, etc.
|
||||
|
||||
You can find instructions in the docs on how to install as a **Linux package**, **Linux Container**, on **FreeBSD**, **Windows**, **MacOS** and other platforms, too!
|
||||
You can find instructions in the docs on how to install as a **Linux package**, **Linux Container**, on **FreeBSD**, **Windows**, **macOS** and other platforms, too!
|
||||
|
||||
## Donations & Sponsorship
|
||||
|
||||
OliveTin does not accept monetary donations or sponsorship. See [Donations & Sponsorship](https://docs.olivetin.app/reference/donations_and_sponsorship.html) for the full explanation.
|
||||
|
||||
* Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/).
|
||||
|
|
|
|||
|
|
@ -1,11 +1,29 @@
|
|||
= Diagnostics
|
||||
|
||||
OliveTin provides a built-in diagnostics page that can be used to help check how OliveTin is running and help to troubleshoot issues. It's mainly designed for checking SSH configuration at the moment.
|
||||
OliveTin provides a built-in diagnostics page that can be used to help check how OliveTin is running and help to troubleshoot issues.
|
||||
|
||||
This is a screenshot of the diagnostics page, which can be accessed by clicking the "Diagnostics" link in the navigation bar:
|
||||
|
||||
image::advanced_configuration/diagnostics/diagnostics.png[]
|
||||
|
||||
== Configuration issues
|
||||
|
||||
The Diagnostics page includes a **Configuration issues** section. OliveTin checks the loaded configuration for problems such as:
|
||||
|
||||
* Filesystem watch paths that cannot be created (for example missing directories used by `execOnFileCreatedInDir`)
|
||||
* Argument templates that fail to parse
|
||||
* Missing entity files or empty entity data for entity-bound actions
|
||||
* Invalid cron schedules
|
||||
* Entity-bound actions that also use `execOnCron` (cron cannot bind an entity)
|
||||
* Unset environment variables referenced from configuration
|
||||
* Other sanitize-time warnings (for example unknown action groups)
|
||||
|
||||
When one or more issues exist, the Diagnostics navigation link shows a count badge with the number of issues so operators notice without opening the page first. The list and badge refresh after configuration reload and after entity file changes.
|
||||
|
||||
Issues that refer to a specific action are only shown to users who are allowed to view that action. The badge count uses the same per-user filtering. Issues that are not tied to an action (for example unset environment variables) remain visible to anyone who can open Diagnostics.
|
||||
|
||||
Where possible, each issue includes the configuration source file that defined the related action or entity (for example a file under an `include` directory).
|
||||
|
||||
== Disabling Diagnostics
|
||||
|
||||
The diagnostics page is enabled by default, but you can disable it by using the OliveTin xref::security/acl.adoc#_acls_and_policies_global[security policy configuration], using the defaults, or via an ACL. Examples are shown below for each of these methods.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
The `confirmation` type argument is a special argument type, which simply disables the "Start" button until a checkbox is ticked. This can be useful if you have an action with no other arguments, but you want to prevent accidental button-clicks starting the action.
|
||||
|
||||
Confirmation arguments are usually left unnamed, so nothing is substituted into the command. If you do give the argument a `name`, the only allowed values are `0` (unchecked) and `1` (checked), so confirmation is safe to use with `shell:` actions.
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
actions:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ A full list of argument types are below;
|
|||
| (default) | xref:args/input.adoc[Textbox] | If a `type:` is not set, and `choices:` is empty, then ascii will be used, and a warning will be logged. It is recommended that you set the type explicitly, rather than relying on defaults.
|
||||
| ascii | xref:args/input.adoc[Textbox] | a-z (case insensitive), 0-9, but no spaces or punctuation
|
||||
| ascii_identifier | xref:args/input.adoc[Textbox] | Like a DNS name, a-Z (case insensitive), 0-9, `-`, `.`, and `_`.
|
||||
| dnsname | xref:args/input.adoc[Textbox] | A DNS hostname (RFC 1123). Short names (e.g. `webserver`) and FQDNs (e.g. `webserver.example.com`). Letters/digits/hyphens only, no underscores. Optional trailing dot allowed.
|
||||
| shell_safe_identifier | xref:args/input.adoc[Textbox] | Like an ascii identifier, but also allows `@` and `+`. Useful for shell-safe usernames and email-style identifiers.
|
||||
| ascii_sentence | xref:args/input.adoc[Textbox] | a-z (case insensitive), 0-9, with spaces, `.` and `,`.
|
||||
| unicode_identifier | xref:args/input.adoc[Textbox] | Like an ascii identifier, but allows unicode characters. This is useful for languages that use non-ascii characters, such as Chinese, Japanese, etc.
|
||||
|
|
@ -19,7 +20,7 @@ A full list of argument types are below;
|
|||
| regex:... | xref:args/input.adoc[Textbox] | Version 2024.03.081 and above support custom regex patterns. See xref:args/regex.adoc[Custom regex arguments].
|
||||
| int | xref:args/input.adoc[Textbox] | Any number, made up of the characters 0 to 9. Negative numbers are not supported.
|
||||
| url | xref:args/input.adoc[Textbox] | A URL (e.g. https://example.com). Accepts any scheme, including `file://` and `ftp://`. See warning below.
|
||||
| confirmation | xref:args/input_confirmation.adoc[Confirmation] | A "hidden" argument that makes the action require a confirmation before launching.
|
||||
| confirmation | xref:args/input_confirmation.adoc[Confirmation] | A UI gate that requires a checkbox before starting. Usually unnamed (nothing is substituted). If named, the value is only `0` or `1`.
|
||||
| checklist | xref:args/input_checklist.adoc[Checklist] | Multiple checkboxes from predefined choices. Selected values are passed as a comma-separated string.
|
||||
| n/a, but `choices` used | xref:args/input_dropdown.adoc[Dropdown] | A "hidden" argument that makes the action require a confirmation before launching.
|
||||
| raw_string_multiline | xref:args/input_textarea.adoc[Textarea] | Anything. This is **dangerous**, as effectively people can type anything they like
|
||||
|
|
|
|||
|
|
@ -6,10 +6,6 @@ You will need approximately **10 minutes** to install OliveTin on almost every p
|
|||
|
||||
You probably will need about **10-20 minutes** to understand how OliveTin configuration works, and to be able to write your first action to make it do something useful.
|
||||
|
||||
== Where to find help
|
||||
|
||||
include::partial$support.adoc[]
|
||||
|
||||
== First step: where are you installing?
|
||||
|
||||
* Linux
|
||||
|
|
@ -27,9 +23,17 @@ include::partial$support.adoc[]
|
|||
*** xref:install/docker_compose.adoc[Docker Compose]
|
||||
*** xref:install/helm.adoc[Kubernetes with Helm]
|
||||
*** xref:install/k8s.adoc[Kubernetes with Manifests]
|
||||
* xref:install/bsd.adoc[FreeBSD]
|
||||
* xref:install/windows.adoc[Windows]
|
||||
* xref:install/macos.adoc[MacOS]
|
||||
* xref:install/bsd.adoc[BSD]
|
||||
* Windows
|
||||
** xref:install/windows.adoc[Windows]
|
||||
** xref:install/windows.adoc[Windows Service]
|
||||
* macOS
|
||||
** xref:install/macos.adoc[macOS Desktop]
|
||||
** xref:install/macos.adoc[macOS Service]
|
||||
|
||||
== Where to find help
|
||||
|
||||
include::partial$support.adoc[]
|
||||
|
||||
== What's Next?
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,11 @@ tar -xzf OliveTin-darwin-arm64.tar.gz
|
|||
cd OliveTin-darwin-arm64
|
||||
----
|
||||
|
||||
== Remove the Gatekeeper quarantine
|
||||
== Gatekeeper and notarization
|
||||
|
||||
The binary is downloaded from the internet and is not notarized by Apple, so on first run Gatekeeper blocks it with a message like _"OliveTin can't be opened because Apple cannot check it for malicious software."_
|
||||
Current release binaries are **Developer ID signed and notarized** by Apple. After extract, you should be able to run `./OliveTin` normally.
|
||||
|
||||
Clear the quarantine attribute so it will run:
|
||||
If Gatekeeper still blocks an older (unsigned) build, or you see a prompt that Apple cannot check the binary for malicious software, clear the quarantine attribute:
|
||||
|
||||
[source,shell]
|
||||
----
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This option installs OliveTin as a launchd service, so it runs in the background and starts automatically. This is the macOS equivalent of running OliveTin as a Linux systemd service or a xref:install/windows_service.adoc[Windows service]. If you just want to run OliveTin as a regular application, follow the xref:install/macos.adoc[macOS install] instructions instead.
|
||||
|
||||
Before continuing, complete the xref:install/macos.adoc[macOS install] steps (download, extract, and clear the Gatekeeper quarantine) and confirm OliveTin starts correctly by running `./OliveTin`.
|
||||
Before continuing, complete the xref:install/macos.adoc[macOS install] steps (download, extract, and confirm Gatekeeper allows the binary) and confirm OliveTin starts correctly by running `./OliveTin`.
|
||||
|
||||
== Choose LaunchAgent or LaunchDaemon
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ actions:
|
|||
|
||||
From the above example, you can see there There are two types of logs - **results (.yaml)** and **output (.log)**
|
||||
|
||||
Filenames are built from the action title, a unix timestamp, and the execution tracking ID. Characters that are unsafe in filenames (for example `/` and `\`) are replaced with `_` so titles like `Create/update Monthly Report` still write a single file. The original title is unchanged inside the results YAML.
|
||||
|
||||
* **Results (.yaml)** - this captures almost everything that OliveTin knows about the action and looks like this.
|
||||
+
|
||||
[source,yaml]
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
Sometimes I (James Read) get asked if people can donate money, or sponsor me for OliveTin. If you are reading this page, maybe you are thinking the same.
|
||||
|
||||
**I do not accept donations or sponsorship** for OliveTin, but I want to **thank you very much indeed** for thinking about the potential.
|
||||
**I do not accept monetary donations or sponsorship** for OliveTin, but I want to **thank you very much indeed** for thinking about the potential.
|
||||
|
||||
I have a job that pays me, where I don't write code as part of my day job (very often) - I enjoy coding as a hobby in my spare time. If I get money for that, it somehow takes the pleasure or fun out of it, or makes it feel like a job. There are concerns that I might "prefer" to work on one user's feature request if I am sponsored, or even feel compelled that I have to work on something because someone gave me money. I don't want OliveTin development to go that way. There are also other little considerations, like being paid on the side out of my job then affects my job, and tax, and so on and so on. This is why I don't take any form of money, donations or sponsorship.
|
||||
|
||||
Another way that you can show your appreciation for OliveTin, that actually means a lot more than money, is to xref:reference/contribute.adoc[contribute to OliveTin]. There are little ways and big ways to contribute - depends on if you have 2 minutes or 2 weeks to give!
|
||||
|
||||
|
|
|
|||
|
|
@ -199,6 +199,11 @@ async function handleConfigChangedEvent (j) {
|
|||
window.dispatchEvent(j)
|
||||
}
|
||||
|
||||
async function handleEntityChangedEvent (j) {
|
||||
await refreshInitAfterConfigChange()
|
||||
window.dispatchEvent(j)
|
||||
}
|
||||
|
||||
const eventCaseToTypeName = {
|
||||
entityChanged: 'EventEntityChanged',
|
||||
configChanged: 'EventConfigChanged',
|
||||
|
|
@ -230,9 +235,13 @@ function handleEvent (msg) {
|
|||
case 'EventHeartbeat':
|
||||
break
|
||||
case 'EventOutputChunk':
|
||||
case 'EventEntityChanged':
|
||||
window.dispatchEvent(j)
|
||||
break
|
||||
case 'EventEntityChanged':
|
||||
handleEntityChangedEvent(j).catch((err) => {
|
||||
console.error('EventEntityChanged handler failed:', err)
|
||||
})
|
||||
break
|
||||
case 'EventExecutionFinished':
|
||||
case 'EventExecutionStarted':
|
||||
window.dispatchEvent(j)
|
||||
|
|
|
|||
|
|
@ -11,25 +11,25 @@
|
|||
"dependencies": {
|
||||
"@connectrpc/connect": "^2.1.2",
|
||||
"@connectrpc/connect-web": "^2.1.2",
|
||||
"@hugeicons/core-free-icons": "^4.2.2",
|
||||
"@hugeicons/core-free-icons": "^4.2.3",
|
||||
"@hugeicons/vue": "^1.0.7",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"iconify-icon": "^3.0.2",
|
||||
"picocrank": "^1.18.2",
|
||||
"picocrank": "^1.21.1",
|
||||
"standard": "^17.1.2",
|
||||
"unplugin-vue-components": "^32.1.0",
|
||||
"vite": "^8.1.3",
|
||||
"vue": "^3.5.39",
|
||||
"vue-i18n": "^11.4.6",
|
||||
"vue-router": "^5.1.0"
|
||||
"vite": "^8.1.5",
|
||||
"vue": "^3.5.40",
|
||||
"vue-i18n": "^11.4.7",
|
||||
"vue-router": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-vue": "^10.9.2",
|
||||
"eslint-plugin-vue": "^10.10.0",
|
||||
"process": "^0.11.10",
|
||||
"stylelint": "^17.14.0",
|
||||
"stylelint": "^17.14.1",
|
||||
"stylelint-config-standard": "^40.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -52,13 +52,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "8.0.0-rc.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0-rc.5.tgz",
|
||||
"integrity": "sha512-nFZPWz3FHIS7y6rMIVoa/WBwjdutfIaRJIBQjzn+t3RnecZoRNlGmGcyR2wb0T/IgSd50Kz/6dG8/LvMCRunjg==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0.tgz",
|
||||
"integrity": "sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^8.0.0-rc.5",
|
||||
"@babel/types": "^8.0.0-rc.5",
|
||||
"@babel/parser": "^8.0.0",
|
||||
"@babel/types": "^8.0.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"@types/jsesc": "^2.5.0",
|
||||
|
|
@ -69,30 +69,30 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/generator/node_modules/@babel/helper-string-parser": {
|
||||
"version": "8.0.0-rc.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0-rc.5.tgz",
|
||||
"integrity": "sha512-sN7R8rBvDurfaziNfDEIjIntlazmlkCDGO4SNl2RJ3wRCn+QxspLV7hzYAE8WWVd2joVuT8sUxeePdLp2idI1A==",
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz",
|
||||
"integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator/node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "8.0.0-rc.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-rc.5.tgz",
|
||||
"integrity": "sha512-ehJDxHvtbZ85RtX/L2fi0h9AGsBNqB5Euv1EB8RMAvGYvD+2X+QbpzzOpbklnNXO+WSZJNOaetw2BBj27xsWVg==",
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz",
|
||||
"integrity": "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator/node_modules/@babel/parser": {
|
||||
"version": "8.0.0-rc.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0-rc.5.tgz",
|
||||
"integrity": "sha512-/Mfg83rK3+jsRbl4Vbd0jqxc6M1A1/WNFtgrowRM1unEsD3XcNnrBdMM0JWakd0/RN9lseQKwPduW1TiEwKOlQ==",
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.4.tgz",
|
||||
"integrity": "sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^8.0.0-rc.5"
|
||||
"@babel/types": "^8.0.4"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
|
|
@ -102,13 +102,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/generator/node_modules/@babel/types": {
|
||||
"version": "8.0.0-rc.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0-rc.5.tgz",
|
||||
"integrity": "sha512-JeSVu/m8x/zpp4CLjYHVNXuhEyOkhPXuxM8YOXjh6L4LlvQNKuUNOTo5KdBuKAcTDHw8DquToTaEkhsBqPXOaA==",
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.4.tgz",
|
||||
"integrity": "sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^8.0.0-rc.5",
|
||||
"@babel/helper-validator-identifier": "^8.0.0-rc.5"
|
||||
"@babel/helper-string-parser": "^8.0.0",
|
||||
"@babel/helper-validator-identifier": "^8.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.18.0 || >=24.11.0"
|
||||
|
|
@ -168,13 +168,13 @@
|
|||
"peer": true
|
||||
},
|
||||
"node_modules/@cacheable/memory": {
|
||||
"version": "2.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.9.tgz",
|
||||
"integrity": "sha512-HdMx6DoGywB30vacDbBsITbIX4pgFqj1zsrV58jZBUw3klzkNoXhj7qOqAgledhxG7YZI5rBSJg7Zp8/VG0DuA==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz",
|
||||
"integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cacheable/utils": "^2.4.1",
|
||||
"@cacheable/utils": "^2.5.0",
|
||||
"@keyv/bigmap": "^1.3.1",
|
||||
"hookified": "^1.15.1",
|
||||
"keyv": "^5.6.0"
|
||||
|
|
@ -208,9 +208,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@cacheable/utils": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.1.tgz",
|
||||
"integrity": "sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz",
|
||||
"integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -295,9 +295,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@csstools/css-syntax-patches-for-csstree": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz",
|
||||
"integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz",
|
||||
"integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -883,9 +883,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
|
||||
"integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
|
||||
"integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eslint-visitor-keys": "^3.4.3"
|
||||
|
|
@ -942,9 +942,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@hugeicons/core-free-icons": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-4.2.2.tgz",
|
||||
"integrity": "sha512-fjQW3p6cwoJmwK3oCnV8Z3PC5sHihDvr2jkAHax8cxqp62GaV/D7B/Rnao+JwicW8fmLZUQ6QrzWfoyMFOEQlQ==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-4.2.3.tgz",
|
||||
"integrity": "sha512-fpiU0qFZkv4ABi23xJ2m4qNr0BBfuIaYjPboN8WDA6aj9D+OzXa7eykxKOpACZRuGbDz1U9S5DsNx1d5KNnsgA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@hugeicons/vue": {
|
||||
|
|
@ -998,14 +998,14 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "11.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.6.tgz",
|
||||
"integrity": "sha512-EOeHO95XESK9IFHgHeZXunsM/WBAoCA0DlaWODvx14vKmetAuS97t+l6Xe9hTUqntPpF93vtVSjjUDafw3wXMw==",
|
||||
"version": "11.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.7.tgz",
|
||||
"integrity": "sha512-MSB/sBKwEWJTILvQIhg2rnIcwPpLayo3wGwvVA+dJTNeUBD9GoqQgAaSOLdI9iOPDHCm9YoVnLqpfzza98MpkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/devtools-types": "11.4.6",
|
||||
"@intlify/message-compiler": "11.4.6",
|
||||
"@intlify/shared": "11.4.6"
|
||||
"@intlify/devtools-types": "11.4.7",
|
||||
"@intlify/message-compiler": "11.4.7",
|
||||
"@intlify/shared": "11.4.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22"
|
||||
|
|
@ -1015,13 +1015,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@intlify/devtools-types": {
|
||||
"version": "11.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.6.tgz",
|
||||
"integrity": "sha512-wowQPpNem56b2d43IJmqbrzG2FeBKe5f/kUGlpNuBmXs6OSqncF8m1+1lxHuW8ISZJF0ma2RkW3iLkw0g0G4VA==",
|
||||
"version": "11.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.7.tgz",
|
||||
"integrity": "sha512-GSz+J+hqH+AEpAHIYya6fSufS30OaMnG39HiZX7DmGKi3+aaLvassCfsXENEc4Wr4m68q2YP0QdMdB3D9UeAXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.4.6",
|
||||
"@intlify/shared": "11.4.6"
|
||||
"@intlify/core-base": "11.4.7",
|
||||
"@intlify/shared": "11.4.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22"
|
||||
|
|
@ -1031,12 +1031,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "11.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.6.tgz",
|
||||
"integrity": "sha512-5nj3jULqeTAC1WovwMs1LQWgatTa2pM/rXN9T3XW8rdOtXW9ZF6/GLSNFTKDQmPLwclhPdgUWLJ/4w3fMeeC/Q==",
|
||||
"version": "11.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.7.tgz",
|
||||
"integrity": "sha512-bHxmh7n94N4N1evADeb7XTkc3jTw6Ki5biMFZVSX6Jmk+iehy8/maeH2XUsBI27rtKIK+Hzc6QnVAKggUwylKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "11.4.6",
|
||||
"@intlify/shared": "11.4.7",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -1047,9 +1047,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "11.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.6.tgz",
|
||||
"integrity": "sha512-m1p1HHAMLhqSpTRH7VnXdrN0CQ4y+9vunFkpLkbD8soIuBsnQdawZXqMCgvwI2UVF9Ww7sVaw7g9tV2VO7shoA==",
|
||||
"version": "11.4.7",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.7.tgz",
|
||||
"integrity": "sha512-OtjPZan3No2OZZFnMUiCVsXC6+j+XRwEywaFDk0AoayAbLuPesyDloXhJZLl9JUl5vHZeQUkYSbEA8VX+CWMjg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 22"
|
||||
|
|
@ -1481,9 +1481,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "6.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz",
|
||||
"integrity": "sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==",
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz",
|
||||
"integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rolldown/pluginutils": "^1.0.1"
|
||||
|
|
@ -1497,9 +1497,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue-macros/common": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.2.tgz",
|
||||
"integrity": "sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.4.tgz",
|
||||
"integrity": "sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-sfc": "^3.5.22",
|
||||
|
|
@ -1524,53 +1524,53 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz",
|
||||
"integrity": "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.40.tgz",
|
||||
"integrity": "sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@vue/shared": "3.5.39",
|
||||
"@vue/shared": "3.5.40",
|
||||
"entities": "^7.0.1",
|
||||
"estree-walker": "^2.0.2",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-dom": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz",
|
||||
"integrity": "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz",
|
||||
"integrity": "sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-core": "3.5.39",
|
||||
"@vue/shared": "3.5.39"
|
||||
"@vue/compiler-core": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-sfc": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.39.tgz",
|
||||
"integrity": "sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.40.tgz",
|
||||
"integrity": "sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@vue/compiler-core": "3.5.39",
|
||||
"@vue/compiler-dom": "3.5.39",
|
||||
"@vue/compiler-ssr": "3.5.39",
|
||||
"@vue/shared": "3.5.39",
|
||||
"@vue/compiler-core": "3.5.40",
|
||||
"@vue/compiler-dom": "3.5.40",
|
||||
"@vue/compiler-ssr": "3.5.40",
|
||||
"@vue/shared": "3.5.40",
|
||||
"estree-walker": "^2.0.2",
|
||||
"magic-string": "^0.30.21",
|
||||
"postcss": "^8.5.15",
|
||||
"postcss": "^8.5.19",
|
||||
"source-map-js": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-ssr": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.39.tgz",
|
||||
"integrity": "sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.40.tgz",
|
||||
"integrity": "sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.39",
|
||||
"@vue/shared": "3.5.39"
|
||||
"@vue/compiler-dom": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-api": {
|
||||
|
|
@ -1580,71 +1580,69 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/devtools-kit": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.1.2.tgz",
|
||||
"integrity": "sha512-f75/upc+GCyjXErpgPGz4582ujS0L/adAltGy+tqXMGUJpgAcfGr6CxnnhpZY8BHuMYt6KpbF8uaFrrQG66rGQ==",
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.1.5.tgz",
|
||||
"integrity": "sha512-FcSAxsi4eWuXLCB7Rv9lj0aIVHHPNVQ2BazGf4RJTc2JCqb4BQg0hk87ZFhminCfl+mD5OUI0rX2cgyu4kJOGA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-shared": "^8.1.2",
|
||||
"@vue/devtools-shared": "^8.1.5",
|
||||
"birpc": "^2.6.1",
|
||||
"hookable": "^5.5.3",
|
||||
"perfect-debounce": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/devtools-shared": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.1.2.tgz",
|
||||
"integrity": "sha512-X9RyVFYAdkBe4IUf5v48TxBF/6QPmF8CmWrDAjXzfUHrgQ/HGfTC1A6TqgXqZ03ye66l3AD51BAGD69IvKM9sw==",
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.1.5.tgz",
|
||||
"integrity": "sha512-mhT4zcPFhF+Xk1O4BfhhrbXzpmfqY03fS6xGpcllbQG7lDjhQf8pQHcTIhqQIYx1hfwtHmk/6jM96ele0UxPqQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/reactivity": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.39.tgz",
|
||||
"integrity": "sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.40.tgz",
|
||||
"integrity": "sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/shared": "3.5.39"
|
||||
"@vue/shared": "3.5.40"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-core": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.39.tgz",
|
||||
"integrity": "sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.40.tgz",
|
||||
"integrity": "sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.39",
|
||||
"@vue/shared": "3.5.39"
|
||||
"@vue/reactivity": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/runtime-dom": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.39.tgz",
|
||||
"integrity": "sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.40.tgz",
|
||||
"integrity": "sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/reactivity": "3.5.39",
|
||||
"@vue/runtime-core": "3.5.39",
|
||||
"@vue/shared": "3.5.39",
|
||||
"@vue/reactivity": "3.5.40",
|
||||
"@vue/runtime-core": "3.5.40",
|
||||
"@vue/shared": "3.5.40",
|
||||
"csstype": "^3.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/server-renderer": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.39.tgz",
|
||||
"integrity": "sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.40.tgz",
|
||||
"integrity": "sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-ssr": "3.5.39",
|
||||
"@vue/shared": "3.5.39"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.5.39"
|
||||
"@vue/compiler-ssr": "3.5.40",
|
||||
"@vue/runtime-dom": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/shared": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.39.tgz",
|
||||
"integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.40.tgz",
|
||||
"integrity": "sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@xterm/addon-fit": {
|
||||
|
|
@ -1974,9 +1972,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
|
||||
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
|
|
@ -2018,14 +2016,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/cacheable": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.5.tgz",
|
||||
"integrity": "sha512-EQfaKe09tl615iNvq/TBRWTFf1AKJNXYQSsMx0Z3EI0nA+pVsVPS8wJhnRlkbdacKPh1d0qVIhwTc2zsQNFEEg==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz",
|
||||
"integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cacheable/memory": "^2.0.8",
|
||||
"@cacheable/utils": "^2.4.1",
|
||||
"@cacheable/memory": "^2.2.0",
|
||||
"@cacheable/utils": "^2.5.0",
|
||||
"hookified": "^1.15.0",
|
||||
"keyv": "^5.6.0",
|
||||
"qified": "^0.10.1"
|
||||
|
|
@ -3015,18 +3013,18 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vue": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.9.2.tgz",
|
||||
"integrity": "sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==",
|
||||
"version": "10.10.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.10.0.tgz",
|
||||
"integrity": "sha512-dL9x9rBHqqNcByWiLOHK6L0SB97V82/NC0cZRn9cXPjM7pCuWlpQQP9bFH4vjBv80ej1ZpzAkuD8zWH1o9bZbA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"natural-compare": "^1.4.0",
|
||||
"nth-check": "^2.1.1",
|
||||
"postcss-selector-parser": "^7.1.0",
|
||||
"semver": "^7.6.3",
|
||||
"xml-name-validator": "^4.0.0"
|
||||
"postcss-selector-parser": "^7.1.4",
|
||||
"semver": "^7.8.5",
|
||||
"xml-name-validator": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
|
@ -3232,9 +3230,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
|
||||
"integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3597,9 +3595,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/globby": {
|
||||
"version": "16.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz",
|
||||
"integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==",
|
||||
"version": "16.2.2",
|
||||
"resolved": "https://registry.npmjs.org/globby/-/globby-16.2.2.tgz",
|
||||
"integrity": "sha512-NLvV9ubZ6NDsJaOpKPy3cQeJpKi9DcWiyCiFUpJPA0YihRqiE6RWaLUmgNNPr8MgPpLZjnBjSmou7uZBRJv9wA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -3618,9 +3616,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/globby/node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
|
||||
"integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -4311,9 +4309,9 @@
|
|||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
|
@ -4934,9 +4932,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||
"version": "3.3.16",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
|
@ -4966,6 +4964,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nostics": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nostics/-/nostics-1.2.0.tgz",
|
||||
"integrity": "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
|
|
@ -5268,9 +5272,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picocrank": {
|
||||
"version": "1.18.2",
|
||||
"resolved": "https://registry.npmjs.org/picocrank/-/picocrank-1.18.2.tgz",
|
||||
"integrity": "sha512-YR1Y00KnEPjH6OCrijSxzTHoyHLhKrsPhkA7ubOkbvcUuD7wtVKbRxfPkjAkA838dGymjoG//iFca2oLnNT5tg==",
|
||||
"version": "1.21.1",
|
||||
"resolved": "https://registry.npmjs.org/picocrank/-/picocrank-1.21.1.tgz",
|
||||
"integrity": "sha512-VnfNFok7BkpLKymAIbD2zUA0bf/AiNBS63Xk2mfi8uXOkdAmf06pQaI0ssGBv2/S9p1jbCATWj9OFz1uqa39NQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@hugeicons/core-free-icons": "^4.2.2",
|
||||
|
|
@ -5400,9 +5404,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.16",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
|
||||
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
|
||||
"version": "8.5.22",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz",
|
||||
"integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -5419,7 +5423,7 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.12",
|
||||
"nanoid": "^3.3.16",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
|
|
@ -6233,9 +6237,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/stylelint": {
|
||||
"version": "17.14.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.0.tgz",
|
||||
"integrity": "sha512-8xkHPpdqYryeIsOgfsYTmr6cIeC4nLYWk5S8BPxpodq8mIuepggkMljsHewWfuAjj/+qpRKou2QerhjMH3iasg==",
|
||||
"version": "17.14.1",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.14.1.tgz",
|
||||
"integrity": "sha512-xVQwyiuxALUBNB2fBe0tmNemg9KqLtdj3T64mioFDar79B2cU8LIyz+3KL6LdiHs9NkeNfwxpKSaIVOY8f112g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -6251,7 +6255,7 @@
|
|||
"dependencies": {
|
||||
"@csstools/css-calc": "^3.2.1",
|
||||
"@csstools/css-parser-algorithms": "^4.0.0",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.5",
|
||||
"@csstools/css-syntax-patches-for-csstree": "^1.1.6",
|
||||
"@csstools/css-tokenizer": "^4.0.0",
|
||||
"@csstools/media-query-list-parser": "^5.0.0",
|
||||
"@csstools/selector-resolve-nested": "^4.0.0",
|
||||
|
|
@ -6263,9 +6267,9 @@
|
|||
"debug": "^4.4.3",
|
||||
"fast-glob": "^3.3.3",
|
||||
"fastest-levenshtein": "^1.0.16",
|
||||
"file-entry-cache": "^11.1.3",
|
||||
"file-entry-cache": "^11.1.5",
|
||||
"global-modules": "^2.0.0",
|
||||
"globby": "^16.2.0",
|
||||
"globby": "^16.2.1",
|
||||
"globjoin": "^0.1.4",
|
||||
"html-tags": "^5.1.0",
|
||||
"ignore": "^7.0.5",
|
||||
|
|
@ -6275,12 +6279,12 @@
|
|||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.5.15",
|
||||
"postcss": "^8.5.16",
|
||||
"postcss-safe-parser": "^7.0.1",
|
||||
"postcss-selector-parser": "^7.1.4",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"string-width": "^8.2.1",
|
||||
"supports-hyperlinks": "^4.4.0",
|
||||
"supports-hyperlinks": "^4.5.0",
|
||||
"svg-tags": "^1.0.0",
|
||||
"table": "^6.9.0",
|
||||
"write-file-atomic": "^7.0.1"
|
||||
|
|
@ -6355,23 +6359,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/stylelint/node_modules/file-entry-cache": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.3.tgz",
|
||||
"integrity": "sha512-oMbq0PD6VIiIwMF6LIa7MEwd/l9huKwmqRKXqmrkqIZv8CvRbfowL+L0ryAl8h//HfAS0zS+4SbYoRyAoA6BJA==",
|
||||
"version": "11.1.5",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz",
|
||||
"integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"flat-cache": "^6.1.22"
|
||||
"flat-cache": "^6.1.23"
|
||||
}
|
||||
},
|
||||
"node_modules/stylelint/node_modules/flat-cache": {
|
||||
"version": "6.1.22",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.22.tgz",
|
||||
"integrity": "sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug==",
|
||||
"version": "6.1.23",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz",
|
||||
"integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cacheable": "^2.3.4",
|
||||
"cacheable": "^2.5.0",
|
||||
"flatted": "^3.4.2",
|
||||
"hookified": "^1.15.0"
|
||||
}
|
||||
|
|
@ -6431,9 +6435,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/supports-hyperlinks": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz",
|
||||
"integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.5.0.tgz",
|
||||
"integrity": "sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -6750,27 +6754,67 @@
|
|||
}
|
||||
},
|
||||
"node_modules/unplugin": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz",
|
||||
"integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.3.0.tgz",
|
||||
"integrity": "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"picomatch": "^4.0.3",
|
||||
"picomatch": "^4.0.4",
|
||||
"webpack-virtual-modules": "^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@farmfe/core": "*",
|
||||
"@rspack/core": "*",
|
||||
"bun-types-no-globals": "*",
|
||||
"esbuild": "*",
|
||||
"rolldown": "*",
|
||||
"rollup": "*",
|
||||
"unloader": "*",
|
||||
"vite": "*",
|
||||
"webpack": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@farmfe/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@rspack/core": {
|
||||
"optional": true
|
||||
},
|
||||
"bun-types-no-globals": {
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
},
|
||||
"rolldown": {
|
||||
"optional": true
|
||||
},
|
||||
"rollup": {
|
||||
"optional": true
|
||||
},
|
||||
"unloader": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
},
|
||||
"webpack": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-utils": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
|
||||
"integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz",
|
||||
"integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pathe": "^2.0.3",
|
||||
"picomatch": "^4.0.3"
|
||||
"picomatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0"
|
||||
|
|
@ -6872,15 +6916,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz",
|
||||
"integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==",
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz",
|
||||
"integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
"postcss": "^8.5.16",
|
||||
"rolldown": "~1.1.3",
|
||||
"picomatch": "^4.0.5",
|
||||
"postcss": "^8.5.17",
|
||||
"rolldown": "~1.1.5",
|
||||
"tinyglobby": "^0.2.17"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -6949,9 +6993,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
|
@ -6961,16 +7005,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.5.39",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.39.tgz",
|
||||
"integrity": "sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==",
|
||||
"version": "3.5.40",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.40.tgz",
|
||||
"integrity": "sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.39",
|
||||
"@vue/compiler-sfc": "3.5.39",
|
||||
"@vue/runtime-dom": "3.5.39",
|
||||
"@vue/server-renderer": "3.5.39",
|
||||
"@vue/shared": "3.5.39"
|
||||
"@vue/compiler-dom": "3.5.40",
|
||||
"@vue/compiler-sfc": "3.5.40",
|
||||
"@vue/runtime-dom": "3.5.40",
|
||||
"@vue/server-renderer": "3.5.40",
|
||||
"@vue/shared": "3.5.40"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "*"
|
||||
|
|
@ -7074,14 +7118,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "11.4.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.6.tgz",
|
||||
"integrity": "sha512-l0gE7Rfy0phCa5ChKYkOq543Wgd39BCK6hkktfr1Ed4D99oRkgPK9ffShASZdeC8OJxGfdWmpYoAaAH6iLEuIg==",
|
||||
"version": "11.4.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.7.tgz",
|
||||
"integrity": "sha512-j6RyshdPPzqLiMAUpnpvZGFPM+rRoWi14Sl5yTsquvoW0/56DWyvhAj2o9TO2YXGvb6teg8T0xrYO9jR3urvdw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "11.4.6",
|
||||
"@intlify/devtools-types": "11.4.6",
|
||||
"@intlify/shared": "11.4.6",
|
||||
"@intlify/core-base": "11.4.7",
|
||||
"@intlify/devtools-types": "11.4.7",
|
||||
"@intlify/shared": "11.4.7",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -7095,27 +7139,28 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.1.0.tgz",
|
||||
"integrity": "sha512-HAbiLzLEHQwxPgvsbOJDAwtavszEgLwri6XfyrsPECIFez8+59xc9LofWVdc/HEaSRT822lJ8H9Ns38VVond5g==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.2.0.tgz",
|
||||
"integrity": "sha512-QAC5i0LEb1GLG0LXDQmHu8L7FX12j0KwU/JTKmLQUJMrn04gQdKP6Du+p0QwpHb3iy71vBlqnHQ8WAfOSAWhqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/generator": "^8.0.0-rc.4",
|
||||
"@vue-macros/common": "^3.1.1",
|
||||
"@vue/devtools-api": "^8.1.2",
|
||||
"@babel/generator": "^8.0.0",
|
||||
"@vue-macros/common": "^3.1.3",
|
||||
"@vue/devtools-api": "^8.1.5",
|
||||
"ast-walker-scope": "^0.9.0",
|
||||
"chokidar": "^5.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"local-pkg": "^1.1.2",
|
||||
"local-pkg": "^1.2.1",
|
||||
"magic-string": "^0.30.21",
|
||||
"mlly": "^1.8.2",
|
||||
"muggle-string": "^0.4.1",
|
||||
"nostics": "^1.1.4",
|
||||
"pathe": "^2.0.3",
|
||||
"picomatch": "^4.0.3",
|
||||
"picomatch": "^4.0.5",
|
||||
"scule": "^1.3.0",
|
||||
"tinyglobby": "^0.2.16",
|
||||
"unplugin": "^3.0.0",
|
||||
"unplugin-utils": "^0.3.1",
|
||||
"tinyglobby": "^0.2.17",
|
||||
"unplugin": "^3.3.0",
|
||||
"unplugin-utils": "^0.3.2",
|
||||
"yaml": "^2.9.0"
|
||||
},
|
||||
"funding": {
|
||||
|
|
@ -7123,10 +7168,10 @@
|
|||
},
|
||||
"peerDependencies": {
|
||||
"@pinia/colada": ">=0.21.2",
|
||||
"@vue/compiler-sfc": "^3.5.34",
|
||||
"pinia": "^3.0.4",
|
||||
"vite": "^7.0.0 || ^8.0.0",
|
||||
"vue": "^3.5.34"
|
||||
"@vue/compiler-sfc": "^3.5.34 || ^4.0.0",
|
||||
"pinia": "^3.0.4 || ^4.0.2",
|
||||
"vite": "^7.3.0 || ^8.0.0",
|
||||
"vue": "^3.5.34 || ^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@pinia/colada": {
|
||||
|
|
@ -7144,12 +7189,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@vue/devtools-api": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.2.tgz",
|
||||
"integrity": "sha512-vA0O112YqyDuNA1s7Yb2gCgToQ/OxOWiFDO5ThLCcDy0ldHnSd1dUTaSYhOldbqoNgumE4dxtGAoAaSUKUD1Zg==",
|
||||
"version": "8.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.5.tgz",
|
||||
"integrity": "sha512-YJipMVAKe5wT5CWf5kTYCaNV7NMNjFVxJkIkJaJ4W/nCxEBzlZzrOsYKeCymdCrFZmBS/+wTWFoUs3Jf/Q6XSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-kit": "^8.1.2"
|
||||
"@vue/devtools-kit": "^8.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/json5": {
|
||||
|
|
@ -7165,9 +7210,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
|
|
@ -7320,13 +7365,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
||||
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||
"integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
"repository": "https://github.com/OliveTin/OliveTin",
|
||||
"source": "index.html",
|
||||
"devDependencies": {
|
||||
"eslint-plugin-vue": "^10.9.2",
|
||||
"eslint-plugin-vue": "^10.10.0",
|
||||
"process": "^0.11.10",
|
||||
"stylelint": "^17.14.0",
|
||||
"stylelint": "^17.14.1",
|
||||
"stylelint-config-standard": "^40.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
@ -25,20 +25,20 @@
|
|||
"dependencies": {
|
||||
"@connectrpc/connect": "^2.1.2",
|
||||
"@connectrpc/connect-web": "^2.1.2",
|
||||
"@hugeicons/core-free-icons": "^4.2.2",
|
||||
"@hugeicons/core-free-icons": "^4.2.3",
|
||||
"@hugeicons/vue": "^1.0.7",
|
||||
"@vitejs/plugin-vue": "^6.0.7",
|
||||
"@vitejs/plugin-vue": "^6.0.8",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"iconify-icon": "^3.0.2",
|
||||
"picocrank": "^1.18.2",
|
||||
"picocrank": "^1.21.1",
|
||||
"standard": "^17.1.2",
|
||||
"unplugin-vue-components": "^32.1.0",
|
||||
"vite": "^8.1.3",
|
||||
"vue": "^3.5.39",
|
||||
"vue-i18n": "^11.4.6",
|
||||
"vue-router": "^5.1.0"
|
||||
"vite": "^8.1.5",
|
||||
"vue": "^3.5.40",
|
||||
"vue-i18n": "^11.4.7",
|
||||
"vue-router": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.12.1
|
||||
// @generated by protoc-gen-es v2.13.0
|
||||
// @generated from file olivetin/api/v1/olivetin.proto (package olivetin.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
@ -1666,6 +1666,57 @@ export declare type GetDiagnosticsRequest = Message<"olivetin.api.v1.GetDiagnost
|
|||
*/
|
||||
export declare const GetDiagnosticsRequestSchema: GenMessage<GetDiagnosticsRequest>;
|
||||
|
||||
/**
|
||||
* @generated from message olivetin.api.v1.ConfigIssue
|
||||
*/
|
||||
export declare type ConfigIssue = Message<"olivetin.api.v1.ConfigIssue"> & {
|
||||
/**
|
||||
* @generated from field: string severity = 1;
|
||||
*/
|
||||
severity: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string code = 2;
|
||||
*/
|
||||
code: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string message = 3;
|
||||
*/
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string action_id = 4;
|
||||
*/
|
||||
actionId: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string action_title = 5;
|
||||
*/
|
||||
actionTitle: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string argument_name = 6;
|
||||
*/
|
||||
argumentName: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string source = 7;
|
||||
*/
|
||||
source: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string config_file = 8;
|
||||
*/
|
||||
configFile: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message olivetin.api.v1.ConfigIssue.
|
||||
* Use `create(ConfigIssueSchema)` to create a new message.
|
||||
*/
|
||||
export declare const ConfigIssueSchema: GenMessage<ConfigIssue>;
|
||||
|
||||
/**
|
||||
* @generated from message olivetin.api.v1.GetDiagnosticsResponse
|
||||
*/
|
||||
|
|
@ -1679,6 +1730,11 @@ export declare type GetDiagnosticsResponse = Message<"olivetin.api.v1.GetDiagnos
|
|||
* @generated from field: string SshFoundConfig = 2;
|
||||
*/
|
||||
SshFoundConfig: string;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated olivetin.api.v1.ConfigIssue config_issues = 3;
|
||||
*/
|
||||
configIssues: ConfigIssue[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1829,6 +1885,11 @@ export declare type InitResponse = Message<"olivetin.api.v1.InitResponse"> & {
|
|||
* @generated from field: bool show_navigate_on_start_icons = 25;
|
||||
*/
|
||||
showNavigateOnStartIcons: boolean;
|
||||
|
||||
/**
|
||||
* @generated from field: int32 config_issue_count = 26;
|
||||
*/
|
||||
configIssueCount: number;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -345,8 +345,8 @@ function renderNavigation () {
|
|||
|
||||
const rootDashboards = window.initResponse?.rootDashboards || []
|
||||
|
||||
if (typeof navigation.value.clear === 'function') {
|
||||
navigation.value.clear()
|
||||
if (typeof navigation.value.clearNavigationLinks === 'function') {
|
||||
navigation.value.clearNavigationLinks()
|
||||
}
|
||||
|
||||
for (const rootDashboard of rootDashboards) {
|
||||
|
|
@ -367,7 +367,10 @@ function renderNavigation () {
|
|||
}
|
||||
|
||||
if (showDiagnostics.value) {
|
||||
navigation.value.addRouterLink('Diagnostics', t('nav.diagnostics'))
|
||||
const issueCount = window.initResponse?.configIssueCount || 0
|
||||
navigation.value.addRouterLink('Diagnostics', t('nav.diagnostics'), {
|
||||
count: issueCount
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ function getInputType (arg) {
|
|||
return 'checkbox'
|
||||
}
|
||||
|
||||
if (arg.type === 'ascii_identifier' || arg.type === 'shell_safe_identifier' || arg.type === 'ascii' || arg.type === 'ascii_sentence') {
|
||||
if (arg.type === 'ascii_identifier' || arg.type === 'dnsname' || arg.type === 'shell_safe_identifier' || arg.type === 'ascii' || arg.type === 'ascii_sentence') {
|
||||
return 'text'
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,28 @@
|
|||
<template>
|
||||
<Section :title="t('diagnostics.config-issues')">
|
||||
<p>{{ t('diagnostics.config-issues-description') }}</p>
|
||||
|
||||
<p v-if="!loading && configIssues.length === 0">
|
||||
{{ t('diagnostics.config-issues-none') }}
|
||||
</p>
|
||||
|
||||
<Table
|
||||
v-else
|
||||
:data="configIssueRows"
|
||||
:headers="configIssueHeaders"
|
||||
:show-pagination="false"
|
||||
>
|
||||
<template #cell-severity="{ value }">
|
||||
<div
|
||||
class="tag"
|
||||
:class="value === 'error' ? 'fg-bad' : 'fg-warning'"
|
||||
>
|
||||
{{ value }}
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
</Section>
|
||||
|
||||
<Section :title="t('diagnostics.get-support')">
|
||||
<p>
|
||||
{{ t('diagnostics.get-support-description') }}
|
||||
|
|
@ -84,19 +108,41 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import Section from 'picocrank/vue/components/Section.vue'
|
||||
import Table from 'picocrank/vue/components/Table.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
const diagnostics = ref({})
|
||||
const configIssues = ref([])
|
||||
const loading = ref(false)
|
||||
const serverDiagnostics = ref('')
|
||||
const browserInfo = ref('')
|
||||
const serverDiagnosticsCopied = ref(false)
|
||||
const browserInfoCopied = ref(false)
|
||||
|
||||
const configIssueHeaders = computed(() => [
|
||||
{ key: 'severity', label: t('diagnostics.config-issue-severity'), sortable: true, width: '7rem' },
|
||||
{ key: 'code', label: t('diagnostics.config-issue-code'), sortable: true, width: '12rem' },
|
||||
{ key: 'message', label: t('diagnostics.config-issue-message'), sortable: false },
|
||||
{ key: 'actionTitle', label: t('diagnostics.config-issue-action'), sortable: true, width: '10rem' },
|
||||
{ key: 'argumentName', label: t('diagnostics.config-issue-argument'), sortable: true, width: '8rem' },
|
||||
{ key: 'configFile', label: t('diagnostics.config-issue-config-file'), sortable: true, width: '14rem' },
|
||||
{ key: 'source', label: t('diagnostics.config-issue-source'), sortable: false, width: '12rem' }
|
||||
])
|
||||
|
||||
const configIssueRows = computed(() => configIssues.value.map((issue) => ({
|
||||
severity: issue.severity || '',
|
||||
code: issue.code || '',
|
||||
message: issue.message || '',
|
||||
actionTitle: issue.actionTitle || '',
|
||||
argumentName: issue.argumentName || '',
|
||||
configFile: issue.configFile || '',
|
||||
source: issue.source || ''
|
||||
})))
|
||||
|
||||
async function fetchDiagnostics () {
|
||||
loading.value = true
|
||||
|
||||
|
|
@ -106,12 +152,14 @@ async function fetchDiagnostics () {
|
|||
sshFoundKey: response.SshFoundKey,
|
||||
sshFoundConfig: response.SshFoundConfig
|
||||
}
|
||||
configIssues.value = response.configIssues || []
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch diagnostics:', err)
|
||||
diagnostics.value = {
|
||||
sshFoundKey: t('diagnostics.unknown'),
|
||||
sshFoundConfig: t('diagnostics.unknown')
|
||||
}
|
||||
configIssues.value = []
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
|
@ -263,85 +311,12 @@ async function copyBrowserInfo () {
|
|||
|
||||
onMounted(() => {
|
||||
fetchDiagnostics()
|
||||
window.addEventListener('EventConfigChanged', fetchDiagnostics)
|
||||
window.addEventListener('EventEntityChanged', fetchDiagnostics)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('EventConfigChanged', fetchDiagnostics)
|
||||
window.removeEventListener('EventEntityChanged', fetchDiagnostics)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.diagnostics-view {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.diagnostics-content {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.note {
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid #007bff;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 0 4px 4px 0;
|
||||
font-size: 0.875rem;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.note a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.note a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.diagnostics-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.diagnostics-table td {
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom: 1px solid #f1f3f4;
|
||||
}
|
||||
|
||||
.diagnostics-table td:first-child {
|
||||
font-weight: 500;
|
||||
color: #495057;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.diagnostics-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.error-list {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.error-item {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #dc3545;
|
||||
font-family: monospace;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.error-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"wait-on": "^9.0.10"
|
||||
"wait-on": "^9.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^6.2.2",
|
||||
"eslint": "^10.6.0",
|
||||
"eslint": "^10.8.0",
|
||||
"mocha": "^11.7.6",
|
||||
"selenium-webdriver": "^4.45.0"
|
||||
"selenium-webdriver": "^4.46.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
|
|
@ -92,9 +92,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz",
|
||||
"integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
|
@ -172,9 +172,9 @@
|
|||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@hapi/tlds": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz",
|
||||
"integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==",
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.7.tgz",
|
||||
"integrity": "sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
|
@ -333,6 +333,18 @@
|
|||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
|
||||
|
|
@ -391,13 +403,14 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
|
||||
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz",
|
||||
"integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.16.0",
|
||||
"form-data": "^4.0.5",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"proxy-from-env": "^2.1.0"
|
||||
}
|
||||
},
|
||||
|
|
@ -408,16 +421,16 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion/node_modules/balanced-match": {
|
||||
|
|
@ -636,7 +649,6 @@
|
|||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
|
|
@ -734,9 +746,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
||||
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
|
|
@ -783,9 +795,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
|
||||
"integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
|
||||
"version": "10.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz",
|
||||
"integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
|
|
@ -795,7 +807,7 @@
|
|||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@eslint/config-array": "^0.23.5",
|
||||
"@eslint/config-helpers": "^0.6.0",
|
||||
"@eslint/config-helpers": "^0.7.0",
|
||||
"@eslint/core": "^1.2.1",
|
||||
"@eslint/plugin-kit": "^0.7.2",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
|
|
@ -819,7 +831,7 @@
|
|||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"minimatch": "^10.2.4",
|
||||
"minimatch": "^10.2.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.3"
|
||||
},
|
||||
|
|
@ -1159,9 +1171,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -1253,6 +1265,19 @@
|
|||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
|
|
@ -1375,9 +1400,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "18.2.1",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.1.tgz",
|
||||
"integrity": "sha512-2/OKlogiESf2Nh3TFCrRjrr9z1DRHeW0I+KReF67+4J0Ns+8hBtHRmoWAZ2OFU6I5+TWLEe6sVlSdXPjHm5UbQ==",
|
||||
"version": "18.2.3",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz",
|
||||
"integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@hapi/address": "^5.1.1",
|
||||
|
|
@ -1393,9 +1418,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
|
||||
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1625,9 +1650,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mocha/node_modules/brace-expansion": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
|
||||
"integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
|
||||
"integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -1669,7 +1694,6 @@
|
|||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
|
|
@ -1879,9 +1903,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/selenium-webdriver": {
|
||||
"version": "4.45.0",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.45.0.tgz",
|
||||
"integrity": "sha512-Cb2nqvJiwXVOtRTCYHX9D1FJR5+Ls7aL3Nev0t6n4CpXsQ//YGiiUmSCbvTDDeLtbV85SZ46qmLab4SIYKXWRw==",
|
||||
"version": "4.46.0",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.46.0.tgz",
|
||||
"integrity": "sha512-UlTkgnx9y+bf3QxFsrgUyMqC2oy1Yf+qcOs7xIC/XfsUPv/ow7Sicx6SJ7AeOn2/Z+xF1M8SLqyn983wipI82w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -2136,13 +2160,13 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/wait-on": {
|
||||
"version": "9.0.10",
|
||||
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.10.tgz",
|
||||
"integrity": "sha512-rCoJEhvMr0X6alHmwc9abbrA5ZrLZFKpFQVKPNFwl2h7DapXOGdmimIHDtLOWhT4PjhZhxFEtZoQgEXbkDWdZw==",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.1.0.tgz",
|
||||
"integrity": "sha512-PymrLXHLBM1Ju/Xspb2ADUhbPSMvbnuNvy/mN2hWtpbJ3da0h3Ky1LqwKPG5QSVR57liyO0iUpfipYl/s5qNvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.16.0",
|
||||
"joi": "^18.2.1",
|
||||
"axios": "^1.18.1",
|
||||
"joi": "^18.2.3",
|
||||
"lodash": "^4.18.1",
|
||||
"minimist": "^1.2.8",
|
||||
"rxjs": "^7.8.2"
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@
|
|||
"license": "AGPL-3.0-only",
|
||||
"devDependencies": {
|
||||
"chai": "^6.2.2",
|
||||
"eslint": "^10.6.0",
|
||||
"eslint": "^10.8.0",
|
||||
"mocha": "^11.7.6",
|
||||
"selenium-webdriver": "^4.45.0"
|
||||
"selenium-webdriver": "^4.46.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"wait-on": "^9.0.10"
|
||||
"wait-on": "^9.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe('config: dashboards with basic fieldsets', function () {
|
|||
await openSidebar()
|
||||
|
||||
const navigationLinks = await getNavigationLinks()
|
||||
assert.equal(navigationLinks.length, 5, 'Expected the nav to only have 5 links') // test dashboard + logs + diagnostics + entities + separator
|
||||
assert.equal(navigationLinks.length, 4, 'Expected the nav to only have 4 links') // test dashboard + entities + logs + diagnostics
|
||||
|
||||
const firstLink = await navigationLinks[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,6 @@ describe('config: empty dashboards are hidden', function () {
|
|||
|
||||
const navigationLinks = await getNavigationLinks()
|
||||
expect(navigationLinks).to.not.be.empty
|
||||
expect(navigationLinks.length).to.be.equal(4, 'Expected the nav to only have 4 links')
|
||||
expect(navigationLinks.length).to.be.equal(3, 'Expected the nav to only have 3 links') // entities + logs + diagnostics
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,17 @@
|
|||
"connected": "Verbunden",
|
||||
"diagnostics.browser-info": "Browser-Informationen",
|
||||
"diagnostics.browser-info-description": "Dieser Abschnitt ermöglicht es Ihnen, einen detaillierten Bericht über Ihre Browser-Informationen zu erstellen. Dies kann bei der Fehlerbehebung von browser-spezifischen Problemen hilfreich sein.",
|
||||
"diagnostics.config-issue-action": "Aktion",
|
||||
"diagnostics.config-issue-argument": "Argument",
|
||||
"diagnostics.config-issue-code": "Code",
|
||||
"diagnostics.config-issue-config-file": "Konfigurationsdatei",
|
||||
"diagnostics.config-issue-detail": "Detail",
|
||||
"diagnostics.config-issue-message": "Meldung",
|
||||
"diagnostics.config-issue-severity": "Schweregrad",
|
||||
"diagnostics.config-issue-source": "Quelle",
|
||||
"diagnostics.config-issues": "Konfigurationsprobleme",
|
||||
"diagnostics.config-issues-description": "In der aktuellen OliveTin-Konfiguration erkannte Probleme. Beheben Sie diese in Ihren Konfigurationsdateien und laden Sie OliveTin neu.",
|
||||
"diagnostics.config-issues-none": "Keine Konfigurationsprobleme erkannt.",
|
||||
"diagnostics.copied": "Kopiert!",
|
||||
"diagnostics.copy-to-clipboard": "In Zwischenablage kopieren",
|
||||
"diagnostics.found-config": "Konfiguration gefunden",
|
||||
|
|
@ -41,10 +52,18 @@
|
|||
"logs.clear-filter": "Suchfilter löschen",
|
||||
"logs.completed": "Abgeschlossen",
|
||||
"logs.exit-code": "Ausführungscode",
|
||||
"logs.filter-error": "Filterausdruck konnte nicht angewendet werden.",
|
||||
"logs.filter-help-examples": "Beispiele: backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed",
|
||||
"logs.filter-help-fields": "Felder: Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode. Operatoren: ==, !=, contains. Ein !-Präfix vor einem einzelnen Wort schließt passende Einträge aus.",
|
||||
"logs.filter-help-intro": "Filter laufen auf dem Server über Einträge, die Sie anzeigen dürfen. Kombinieren Sie Begriffe mit and / or.",
|
||||
"logs.filter-help-title": "Filtersyntax",
|
||||
"logs.filter-placeholder": "Protokolle filtern (z. B. !Update oder Status != Completed)",
|
||||
"logs.metadata": "Metadaten",
|
||||
"logs.no-logs-for-filter": "Keine Protokolle entsprechen dem aktuellen Filter.",
|
||||
"logs.no-logs-to-display": "Es gibt keine Protokolle zu anzeigen.",
|
||||
"logs.page-description": "Dies ist eine Liste von Protokollen von Aktionen, die ausgeführt wurden. Sie können die Liste nach Aktionstitel filtern.",
|
||||
"logs.queue": "Warteschlange",
|
||||
"logs.queue-action-details": "Aktionsdetails",
|
||||
"logs.queue-default-group": "Standard",
|
||||
"logs.queue-empty": "Derzeit gibt es keine aktiven oder wartenden Ausführungen.",
|
||||
"logs.queue-entity": "Entität",
|
||||
|
|
@ -76,6 +95,17 @@
|
|||
"connected": "Connected",
|
||||
"diagnostics.browser-info": "Browser Info",
|
||||
"diagnostics.browser-info-description": "This section allows you to generate a detailed report of your browser information. This can be helpful when troubleshooting browser-specific issues.",
|
||||
"diagnostics.config-issue-action": "Action",
|
||||
"diagnostics.config-issue-argument": "Argument",
|
||||
"diagnostics.config-issue-code": "Code",
|
||||
"diagnostics.config-issue-config-file": "Config file",
|
||||
"diagnostics.config-issue-detail": "Detail",
|
||||
"diagnostics.config-issue-message": "Message",
|
||||
"diagnostics.config-issue-severity": "Severity",
|
||||
"diagnostics.config-issue-source": "Source",
|
||||
"diagnostics.config-issues": "Configuration issues",
|
||||
"diagnostics.config-issues-description": "Problems detected in the current OliveTin configuration. Fix these in your config files and reload OliveTin.",
|
||||
"diagnostics.config-issues-none": "No configuration issues detected.",
|
||||
"diagnostics.copied": "Copied!",
|
||||
"diagnostics.copy-to-clipboard": "Copy to Clipboard",
|
||||
"diagnostics.found-config": "Found Config",
|
||||
|
|
@ -155,6 +185,17 @@
|
|||
"connected": "Conectado",
|
||||
"diagnostics.browser-info": "Información del navegador",
|
||||
"diagnostics.browser-info-description": "Esta sección le permite generar un informe detallado de su información del navegador. Esto puede ser útil al solucionar problemas específicos del navegador.",
|
||||
"diagnostics.config-issue-action": "Acción",
|
||||
"diagnostics.config-issue-argument": "Argumento",
|
||||
"diagnostics.config-issue-code": "Código",
|
||||
"diagnostics.config-issue-config-file": "Archivo de configuración",
|
||||
"diagnostics.config-issue-detail": "Detalle",
|
||||
"diagnostics.config-issue-message": "Mensaje",
|
||||
"diagnostics.config-issue-severity": "Severidad",
|
||||
"diagnostics.config-issue-source": "Origen",
|
||||
"diagnostics.config-issues": "Problemas de configuración",
|
||||
"diagnostics.config-issues-description": "Problemas detectados en la configuración actual de OliveTin. Corríjalos en sus archivos de configuración y vuelva a cargar OliveTin.",
|
||||
"diagnostics.config-issues-none": "No se detectaron problemas de configuración.",
|
||||
"diagnostics.copied": "¡Copiado!",
|
||||
"diagnostics.copy-to-clipboard": "Copiar al portapapeles",
|
||||
"diagnostics.found-config": "Configuración encontrada",
|
||||
|
|
@ -191,10 +232,18 @@
|
|||
"logs.clear-filter": "Limpiar filtro de búsqueda",
|
||||
"logs.completed": "Completado",
|
||||
"logs.exit-code": "Código de salida",
|
||||
"logs.filter-error": "No se pudo aplicar la expresión del filtro.",
|
||||
"logs.filter-help-examples": "Ejemplos: backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed",
|
||||
"logs.filter-help-fields": "Campos: Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode. Operadores: ==, !=, contains. Un prefijo ! en una sola palabra excluye las entradas coincidentes.",
|
||||
"logs.filter-help-intro": "Los filtros se ejecutan en el servidor sobre las entradas que puede ver. Combine términos con and / or.",
|
||||
"logs.filter-help-title": "Sintaxis del filtro",
|
||||
"logs.filter-placeholder": "Filtrar registros (p. ej. !Update o Status != Completed)",
|
||||
"logs.metadata": "Metadatos",
|
||||
"logs.no-logs-for-filter": "Ningún registro coincide con el filtro actual.",
|
||||
"logs.no-logs-to-display": "No hay registros para mostrar.",
|
||||
"logs.page-description": "Esta es una lista de registros de acciones que han sido ejecutadas. Puede filtrar la lista por título de acción.",
|
||||
"logs.queue": "Cola",
|
||||
"logs.queue-action-details": "Detalles de la acción",
|
||||
"logs.queue-default-group": "Predeterminado",
|
||||
"logs.queue-empty": "No hay ejecuciones activas o en espera en este momento.",
|
||||
"logs.queue-entity": "Entidad",
|
||||
|
|
@ -226,6 +275,17 @@
|
|||
"connected": "Connesso",
|
||||
"diagnostics.browser-info": "Informazioni del browser",
|
||||
"diagnostics.browser-info-description": "Questa sezione ti consente di generare un rapporto dettagliato delle informazioni del tuo browser. Questo può essere utile durante la risoluzione dei problemi specifici del browser.",
|
||||
"diagnostics.config-issue-action": "Azione",
|
||||
"diagnostics.config-issue-argument": "Argomento",
|
||||
"diagnostics.config-issue-code": "Codice",
|
||||
"diagnostics.config-issue-config-file": "File di configurazione",
|
||||
"diagnostics.config-issue-detail": "Dettaglio",
|
||||
"diagnostics.config-issue-message": "Messaggio",
|
||||
"diagnostics.config-issue-severity": "Gravità",
|
||||
"diagnostics.config-issue-source": "Origine",
|
||||
"diagnostics.config-issues": "Problemi di configurazione",
|
||||
"diagnostics.config-issues-description": "Problemi rilevati nella configurazione corrente di OliveTin. Correggili nei file di configurazione e ricarica OliveTin.",
|
||||
"diagnostics.config-issues-none": "Nessun problema di configurazione rilevato.",
|
||||
"diagnostics.copied": "Copiato!",
|
||||
"diagnostics.copy-to-clipboard": "Copia negli appunti",
|
||||
"diagnostics.found-config": "Configurazione trovata",
|
||||
|
|
@ -262,10 +322,18 @@
|
|||
"logs.clear-filter": "Cancella filtro di ricerca",
|
||||
"logs.completed": "Completato",
|
||||
"logs.exit-code": "Codice di uscita",
|
||||
"logs.filter-error": "Impossibile applicare l'espressione di filtro.",
|
||||
"logs.filter-help-examples": "Esempi: backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed",
|
||||
"logs.filter-help-fields": "Campi: Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode. Operatori: ==, !=, contains. Un prefisso ! su una singola parola esclude le voci corrispondenti.",
|
||||
"logs.filter-help-intro": "I filtri vengono eseguiti sul server sulle voci che sei autorizzato a visualizzare. Combina i termini con and / or.",
|
||||
"logs.filter-help-title": "Sintassi del filtro",
|
||||
"logs.filter-placeholder": "Filtra i registri (es. !Update o Status != Completed)",
|
||||
"logs.metadata": "Metadati",
|
||||
"logs.no-logs-for-filter": "Nessun registro corrisponde al filtro corrente.",
|
||||
"logs.no-logs-to-display": "Non ci sono registri da mostrare.",
|
||||
"logs.page-description": "Questa è una lista di registri delle azioni che sono state eseguite. Puoi filtrare la lista per titolo dell'azione.",
|
||||
"logs.queue": "Coda",
|
||||
"logs.queue-action-details": "Dettagli azione",
|
||||
"logs.queue-default-group": "Predefinito",
|
||||
"logs.queue-empty": "Non ci sono esecuzioni attive o in attesa al momento.",
|
||||
"logs.queue-entity": "Entità",
|
||||
|
|
@ -297,6 +365,17 @@
|
|||
"connected": "已连接",
|
||||
"diagnostics.browser-info": "浏览器信息",
|
||||
"diagnostics.browser-info-description": "此部分允许您生成浏览器信息的详细报告。这在排查浏览器特定问题时很有帮助。",
|
||||
"diagnostics.config-issue-action": "操作",
|
||||
"diagnostics.config-issue-argument": "参数",
|
||||
"diagnostics.config-issue-code": "代码",
|
||||
"diagnostics.config-issue-config-file": "配置文件",
|
||||
"diagnostics.config-issue-detail": "详情",
|
||||
"diagnostics.config-issue-message": "消息",
|
||||
"diagnostics.config-issue-severity": "严重程度",
|
||||
"diagnostics.config-issue-source": "来源",
|
||||
"diagnostics.config-issues": "配置问题",
|
||||
"diagnostics.config-issues-description": "当前 OliveTin 配置中检测到的问题。请在配置文件中修复这些问题并重新加载 OliveTin。",
|
||||
"diagnostics.config-issues-none": "未检测到配置问题。",
|
||||
"diagnostics.copied": "已复制!",
|
||||
"diagnostics.copy-to-clipboard": "复制到剪贴板",
|
||||
"diagnostics.found-config": "找到配置",
|
||||
|
|
@ -333,10 +412,18 @@
|
|||
"logs.clear-filter": "清除搜索筛选器",
|
||||
"logs.completed": "完成",
|
||||
"logs.exit-code": "退出代码",
|
||||
"logs.filter-error": "无法应用过滤表达式。",
|
||||
"logs.filter-help-examples": "示例:backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed",
|
||||
"logs.filter-help-fields": "字段:Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode。运算符:==, !=, contains。在单个单词前加上 ! 前缀可排除匹配的条目。",
|
||||
"logs.filter-help-intro": "过滤器会在服务器端针对您有权限查看的条目进行筛选。您可以使用 and / or 来组合搜索条件。",
|
||||
"logs.filter-help-title": "过滤语法",
|
||||
"logs.filter-placeholder": "过滤日志(例如 !Update 或 Status != Completed)",
|
||||
"logs.metadata": "元数据",
|
||||
"logs.no-logs-for-filter": "没有符合当前过滤条件的日志。",
|
||||
"logs.no-logs-to-display": "没有日志可显示。",
|
||||
"logs.page-description": "这是一个动作执行日志列表。您可以按动作标题过滤列表。",
|
||||
"logs.queue": "队列",
|
||||
"logs.queue-action-details": "动作详细信息",
|
||||
"logs.queue-default-group": "默认",
|
||||
"logs.queue-empty": "当前没有正在运行或等待中的执行。",
|
||||
"logs.queue-entity": "实体",
|
||||
|
|
@ -363,6 +450,96 @@
|
|||
"theme-dialog.default": "默认主题",
|
||||
"theme-dialog.title": "选择主题",
|
||||
"welcome": "欢迎使用 OliveTin"
|
||||
},
|
||||
"zh-Hant-TW": {
|
||||
"connected": "已連線",
|
||||
"diagnostics.browser-info": "瀏覽器資訊",
|
||||
"diagnostics.browser-info-description": "此區塊可讓您產生瀏覽器資訊的詳細報告。這在排解特定瀏覽器問題時相當有幫助。",
|
||||
"diagnostics.config-issue-action": "動作",
|
||||
"diagnostics.config-issue-argument": "參數",
|
||||
"diagnostics.config-issue-code": "代碼",
|
||||
"diagnostics.config-issue-config-file": "設定檔",
|
||||
"diagnostics.config-issue-detail": "詳細資料",
|
||||
"diagnostics.config-issue-message": "訊息",
|
||||
"diagnostics.config-issue-severity": "嚴重程度",
|
||||
"diagnostics.config-issue-source": "來源",
|
||||
"diagnostics.config-issues": "設定問題",
|
||||
"diagnostics.config-issues-description": "目前 OliveTin 設定中偵測到的問題。請在設定檔中修正這些問題並重新載入 OliveTin。",
|
||||
"diagnostics.config-issues-none": "未偵測到設定問題。",
|
||||
"diagnostics.copied": "已複製!",
|
||||
"diagnostics.copy-to-clipboard": "複製到剪貼簿",
|
||||
"diagnostics.found-config": "找到設定檔 (Config)",
|
||||
"diagnostics.found-key": "找到金鑰 (Key)",
|
||||
"diagnostics.generate-browser-info": "產生瀏覽器資訊",
|
||||
"diagnostics.generate-server-diagnostics": "產生伺服器診斷資訊",
|
||||
"diagnostics.get-support": "尋求技術支援",
|
||||
"diagnostics.get-support-description": "如果您在使用 OliveTin 時遇到問題並希望提出支援請求,提供此頁面的「伺服器診斷資訊」將會有很大的幫助。",
|
||||
"diagnostics.server-diagnostics": "伺服器診斷",
|
||||
"diagnostics.server-diagnostics-description": "此區塊可讓您產生詳細的設定與環境報告。建議在提出支援請求時附上此報告。",
|
||||
"diagnostics.server-diagnostics-docs": "伺服器診斷官方文件",
|
||||
"diagnostics.ssh": "SSH",
|
||||
"diagnostics.unknown": "未知",
|
||||
"diagnostics.useragent-data-error": "取得 userAgentData 時發生錯誤",
|
||||
"diagnostics.where-to-find-help": "哪裡可以尋求協助",
|
||||
"disconnected": "已斷線",
|
||||
"disconnected-banner-announcement": "系統事件 WebSocket 已斷線。",
|
||||
"disconnected-banner-link-text": "系統事件 WebSocket 已斷線",
|
||||
"disconnected-banner-suffix": " 自 {disconnectedSince} 起。將在 {reconnectIn} 後嘗試重新連線。",
|
||||
"disconnected-banner-suffix-reconnecting": " 自 {disconnectedSince} 起。嘗試重新連線中…",
|
||||
"docs": "官方文件",
|
||||
"language-dialog.browser-languages": "瀏覽器語言",
|
||||
"language-dialog.close": "關閉",
|
||||
"language-dialog.not-available": "無法使用",
|
||||
"language-dialog.title": "選擇語言",
|
||||
"login-button": "登入",
|
||||
"logs.action": "動作",
|
||||
"logs.action-group-limits": "並行數:{concurrent},佇列大小:{queueSize}",
|
||||
"logs.back-to-list": "返回清單",
|
||||
"logs.blocked": "已阻擋",
|
||||
"logs.calendar": "日曆",
|
||||
"logs.calendar-title": "日誌日曆",
|
||||
"logs.clear-date-filter": "清除日期過濾條件",
|
||||
"logs.clear-filter": "清除搜尋過濾條件",
|
||||
"logs.completed": "已完成",
|
||||
"logs.exit-code": "結束代碼",
|
||||
"logs.filter-error": "無法套用過濾運算式。",
|
||||
"logs.filter-help-examples": "範例:backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed",
|
||||
"logs.filter-help-fields": "欄位:Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode。運算子:==, !=, contains。在單一單字前加上 ! 前綴可排除相符的項目。",
|
||||
"logs.filter-help-intro": "過濾器會在伺服器端針對您有權限檢視的項目進行篩選。您可以使用 and / or 來組合搜尋條件。",
|
||||
"logs.filter-help-title": "過濾語法",
|
||||
"logs.filter-placeholder": "過濾日誌 (例如 !Update 或 Status != Completed)",
|
||||
"logs.metadata": "中繼資料",
|
||||
"logs.no-logs-for-filter": "沒有符合目前過濾條件的日誌。",
|
||||
"logs.no-logs-to-display": "目前沒有可顯示的日誌。",
|
||||
"logs.page-description": "這是已執行動作的日誌清單。請使用過濾條件欄位來搜尋關鍵字與運算式。",
|
||||
"logs.queue": "佇列",
|
||||
"logs.queue-action-details": "動作詳細資訊",
|
||||
"logs.queue-default-group": "預設",
|
||||
"logs.queue-empty": "目前沒有使用中或等待中的執行項目。",
|
||||
"logs.queue-entity": "實體",
|
||||
"logs.queue-group-active": "{active} 個使用中 (最大值 {max})",
|
||||
"logs.queue-group-active-unlimited": "{active} 個使用中",
|
||||
"logs.queue-page-description": "依動作群組分類的使用中與等待中執行項目。您無權檢視的項目將會被隱藏。",
|
||||
"logs.queue-position": "#{position}",
|
||||
"logs.queue-running": "執行中",
|
||||
"logs.queue-title": "執行佇列",
|
||||
"logs.queue-waiting": "等待中",
|
||||
"logs.status": "狀態",
|
||||
"logs.timed-out": "執行逾時",
|
||||
"logs.timestamp": "時間戳記",
|
||||
"logs.title": "日誌",
|
||||
"nav.actions": "動作",
|
||||
"nav.diagnostics": "診斷",
|
||||
"nav.entities": "實體",
|
||||
"nav.logs": "日誌",
|
||||
"raise-issue": "在 GitHub 上建立 Issue",
|
||||
"reconnecting": "重新連線中…",
|
||||
"return-to-index": "返回首頁",
|
||||
"search-filter": "過濾目前頁面",
|
||||
"theme-dialog.close": "關閉",
|
||||
"theme-dialog.default": "預設佈景主題",
|
||||
"theme-dialog.title": "選擇佈景主題",
|
||||
"welcome": "歡迎使用 OliveTin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ translations:
|
|||
docs: Dokumentation
|
||||
logs.title: Protokolle
|
||||
logs.page-description: Dies ist eine Liste von Protokollen von Aktionen, die ausgeführt wurden. Sie können die Liste nach Aktionstitel filtern.
|
||||
logs.filter-placeholder: Protokolle filtern (z. B. !Update oder Status != Completed)
|
||||
logs.filter-help-title: Filtersyntax
|
||||
logs.filter-help-intro: Filter laufen auf dem Server über Einträge, die Sie anzeigen dürfen. Kombinieren Sie Begriffe mit and / or.
|
||||
logs.filter-help-fields: "Felder: Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode. Operatoren: ==, !=, contains. Ein !-Präfix vor einem einzelnen Wort schließt passende Einträge aus."
|
||||
logs.filter-help-examples: "Beispiele: backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed"
|
||||
logs.filter-error: Filterausdruck konnte nicht angewendet werden.
|
||||
logs.no-logs-for-filter: Keine Protokolle entsprechen dem aktuellen Filter.
|
||||
logs.timestamp: Zeitstempel
|
||||
logs.action: Aktion
|
||||
logs.metadata: Metadaten
|
||||
|
|
@ -43,9 +50,21 @@ translations:
|
|||
logs.queue-running: Läuft
|
||||
logs.queue-position: "#{position}"
|
||||
logs.queue-entity: Entität
|
||||
logs.queue-action-details: Aktionsdetails
|
||||
diagnostics.get-support: Unterstützung erhalten
|
||||
diagnostics.get-support-description: Wenn Sie Probleme mit OliveTin haben und eine Support-Anfrage stellen möchten, wäre es sehr hilfreich, Server-Diagnostik von dieser Seite einzufügen.
|
||||
diagnostics.where-to-find-help: Wo Sie Hilfe finden
|
||||
diagnostics.config-issues: Konfigurationsprobleme
|
||||
diagnostics.config-issues-description: In der aktuellen OliveTin-Konfiguration erkannte Probleme. Beheben Sie diese in Ihren Konfigurationsdateien und laden Sie OliveTin neu.
|
||||
diagnostics.config-issues-none: Keine Konfigurationsprobleme erkannt.
|
||||
diagnostics.config-issue-severity: Schweregrad
|
||||
diagnostics.config-issue-code: Code
|
||||
diagnostics.config-issue-message: Meldung
|
||||
diagnostics.config-issue-action: Aktion
|
||||
diagnostics.config-issue-argument: Argument
|
||||
diagnostics.config-issue-config-file: Konfigurationsdatei
|
||||
diagnostics.config-issue-detail: Detail
|
||||
diagnostics.config-issue-source: Quelle
|
||||
diagnostics.ssh: SSH
|
||||
diagnostics.found-key: Schlüssel gefunden
|
||||
diagnostics.found-config: Konfiguration gefunden
|
||||
|
|
|
|||
11
lang/en.yaml
11
lang/en.yaml
|
|
@ -54,6 +54,17 @@ translations:
|
|||
diagnostics.get-support: Get support
|
||||
diagnostics.get-support-description: If you are having problems with OliveTin and want to raise a support request, it would be very helpful to include Server Diagnostics from this page.
|
||||
diagnostics.where-to-find-help: Where to find help
|
||||
diagnostics.config-issues: Configuration issues
|
||||
diagnostics.config-issues-description: Problems detected in the current OliveTin configuration. Fix these in your config files and reload OliveTin.
|
||||
diagnostics.config-issues-none: No configuration issues detected.
|
||||
diagnostics.config-issue-severity: Severity
|
||||
diagnostics.config-issue-code: Code
|
||||
diagnostics.config-issue-message: Message
|
||||
diagnostics.config-issue-action: Action
|
||||
diagnostics.config-issue-argument: Argument
|
||||
diagnostics.config-issue-config-file: Config file
|
||||
diagnostics.config-issue-detail: Detail
|
||||
diagnostics.config-issue-source: Source
|
||||
diagnostics.ssh: SSH
|
||||
diagnostics.found-key: Found Key
|
||||
diagnostics.found-config: Found Config
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ translations:
|
|||
docs: Documentación
|
||||
logs.title: Registros
|
||||
logs.page-description: Esta es una lista de registros de acciones que han sido ejecutadas. Puede filtrar la lista por título de acción.
|
||||
logs.filter-placeholder: Filtrar registros (p. ej. !Update o Status != Completed)
|
||||
logs.filter-help-title: Sintaxis del filtro
|
||||
logs.filter-help-intro: Los filtros se ejecutan en el servidor sobre las entradas que puede ver. Combine términos con and / or.
|
||||
logs.filter-help-fields: "Campos: Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode. Operadores: ==, !=, contains. Un prefijo ! en una sola palabra excluye las entradas coincidentes."
|
||||
logs.filter-help-examples: "Ejemplos: backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed"
|
||||
logs.filter-error: No se pudo aplicar la expresión del filtro.
|
||||
logs.no-logs-for-filter: Ningún registro coincide con el filtro actual.
|
||||
logs.timestamp: Marca de tiempo
|
||||
logs.action: Acción
|
||||
logs.metadata: Metadatos
|
||||
|
|
@ -43,9 +50,21 @@ translations:
|
|||
logs.queue-running: En ejecución
|
||||
logs.queue-position: "#{position}"
|
||||
logs.queue-entity: Entidad
|
||||
logs.queue-action-details: Detalles de la acción
|
||||
diagnostics.get-support: Obtener soporte
|
||||
diagnostics.get-support-description: Si tiene problemas con OliveTin y desea presentar una solicitud de soporte, sería muy útil incluir diagnósticos del servidor desde esta página.
|
||||
diagnostics.where-to-find-help: Dónde encontrar ayuda
|
||||
diagnostics.config-issues: Problemas de configuración
|
||||
diagnostics.config-issues-description: Problemas detectados en la configuración actual de OliveTin. Corríjalos en sus archivos de configuración y vuelva a cargar OliveTin.
|
||||
diagnostics.config-issues-none: No se detectaron problemas de configuración.
|
||||
diagnostics.config-issue-severity: Severidad
|
||||
diagnostics.config-issue-code: Código
|
||||
diagnostics.config-issue-message: Mensaje
|
||||
diagnostics.config-issue-action: Acción
|
||||
diagnostics.config-issue-argument: Argumento
|
||||
diagnostics.config-issue-config-file: Archivo de configuración
|
||||
diagnostics.config-issue-detail: Detalle
|
||||
diagnostics.config-issue-source: Origen
|
||||
diagnostics.ssh: SSH
|
||||
diagnostics.found-key: Clave encontrada
|
||||
diagnostics.found-config: Configuración encontrada
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ translations:
|
|||
raise-issue: Segnala un problema su GitHub
|
||||
logs.title: Registri
|
||||
logs.page-description: Questa è una lista di registri delle azioni che sono state eseguite. Puoi filtrare la lista per titolo dell'azione.
|
||||
logs.filter-placeholder: Filtra i registri (es. !Update o Status != Completed)
|
||||
logs.filter-help-title: Sintassi del filtro
|
||||
logs.filter-help-intro: I filtri vengono eseguiti sul server sulle voci che sei autorizzato a visualizzare. Combina i termini con and / or.
|
||||
logs.filter-help-fields: "Campi: Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode. Operatori: ==, !=, contains. Un prefisso ! su una singola parola esclude le voci corrispondenti."
|
||||
logs.filter-help-examples: "Esempi: backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed"
|
||||
logs.filter-error: Impossibile applicare l'espressione di filtro.
|
||||
logs.no-logs-for-filter: Nessun registro corrisponde al filtro corrente.
|
||||
logs.timestamp: Date e ora
|
||||
logs.action: Azione
|
||||
logs.metadata: Metadati
|
||||
|
|
@ -43,9 +50,21 @@ translations:
|
|||
logs.queue-running: In esecuzione
|
||||
logs.queue-position: "#{position}"
|
||||
logs.queue-entity: Entità
|
||||
logs.queue-action-details: Dettagli azione
|
||||
diagnostics.get-support: Ottenere supporto
|
||||
diagnostics.get-support-description: Se hai problemi con OliveTin e vuoi presentare una richiesta di supporto, sarebbe molto utile includere la diagnostica del server da questa pagina.
|
||||
diagnostics.where-to-find-help: Dove trovare aiuto
|
||||
diagnostics.config-issues: Problemi di configurazione
|
||||
diagnostics.config-issues-description: Problemi rilevati nella configurazione corrente di OliveTin. Correggili nei file di configurazione e ricarica OliveTin.
|
||||
diagnostics.config-issues-none: Nessun problema di configurazione rilevato.
|
||||
diagnostics.config-issue-severity: Gravità
|
||||
diagnostics.config-issue-code: Codice
|
||||
diagnostics.config-issue-message: Messaggio
|
||||
diagnostics.config-issue-action: Azione
|
||||
diagnostics.config-issue-argument: Argomento
|
||||
diagnostics.config-issue-config-file: File di configurazione
|
||||
diagnostics.config-issue-detail: Dettaglio
|
||||
diagnostics.config-issue-source: Origine
|
||||
diagnostics.ssh: SSH
|
||||
diagnostics.found-key: Chiave trovata
|
||||
diagnostics.found-config: Configurazione trovata
|
||||
|
|
|
|||
|
|
@ -17,6 +17,13 @@ translations:
|
|||
docs: 文档
|
||||
logs.title: 日志
|
||||
logs.page-description: 这是一个动作执行日志列表。您可以按动作标题过滤列表。
|
||||
logs.filter-placeholder: 过滤日志(例如 !Update 或 Status != Completed)
|
||||
logs.filter-help-title: 过滤语法
|
||||
logs.filter-help-intro: 过滤器会在服务器端针对您有权限查看的条目进行筛选。您可以使用 and / or 来组合搜索条件。
|
||||
logs.filter-help-fields: "字段:Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode。运算符:==, !=, contains。在单个单词前加上 ! 前缀可排除匹配的条目。"
|
||||
logs.filter-help-examples: "示例:backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed"
|
||||
logs.filter-error: 无法应用过滤表达式。
|
||||
logs.no-logs-for-filter: 没有符合当前过滤条件的日志。
|
||||
logs.timestamp: 时间戳
|
||||
logs.action: 动作
|
||||
logs.metadata: 元数据
|
||||
|
|
@ -52,9 +59,21 @@ translations:
|
|||
logs.queue-running: 运行中
|
||||
logs.queue-position: "第 {position} 位"
|
||||
logs.queue-entity: 实体
|
||||
logs.queue-action-details: 动作详细信息
|
||||
diagnostics.get-support: 获取支持
|
||||
diagnostics.get-support-description: 如果您在使用 OliveTin 时遇到问题并希望提交支持请求,从本页面包含服务器诊断将非常有帮助。
|
||||
diagnostics.where-to-find-help: 在哪里找到帮助
|
||||
diagnostics.config-issues: 配置问题
|
||||
diagnostics.config-issues-description: 当前 OliveTin 配置中检测到的问题。请在配置文件中修复这些问题并重新加载 OliveTin。
|
||||
diagnostics.config-issues-none: 未检测到配置问题。
|
||||
diagnostics.config-issue-severity: 严重程度
|
||||
diagnostics.config-issue-code: 代码
|
||||
diagnostics.config-issue-message: 消息
|
||||
diagnostics.config-issue-action: 操作
|
||||
diagnostics.config-issue-argument: 参数
|
||||
diagnostics.config-issue-config-file: 配置文件
|
||||
diagnostics.config-issue-detail: 详情
|
||||
diagnostics.config-issue-source: 来源
|
||||
diagnostics.ssh: SSH
|
||||
diagnostics.found-key: 找到密钥
|
||||
diagnostics.found-config: 找到配置
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
schemaVersion: 1
|
||||
translations:
|
||||
welcome: 歡迎使用 OliveTin
|
||||
docs: 官方文件
|
||||
raise-issue: 在 GitHub 上建立 Issue
|
||||
nav.actions: 動作
|
||||
nav.logs: 日誌
|
||||
nav.entities: 實體
|
||||
nav.diagnostics: 診斷
|
||||
connected: 已連線
|
||||
disconnected: 已斷線
|
||||
reconnecting: 重新連線中…
|
||||
disconnected-banner-announcement: 系統事件 WebSocket 已斷線。
|
||||
disconnected-banner-link-text: "系統事件 WebSocket 已斷線"
|
||||
disconnected-banner-suffix: " 自 {disconnectedSince} 起。將在 {reconnectIn} 後嘗試重新連線。"
|
||||
disconnected-banner-suffix-reconnecting: " 自 {disconnectedSince} 起。嘗試重新連線中…"
|
||||
login-button: 登入
|
||||
logs.title: 日誌
|
||||
logs.page-description: 這是已執行動作的日誌清單。請使用過濾條件欄位來搜尋關鍵字與運算式。
|
||||
logs.filter-placeholder: 過濾日誌 (例如 !Update 或 Status != Completed)
|
||||
logs.filter-help-title: 過濾語法
|
||||
logs.filter-help-intro: 過濾器會在伺服器端針對您有權限檢視的項目進行篩選。您可以使用 and / or 來組合搜尋條件。
|
||||
logs.filter-help-fields: "欄位:Status, Action, User, Output, Blocked, TimedOut, Running, ExitCode。運算子:==, !=, contains。在單一單字前加上 ! 前綴可排除相符的項目。"
|
||||
logs.filter-help-examples: "範例:backup · !Update · Status != Completed · Status == Blocked · Action contains backup and Status == Completed"
|
||||
logs.filter-error: 無法套用過濾運算式。
|
||||
logs.no-logs-for-filter: 沒有符合目前過濾條件的日誌。
|
||||
logs.timestamp: 時間戳記
|
||||
logs.action: 動作
|
||||
logs.metadata: 中繼資料
|
||||
logs.status: 狀態
|
||||
logs.no-logs-to-display: 目前沒有可顯示的日誌。
|
||||
logs.timed-out: 執行逾時
|
||||
logs.blocked: 已阻擋
|
||||
logs.exit-code: 結束代碼
|
||||
logs.completed: 已完成
|
||||
logs.clear-filter: 清除搜尋過濾條件
|
||||
logs.clear-date-filter: 清除日期過濾條件
|
||||
logs.calendar: 日曆
|
||||
logs.calendar-title: 日誌日曆
|
||||
logs.back-to-list: 返回清單
|
||||
logs.queue: 佇列
|
||||
logs.queue-title: 執行佇列
|
||||
logs.queue-page-description: 依動作群組分類的使用中與等待中執行項目。您無權檢視的項目將會被隱藏。
|
||||
logs.queue-empty: 目前沒有使用中或等待中的執行項目。
|
||||
logs.queue-default-group: 預設
|
||||
logs.action-group-limits: "並行數:{concurrent},佇列大小:{queueSize}"
|
||||
logs.queue-group-active: "{active} 個使用中 (最大值 {max})"
|
||||
logs.queue-group-active-unlimited: "{active} 個使用中"
|
||||
logs.queue-waiting: 等待中
|
||||
logs.queue-running: 執行中
|
||||
logs.queue-position: "#{position}"
|
||||
logs.queue-entity: 實體
|
||||
logs.queue-action-details: 動作詳細資訊
|
||||
diagnostics.get-support: 尋求技術支援
|
||||
diagnostics.get-support-description: 如果您在使用 OliveTin 時遇到問題並希望提出支援請求,提供此頁面的「伺服器診斷資訊」將會有很大的幫助。
|
||||
diagnostics.where-to-find-help: 哪裡可以尋求協助
|
||||
diagnostics.config-issues: 設定問題
|
||||
diagnostics.config-issues-description: 目前 OliveTin 設定中偵測到的問題。請在設定檔中修正這些問題並重新載入 OliveTin。
|
||||
diagnostics.config-issues-none: 未偵測到設定問題。
|
||||
diagnostics.config-issue-severity: 嚴重程度
|
||||
diagnostics.config-issue-code: 代碼
|
||||
diagnostics.config-issue-message: 訊息
|
||||
diagnostics.config-issue-action: 動作
|
||||
diagnostics.config-issue-argument: 參數
|
||||
diagnostics.config-issue-config-file: 設定檔
|
||||
diagnostics.config-issue-detail: 詳細資料
|
||||
diagnostics.config-issue-source: 來源
|
||||
diagnostics.ssh: SSH
|
||||
diagnostics.found-key: 找到金鑰 (Key)
|
||||
diagnostics.found-config: 找到設定檔 (Config)
|
||||
diagnostics.server-diagnostics: 伺服器診斷
|
||||
diagnostics.server-diagnostics-description: 此區塊可讓您產生詳細的設定與環境報告。建議在提出支援請求時附上此報告。
|
||||
diagnostics.server-diagnostics-docs: 伺服器診斷官方文件
|
||||
diagnostics.generate-server-diagnostics: 產生伺服器診斷資訊
|
||||
diagnostics.browser-info: 瀏覽器資訊
|
||||
diagnostics.browser-info-description: 此區塊可讓您產生瀏覽器資訊的詳細報告。這在排解特定瀏覽器問題時相當有幫助。
|
||||
diagnostics.generate-browser-info: 產生瀏覽器資訊
|
||||
diagnostics.copy-to-clipboard: 複製到剪貼簿
|
||||
diagnostics.copied: 已複製!
|
||||
diagnostics.unknown: 未知
|
||||
diagnostics.useragent-data-error: 取得 userAgentData 時發生錯誤
|
||||
return-to-index: 返回首頁
|
||||
search-filter: 過濾目前頁面
|
||||
language-dialog.title: 選擇語言
|
||||
language-dialog.browser-languages: 瀏覽器語言
|
||||
language-dialog.not-available: 無法使用
|
||||
language-dialog.close: 關閉
|
||||
theme-dialog.title: 選擇佈景主題
|
||||
theme-dialog.default: 預設佈景主題
|
||||
theme-dialog.close: 關閉
|
||||
|
|
@ -379,9 +379,21 @@ message LogoutResponse {
|
|||
message GetDiagnosticsRequest {
|
||||
}
|
||||
|
||||
message ConfigIssue {
|
||||
string severity = 1;
|
||||
string code = 2;
|
||||
string message = 3;
|
||||
string action_id = 4;
|
||||
string action_title = 5;
|
||||
string argument_name = 6;
|
||||
string source = 7;
|
||||
string config_file = 8;
|
||||
}
|
||||
|
||||
message GetDiagnosticsResponse {
|
||||
string SshFoundKey = 1;
|
||||
string SshFoundConfig = 2;
|
||||
repeated ConfigIssue config_issues = 3;
|
||||
}
|
||||
|
||||
message InitRequest {}
|
||||
|
|
@ -412,6 +424,7 @@ message InitResponse {
|
|||
bool login_required = 23;
|
||||
repeated string available_themes = 24; // List of available theme names
|
||||
bool show_navigate_on_start_icons = 25;
|
||||
int32 config_issue_count = 26;
|
||||
}
|
||||
|
||||
message AdditionalLink {
|
||||
|
|
|
|||
|
|
@ -3871,17 +3871,118 @@ func (*GetDiagnosticsRequest) Descriptor() ([]byte, []int) {
|
|||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{64}
|
||||
}
|
||||
|
||||
type ConfigIssue struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Severity string `protobuf:"bytes,1,opt,name=severity,proto3" json:"severity,omitempty"`
|
||||
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
|
||||
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
|
||||
ActionId string `protobuf:"bytes,4,opt,name=action_id,json=actionId,proto3" json:"action_id,omitempty"`
|
||||
ActionTitle string `protobuf:"bytes,5,opt,name=action_title,json=actionTitle,proto3" json:"action_title,omitempty"`
|
||||
ArgumentName string `protobuf:"bytes,6,opt,name=argument_name,json=argumentName,proto3" json:"argument_name,omitempty"`
|
||||
Source string `protobuf:"bytes,7,opt,name=source,proto3" json:"source,omitempty"`
|
||||
ConfigFile string `protobuf:"bytes,8,opt,name=config_file,json=configFile,proto3" json:"config_file,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) Reset() {
|
||||
*x = ConfigIssue{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[65]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConfigIssue) ProtoMessage() {}
|
||||
|
||||
func (x *ConfigIssue) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[65]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConfigIssue.ProtoReflect.Descriptor instead.
|
||||
func (*ConfigIssue) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{65}
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetSeverity() string {
|
||||
if x != nil {
|
||||
return x.Severity
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetCode() string {
|
||||
if x != nil {
|
||||
return x.Code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetActionId() string {
|
||||
if x != nil {
|
||||
return x.ActionId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetActionTitle() string {
|
||||
if x != nil {
|
||||
return x.ActionTitle
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetArgumentName() string {
|
||||
if x != nil {
|
||||
return x.ArgumentName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetSource() string {
|
||||
if x != nil {
|
||||
return x.Source
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConfigIssue) GetConfigFile() string {
|
||||
if x != nil {
|
||||
return x.ConfigFile
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetDiagnosticsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
SshFoundKey string `protobuf:"bytes,1,opt,name=SshFoundKey,proto3" json:"SshFoundKey,omitempty"`
|
||||
SshFoundConfig string `protobuf:"bytes,2,opt,name=SshFoundConfig,proto3" json:"SshFoundConfig,omitempty"`
|
||||
ConfigIssues []*ConfigIssue `protobuf:"bytes,3,rep,name=config_issues,json=configIssues,proto3" json:"config_issues,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetDiagnosticsResponse) Reset() {
|
||||
*x = GetDiagnosticsResponse{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[65]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[66]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -3893,7 +3994,7 @@ func (x *GetDiagnosticsResponse) String() string {
|
|||
func (*GetDiagnosticsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetDiagnosticsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[65]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[66]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -3906,7 +4007,7 @@ func (x *GetDiagnosticsResponse) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use GetDiagnosticsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetDiagnosticsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{65}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{66}
|
||||
}
|
||||
|
||||
func (x *GetDiagnosticsResponse) GetSshFoundKey() string {
|
||||
|
|
@ -3923,6 +4024,13 @@ func (x *GetDiagnosticsResponse) GetSshFoundConfig() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (x *GetDiagnosticsResponse) GetConfigIssues() []*ConfigIssue {
|
||||
if x != nil {
|
||||
return x.ConfigIssues
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type InitRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
|
@ -3931,7 +4039,7 @@ type InitRequest struct {
|
|||
|
||||
func (x *InitRequest) Reset() {
|
||||
*x = InitRequest{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[66]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[67]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -3943,7 +4051,7 @@ func (x *InitRequest) String() string {
|
|||
func (*InitRequest) ProtoMessage() {}
|
||||
|
||||
func (x *InitRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[66]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[67]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -3956,7 +4064,7 @@ func (x *InitRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use InitRequest.ProtoReflect.Descriptor instead.
|
||||
func (*InitRequest) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{66}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{67}
|
||||
}
|
||||
|
||||
type InitResponse struct {
|
||||
|
|
@ -3986,13 +4094,14 @@ type InitResponse struct {
|
|||
LoginRequired bool `protobuf:"varint,23,opt,name=login_required,json=loginRequired,proto3" json:"login_required,omitempty"`
|
||||
AvailableThemes []string `protobuf:"bytes,24,rep,name=available_themes,json=availableThemes,proto3" json:"available_themes,omitempty"` // List of available theme names
|
||||
ShowNavigateOnStartIcons bool `protobuf:"varint,25,opt,name=show_navigate_on_start_icons,json=showNavigateOnStartIcons,proto3" json:"show_navigate_on_start_icons,omitempty"`
|
||||
ConfigIssueCount int32 `protobuf:"varint,26,opt,name=config_issue_count,json=configIssueCount,proto3" json:"config_issue_count,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *InitResponse) Reset() {
|
||||
*x = InitResponse{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[67]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[68]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4004,7 +4113,7 @@ func (x *InitResponse) String() string {
|
|||
func (*InitResponse) ProtoMessage() {}
|
||||
|
||||
func (x *InitResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[67]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[68]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4017,7 +4126,7 @@ func (x *InitResponse) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use InitResponse.ProtoReflect.Descriptor instead.
|
||||
func (*InitResponse) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{67}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{68}
|
||||
}
|
||||
|
||||
func (x *InitResponse) GetShowFooter() bool {
|
||||
|
|
@ -4195,6 +4304,13 @@ func (x *InitResponse) GetShowNavigateOnStartIcons() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (x *InitResponse) GetConfigIssueCount() int32 {
|
||||
if x != nil {
|
||||
return x.ConfigIssueCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type AdditionalLink struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
|
||||
|
|
@ -4205,7 +4321,7 @@ type AdditionalLink struct {
|
|||
|
||||
func (x *AdditionalLink) Reset() {
|
||||
*x = AdditionalLink{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[68]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[69]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4217,7 +4333,7 @@ func (x *AdditionalLink) String() string {
|
|||
func (*AdditionalLink) ProtoMessage() {}
|
||||
|
||||
func (x *AdditionalLink) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[68]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[69]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4230,7 +4346,7 @@ func (x *AdditionalLink) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use AdditionalLink.ProtoReflect.Descriptor instead.
|
||||
func (*AdditionalLink) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{68}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{69}
|
||||
}
|
||||
|
||||
func (x *AdditionalLink) GetTitle() string {
|
||||
|
|
@ -4258,7 +4374,7 @@ type OAuth2Provider struct {
|
|||
|
||||
func (x *OAuth2Provider) Reset() {
|
||||
*x = OAuth2Provider{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[69]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[70]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4270,7 +4386,7 @@ func (x *OAuth2Provider) String() string {
|
|||
func (*OAuth2Provider) ProtoMessage() {}
|
||||
|
||||
func (x *OAuth2Provider) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[69]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[70]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4283,7 +4399,7 @@ func (x *OAuth2Provider) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use OAuth2Provider.ProtoReflect.Descriptor instead.
|
||||
func (*OAuth2Provider) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{69}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{70}
|
||||
}
|
||||
|
||||
func (x *OAuth2Provider) GetTitle() string {
|
||||
|
|
@ -4316,7 +4432,7 @@ type GetActionBindingRequest struct {
|
|||
|
||||
func (x *GetActionBindingRequest) Reset() {
|
||||
*x = GetActionBindingRequest{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[70]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[71]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4328,7 +4444,7 @@ func (x *GetActionBindingRequest) String() string {
|
|||
func (*GetActionBindingRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetActionBindingRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[70]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[71]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4341,7 +4457,7 @@ func (x *GetActionBindingRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use GetActionBindingRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetActionBindingRequest) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{70}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{71}
|
||||
}
|
||||
|
||||
func (x *GetActionBindingRequest) GetBindingId() string {
|
||||
|
|
@ -4361,7 +4477,7 @@ type GetActionBindingResponse struct {
|
|||
|
||||
func (x *GetActionBindingResponse) Reset() {
|
||||
*x = GetActionBindingResponse{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[71]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[72]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4373,7 +4489,7 @@ func (x *GetActionBindingResponse) String() string {
|
|||
func (*GetActionBindingResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetActionBindingResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[71]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[72]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4386,7 +4502,7 @@ func (x *GetActionBindingResponse) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use GetActionBindingResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetActionBindingResponse) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{71}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{72}
|
||||
}
|
||||
|
||||
func (x *GetActionBindingResponse) GetAction() *Action {
|
||||
|
|
@ -4415,7 +4531,7 @@ type GetEntitiesRequest struct {
|
|||
|
||||
func (x *GetEntitiesRequest) Reset() {
|
||||
*x = GetEntitiesRequest{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[72]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[73]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4427,7 +4543,7 @@ func (x *GetEntitiesRequest) String() string {
|
|||
func (*GetEntitiesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetEntitiesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[72]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[73]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4440,7 +4556,7 @@ func (x *GetEntitiesRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use GetEntitiesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetEntitiesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{72}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{73}
|
||||
}
|
||||
|
||||
func (x *GetEntitiesRequest) GetEntityType() string {
|
||||
|
|
@ -4480,7 +4596,7 @@ type GetEntitiesResponse struct {
|
|||
|
||||
func (x *GetEntitiesResponse) Reset() {
|
||||
*x = GetEntitiesResponse{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[73]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[74]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4492,7 +4608,7 @@ func (x *GetEntitiesResponse) String() string {
|
|||
func (*GetEntitiesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetEntitiesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[73]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[74]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4505,7 +4621,7 @@ func (x *GetEntitiesResponse) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use GetEntitiesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetEntitiesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{73}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{74}
|
||||
}
|
||||
|
||||
func (x *GetEntitiesResponse) GetEntityDefinitions() []*EntityDefinition {
|
||||
|
|
@ -4529,7 +4645,7 @@ type EntityDefinition struct {
|
|||
|
||||
func (x *EntityDefinition) Reset() {
|
||||
*x = EntityDefinition{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[74]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[75]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4541,7 +4657,7 @@ func (x *EntityDefinition) String() string {
|
|||
func (*EntityDefinition) ProtoMessage() {}
|
||||
|
||||
func (x *EntityDefinition) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[74]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[75]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4554,7 +4670,7 @@ func (x *EntityDefinition) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use EntityDefinition.ProtoReflect.Descriptor instead.
|
||||
func (*EntityDefinition) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{74}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{75}
|
||||
}
|
||||
|
||||
func (x *EntityDefinition) GetTitle() string {
|
||||
|
|
@ -4609,7 +4725,7 @@ type EntityProperty struct {
|
|||
|
||||
func (x *EntityProperty) Reset() {
|
||||
*x = EntityProperty{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[75]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[76]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4621,7 +4737,7 @@ func (x *EntityProperty) String() string {
|
|||
func (*EntityProperty) ProtoMessage() {}
|
||||
|
||||
func (x *EntityProperty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[75]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[76]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4634,7 +4750,7 @@ func (x *EntityProperty) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use EntityProperty.ProtoReflect.Descriptor instead.
|
||||
func (*EntityProperty) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{75}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{76}
|
||||
}
|
||||
|
||||
func (x *EntityProperty) GetName() string {
|
||||
|
|
@ -4661,7 +4777,7 @@ type GetEntityRequest struct {
|
|||
|
||||
func (x *GetEntityRequest) Reset() {
|
||||
*x = GetEntityRequest{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[76]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[77]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4673,7 +4789,7 @@ func (x *GetEntityRequest) String() string {
|
|||
func (*GetEntityRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetEntityRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[76]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[77]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4686,7 +4802,7 @@ func (x *GetEntityRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use GetEntityRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetEntityRequest) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{76}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{77}
|
||||
}
|
||||
|
||||
func (x *GetEntityRequest) GetUniqueKey() string {
|
||||
|
|
@ -4712,7 +4828,7 @@ type RestartActionRequest struct {
|
|||
|
||||
func (x *RestartActionRequest) Reset() {
|
||||
*x = RestartActionRequest{}
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[77]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[78]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
|
@ -4724,7 +4840,7 @@ func (x *RestartActionRequest) String() string {
|
|||
func (*RestartActionRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RestartActionRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[77]
|
||||
mi := &file_olivetin_api_v1_olivetin_proto_msgTypes[78]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
|
|
@ -4737,7 +4853,7 @@ func (x *RestartActionRequest) ProtoReflect() protoreflect.Message {
|
|||
|
||||
// Deprecated: Use RestartActionRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RestartActionRequest) Descriptor() ([]byte, []int) {
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{77}
|
||||
return file_olivetin_api_v1_olivetin_proto_rawDescGZIP(), []int{78}
|
||||
}
|
||||
|
||||
func (x *RestartActionRequest) GetExecutionTrackingId() string {
|
||||
|
|
@ -5044,11 +5160,22 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" +
|
|||
"\x04hash\x18\x01 \x01(\tR\x04hash\"\x0f\n" +
|
||||
"\rLogoutRequest\"\x10\n" +
|
||||
"\x0eLogoutResponse\"\x17\n" +
|
||||
"\x15GetDiagnosticsRequest\"b\n" +
|
||||
"\x15GetDiagnosticsRequest\"\xf5\x01\n" +
|
||||
"\vConfigIssue\x12\x1a\n" +
|
||||
"\bseverity\x18\x01 \x01(\tR\bseverity\x12\x12\n" +
|
||||
"\x04code\x18\x02 \x01(\tR\x04code\x12\x18\n" +
|
||||
"\amessage\x18\x03 \x01(\tR\amessage\x12\x1b\n" +
|
||||
"\taction_id\x18\x04 \x01(\tR\bactionId\x12!\n" +
|
||||
"\faction_title\x18\x05 \x01(\tR\vactionTitle\x12#\n" +
|
||||
"\rargument_name\x18\x06 \x01(\tR\fargumentName\x12\x16\n" +
|
||||
"\x06source\x18\a \x01(\tR\x06source\x12\x1f\n" +
|
||||
"\vconfig_file\x18\b \x01(\tR\n" +
|
||||
"configFile\"\xa5\x01\n" +
|
||||
"\x16GetDiagnosticsResponse\x12 \n" +
|
||||
"\vSshFoundKey\x18\x01 \x01(\tR\vSshFoundKey\x12&\n" +
|
||||
"\x0eSshFoundConfig\x18\x02 \x01(\tR\x0eSshFoundConfig\"\r\n" +
|
||||
"\vInitRequest\"\x8d\t\n" +
|
||||
"\x0eSshFoundConfig\x18\x02 \x01(\tR\x0eSshFoundConfig\x12A\n" +
|
||||
"\rconfig_issues\x18\x03 \x03(\v2\x1c.olivetin.api.v1.ConfigIssueR\fconfigIssues\"\r\n" +
|
||||
"\vInitRequest\"\xbb\t\n" +
|
||||
"\fInitResponse\x12\x1e\n" +
|
||||
"\n" +
|
||||
"showFooter\x18\x01 \x01(\bR\n" +
|
||||
|
|
@ -5078,7 +5205,8 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" +
|
|||
"\rshow_log_list\x18\x16 \x01(\bR\vshowLogList\x12%\n" +
|
||||
"\x0elogin_required\x18\x17 \x01(\bR\rloginRequired\x12)\n" +
|
||||
"\x10available_themes\x18\x18 \x03(\tR\x0favailableThemes\x12>\n" +
|
||||
"\x1cshow_navigate_on_start_icons\x18\x19 \x01(\bR\x18showNavigateOnStartIcons\"8\n" +
|
||||
"\x1cshow_navigate_on_start_icons\x18\x19 \x01(\bR\x18showNavigateOnStartIcons\x12,\n" +
|
||||
"\x12config_issue_count\x18\x1a \x01(\x05R\x10configIssueCount\"8\n" +
|
||||
"\x0eAdditionalLink\x12\x14\n" +
|
||||
"\x05title\x18\x01 \x01(\tR\x05title\x12\x10\n" +
|
||||
"\x03url\x18\x02 \x01(\tR\x03url\"L\n" +
|
||||
|
|
@ -5159,7 +5287,7 @@ func file_olivetin_api_v1_olivetin_proto_rawDescGZIP() []byte {
|
|||
return file_olivetin_api_v1_olivetin_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_olivetin_api_v1_olivetin_proto_msgTypes = make([]protoimpl.MessageInfo, 85)
|
||||
var file_olivetin_api_v1_olivetin_proto_msgTypes = make([]protoimpl.MessageInfo, 86)
|
||||
var file_olivetin_api_v1_olivetin_proto_goTypes = []any{
|
||||
(*Action)(nil), // 0: olivetin.api.v1.Action
|
||||
(*ActionGroupMembership)(nil), // 1: olivetin.api.v1.ActionGroupMembership
|
||||
|
|
@ -5226,38 +5354,39 @@ var file_olivetin_api_v1_olivetin_proto_goTypes = []any{
|
|||
(*LogoutRequest)(nil), // 62: olivetin.api.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 63: olivetin.api.v1.LogoutResponse
|
||||
(*GetDiagnosticsRequest)(nil), // 64: olivetin.api.v1.GetDiagnosticsRequest
|
||||
(*GetDiagnosticsResponse)(nil), // 65: olivetin.api.v1.GetDiagnosticsResponse
|
||||
(*InitRequest)(nil), // 66: olivetin.api.v1.InitRequest
|
||||
(*InitResponse)(nil), // 67: olivetin.api.v1.InitResponse
|
||||
(*AdditionalLink)(nil), // 68: olivetin.api.v1.AdditionalLink
|
||||
(*OAuth2Provider)(nil), // 69: olivetin.api.v1.OAuth2Provider
|
||||
(*GetActionBindingRequest)(nil), // 70: olivetin.api.v1.GetActionBindingRequest
|
||||
(*GetActionBindingResponse)(nil), // 71: olivetin.api.v1.GetActionBindingResponse
|
||||
(*GetEntitiesRequest)(nil), // 72: olivetin.api.v1.GetEntitiesRequest
|
||||
(*GetEntitiesResponse)(nil), // 73: olivetin.api.v1.GetEntitiesResponse
|
||||
(*EntityDefinition)(nil), // 74: olivetin.api.v1.EntityDefinition
|
||||
(*EntityProperty)(nil), // 75: olivetin.api.v1.EntityProperty
|
||||
(*GetEntityRequest)(nil), // 76: olivetin.api.v1.GetEntityRequest
|
||||
(*RestartActionRequest)(nil), // 77: olivetin.api.v1.RestartActionRequest
|
||||
nil, // 78: olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntry
|
||||
nil, // 79: olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntry
|
||||
nil, // 80: olivetin.api.v1.ActionArgument.SuggestionsEntry
|
||||
nil, // 81: olivetin.api.v1.EntityRelatedAction.PrefilledArgumentsEntry
|
||||
nil, // 82: olivetin.api.v1.Entity.FieldsEntry
|
||||
nil, // 83: olivetin.api.v1.DumpVarsResponse.ContentsEntry
|
||||
nil, // 84: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
|
||||
(*ConfigIssue)(nil), // 65: olivetin.api.v1.ConfigIssue
|
||||
(*GetDiagnosticsResponse)(nil), // 66: olivetin.api.v1.GetDiagnosticsResponse
|
||||
(*InitRequest)(nil), // 67: olivetin.api.v1.InitRequest
|
||||
(*InitResponse)(nil), // 68: olivetin.api.v1.InitResponse
|
||||
(*AdditionalLink)(nil), // 69: olivetin.api.v1.AdditionalLink
|
||||
(*OAuth2Provider)(nil), // 70: olivetin.api.v1.OAuth2Provider
|
||||
(*GetActionBindingRequest)(nil), // 71: olivetin.api.v1.GetActionBindingRequest
|
||||
(*GetActionBindingResponse)(nil), // 72: olivetin.api.v1.GetActionBindingResponse
|
||||
(*GetEntitiesRequest)(nil), // 73: olivetin.api.v1.GetEntitiesRequest
|
||||
(*GetEntitiesResponse)(nil), // 74: olivetin.api.v1.GetEntitiesResponse
|
||||
(*EntityDefinition)(nil), // 75: olivetin.api.v1.EntityDefinition
|
||||
(*EntityProperty)(nil), // 76: olivetin.api.v1.EntityProperty
|
||||
(*GetEntityRequest)(nil), // 77: olivetin.api.v1.GetEntityRequest
|
||||
(*RestartActionRequest)(nil), // 78: olivetin.api.v1.RestartActionRequest
|
||||
nil, // 79: olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntry
|
||||
nil, // 80: olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntry
|
||||
nil, // 81: olivetin.api.v1.ActionArgument.SuggestionsEntry
|
||||
nil, // 82: olivetin.api.v1.EntityRelatedAction.PrefilledArgumentsEntry
|
||||
nil, // 83: olivetin.api.v1.Entity.FieldsEntry
|
||||
nil, // 84: olivetin.api.v1.DumpVarsResponse.ContentsEntry
|
||||
nil, // 85: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
|
||||
}
|
||||
var file_olivetin_api_v1_olivetin_proto_depIdxs = []int32{
|
||||
3, // 0: olivetin.api.v1.Action.arguments:type_name -> olivetin.api.v1.ActionArgument
|
||||
2, // 1: olivetin.api.v1.Action.exec_on_webhooks:type_name -> olivetin.api.v1.ActionWebhookExecHint
|
||||
1, // 2: olivetin.api.v1.Action.groups:type_name -> olivetin.api.v1.ActionGroupMembership
|
||||
78, // 3: olivetin.api.v1.ActionWebhookExecHint.match_headers:type_name -> olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntry
|
||||
79, // 4: olivetin.api.v1.ActionWebhookExecHint.match_query:type_name -> olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntry
|
||||
79, // 3: olivetin.api.v1.ActionWebhookExecHint.match_headers:type_name -> olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntry
|
||||
80, // 4: olivetin.api.v1.ActionWebhookExecHint.match_query:type_name -> olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntry
|
||||
4, // 5: olivetin.api.v1.ActionArgument.choices:type_name -> olivetin.api.v1.ActionArgumentChoice
|
||||
80, // 6: olivetin.api.v1.ActionArgument.suggestions:type_name -> olivetin.api.v1.ActionArgument.SuggestionsEntry
|
||||
81, // 6: olivetin.api.v1.ActionArgument.suggestions:type_name -> olivetin.api.v1.ActionArgument.SuggestionsEntry
|
||||
0, // 7: olivetin.api.v1.EntityRelatedAction.action:type_name -> olivetin.api.v1.Action
|
||||
81, // 8: olivetin.api.v1.EntityRelatedAction.prefilled_arguments:type_name -> olivetin.api.v1.EntityRelatedAction.PrefilledArgumentsEntry
|
||||
82, // 9: olivetin.api.v1.Entity.fields:type_name -> olivetin.api.v1.Entity.FieldsEntry
|
||||
82, // 8: olivetin.api.v1.EntityRelatedAction.prefilled_arguments:type_name -> olivetin.api.v1.EntityRelatedAction.PrefilledArgumentsEntry
|
||||
83, // 9: olivetin.api.v1.Entity.fields:type_name -> olivetin.api.v1.Entity.FieldsEntry
|
||||
5, // 10: olivetin.api.v1.Entity.related_actions:type_name -> olivetin.api.v1.EntityRelatedAction
|
||||
10, // 11: olivetin.api.v1.GetDashboardResponse.dashboard:type_name -> olivetin.api.v1.Dashboard
|
||||
11, // 12: olivetin.api.v1.Dashboard.contents:type_name -> olivetin.api.v1.DashboardComponent
|
||||
|
|
@ -5275,8 +5404,8 @@ var file_olivetin_api_v1_olivetin_proto_depIdxs = []int32{
|
|||
28, // 24: olivetin.api.v1.GetExecutionQueueResponse.groups:type_name -> olivetin.api.v1.ExecutionQueueGroup
|
||||
22, // 25: olivetin.api.v1.ExecutionStatusResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
|
||||
35, // 26: olivetin.api.v1.ExecutionStatusResponse.back_to_dashboards:type_name -> olivetin.api.v1.DashboardNavigationTarget
|
||||
83, // 27: olivetin.api.v1.DumpVarsResponse.contents:type_name -> olivetin.api.v1.DumpVarsResponse.ContentsEntry
|
||||
84, // 28: olivetin.api.v1.DumpPublicIdActionMapResponse.contents:type_name -> olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
|
||||
84, // 27: olivetin.api.v1.DumpVarsResponse.contents:type_name -> olivetin.api.v1.DumpVarsResponse.ContentsEntry
|
||||
85, // 28: olivetin.api.v1.DumpPublicIdActionMapResponse.contents:type_name -> olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
|
||||
51, // 29: olivetin.api.v1.EventStreamResponse.entity_changed:type_name -> olivetin.api.v1.EventEntityChanged
|
||||
52, // 30: olivetin.api.v1.EventStreamResponse.config_changed:type_name -> olivetin.api.v1.EventConfigChanged
|
||||
54, // 31: olivetin.api.v1.EventStreamResponse.execution_finished:type_name -> olivetin.api.v1.EventExecutionFinished
|
||||
|
|
@ -5285,72 +5414,73 @@ var file_olivetin_api_v1_olivetin_proto_depIdxs = []int32{
|
|||
53, // 34: olivetin.api.v1.EventStreamResponse.heartbeat:type_name -> olivetin.api.v1.EventHeartbeat
|
||||
22, // 35: olivetin.api.v1.EventExecutionFinished.log_entry:type_name -> olivetin.api.v1.LogEntry
|
||||
22, // 36: olivetin.api.v1.EventExecutionStarted.log_entry:type_name -> olivetin.api.v1.LogEntry
|
||||
69, // 37: olivetin.api.v1.InitResponse.oAuth2Providers:type_name -> olivetin.api.v1.OAuth2Provider
|
||||
68, // 38: olivetin.api.v1.InitResponse.additionalLinks:type_name -> olivetin.api.v1.AdditionalLink
|
||||
8, // 39: olivetin.api.v1.InitResponse.effective_policy:type_name -> olivetin.api.v1.EffectivePolicy
|
||||
0, // 40: olivetin.api.v1.GetActionBindingResponse.action:type_name -> olivetin.api.v1.Action
|
||||
35, // 41: olivetin.api.v1.GetActionBindingResponse.back_to_dashboards:type_name -> olivetin.api.v1.DashboardNavigationTarget
|
||||
74, // 42: olivetin.api.v1.GetEntitiesResponse.entity_definitions:type_name -> olivetin.api.v1.EntityDefinition
|
||||
6, // 43: olivetin.api.v1.EntityDefinition.instances:type_name -> olivetin.api.v1.Entity
|
||||
75, // 44: olivetin.api.v1.EntityDefinition.properties:type_name -> olivetin.api.v1.EntityProperty
|
||||
43, // 45: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry.value:type_name -> olivetin.api.v1.DebugBinding
|
||||
9, // 46: olivetin.api.v1.OliveTinApiService.GetDashboard:input_type -> olivetin.api.v1.GetDashboardRequest
|
||||
12, // 47: olivetin.api.v1.OliveTinApiService.StartAction:input_type -> olivetin.api.v1.StartActionRequest
|
||||
15, // 48: olivetin.api.v1.OliveTinApiService.StartActionAndWait:input_type -> olivetin.api.v1.StartActionAndWaitRequest
|
||||
17, // 49: olivetin.api.v1.OliveTinApiService.StartActionByGet:input_type -> olivetin.api.v1.StartActionByGetRequest
|
||||
19, // 50: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:input_type -> olivetin.api.v1.StartActionByGetAndWaitRequest
|
||||
77, // 51: olivetin.api.v1.OliveTinApiService.RestartAction:input_type -> olivetin.api.v1.RestartActionRequest
|
||||
56, // 52: olivetin.api.v1.OliveTinApiService.KillAction:input_type -> olivetin.api.v1.KillActionRequest
|
||||
34, // 53: olivetin.api.v1.OliveTinApiService.ExecutionStatus:input_type -> olivetin.api.v1.ExecutionStatusRequest
|
||||
21, // 54: olivetin.api.v1.OliveTinApiService.GetLogs:input_type -> olivetin.api.v1.GetLogsRequest
|
||||
24, // 55: olivetin.api.v1.OliveTinApiService.GetActionLogs:input_type -> olivetin.api.v1.GetActionLogsRequest
|
||||
26, // 56: olivetin.api.v1.OliveTinApiService.GetExecutionQueue:input_type -> olivetin.api.v1.GetExecutionQueueRequest
|
||||
30, // 57: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:input_type -> olivetin.api.v1.ValidateArgumentTypeRequest
|
||||
37, // 58: olivetin.api.v1.OliveTinApiService.WhoAmI:input_type -> olivetin.api.v1.WhoAmIRequest
|
||||
39, // 59: olivetin.api.v1.OliveTinApiService.ServerDiagnostics:input_type -> olivetin.api.v1.ServerDiagnosticsRequest
|
||||
41, // 60: olivetin.api.v1.OliveTinApiService.DumpVars:input_type -> olivetin.api.v1.DumpVarsRequest
|
||||
44, // 61: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:input_type -> olivetin.api.v1.DumpPublicIdActionMapRequest
|
||||
46, // 62: olivetin.api.v1.OliveTinApiService.GetReadyz:input_type -> olivetin.api.v1.GetReadyzRequest
|
||||
58, // 63: olivetin.api.v1.OliveTinApiService.LocalUserLogin:input_type -> olivetin.api.v1.LocalUserLoginRequest
|
||||
60, // 64: olivetin.api.v1.OliveTinApiService.PasswordHash:input_type -> olivetin.api.v1.PasswordHashRequest
|
||||
62, // 65: olivetin.api.v1.OliveTinApiService.Logout:input_type -> olivetin.api.v1.LogoutRequest
|
||||
48, // 66: olivetin.api.v1.OliveTinApiService.EventStream:input_type -> olivetin.api.v1.EventStreamRequest
|
||||
64, // 67: olivetin.api.v1.OliveTinApiService.GetDiagnostics:input_type -> olivetin.api.v1.GetDiagnosticsRequest
|
||||
66, // 68: olivetin.api.v1.OliveTinApiService.Init:input_type -> olivetin.api.v1.InitRequest
|
||||
70, // 69: olivetin.api.v1.OliveTinApiService.GetActionBinding:input_type -> olivetin.api.v1.GetActionBindingRequest
|
||||
72, // 70: olivetin.api.v1.OliveTinApiService.GetEntities:input_type -> olivetin.api.v1.GetEntitiesRequest
|
||||
76, // 71: olivetin.api.v1.OliveTinApiService.GetEntity:input_type -> olivetin.api.v1.GetEntityRequest
|
||||
7, // 72: olivetin.api.v1.OliveTinApiService.GetDashboard:output_type -> olivetin.api.v1.GetDashboardResponse
|
||||
14, // 73: olivetin.api.v1.OliveTinApiService.StartAction:output_type -> olivetin.api.v1.StartActionResponse
|
||||
16, // 74: olivetin.api.v1.OliveTinApiService.StartActionAndWait:output_type -> olivetin.api.v1.StartActionAndWaitResponse
|
||||
18, // 75: olivetin.api.v1.OliveTinApiService.StartActionByGet:output_type -> olivetin.api.v1.StartActionByGetResponse
|
||||
20, // 76: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:output_type -> olivetin.api.v1.StartActionByGetAndWaitResponse
|
||||
14, // 77: olivetin.api.v1.OliveTinApiService.RestartAction:output_type -> olivetin.api.v1.StartActionResponse
|
||||
57, // 78: olivetin.api.v1.OliveTinApiService.KillAction:output_type -> olivetin.api.v1.KillActionResponse
|
||||
36, // 79: olivetin.api.v1.OliveTinApiService.ExecutionStatus:output_type -> olivetin.api.v1.ExecutionStatusResponse
|
||||
23, // 80: olivetin.api.v1.OliveTinApiService.GetLogs:output_type -> olivetin.api.v1.GetLogsResponse
|
||||
25, // 81: olivetin.api.v1.OliveTinApiService.GetActionLogs:output_type -> olivetin.api.v1.GetActionLogsResponse
|
||||
29, // 82: olivetin.api.v1.OliveTinApiService.GetExecutionQueue:output_type -> olivetin.api.v1.GetExecutionQueueResponse
|
||||
31, // 83: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:output_type -> olivetin.api.v1.ValidateArgumentTypeResponse
|
||||
38, // 84: olivetin.api.v1.OliveTinApiService.WhoAmI:output_type -> olivetin.api.v1.WhoAmIResponse
|
||||
40, // 85: olivetin.api.v1.OliveTinApiService.ServerDiagnostics:output_type -> olivetin.api.v1.ServerDiagnosticsResponse
|
||||
42, // 86: olivetin.api.v1.OliveTinApiService.DumpVars:output_type -> olivetin.api.v1.DumpVarsResponse
|
||||
45, // 87: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:output_type -> olivetin.api.v1.DumpPublicIdActionMapResponse
|
||||
47, // 88: olivetin.api.v1.OliveTinApiService.GetReadyz:output_type -> olivetin.api.v1.GetReadyzResponse
|
||||
59, // 89: olivetin.api.v1.OliveTinApiService.LocalUserLogin:output_type -> olivetin.api.v1.LocalUserLoginResponse
|
||||
61, // 90: olivetin.api.v1.OliveTinApiService.PasswordHash:output_type -> olivetin.api.v1.PasswordHashResponse
|
||||
63, // 91: olivetin.api.v1.OliveTinApiService.Logout:output_type -> olivetin.api.v1.LogoutResponse
|
||||
49, // 92: olivetin.api.v1.OliveTinApiService.EventStream:output_type -> olivetin.api.v1.EventStreamResponse
|
||||
65, // 93: olivetin.api.v1.OliveTinApiService.GetDiagnostics:output_type -> olivetin.api.v1.GetDiagnosticsResponse
|
||||
67, // 94: olivetin.api.v1.OliveTinApiService.Init:output_type -> olivetin.api.v1.InitResponse
|
||||
71, // 95: olivetin.api.v1.OliveTinApiService.GetActionBinding:output_type -> olivetin.api.v1.GetActionBindingResponse
|
||||
73, // 96: olivetin.api.v1.OliveTinApiService.GetEntities:output_type -> olivetin.api.v1.GetEntitiesResponse
|
||||
6, // 97: olivetin.api.v1.OliveTinApiService.GetEntity:output_type -> olivetin.api.v1.Entity
|
||||
72, // [72:98] is the sub-list for method output_type
|
||||
46, // [46:72] is the sub-list for method input_type
|
||||
46, // [46:46] is the sub-list for extension type_name
|
||||
46, // [46:46] is the sub-list for extension extendee
|
||||
0, // [0:46] is the sub-list for field type_name
|
||||
65, // 37: olivetin.api.v1.GetDiagnosticsResponse.config_issues:type_name -> olivetin.api.v1.ConfigIssue
|
||||
70, // 38: olivetin.api.v1.InitResponse.oAuth2Providers:type_name -> olivetin.api.v1.OAuth2Provider
|
||||
69, // 39: olivetin.api.v1.InitResponse.additionalLinks:type_name -> olivetin.api.v1.AdditionalLink
|
||||
8, // 40: olivetin.api.v1.InitResponse.effective_policy:type_name -> olivetin.api.v1.EffectivePolicy
|
||||
0, // 41: olivetin.api.v1.GetActionBindingResponse.action:type_name -> olivetin.api.v1.Action
|
||||
35, // 42: olivetin.api.v1.GetActionBindingResponse.back_to_dashboards:type_name -> olivetin.api.v1.DashboardNavigationTarget
|
||||
75, // 43: olivetin.api.v1.GetEntitiesResponse.entity_definitions:type_name -> olivetin.api.v1.EntityDefinition
|
||||
6, // 44: olivetin.api.v1.EntityDefinition.instances:type_name -> olivetin.api.v1.Entity
|
||||
76, // 45: olivetin.api.v1.EntityDefinition.properties:type_name -> olivetin.api.v1.EntityProperty
|
||||
43, // 46: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry.value:type_name -> olivetin.api.v1.DebugBinding
|
||||
9, // 47: olivetin.api.v1.OliveTinApiService.GetDashboard:input_type -> olivetin.api.v1.GetDashboardRequest
|
||||
12, // 48: olivetin.api.v1.OliveTinApiService.StartAction:input_type -> olivetin.api.v1.StartActionRequest
|
||||
15, // 49: olivetin.api.v1.OliveTinApiService.StartActionAndWait:input_type -> olivetin.api.v1.StartActionAndWaitRequest
|
||||
17, // 50: olivetin.api.v1.OliveTinApiService.StartActionByGet:input_type -> olivetin.api.v1.StartActionByGetRequest
|
||||
19, // 51: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:input_type -> olivetin.api.v1.StartActionByGetAndWaitRequest
|
||||
78, // 52: olivetin.api.v1.OliveTinApiService.RestartAction:input_type -> olivetin.api.v1.RestartActionRequest
|
||||
56, // 53: olivetin.api.v1.OliveTinApiService.KillAction:input_type -> olivetin.api.v1.KillActionRequest
|
||||
34, // 54: olivetin.api.v1.OliveTinApiService.ExecutionStatus:input_type -> olivetin.api.v1.ExecutionStatusRequest
|
||||
21, // 55: olivetin.api.v1.OliveTinApiService.GetLogs:input_type -> olivetin.api.v1.GetLogsRequest
|
||||
24, // 56: olivetin.api.v1.OliveTinApiService.GetActionLogs:input_type -> olivetin.api.v1.GetActionLogsRequest
|
||||
26, // 57: olivetin.api.v1.OliveTinApiService.GetExecutionQueue:input_type -> olivetin.api.v1.GetExecutionQueueRequest
|
||||
30, // 58: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:input_type -> olivetin.api.v1.ValidateArgumentTypeRequest
|
||||
37, // 59: olivetin.api.v1.OliveTinApiService.WhoAmI:input_type -> olivetin.api.v1.WhoAmIRequest
|
||||
39, // 60: olivetin.api.v1.OliveTinApiService.ServerDiagnostics:input_type -> olivetin.api.v1.ServerDiagnosticsRequest
|
||||
41, // 61: olivetin.api.v1.OliveTinApiService.DumpVars:input_type -> olivetin.api.v1.DumpVarsRequest
|
||||
44, // 62: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:input_type -> olivetin.api.v1.DumpPublicIdActionMapRequest
|
||||
46, // 63: olivetin.api.v1.OliveTinApiService.GetReadyz:input_type -> olivetin.api.v1.GetReadyzRequest
|
||||
58, // 64: olivetin.api.v1.OliveTinApiService.LocalUserLogin:input_type -> olivetin.api.v1.LocalUserLoginRequest
|
||||
60, // 65: olivetin.api.v1.OliveTinApiService.PasswordHash:input_type -> olivetin.api.v1.PasswordHashRequest
|
||||
62, // 66: olivetin.api.v1.OliveTinApiService.Logout:input_type -> olivetin.api.v1.LogoutRequest
|
||||
48, // 67: olivetin.api.v1.OliveTinApiService.EventStream:input_type -> olivetin.api.v1.EventStreamRequest
|
||||
64, // 68: olivetin.api.v1.OliveTinApiService.GetDiagnostics:input_type -> olivetin.api.v1.GetDiagnosticsRequest
|
||||
67, // 69: olivetin.api.v1.OliveTinApiService.Init:input_type -> olivetin.api.v1.InitRequest
|
||||
71, // 70: olivetin.api.v1.OliveTinApiService.GetActionBinding:input_type -> olivetin.api.v1.GetActionBindingRequest
|
||||
73, // 71: olivetin.api.v1.OliveTinApiService.GetEntities:input_type -> olivetin.api.v1.GetEntitiesRequest
|
||||
77, // 72: olivetin.api.v1.OliveTinApiService.GetEntity:input_type -> olivetin.api.v1.GetEntityRequest
|
||||
7, // 73: olivetin.api.v1.OliveTinApiService.GetDashboard:output_type -> olivetin.api.v1.GetDashboardResponse
|
||||
14, // 74: olivetin.api.v1.OliveTinApiService.StartAction:output_type -> olivetin.api.v1.StartActionResponse
|
||||
16, // 75: olivetin.api.v1.OliveTinApiService.StartActionAndWait:output_type -> olivetin.api.v1.StartActionAndWaitResponse
|
||||
18, // 76: olivetin.api.v1.OliveTinApiService.StartActionByGet:output_type -> olivetin.api.v1.StartActionByGetResponse
|
||||
20, // 77: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:output_type -> olivetin.api.v1.StartActionByGetAndWaitResponse
|
||||
14, // 78: olivetin.api.v1.OliveTinApiService.RestartAction:output_type -> olivetin.api.v1.StartActionResponse
|
||||
57, // 79: olivetin.api.v1.OliveTinApiService.KillAction:output_type -> olivetin.api.v1.KillActionResponse
|
||||
36, // 80: olivetin.api.v1.OliveTinApiService.ExecutionStatus:output_type -> olivetin.api.v1.ExecutionStatusResponse
|
||||
23, // 81: olivetin.api.v1.OliveTinApiService.GetLogs:output_type -> olivetin.api.v1.GetLogsResponse
|
||||
25, // 82: olivetin.api.v1.OliveTinApiService.GetActionLogs:output_type -> olivetin.api.v1.GetActionLogsResponse
|
||||
29, // 83: olivetin.api.v1.OliveTinApiService.GetExecutionQueue:output_type -> olivetin.api.v1.GetExecutionQueueResponse
|
||||
31, // 84: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:output_type -> olivetin.api.v1.ValidateArgumentTypeResponse
|
||||
38, // 85: olivetin.api.v1.OliveTinApiService.WhoAmI:output_type -> olivetin.api.v1.WhoAmIResponse
|
||||
40, // 86: olivetin.api.v1.OliveTinApiService.ServerDiagnostics:output_type -> olivetin.api.v1.ServerDiagnosticsResponse
|
||||
42, // 87: olivetin.api.v1.OliveTinApiService.DumpVars:output_type -> olivetin.api.v1.DumpVarsResponse
|
||||
45, // 88: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:output_type -> olivetin.api.v1.DumpPublicIdActionMapResponse
|
||||
47, // 89: olivetin.api.v1.OliveTinApiService.GetReadyz:output_type -> olivetin.api.v1.GetReadyzResponse
|
||||
59, // 90: olivetin.api.v1.OliveTinApiService.LocalUserLogin:output_type -> olivetin.api.v1.LocalUserLoginResponse
|
||||
61, // 91: olivetin.api.v1.OliveTinApiService.PasswordHash:output_type -> olivetin.api.v1.PasswordHashResponse
|
||||
63, // 92: olivetin.api.v1.OliveTinApiService.Logout:output_type -> olivetin.api.v1.LogoutResponse
|
||||
49, // 93: olivetin.api.v1.OliveTinApiService.EventStream:output_type -> olivetin.api.v1.EventStreamResponse
|
||||
66, // 94: olivetin.api.v1.OliveTinApiService.GetDiagnostics:output_type -> olivetin.api.v1.GetDiagnosticsResponse
|
||||
68, // 95: olivetin.api.v1.OliveTinApiService.Init:output_type -> olivetin.api.v1.InitResponse
|
||||
72, // 96: olivetin.api.v1.OliveTinApiService.GetActionBinding:output_type -> olivetin.api.v1.GetActionBindingResponse
|
||||
74, // 97: olivetin.api.v1.OliveTinApiService.GetEntities:output_type -> olivetin.api.v1.GetEntitiesResponse
|
||||
6, // 98: olivetin.api.v1.OliveTinApiService.GetEntity:output_type -> olivetin.api.v1.Entity
|
||||
73, // [73:99] is the sub-list for method output_type
|
||||
47, // [47:73] is the sub-list for method input_type
|
||||
47, // [47:47] is the sub-list for extension type_name
|
||||
47, // [47:47] is the sub-list for extension extendee
|
||||
0, // [0:47] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_olivetin_api_v1_olivetin_proto_init() }
|
||||
|
|
@ -5372,7 +5502,7 @@ func file_olivetin_api_v1_olivetin_proto_init() {
|
|||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_olivetin_api_v1_olivetin_proto_rawDesc), len(file_olivetin_api_v1_olivetin_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 85,
|
||||
NumMessages: 86,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
|
|
|||
132
service/go.mod
132
service/go.mod
|
|
@ -10,7 +10,7 @@ require (
|
|||
github.com/MicahParks/keyfunc/v3 v3.8.0
|
||||
github.com/PaesslerAG/jsonpath v0.1.1
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/bufbuild/buf v1.71.0
|
||||
github.com/bufbuild/buf v1.72.0
|
||||
github.com/expr-lang/expr v1.17.8
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
|
|
@ -23,12 +23,12 @@ require (
|
|||
github.com/knadh/koanf/providers/file v1.2.1
|
||||
github.com/knadh/koanf/providers/rawbytes v1.0.0
|
||||
github.com/knadh/koanf/v2 v2.3.5
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/prometheus/client_golang v1.24.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.akshayshah.org/connectproto v0.6.0
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976
|
||||
golang.org/x/exp v0.0.0-20260718201538-764159d718ef
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sys v0.47.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
|
|
@ -36,15 +36,15 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0 // indirect
|
||||
4d63.com/gocheckcompilerdirectives v1.4.0 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.2 // indirect
|
||||
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20260626152828-968bf0468096.1 // indirect
|
||||
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20260722160903-4d94f3df3a7b.1 // indirect
|
||||
buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.11-20250109164928-1da0de137947.1 // indirect
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 // indirect
|
||||
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.20.0-20260629211719-be09125d9193.1 // indirect
|
||||
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260629211719-be09125d9193.1 // indirect
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 // indirect
|
||||
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.20.0-20260713175918-10d915f5b43b.1 // indirect
|
||||
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260713175918-10d915f5b43b.1 // indirect
|
||||
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.11-20241007202033-cf42259fcbfc.1 // indirect
|
||||
buf.build/go/app v0.2.1-0.20260407195847-833f8f978cda // indirect
|
||||
buf.build/go/app v0.2.1-0.20260626143626-be153867abea // indirect
|
||||
buf.build/go/bufplugin v0.10.0 // indirect
|
||||
buf.build/go/bufprivateusage v0.1.0 // indirect
|
||||
buf.build/go/interrupt v1.1.0 // indirect
|
||||
|
|
@ -53,21 +53,21 @@ require (
|
|||
buf.build/go/spdx v0.2.0 // indirect
|
||||
buf.build/go/standard v0.1.1-0.20260325175353-2b287e071df5 // indirect
|
||||
cel.dev/expr v0.25.2 // indirect
|
||||
charm.land/lipgloss/v2 v2.0.3 // indirect
|
||||
codeberg.org/chavacava/garif v0.2.0 // indirect
|
||||
charm.land/lipgloss/v2 v2.0.5 // indirect
|
||||
codeberg.org/chavacava/garif v0.2.1 // indirect
|
||||
codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect
|
||||
connectrpc.com/otelconnect v0.9.0 // indirect
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect
|
||||
dev.gaijin.team/go/golib v0.6.0 // indirect
|
||||
dev.gaijin.team/go/golib v0.8.1 // indirect
|
||||
github.com/4meepo/tagalign v1.4.3 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.7 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.8 // indirect
|
||||
github.com/AdminBenni/iota-mixing v1.0.0 // indirect
|
||||
github.com/AlwxSin/noinlineerr v1.0.5 // indirect
|
||||
github.com/Antonboom/errname v1.1.1 // indirect
|
||||
github.com/Antonboom/nilnil v1.1.1 // indirect
|
||||
github.com/Antonboom/errname v1.1.2 // indirect
|
||||
github.com/Antonboom/nilnil v1.1.2 // indirect
|
||||
github.com/Antonboom/testifylint v1.6.4 // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go-linter v1.2.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go-linter v1.2.1 // indirect
|
||||
github.com/Djarvur/go-err113 v0.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.5.0 // indirect
|
||||
github.com/MicahParks/jwkset v0.11.0 // indirect
|
||||
|
|
@ -75,7 +75,7 @@ require (
|
|||
github.com/MirrexOne/unqueryvet v1.5.4 // indirect
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect
|
||||
github.com/PaesslerAG/gval v1.2.4 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.24.1 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.27.0 // indirect
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.6 // indirect
|
||||
github.com/alexkohler/prealloc v1.1.0 // indirect
|
||||
|
|
@ -92,16 +92,16 @@ require (
|
|||
github.com/bombsimon/wsl/v5 v5.8.0 // indirect
|
||||
github.com/breml/bidichk v0.3.3 // indirect
|
||||
github.com/breml/errchkjson v0.4.1 // indirect
|
||||
github.com/bufbuild/protocompile v0.14.2-0.20260605203730-cd7c3c124e10 // indirect
|
||||
github.com/bufbuild/protocompile v0.14.2-0.20260716165721-bb5762d29672 // indirect
|
||||
github.com/bufbuild/protoplugin v0.0.0-20260414125817-25d1d281b46b // indirect
|
||||
github.com/butuzov/ireturn v0.4.1 // indirect
|
||||
github.com/butuzov/mirror v1.3.0 // indirect
|
||||
github.com/butuzov/mirror v1.3.3 // indirect
|
||||
github.com/catenacyber/perfsprint v0.10.1 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charithe/durationcheck v0.0.11 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 // indirect
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260720091822-7cc6674724ac // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.7 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||
|
|
@ -114,26 +114,26 @@ require (
|
|||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/curioswitch/go-reassign v0.3.0 // indirect
|
||||
github.com/daixiang0/gci v0.13.7 // indirect
|
||||
github.com/dave/dst v0.27.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
||||
github.com/daixiang0/gci v0.14.0 // indirect
|
||||
github.com/dave/dst v0.27.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/denis-tingaikin/go-header v1.0.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.12.0 // indirect
|
||||
github.com/docker/cli v29.6.1+incompatible // indirect
|
||||
github.com/dlclark/regexp2/v2 v2.5.2 // indirect
|
||||
github.com/docker/cli v29.6.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.8 // indirect
|
||||
github.com/docker/go-connections v0.7.0 // indirect
|
||||
github.com/docker/go-connections v0.8.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/ettle/strcase v0.2.0 // indirect
|
||||
github.com/fatih/color v1.19.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.6 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.8 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.20 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.21 // indirect
|
||||
github.com/go-critic/go-critic v0.14.4 // indirect
|
||||
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
||||
|
|
@ -154,7 +154,7 @@ require (
|
|||
github.com/golangci/misspell v0.8.0 // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.2 // indirect
|
||||
github.com/golangci/revgrep v0.8.0 // indirect
|
||||
github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba // indirect
|
||||
github.com/golangci/rowserrcheck v0.0.0-20260701145345-399602f983c8 // indirect
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
|
||||
github.com/google/cel-go v0.29.2 // indirect
|
||||
|
|
@ -168,24 +168,23 @@ require (
|
|||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-version v1.9.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jdx/go-netrc v1.0.0 // indirect
|
||||
github.com/jgautheron/goconst v1.10.0 // indirect
|
||||
github.com/jgautheron/goconst v1.10.2 // indirect
|
||||
github.com/jjti/go-spancheck v0.6.5 // indirect
|
||||
github.com/julz/importas v0.2.0 // indirect
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect
|
||||
github.com/kisielk/errcheck v1.10.0 // indirect
|
||||
github.com/kisielk/errcheck v1.20.0 // indirect
|
||||
github.com/kkHAIKE/contextcheck v1.1.6 // indirect
|
||||
github.com/klauspost/compress v1.19.0 // indirect
|
||||
github.com/klauspost/compress v1.19.1 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||
github.com/kulti/thelper v0.7.1 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.15 // indirect
|
||||
github.com/lasiar/canonicalheader v1.1.2 // indirect
|
||||
github.com/ldez/exptostd v0.4.5 // indirect
|
||||
github.com/ldez/gomoddirectives v0.8.0 // indirect
|
||||
github.com/ldez/gomoddirectives v0.9.0 // indirect
|
||||
github.com/ldez/grignotin v0.10.1 // indirect
|
||||
github.com/ldez/structtags v0.6.1 // indirect
|
||||
github.com/ldez/tagliatelle v0.7.2 // indirect
|
||||
|
|
@ -193,19 +192,17 @@ require (
|
|||
github.com/leonklingele/grouper v1.1.2 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/macabu/inamedparam v0.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect
|
||||
github.com/manuelarte/funcorder v0.6.0 // indirect
|
||||
github.com/maratori/testableexamples v1.0.1 // indirect
|
||||
github.com/maratori/testpackage v1.1.2 // indirect
|
||||
github.com/matoous/godox v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.23 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.27 // indirect
|
||||
github.com/mgechev/revive v1.15.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/moby/api v1.55.0 // indirect
|
||||
|
|
@ -216,15 +213,14 @@ require (
|
|||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.23.0 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.23.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260701081913-4f67fd55d3b4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260725062400-500c67a39b75 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.69.0 // indirect
|
||||
github.com/prometheus/common v0.70.1 // indirect
|
||||
github.com/prometheus/procfs v0.21.1 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.4.5 // indirect
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect
|
||||
|
|
@ -232,45 +228,45 @@ require (
|
|||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.60.0 // indirect
|
||||
github.com/raeperd/recvcheck v0.2.0 // indirect
|
||||
github.com/quic-go/quic-go v0.61.0 // indirect
|
||||
github.com/raeperd/recvcheck v0.3.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.15.0 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/ryancurrah/gomodguard v1.4.1 // indirect
|
||||
github.com/ryancurrah/gomodguard/v2 v2.1.3 // indirect
|
||||
github.com/ryanrolds/sqlclosecheck v0.6.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.26.1 // indirect
|
||||
github.com/securego/gosec/v2 v2.28.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
github.com/sonatard/noctx v0.5.1 // indirect
|
||||
github.com/sourcegraph/go-diff v0.8.0 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/cobra v1.10.2 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tetafro/godot v1.5.6 // indirect
|
||||
github.com/tetratelabs/wazero v1.12.0 // indirect
|
||||
github.com/tidwall/btree v1.8.1 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20260723120731-857993a2939c // indirect
|
||||
github.com/timonwong/loggercheck v0.11.0 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.12.0 // indirect
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
||||
github.com/ultraware/funlen v0.2.0 // indirect
|
||||
github.com/ultraware/whitespace v0.2.0 // indirect
|
||||
github.com/uudashr/gocognit v1.2.1 // indirect
|
||||
github.com/uudashr/iface v1.4.2 // indirect
|
||||
github.com/uudashr/iface v1.5.0 // indirect
|
||||
github.com/xen0n/gosmopolitan v1.3.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
|
|
@ -280,7 +276,7 @@ require (
|
|||
go-simpler.org/musttag v0.14.0 // indirect
|
||||
go-simpler.org/sloglint v0.12.0 // indirect
|
||||
go.augendre.info/arangolint v0.4.0 // indirect
|
||||
go.augendre.info/fatcontext v0.9.0 // indirect
|
||||
go.augendre.info/fatcontext v0.10.0 // indirect
|
||||
go.lsp.dev/jsonrpc2 v1.0.1 // indirect
|
||||
go.lsp.dev/protocol v1.0.1 // indirect
|
||||
go.lsp.dev/uri v1.0.1 // indirect
|
||||
|
|
@ -293,19 +289,17 @@ require (
|
|||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.28.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20260611194520-c48552f49976 // indirect
|
||||
golang.org/x/mod v0.37.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/crypto v0.54.0 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20260718201538-764159d718ef // indirect
|
||||
golang.org/x/mod v0.38.0 // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/text v0.39.0 // indirect
|
||||
golang.org/x/term v0.45.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.47.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
golang.org/x/tools v0.48.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260724162435-b2f20204f0df // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df // indirect
|
||||
honnef.co/go/tools v0.7.0 // indirect
|
||||
mvdan.cc/gofumpt v0.10.0 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect
|
||||
|
|
|
|||
277
service/go.sum
277
service/go.sum
|
|
@ -1,21 +1,21 @@
|
|||
4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A=
|
||||
4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY=
|
||||
4d63.com/gocheckcompilerdirectives v1.4.0 h1:ZLq62rbGWVmQhiZ8kuNVIT/M09xCSTdJz9K3xOdT/CY=
|
||||
4d63.com/gocheckcompilerdirectives v1.4.0/go.mod h1:9ZOAiMOjqC/nRwci2fcUXVHUNLG/cH6r6rhUh+jTFtQ=
|
||||
4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU=
|
||||
4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0=
|
||||
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20260626152828-968bf0468096.1 h1:bHi5/cwz7IPRhFcrRA4iytTjDI3WwFkv1m5K7y+YOTc=
|
||||
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20260626152828-968bf0468096.1/go.mod h1:1Znr6gmYBhbxWUPRrrVnSLXQsz8bvFVw1HHJq2bI3VQ=
|
||||
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20260722160903-4d94f3df3a7b.1 h1:IbnjTSFlFUyPCRF50eY1u9EyPpU1wAyAN5neSgI5jyE=
|
||||
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20260722160903-4d94f3df3a7b.1/go.mod h1:1Znr6gmYBhbxWUPRrrVnSLXQsz8bvFVw1HHJq2bI3VQ=
|
||||
buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.11-20250109164928-1da0de137947.1 h1:HwzzCRS4ZrEm1++rzSDxHnO0DOjiT1b8I/24e8a4exY=
|
||||
buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.11-20250109164928-1da0de137947.1/go.mod h1:8PRKXhgNes29Tjrnv8KdZzg3I1QceOkzibW1QK7EXv0=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 h1:s6hzCXtND/ICdGPTMGk7C+/BFlr2Jg5GyH0NKf4XGXg=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
|
||||
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.20.0-20260629211719-be09125d9193.1 h1:szEsAEWp6MqAX3NHUFq3ZH3oGKyajcte99KrCz0MpQ0=
|
||||
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.20.0-20260629211719-be09125d9193.1/go.mod h1:VKtlgFOV7s6j5/wPr+d6c0Q80c8MF4OfZev4e0d9pNo=
|
||||
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260629211719-be09125d9193.1 h1:JYYze3qnJZxo4PWTSZGKH+NJs0Vi2DYFo/NzUpa8Em4=
|
||||
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260629211719-be09125d9193.1/go.mod h1:5QgvXU4BhJqHgkO27sFJrRQHABWN4X6QnGvL8hWCJKk=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 h1:fXh8CsdNpjRr8R5vFdqtIxPt/Lno2IIJlYOdZBIZn0w=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
|
||||
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.20.0-20260713175918-10d915f5b43b.1 h1:c4w6fQ6wAhZLgVI+HZozlWX9I2aVPN34vrl/8cuovVc=
|
||||
buf.build/gen/go/bufbuild/registry/connectrpc/go v1.20.0-20260713175918-10d915f5b43b.1/go.mod h1:mbcuFKpnCQsF8H2fBs1lN1dQVAiASPQKwFgTkXqYscI=
|
||||
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260713175918-10d915f5b43b.1 h1:qJUlFKLFdb3OFCpmA+r5AQbysgBD7WLRapE9lOs08r8=
|
||||
buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260713175918-10d915f5b43b.1/go.mod h1:1JJi9jvOqRxSMa+JxiZSm57doB+db/1WYCIa2lHfc40=
|
||||
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.11-20241007202033-cf42259fcbfc.1 h1:iGPvEJltOXUMANWf0zajcRcbiOXLD90ZwPUFvbcuv6Q=
|
||||
buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.11-20241007202033-cf42259fcbfc.1/go.mod h1:nWVKKRA29zdt4uvkjka3i/y4mkrswyWwiu0TbdX0zts=
|
||||
buf.build/go/app v0.2.1-0.20260407195847-833f8f978cda h1:eysSyjrJtkxU1A/9+Kv+1Mwq9K6BYBw+STIOVsZ256Y=
|
||||
buf.build/go/app v0.2.1-0.20260407195847-833f8f978cda/go.mod h1:V32mBaPWsfq6REAeZvvs/rQl7ZCl9Dn7eW1BBrmH0GQ=
|
||||
buf.build/go/app v0.2.1-0.20260626143626-be153867abea h1:krHaIyUJSTnurb0fbYQtsXkZWiWQ/ydOgfm61TPSODc=
|
||||
buf.build/go/app v0.2.1-0.20260626143626-be153867abea/go.mod h1:V32mBaPWsfq6REAeZvvs/rQl7ZCl9Dn7eW1BBrmH0GQ=
|
||||
buf.build/go/bufplugin v0.10.0 h1:vZBX0mq9as5UIBug8U+/DkGRaHNlM/HVOw59O8fvOIU=
|
||||
buf.build/go/bufplugin v0.10.0/go.mod h1:ax7obVurKDH1I2nR4pFTS+TE6K3kZhTmwDCN2YgdV8I=
|
||||
buf.build/go/bufprivateusage v0.1.0 h1:SzCoCcmzS3zyXHEXHeSQhGI7OTkgtljoknLzsUz9Gg4=
|
||||
|
|
@ -32,10 +32,10 @@ buf.build/go/standard v0.1.1-0.20260325175353-2b287e071df5 h1:njYKSWoLiq2i5O7y2b
|
|||
buf.build/go/standard v0.1.1-0.20260325175353-2b287e071df5/go.mod h1:DQmodNT9EHX94WzUaWiZK+/4EaFa/xZTc1gzfCxZVXU=
|
||||
cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs=
|
||||
cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
charm.land/lipgloss/v2 v2.0.3 h1:yM2zJ4Cf5Y51b7RHIwioil4ApI/aypFXXVHSwlM6RzU=
|
||||
charm.land/lipgloss/v2 v2.0.3/go.mod h1:7myLU9iG/3xluAWzpY/fSxYYHCgoKTie7laxk6ATwXA=
|
||||
codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY=
|
||||
codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ=
|
||||
charm.land/lipgloss/v2 v2.0.5 h1:kbNxgeeUOYv5J0YdpxFjfvf3dFvqH8Aci4zB6xqFtrY=
|
||||
charm.land/lipgloss/v2 v2.0.5/go.mod h1:9oqhxt4yxIMe6q5A4kHr44DremZk7J9UNh74GlWa5nc=
|
||||
codeberg.org/chavacava/garif v0.2.1 h1:K9oYxSlvlXrHXyW26Z4q61bTpJDo1wbvXcYKar/F/LM=
|
||||
codeberg.org/chavacava/garif v0.2.1/go.mod h1:oHnDSmc0f9K1MeE+MQD/yjkiIB5Xsn5y3S9Dg96Xk84=
|
||||
codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI=
|
||||
codeberg.org/polyfloyd/go-errorlint v1.9.0/go.mod h1:GPRRu2LzVijNn4YkrZYJfatQIdS+TrcK8rL5Xs24qw8=
|
||||
connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ=
|
||||
|
|
@ -46,26 +46,26 @@ connectrpc.com/otelconnect v0.9.0 h1:NggB3pzRC3pukQWaYbRHJulxuXvmCKCKkQ9hbrHAWoA
|
|||
connectrpc.com/otelconnect v0.9.0/go.mod h1:AEkVLjCPXra+ObGFCOClcJkNjS7zPaQSqvO0lCyjfZc=
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y=
|
||||
dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI=
|
||||
dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo=
|
||||
dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE=
|
||||
dev.gaijin.team/go/golib v0.8.1 h1:JYju4x9BSo+QD/AYeHULVDcvEhiFg8wOi6pT0IaZF5E=
|
||||
dev.gaijin.team/go/golib v0.8.1/go.mod h1:c5fu7t1RSGMxSQgcUYO1sODbzsYnOCXJLmHeNG1Eb+0=
|
||||
github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8=
|
||||
github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c=
|
||||
github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ=
|
||||
github.com/Abirdcfly/dupword v0.1.7/go.mod h1:K0DkBeOebJ4VyOICFdppB23Q0YMOgVafM0zYW0n9lF4=
|
||||
github.com/Abirdcfly/dupword v0.1.8 h1:SrhcUuGsROBuChFxHALRYzyyPODWn9zwghmzPvD9Cd8=
|
||||
github.com/Abirdcfly/dupword v0.1.8/go.mod h1:XZrhVnI7YGpsTiWZANSQaBJ4QpL/Tq5vIEdKJJAs9WI=
|
||||
github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo=
|
||||
github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY=
|
||||
github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY=
|
||||
github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc=
|
||||
github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q=
|
||||
github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ=
|
||||
github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ=
|
||||
github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II=
|
||||
github.com/Antonboom/errname v1.1.2 h1:dxwONZJua3VB8Xh/VaCjqAcqF645sWWv7xj26zy7tdQ=
|
||||
github.com/Antonboom/errname v1.1.2/go.mod h1:YeZIpgLMxT+SNkruGgYkLhzq/9vs3fsolTZegKaKDZI=
|
||||
github.com/Antonboom/nilnil v1.1.2 h1:aNlFuJhaEseXe4fHO3xbjXlSeEiQVYa2lEkWD2s2hAY=
|
||||
github.com/Antonboom/nilnil v1.1.2/go.mod h1:0ynwvphOLmAuMwTNDyBnDZmSwZoDpcFXmUHmzoHH2WA=
|
||||
github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ=
|
||||
github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/ClickHouse/clickhouse-go-linter v1.2.0 h1:zbm174up3hTKjp0wKZVnTzRiG7tSF5XZF0FJG/MuCBI=
|
||||
github.com/ClickHouse/clickhouse-go-linter v1.2.0/go.mod h1:pLorS7ffPTfuUV9M0SJgfHA/h/WQPQUk2FWG9x74cQ4=
|
||||
github.com/ClickHouse/clickhouse-go-linter v1.2.1 h1:zGEKIyd5YL08ieWG/LOUmlau2DxbxPVOfAeo+4Jz3ck=
|
||||
github.com/ClickHouse/clickhouse-go-linter v1.2.1/go.mod h1:pLorS7ffPTfuUV9M0SJgfHA/h/WQPQUk2FWG9x74cQ4=
|
||||
github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g=
|
||||
github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
|
|
@ -90,8 +90,8 @@ github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEs
|
|||
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.24.1 h1:m5ffpfZbIb++k8AqFEKy9uVgY12xIQtBsQlc6DfZJQM=
|
||||
github.com/alecthomas/chroma/v2 v2.24.1/go.mod h1:l+ohZ9xRXIbGe7cIW+YZgOGbvuVLjMps/FYN/CwuabI=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
|
||||
github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU=
|
||||
github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
|
|
@ -132,16 +132,16 @@ github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDw
|
|||
github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s=
|
||||
github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4=
|
||||
github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs=
|
||||
github.com/bufbuild/buf v1.71.0 h1:/XLs8mFKjlM/LVbBBBQ3uSNcyEClyAV8lJiO7QR04d8=
|
||||
github.com/bufbuild/buf v1.71.0/go.mod h1:fbHMfu0nEbIPQT3IG7rAfPZXoFYEfZJuqFbAXu1wnRU=
|
||||
github.com/bufbuild/protocompile v0.14.2-0.20260605203730-cd7c3c124e10 h1:N0ZOKMwHpKsFhMGSxnB6JiVkM7SbH8IAKlGGO0SpQBA=
|
||||
github.com/bufbuild/protocompile v0.14.2-0.20260605203730-cd7c3c124e10/go.mod h1:jPUiZUFWc8E3Kc2Y4SRlGAdjde4amGkHY0BUACNS43E=
|
||||
github.com/bufbuild/buf v1.72.0 h1:VMmGFtCLrxyS2wkpghExmhhiqJDdmc8DcwAvsGJGJ94=
|
||||
github.com/bufbuild/buf v1.72.0/go.mod h1:bhtIlPDo3q/PDw4yaTdx2+jxkc33Aq+ygFIi6Diz2yU=
|
||||
github.com/bufbuild/protocompile v0.14.2-0.20260716165721-bb5762d29672 h1:6xykiXQPoF/hhOjAhwiHQ0kgcAvrDZw0Tjl1VQkpu5c=
|
||||
github.com/bufbuild/protocompile v0.14.2-0.20260716165721-bb5762d29672/go.mod h1:jPUiZUFWc8E3Kc2Y4SRlGAdjde4amGkHY0BUACNS43E=
|
||||
github.com/bufbuild/protoplugin v0.0.0-20260414125817-25d1d281b46b h1:b7wvo9ZhjLzCp7tGbOUMvgtYTnd33zGSAmMxcdxMnhQ=
|
||||
github.com/bufbuild/protoplugin v0.0.0-20260414125817-25d1d281b46b/go.mod h1:c5D8gWRIZ2HLWO3gXYTtUfw/hbJyD8xikv2ooPxnklQ=
|
||||
github.com/butuzov/ireturn v0.4.1 h1:vWb3NO4t77iku/sjCQ/2pHTQeOmxEhjIriJqRLg1Y+I=
|
||||
github.com/butuzov/ireturn v0.4.1/go.mod h1:q+DXKzTDV5guNuXLnIab9fKXizTn2miZHLhxH7V/GB4=
|
||||
github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc=
|
||||
github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI=
|
||||
github.com/butuzov/mirror v1.3.3 h1:v0RsWBhfFc1RQqE/f3sHpSttKDtodFn0gFmtYyD4/hA=
|
||||
github.com/butuzov/mirror v1.3.3/go.mod h1:h9BzzwYnTiHO0GzgvaTqIg7VSsOUhdIv51cHFFBmX1w=
|
||||
github.com/catenacyber/perfsprint v0.10.1 h1:u7Riei30bk46XsG8nknMhKLXG9BcXz3+3tl/WpKm0PQ=
|
||||
github.com/catenacyber/perfsprint v0.10.1/go.mod h1:DJTGsi/Zufpuus6XPGJyKOTMELe347o6akPvWG9Zcsc=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc=
|
||||
|
|
@ -152,8 +152,8 @@ github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1Di
|
|||
github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4=
|
||||
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 h1:OqDqxQZliC7C8adA7KjelW3OjtAxREfeHkNcd66wpeI=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318/go.mod h1:Y6kE2GzHfkyQQVCSL9r2hwokSrIlHGzZG+71+wDYSZI=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260720091822-7cc6674724ac h1:BP8qMDGjmOejoVTklEXXTHI0OwMIt8JHPSPHxscFFwA=
|
||||
github.com/charmbracelet/ultraviolet v0.0.0-20260720091822-7cc6674724ac/go.mod h1:psnCZIfwwxVs6v6DhUc6NJ8AQ3ejvs2ejKwoOMeVmUk=
|
||||
github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
|
||||
github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
|
|
@ -179,27 +179,30 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs=
|
||||
github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88=
|
||||
github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ=
|
||||
github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ=
|
||||
github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY=
|
||||
github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
|
||||
github.com/daixiang0/gci v0.14.0 h1:h6AcLqmjIOBgojhtzY2CvBnA6RawPTkBHgtMvYD5YZ8=
|
||||
github.com/daixiang0/gci v0.14.0/go.mod h1:w9E+SWQ4aPQ+xYPUdqitGDoXpT4mayqOfk7Szz2U6wQ=
|
||||
github.com/dave/dst v0.27.4 h1:d+EVnOZmphH+lUEXq9rit4GjsFSKJ3AhfRWf7eobTps=
|
||||
github.com/dave/dst v0.27.4/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
|
||||
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingaikin/go-header v1.0.0 h1:QIwZWb3jLC6pOp9NEFldiD8raqRmCE/n0VUdZKW32x8=
|
||||
github.com/denis-tingaikin/go-header v1.0.0/go.mod h1:NT3qKwqsXQYp8WHVgkwxL49qB5jsRmdr9dGQCDfpmZ0=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=
|
||||
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/cli v29.6.1+incompatible h1:oO7F4nn3Ovr/5TlfTUWFbMwBSS/B7Xs6Epv26gBrUP8=
|
||||
github.com/docker/cli v29.6.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/dlclark/regexp2/v2 v2.5.2 h1:HAsucWRhsqcDzl6Ua9aR8JwYOTzrZyPrF0/FNxJVAI0=
|
||||
github.com/dlclark/regexp2/v2 v2.5.2/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
||||
github.com/docker/cli v29.6.2+incompatible h1:/bjePvcbbFTnRrMfWJBY7AjfICdsiLVgHn6LwTVOcqw=
|
||||
github.com/docker/cli v29.6.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q=
|
||||
github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
||||
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-connections v0.8.0 h1:T9UlP76qPLA/HaLrcC+s4Doqqv5XsWMMUGPF5Aih/k0=
|
||||
github.com/docker/go-connections v0.8.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
|
||||
|
|
@ -212,23 +215,23 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4
|
|||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||
github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E=
|
||||
github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/firefart/nonamedreturns v1.0.8 h1:iB32Dl17zJl1zlVEj/WlUWgx0HiRyQ85OUw1WHa4/II=
|
||||
github.com/firefart/nonamedreturns v1.0.8/go.mod h1:vxFNvm5AfP/8rgAKFzYmnqx0yp1HjrYsErZ9pHPTznA=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0=
|
||||
github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI=
|
||||
github.com/ghostiam/protogetter v0.3.21 h1:EeWTGvL/Eyosp653hiWb6Byx4b69iJC4/E+za7vQHoI=
|
||||
github.com/ghostiam/protogetter v0.3.21/go.mod h1:iAKSpyoHwYzay+OpjoWgwzRtPFthEfuUvmlomTThck0=
|
||||
github.com/go-critic/go-critic v0.14.4 h1:dSX4C3pWSeuMVxvQh6yG8U0ReSf3YOmKi4nwX5q7n/8=
|
||||
github.com/go-critic/go-critic v0.14.4/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ=
|
||||
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 h1:KZaTBSyshWX3MP5jukJcNSuXDQTO+rNpt0J564dX/eg=
|
||||
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-quicktest/qt v1.102.0 h1:HSQxCeh5YZH3EL3W39ixjtyaEhcWSXQHtHnMBzSs474=
|
||||
|
|
@ -284,8 +287,8 @@ github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3H
|
|||
github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw=
|
||||
github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s=
|
||||
github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
|
||||
github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba h1:lqtcnSMDuuJdu/LrKWi5RJzpSNLOJXYe/nzQutTI5kg=
|
||||
github.com/golangci/rowserrcheck v0.0.0-20260419091836-c5f79b8a11ba/go.mod h1:sCBNcpRmhJCtbFGz49+IM3ETTFf7QdJ30AeYCd43NKk=
|
||||
github.com/golangci/rowserrcheck v0.0.0-20260701145345-399602f983c8 h1:OtbxoLqMgJUWEHXhlNkvTlxJMQNSddWMZP/e6FaNkBI=
|
||||
github.com/golangci/rowserrcheck v0.0.0-20260701145345-399602f983c8/go.mod h1:RLgEC6y0EaFAJT8kkHrRxwsGFwYamOB6BYzUZOpfTVs=
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM=
|
||||
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s=
|
||||
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM=
|
||||
|
|
@ -299,8 +302,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.21.7 h1:/vPFuVXDjtFREsVArW+0h1CIl5urnOhzei4X2DMW9IU=
|
||||
github.com/google/go-containerregistry v0.21.7/go.mod h1:kjSbt7/zMsKLWfnHrIvKvhXHUw91jbe9DNjPPJ32gXE=
|
||||
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc=
|
||||
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
||||
github.com/google/pprof v0.0.0-20260709232956-b9395ee17fa0 h1:du0WGc8xSKq/++e0cglxhS/mXVqsR7+c7jLEi5Vqduw=
|
||||
github.com/google/pprof v0.0.0-20260709232956-b9395ee17fa0/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs=
|
||||
|
|
@ -326,8 +329,6 @@ github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaX
|
|||
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
|
|
@ -336,8 +337,8 @@ github.com/jamesread/golure v0.0.0-20260510214136-6ef80e0ce8da h1:hYsJqujd3A4Xtp
|
|||
github.com/jamesread/golure v0.0.0-20260510214136-6ef80e0ce8da/go.mod h1:BZ/CMtZJJ4LNEBDSjGfafTJMjlDPIA9FS16+reN9NUE=
|
||||
github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ=
|
||||
github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8=
|
||||
github.com/jgautheron/goconst v1.10.0 h1:Ptt+OoE4NaEWKhLrWrrN3IpZdGLiqaf7WLnEX/iv4Jw=
|
||||
github.com/jgautheron/goconst v1.10.0/go.mod h1:0p+wv1lFOiUr0IlNNT1nrm6+8DB8u2sU6KHGzFRXHDc=
|
||||
github.com/jgautheron/goconst v1.10.2 h1:0wFg/DbpYB0ChoP94qUM+SuQpqj5oCzPEqAil5dV8Pg=
|
||||
github.com/jgautheron/goconst v1.10.2/go.mod h1:0p+wv1lFOiUr0IlNNT1nrm6+8DB8u2sU6KHGzFRXHDc=
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.2 h1:qZU+rEZUOYTz1Bnhi3xbwn+VxdXkLVeEpAeZzVXLY88=
|
||||
github.com/jhump/protoreflect/v2 v2.0.0-beta.2/go.mod h1:4tnOYkB/mq7QTyS3YKtVtNrJv4Psqout8HA1U+hZtgM=
|
||||
github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8=
|
||||
|
|
@ -346,12 +347,12 @@ github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ=
|
|||
github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY=
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0=
|
||||
github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY=
|
||||
github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw=
|
||||
github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8=
|
||||
github.com/kisielk/errcheck v1.20.0 h1:9rwHBNKzd4wkDWcROy3DvFGNqEPlkxBg305rvk7HabI=
|
||||
github.com/kisielk/errcheck v1.20.0/go.mod h1:O+f80MKNwX8Oor2jwgpeQ9An7uJm+hRSgT+h22knRJU=
|
||||
github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE=
|
||||
github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg=
|
||||
github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
|
||||
github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
|
||||
github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
|
||||
|
|
@ -380,8 +381,8 @@ github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0
|
|||
github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI=
|
||||
github.com/ldez/exptostd v0.4.5 h1:kv2ZGUVI6VwRfp/+bcQ6Nbx0ghFWcGIKInkG/oFn1aQ=
|
||||
github.com/ldez/exptostd v0.4.5/go.mod h1:QRjHRMXJrCTIm9WxVNH6VW7oN7KrGSht69bIRwvdFsM=
|
||||
github.com/ldez/gomoddirectives v0.8.0 h1:JqIuTtgvFC2RdH1s357vrE23WJF2cpDCPFgA/TWDGpk=
|
||||
github.com/ldez/gomoddirectives v0.8.0/go.mod h1:jutzamvZR4XYJLr0d5Honycp4Gy6GEg2mS9+2YX3F1Q=
|
||||
github.com/ldez/gomoddirectives v0.9.0 h1:2YV/EX7nVlWL4jySusYTzBKHuE3D2fgcRsQuMa3yIoo=
|
||||
github.com/ldez/gomoddirectives v0.9.0/go.mod h1:DdZzfm9MdXCjn2/UGYXCFfo+tzrp2Ib4iD2Q0kIJkwE=
|
||||
github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o=
|
||||
github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas=
|
||||
github.com/ldez/structtags v0.6.1 h1:bUooFLbXx41tW8SvkfwfFkkjPYvFFs59AAMgVg6DUBk=
|
||||
|
|
@ -396,8 +397,6 @@ github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW
|
|||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE=
|
||||
github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww=
|
||||
github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM=
|
||||
github.com/manuelarte/funcorder v0.6.0 h1:0hBngc4fa1IgNiI65A7sFGkMvoMCc878RjqB5V7rWP0=
|
||||
|
|
@ -412,18 +411,16 @@ github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
|||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
||||
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0=
|
||||
github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
|
||||
github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q=
|
||||
github.com/mgechev/revive v1.15.0/go.mod h1:LlAKO3QQe9OJ0pVZzI2GPa8CbXGZ/9lNpCGvK4T/a8A=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
|
|
@ -444,12 +441,12 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK
|
|||
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.23.0 h1:x3o4DGYOWbBMP/VdNQKgSj+25aJKx2Pe6lHr8gBcgf8=
|
||||
github.com/nunnatsa/ginkgolinter v0.23.0/go.mod h1:9qN1+0akwXEccwV1CAcCDfcoBlWXHB+ML9884pL4SZ4=
|
||||
github.com/onsi/ginkgo/v2 v2.28.2 h1:DTrMfpqxiNUyQ3Y0zhn1n3cOO2euFgQPYIpkWwxVFps=
|
||||
github.com/onsi/ginkgo/v2 v2.28.2/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
|
||||
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
|
||||
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
|
||||
github.com/nunnatsa/ginkgolinter v0.23.1 h1:1JOE+iDubBmH2YvP485w9AHBI8osHFBdVeMucRRvkDM=
|
||||
github.com/nunnatsa/ginkgolinter v0.23.1/go.mod h1:jh1sHyjC5TzL24rFUJvxtXV0iD2/9S7C/8N4aQd/lag=
|
||||
github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E=
|
||||
github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
|
||||
github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
|
||||
github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
|
|
@ -461,20 +458,19 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ
|
|||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/petermattis/goid v0.0.0-20260701081913-4f67fd55d3b4 h1:YCvN6o1QTbVkeHMlTjnzfP02SvsmOZLlJdFzw/f9BTk=
|
||||
github.com/petermattis/goid v0.0.0-20260701081913-4f67fd55d3b4/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/petermattis/goid v0.0.0-20260725062400-500c67a39b75 h1:VmZ6mKVkxavKEhEy4ZYyV7BwBYBFBP0TwIqmLk84fpU=
|
||||
github.com/petermattis/goid v0.0.0-20260725062400-500c67a39b75/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
|
||||
github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.69.0 h1:OA85nJQS/T/MaYh/Q2CcgDKSGWqNIgrBDvDH85CuiNk=
|
||||
github.com/prometheus/common v0.69.0/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y=
|
||||
github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
|
||||
github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9 h1:arwj11zP0yJIxIRiDn22E0H8PxfF7TsTrc2wIPFIsf4=
|
||||
|
|
@ -493,18 +489,18 @@ github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4
|
|||
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0=
|
||||
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
|
||||
github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI=
|
||||
github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU=
|
||||
github.com/quic-go/quic-go v0.61.0 h1:ui88A53s8MSVYLC56en0KQ17HARk+9986Dn0SBfKNvA=
|
||||
github.com/quic-go/quic-go v0.61.0/go.mod h1:9So2anK4Tp22URSQq00k+Vo2PNkle96ycDPDHL4s9vs=
|
||||
github.com/raeperd/recvcheck v0.3.0 h1:PM+XYvyxIj3bo+kobJfFTdTuU3Lmfu96mKDbyHDbRt8=
|
||||
github.com/raeperd/recvcheck v0.3.0/go.mod h1:PZNwG+HztFYMH2ZPq0Hu3QgkV2yiA6VrtNz9c1fXWJo=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8=
|
||||
github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc=
|
||||
github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
|
||||
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
|
|
@ -515,6 +511,8 @@ github.com/ryancurrah/gomodguard/v2 v2.1.3 h1:E7sz3PJwE9Ba1reVxSpF6XLCPJZ74Kfw/L
|
|||
github.com/ryancurrah/gomodguard/v2 v2.1.3/go.mod h1:CQicdLGatWMxLX53JzoBjYlsNZhHbmLv2AVa0s2aivU=
|
||||
github.com/ryanrolds/sqlclosecheck v0.6.0 h1:pEyL9okISdg1F1SEpJNlrEotkTGerv5BMk7U4AG0eVg=
|
||||
github.com/ryanrolds/sqlclosecheck v0.6.0/go.mod h1:xyX16hsDaCMXHrMJ3JMzGf5OpDfHTOTTQrT7HOFUmeU=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
|
|
@ -523,8 +521,8 @@ github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tM
|
|||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8=
|
||||
github.com/securego/gosec/v2 v2.26.1 h1:gdkttGhQFVehqRJ8grKH4DrpqM/QlPKNHBnl8QgcEC4=
|
||||
github.com/securego/gosec/v2 v2.26.1/go.mod h1:57UW4p0uoP3kxoTkhoo3axLdVAi+OWrLg/Ax/kdqtPE=
|
||||
github.com/securego/gosec/v2 v2.28.0 h1:ZsSdiDb0AtTpLFVol5z91gbMei9ZiLEPG/pZjZujp7c=
|
||||
github.com/securego/gosec/v2 v2.28.0/go.mod h1:lb4/9AHe+lJy/kjWmWRWWsEipvbwGKuxf+tY1Pmjdnk=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
|
|
@ -540,32 +538,29 @@ github.com/sourcegraph/go-diff v0.8.0 h1:ipIyu4cTsLbIrln4l0qtHA3r0a7gyK4ntKjtQyt
|
|||
github.com/sourcegraph/go-diff v0.8.0/go.mod h1:hWlcO7Al+UZStZAP8rBumHpCK5ZHQ5BXsMls8p4+F5E=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.3.1 h1:AyX7+dxI4IdLBPtDbsGAyqiTSLpCP9hWRrXQDU4Cm/g=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.3.1/go.mod h1:ODbZesTCHMVKthBHskvUUexdcNHAQRXk9NpSsL8p/HQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
|
||||
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
|
||||
|
|
@ -576,8 +571,8 @@ github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZ
|
|||
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
|
||||
github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA=
|
||||
github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A=
|
||||
github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 h1:SiHe5XLTn9sFWJ5pBwJ5FN/4j34q9ZlOAD//kMoMYp0=
|
||||
github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4/go.mod h1:sDHLK7rb/59v/ZxZ7KtymgcoxuUMxjXq8gtu9VMOK8M=
|
||||
github.com/timakin/bodyclose v0.0.0-20260723120731-857993a2939c h1:d/FUP8GD4QiY/rdFzZmBu3KqL9eBNVzMu7W/5pGkaMM=
|
||||
github.com/timakin/bodyclose v0.0.0-20260723120731-857993a2939c/go.mod h1:sDHLK7rb/59v/ZxZ7KtymgcoxuUMxjXq8gtu9VMOK8M=
|
||||
github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M=
|
||||
github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.12.0 h1:H/qQ1aNWz/eeIhxKAFvkfIA+N7YDvq6TWVFL27Of9is=
|
||||
|
|
@ -590,8 +585,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW
|
|||
github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
|
||||
github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4=
|
||||
github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q=
|
||||
github.com/uudashr/iface v1.4.2 h1:06Vq5RKVYThBsj0Bnw4oasMjD1r+7CE/bcKOA8dVSvg=
|
||||
github.com/uudashr/iface v1.4.2/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
|
||||
github.com/uudashr/iface v1.5.0 h1:PgdMt4uAettGG8K/Kbamc4B9FABgUgnS3TLbl6fnjEk=
|
||||
github.com/uudashr/iface v1.5.0/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg=
|
||||
github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM=
|
||||
github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
|
|
@ -621,8 +616,8 @@ go.akshayshah.org/connectproto v0.6.0 h1:tqmysQF2AfvUeYS03mRAAZTFpiQeXqhGIDnH1GO
|
|||
go.akshayshah.org/connectproto v0.6.0/go.mod h1:uA9TR/6MhBlLn0fh8VXRyL26EKTJlimWao4jbz7JHbA=
|
||||
go.augendre.info/arangolint v0.4.0 h1:xSCZjRoS93nXazBSg5d0OGCi9APPLNMmmLrC995tR50=
|
||||
go.augendre.info/arangolint v0.4.0/go.mod h1:l+f/b4plABuFISuKnTGD4RioXiCCgghv2xqst/xOvAA=
|
||||
go.augendre.info/fatcontext v0.9.0 h1:Gt5jGD4Zcj8CDMVzjOJITlSb9cEch54hjRRlN3qDojE=
|
||||
go.augendre.info/fatcontext v0.9.0/go.mod h1:L94brOAT1OOUNue6ph/2HnwxoNlds9aXDF2FcUntbNw=
|
||||
go.augendre.info/fatcontext v0.10.0 h1:HhFopmivh8U1+AU7f0kuwUeg2eiIns7YsGQOMHwSJ90=
|
||||
go.augendre.info/fatcontext v0.10.0/go.mod h1:pqpGvA9GlrXy+aXkp8L2dKz12Zp4g2FhzcAtwToU+2w=
|
||||
go.lsp.dev/jsonrpc2 v1.0.1 h1:JZAchpm6RcOuwEOnZ7kF3kA1EpEjdJZ/6rMSfOF3+F4=
|
||||
go.lsp.dev/jsonrpc2 v1.0.1/go.mod h1:WN+TesqrPumBmUdsuWyn9DHq3zl4i85L/qG0NLdj8qg=
|
||||
go.lsp.dev/protocol v1.0.1 h1:kLWUIk1v/deXxo1D0VO4avjZnfeuZ82mouJgJd/NbdU=
|
||||
|
|
@ -660,22 +655,22 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/exp v0.0.0-20260718201538-764159d718ef h1:LkZ48HFgy/TvhTI0bcWkjgFkgLyKUwcTbDjS0DUjw+A=
|
||||
golang.org/x/exp v0.0.0-20260718201538-764159d718ef/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20260611194520-c48552f49976 h1:GTD/WuaexTazIG/SxLOz4rEKZPDVilmVVC2nz4xhwfE=
|
||||
golang.org/x/exp/typeparams v0.0.0-20260611194520-c48552f49976/go.mod h1:PqrXSW65cXDZH0k4IeUbhmg/bcAZDbzNz3byBpKCsXo=
|
||||
golang.org/x/exp/typeparams v0.0.0-20260718201538-764159d718ef h1:HbgfnigKyfbszJsaFQE4IMPTcjdKZMNxyYlF00yVkf4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20260718201538-764159d718ef/go.mod h1:PqrXSW65cXDZH0k4IeUbhmg/bcAZDbzNz3byBpKCsXo=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
|
@ -686,8 +681,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -721,8 +716,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
|
|||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
|
@ -731,8 +726,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
|
||||
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
@ -744,8 +739,8 @@ golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0t
|
|||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
|
||||
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
|
|
@ -754,20 +749,16 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 h1:admdQBe8jR3VWhBsUrAOaF2Qw6K/+p5pSm1GN8+6Fw4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800/go.mod h1:FPk7EXUKMtImne7AmknoYjT4QXqKIzzRbeQIXzLk6fQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 h1:qEHAMpSaUhtD0p3NbEEI83HwNGFxEwaSJ1G9PLnCBZE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260724162435-b2f20204f0df h1:NsJx+hCSwIBI6+C4BuJIkb8xOG1M+nfQDsqIrQHT92k=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260724162435-b2f20204f0df/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df h1:O3ig1i5WDDzsVzRp+cCdgelT9vXnlnOFdlEeFtL4HCc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
|
|
|
|||
|
|
@ -1200,6 +1200,7 @@ func (api *oliveTinAPI) GetDiagnostics(ctx ctx.Context, req *connect.Request[api
|
|||
res := &apiv1.GetDiagnosticsResponse{
|
||||
SshFoundKey: installationinfo.Runtime.SshFoundKey,
|
||||
SshFoundConfig: installationinfo.Runtime.SshFoundConfig,
|
||||
ConfigIssues: api.buildConfigIssuesForUser(user),
|
||||
}
|
||||
return connect.NewResponse(res), nil
|
||||
}
|
||||
|
|
@ -1242,6 +1243,7 @@ func (api *oliveTinAPI) Init(ctx ctx.Context, req *connect.Request[apiv1.InitReq
|
|||
LoginRequired: loginRequired,
|
||||
AvailableThemes: discoverAvailableThemes(api.cfg),
|
||||
ShowNavigateOnStartIcons: api.cfg.ShowNavigateOnStartIcons,
|
||||
ConfigIssueCount: configIssueCountForUser(api, user),
|
||||
}
|
||||
|
||||
return connect.NewResponse(res), nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
||||
config "github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
)
|
||||
|
||||
func TestGetDiagnosticsReturnsConfigIssues(t *testing.T) {
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Deploy",
|
||||
ID: "deploy",
|
||||
Entity: "host",
|
||||
Arguments: []config.ActionArgument{
|
||||
{
|
||||
Name: "ansible_host",
|
||||
Type: "ascii_identifier",
|
||||
Default: `{{ host.name | default "All" }}`,
|
||||
},
|
||||
},
|
||||
ExecOnCron: []string{"0 1 * * *"},
|
||||
},
|
||||
}
|
||||
|
||||
testServer, client := getNewTestServerAndClient(cfg)
|
||||
defer testServer.Close()
|
||||
|
||||
res, err := client.GetDiagnostics(context.Background(), connect.NewRequest(&apiv1.GetDiagnosticsRequest{}))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res.Msg)
|
||||
require.NotEmpty(t, res.Msg.ConfigIssues)
|
||||
|
||||
codes := map[string]bool{}
|
||||
for _, issue := range res.Msg.ConfigIssues {
|
||||
codes[issue.Code] = true
|
||||
}
|
||||
assert.True(t, codes[configissues.CodeTemplateParse])
|
||||
assert.True(t, codes[configissues.CodeCronEntityBinding])
|
||||
|
||||
initRes, err := client.Init(context.Background(), connect.NewRequest(&apiv1.InitRequest{}))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int32(len(res.Msg.ConfigIssues)), initRes.Msg.ConfigIssueCount)
|
||||
}
|
||||
|
||||
func TestInitHidesConfigIssueCountWithoutDiagnostics(t *testing.T) {
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.DefaultPolicy.ShowDiagnostics = false
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Deploy",
|
||||
ID: "deploy",
|
||||
Entity: "host",
|
||||
Arguments: []config.ActionArgument{
|
||||
{
|
||||
Name: "ansible_host",
|
||||
Type: "ascii_identifier",
|
||||
Default: `{{ host.name | default "All" }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testServer, client := getNewTestServerAndClient(cfg)
|
||||
defer testServer.Close()
|
||||
|
||||
initRes, err := client.Init(context.Background(), connect.NewRequest(&apiv1.InitRequest{}))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int32(0), initRes.Msg.ConfigIssueCount)
|
||||
|
||||
_, err = client.GetDiagnostics(context.Background(), connect.NewRequest(&apiv1.GetDiagnosticsRequest{}))
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, connect.CodePermissionDenied, connect.CodeOf(err))
|
||||
}
|
||||
|
||||
func attachBrokenTemplateArgToSecretAction(cfg *config.Config) {
|
||||
for i := range cfg.Actions {
|
||||
if cfg.Actions[i].ID != "secret_action" {
|
||||
continue
|
||||
}
|
||||
cfg.Actions[i].Arguments = []config.ActionArgument{
|
||||
{
|
||||
Name: "target",
|
||||
Type: "ascii_identifier",
|
||||
Default: `{{ host.name | default "All" }}`,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func assertNoActionScopedConfigIssues(t *testing.T, issues []*apiv1.ConfigIssue) {
|
||||
t.Helper()
|
||||
for _, issue := range issues {
|
||||
assert.Empty(t, issue.ActionId,
|
||||
"user with view:false must not see action-scoped config issues")
|
||||
}
|
||||
}
|
||||
|
||||
func configIssuesContainActionID(issues []*apiv1.ConfigIssue, actionID string) bool {
|
||||
for _, issue := range issues {
|
||||
if issue.ActionId == actionID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestConfigIssuesHideActionsWithoutViewPermission(t *testing.T) {
|
||||
cfg, _, _ := buildViewPermissionTestConfig(t)
|
||||
cfg.AuthHttpHeaderUsername = "X-Ot-User"
|
||||
attachBrokenTemplateArgToSecretAction(cfg)
|
||||
|
||||
testServer, client := getNewTestServerAndClient(cfg)
|
||||
defer testServer.Close()
|
||||
|
||||
configissues.Report(configissues.Issue{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeEnvUnset,
|
||||
Message: "TEST_GLOBAL_ENV is not set",
|
||||
Source: "TEST_GLOBAL_ENV",
|
||||
})
|
||||
|
||||
lowReq := connect.NewRequest(&apiv1.GetDiagnosticsRequest{})
|
||||
lowReq.Header().Set("X-Ot-User", "low")
|
||||
lowRes, err := client.GetDiagnostics(context.Background(), lowReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertNoActionScopedConfigIssues(t, lowRes.Msg.ConfigIssues)
|
||||
require.NotEmpty(t, lowRes.Msg.ConfigIssues, "low user should still see global config issues")
|
||||
|
||||
lowInit := connect.NewRequest(&apiv1.InitRequest{})
|
||||
lowInit.Header().Set("X-Ot-User", "low")
|
||||
lowInitRes, err := client.Init(context.Background(), lowInit)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int32(len(lowRes.Msg.ConfigIssues)), lowInitRes.Msg.ConfigIssueCount)
|
||||
|
||||
adminReq := connect.NewRequest(&apiv1.GetDiagnosticsRequest{})
|
||||
adminReq.Header().Set("X-Ot-User", "admin")
|
||||
adminRes, err := client.GetDiagnostics(context.Background(), adminReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, configIssuesContainActionID(adminRes.Msg.ConfigIssues, "secret_action"),
|
||||
"admin must still see config issues for secret_action")
|
||||
assert.Greater(t, len(adminRes.Msg.ConfigIssues), len(lowRes.Msg.ConfigIssues))
|
||||
}
|
||||
|
||||
func TestConfigIssuesHideRuntimeWatcherFailuresWithoutViewPermission(t *testing.T) {
|
||||
cfg, _, _ := buildViewPermissionTestConfig(t)
|
||||
cfg.AuthHttpHeaderUsername = "X-Ot-User"
|
||||
|
||||
testServer, client := getNewTestServerAndClient(cfg)
|
||||
defer testServer.Close()
|
||||
|
||||
configissues.Report(configissues.Issue{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeWatcherPath,
|
||||
Message: `Could not create watcher for "/secret/path": permission denied`,
|
||||
ActionID: "secret_action",
|
||||
ActionTitle: "Secret Action",
|
||||
Source: "/secret/path",
|
||||
ConfigFile: "/etc/OliveTin/secret.yaml",
|
||||
})
|
||||
|
||||
lowReq := connect.NewRequest(&apiv1.GetDiagnosticsRequest{})
|
||||
lowReq.Header().Set("X-Ot-User", "low")
|
||||
lowRes, err := client.GetDiagnostics(context.Background(), lowReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, issue := range lowRes.Msg.ConfigIssues {
|
||||
assert.NotEqual(t, configissues.CodeWatcherPath, issue.Code,
|
||||
"user with view:false must not see action-scoped watcher_path issues")
|
||||
assert.NotContains(t, issue.Source, "/secret/path")
|
||||
}
|
||||
|
||||
adminReq := connect.NewRequest(&apiv1.GetDiagnosticsRequest{})
|
||||
adminReq.Header().Set("X-Ot-User", "admin")
|
||||
adminRes, err := client.GetDiagnostics(context.Background(), adminReq)
|
||||
require.NoError(t, err)
|
||||
|
||||
foundWatcher := false
|
||||
for _, issue := range adminRes.Msg.ConfigIssues {
|
||||
if issue.Code == configissues.CodeWatcherPath && issue.ActionId == "secret_action" {
|
||||
foundWatcher = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, foundWatcher, "admin must see action-scoped watcher_path issues")
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
||||
authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
|
||||
"github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
)
|
||||
|
||||
func configIssueCountForUser(api *oliveTinAPI, user *authpublic.AuthenticatedUser) int32 {
|
||||
return int32(len(api.buildConfigIssuesForUser(user)))
|
||||
}
|
||||
|
||||
func (api *oliveTinAPI) buildConfigIssuesForUser(user *authpublic.AuthenticatedUser) []*apiv1.ConfigIssue {
|
||||
if !userCanSeeDiagnostics(user) {
|
||||
return []*apiv1.ConfigIssue{}
|
||||
}
|
||||
|
||||
return filterConfigIssuesForUser(api, user, configissues.List())
|
||||
}
|
||||
|
||||
func userCanSeeDiagnostics(user *authpublic.AuthenticatedUser) bool {
|
||||
return user != nil && user.EffectivePolicy != nil && user.EffectivePolicy.ShowDiagnostics
|
||||
}
|
||||
|
||||
func filterConfigIssuesForUser(api *oliveTinAPI, user *authpublic.AuthenticatedUser, src []configissues.Issue) []*apiv1.ConfigIssue {
|
||||
out := make([]*apiv1.ConfigIssue, 0, len(src))
|
||||
for _, issue := range src {
|
||||
if api.includeConfigIssueForUser(user, issue) {
|
||||
out = append(out, toProtoConfigIssue(issue))
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (api *oliveTinAPI) includeConfigIssueForUser(user *authpublic.AuthenticatedUser, issue configissues.Issue) bool {
|
||||
if issue.ActionID == "" {
|
||||
return true
|
||||
}
|
||||
return api.canViewActionID(user, issue.ActionID)
|
||||
}
|
||||
|
||||
func (api *oliveTinAPI) canViewActionID(user *authpublic.AuthenticatedUser, actionID string) bool {
|
||||
action := findActionInList(api.cfg.Actions, actionID)
|
||||
if action == nil {
|
||||
return false
|
||||
}
|
||||
return api.userCanViewAction(user, action)
|
||||
}
|
||||
|
||||
func findActionInList(actions []*config.Action, actionID string) *config.Action {
|
||||
for _, action := range actions {
|
||||
if actionHasID(action, actionID) {
|
||||
return action
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionHasID(action *config.Action, actionID string) bool {
|
||||
return action != nil && action.ID == actionID
|
||||
}
|
||||
|
||||
func toProtoConfigIssue(issue configissues.Issue) *apiv1.ConfigIssue {
|
||||
return &apiv1.ConfigIssue{
|
||||
Severity: issue.Severity,
|
||||
Code: issue.Code,
|
||||
Message: issue.Message,
|
||||
ActionId: issue.ActionID,
|
||||
ActionTitle: issue.ActionTitle,
|
||||
ArgumentName: issue.ArgumentName,
|
||||
Source: issue.Source,
|
||||
ConfigFile: issue.ConfigFile,
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,8 @@ type Action struct {
|
|||
EnabledExpression string `koanf:"enabledExpression"`
|
||||
Groups []string `koanf:"groups"`
|
||||
Justification string `koanf:"justification"`
|
||||
// SourceFile is set by OliveTin when loading config (not user YAML).
|
||||
SourceFile string `koanf:"-"`
|
||||
}
|
||||
|
||||
func (action *Action) RequiresJustification() bool {
|
||||
|
|
@ -110,6 +112,8 @@ type EntityFile struct {
|
|||
Name string `koanf:"name"`
|
||||
Icon string `koanf:"icon"`
|
||||
Properties []EntityProperty `koanf:"properties"`
|
||||
// SourceFile is set by OliveTin when loading config (not user YAML).
|
||||
SourceFile string `koanf:"-"`
|
||||
}
|
||||
|
||||
// EntityProperty defines a column shown when listing entity instances in the UI.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
|
@ -8,6 +9,7 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
|
|
@ -40,12 +42,15 @@ func AppendSource(cfg *Config, k *koanf.Koanf, configPath string) {
|
|||
"configPath": configPath,
|
||||
}).Info("Appending cfg source")
|
||||
|
||||
configissues.BeginConfigLoad()
|
||||
stampLoadedConfigSources(k, configPath)
|
||||
loadIncludedConfigsFromDir(k, configPath)
|
||||
|
||||
if !unmarshalRoot(k, cfg) {
|
||||
return
|
||||
}
|
||||
|
||||
applyStampedSourceFiles(k, cfg)
|
||||
afterLoadFinalize(cfg, configPath)
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +177,7 @@ func listYamlFiles(includePath string) ([]string, bool) {
|
|||
func loadAndMergeIncludedFile(k *koanf.Koanf, includePath, filename string) {
|
||||
filePath := filepath.Join(includePath, filename)
|
||||
|
||||
if err := k.Load(file.Provider(filePath), yaml.Parser(), koanf.WithMergeFunc(mergeFunc)); err != nil {
|
||||
if err := k.Load(file.Provider(filePath), yaml.Parser(), koanf.WithMergeFunc(mergeFuncForSource(filePath))); err != nil {
|
||||
log.Errorf("Error loading included config file %s: %v", filePath, err)
|
||||
return
|
||||
}
|
||||
|
|
@ -182,8 +187,15 @@ func loadAndMergeIncludedFile(k *koanf.Koanf, includePath, filename string) {
|
|||
}).Info("Successfully loaded included config file")
|
||||
}
|
||||
|
||||
func mergeFuncForSource(sourceFile string) func(src, dest map[string]interface{}) error {
|
||||
return func(src map[string]interface{}, dest map[string]interface{}) error {
|
||||
return mergeFunc(src, dest, sourceFile)
|
||||
}
|
||||
}
|
||||
|
||||
// mergeActionsWhenBothExist merges actions when both src and dest have actions.
|
||||
func mergeActionsWhenBothExist(srcActions interface{}, destActions interface{}, dest map[string]interface{}) {
|
||||
func mergeActionsWhenBothExist(srcActions interface{}, destActions interface{}, dest map[string]interface{}, sourceFile string) {
|
||||
stampSourceOnMaps(srcActions, sourceFile)
|
||||
srcSlice, ok1 := srcActions.([]interface{})
|
||||
destSlice, ok2 := destActions.([]interface{})
|
||||
if ok1 && ok2 {
|
||||
|
|
@ -194,10 +206,11 @@ func mergeActionsWhenBothExist(srcActions interface{}, destActions interface{},
|
|||
}
|
||||
|
||||
// mergeActionsFromSource merges actions from source into destination.
|
||||
func mergeActionsFromSource(srcActions interface{}, dest map[string]interface{}) {
|
||||
func mergeActionsFromSource(srcActions interface{}, dest map[string]interface{}, sourceFile string) {
|
||||
if destActions, ok := dest["actions"]; ok {
|
||||
mergeActionsWhenBothExist(srcActions, destActions, dest)
|
||||
mergeActionsWhenBothExist(srcActions, destActions, dest, sourceFile)
|
||||
} else {
|
||||
stampSourceOnMaps(srcActions, sourceFile)
|
||||
dest["actions"] = srcActions
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +236,8 @@ func mergeDashboardsFromSource(srcDashboards interface{}, dest map[string]interf
|
|||
}
|
||||
|
||||
// mergeEntitiesWhenBothExist merges entities when both src and dest have entities.
|
||||
func mergeEntitiesWhenBothExist(srcEntities interface{}, destEntities interface{}, dest map[string]interface{}) {
|
||||
func mergeEntitiesWhenBothExist(srcEntities interface{}, destEntities interface{}, dest map[string]interface{}, sourceFile string) {
|
||||
stampSourceOnMaps(srcEntities, sourceFile)
|
||||
srcSlice, ok1 := srcEntities.([]interface{})
|
||||
destSlice, ok2 := destEntities.([]interface{})
|
||||
if ok1 && ok2 {
|
||||
|
|
@ -234,17 +248,18 @@ func mergeEntitiesWhenBothExist(srcEntities interface{}, destEntities interface{
|
|||
}
|
||||
|
||||
// mergeEntitiesFromSource merges entities from source into destination.
|
||||
func mergeEntitiesFromSource(srcEntities interface{}, dest map[string]interface{}) {
|
||||
func mergeEntitiesFromSource(srcEntities interface{}, dest map[string]interface{}, sourceFile string) {
|
||||
if destEntities, ok := dest["entities"]; ok {
|
||||
mergeEntitiesWhenBothExist(srcEntities, destEntities, dest)
|
||||
mergeEntitiesWhenBothExist(srcEntities, destEntities, dest, sourceFile)
|
||||
} else {
|
||||
stampSourceOnMaps(srcEntities, sourceFile)
|
||||
dest["entities"] = srcEntities
|
||||
}
|
||||
}
|
||||
|
||||
func mergeFunc(src map[string]interface{}, dest map[string]interface{}) error {
|
||||
func mergeFunc(src map[string]interface{}, dest map[string]interface{}, sourceFile string) error {
|
||||
if srcActions, ok := src["actions"]; ok {
|
||||
mergeActionsFromSource(srcActions, dest)
|
||||
mergeActionsFromSource(srcActions, dest, sourceFile)
|
||||
}
|
||||
|
||||
if srcDashboards, ok := src["dashboards"]; ok {
|
||||
|
|
@ -252,7 +267,7 @@ func mergeFunc(src map[string]interface{}, dest map[string]interface{}) error {
|
|||
}
|
||||
|
||||
if srcEntities, ok := src["entities"]; ok {
|
||||
mergeEntitiesFromSource(srcEntities, dest)
|
||||
mergeEntitiesFromSource(srcEntities, dest, sourceFile)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -285,7 +300,12 @@ func envDecodeHookFunc(from reflect.Type, to reflect.Type, data any) (any, error
|
|||
val, set := os.LookupEnv(key)
|
||||
log.Debugf("Environment variable %q: set=%v, value=%q", key, set, val)
|
||||
if !set {
|
||||
log.Warnf("Config file references unset environment variable: \"%s\"", key)
|
||||
configissues.ReportSticky(configissues.Issue{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeEnvUnset,
|
||||
Message: fmt.Sprintf("Config file references unset environment variable %q", key),
|
||||
Source: key,
|
||||
})
|
||||
}
|
||||
return val
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/OliveTin/OliveTin/internal/env"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
|
@ -29,7 +30,6 @@ func (cfg *Config) Sanitize() {
|
|||
cfg.sanitizeDashboardsForInlineActions()
|
||||
|
||||
cfg.sanitizeActionGroups()
|
||||
cfg.sanitizeActionGroupReferences()
|
||||
cfg.sanitizeEntities()
|
||||
|
||||
if err := cfg.validateReservedActionArgumentNames(); err != nil {
|
||||
|
|
@ -238,7 +238,7 @@ func (action *Action) sanitize(cfg *Config) {
|
|||
action.Groups = dedupeStrings(action.Groups)
|
||||
|
||||
for idx := range action.Arguments {
|
||||
action.Arguments[idx].sanitize()
|
||||
action.Arguments[idx].sanitize(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -283,14 +283,6 @@ func (cfg *Config) sanitizeActionGroups() {
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeActionGroupReferences() {
|
||||
for _, action := range cfg.Actions {
|
||||
for _, groupName := range action.Groups {
|
||||
cfg.warnInvalidActionGroupReference(action, groupName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeEntities() {
|
||||
for _, entityFile := range cfg.Entities {
|
||||
if entityFile == nil {
|
||||
|
|
@ -310,24 +302,6 @@ func sanitizeEntityProperties(entityFile *EntityFile) {
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) warnInvalidActionGroupReference(action *Action, groupName string) {
|
||||
group, found := cfg.ActionGroups[groupName]
|
||||
if !found {
|
||||
log.WithFields(log.Fields{
|
||||
"actionTitle": action.Title,
|
||||
"groupName": groupName,
|
||||
}).Warn("Action references unknown action group")
|
||||
return
|
||||
}
|
||||
|
||||
if group == nil || group.MaxConcurrent < 1 {
|
||||
log.WithFields(log.Fields{
|
||||
"actionTitle": action.Title,
|
||||
"groupName": groupName,
|
||||
}).Warn("Action references action group that will not be enforced at runtime")
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) sanitizeAuthRequireGuestsToLogin() {
|
||||
if cfg.AuthRequireGuestsToLogin {
|
||||
log.Infof("AuthRequireGuestsToLogin is enabled. All defaultPermissions will be set to false")
|
||||
|
|
@ -505,7 +479,7 @@ func (cfg *Config) sanitizeOnClickDefaults() {
|
|||
cfg.DefaultPopupOnStart = cfg.DefaultOnClick
|
||||
}
|
||||
|
||||
func (arg *ActionArgument) sanitize() {
|
||||
func (arg *ActionArgument) sanitize(action *Action) {
|
||||
if arg.Title == "" {
|
||||
arg.Title = arg.Name
|
||||
}
|
||||
|
|
@ -516,45 +490,24 @@ func (arg *ActionArgument) sanitize() {
|
|||
}
|
||||
}
|
||||
|
||||
arg.sanitizeNoType()
|
||||
arg.sanitizeChecklist()
|
||||
|
||||
// Default value validation runs in executor at config load (validateArgumentDefaults).
|
||||
arg.sanitizeNoType(action)
|
||||
// Checklist configuration problems are reported by configcheck.
|
||||
}
|
||||
|
||||
func (arg *ActionArgument) sanitizeChecklist() {
|
||||
if arg.Type != "checklist" {
|
||||
return
|
||||
}
|
||||
|
||||
arg.warnMissingChecklistChoices()
|
||||
arg.warnInvalidChecklistEntityTemplate()
|
||||
}
|
||||
|
||||
func (arg *ActionArgument) warnMissingChecklistChoices() {
|
||||
if len(arg.Choices) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"arg": arg.Name,
|
||||
}).Warn("Checklist argument has no choices defined")
|
||||
}
|
||||
}
|
||||
|
||||
func (arg *ActionArgument) warnInvalidChecklistEntityTemplate() {
|
||||
if arg.Entity == "" || len(arg.Choices) == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"arg": arg.Name,
|
||||
"entity": arg.Entity,
|
||||
}).Warn("Checklist argument with entity should define exactly one choice template")
|
||||
}
|
||||
|
||||
func (arg *ActionArgument) sanitizeNoType() {
|
||||
func (arg *ActionArgument) sanitizeNoType(action *Action) {
|
||||
if len(arg.Choices) == 0 && arg.Type == "" {
|
||||
log.WithFields(log.Fields{
|
||||
"arg": arg.Name,
|
||||
}).Warn("Argument type isn't set, will default to 'ascii' but this may not be safe. You should set a type specifically.")
|
||||
issue := configissues.Issue{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeArgTypeUnset,
|
||||
Message: "Argument type isn't set, will default to 'ascii' but this may not be safe",
|
||||
ArgumentName: arg.Name,
|
||||
}
|
||||
if action != nil {
|
||||
issue.ActionID = action.ID
|
||||
issue.ActionTitle = action.Title
|
||||
issue.ConfigFile = action.SourceFile
|
||||
}
|
||||
configissues.ReportSticky(issue)
|
||||
arg.Type = "ascii"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/knadh/koanf/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const sourceFileKey = "x-olivetin-source-file"
|
||||
|
||||
// stampSourceOnMaps sets the OliveTin source-file marker on each map in a
|
||||
// koanf slice value (actions or entities). Always overwrites so user-provided
|
||||
// x-olivetin-source-file values cannot spoof the real config path.
|
||||
func stampSourceOnMaps(raw any, sourceFile string) any {
|
||||
if sourceFile == "" {
|
||||
return raw
|
||||
}
|
||||
|
||||
items, ok := raw.([]interface{})
|
||||
if !ok {
|
||||
return raw
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
stampSourceOnMap(item, sourceFile)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func stampSourceOnMap(item any, sourceFile string) {
|
||||
m, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
m[sourceFileKey] = sourceFile
|
||||
}
|
||||
|
||||
func stampLoadedConfigSources(k *koanf.Koanf, configPath string) {
|
||||
stampConfigKey(k, "actions", configPath)
|
||||
stampConfigKey(k, "entities", configPath)
|
||||
}
|
||||
|
||||
func stampConfigKey(k *koanf.Koanf, key, configPath string) {
|
||||
if err := k.Set(key, stampSourceOnMaps(k.Get(key), configPath)); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"key": key,
|
||||
"configPath": configPath,
|
||||
}).Errorf("Failed to persist source stamps: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// applyStampedSourceFiles copies stamped source paths from koanf maps onto
|
||||
// unmarshaled actions/entities. SourceFile uses koanf:"-" so YAML cannot set it.
|
||||
func applyStampedSourceFiles(k *koanf.Koanf, cfg *Config) {
|
||||
actionPaths := stampedSourcePaths(k.Get("actions"))
|
||||
for i, action := range cfg.Actions {
|
||||
if action != nil {
|
||||
action.SourceFile = sourcePathAt(actionPaths, i)
|
||||
}
|
||||
}
|
||||
|
||||
entityPaths := stampedSourcePaths(k.Get("entities"))
|
||||
for i, entity := range cfg.Entities {
|
||||
if entity != nil {
|
||||
entity.SourceFile = sourcePathAt(entityPaths, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sourcePathAt(paths []string, index int) string {
|
||||
if index >= len(paths) {
|
||||
return ""
|
||||
}
|
||||
return paths[index]
|
||||
}
|
||||
|
||||
func stampedSourcePaths(raw any) []string {
|
||||
items, ok := raw.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
paths := make([]string, len(items))
|
||||
for i, item := range items {
|
||||
paths[i] = stampedSourceFromMap(item)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func stampedSourceFromMap(item any) string {
|
||||
m, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
path, _ := m[sourceFileKey].(string)
|
||||
return path
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package config_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/knadh/koanf/providers/file"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAppendSourceStampsActionSourceFiles(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
includeDir := filepath.Join(dir, "config.d")
|
||||
require.NoError(t, os.Mkdir(includeDir, 0o755))
|
||||
|
||||
basePath := filepath.Join(dir, "config.yaml")
|
||||
require.NoError(t, os.WriteFile(basePath, []byte(`
|
||||
include: config.d
|
||||
actions:
|
||||
- title: From base
|
||||
shell: echo base
|
||||
`), 0o644))
|
||||
|
||||
includePath := filepath.Join(includeDir, "10-extra.yaml")
|
||||
require.NoError(t, os.WriteFile(includePath, []byte(`
|
||||
actions:
|
||||
- title: From include
|
||||
shell: echo include
|
||||
entities:
|
||||
- name: host
|
||||
file: hosts.yaml
|
||||
`), 0o644))
|
||||
|
||||
k := koanf.New(".")
|
||||
require.NoError(t, k.Load(file.Provider(basePath), yaml.Parser()))
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
config.AppendSource(cfg, k, basePath)
|
||||
|
||||
require.Len(t, cfg.Actions, 2)
|
||||
assert.Equal(t, basePath, cfg.Actions[0].SourceFile)
|
||||
assert.Equal(t, includePath, cfg.Actions[1].SourceFile)
|
||||
|
||||
require.Len(t, cfg.Entities, 1)
|
||||
assert.Equal(t, includePath, cfg.Entities[0].SourceFile)
|
||||
}
|
||||
|
||||
func TestAppendSourceIgnoresUserProvidedSourceFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
basePath := filepath.Join(dir, "config.yaml")
|
||||
require.NoError(t, os.WriteFile(basePath, []byte(`
|
||||
actions:
|
||||
- title: Spoofed
|
||||
shell: echo hi
|
||||
x-olivetin-source-file: /tmp/fake-user-path.yaml
|
||||
entities:
|
||||
- name: host
|
||||
file: hosts.yaml
|
||||
x-olivetin-source-file: /tmp/fake-entity-path.yaml
|
||||
`), 0o644))
|
||||
|
||||
k := koanf.New(".")
|
||||
require.NoError(t, k.Load(file.Provider(basePath), yaml.Parser()))
|
||||
|
||||
cfg := config.DefaultConfig()
|
||||
config.AppendSource(cfg, k, basePath)
|
||||
|
||||
require.Len(t, cfg.Actions, 1)
|
||||
assert.Equal(t, basePath, cfg.Actions[0].SourceFile)
|
||||
assert.NotEqual(t, "/tmp/fake-user-path.yaml", cfg.Actions[0].SourceFile)
|
||||
|
||||
require.Len(t, cfg.Entities, 1)
|
||||
assert.Equal(t, basePath, cfg.Entities[0].SourceFile)
|
||||
assert.NotEqual(t, "/tmp/fake-entity-path.yaml", cfg.Entities[0].SourceFile)
|
||||
}
|
||||
|
|
@ -0,0 +1,486 @@
|
|||
package configcheck
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/OliveTin/OliveTin/internal/entities"
|
||||
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||
"github.com/robfig/cron/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Rebuild clears and rebuilds the issue list from the current config, entity
|
||||
// state, sticky load-time reports, and any extra issues provided by callers
|
||||
// (for example literal argument default validation from the executor).
|
||||
func Rebuild(cfg *config.Config, extra ...configissues.Issue) {
|
||||
if cfg == nil {
|
||||
configissues.Replace(extra)
|
||||
return
|
||||
}
|
||||
|
||||
collected := make([]configissues.Issue, 0)
|
||||
collected = append(collected, configissues.CopySticky()...)
|
||||
collected = append(collected, collectActionGroupIssues(cfg)...)
|
||||
collected = append(collected, collectArgumentIssues(cfg)...)
|
||||
collected = append(collected, collectIncludeIssues(cfg)...)
|
||||
collected = append(collected, collectTemplateParseIssues(cfg)...)
|
||||
collected = append(collected, collectEntityFileIssues(cfg)...)
|
||||
collected = append(collected, collectEntityEmptyIssues(cfg)...)
|
||||
collected = append(collected, collectCronIssues(cfg)...)
|
||||
collected = append(collected, collectWatcherPathIssues(cfg)...)
|
||||
collected = append(collected, extra...)
|
||||
configissues.Replace(collected)
|
||||
}
|
||||
|
||||
func collectActionGroupIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, action := range cfg.Actions {
|
||||
if action == nil {
|
||||
continue
|
||||
}
|
||||
for _, groupName := range action.Groups {
|
||||
out = append(out, actionGroupIssue(cfg, action, groupName)...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func actionGroupIssue(cfg *config.Config, action *config.Action, groupName string) []configissues.Issue {
|
||||
group, found := cfg.ActionGroups[groupName]
|
||||
if !found {
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityWarning, configissues.CodeActionGroupUnknown,
|
||||
fmt.Sprintf("Action references unknown action group %q", groupName), groupName, "")}
|
||||
}
|
||||
|
||||
if group == nil || group.MaxConcurrent < 1 {
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityWarning, configissues.CodeActionGroupUnenforced,
|
||||
fmt.Sprintf("Action references action group %q that will not be enforced at runtime", groupName), groupName, "")}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionIssue(action *config.Action, severity, code, message, source, argName string) configissues.Issue {
|
||||
return configissues.Issue{
|
||||
Severity: severity,
|
||||
Code: code,
|
||||
Message: message,
|
||||
ActionID: action.ID,
|
||||
ActionTitle: action.Title,
|
||||
ArgumentName: argName,
|
||||
Source: source,
|
||||
ConfigFile: action.SourceFile,
|
||||
}
|
||||
}
|
||||
|
||||
func collectArgumentIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, action := range cfg.Actions {
|
||||
if action == nil {
|
||||
continue
|
||||
}
|
||||
for i := range action.Arguments {
|
||||
out = append(out, argumentIssues(action, &action.Arguments[i])...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func argumentIssues(action *config.Action, arg *config.ActionArgument) []configissues.Issue {
|
||||
if arg.Type != "checklist" {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]configissues.Issue, 0)
|
||||
out = append(out, checklistNoChoicesIssue(action, arg)...)
|
||||
out = append(out, checklistEntityChoicesIssue(action, arg)...)
|
||||
return out
|
||||
}
|
||||
|
||||
func checklistNoChoicesIssue(action *config.Action, arg *config.ActionArgument) []configissues.Issue {
|
||||
if len(arg.Choices) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityWarning, configissues.CodeChecklistNoChoices,
|
||||
"Checklist argument has no choices defined", "", arg.Name)}
|
||||
}
|
||||
|
||||
func checklistEntityChoicesIssue(action *config.Action, arg *config.ActionArgument) []configissues.Issue {
|
||||
if arg.Entity == "" || len(arg.Choices) <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityWarning, configissues.CodeChecklistEntityChoices,
|
||||
"Checklist argument with entity should define exactly one choice template", arg.Entity, arg.Name)}
|
||||
}
|
||||
|
||||
func collectIncludeIssues(cfg *config.Config) []configissues.Issue {
|
||||
if cfg.Include == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
includePath := cfg.Include
|
||||
if !filepath.IsAbs(includePath) {
|
||||
includePath = filepath.Join(cfg.GetDir(), cfg.Include)
|
||||
}
|
||||
info, err := os.Stat(includePath)
|
||||
if err != nil {
|
||||
return []configissues.Issue{{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeIncludeMissing,
|
||||
Message: fmt.Sprintf("Include directory not found: %s", includePath),
|
||||
Source: includePath,
|
||||
ConfigFile: includePath,
|
||||
}}
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return []configissues.Issue{{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeIncludeMissing,
|
||||
Message: fmt.Sprintf("Include path is not a directory: %s", includePath),
|
||||
Source: includePath,
|
||||
ConfigFile: includePath,
|
||||
}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectTemplateParseIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, action := range cfg.Actions {
|
||||
if action == nil {
|
||||
continue
|
||||
}
|
||||
for i := range action.Arguments {
|
||||
arg := &action.Arguments[i]
|
||||
out = append(out, templateIssuesForArg(action, arg)...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func templateIssuesForArg(action *config.Action, arg *config.ActionArgument) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
|
||||
if looksLikeTemplate(arg.Default) {
|
||||
if err := tpl.CheckActionTemplateParses(arg.Default); err != nil {
|
||||
out = append(out, actionIssue(action, configissues.SeverityError, configissues.CodeTemplateParse,
|
||||
fmt.Sprintf("Argument default template failed to parse: %v", err), arg.Default, arg.Name))
|
||||
}
|
||||
}
|
||||
|
||||
for _, choice := range arg.Choices {
|
||||
out = append(out, templateIssueForChoice(action, arg, choice.Title, "title")...)
|
||||
out = append(out, templateIssueForChoice(action, arg, choice.Value, "value")...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func templateIssueForChoice(action *config.Action, arg *config.ActionArgument, value, field string) []configissues.Issue {
|
||||
if !looksLikeTemplate(value) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := tpl.CheckActionTemplateParses(value); err != nil {
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityError, configissues.CodeTemplateParse,
|
||||
fmt.Sprintf("Argument choice %s template failed to parse: %v", field, err), value, arg.Name)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func looksLikeTemplate(value string) bool {
|
||||
return strings.Contains(value, "{{")
|
||||
}
|
||||
|
||||
func collectEntityFileIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
baseDir := entities.ResolveEntitiesBaseDir(cfg.GetDir())
|
||||
|
||||
for _, ef := range cfg.Entities {
|
||||
out = append(out, entityFileIssuesFor(ef, baseDir)...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func entityFileIssuesFor(ef *config.EntityFile, baseDir string) []configissues.Issue {
|
||||
if ef == nil || ef.File == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
path := resolveEntityPath(ef.File, baseDir)
|
||||
issue := entityFileIssue(ef, path)
|
||||
if issue == nil {
|
||||
return nil
|
||||
}
|
||||
return []configissues.Issue{*issue}
|
||||
}
|
||||
|
||||
func resolveEntityPath(file, baseDir string) string {
|
||||
if filepath.IsAbs(file) {
|
||||
return file
|
||||
}
|
||||
return filepath.Join(baseDir, file)
|
||||
}
|
||||
|
||||
func entityFileIssue(ef *config.EntityFile, path string) *configissues.Issue {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return entityReadIssue(ef, path, err)
|
||||
}
|
||||
|
||||
if len(strings.TrimSpace(string(data))) == 0 {
|
||||
return &configissues.Issue{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeEntityFile,
|
||||
Message: fmt.Sprintf("Entity %q file is empty", ef.Name),
|
||||
Source: path,
|
||||
ConfigFile: ef.SourceFile,
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(path, ".json") {
|
||||
return entityJSONParseIssue(ef, path, data)
|
||||
}
|
||||
|
||||
return entityYAMLParseIssue(ef, path, data)
|
||||
}
|
||||
|
||||
func entityReadIssue(ef *config.EntityFile, path string, err error) *configissues.Issue {
|
||||
return &configissues.Issue{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeEntityFile,
|
||||
Message: fmt.Sprintf("Entity %q file could not be read: %v", ef.Name, err),
|
||||
Source: path,
|
||||
ConfigFile: ef.SourceFile,
|
||||
}
|
||||
}
|
||||
|
||||
func entityJSONParseIssue(ef *config.EntityFile, path string, data []byte) *configissues.Issue {
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
for decoder.More() {
|
||||
d := make(map[string]any)
|
||||
if err := decoder.Decode(&d); err != nil {
|
||||
return &configissues.Issue{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeEntityFile,
|
||||
Message: fmt.Sprintf("Entity %q file could not be parsed as JSON: %v", ef.Name, err),
|
||||
Source: path,
|
||||
ConfigFile: ef.SourceFile,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func entityYAMLParseIssue(ef *config.EntityFile, path string, data []byte) *configissues.Issue {
|
||||
var parsed []map[string]any
|
||||
if err := yaml.Unmarshal(data, &parsed); err != nil {
|
||||
return &configissues.Issue{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeEntityFile,
|
||||
Message: fmt.Sprintf("Entity %q file could not be parsed as YAML: %v", ef.Name, err),
|
||||
Source: path,
|
||||
ConfigFile: ef.SourceFile,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectEntityEmptyIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, action := range cfg.Actions {
|
||||
out = append(out, entityEmptyIssueFor(cfg, action)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func entityEmptyIssueFor(cfg *config.Config, action *config.Action) []configissues.Issue {
|
||||
if !shouldReportEntityEmpty(cfg, action) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityWarning, configissues.CodeEntityEmpty,
|
||||
fmt.Sprintf("Entity-bound action has no instances of entity %q", action.Entity), action.Entity, "")}
|
||||
}
|
||||
|
||||
func shouldReportEntityEmpty(cfg *config.Config, action *config.Action) bool {
|
||||
if action == nil || action.Entity == "" {
|
||||
return false
|
||||
}
|
||||
if len(entities.GetEntityInstances(action.Entity)) > 0 {
|
||||
return false
|
||||
}
|
||||
return entityEmptyLoadGatePassed(cfg, action.Entity)
|
||||
}
|
||||
|
||||
func entityEmptyLoadGatePassed(cfg *config.Config, entityName string) bool {
|
||||
// Avoid startup false positives: entity files load after the first action-map rebuild.
|
||||
if entityTypeConfigured(cfg, entityName) && !entities.HasEntityLoadAttempted(entityName) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func entityTypeConfigured(cfg *config.Config, entityName string) bool {
|
||||
for _, ef := range cfg.Entities {
|
||||
if ef != nil && ef.Name == entityName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func collectCronIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
parser := cronParser(cfg)
|
||||
|
||||
for _, action := range cfg.Actions {
|
||||
out = append(out, cronIssuesForAction(action, parser)...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func cronIssuesForAction(action *config.Action, parser cron.Parser) []configissues.Issue {
|
||||
if action == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]configissues.Issue, 0)
|
||||
out = append(out, cronEntityBindingIssue(action)...)
|
||||
out = append(out, cronScheduleIssues(action, parser)...)
|
||||
return out
|
||||
}
|
||||
|
||||
func cronEntityBindingIssue(action *config.Action) []configissues.Issue {
|
||||
if action.Entity == "" || len(action.ExecOnCron) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityWarning, configissues.CodeCronEntityBinding,
|
||||
"Entity-bound action uses execOnCron; cron runs without an entity binding and will not execute", action.Entity, "")}
|
||||
}
|
||||
|
||||
func cronScheduleIssues(action *config.Action, parser cron.Parser) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, cronline := range action.ExecOnCron {
|
||||
out = append(out, cronScheduleIssue(action, parser, cronline)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func cronScheduleIssue(action *config.Action, parser cron.Parser, cronline string) []configissues.Issue {
|
||||
_, err := parser.Parse(cronline)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []configissues.Issue{actionIssue(action, configissues.SeverityError, configissues.CodeCronInvalid,
|
||||
fmt.Sprintf("Invalid cron schedule %q: %v", cronline, err), cronline, "")}
|
||||
}
|
||||
|
||||
func cronParser(cfg *config.Config) cron.Parser {
|
||||
if cfg.CronSupportForSeconds {
|
||||
return cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||||
}
|
||||
return cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||||
}
|
||||
|
||||
func collectWatcherPathIssues(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, action := range cfg.Actions {
|
||||
out = append(out, watcherPathIssuesForAction(action)...)
|
||||
}
|
||||
out = append(out, watcherPathIssuesForEntities(cfg)...)
|
||||
return out
|
||||
}
|
||||
|
||||
func watcherPathIssuesForAction(action *config.Action) []configissues.Issue {
|
||||
if action == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]configissues.Issue, 0)
|
||||
out = append(out, watcherCreatedDirIssues(action)...)
|
||||
out = append(out, watcherChangedDirIssues(action)...)
|
||||
out = append(out, watcherCalendarFileIssues(action)...)
|
||||
return out
|
||||
}
|
||||
|
||||
func watcherCreatedDirIssues(action *config.Action) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, dir := range action.ExecOnFileCreatedInDir {
|
||||
out = append(out, watcherDirIssue(action, dir)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func watcherChangedDirIssues(action *config.Action) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, dir := range action.ExecOnFileChangedInDir {
|
||||
out = append(out, watcherDirIssue(action, dir)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func watcherCalendarFileIssues(action *config.Action) []configissues.Issue {
|
||||
return watcherFileIssue(action, action.ExecOnCalendarFile)
|
||||
}
|
||||
|
||||
func watcherPathIssuesForEntities(cfg *config.Config) []configissues.Issue {
|
||||
out := make([]configissues.Issue, 0)
|
||||
baseDir := entities.ResolveEntitiesBaseDir(cfg.GetDir())
|
||||
|
||||
for _, ef := range cfg.Entities {
|
||||
if ef == nil || ef.File == "" {
|
||||
continue
|
||||
}
|
||||
path := resolveEntityPath(ef.File, baseDir)
|
||||
out = append(out, missingWatchDirIssue(path, filepath.Dir(path), ef.SourceFile, "", "")...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func watcherDirIssue(action *config.Action, dir string) []configissues.Issue {
|
||||
if dir == "" {
|
||||
return nil
|
||||
}
|
||||
return missingWatchDirIssue(dir, dir, action.SourceFile, action.ID, action.Title)
|
||||
}
|
||||
|
||||
func watcherFileIssue(action *config.Action, filePath string) []configissues.Issue {
|
||||
if filePath == "" {
|
||||
return nil
|
||||
}
|
||||
return missingWatchDirIssue(filePath, filepath.Dir(filePath), action.SourceFile, action.ID, action.Title)
|
||||
}
|
||||
|
||||
func missingWatchDirIssue(displayPath, dirToStat, configFile, actionID, actionTitle string) []configissues.Issue {
|
||||
_, err := os.Stat(dirToStat)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []configissues.Issue{{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeWatcherPath,
|
||||
Message: fmt.Sprintf("Could not create watcher for %q: %v", displayPath, err),
|
||||
ActionID: actionID,
|
||||
ActionTitle: actionTitle,
|
||||
Source: displayPath,
|
||||
ConfigFile: configFile,
|
||||
}}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package configcheck_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configcheck"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/OliveTin/OliveTin/internal/entities"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRebuildTemplateParseDefault(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Deploy",
|
||||
ID: "deploy",
|
||||
Entity: "host",
|
||||
Arguments: []config.ActionArgument{
|
||||
{
|
||||
Name: "ansible_host",
|
||||
Type: "ascii_identifier",
|
||||
Default: `{{ host.name | default "All" }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
issues := configissues.List()
|
||||
require.NotEmpty(t, issues)
|
||||
assert.True(t, hasCode(issues, configissues.CodeTemplateParse))
|
||||
assert.True(t, hasCode(issues, configissues.CodeEntityEmpty))
|
||||
assert.False(t, hasCode(issues, configissues.CodeCronEntityBinding))
|
||||
}
|
||||
|
||||
func TestRebuildValidTemplateDefault(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Sleep",
|
||||
ID: "sleep",
|
||||
Arguments: []config.ActionArgument{
|
||||
{
|
||||
Name: "ssh_host",
|
||||
Type: "ascii_identifier",
|
||||
Default: `{{ host.hostname }}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
assert.False(t, hasCode(configissues.List(), configissues.CodeTemplateParse))
|
||||
}
|
||||
|
||||
func TestRebuildCronEntityBinding(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Nightly",
|
||||
ID: "nightly",
|
||||
Entity: "host",
|
||||
ExecOnCron: []string{"0 1 * * *"},
|
||||
},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
assert.True(t, hasCode(configissues.List(), configissues.CodeCronEntityBinding))
|
||||
}
|
||||
|
||||
func TestRebuildInvalidCron(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Broken cron",
|
||||
ID: "broken",
|
||||
ExecOnCron: []string{"not a cron line"},
|
||||
},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
assert.True(t, hasCode(configissues.List(), configissues.CodeCronInvalid))
|
||||
}
|
||||
|
||||
func TestRebuildMissingEntityFile(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
dir := t.TempDir()
|
||||
cfg.SetDir(dir)
|
||||
cfg.Entities = []*config.EntityFile{
|
||||
{Name: "host", File: "missing-hosts.yaml"},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
assert.True(t, hasCode(configissues.List(), configissues.CodeEntityFile))
|
||||
}
|
||||
|
||||
func TestRebuildEntityFileLoadsAndClearsEmpty(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
dir := t.TempDir()
|
||||
cfg.SetDir(dir)
|
||||
|
||||
entityPath := filepath.Join(dir, "hosts.yaml")
|
||||
require.NoError(t, os.WriteFile(entityPath, []byte("- name: cradle\n"), 0o644))
|
||||
|
||||
cfg.Entities = []*config.EntityFile{
|
||||
{Name: "host", File: "hosts.yaml"},
|
||||
}
|
||||
cfg.Actions = []*config.Action{
|
||||
{Title: "Wake", ID: "wake", Entity: "host"},
|
||||
}
|
||||
|
||||
entities.AddEntity("host", "0", map[string]any{"name": "cradle"})
|
||||
t.Cleanup(func() { entities.ClearEntitiesOfType("host") })
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
issues := configissues.List()
|
||||
assert.False(t, hasCode(issues, configissues.CodeEntityFile))
|
||||
assert.False(t, hasCode(issues, configissues.CodeEntityEmpty))
|
||||
}
|
||||
|
||||
func TestRebuildIncludesActionConfigFile(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "Nightly",
|
||||
ID: "nightly",
|
||||
Entity: "host",
|
||||
SourceFile: "/etc/OliveTin/config.d/16-gitops-sr-soe.yaml",
|
||||
ExecOnCron: []string{"0 1 * * *"},
|
||||
},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
var found bool
|
||||
for _, issue := range configissues.List() {
|
||||
if issue.Code == configissues.CodeCronEntityBinding {
|
||||
assert.Equal(t, "/etc/OliveTin/config.d/16-gitops-sr-soe.yaml", issue.ConfigFile)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestRebuildSkipsEntityEmptyBeforeLoadAttempt(t *testing.T) {
|
||||
entities.ResetEntityLoadAttempts()
|
||||
configissues.BeginConfigLoad()
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Entities = []*config.EntityFile{{Name: "container", File: "containers.json"}}
|
||||
cfg.Actions = []*config.Action{
|
||||
{Title: "Start container", ID: "start", Entity: "container"},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
assert.False(t, hasCode(configissues.List(), configissues.CodeEntityEmpty))
|
||||
|
||||
entities.MarkEntityLoadAttempted("container")
|
||||
configcheck.Rebuild(cfg)
|
||||
assert.True(t, hasCode(configissues.List(), configissues.CodeEntityEmpty))
|
||||
}
|
||||
|
||||
func TestStickyEnvUnsetSurvivesRebuild(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
configissues.ReportSticky(configissues.Issue{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeEnvUnset,
|
||||
Message: "unset",
|
||||
Source: "MISSING_VAR",
|
||||
})
|
||||
|
||||
configcheck.Rebuild(config.DefaultConfig())
|
||||
|
||||
assert.True(t, hasCode(configissues.List(), configissues.CodeEnvUnset))
|
||||
}
|
||||
|
||||
func hasCode(issues []configissues.Issue, code string) bool {
|
||||
for _, issue := range issues {
|
||||
if issue.Code == code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package configcheck_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configcheck"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRebuildWatcherPathMissingDir(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
configissues.Replace(nil)
|
||||
|
||||
missing := filepath.Join(t.TempDir(), "does-not-exist")
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Actions = []*config.Action{
|
||||
{
|
||||
Title: "On create",
|
||||
ID: "on_create",
|
||||
SourceFile: "/etc/OliveTin/config.yaml",
|
||||
ExecOnFileCreatedInDir: []string{missing},
|
||||
},
|
||||
}
|
||||
|
||||
configcheck.Rebuild(cfg)
|
||||
|
||||
issues := configissues.List()
|
||||
require.True(t, hasCode(issues, configissues.CodeWatcherPath))
|
||||
|
||||
var found *configissues.Issue
|
||||
for i := range issues {
|
||||
if issues[i].Code == configissues.CodeWatcherPath {
|
||||
found = &issues[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, found)
|
||||
assert.Equal(t, missing, found.Source)
|
||||
assert.Equal(t, "/etc/OliveTin/config.yaml", found.ConfigFile)
|
||||
assert.Contains(t, found.Message, missing)
|
||||
assert.Contains(t, found.Message, "Could not create watcher")
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package configissues
|
||||
|
||||
const (
|
||||
SeverityWarning = "warning"
|
||||
SeverityError = "error"
|
||||
|
||||
CodeActionGroupUnknown = "action_group_unknown"
|
||||
CodeActionGroupUnenforced = "action_group_unenforced"
|
||||
CodeArgTypeUnset = "arg_type_unset"
|
||||
CodeChecklistNoChoices = "checklist_no_choices"
|
||||
CodeChecklistEntityChoices = "checklist_entity_choices"
|
||||
CodeEnvUnset = "env_unset"
|
||||
CodeIncludeMissing = "include_missing"
|
||||
CodeTemplateParse = "template_parse"
|
||||
CodeArgDefaultInvalid = "arg_default_invalid"
|
||||
CodeEntityFile = "entity_file"
|
||||
CodeEntityEmpty = "entity_empty"
|
||||
CodeCronInvalid = "cron_invalid"
|
||||
CodeCronEntityBinding = "cron_entity_binding"
|
||||
CodeWatcherPath = "watcher_path"
|
||||
)
|
||||
|
||||
// Issue is a configuration warning or error surfaced on Diagnostics.
|
||||
type Issue struct {
|
||||
Severity string
|
||||
Code string
|
||||
Message string
|
||||
ActionID string
|
||||
ActionTitle string
|
||||
ArgumentName string
|
||||
Source string
|
||||
ConfigFile string
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package configissues
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
storeMu sync.RWMutex
|
||||
issues []Issue
|
||||
|
||||
stickyMu sync.Mutex
|
||||
sticky []Issue
|
||||
)
|
||||
|
||||
// BeginConfigLoad clears sticky issues captured during config decode/include load.
|
||||
func BeginConfigLoad() {
|
||||
stickyMu.Lock()
|
||||
defer stickyMu.Unlock()
|
||||
sticky = nil
|
||||
}
|
||||
|
||||
// ReportSticky records an issue that cannot be re-derived after config decode
|
||||
// (for example unset environment variables that were already expanded).
|
||||
func ReportSticky(issue Issue) {
|
||||
stickyMu.Lock()
|
||||
defer stickyMu.Unlock()
|
||||
sticky = append(sticky, issue)
|
||||
}
|
||||
|
||||
func copySticky() []Issue {
|
||||
stickyMu.Lock()
|
||||
defer stickyMu.Unlock()
|
||||
out := make([]Issue, len(sticky))
|
||||
copy(out, sticky)
|
||||
return out
|
||||
}
|
||||
|
||||
// CopySticky returns a copy of sticky load-time issues.
|
||||
func CopySticky() []Issue {
|
||||
return copySticky()
|
||||
}
|
||||
|
||||
// Replace sets the current issue list and logs only newly appeared issues.
|
||||
func Replace(next []Issue) {
|
||||
storeMu.Lock()
|
||||
defer storeMu.Unlock()
|
||||
|
||||
previous := make(map[string]struct{}, len(issues))
|
||||
for _, issue := range issues {
|
||||
previous[issueFingerprint(issue)] = struct{}{}
|
||||
}
|
||||
|
||||
issues = make([]Issue, len(next))
|
||||
copy(issues, next)
|
||||
|
||||
for _, issue := range issues {
|
||||
if _, exists := previous[issueFingerprint(issue)]; exists {
|
||||
continue
|
||||
}
|
||||
logNewIssue(issue)
|
||||
}
|
||||
}
|
||||
|
||||
func issueFingerprint(issue Issue) string {
|
||||
return issue.Severity + "\x00" +
|
||||
issue.Code + "\x00" +
|
||||
issue.ActionID + "\x00" +
|
||||
issue.ActionTitle + "\x00" +
|
||||
issue.ArgumentName + "\x00" +
|
||||
issue.Source + "\x00" +
|
||||
issue.ConfigFile + "\x00" +
|
||||
issue.Message
|
||||
}
|
||||
|
||||
func logNewIssue(issue Issue) {
|
||||
fields := log.Fields{
|
||||
"code": issue.Code,
|
||||
"actionTitle": issue.ActionTitle,
|
||||
"argumentName": issue.ArgumentName,
|
||||
"configFile": issue.ConfigFile,
|
||||
"source": issue.Source,
|
||||
}
|
||||
|
||||
if issue.Severity == SeverityError {
|
||||
log.WithFields(fields).Error(issue.Message)
|
||||
return
|
||||
}
|
||||
log.WithFields(fields).Warn(issue.Message)
|
||||
}
|
||||
|
||||
// Report adds a live issue if it is not already present and logs it when new.
|
||||
// Use this for runtime discoveries that happen outside Rebuild (for example
|
||||
// filesystem watcher setup failures).
|
||||
func Report(issue Issue) {
|
||||
storeMu.Lock()
|
||||
defer storeMu.Unlock()
|
||||
|
||||
issueFingerprintValue := issueFingerprint(issue)
|
||||
for _, existing := range issues {
|
||||
if issueFingerprint(existing) == issueFingerprintValue {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
issues = append(issues, issue)
|
||||
logNewIssue(issue)
|
||||
}
|
||||
|
||||
// List returns a copy of the current issues.
|
||||
func List() []Issue {
|
||||
storeMu.RLock()
|
||||
defer storeMu.RUnlock()
|
||||
out := make([]Issue, len(issues))
|
||||
copy(out, issues)
|
||||
return out
|
||||
}
|
||||
|
||||
// Count returns the number of current issues.
|
||||
func Count() int {
|
||||
storeMu.RLock()
|
||||
defer storeMu.RUnlock()
|
||||
return len(issues)
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package configissues_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStoreReplaceListCount(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
configissues.Replace([]configissues.Issue{{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeTemplateParse,
|
||||
Message: "broken",
|
||||
}})
|
||||
|
||||
assert.Equal(t, 1, configissues.Count())
|
||||
assert.Equal(t, configissues.CodeTemplateParse, configissues.List()[0].Code)
|
||||
|
||||
configissues.Replace(nil)
|
||||
assert.Equal(t, 0, configissues.Count())
|
||||
}
|
||||
|
||||
func TestStickyCopy(t *testing.T) {
|
||||
configissues.BeginConfigLoad()
|
||||
configissues.ReportSticky(configissues.Issue{
|
||||
Code: configissues.CodeEnvUnset,
|
||||
Message: "missing",
|
||||
Source: "FOO",
|
||||
})
|
||||
|
||||
copied := configissues.CopySticky()
|
||||
assert.Len(t, copied, 1)
|
||||
assert.Equal(t, configissues.CodeEnvUnset, copied[0].Code)
|
||||
|
||||
configissues.BeginConfigLoad()
|
||||
assert.Empty(t, configissues.CopySticky())
|
||||
}
|
||||
|
|
@ -30,13 +30,18 @@ func AddListener(l func()) {
|
|||
}
|
||||
|
||||
func SetupEntityFileWatchers(cfg *config.Config) {
|
||||
baseDir := resolveEntitiesBaseDir(cfg.GetDir())
|
||||
baseDir := ResolveEntitiesBaseDir(cfg.GetDir())
|
||||
for i := range cfg.Entities { // #337 - iterate by key, not by value
|
||||
ef := cfg.Entities[i]
|
||||
watchAndLoadEntity(baseDir, ef)
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveEntitiesBaseDir returns the directory used to resolve relative entity file paths.
|
||||
func ResolveEntitiesBaseDir(configDir string) string {
|
||||
return resolveEntitiesBaseDir(configDir)
|
||||
}
|
||||
|
||||
//gocyclo:ignore
|
||||
func resolveEntitiesBaseDir(configDir string) string {
|
||||
absConfigDir, err := filepath.Abs(configDir)
|
||||
|
|
@ -64,15 +69,28 @@ func watchAndLoadEntity(baseDir string, ef *config.EntityFile) {
|
|||
p = filepath.Join(baseDir, p)
|
||||
log.WithFields(log.Fields{"entityFile": p}).Debugf("Adding config dir to entity file path")
|
||||
}
|
||||
go filehelper.WatchFileWrite(p, func(filename string) { loadEntityFile(p, ef.Name) })
|
||||
go filehelper.WatchFileWrite(p, func(filename string) { loadEntityFile(p, ef.Name) }, filehelper.WatchMeta{
|
||||
ConfigFile: ef.SourceFile,
|
||||
})
|
||||
loadEntityFile(p, ef.Name)
|
||||
}
|
||||
|
||||
func loadEntityFile(filename string, entityname string) {
|
||||
defer func() {
|
||||
MarkEntityLoadAttempted(entityname)
|
||||
notifyEntityListeners()
|
||||
}()
|
||||
|
||||
if strings.HasSuffix(filename, ".json") {
|
||||
loadEntityFileJson(filename, entityname)
|
||||
} else {
|
||||
loadEntityFileYaml(filename, entityname)
|
||||
return
|
||||
}
|
||||
loadEntityFileYaml(filename, entityname)
|
||||
}
|
||||
|
||||
func notifyEntityListeners() {
|
||||
for _, l := range listeners {
|
||||
l()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +124,7 @@ func loadEntityFileJson(filename string, entityname string) {
|
|||
data = append(data, d)
|
||||
}
|
||||
|
||||
updateSvFromFile(entityname, data)
|
||||
replaceEntitiesFromFile(entityname, data)
|
||||
}
|
||||
|
||||
func loadEntityFileYaml(filename string, entityname string) {
|
||||
|
|
@ -131,18 +149,27 @@ func loadEntityFileYaml(filename string, entityname string) {
|
|||
return
|
||||
}
|
||||
|
||||
updateSvFromFile(entityname, data)
|
||||
replaceEntitiesFromFile(entityname, data)
|
||||
}
|
||||
|
||||
func updateSvFromFile(entityname string, data []map[string]any) {
|
||||
ClearEntitiesOfType(entityname)
|
||||
func replaceEntitiesFromFile(entityname string, data []map[string]any) {
|
||||
rwmutex.Lock()
|
||||
defer rwmutex.Unlock()
|
||||
|
||||
for i, mapp := range data {
|
||||
AddEntity(entityname, fmt.Sprintf("%d", i), mapp)
|
||||
delete(entities, entityname)
|
||||
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, l := range listeners {
|
||||
l()
|
||||
entities[entityname] = make(entityInstancesByKey, 0)
|
||||
for i, mapp := range data {
|
||||
entityKey := fmt.Sprintf("%d", i)
|
||||
entities[entityname][entityKey] = &Entity{
|
||||
Data: mapp,
|
||||
UniqueKey: entityKey,
|
||||
Title: findEntityTitle(mapp),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package entities
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -59,3 +61,34 @@ func TestGetEntityInstancesOrdered_emptyOrMissing(t *testing.T) {
|
|||
ordered = GetEntityInstancesOrdered("empty_test")
|
||||
assert.Nil(t, ordered)
|
||||
}
|
||||
|
||||
func TestLoadEntityFile_preservesEntitiesOnTransientFailure(t *testing.T) {
|
||||
const entityName = "preserve_on_fail"
|
||||
ClearEntitiesOfType(entityName)
|
||||
defer ClearEntitiesOfType(entityName)
|
||||
|
||||
dir := t.TempDir()
|
||||
yamlPath := filepath.Join(dir, "hosts.yaml")
|
||||
require.NoError(t, os.WriteFile(yamlPath, []byte("- title: kept\n"), 0o600))
|
||||
|
||||
loadEntityFile(yamlPath, entityName)
|
||||
require.Len(t, GetEntityInstancesOrdered(entityName), 1)
|
||||
|
||||
loadEntityFile(filepath.Join(dir, "missing.yaml"), entityName)
|
||||
require.Len(t, GetEntityInstancesOrdered(entityName), 1, "read failure should keep last good entities")
|
||||
|
||||
require.NoError(t, os.WriteFile(yamlPath, []byte("not: valid: yaml: ["), 0o600))
|
||||
loadEntityFile(yamlPath, entityName)
|
||||
require.Len(t, GetEntityInstancesOrdered(entityName), 1, "parse failure should keep last good entities")
|
||||
|
||||
jsonPath := filepath.Join(dir, "hosts.json")
|
||||
require.NoError(t, os.WriteFile(jsonPath, []byte("{\"title\":\"json-kept\"}\n"), 0o600))
|
||||
loadEntityFile(jsonPath, entityName)
|
||||
require.Len(t, GetEntityInstancesOrdered(entityName), 1)
|
||||
|
||||
require.NoError(t, os.WriteFile(jsonPath, []byte("{bad json"), 0o600))
|
||||
loadEntityFile(jsonPath, entityName)
|
||||
ordered := GetEntityInstancesOrdered(entityName)
|
||||
require.Len(t, ordered, 1, "JSON parse failure should keep last good entities")
|
||||
assert.Equal(t, "json-kept", ordered[0].Title)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package entities
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
loadAttemptedMu sync.RWMutex
|
||||
loadAttempted = map[string]bool{}
|
||||
)
|
||||
|
||||
// MarkEntityLoadAttempted records that OliveTin has tried to load this entity
|
||||
// type from its entity file (successfully or not).
|
||||
func MarkEntityLoadAttempted(entityName string) {
|
||||
if entityName == "" {
|
||||
return
|
||||
}
|
||||
loadAttemptedMu.Lock()
|
||||
defer loadAttemptedMu.Unlock()
|
||||
loadAttempted[entityName] = true
|
||||
}
|
||||
|
||||
// HasEntityLoadAttempted reports whether a load has been attempted for the entity type.
|
||||
func HasEntityLoadAttempted(entityName string) bool {
|
||||
loadAttemptedMu.RLock()
|
||||
defer loadAttemptedMu.RUnlock()
|
||||
return loadAttempted[entityName]
|
||||
}
|
||||
|
||||
// ResetEntityLoadAttempts clears load-attempt tracking (used by tests).
|
||||
func ResetEntityLoadAttempts() {
|
||||
loadAttemptedMu.Lock()
|
||||
defer loadAttemptedMu.Unlock()
|
||||
loadAttempted = map[string]bool{}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
config "github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestValidateArgumentDefaultSkipsTemplates(t *testing.T) {
|
||||
action := &config.Action{Title: "Deploy", ID: "deploy"}
|
||||
arg := &config.ActionArgument{
|
||||
Name: "host",
|
||||
Type: "ascii_identifier",
|
||||
Default: `{{ host.name }}`,
|
||||
}
|
||||
|
||||
assert.Nil(t, validateArgumentDefault(action, arg))
|
||||
}
|
||||
|
||||
func TestValidateArgumentDefaultLiteralInvalid(t *testing.T) {
|
||||
action := &config.Action{Title: "Deploy", ID: "deploy"}
|
||||
arg := &config.ActionArgument{
|
||||
Name: "host",
|
||||
Type: "ascii_identifier",
|
||||
Default: "not a valid id!!",
|
||||
}
|
||||
|
||||
issue := validateArgumentDefault(action, arg)
|
||||
require.NotNil(t, issue)
|
||||
assert.Equal(t, configissues.CodeArgDefaultInvalid, issue.Code)
|
||||
}
|
||||
|
||||
func TestValidateArgumentDefaultLiteralValid(t *testing.T) {
|
||||
action := &config.Action{Title: "Deploy", ID: "deploy"}
|
||||
arg := &config.ActionArgument{
|
||||
Name: "host",
|
||||
Type: "ascii_identifier",
|
||||
Default: "example.com",
|
||||
}
|
||||
|
||||
assert.Nil(t, validateArgumentDefault(action, arg))
|
||||
}
|
||||
|
|
@ -24,6 +24,9 @@ var (
|
|||
"shell_safe_identifier": `^[a-zA-Z0-9@\.\_\+\-]+$`,
|
||||
"ascii_sentence": `^[a-zA-Z0-9\-\._, ]+$`,
|
||||
}
|
||||
|
||||
dnsNameLabelPattern = regexp.MustCompile(`^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
|
||||
dnsNameAllNumericPattern = regexp.MustCompile(`^[0-9]+$`)
|
||||
)
|
||||
|
||||
// parseExecArray parses all exec arguments in the action.
|
||||
|
|
@ -147,12 +150,7 @@ func redactExecArgs(execArgs []string, arguments []config.ActionArgument, argume
|
|||
}
|
||||
|
||||
func argumentSkipsValidation(arg *config.ActionArgument) bool {
|
||||
switch arg.Type {
|
||||
case "confirmation", "html":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return arg.Type == "html"
|
||||
}
|
||||
|
||||
func typecheckActionArgument(arg *config.ActionArgument, value string, action *config.Action) error {
|
||||
|
|
@ -160,6 +158,10 @@ func typecheckActionArgument(arg *config.ActionArgument, value string, action *c
|
|||
return nil
|
||||
}
|
||||
|
||||
if arg.Type == "confirmation" {
|
||||
return typecheckConfirmation(arg, value)
|
||||
}
|
||||
|
||||
if arg.Name == "" {
|
||||
return fmt.Errorf("argument name cannot be empty")
|
||||
}
|
||||
|
|
@ -167,6 +169,20 @@ func typecheckActionArgument(arg *config.ActionArgument, value string, action *c
|
|||
return typecheckActionArgumentFound(value, arg)
|
||||
}
|
||||
|
||||
// typecheckConfirmation allows unnamed confirmation args as UI-only gates.
|
||||
// Named confirmation values are only ever "0" or "1", matching the web UI.
|
||||
func typecheckConfirmation(arg *config.ActionArgument, value string) error {
|
||||
if arg.Name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if value == "0" || value == "1" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("argument %q of type confirmation must be \"0\" or \"1\"", arg.Name)
|
||||
}
|
||||
|
||||
// ValidateArgument validates a single argument value using the same logic as the executor.
|
||||
// It applies mangling transformations and performs full validation including null checks,
|
||||
// choice validation, and type safety checks.
|
||||
|
|
@ -223,6 +239,8 @@ func TypeSafetyCheck(name string, value string, argumentType string) error {
|
|||
return typeSafetyCheckUrl(value)
|
||||
case "datetime":
|
||||
return typeSafetyCheckDatetime(value)
|
||||
case "dnsname":
|
||||
return typeSafetyCheckDnsName(value)
|
||||
}
|
||||
|
||||
return typeSafetyCheckRegex(name, value, argumentType)
|
||||
|
|
@ -304,6 +322,33 @@ func typeSafetyCheckEmail(value string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// typeSafetyCheckDnsName validates a DNS hostname (RFC 1123 LDH labels).
|
||||
// Accepts short names (e.g. webserver) and FQDNs (e.g. webserver.example.com).
|
||||
// An optional trailing dot is allowed.
|
||||
func typeSafetyCheckDnsName(value string) error {
|
||||
hostname := strings.TrimSuffix(value, ".")
|
||||
if hostname == "" || len(hostname) > 253 {
|
||||
return fmt.Errorf("invalid dnsname length")
|
||||
}
|
||||
|
||||
return typeSafetyCheckDnsNameLabels(strings.Split(hostname, "."))
|
||||
}
|
||||
|
||||
func typeSafetyCheckDnsNameLabels(labels []string) error {
|
||||
for _, label := range labels {
|
||||
if !dnsNameLabelPattern.MatchString(label) {
|
||||
return fmt.Errorf("invalid dnsname label %q", label)
|
||||
}
|
||||
}
|
||||
|
||||
tld := labels[len(labels)-1]
|
||||
if dnsNameAllNumericPattern.MatchString(tld) {
|
||||
return fmt.Errorf("dnsname top-level label must not be all-numeric")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func typeSafetyCheckDatetime(value string) error {
|
||||
_, err := time.Parse("2006-01-02T15:04:05", value)
|
||||
|
||||
|
|
@ -371,7 +416,6 @@ var shellUnsafeArgumentTypes = map[string]struct{}{
|
|||
"very_dangerous_raw_string": {},
|
||||
"password": {},
|
||||
"html": {},
|
||||
"confirmation": {},
|
||||
}
|
||||
|
||||
func isUnsafeShellArgumentType(arg *config.ActionArgument) bool {
|
||||
|
|
|
|||
|
|
@ -523,8 +523,20 @@ func TestCheckShellArgumentSafetyWithConfirmation(t *testing.T) {
|
|||
}
|
||||
|
||||
err := checkShellArgumentSafety(&a1)
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "unsafe argument type 'confirmation'")
|
||||
assert.Nil(t, err, "confirmation is constrained to 0/1 and is safe with shell")
|
||||
}
|
||||
|
||||
func TestCheckShellArgumentSafetyWithUnnamedConfirmation(t *testing.T) {
|
||||
a1 := config.Action{
|
||||
Title: "Confirm shell unnamed",
|
||||
Shell: "echo ok",
|
||||
Arguments: []config.ActionArgument{
|
||||
{Type: "confirmation", Title: "Are you sure?!"},
|
||||
},
|
||||
}
|
||||
|
||||
err := checkShellArgumentSafety(&a1)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestCheckShellArgumentSafetyWithChoicelessCheckbox(t *testing.T) {
|
||||
|
|
@ -812,6 +824,44 @@ func TestTypeSafetyCheckAsciiIdentifier(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTypeSafetyCheckDnsName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
hasError bool
|
||||
}{
|
||||
{"Short name", "webserver", false},
|
||||
{"Localhost", "localhost", false},
|
||||
{"Simple domain", "example.com", false},
|
||||
{"Host with subdomain", "webserver.example.com", false},
|
||||
{"Deep subdomain", "a.b.c.example.co.uk", false},
|
||||
{"Label starting with digit", "1host.example.com", false},
|
||||
{"Trailing dot", "example.com.", false},
|
||||
{"Punycode IDN", "xn--bcher-kva.example", false},
|
||||
{"Underscore", "my_host.example.com", true},
|
||||
{"Space", "example .com", true},
|
||||
{"Leading hyphen label", "-host.example.com", true},
|
||||
{"Trailing hyphen label", "host-.example.com", true},
|
||||
{"Empty label", "example..com", true},
|
||||
{"IP address", "192.168.1.1", true},
|
||||
{"All numeric TLD", "example.123", true},
|
||||
{"All numeric short name", "12345", true},
|
||||
{"Special chars", "exam!ple.com", true},
|
||||
{"Unicode label", "bücher.example.com", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := TypeSafetyCheck("host", tt.value, "dnsname")
|
||||
if tt.hasError {
|
||||
assert.NotNil(t, err, "Expected error for value '%s'", tt.value)
|
||||
} else {
|
||||
assert.Nil(t, err, "Expected no error for value '%s', but got: %v", tt.value, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeSafetyCheckShellSafeIdentifier(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -892,8 +942,27 @@ func TestTypecheckActionArgumentConfirmation(t *testing.T) {
|
|||
}
|
||||
action := config.Action{Title: "Test"}
|
||||
|
||||
assert.Nil(t, typecheckActionArgument(&arg, "0", &action))
|
||||
assert.Nil(t, typecheckActionArgument(&arg, "1", &action))
|
||||
|
||||
err := typecheckActionArgument(&arg, "any_value", &action)
|
||||
assert.Nil(t, err, "Confirmation type should always pass validation")
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "must be \"0\" or \"1\"")
|
||||
|
||||
err = typecheckActionArgument(&arg, "", &action)
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "must be \"0\" or \"1\"")
|
||||
}
|
||||
|
||||
func TestTypecheckActionArgumentUnnamedConfirmation(t *testing.T) {
|
||||
arg := config.ActionArgument{
|
||||
Type: "confirmation",
|
||||
Title: "Are you sure?!",
|
||||
}
|
||||
action := config.Action{Title: "Test"}
|
||||
|
||||
assert.Nil(t, typecheckActionArgument(&arg, "", &action))
|
||||
assert.Nil(t, typecheckActionArgument(&arg, "ignored", &action))
|
||||
}
|
||||
|
||||
func TestTypecheckActionArgumentHtmlWithoutName(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -1097,6 +1097,7 @@ func appendErrorToStderr(req *ExecutionRequest, err error) {
|
|||
|
||||
type OutputStreamer struct {
|
||||
Req *ExecutionRequest
|
||||
mu sync.Mutex
|
||||
output bytes.Buffer
|
||||
}
|
||||
|
||||
|
|
@ -1105,10 +1106,31 @@ func (ost *OutputStreamer) Write(o []byte) (n int, err error) {
|
|||
listener.OnOutputChunk(o, ost.Req.TrackingID)
|
||||
}
|
||||
|
||||
return ost.output.Write(o)
|
||||
ost.mu.Lock()
|
||||
n, err = ost.output.Write(o)
|
||||
outputSoFar := ""
|
||||
if err == nil {
|
||||
outputSoFar = ost.output.String()
|
||||
}
|
||||
ost.mu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Keep the log entry's Output in sync while the command is still running so
|
||||
// ExecutionStatus / mid-run result views can show output produced so far.
|
||||
ost.Req.mutateLogEntry(func(entry *InternalLogEntry) {
|
||||
entry.Output = outputSoFar
|
||||
})
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (ost *OutputStreamer) String() string {
|
||||
ost.mu.Lock()
|
||||
defer ost.mu.Unlock()
|
||||
|
||||
return ost.output.String()
|
||||
}
|
||||
|
||||
|
|
@ -1155,9 +1177,10 @@ func stepExec(req *ExecutionRequest) bool {
|
|||
})
|
||||
ctx.setProcess(cmd.Process)
|
||||
waiterr := cmd.Wait()
|
||||
finalOutput := streamer.String()
|
||||
req.mutateLogEntry(func(entry *InternalLogEntry) {
|
||||
entry.ExitCode = int32(commandExitCode(cmd))
|
||||
entry.Output = streamer.String()
|
||||
entry.Output = finalOutput
|
||||
})
|
||||
|
||||
appendErrorToStderr(req, runerr)
|
||||
|
|
@ -1391,7 +1414,12 @@ func triggerLoop(req *ExecutionRequest) {
|
|||
}
|
||||
|
||||
func stepSaveLog(req *ExecutionRequest) bool {
|
||||
filename := fmt.Sprintf("%v.%v.%v", req.logEntry.ActionTitle, req.logEntry.DatetimeStarted.Unix(), req.logEntry.ExecutionTrackingID)
|
||||
if !canSaveExecutionLog(req) {
|
||||
log.Warnf("Cannot save execution log; missing request, log entry, binding/action, or config")
|
||||
return false
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("%v.%v.%v", sanitizeLogFilename(req.logEntry.ActionTitle), req.logEntry.DatetimeStarted.Unix(), req.logEntry.ExecutionTrackingID)
|
||||
|
||||
saveLogResults(req, filename)
|
||||
saveLogOutput(req, filename)
|
||||
|
|
@ -1399,6 +1427,34 @@ func stepSaveLog(req *ExecutionRequest) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func canSaveExecutionLog(req *ExecutionRequest) bool {
|
||||
return req != nil && req.logEntry != nil && req.Binding != nil && req.Binding.Action != nil && req.Cfg != nil
|
||||
}
|
||||
|
||||
// sanitizeLogFilename replaces characters that are unsafe in filenames so action
|
||||
// titles like "Create/update Report" do not create nested paths or fail to write.
|
||||
func sanitizeLogFilename(title string) string {
|
||||
oldnew := []string{
|
||||
"/", "_",
|
||||
"\\", "_",
|
||||
":", "_",
|
||||
"*", "_",
|
||||
"?", "_",
|
||||
"\"", "_",
|
||||
"<", "_",
|
||||
">", "_",
|
||||
"|", "_",
|
||||
}
|
||||
|
||||
// NUL and other C0 controls plus DEL are invalid or problematic in filenames.
|
||||
for i := 0; i < 32; i++ {
|
||||
oldnew = append(oldnew, string(rune(i)), "_")
|
||||
}
|
||||
oldnew = append(oldnew, "\x7f", "_")
|
||||
|
||||
return strings.NewReplacer(oldnew...).Replace(title)
|
||||
}
|
||||
|
||||
func firstNonEmpty(one, two string) string {
|
||||
if one != "" {
|
||||
return one
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ package executor
|
|||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
config "github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/config"
|
||||
"github.com/OliveTin/OliveTin/internal/configcheck"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/OliveTin/OliveTin/internal/entities"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (e *Executor) FindBindingByID(id string) *ActionBinding {
|
||||
|
|
@ -40,40 +42,54 @@ type RebuildActionMapRequest struct {
|
|||
dashboardTargets *dashboardTargetIndex
|
||||
}
|
||||
|
||||
func validateArgumentDefaults(cfg *config.Config) {
|
||||
func validateArgumentDefaults(cfg *config.Config) []configissues.Issue {
|
||||
if cfg == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
out := make([]configissues.Issue, 0)
|
||||
for _, action := range cfg.Actions {
|
||||
validateActionArgumentDefaults(action)
|
||||
out = append(out, validateActionArgumentDefaults(action)...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func validateActionArgumentDefaults(action *config.Action) {
|
||||
func validateActionArgumentDefaults(action *config.Action) []configissues.Issue {
|
||||
if action == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
out := make([]configissues.Issue, 0)
|
||||
for i := range action.Arguments {
|
||||
validateArgumentDefault(action, &action.Arguments[i])
|
||||
if issue := validateArgumentDefault(action, &action.Arguments[i]); issue != nil {
|
||||
out = append(out, *issue)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func validateArgumentDefault(action *config.Action, arg *config.ActionArgument) {
|
||||
func validateArgumentDefault(action *config.Action, arg *config.ActionArgument) *configissues.Issue {
|
||||
if arg.Default == "" {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if strings.Contains(arg.Default, "{{") {
|
||||
return nil
|
||||
}
|
||||
if err := ValidateArgument(arg, arg.Default, action); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"actionTitle": action.Title,
|
||||
"argName": arg.Name,
|
||||
"default": arg.Default,
|
||||
"error": err,
|
||||
}).Warn("Argument default value failed validation")
|
||||
return &configissues.Issue{
|
||||
Severity: configissues.SeverityWarning,
|
||||
Code: configissues.CodeArgDefaultInvalid,
|
||||
Message: fmt.Sprintf("Argument default value failed validation: %v", err),
|
||||
ActionID: action.ID,
|
||||
ActionTitle: action.Title,
|
||||
ArgumentName: arg.Name,
|
||||
Source: arg.Default,
|
||||
ConfigFile: action.SourceFile,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Executor) RebuildActionMap() {
|
||||
validateArgumentDefaults(e.Cfg)
|
||||
defaultIssues := validateArgumentDefaults(e.Cfg)
|
||||
|
||||
e.MapActionBindingsLock.Lock()
|
||||
|
||||
|
|
@ -94,6 +110,8 @@ func (e *Executor) RebuildActionMap() {
|
|||
|
||||
e.MapActionBindingsLock.Unlock()
|
||||
|
||||
configcheck.Rebuild(e.Cfg, defaultIssues...)
|
||||
|
||||
for _, l := range e.copyListeners() {
|
||||
l.OnActionMapRebuilt()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/auth"
|
||||
authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
|
||||
|
|
@ -780,3 +781,216 @@ func (c *executionFinishedCollector) OnExecutionFinished(entry *InternalLogEntry
|
|||
func (c *executionFinishedCollector) OnOutputChunk(_ []byte, _ string) {}
|
||||
|
||||
func (c *executionFinishedCollector) OnActionMapRebuilt() {}
|
||||
|
||||
func TestSanitizeLogFilename(t *testing.T) {
|
||||
tests := []struct {
|
||||
title string
|
||||
want string
|
||||
}{
|
||||
{"Echo Test", "Echo Test"},
|
||||
{"Create/update Monthly Report", "Create_update Monthly Report"},
|
||||
{`path\with\backslashes`, "path_with_backslashes"},
|
||||
{`a:b*c?d"e<f>g|h`, "a_b_c_d_e_f_g_h"},
|
||||
{"has\x00nul", "has_nul"},
|
||||
{"tab\there\nand\rreturn", "tab_here_and_return"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(t, tt.want, sanitizeLogFilename(tt.title), "title=%q", tt.title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepSaveLogSanitizesSlashInTitle(t *testing.T) {
|
||||
resultsDir := t.TempDir()
|
||||
outputDir := t.TempDir()
|
||||
started := time.Unix(1714333384, 0)
|
||||
trackingID := "5e2dc9e5-b6b3-445b-bff9-c2082b0bbbb2"
|
||||
title := "Create/update Monthly Report"
|
||||
|
||||
req := &ExecutionRequest{
|
||||
Cfg: &config.Config{
|
||||
SaveLogs: config.SaveLogsConfig{
|
||||
ResultsDirectory: resultsDir,
|
||||
OutputDirectory: outputDir,
|
||||
},
|
||||
},
|
||||
Binding: &ActionBinding{
|
||||
Action: &config.Action{},
|
||||
},
|
||||
logEntry: &InternalLogEntry{
|
||||
ActionTitle: title,
|
||||
DatetimeStarted: started,
|
||||
ExecutionTrackingID: trackingID,
|
||||
Output: "report ok",
|
||||
},
|
||||
}
|
||||
|
||||
assert.True(t, stepSaveLog(req))
|
||||
|
||||
expectedBase := "Create_update Monthly Report.1714333384." + trackingID
|
||||
resultsPath := filepath.Join(resultsDir, expectedBase+".yaml")
|
||||
outputPath := filepath.Join(outputDir, expectedBase+".log")
|
||||
|
||||
assert.FileExists(t, resultsPath)
|
||||
assert.FileExists(t, outputPath)
|
||||
|
||||
resultsEntries, err := os.ReadDir(resultsDir)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, resultsEntries, 1, "results file must be flat under resultsDirectory, not a subdirectory")
|
||||
|
||||
data, err := os.ReadFile(resultsPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(data), title, "YAML content keeps the original action title")
|
||||
|
||||
output, err := os.ReadFile(outputPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "report ok", string(output))
|
||||
}
|
||||
|
||||
func TestStepSaveLogKeepsSafeTitleFilename(t *testing.T) {
|
||||
resultsDir := t.TempDir()
|
||||
started := time.Unix(1714333384, 0)
|
||||
trackingID := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||
|
||||
req := &ExecutionRequest{
|
||||
Cfg: &config.Config{
|
||||
SaveLogs: config.SaveLogsConfig{
|
||||
ResultsDirectory: resultsDir,
|
||||
},
|
||||
},
|
||||
Binding: &ActionBinding{
|
||||
Action: &config.Action{},
|
||||
},
|
||||
logEntry: &InternalLogEntry{
|
||||
ActionTitle: "Echo Test",
|
||||
DatetimeStarted: started,
|
||||
ExecutionTrackingID: trackingID,
|
||||
},
|
||||
}
|
||||
|
||||
assert.True(t, stepSaveLog(req))
|
||||
|
||||
expectedPath := filepath.Join(resultsDir, "Echo Test.1714333384."+trackingID+".yaml")
|
||||
assert.FileExists(t, expectedPath)
|
||||
}
|
||||
|
||||
func TestStepSaveLogSanitizesNULInTitle(t *testing.T) {
|
||||
resultsDir := t.TempDir()
|
||||
outputDir := t.TempDir()
|
||||
started := time.Unix(1714333384, 0)
|
||||
trackingID := "bbbbbbbb-cccc-dddd-eeee-ffffffffffff"
|
||||
title := "Bad\x00Title"
|
||||
|
||||
req := &ExecutionRequest{
|
||||
Cfg: &config.Config{
|
||||
SaveLogs: config.SaveLogsConfig{
|
||||
ResultsDirectory: resultsDir,
|
||||
OutputDirectory: outputDir,
|
||||
},
|
||||
},
|
||||
Binding: &ActionBinding{
|
||||
Action: &config.Action{},
|
||||
},
|
||||
logEntry: &InternalLogEntry{
|
||||
ActionTitle: title,
|
||||
DatetimeStarted: started,
|
||||
ExecutionTrackingID: trackingID,
|
||||
Output: "nul ok",
|
||||
},
|
||||
}
|
||||
|
||||
assert.True(t, stepSaveLog(req))
|
||||
|
||||
expectedBase := "Bad_Title.1714333384." + trackingID
|
||||
resultsPath := filepath.Join(resultsDir, expectedBase+".yaml")
|
||||
outputPath := filepath.Join(outputDir, expectedBase+".log")
|
||||
|
||||
assert.FileExists(t, resultsPath)
|
||||
assert.FileExists(t, outputPath)
|
||||
assert.NotContains(t, resultsPath, "\x00")
|
||||
assert.NotContains(t, outputPath, "\x00")
|
||||
|
||||
output, err := os.ReadFile(outputPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "nul ok", string(output))
|
||||
}
|
||||
|
||||
func TestStepSaveLogReturnsFalseWhenDependenciesMissing(t *testing.T) {
|
||||
started := time.Unix(1714333384, 0)
|
||||
valid := &ExecutionRequest{
|
||||
Cfg: &config.Config{},
|
||||
Binding: &ActionBinding{
|
||||
Action: &config.Action{},
|
||||
},
|
||||
logEntry: &InternalLogEntry{
|
||||
ActionTitle: "Echo",
|
||||
DatetimeStarted: started,
|
||||
ExecutionTrackingID: "cccccccc-dddd-eeee-ffff-000000000000",
|
||||
},
|
||||
}
|
||||
|
||||
assert.False(t, stepSaveLog(nil))
|
||||
assert.False(t, stepSaveLog(&ExecutionRequest{}))
|
||||
|
||||
missingLog := *valid
|
||||
missingLog.logEntry = nil
|
||||
assert.False(t, stepSaveLog(&missingLog))
|
||||
|
||||
missingBinding := *valid
|
||||
missingBinding.Binding = nil
|
||||
assert.False(t, stepSaveLog(&missingBinding))
|
||||
|
||||
missingAction := *valid
|
||||
missingAction.Binding = &ActionBinding{}
|
||||
assert.False(t, stepSaveLog(&missingAction))
|
||||
|
||||
missingCfg := *valid
|
||||
missingCfg.Cfg = nil
|
||||
assert.False(t, stepSaveLog(&missingCfg))
|
||||
}
|
||||
|
||||
func TestLogEntryOutputAvailableWhileRunning(t *testing.T) {
|
||||
cfg := config.DefaultConfig()
|
||||
e := DefaultExecutor(cfg)
|
||||
action := &config.Action{
|
||||
Title: "Slow output",
|
||||
Shell: "echo hello-mid-run; sleep 2",
|
||||
}
|
||||
cfg.Actions = append(cfg.Actions, action)
|
||||
cfg.Sanitize()
|
||||
e.RebuildActionMap()
|
||||
|
||||
binding := e.FindBindingWithNoEntity(action)
|
||||
require.NotNil(t, binding)
|
||||
|
||||
wg, trackingID := e.ExecRequest(&ExecutionRequest{
|
||||
Binding: binding,
|
||||
Cfg: cfg,
|
||||
AuthenticatedUser: auth.UserFromSystem(cfg, "testuser"),
|
||||
})
|
||||
|
||||
var sawOutputWhileRunning bool
|
||||
require.Eventually(t, func() bool {
|
||||
snapshot, ok := e.SnapshotLog(trackingID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if snapshot.ExecutionFinished {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(snapshot.Output, "hello-mid-run") {
|
||||
sawOutputWhileRunning = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}, 2*time.Second, 10*time.Millisecond)
|
||||
|
||||
wg.Wait()
|
||||
|
||||
require.True(t, sawOutputWhileRunning, "expected Output to contain printed text before ExecutionFinished")
|
||||
|
||||
snapshot, ok := e.SnapshotLog(trackingID)
|
||||
require.True(t, ok)
|
||||
assert.True(t, snapshot.ExecutionFinished)
|
||||
assert.Contains(t, snapshot.Output, "hello-mid-run")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ func RestartArgumentsIncomplete(action *config.Action, entity *entities.Entity,
|
|||
}
|
||||
|
||||
func restartArgumentRequired(arg *config.ActionArgument, entity *entities.Entity) bool {
|
||||
if argumentSkipsValidation(arg) {
|
||||
// confirmation is a UI gate; html skips validation. Neither needs a stored value to restart.
|
||||
if argumentSkipsValidation(arg) || arg.Type == "confirmation" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package filehelper
|
||||
|
||||
import (
|
||||
"github.com/fsnotify/fsnotify"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"sync"
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -28,33 +30,44 @@ const (
|
|||
debounceDelay = 300 * time.Millisecond
|
||||
)
|
||||
|
||||
// WatchMeta attaches action context to watcher setup failures so Diagnostics
|
||||
// can apply the same action view ACL as other configuration issues.
|
||||
type WatchMeta struct {
|
||||
ActionID string
|
||||
ActionTitle string
|
||||
ConfigFile string
|
||||
}
|
||||
|
||||
type watchContext struct {
|
||||
filename string
|
||||
filedir string
|
||||
callback func(filename string)
|
||||
interestedEvent fsnotify.Op
|
||||
event *fsnotify.Event
|
||||
meta WatchMeta
|
||||
}
|
||||
|
||||
func WatchDirectoryCreate(fullpath string, callback func(filename string)) {
|
||||
func WatchDirectoryCreate(fullpath string, callback func(filename string), meta WatchMeta) {
|
||||
watchPath(&watchContext{
|
||||
filedir: fullpath,
|
||||
filename: "",
|
||||
callback: callback,
|
||||
interestedEvent: fsnotify.Create,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
func WatchDirectoryWrite(fullpath string, callback func(filename string)) {
|
||||
func WatchDirectoryWrite(fullpath string, callback func(filename string), meta WatchMeta) {
|
||||
watchPath(&watchContext{
|
||||
filedir: fullpath,
|
||||
filename: "",
|
||||
callback: callback,
|
||||
interestedEvent: fsnotify.Write,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
func WatchFileWrite(fullpath string, callback func(filename string)) {
|
||||
func WatchFileWrite(fullpath string, callback func(filename string), meta WatchMeta) {
|
||||
filename := filepath.Base(fullpath)
|
||||
filedir := filepath.Dir(fullpath)
|
||||
|
||||
|
|
@ -63,69 +76,94 @@ func WatchFileWrite(fullpath string, callback func(filename string)) {
|
|||
filename: filename,
|
||||
callback: callback,
|
||||
interestedEvent: fsnotify.Write,
|
||||
meta: meta,
|
||||
})
|
||||
}
|
||||
|
||||
func watchPath(ctx *watchContext) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Could not watch for files being created: %v", err)
|
||||
reportWatcherFailure(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := watcher.Close(); err != nil {
|
||||
log.Errorf("Failed to close file watcher: %v", err)
|
||||
}
|
||||
}()
|
||||
defer closeWatcher(watcher)
|
||||
|
||||
done := make(chan bool)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
processEvent(ctx, watcher)
|
||||
}
|
||||
}()
|
||||
|
||||
err = watcher.Add(ctx.filedir)
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Could not create watcher: %v", err)
|
||||
if err := watcher.Add(ctx.filedir); err != nil {
|
||||
reportWatcherFailure(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
<-done
|
||||
for processEvent(ctx, watcher) {
|
||||
}
|
||||
}
|
||||
|
||||
func processEvent(ctx *watchContext, watcher *fsnotify.Watcher) {
|
||||
func closeWatcher(watcher *fsnotify.Watcher) {
|
||||
if err := watcher.Close(); err != nil {
|
||||
log.Errorf("Failed to close file watcher: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func watchTarget(ctx *watchContext) string {
|
||||
if ctx.filename == "" {
|
||||
return ctx.filedir
|
||||
}
|
||||
return filepath.Join(ctx.filedir, ctx.filename)
|
||||
}
|
||||
|
||||
func reportWatcherFailure(ctx *watchContext, err error) {
|
||||
path := watchTarget(ctx)
|
||||
message := fmt.Sprintf("Could not create watcher for %q: %v", path, err)
|
||||
configissues.Report(configissues.Issue{
|
||||
Severity: configissues.SeverityError,
|
||||
Code: configissues.CodeWatcherPath,
|
||||
Message: message,
|
||||
ActionID: ctx.meta.ActionID,
|
||||
ActionTitle: ctx.meta.ActionTitle,
|
||||
Source: path,
|
||||
ConfigFile: ctx.meta.ConfigFile,
|
||||
})
|
||||
}
|
||||
|
||||
// processEvent waits for one watcher event. It returns false when the watcher
|
||||
// channels are closed so the caller can stop looping.
|
||||
func processEvent(ctx *watchContext, watcher *fsnotify.Watcher) bool {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
ctx.event = &event
|
||||
|
||||
if !consumeEvent(ok, ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
case err := <-watcher.Errors:
|
||||
log.Errorf("Error in fsnotify: %v", err)
|
||||
return
|
||||
return handleWatcherEvent(ctx, event, ok)
|
||||
case err, ok := <-watcher.Errors:
|
||||
return handleWatcherError(ctx, err, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func consumeEvent(ok bool, ctx *watchContext) bool {
|
||||
func handleWatcherEvent(ctx *watchContext, event fsnotify.Event, ok bool) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ctx.event = &event
|
||||
consumeEvent(ctx)
|
||||
return true
|
||||
}
|
||||
|
||||
func handleWatcherError(ctx *watchContext, err error, ok bool) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"path": watchTarget(ctx),
|
||||
}).Errorf("Error in fsnotify: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func consumeEvent(ctx *watchContext) {
|
||||
if ctx.filename != "" && filepath.Base(ctx.event.Name) != ctx.filename {
|
||||
log.Tracef("fsnotify irreleventa event different file %+v", ctx.event)
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
consumeRelevantEvents(ctx)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func consumeRelevantEvents(ctx *watchContext) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
package filehelper_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/configissues"
|
||||
"github.com/OliveTin/OliveTin/internal/filehelper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWatchDirectoryCreateReportsMissingPath(t *testing.T) {
|
||||
configissues.Replace(nil)
|
||||
|
||||
missing := filepath.Join(t.TempDir(), "missing-watch-dir")
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
filehelper.WatchDirectoryCreate(missing, func(string) {}, filehelper.WatchMeta{})
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("WatchDirectoryCreate did not return after watcher failure")
|
||||
}
|
||||
|
||||
issues := configissues.List()
|
||||
require.Len(t, issues, 1)
|
||||
assert.Equal(t, configissues.CodeWatcherPath, issues[0].Code)
|
||||
assert.Equal(t, missing, issues[0].Source)
|
||||
assert.Contains(t, issues[0].Message, missing)
|
||||
assert.Empty(t, issues[0].ActionID)
|
||||
}
|
||||
|
||||
func TestWatchDirectoryCreateReportsActionMeta(t *testing.T) {
|
||||
configissues.Replace(nil)
|
||||
|
||||
missing := filepath.Join(t.TempDir(), "missing-secret-watch")
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
filehelper.WatchDirectoryCreate(missing, func(string) {}, filehelper.WatchMeta{
|
||||
ActionID: "secret_action",
|
||||
ActionTitle: "Secret Action",
|
||||
ConfigFile: "/etc/OliveTin/secret.yaml",
|
||||
})
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("WatchDirectoryCreate did not return after watcher failure")
|
||||
}
|
||||
|
||||
issues := configissues.List()
|
||||
require.Len(t, issues, 1)
|
||||
assert.Equal(t, "secret_action", issues[0].ActionID)
|
||||
assert.Equal(t, "Secret Action", issues[0].ActionTitle)
|
||||
assert.Equal(t, "/etc/OliveTin/secret.yaml", issues[0].ConfigFile)
|
||||
}
|
||||
|
|
@ -37,7 +37,11 @@ func Schedule(cfg *config.Config, ex *executor.Executor) {
|
|||
parseCalendarFile(captured, cfg, ex, filename)
|
||||
}
|
||||
|
||||
go filehelper.WatchFileWrite(action.ExecOnCalendarFile, x)
|
||||
go filehelper.WatchFileWrite(action.ExecOnCalendarFile, x, filehelper.WatchMeta{
|
||||
ActionID: action.ID,
|
||||
ActionTitle: action.Title,
|
||||
ConfigFile: action.SourceFile,
|
||||
})
|
||||
|
||||
x(action.ExecOnCalendarFile)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ func WatchFilesInDirectory(cfg *config.Config, ex *executor.Executor) {
|
|||
go func(act *config.Action, dir string) {
|
||||
filehelper.WatchDirectoryCreate(dir, func(filename string) {
|
||||
scheduleExec(act, cfg, ex, filename)
|
||||
})
|
||||
}, watchMetaForAction(act))
|
||||
}(action, dirname)
|
||||
}
|
||||
for _, dirname := range action.ExecOnFileChangedInDir {
|
||||
|
|
@ -27,12 +27,20 @@ func WatchFilesInDirectory(cfg *config.Config, ex *executor.Executor) {
|
|||
go func(act *config.Action, dir string) {
|
||||
filehelper.WatchDirectoryWrite(dir, func(filename string) {
|
||||
scheduleExec(act, cfg, ex, filename)
|
||||
})
|
||||
}, watchMetaForAction(act))
|
||||
}(action, dirname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func watchMetaForAction(action *config.Action) filehelper.WatchMeta {
|
||||
return filehelper.WatchMeta{
|
||||
ActionID: action.ID,
|
||||
ActionTitle: action.Title,
|
||||
ConfigFile: action.SourceFile,
|
||||
}
|
||||
}
|
||||
|
||||
func scheduleExec(action *config.Action, cfg *config.Config, ex *executor.Executor, path string) {
|
||||
args := map[string]string{
|
||||
"filepath": path,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package tpl_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OliveTin/OliveTin/internal/tpl"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckActionTemplateParses(t *testing.T) {
|
||||
assert.NoError(t, tpl.CheckActionTemplateParses(`{{ host.name }}`))
|
||||
assert.NoError(t, tpl.CheckActionTemplateParses(`{{ host.hostname }}`))
|
||||
|
||||
err := tpl.CheckActionTemplateParses(`{{ host.name | default "All" }}`)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
|
@ -219,6 +219,25 @@ func ParseTemplateOfActionBeforeExec(source string, ent *entities.Entity) string
|
|||
return result
|
||||
}
|
||||
|
||||
// CheckActionTemplateParses migrates legacy template syntax and reports parse
|
||||
// errors only (execution against entity/argument data is not attempted).
|
||||
func CheckActionTemplateParses(source string) error {
|
||||
if source == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
source = migrateLegacyArgumentNames(source)
|
||||
source = migrateLegacyEntityProperties(source)
|
||||
|
||||
clone, err := tpl.Clone()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = clone.Parse(source)
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
func ParseTemplateBoolWith(source string, ent *entities.Entity) bool {
|
||||
source = strings.TrimSpace(source)
|
||||
|
|
|
|||
Loading…
Reference in New Issue