@iobroker/json-config 8.4.6 → 8.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/build/JsonConfigComponent/ConfigAutocomplete.js +11 -2
- package/build/JsonConfigComponent/ConfigAutocomplete.js.map +1 -1
- package/build/JsonConfigComponent/ConfigAutocompleteSendTo.js +9 -2
- package/build/JsonConfigComponent/ConfigAutocompleteSendTo.js.map +1 -1
- package/build/JsonConfigComponent/ConfigCredentialSelect.d.ts +31 -4
- package/build/JsonConfigComponent/ConfigCredentialSelect.js +224 -16
- package/build/JsonConfigComponent/ConfigCredentialSelect.js.map +1 -1
- package/build/types.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -850,13 +850,21 @@ select a certificate collection or just use all collections or don't use let's e
|
|
|
850
850
|
|
|
851
851
|
### `credential`
|
|
852
852
|
|
|
853
|
-
select a credential from the central credential storage. The
|
|
853
|
+
select a credential from the central credential storage. The credentials can be managed in the admin settings
|
|
854
854
|
(Settings → Credentials), and the adapter configuration only stores the ID of the selected credential
|
|
855
|
-
(like `system.credentials.
|
|
855
|
+
(like `system.credentials.anthropic`) in the given attribute.
|
|
856
856
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
857
|
+
Unless `disableCreation` is set, a **➕ button** is shown next to the selector that opens a small "Add credential"
|
|
858
|
+
dialog right there — similar to the admin dialog. It offers templates (with icons) filtered by `credentialType`
|
|
859
|
+
(e.g. Anthropic / ChatGPT / Google Gemini for `ai`, plus the generic "Login & password" and "Key" templates).
|
|
860
|
+
The chosen template defines the form, a proposed name and the icon; the secret fields are encrypted with the
|
|
861
|
+
system secret on save. The newly created credential is stored as `system.credentials.<name>` and is selected
|
|
862
|
+
immediately.
|
|
863
|
+
|
|
864
|
+
| Property | Description |
|
|
865
|
+
|-------------------|----------------------------------------------------------------------------------------------------------------------|
|
|
866
|
+
| `credentialType` | show only credentials of this type: `email`, `cloud`, `ai` or `custom`. If not defined, all credentials are listed |
|
|
867
|
+
| `disableCreation` | if `true`, hide the ➕ button so the user can only pick an existing credential (no creation at this place) |
|
|
860
868
|
|
|
861
869
|
Example:
|
|
862
870
|
|
|
@@ -866,18 +874,22 @@ Example:
|
|
|
866
874
|
"type": "credential",
|
|
867
875
|
"credentialType": "email",
|
|
868
876
|
"label": "E-Mail account",
|
|
877
|
+
"disableCreation": false,
|
|
869
878
|
"sm": 6
|
|
870
879
|
}
|
|
871
880
|
}
|
|
872
881
|
```
|
|
873
882
|
|
|
874
|
-
|
|
883
|
+
Every credential has one of two forms: `login` (a `login` and a `password` field) or `key`
|
|
884
|
+
(a single `key` field, e.g. an API key). In the adapter, read and decrypt the credential
|
|
885
|
+
with `@iobroker/adapter-core`:
|
|
875
886
|
|
|
876
887
|
```typescript
|
|
877
888
|
import { Credentials } from '@iobroker/adapter-core';
|
|
878
889
|
|
|
879
|
-
const cred = await Credentials.getCredentials<Credentials.
|
|
880
|
-
// cred.values.
|
|
890
|
+
const cred = await Credentials.getCredentials<Credentials.LoginPasswordCredentials>(this, this.config.credentialId);
|
|
891
|
+
// cred.values.login, cred.values.password (already decrypted)
|
|
892
|
+
// or for the key form: Credentials.KeyCredentials -> cred.values.key
|
|
881
893
|
```
|
|
882
894
|
|
|
883
895
|
### `custom`
|
|
@@ -1810,7 +1822,10 @@ The schema is used here: https://github.com/SchemaStore/schemastore/blob/6da29cd
|
|
|
1810
1822
|
### **WORK IN PROGRESS**
|
|
1811
1823
|
-->
|
|
1812
1824
|
## Changelog
|
|
1813
|
-
### 8.4.
|
|
1825
|
+
### 8.4.8 (2026-06-18)
|
|
1826
|
+
- (@GermanBluefox) Allowed creating credentials directly in the `credential` component (templates with icons, filtered by `credentialType`; can be disabled with `disableCreation`)
|
|
1827
|
+
|
|
1828
|
+
### 8.4.7 (2026-06-07)
|
|
1814
1829
|
- (@GermanBluefox) Added credential component
|
|
1815
1830
|
|
|
1816
1831
|
### 8.4.5 (2026-05-30)
|
|
@@ -52,14 +52,23 @@ class ConfigAutocomplete extends ConfigGeneric {
|
|
|
52
52
|
item = { value: this.state.value, label: this.state.value };
|
|
53
53
|
options.push(item);
|
|
54
54
|
}
|
|
55
|
+
// never pass `false`/`undefined` as a controlled value to the Autocomplete (would switch it to uncontrolled)
|
|
56
|
+
item ||= null;
|
|
55
57
|
}
|
|
56
58
|
return (React.createElement(Autocomplete, { fullWidth: true, freeSolo: !!this.props.schema.freeSolo, value: item, options: options, isOptionEqualToValue: (option, value) => option.value === value.value, filterOptions: (options, params) => {
|
|
59
|
+
const inputValue = params.inputValue.toLowerCase();
|
|
57
60
|
const filtered = options.filter(option => {
|
|
58
61
|
if (params.inputValue === '') {
|
|
59
62
|
return true;
|
|
60
63
|
}
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
// label/value may be numbers (or null/undefined), so coerce to string before comparing
|
|
65
|
+
// see https://github.com/ioBroker/ioBroker.admin/issues/3507
|
|
66
|
+
return (String(option.label ?? '')
|
|
67
|
+
.toLowerCase()
|
|
68
|
+
.includes(inputValue) ||
|
|
69
|
+
String(option.value ?? '')
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.includes(inputValue));
|
|
63
72
|
});
|
|
64
73
|
if (this.props.schema.freeSolo && params.inputValue !== '') {
|
|
65
74
|
filtered.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigAutocomplete.js","sourceRoot":"./src/","sources":["JsonConfigComponent/ConfigAutocomplete.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type JSX } from 'react';\n\nimport { Autocomplete, TextField } from '@mui/material';\n\nimport { I18n } from '@iobroker/adapter-react-v5';\n\nimport type { ConfigItemAutocomplete, ConfigItemSelectOption } from '../types';\nimport ConfigGeneric, { type ConfigGenericState, type ConfigGenericProps } from './ConfigGeneric';\n\nexport interface ConfigAutocompleteProps extends ConfigGenericProps {\n schema: ConfigItemAutocomplete;\n}\n\nexport interface ConfigAutocompleteState extends ConfigGenericState {\n selectOptions: { value: string; label: string }[];\n}\n\nclass ConfigAutocomplete extends ConfigGeneric<ConfigAutocompleteProps, ConfigAutocompleteState> {\n async componentDidMount(): Promise<void> {\n await super.componentDidMount();\n const value = ConfigGeneric.getValue(this.props.data, this.props.attr);\n\n const selectOptions: { label: string; value: string }[] = this.props.schema.options.map(\n (item: { label: string; value: string } | string) =>\n typeof item === 'string' ? { label: item, value: item } : JSON.parse(JSON.stringify(item)),\n );\n\n // Report value-to-label mapping to parent table for filtering\n if (this.props.onFilterLabelUpdate && this.props.table) {\n const valueToLabel: Record<string, string> = {};\n for (const opt of selectOptions) {\n if (opt.value !== ConfigGeneric.DIFFERENT_VALUE) {\n valueToLabel[opt.value] = opt.label;\n }\n }\n this.props.onFilterLabelUpdate(this.props.attr, valueToLabel);\n }\n\n // if __different\n if (Array.isArray(value)) {\n selectOptions.unshift({\n label: I18n.t(ConfigGeneric.DIFFERENT_LABEL),\n value: ConfigGeneric.DIFFERENT_VALUE,\n });\n this.setState({ value: ConfigGeneric.DIFFERENT_VALUE, selectOptions });\n } else {\n this.setState({ value, selectOptions });\n }\n }\n\n renderItem(error: unknown, disabled: boolean): JSX.Element | null {\n if (!this.state.selectOptions) {\n return null;\n }\n\n let item;\n const options: ConfigItemSelectOption[] = JSON.parse(JSON.stringify(this.state.selectOptions));\n const isIndeterminate = Array.isArray(this.state.value) || this.state.value === ConfigGeneric.DIFFERENT_VALUE;\n\n if (isIndeterminate) {\n [...this.state.value]\n .filter(val => !options.find(it => (typeof it === 'object' ? it.value === val : it === val)))\n .forEach(it => options.push({ label: it.toString(), value: it }));\n\n item = { label: I18n.t(ConfigGeneric.DIFFERENT_LABEL), value: ConfigGeneric.DIFFERENT_VALUE };\n options.unshift(item);\n } else {\n item =\n this.state.value !== null &&\n this.state.value !== undefined &&\n options.find(_item =>\n typeof _item === 'object' ? _item.value == this.state.value : _item == this.state.value,\n ); // let \"==\" be and not ===\n\n if (this.state.value !== null && this.state.value !== undefined && !item && this.props.schema.freeSolo) {\n item = { value: this.state.value, label: this.state.value };\n options.push(item);\n }\n }\n\n return (\n <Autocomplete\n fullWidth\n freeSolo={!!this.props.schema.freeSolo}\n value={item}\n options={options}\n isOptionEqualToValue={(option, value) => option.value === value.value}\n filterOptions={(options: { value: string; label: string }[], params) => {\n const filtered = options.filter(option => {\n if (params.inputValue === '') {\n return true;\n }\n return (\n option.label.toLowerCase().includes(params.inputValue.toLowerCase()) ||\n option.value.toLowerCase().includes(params.inputValue.toLowerCase())\n );\n });\n\n if (this.props.schema.freeSolo && params.inputValue !== '') {\n filtered.push({\n label: params.inputValue,\n value: params.inputValue,\n });\n }\n\n return filtered;\n }}\n // autoComplete\n onInputChange={e => {\n if (!e || !this.props.schema.freeSolo) {\n return;\n }\n\n const val = (e.target as HTMLInputElement).value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n onChange={(_, value) => {\n const val = typeof value === 'object' ? (value ? value.value : '') : value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n getOptionLabel={option => (typeof option === 'object' ? (option?.label ?? '') : '')}\n renderInput={params => (\n <TextField\n variant=\"standard\"\n {...params}\n error={!!error}\n // inputProps are important and will be given in params\n // inputProps={{maxLength: this.props.schema.maxLength || this.props.schema.max || undefined}}\n placeholder={this.getText(this.props.schema.placeholder)}\n label={this.getText(this.props.schema.label)}\n helperText={this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n disabled={disabled}\n />\n )}\n />\n );\n }\n}\n\nexport default ConfigAutocomplete;\n"]}
|
|
1
|
+
{"version":3,"file":"ConfigAutocomplete.js","sourceRoot":"./src/","sources":["JsonConfigComponent/ConfigAutocomplete.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type JSX } from 'react';\n\nimport { Autocomplete, TextField } from '@mui/material';\n\nimport { I18n } from '@iobroker/adapter-react-v5';\n\nimport type { ConfigItemAutocomplete, ConfigItemSelectOption } from '../types';\nimport ConfigGeneric, { type ConfigGenericState, type ConfigGenericProps } from './ConfigGeneric';\n\nexport interface ConfigAutocompleteProps extends ConfigGenericProps {\n schema: ConfigItemAutocomplete;\n}\n\nexport interface ConfigAutocompleteState extends ConfigGenericState {\n selectOptions: { value: string; label: string }[];\n}\n\nclass ConfigAutocomplete extends ConfigGeneric<ConfigAutocompleteProps, ConfigAutocompleteState> {\n async componentDidMount(): Promise<void> {\n await super.componentDidMount();\n const value = ConfigGeneric.getValue(this.props.data, this.props.attr);\n\n const selectOptions: { label: string; value: string }[] = this.props.schema.options.map(\n (item: { label: string; value: string } | string) =>\n typeof item === 'string' ? { label: item, value: item } : JSON.parse(JSON.stringify(item)),\n );\n\n // Report value-to-label mapping to parent table for filtering\n if (this.props.onFilterLabelUpdate && this.props.table) {\n const valueToLabel: Record<string, string> = {};\n for (const opt of selectOptions) {\n if (opt.value !== ConfigGeneric.DIFFERENT_VALUE) {\n valueToLabel[opt.value] = opt.label;\n }\n }\n this.props.onFilterLabelUpdate(this.props.attr, valueToLabel);\n }\n\n // if __different\n if (Array.isArray(value)) {\n selectOptions.unshift({\n label: I18n.t(ConfigGeneric.DIFFERENT_LABEL),\n value: ConfigGeneric.DIFFERENT_VALUE,\n });\n this.setState({ value: ConfigGeneric.DIFFERENT_VALUE, selectOptions });\n } else {\n this.setState({ value, selectOptions });\n }\n }\n\n renderItem(error: unknown, disabled: boolean): JSX.Element | null {\n if (!this.state.selectOptions) {\n return null;\n }\n\n let item;\n const options: ConfigItemSelectOption[] = JSON.parse(JSON.stringify(this.state.selectOptions));\n const isIndeterminate = Array.isArray(this.state.value) || this.state.value === ConfigGeneric.DIFFERENT_VALUE;\n\n if (isIndeterminate) {\n [...this.state.value]\n .filter(val => !options.find(it => (typeof it === 'object' ? it.value === val : it === val)))\n .forEach(it => options.push({ label: it.toString(), value: it }));\n\n item = { label: I18n.t(ConfigGeneric.DIFFERENT_LABEL), value: ConfigGeneric.DIFFERENT_VALUE };\n options.unshift(item);\n } else {\n item =\n this.state.value !== null &&\n this.state.value !== undefined &&\n options.find(_item =>\n typeof _item === 'object' ? _item.value == this.state.value : _item == this.state.value,\n ); // let \"==\" be and not ===\n\n if (this.state.value !== null && this.state.value !== undefined && !item && this.props.schema.freeSolo) {\n item = { value: this.state.value, label: this.state.value };\n options.push(item);\n }\n // never pass `false`/`undefined` as a controlled value to the Autocomplete (would switch it to uncontrolled)\n item ||= null;\n }\n\n return (\n <Autocomplete\n fullWidth\n freeSolo={!!this.props.schema.freeSolo}\n value={item}\n options={options}\n isOptionEqualToValue={(option, value) => option.value === value.value}\n filterOptions={(options: { value: string; label: string }[], params) => {\n const inputValue = params.inputValue.toLowerCase();\n const filtered = options.filter(option => {\n if (params.inputValue === '') {\n return true;\n }\n // label/value may be numbers (or null/undefined), so coerce to string before comparing\n // see https://github.com/ioBroker/ioBroker.admin/issues/3507\n return (\n String(option.label ?? '')\n .toLowerCase()\n .includes(inputValue) ||\n String(option.value ?? '')\n .toLowerCase()\n .includes(inputValue)\n );\n });\n\n if (this.props.schema.freeSolo && params.inputValue !== '') {\n filtered.push({\n label: params.inputValue,\n value: params.inputValue,\n });\n }\n\n return filtered;\n }}\n // autoComplete\n onInputChange={e => {\n if (!e || !this.props.schema.freeSolo) {\n return;\n }\n\n const val = (e.target as HTMLInputElement).value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n onChange={(_, value) => {\n const val = typeof value === 'object' ? (value ? value.value : '') : value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n getOptionLabel={option => (typeof option === 'object' ? (option?.label ?? '') : '')}\n renderInput={params => (\n <TextField\n variant=\"standard\"\n {...params}\n error={!!error}\n // inputProps are important and will be given in params\n // inputProps={{maxLength: this.props.schema.maxLength || this.props.schema.max || undefined}}\n placeholder={this.getText(this.props.schema.placeholder)}\n label={this.getText(this.props.schema.label)}\n helperText={this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n disabled={disabled}\n />\n )}\n />\n );\n }\n}\n\nexport default ConfigAutocomplete;\n"]}
|
|
@@ -142,12 +142,19 @@ export default class ConfigAutocompleteSendTo extends ConfigGeneric {
|
|
|
142
142
|
}, placeholder: this.getText(this.props.schema.placeholder), label: this.getText(this.props.schema.label), helperText: this.renderHelp(this.props.schema.help, this.props.schema.helpLink, this.props.schema.noTranslation) }));
|
|
143
143
|
}
|
|
144
144
|
return (React.createElement(Autocomplete, { value: item, fullWidth: true, freeSolo: !!this.props.schema.freeSolo, options: options, disabled: disabled, isOptionEqualToValue: (option, value) => option.value === value.value, filterOptions: (options, params) => {
|
|
145
|
+
const inputValue = params.inputValue.toLowerCase();
|
|
145
146
|
const filtered = options.filter(option => {
|
|
146
147
|
if (params.inputValue === '') {
|
|
147
148
|
return true;
|
|
148
149
|
}
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
// label/value may be numbers (or null/undefined), so coerce to string before comparing
|
|
151
|
+
// see https://github.com/ioBroker/ioBroker.admin/issues/3507
|
|
152
|
+
return (String(option.label ?? '')
|
|
153
|
+
.toLowerCase()
|
|
154
|
+
.includes(inputValue) ||
|
|
155
|
+
String(option.value ?? '')
|
|
156
|
+
.toLowerCase()
|
|
157
|
+
.includes(inputValue));
|
|
151
158
|
});
|
|
152
159
|
if (this.props.schema.freeSolo && params.inputValue !== '') {
|
|
153
160
|
filtered.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigAutocompleteSendTo.js","sourceRoot":"./src/","sources":["JsonConfigComponent/ConfigAutocompleteSendTo.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type JSX } from 'react';\n\nimport { Autocomplete, TextField, CircularProgress, InputAdornment } from '@mui/material';\n\nimport { I18n } from '@iobroker/adapter-react-v5';\n\nimport type { ConfigItemAutocompleteSendTo } from '../types';\nimport ConfigGeneric, { type ConfigGenericProps } from './ConfigGeneric';\nimport type { ConfigAutocompleteState } from './ConfigAutocomplete';\n\ninterface ConfigAutocompleteSendToProps extends ConfigGenericProps {\n schema: ConfigItemAutocompleteSendTo;\n}\n\ninterface ConfigAutocompleteSendToState extends ConfigAutocompleteState {\n loading?: boolean;\n}\n\nexport default class ConfigAutocompleteSendTo extends ConfigGeneric<\n ConfigAutocompleteSendToProps,\n ConfigAutocompleteSendToState\n> {\n private initialized = false;\n\n private localContext: string | undefined;\n\n async askInstance(): Promise<void> {\n const value = ConfigGeneric.getValue(this.props.data, this.props.attr);\n const selectOptions = this.props.schema.options\n ? this.props.schema.options.map((item: any) =>\n typeof item === 'string' ? { label: item, value: item } : JSON.parse(JSON.stringify(item)),\n )\n : [];\n\n if (this.props.alive) {\n let data = this.props.schema.data;\n if (data === undefined && this.props.schema.jsonData) {\n const dataStr: string = await this.getPatternAsync(this.props.schema.jsonData, null, true);\n try {\n if (typeof dataStr === 'string') {\n data = JSON.parse(dataStr);\n }\n } catch {\n console.error(`Cannot parse json data: ${JSON.stringify(data)}`);\n }\n }\n\n if (data === undefined) {\n data = null;\n }\n\n // Set loading state during sendTo request\n this.setState({ loading: true });\n const instance = await this.getPatternAsync(\n this.props.schema.instance || `${this.props.oContext.adapterName}.${this.props.oContext.instance}`,\n );\n // Check that instance is alive\n if (instance !== `${this.props.oContext.adapterName}.${this.props.oContext.instance}`) {\n const alive = await this.props.oContext.socket.getState(`system.adapter.${instance}.alive`);\n if (!alive?.val) {\n window.alert(I18n.t('ra_Instance %s is not alive', instance));\n return;\n }\n }\n void this.props.oContext.socket\n .sendTo(instance, this.props.schema.command || 'send', data)\n .then((list: unknown) => {\n if (list && Array.isArray(list)) {\n list.forEach(item =>\n selectOptions.push(\n typeof item === 'string'\n ? { label: item, value: item }\n : JSON.parse(JSON.stringify(item)),\n ),\n );\n }\n\n this.reportFilterLabels(selectOptions);\n\n // if __different\n if (Array.isArray(value)) {\n selectOptions.unshift({\n label: I18n.t(ConfigGeneric.DIFFERENT_LABEL),\n value: ConfigGeneric.DIFFERENT_VALUE,\n });\n this.setState({ value: ConfigGeneric.DIFFERENT_VALUE, selectOptions, loading: false });\n } else {\n this.setState({ value, selectOptions, loading: false });\n }\n })\n .catch(error => {\n console.error('Error in autocompleteSendTo:', error);\n // Clear loading state on error\n this.setState({ loading: false });\n });\n } else if (Array.isArray(value)) {\n // if __different\n selectOptions.unshift({\n label: I18n.t(ConfigGeneric.DIFFERENT_LABEL),\n value: ConfigGeneric.DIFFERENT_VALUE,\n });\n this.reportFilterLabels(selectOptions);\n this.setState({ value: ConfigGeneric.DIFFERENT_VALUE, selectOptions });\n } else {\n this.reportFilterLabels(selectOptions);\n this.setState({ value, selectOptions });\n }\n }\n\n /** Report value-to-label mapping to parent table for filtering */\n reportFilterLabels(options: { value: string; label: string }[]): void {\n if (this.props.onFilterLabelUpdate && this.props.table) {\n const valueToLabel: Record<string, string> = {};\n for (const opt of options) {\n if (opt.value !== ConfigGeneric.DIFFERENT_VALUE) {\n valueToLabel[opt.value] = opt.label;\n }\n }\n this.props.onFilterLabelUpdate(this.props.attr, valueToLabel);\n }\n }\n\n getContext(): string {\n const localContext: Record<string, any> = {};\n if (Array.isArray(this.props.schema.alsoDependsOn)) {\n this.props.schema.alsoDependsOn.forEach(\n attr => (localContext[attr] = ConfigGeneric.getValue(this.props.data, attr)),\n );\n }\n return JSON.stringify(localContext);\n }\n\n renderItem(error: unknown, disabled: boolean): JSX.Element | null {\n if (this.props.alive) {\n const localContext = this.getContext();\n if (localContext !== this.localContext || !this.initialized) {\n this.localContext = localContext;\n setTimeout(() => this.askInstance(), this.initialized ? 300 : 50);\n this.initialized = true;\n }\n }\n\n let item;\n const options: { value: string; label: string }[] = this.state.selectOptions\n ? JSON.parse(JSON.stringify(this.state.selectOptions))\n : [];\n const isIndeterminate = Array.isArray(this.state.value) || this.state.value === ConfigGeneric.DIFFERENT_LABEL;\n\n if (isIndeterminate) {\n [...this.state.value]\n .filter(val => !options.find((it: any) => it.value === val))\n .forEach(it => options.push({ label: it.toString(), value: it }));\n\n item = { label: I18n.t(ConfigGeneric.DIFFERENT_LABEL), value: ConfigGeneric.DIFFERENT_VALUE };\n options.unshift(item);\n } else {\n item =\n this.state.value !== null &&\n this.state.value !== undefined &&\n options.find((item: any) => item.value == this.state.value); // let \"==\" be and not ===\n\n if (this.state.value !== null && this.state.value !== undefined && !item && this.props.schema.freeSolo) {\n item = { value: this.state.value, label: this.state.value };\n options.push(item);\n }\n item = item || null;\n }\n\n if (!options.length) {\n return (\n <TextField\n variant=\"standard\"\n fullWidth\n value={this.state.value ?? ''}\n error={!!error}\n disabled={disabled}\n slotProps={{\n htmlInput: { maxLength: this.props.schema.maxLength || this.props.schema.max || undefined },\n input: {\n endAdornment: this.state.loading ? (\n <InputAdornment position=\"end\">\n <CircularProgress size={20} />\n </InputAdornment>\n ) : null,\n },\n }}\n onChange={e => {\n const value = e.target.value;\n this.setState({ value }, () => this.onChange(this.props.attr, (value || '').trim()));\n }}\n placeholder={this.getText(this.props.schema.placeholder)}\n label={this.getText(this.props.schema.label)}\n helperText={this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n />\n );\n }\n return (\n <Autocomplete\n value={item}\n fullWidth\n freeSolo={!!this.props.schema.freeSolo}\n options={options}\n disabled={disabled}\n isOptionEqualToValue={(option, value) => option.value === value.value}\n filterOptions={(options: { value: string; label: string }[], params) => {\n const filtered = options.filter(option => {\n if (params.inputValue === '') {\n return true;\n }\n return (\n option.label.toLowerCase().includes(params.inputValue.toLowerCase()) ||\n option.value.toLowerCase().includes(params.inputValue.toLowerCase())\n );\n });\n\n if (this.props.schema.freeSolo && params.inputValue !== '') {\n filtered.push({\n label: params.inputValue,\n value: params.inputValue,\n });\n }\n\n return filtered;\n }}\n getOptionLabel={(option: { value: string; label: string }): string => option?.label ?? ''}\n onInputChange={e => {\n if (!e || !this.props.schema.freeSolo) {\n return;\n }\n\n const val = (e.target as HTMLInputElement).value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n onChange={(_, value) => {\n const val = typeof value === 'object' ? (value ? value.value : '') : value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n renderInput={params => (\n <TextField\n variant=\"standard\"\n {...params}\n // inputProps are important and will be given in params\n // inputProps={{maxLength: this.props.schema.maxLength || this.props.schema.max || undefined}}\n error={!!error}\n placeholder={this.getText(this.props.schema.placeholder)}\n label={this.getText(this.props.schema.label)}\n helperText={this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n disabled={disabled}\n slotProps={{\n input: {\n ...params.InputProps,\n endAdornment: (\n <>\n {this.state.loading ? (\n <InputAdornment position=\"end\">\n <CircularProgress size={20} />\n </InputAdornment>\n ) : null}\n {disabled ? null : params.InputProps.endAdornment}\n </>\n ),\n },\n }}\n />\n )}\n />\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ConfigAutocompleteSendTo.js","sourceRoot":"./src/","sources":["JsonConfigComponent/ConfigAutocompleteSendTo.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type JSX } from 'react';\n\nimport { Autocomplete, TextField, CircularProgress, InputAdornment } from '@mui/material';\n\nimport { I18n } from '@iobroker/adapter-react-v5';\n\nimport type { ConfigItemAutocompleteSendTo } from '../types';\nimport ConfigGeneric, { type ConfigGenericProps } from './ConfigGeneric';\nimport type { ConfigAutocompleteState } from './ConfigAutocomplete';\n\ninterface ConfigAutocompleteSendToProps extends ConfigGenericProps {\n schema: ConfigItemAutocompleteSendTo;\n}\n\ninterface ConfigAutocompleteSendToState extends ConfigAutocompleteState {\n loading?: boolean;\n}\n\nexport default class ConfigAutocompleteSendTo extends ConfigGeneric<\n ConfigAutocompleteSendToProps,\n ConfigAutocompleteSendToState\n> {\n private initialized = false;\n\n private localContext: string | undefined;\n\n async askInstance(): Promise<void> {\n const value = ConfigGeneric.getValue(this.props.data, this.props.attr);\n const selectOptions = this.props.schema.options\n ? this.props.schema.options.map((item: any) =>\n typeof item === 'string' ? { label: item, value: item } : JSON.parse(JSON.stringify(item)),\n )\n : [];\n\n if (this.props.alive) {\n let data = this.props.schema.data;\n if (data === undefined && this.props.schema.jsonData) {\n const dataStr: string = await this.getPatternAsync(this.props.schema.jsonData, null, true);\n try {\n if (typeof dataStr === 'string') {\n data = JSON.parse(dataStr);\n }\n } catch {\n console.error(`Cannot parse json data: ${JSON.stringify(data)}`);\n }\n }\n\n if (data === undefined) {\n data = null;\n }\n\n // Set loading state during sendTo request\n this.setState({ loading: true });\n const instance = await this.getPatternAsync(\n this.props.schema.instance || `${this.props.oContext.adapterName}.${this.props.oContext.instance}`,\n );\n // Check that instance is alive\n if (instance !== `${this.props.oContext.adapterName}.${this.props.oContext.instance}`) {\n const alive = await this.props.oContext.socket.getState(`system.adapter.${instance}.alive`);\n if (!alive?.val) {\n window.alert(I18n.t('ra_Instance %s is not alive', instance));\n return;\n }\n }\n void this.props.oContext.socket\n .sendTo(instance, this.props.schema.command || 'send', data)\n .then((list: unknown) => {\n if (list && Array.isArray(list)) {\n list.forEach(item =>\n selectOptions.push(\n typeof item === 'string'\n ? { label: item, value: item }\n : JSON.parse(JSON.stringify(item)),\n ),\n );\n }\n\n this.reportFilterLabels(selectOptions);\n\n // if __different\n if (Array.isArray(value)) {\n selectOptions.unshift({\n label: I18n.t(ConfigGeneric.DIFFERENT_LABEL),\n value: ConfigGeneric.DIFFERENT_VALUE,\n });\n this.setState({ value: ConfigGeneric.DIFFERENT_VALUE, selectOptions, loading: false });\n } else {\n this.setState({ value, selectOptions, loading: false });\n }\n })\n .catch(error => {\n console.error('Error in autocompleteSendTo:', error);\n // Clear loading state on error\n this.setState({ loading: false });\n });\n } else if (Array.isArray(value)) {\n // if __different\n selectOptions.unshift({\n label: I18n.t(ConfigGeneric.DIFFERENT_LABEL),\n value: ConfigGeneric.DIFFERENT_VALUE,\n });\n this.reportFilterLabels(selectOptions);\n this.setState({ value: ConfigGeneric.DIFFERENT_VALUE, selectOptions });\n } else {\n this.reportFilterLabels(selectOptions);\n this.setState({ value, selectOptions });\n }\n }\n\n /** Report value-to-label mapping to parent table for filtering */\n reportFilterLabels(options: { value: string; label: string }[]): void {\n if (this.props.onFilterLabelUpdate && this.props.table) {\n const valueToLabel: Record<string, string> = {};\n for (const opt of options) {\n if (opt.value !== ConfigGeneric.DIFFERENT_VALUE) {\n valueToLabel[opt.value] = opt.label;\n }\n }\n this.props.onFilterLabelUpdate(this.props.attr, valueToLabel);\n }\n }\n\n getContext(): string {\n const localContext: Record<string, any> = {};\n if (Array.isArray(this.props.schema.alsoDependsOn)) {\n this.props.schema.alsoDependsOn.forEach(\n attr => (localContext[attr] = ConfigGeneric.getValue(this.props.data, attr)),\n );\n }\n return JSON.stringify(localContext);\n }\n\n renderItem(error: unknown, disabled: boolean): JSX.Element | null {\n if (this.props.alive) {\n const localContext = this.getContext();\n if (localContext !== this.localContext || !this.initialized) {\n this.localContext = localContext;\n setTimeout(() => this.askInstance(), this.initialized ? 300 : 50);\n this.initialized = true;\n }\n }\n\n let item;\n const options: { value: string; label: string }[] = this.state.selectOptions\n ? JSON.parse(JSON.stringify(this.state.selectOptions))\n : [];\n const isIndeterminate = Array.isArray(this.state.value) || this.state.value === ConfigGeneric.DIFFERENT_LABEL;\n\n if (isIndeterminate) {\n [...this.state.value]\n .filter(val => !options.find((it: any) => it.value === val))\n .forEach(it => options.push({ label: it.toString(), value: it }));\n\n item = { label: I18n.t(ConfigGeneric.DIFFERENT_LABEL), value: ConfigGeneric.DIFFERENT_VALUE };\n options.unshift(item);\n } else {\n item =\n this.state.value !== null &&\n this.state.value !== undefined &&\n options.find((item: any) => item.value == this.state.value); // let \"==\" be and not ===\n\n if (this.state.value !== null && this.state.value !== undefined && !item && this.props.schema.freeSolo) {\n item = { value: this.state.value, label: this.state.value };\n options.push(item);\n }\n item = item || null;\n }\n\n if (!options.length) {\n return (\n <TextField\n variant=\"standard\"\n fullWidth\n value={this.state.value ?? ''}\n error={!!error}\n disabled={disabled}\n slotProps={{\n htmlInput: { maxLength: this.props.schema.maxLength || this.props.schema.max || undefined },\n input: {\n endAdornment: this.state.loading ? (\n <InputAdornment position=\"end\">\n <CircularProgress size={20} />\n </InputAdornment>\n ) : null,\n },\n }}\n onChange={e => {\n const value = e.target.value;\n this.setState({ value }, () => this.onChange(this.props.attr, (value || '').trim()));\n }}\n placeholder={this.getText(this.props.schema.placeholder)}\n label={this.getText(this.props.schema.label)}\n helperText={this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n />\n );\n }\n return (\n <Autocomplete\n value={item}\n fullWidth\n freeSolo={!!this.props.schema.freeSolo}\n options={options}\n disabled={disabled}\n isOptionEqualToValue={(option, value) => option.value === value.value}\n filterOptions={(options: { value: string; label: string }[], params) => {\n const inputValue = params.inputValue.toLowerCase();\n const filtered = options.filter(option => {\n if (params.inputValue === '') {\n return true;\n }\n // label/value may be numbers (or null/undefined), so coerce to string before comparing\n // see https://github.com/ioBroker/ioBroker.admin/issues/3507\n return (\n String(option.label ?? '')\n .toLowerCase()\n .includes(inputValue) ||\n String(option.value ?? '')\n .toLowerCase()\n .includes(inputValue)\n );\n });\n\n if (this.props.schema.freeSolo && params.inputValue !== '') {\n filtered.push({\n label: params.inputValue,\n value: params.inputValue,\n });\n }\n\n return filtered;\n }}\n getOptionLabel={(option: { value: string; label: string }): string => option?.label ?? ''}\n onInputChange={e => {\n if (!e || !this.props.schema.freeSolo) {\n return;\n }\n\n const val = (e.target as HTMLInputElement).value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n onChange={(_, value) => {\n const val = typeof value === 'object' ? (value ? value.value : '') : value;\n if (val !== this.state.value) {\n this.setState({ value: val }, () => this.onChange(this.props.attr, val));\n }\n }}\n renderInput={params => (\n <TextField\n variant=\"standard\"\n {...params}\n // inputProps are important and will be given in params\n // inputProps={{maxLength: this.props.schema.maxLength || this.props.schema.max || undefined}}\n error={!!error}\n placeholder={this.getText(this.props.schema.placeholder)}\n label={this.getText(this.props.schema.label)}\n helperText={this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n disabled={disabled}\n slotProps={{\n input: {\n ...params.InputProps,\n endAdornment: (\n <>\n {this.state.loading ? (\n <InputAdornment position=\"end\">\n <CircularProgress size={20} />\n </InputAdornment>\n ) : null}\n {disabled ? null : params.InputProps.endAdornment}\n </>\n ),\n },\n }}\n />\n )}\n />\n );\n }\n}\n"]}
|
|
@@ -1,18 +1,45 @@
|
|
|
1
1
|
import { type JSX } from 'react';
|
|
2
2
|
import type { ConfigItemCredentialSelect } from '../types';
|
|
3
3
|
import ConfigGeneric, { type ConfigGenericProps, type ConfigGenericState } from './ConfigGeneric';
|
|
4
|
+
type CredentialType = 'email' | 'cloud' | 'ai' | 'custom';
|
|
4
5
|
interface ConfigCredentialSelectProps extends ConfigGenericProps {
|
|
5
6
|
schema: ConfigItemCredentialSelect;
|
|
6
7
|
}
|
|
8
|
+
interface CredentialSelectOption {
|
|
9
|
+
label: string;
|
|
10
|
+
value: string;
|
|
11
|
+
/** Icon of the credential (data URL from `common.icon`) */
|
|
12
|
+
icon?: string;
|
|
13
|
+
}
|
|
7
14
|
interface ConfigCredentialSelectState extends ConfigGenericState {
|
|
8
|
-
selectOptions?:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
selectOptions?: CredentialSelectOption[];
|
|
16
|
+
/** Whether the "create credential" dialog is open */
|
|
17
|
+
addOpen?: boolean;
|
|
18
|
+
/** Selected template key */
|
|
19
|
+
addTemplate?: string;
|
|
20
|
+
addName?: string;
|
|
21
|
+
addType?: CredentialType;
|
|
22
|
+
addFields?: Record<string, string>;
|
|
23
|
+
addError?: string;
|
|
24
|
+
addSaving?: boolean;
|
|
12
25
|
}
|
|
13
26
|
export default class ConfigCredentialSelect extends ConfigGeneric<ConfigCredentialSelectProps, ConfigCredentialSelectState> {
|
|
14
27
|
componentDidMount(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Read the credential objects ("system.credentials.<name>") and turn them into select options,
|
|
30
|
+
* filtered by `schema.credentialType` if set. The "none" option is prepended.
|
|
31
|
+
*/
|
|
32
|
+
readCredentials(): Promise<CredentialSelectOption[]>;
|
|
15
33
|
static getCredentialName(obj: ioBroker.Object): string;
|
|
34
|
+
/** Template keys offered for this field, filtered by `schema.credentialType` (type-agnostic ones always shown). */
|
|
35
|
+
templateKeys(): string[];
|
|
36
|
+
/** Apply a template: it defines the form, the (proposed) name, the category and the icon. */
|
|
37
|
+
selectTemplate(key: string): void;
|
|
38
|
+
/** Open the create dialog, pre-selecting the first template that fits the schema's credentialType. */
|
|
39
|
+
openAddDialog(): void;
|
|
40
|
+
/** Create the credential object, encrypt its secret fields, store it (with icon), then select it. */
|
|
41
|
+
createCredential(): Promise<void>;
|
|
42
|
+
renderAddDialog(): JSX.Element | null;
|
|
16
43
|
renderItem(error: unknown, disabled: boolean): JSX.Element | null;
|
|
17
44
|
}
|
|
18
45
|
export {};
|
|
@@ -1,26 +1,93 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { InputLabel, MenuItem, FormControl, Select, FormHelperText } from '@mui/material';
|
|
3
|
-
import {
|
|
2
|
+
import { InputLabel, MenuItem, FormControl, Select, FormHelperText, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, Alert, } from '@mui/material';
|
|
3
|
+
import { Add as AddIcon, Check as CheckIcon, Close as CloseIcon } from '@mui/icons-material';
|
|
4
|
+
import { I18n, Icon, Utils } from '@iobroker/adapter-react-v5';
|
|
4
5
|
import ConfigGeneric from './ConfigGeneric';
|
|
5
6
|
/** Prefix of all credential object IDs. Synchronized with `@iobroker/adapter-core` (src/credentials.ts) */
|
|
6
7
|
const CREDENTIALS_PREFIX = 'system.credentials.';
|
|
8
|
+
/** Current version of the credential data format (keep in sync with `@iobroker/adapter-core`). */
|
|
9
|
+
const CREDENTIALS_VERSION = 1;
|
|
10
|
+
const CREDENTIAL_TYPES = ['email', 'cloud', 'ai', 'custom'];
|
|
11
|
+
/** Readable labels for the category selector (translated where a key exists, English fallback otherwise). */
|
|
12
|
+
const CREDENTIAL_TYPE_LABELS = {
|
|
13
|
+
email: 'E-mail',
|
|
14
|
+
cloud: 'Cloud',
|
|
15
|
+
ai: 'AI',
|
|
16
|
+
custom: 'Custom',
|
|
17
|
+
};
|
|
18
|
+
/** The two credential forms and their fields (keep in sync with admin `credentialTypes.ts`). */
|
|
19
|
+
const CREDENTIAL_FORMS = {
|
|
20
|
+
login: [
|
|
21
|
+
{ name: 'login', type: 'text', required: true, label: 'Login' },
|
|
22
|
+
{ name: 'password', type: 'password', encrypted: true, required: true, label: 'Password' },
|
|
23
|
+
],
|
|
24
|
+
key: [{ name: 'key', type: 'password', encrypted: true, required: true, label: 'Key' }],
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* SVG paths in a 24x24 viewBox: brand logos from https://simpleicons.org (CC0), the rest Material icons.
|
|
28
|
+
* Kept in sync with admin `CredentialsDialog.tsx`.
|
|
29
|
+
*/
|
|
30
|
+
const ICON_PATHS = {
|
|
31
|
+
anthropic: 'M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5527h3.7442L10.5363 3.541Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z',
|
|
32
|
+
chatgpt: 'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.073zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.8956zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654 2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z',
|
|
33
|
+
gemini: 'M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81',
|
|
34
|
+
email: 'M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z',
|
|
35
|
+
login: 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z',
|
|
36
|
+
key: 'M21 10h-8.35A5.99 5.99 0 0 0 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6a5.99 5.99 0 0 0 5.65-4H13l2 2 2-2 2 2 4-4.04L21 10zM7 15c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z',
|
|
37
|
+
};
|
|
38
|
+
/** Convert an SVG path to a base64 data URL (the SVG content is pure ASCII, so `btoa` can encode it directly). */
|
|
39
|
+
function svgDataUrl(path, color) {
|
|
40
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="${color}" d="${path}"/></svg>`;
|
|
41
|
+
return `data:image/svg+xml;base64,${window.btoa(svg)}`;
|
|
42
|
+
}
|
|
43
|
+
/** Icon data URLs per template key — stored in `common.icon` of the created credential and shown in the UI. */
|
|
44
|
+
const ICON_DATA = {
|
|
45
|
+
anthropic: svgDataUrl(ICON_PATHS.anthropic, '#d97757'),
|
|
46
|
+
chatgpt: svgDataUrl(ICON_PATHS.chatgpt, '#74aa9c'),
|
|
47
|
+
gemini: svgDataUrl(ICON_PATHS.gemini, '#8e75b2'),
|
|
48
|
+
email: svgDataUrl(ICON_PATHS.email, '#2196f3'),
|
|
49
|
+
login: svgDataUrl(ICON_PATHS.login, '#9e9e9e'),
|
|
50
|
+
key: svgDataUrl(ICON_PATHS.key, '#ffc107'),
|
|
51
|
+
};
|
|
52
|
+
/** Templates offered in the "Add credential" dialog (a focused subset of the admin's templates). */
|
|
53
|
+
const CREDENTIAL_TEMPLATES = {
|
|
54
|
+
anthropic: { label: 'Anthropic', icon: ICON_DATA.anthropic, form: 'key', type: 'ai', name: 'anthropic' },
|
|
55
|
+
chatgpt: { label: 'ChatGPT', icon: ICON_DATA.chatgpt, form: 'key', type: 'ai', name: 'chatgpt' },
|
|
56
|
+
gemini: { label: 'Google Gemini', icon: ICON_DATA.gemini, form: 'key', type: 'ai', name: 'gemini' },
|
|
57
|
+
email: { label: 'E-mail', icon: ICON_DATA.email, form: 'login', type: 'email' },
|
|
58
|
+
login: { label: 'Login & password', icon: ICON_DATA.login, form: 'login', type: null },
|
|
59
|
+
key: { label: 'Key', icon: ICON_DATA.key, form: 'key', type: null },
|
|
60
|
+
};
|
|
61
|
+
function renderCredentialItem(option, label, anyIcon) {
|
|
62
|
+
return (React.createElement("span", { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
63
|
+
option?.icon ? (React.createElement(Icon, { src: option.icon, style: { width: 20, height: 20 } })) : anyIcon ? (
|
|
64
|
+
// if at least one option has an icon, keep the labels aligned
|
|
65
|
+
React.createElement("span", { style: { width: 20, height: 20, flexShrink: 0 } })) : null,
|
|
66
|
+
label));
|
|
67
|
+
}
|
|
7
68
|
export default class ConfigCredentialSelect extends ConfigGeneric {
|
|
8
69
|
async componentDidMount() {
|
|
9
70
|
await super.componentDidMount();
|
|
10
71
|
const value = ConfigGeneric.getValue(this.props.data, this.props.attr);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
72
|
+
const selectOptions = await this.readCredentials();
|
|
73
|
+
this.setState({ value, selectOptions });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read the credential objects ("system.credentials.<name>") and turn them into select options,
|
|
77
|
+
* filtered by `schema.credentialType` if set. The "none" option is prepended.
|
|
78
|
+
*/
|
|
79
|
+
async readCredentials() {
|
|
16
80
|
let selectOptions = [];
|
|
17
81
|
try {
|
|
18
|
-
const objs = await this.props.oContext.socket.getObjectViewSystem('config',
|
|
82
|
+
const objs = await this.props.oContext.socket.getObjectViewSystem('config', CREDENTIALS_PREFIX, `${CREDENTIALS_PREFIX}香`);
|
|
19
83
|
selectOptions = Object.values(objs)
|
|
20
|
-
.filter(obj => !!obj
|
|
84
|
+
.filter(obj => !!obj &&
|
|
85
|
+
(!this.props.schema.credentialType ||
|
|
86
|
+
obj.native?.type === this.props.schema.credentialType))
|
|
21
87
|
.map(obj => ({
|
|
22
88
|
label: ConfigCredentialSelect.getCredentialName(obj),
|
|
23
89
|
value: obj._id,
|
|
90
|
+
icon: typeof obj.common?.icon === 'string' ? obj.common.icon : undefined,
|
|
24
91
|
}))
|
|
25
92
|
.sort((a, b) => a.label.localeCompare(b.label));
|
|
26
93
|
}
|
|
@@ -28,7 +95,7 @@ export default class ConfigCredentialSelect extends ConfigGeneric {
|
|
|
28
95
|
console.error(`Cannot read credentials: ${e}`);
|
|
29
96
|
}
|
|
30
97
|
selectOptions.unshift({ label: I18n.t(ConfigGeneric.NONE_LABEL), value: ConfigGeneric.NONE_VALUE });
|
|
31
|
-
|
|
98
|
+
return selectOptions;
|
|
32
99
|
}
|
|
33
100
|
static getCredentialName(obj) {
|
|
34
101
|
const name = obj.common?.name;
|
|
@@ -41,6 +108,139 @@ export default class ConfigCredentialSelect extends ConfigGeneric {
|
|
|
41
108
|
}
|
|
42
109
|
return text || obj._id.substring(CREDENTIALS_PREFIX.length);
|
|
43
110
|
}
|
|
111
|
+
/** Template keys offered for this field, filtered by `schema.credentialType` (type-agnostic ones always shown). */
|
|
112
|
+
templateKeys() {
|
|
113
|
+
const credentialType = this.props.schema.credentialType;
|
|
114
|
+
return Object.keys(CREDENTIAL_TEMPLATES).filter(key => {
|
|
115
|
+
const template = CREDENTIAL_TEMPLATES[key];
|
|
116
|
+
return !credentialType || template.type === credentialType || template.type === null;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Apply a template: it defines the form, the (proposed) name, the category and the icon. */
|
|
120
|
+
selectTemplate(key) {
|
|
121
|
+
const template = CREDENTIAL_TEMPLATES[key];
|
|
122
|
+
this.setState({
|
|
123
|
+
addTemplate: key,
|
|
124
|
+
addName: template.name || '',
|
|
125
|
+
addType: template.type || this.props.schema.credentialType || 'custom',
|
|
126
|
+
addFields: {},
|
|
127
|
+
addError: '',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** Open the create dialog, pre-selecting the first template that fits the schema's credentialType. */
|
|
131
|
+
openAddDialog() {
|
|
132
|
+
const keys = this.templateKeys();
|
|
133
|
+
const addTemplate = keys[0];
|
|
134
|
+
const template = CREDENTIAL_TEMPLATES[addTemplate];
|
|
135
|
+
this.setState({
|
|
136
|
+
addOpen: true,
|
|
137
|
+
addTemplate,
|
|
138
|
+
addName: template.name || '',
|
|
139
|
+
addType: template.type || this.props.schema.credentialType || 'custom',
|
|
140
|
+
addFields: {},
|
|
141
|
+
addError: '',
|
|
142
|
+
addSaving: false,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/** Create the credential object, encrypt its secret fields, store it (with icon), then select it. */
|
|
146
|
+
async createCredential() {
|
|
147
|
+
const template = CREDENTIAL_TEMPLATES[this.state.addTemplate || ''];
|
|
148
|
+
const name = (this.state.addName || '').trim().replace(Utils.FORBIDDEN_CHARS, '_');
|
|
149
|
+
if (!name) {
|
|
150
|
+
this.setState({ addError: I18n.t('A unique name is required') });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const id = `${CREDENTIALS_PREFIX}${name}`;
|
|
154
|
+
if ((this.state.selectOptions || []).find(option => option.value === id)) {
|
|
155
|
+
this.setState({ addError: I18n.t('A credential with this name already exists') });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const form = template?.form || 'login';
|
|
159
|
+
const fields = CREDENTIAL_FORMS[form];
|
|
160
|
+
const addFields = this.state.addFields || {};
|
|
161
|
+
if (fields.some(field => field.required && !(addFields[field.name] || '').trim())) {
|
|
162
|
+
this.setState({ addError: I18n.t('Please fill in all required fields') });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const type = template?.type || this.props.schema.credentialType || this.state.addType || 'custom';
|
|
166
|
+
this.setState({ addSaving: true, addError: '' });
|
|
167
|
+
try {
|
|
168
|
+
// The actual socket is always an AdminConnection at runtime (only admin can write credentials);
|
|
169
|
+
// `encrypt` uses the system secret, exactly as the admin "Credentials" dialog does.
|
|
170
|
+
const socket = this.props.oContext.socket;
|
|
171
|
+
const native = {
|
|
172
|
+
type,
|
|
173
|
+
version: CREDENTIALS_VERSION,
|
|
174
|
+
encryptedFields: fields.filter(field => field.encrypted).map(field => field.name),
|
|
175
|
+
};
|
|
176
|
+
for (const field of fields) {
|
|
177
|
+
const raw = addFields[field.name] || '';
|
|
178
|
+
native[field.name] = field.encrypted && raw ? await socket.encrypt(raw) : raw;
|
|
179
|
+
}
|
|
180
|
+
const obj = {
|
|
181
|
+
_id: id,
|
|
182
|
+
type: 'config',
|
|
183
|
+
common: { name, ...(template?.icon ? { icon: template.icon } : {}) },
|
|
184
|
+
native,
|
|
185
|
+
// Only the admin may read credentials.
|
|
186
|
+
acl: {
|
|
187
|
+
object: 0x600,
|
|
188
|
+
owner: 'system.user.admin',
|
|
189
|
+
ownerGroup: 'system.group.administrator',
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
await socket.setObject(id, obj);
|
|
193
|
+
// Insert the new credential into the options (keeping "none" on top), then select it.
|
|
194
|
+
const existing = this.state.selectOptions || [];
|
|
195
|
+
const none = existing.find(option => option.value === ConfigGeneric.NONE_VALUE);
|
|
196
|
+
const rest = existing.filter(option => option.value !== ConfigGeneric.NONE_VALUE);
|
|
197
|
+
rest.push({ label: name, value: id, icon: template?.icon });
|
|
198
|
+
rest.sort((a, b) => a.label.localeCompare(b.label));
|
|
199
|
+
const selectOptions = none ? [none, ...rest] : rest;
|
|
200
|
+
this.setState({ addOpen: false, addSaving: false, selectOptions, value: id }, () => this.onChange(this.props.attr, id));
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
this.setState({
|
|
204
|
+
addSaving: false,
|
|
205
|
+
addError: I18n.t('Cannot create credential: %s', e.toString()),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
renderAddDialog() {
|
|
210
|
+
if (!this.state.addOpen) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const template = CREDENTIAL_TEMPLATES[this.state.addTemplate || ''];
|
|
214
|
+
const form = template?.form || 'login';
|
|
215
|
+
const fields = CREDENTIAL_FORMS[form];
|
|
216
|
+
const id = `${CREDENTIALS_PREFIX}${(this.state.addName || '').trim().replace(Utils.FORBIDDEN_CHARS, '_')}`;
|
|
217
|
+
// The category can only be chosen for type-agnostic templates when the schema doesn't pin a type.
|
|
218
|
+
const showCategory = !this.props.schema.credentialType && template?.type === null;
|
|
219
|
+
return (React.createElement(Dialog, { open: true, maxWidth: "sm", fullWidth: true, onClose: () => this.setState({ addOpen: false }) },
|
|
220
|
+
React.createElement(DialogTitle, null, I18n.t('Add credential')),
|
|
221
|
+
React.createElement(DialogContent, { style: { display: 'flex', flexDirection: 'column', gap: 12, paddingTop: 8 } },
|
|
222
|
+
React.createElement(FormControl, { fullWidth: true, variant: "standard" },
|
|
223
|
+
React.createElement(InputLabel, { shrink: true }, I18n.t('Template')),
|
|
224
|
+
React.createElement(Select, { variant: "standard", value: this.state.addTemplate || '', onChange: e => this.selectTemplate(e.target.value) }, this.templateKeys().map(key => (React.createElement(MenuItem, { key: key, value: key },
|
|
225
|
+
React.createElement("span", { style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
226
|
+
React.createElement("img", { src: CREDENTIAL_TEMPLATES[key].icon, width: 20, height: 20, alt: "" }),
|
|
227
|
+
I18n.t(CREDENTIAL_TEMPLATES[key].label))))))),
|
|
228
|
+
showCategory ? (React.createElement(FormControl, { fullWidth: true, variant: "standard" },
|
|
229
|
+
React.createElement(InputLabel, { shrink: true }, I18n.t('Credential type')),
|
|
230
|
+
React.createElement(Select, { variant: "standard", value: this.state.addType || 'custom', onChange: e => this.setState({ addType: e.target.value }) }, CREDENTIAL_TYPES.map(type => (React.createElement(MenuItem, { key: type, value: type }, I18n.t(CREDENTIAL_TYPE_LABELS[type]))))))) : null,
|
|
231
|
+
React.createElement(TextField, { variant: "standard", fullWidth: true, label: I18n.t('ra_Name'), value: this.state.addName || '', error: !!this.state.addError, helperText: this.state.addName ? id : '', slotProps: { inputLabel: { shrink: true }, htmlInput: { autoComplete: 'off' } }, onChange: e => this.setState({ addName: e.target.value, addError: '' }) }),
|
|
232
|
+
fields.map(field => (React.createElement(TextField, { key: field.name, variant: "standard", fullWidth: true, type: field.type === 'password' ? 'password' : 'text', required: field.required, label: I18n.t(field.label), value: this.state.addFields?.[field.name] || '', slotProps: {
|
|
233
|
+
inputLabel: { shrink: true },
|
|
234
|
+
htmlInput: { autoComplete: field.type === 'password' ? 'new-password' : 'off' },
|
|
235
|
+
}, onChange: e => this.setState({
|
|
236
|
+
addFields: { ...(this.state.addFields || {}), [field.name]: e.target.value },
|
|
237
|
+
addError: '',
|
|
238
|
+
}) }))),
|
|
239
|
+
this.state.addError ? React.createElement(Alert, { severity: "error" }, this.state.addError) : null),
|
|
240
|
+
React.createElement(DialogActions, null,
|
|
241
|
+
React.createElement(Button, { variant: "contained", color: "primary", disabled: !!this.state.addSaving || !(this.state.addName || '').trim(), startIcon: React.createElement(CheckIcon, null), onClick: () => this.createCredential() }, I18n.t('ra_Create')),
|
|
242
|
+
React.createElement(Button, { variant: "contained", color: "grey", disabled: !!this.state.addSaving, startIcon: React.createElement(CloseIcon, null), onClick: () => this.setState({ addOpen: false }) }, I18n.t('ra_Cancel')))));
|
|
243
|
+
}
|
|
44
244
|
renderItem(error, disabled /* , defaultValue */) {
|
|
45
245
|
if (!this.state.selectOptions) {
|
|
46
246
|
return null;
|
|
@@ -48,12 +248,20 @@ export default class ConfigCredentialSelect extends ConfigGeneric {
|
|
|
48
248
|
const item = this.state.selectOptions?.find(_item => _item.value === this.state.value);
|
|
49
249
|
// The stored value could point to a meanwhile deleted credential
|
|
50
250
|
const unknownValue = this.state.value && this.state.value !== ConfigGeneric.NONE_VALUE && !item ? this.state.value : null;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
251
|
+
// if at least one option has an icon, options without icon get a placeholder for alignment
|
|
252
|
+
const anyIcon = this.state.selectOptions.some(option => !!option.icon);
|
|
253
|
+
// creation can be suppressed per schema (`disableCreation`)
|
|
254
|
+
const canCreate = !this.props.schema.disableCreation;
|
|
255
|
+
return (React.createElement("div", { style: { display: 'flex', alignItems: 'flex-end', gap: 4, width: '100%' } },
|
|
256
|
+
React.createElement(FormControl, { style: { flex: 1, minWidth: 0 }, variant: "standard" },
|
|
257
|
+
this.props.schema.label ? (React.createElement(InputLabel, { shrink: true }, this.getText(this.props.schema.label))) : null,
|
|
258
|
+
React.createElement(Select, { variant: "standard", error: !!error || !!unknownValue, displayEmpty: true, disabled: !!disabled, value: this.state.value || ConfigGeneric.NONE_VALUE, renderValue: () => unknownValue
|
|
259
|
+
? unknownValue
|
|
260
|
+
: renderCredentialItem(item, this.getText(item?.label, this.props.schema.noTranslation !== false), anyIcon), onChange: e => this.setState({ value: e.target.value === ConfigGeneric.NONE_VALUE ? '' : e.target.value }, () => this.onChange(this.props.attr, this.state.value)) }, this.state.selectOptions?.map(item_ => (React.createElement(MenuItem, { key: item_.value, value: item_.value, style: item_.value === ConfigGeneric.NONE_VALUE ? { opacity: 0.5 } : {} }, renderCredentialItem(item_, this.getText(item_.label, this.props.schema.noTranslation !== false), anyIcon))))),
|
|
261
|
+
this.props.schema.help ? (React.createElement(FormHelperText, null, this.renderHelp(this.props.schema.help, this.props.schema.helpLink, this.props.schema.noTranslation))) : null),
|
|
262
|
+
canCreate ? (React.createElement(IconButton, { size: "small", disabled: !!disabled, title: I18n.t('Add credential'), onClick: () => this.openAddDialog() },
|
|
263
|
+
React.createElement(AddIcon, null))) : null,
|
|
264
|
+
this.renderAddDialog()));
|
|
57
265
|
}
|
|
58
266
|
}
|
|
59
267
|
//# sourceMappingURL=ConfigCredentialSelect.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigCredentialSelect.js","sourceRoot":"./src/","sources":["JsonConfigComponent/ConfigCredentialSelect.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type JSX } from 'react';\n\nimport { InputLabel, MenuItem, FormControl, Select, FormHelperText } from '@mui/material';\n\nimport { I18n } from '@iobroker/adapter-react-v5';\nimport type { ConfigItemCredentialSelect } from '../types';\nimport ConfigGeneric, { type ConfigGenericProps, type ConfigGenericState } from './ConfigGeneric';\n\n/** Prefix of all credential object IDs. Synchronized with `@iobroker/adapter-core` (src/credentials.ts) */\nconst CREDENTIALS_PREFIX = 'system.credentials.';\n\ninterface ConfigCredentialSelectProps extends ConfigGenericProps {\n schema: ConfigItemCredentialSelect;\n}\n\ninterface ConfigCredentialSelectState extends ConfigGenericState {\n selectOptions?: { label: string; value: string }[];\n}\n\nexport default class ConfigCredentialSelect extends ConfigGeneric<\n ConfigCredentialSelectProps,\n ConfigCredentialSelectState\n> {\n async componentDidMount(): Promise<void> {\n await super.componentDidMount();\n const value = ConfigGeneric.getValue(this.props.data, this.props.attr);\n\n // Credentials are managed in admin: Settings -> Credentials.\n // They are stored as objects \"system.credentials.<type>.<name>\".\n const prefix = this.props.schema.credentialType\n ? `${CREDENTIALS_PREFIX}${this.props.schema.credentialType}.`\n : CREDENTIALS_PREFIX;\n\n let selectOptions: { label: string; value: string }[] = [];\n try {\n const objs = await this.props.oContext.socket.getObjectViewSystem('config', prefix, `${prefix}香`);\n selectOptions = Object.values(objs)\n .filter(obj => !!obj)\n .map(obj => ({\n label: ConfigCredentialSelect.getCredentialName(obj as ioBroker.Object),\n value: obj._id,\n }))\n .sort((a, b) => a.label.localeCompare(b.label));\n } catch (e) {\n console.error(`Cannot read credentials: ${e}`);\n }\n\n selectOptions.unshift({ label: I18n.t(ConfigGeneric.NONE_LABEL), value: ConfigGeneric.NONE_VALUE });\n\n this.setState({ value, selectOptions });\n }\n\n static getCredentialName(obj: ioBroker.Object): string {\n const name = obj.common?.name;\n let text: string;\n if (name && typeof name === 'object') {\n text = name[I18n.getLanguage()] || name.en || Object.values(name)[0] || '';\n } else {\n text = (name as string) || '';\n }\n return text || obj._id.substring(CREDENTIALS_PREFIX.length);\n }\n\n renderItem(error: unknown, disabled: boolean /* , defaultValue */): JSX.Element | null {\n if (!this.state.selectOptions) {\n return null;\n }\n\n const item = this.state.selectOptions?.find(_item => _item.value === this.state.value);\n // The stored value could point to a meanwhile deleted credential\n const unknownValue =\n this.state.value && this.state.value !== ConfigGeneric.NONE_VALUE && !item ? this.state.value : null;\n\n return (\n <FormControl\n style={{ width: '100%' }}\n variant=\"standard\"\n >\n {this.props.schema.label ? (\n <InputLabel shrink>{this.getText(this.props.schema.label)}</InputLabel>\n ) : null}\n <Select\n variant=\"standard\"\n error={!!error || !!unknownValue}\n displayEmpty\n disabled={!!disabled}\n value={this.state.value || ConfigGeneric.NONE_VALUE}\n renderValue={() =>\n unknownValue\n ? unknownValue\n : this.getText(item?.label, this.props.schema.noTranslation !== false)\n }\n onChange={e =>\n this.setState(\n { value: e.target.value === ConfigGeneric.NONE_VALUE ? '' : e.target.value },\n () => this.onChange(this.props.attr, this.state.value),\n )\n }\n >\n {this.state.selectOptions?.map(item_ => (\n <MenuItem\n key={item_.value}\n value={item_.value}\n style={item_.value === ConfigGeneric.NONE_VALUE ? { opacity: 0.5 } : {}}\n >\n {this.getText(item_.label, this.props.schema.noTranslation !== false)}\n </MenuItem>\n ))}\n </Select>\n {this.props.schema.help ? (\n <FormHelperText>\n {this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n </FormHelperText>\n ) : null}\n </FormControl>\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ConfigCredentialSelect.js","sourceRoot":"./src/","sources":["JsonConfigComponent/ConfigCredentialSelect.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { type JSX } from 'react';\n\nimport {\n InputLabel,\n MenuItem,\n FormControl,\n Select,\n FormHelperText,\n IconButton,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n TextField,\n Alert,\n} from '@mui/material';\nimport { Add as AddIcon, Check as CheckIcon, Close as CloseIcon } from '@mui/icons-material';\n\nimport { I18n, Icon, Utils } from '@iobroker/adapter-react-v5';\nimport type { ConfigItemCredentialSelect } from '../types';\nimport ConfigGeneric, { type ConfigGenericProps, type ConfigGenericState } from './ConfigGeneric';\n\n/** Prefix of all credential object IDs. Synchronized with `@iobroker/adapter-core` (src/credentials.ts) */\nconst CREDENTIALS_PREFIX = 'system.credentials.';\n\n/** Current version of the credential data format (keep in sync with `@iobroker/adapter-core`). */\nconst CREDENTIALS_VERSION = 1;\n\ntype CredentialType = 'email' | 'cloud' | 'ai' | 'custom';\nconst CREDENTIAL_TYPES: CredentialType[] = ['email', 'cloud', 'ai', 'custom'];\n/** Readable labels for the category selector (translated where a key exists, English fallback otherwise). */\nconst CREDENTIAL_TYPE_LABELS: Record<CredentialType, string> = {\n email: 'E-mail',\n cloud: 'Cloud',\n ai: 'AI',\n custom: 'Custom',\n};\n\ntype CredentialForm = 'login' | 'key';\n\ninterface CredentialFieldDefinition {\n /** Attribute name in the object's `native` */\n name: string;\n type: 'text' | 'password';\n /** Stored encrypted with the system secret */\n encrypted?: boolean;\n required?: boolean;\n /** Label shown in the create dialog */\n label: string;\n}\n\n/** The two credential forms and their fields (keep in sync with admin `credentialTypes.ts`). */\nconst CREDENTIAL_FORMS: Record<CredentialForm, CredentialFieldDefinition[]> = {\n login: [\n { name: 'login', type: 'text', required: true, label: 'Login' },\n { name: 'password', type: 'password', encrypted: true, required: true, label: 'Password' },\n ],\n key: [{ name: 'key', type: 'password', encrypted: true, required: true, label: 'Key' }],\n};\n\n/**\n * SVG paths in a 24x24 viewBox: brand logos from https://simpleicons.org (CC0), the rest Material icons.\n * Kept in sync with admin `CredentialsDialog.tsx`.\n */\nconst ICON_PATHS = {\n anthropic:\n 'M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5527h3.7442L10.5363 3.541Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z',\n chatgpt:\n 'M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.073zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.8956zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654 2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z',\n gemini: 'M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81',\n email: 'M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4-8 5-8-5V6l8 5 8-5v2z',\n login: 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z',\n key: 'M21 10h-8.35A5.99 5.99 0 0 0 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6a5.99 5.99 0 0 0 5.65-4H13l2 2 2-2 2 2 4-4.04L21 10zM7 15c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z',\n};\n\n/** Convert an SVG path to a base64 data URL (the SVG content is pure ASCII, so `btoa` can encode it directly). */\nfunction svgDataUrl(path: string, color: string): string {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"${color}\" d=\"${path}\"/></svg>`;\n return `data:image/svg+xml;base64,${window.btoa(svg)}`;\n}\n\n/** Icon data URLs per template key — stored in `common.icon` of the created credential and shown in the UI. */\nconst ICON_DATA: Record<string, string> = {\n anthropic: svgDataUrl(ICON_PATHS.anthropic, '#d97757'),\n chatgpt: svgDataUrl(ICON_PATHS.chatgpt, '#74aa9c'),\n gemini: svgDataUrl(ICON_PATHS.gemini, '#8e75b2'),\n email: svgDataUrl(ICON_PATHS.email, '#2196f3'),\n login: svgDataUrl(ICON_PATHS.login, '#9e9e9e'),\n key: svgDataUrl(ICON_PATHS.key, '#ffc107'),\n};\n\ninterface CredentialTemplate {\n /** Label shown in the template selector */\n label: string;\n /** Icon (data URL) stored in `common.icon` */\n icon: string;\n /** Form of the credential: login/password or a single key */\n form: CredentialForm;\n /** Fixed category, or null if it follows the schema/user selection */\n type: CredentialType | null;\n /** Proposed unique name */\n name?: string;\n}\n\n/** Templates offered in the \"Add credential\" dialog (a focused subset of the admin's templates). */\nconst CREDENTIAL_TEMPLATES: Record<string, CredentialTemplate> = {\n anthropic: { label: 'Anthropic', icon: ICON_DATA.anthropic, form: 'key', type: 'ai', name: 'anthropic' },\n chatgpt: { label: 'ChatGPT', icon: ICON_DATA.chatgpt, form: 'key', type: 'ai', name: 'chatgpt' },\n gemini: { label: 'Google Gemini', icon: ICON_DATA.gemini, form: 'key', type: 'ai', name: 'gemini' },\n email: { label: 'E-mail', icon: ICON_DATA.email, form: 'login', type: 'email' },\n login: { label: 'Login & password', icon: ICON_DATA.login, form: 'login', type: null },\n key: { label: 'Key', icon: ICON_DATA.key, form: 'key', type: null },\n};\n\ninterface ConfigCredentialSelectProps extends ConfigGenericProps {\n schema: ConfigItemCredentialSelect;\n}\n\ninterface CredentialSelectOption {\n label: string;\n value: string;\n /** Icon of the credential (data URL from `common.icon`) */\n icon?: string;\n}\n\ninterface ConfigCredentialSelectState extends ConfigGenericState {\n selectOptions?: CredentialSelectOption[];\n /** Whether the \"create credential\" dialog is open */\n addOpen?: boolean;\n /** Selected template key */\n addTemplate?: string;\n addName?: string;\n addType?: CredentialType;\n addFields?: Record<string, string>;\n addError?: string;\n addSaving?: boolean;\n}\n\nfunction renderCredentialItem(\n option: CredentialSelectOption | undefined,\n label: string,\n anyIcon: boolean,\n): JSX.Element {\n return (\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n {option?.icon ? (\n <Icon\n src={option.icon}\n style={{ width: 20, height: 20 }}\n />\n ) : anyIcon ? (\n // if at least one option has an icon, keep the labels aligned\n <span style={{ width: 20, height: 20, flexShrink: 0 }} />\n ) : null}\n {label}\n </span>\n );\n}\n\nexport default class ConfigCredentialSelect extends ConfigGeneric<\n ConfigCredentialSelectProps,\n ConfigCredentialSelectState\n> {\n async componentDidMount(): Promise<void> {\n await super.componentDidMount();\n const value = ConfigGeneric.getValue(this.props.data, this.props.attr);\n\n const selectOptions = await this.readCredentials();\n this.setState({ value, selectOptions });\n }\n\n /**\n * Read the credential objects (\"system.credentials.<name>\") and turn them into select options,\n * filtered by `schema.credentialType` if set. The \"none\" option is prepended.\n */\n async readCredentials(): Promise<CredentialSelectOption[]> {\n let selectOptions: CredentialSelectOption[] = [];\n try {\n const objs = await this.props.oContext.socket.getObjectViewSystem(\n 'config',\n CREDENTIALS_PREFIX,\n `${CREDENTIALS_PREFIX}香`,\n );\n selectOptions = Object.values(objs)\n .filter(\n obj =>\n !!obj &&\n (!this.props.schema.credentialType ||\n (obj.native as Record<string, any>)?.type === this.props.schema.credentialType),\n )\n .map(obj => ({\n label: ConfigCredentialSelect.getCredentialName(obj as ioBroker.Object),\n value: obj._id,\n icon: typeof obj.common?.icon === 'string' ? obj.common.icon : undefined,\n }))\n .sort((a, b) => a.label.localeCompare(b.label));\n } catch (e) {\n console.error(`Cannot read credentials: ${e}`);\n }\n\n selectOptions.unshift({ label: I18n.t(ConfigGeneric.NONE_LABEL), value: ConfigGeneric.NONE_VALUE });\n return selectOptions;\n }\n\n static getCredentialName(obj: ioBroker.Object): string {\n const name = obj.common?.name;\n let text: string;\n if (name && typeof name === 'object') {\n text = name[I18n.getLanguage()] || name.en || Object.values(name)[0] || '';\n } else {\n text = (name as string) || '';\n }\n return text || obj._id.substring(CREDENTIALS_PREFIX.length);\n }\n\n /** Template keys offered for this field, filtered by `schema.credentialType` (type-agnostic ones always shown). */\n templateKeys(): string[] {\n const credentialType = this.props.schema.credentialType;\n return Object.keys(CREDENTIAL_TEMPLATES).filter(key => {\n const template = CREDENTIAL_TEMPLATES[key];\n return !credentialType || template.type === credentialType || template.type === null;\n });\n }\n\n /** Apply a template: it defines the form, the (proposed) name, the category and the icon. */\n selectTemplate(key: string): void {\n const template = CREDENTIAL_TEMPLATES[key];\n this.setState({\n addTemplate: key,\n addName: template.name || '',\n addType: template.type || this.props.schema.credentialType || 'custom',\n addFields: {},\n addError: '',\n });\n }\n\n /** Open the create dialog, pre-selecting the first template that fits the schema's credentialType. */\n openAddDialog(): void {\n const keys = this.templateKeys();\n const addTemplate = keys[0];\n const template = CREDENTIAL_TEMPLATES[addTemplate];\n this.setState({\n addOpen: true,\n addTemplate,\n addName: template.name || '',\n addType: template.type || this.props.schema.credentialType || 'custom',\n addFields: {},\n addError: '',\n addSaving: false,\n });\n }\n\n /** Create the credential object, encrypt its secret fields, store it (with icon), then select it. */\n async createCredential(): Promise<void> {\n const template = CREDENTIAL_TEMPLATES[this.state.addTemplate || ''];\n const name = (this.state.addName || '').trim().replace(Utils.FORBIDDEN_CHARS, '_');\n if (!name) {\n this.setState({ addError: I18n.t('A unique name is required') });\n return;\n }\n const id = `${CREDENTIALS_PREFIX}${name}`;\n if ((this.state.selectOptions || []).find(option => option.value === id)) {\n this.setState({ addError: I18n.t('A credential with this name already exists') });\n return;\n }\n\n const form = template?.form || 'login';\n const fields = CREDENTIAL_FORMS[form];\n const addFields = this.state.addFields || {};\n if (fields.some(field => field.required && !(addFields[field.name] || '').trim())) {\n this.setState({ addError: I18n.t('Please fill in all required fields') });\n return;\n }\n const type: CredentialType =\n template?.type || this.props.schema.credentialType || this.state.addType || 'custom';\n\n this.setState({ addSaving: true, addError: '' });\n try {\n // The actual socket is always an AdminConnection at runtime (only admin can write credentials);\n // `encrypt` uses the system secret, exactly as the admin \"Credentials\" dialog does.\n const socket = this.props.oContext.socket;\n const native: Record<string, any> = {\n type,\n version: CREDENTIALS_VERSION,\n encryptedFields: fields.filter(field => field.encrypted).map(field => field.name),\n };\n for (const field of fields) {\n const raw = addFields[field.name] || '';\n native[field.name] = field.encrypted && raw ? await socket.encrypt(raw) : raw;\n }\n\n const obj = {\n _id: id,\n type: 'config',\n common: { name, ...(template?.icon ? { icon: template.icon } : {}) },\n native,\n // Only the admin may read credentials.\n acl: {\n object: 0x600,\n owner: 'system.user.admin',\n ownerGroup: 'system.group.administrator',\n },\n } as unknown as ioBroker.SettableObject;\n\n await socket.setObject(id, obj);\n\n // Insert the new credential into the options (keeping \"none\" on top), then select it.\n const existing = this.state.selectOptions || [];\n const none = existing.find(option => option.value === ConfigGeneric.NONE_VALUE);\n const rest = existing.filter(option => option.value !== ConfigGeneric.NONE_VALUE);\n rest.push({ label: name, value: id, icon: template?.icon });\n rest.sort((a, b) => a.label.localeCompare(b.label));\n const selectOptions = none ? [none, ...rest] : rest;\n\n this.setState({ addOpen: false, addSaving: false, selectOptions, value: id }, () =>\n this.onChange(this.props.attr, id),\n );\n } catch (e) {\n this.setState({\n addSaving: false,\n addError: I18n.t('Cannot create credential: %s', (e as Error).toString()),\n });\n }\n }\n\n renderAddDialog(): JSX.Element | null {\n if (!this.state.addOpen) {\n return null;\n }\n const template = CREDENTIAL_TEMPLATES[this.state.addTemplate || ''];\n const form = template?.form || 'login';\n const fields = CREDENTIAL_FORMS[form];\n const id = `${CREDENTIALS_PREFIX}${(this.state.addName || '').trim().replace(Utils.FORBIDDEN_CHARS, '_')}`;\n // The category can only be chosen for type-agnostic templates when the schema doesn't pin a type.\n const showCategory = !this.props.schema.credentialType && template?.type === null;\n\n return (\n <Dialog\n open\n maxWidth=\"sm\"\n fullWidth\n onClose={() => this.setState({ addOpen: false })}\n >\n <DialogTitle>{I18n.t('Add credential')}</DialogTitle>\n <DialogContent style={{ display: 'flex', flexDirection: 'column', gap: 12, paddingTop: 8 }}>\n <FormControl\n fullWidth\n variant=\"standard\"\n >\n <InputLabel shrink>{I18n.t('Template')}</InputLabel>\n <Select\n variant=\"standard\"\n value={this.state.addTemplate || ''}\n onChange={e => this.selectTemplate(e.target.value)}\n >\n {this.templateKeys().map(key => (\n <MenuItem\n key={key}\n value={key}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <img\n src={CREDENTIAL_TEMPLATES[key].icon}\n width={20}\n height={20}\n alt=\"\"\n />\n {I18n.t(CREDENTIAL_TEMPLATES[key].label)}\n </span>\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n\n {showCategory ? (\n <FormControl\n fullWidth\n variant=\"standard\"\n >\n <InputLabel shrink>{I18n.t('Credential type')}</InputLabel>\n <Select\n variant=\"standard\"\n value={this.state.addType || 'custom'}\n onChange={e => this.setState({ addType: e.target.value as CredentialType })}\n >\n {CREDENTIAL_TYPES.map(type => (\n <MenuItem\n key={type}\n value={type}\n >\n {I18n.t(CREDENTIAL_TYPE_LABELS[type])}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n ) : null}\n\n <TextField\n variant=\"standard\"\n fullWidth\n label={I18n.t('ra_Name')}\n value={this.state.addName || ''}\n error={!!this.state.addError}\n helperText={this.state.addName ? id : ''}\n slotProps={{ inputLabel: { shrink: true }, htmlInput: { autoComplete: 'off' } }}\n onChange={e => this.setState({ addName: e.target.value, addError: '' })}\n />\n\n {fields.map(field => (\n <TextField\n key={field.name}\n variant=\"standard\"\n fullWidth\n type={field.type === 'password' ? 'password' : 'text'}\n required={field.required}\n label={I18n.t(field.label)}\n value={this.state.addFields?.[field.name] || ''}\n slotProps={{\n inputLabel: { shrink: true },\n htmlInput: { autoComplete: field.type === 'password' ? 'new-password' : 'off' },\n }}\n onChange={e =>\n this.setState({\n addFields: { ...(this.state.addFields || {}), [field.name]: e.target.value },\n addError: '',\n })\n }\n />\n ))}\n\n {this.state.addError ? <Alert severity=\"error\">{this.state.addError}</Alert> : null}\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"contained\"\n color=\"primary\"\n disabled={!!this.state.addSaving || !(this.state.addName || '').trim()}\n startIcon={<CheckIcon />}\n onClick={() => this.createCredential()}\n >\n {I18n.t('ra_Create')}\n </Button>\n <Button\n variant=\"contained\"\n color=\"grey\"\n disabled={!!this.state.addSaving}\n startIcon={<CloseIcon />}\n onClick={() => this.setState({ addOpen: false })}\n >\n {I18n.t('ra_Cancel')}\n </Button>\n </DialogActions>\n </Dialog>\n );\n }\n\n renderItem(error: unknown, disabled: boolean /* , defaultValue */): JSX.Element | null {\n if (!this.state.selectOptions) {\n return null;\n }\n\n const item = this.state.selectOptions?.find(_item => _item.value === this.state.value);\n // The stored value could point to a meanwhile deleted credential\n const unknownValue =\n this.state.value && this.state.value !== ConfigGeneric.NONE_VALUE && !item ? this.state.value : null;\n // if at least one option has an icon, options without icon get a placeholder for alignment\n const anyIcon = this.state.selectOptions.some(option => !!option.icon);\n // creation can be suppressed per schema (`disableCreation`)\n const canCreate = !this.props.schema.disableCreation;\n\n return (\n <div style={{ display: 'flex', alignItems: 'flex-end', gap: 4, width: '100%' }}>\n <FormControl\n style={{ flex: 1, minWidth: 0 }}\n variant=\"standard\"\n >\n {this.props.schema.label ? (\n <InputLabel shrink>{this.getText(this.props.schema.label)}</InputLabel>\n ) : null}\n <Select\n variant=\"standard\"\n error={!!error || !!unknownValue}\n displayEmpty\n disabled={!!disabled}\n value={this.state.value || ConfigGeneric.NONE_VALUE}\n renderValue={() =>\n unknownValue\n ? unknownValue\n : renderCredentialItem(\n item,\n this.getText(item?.label, this.props.schema.noTranslation !== false),\n anyIcon,\n )\n }\n onChange={e =>\n this.setState(\n { value: e.target.value === ConfigGeneric.NONE_VALUE ? '' : e.target.value },\n () => this.onChange(this.props.attr, this.state.value),\n )\n }\n >\n {this.state.selectOptions?.map(item_ => (\n <MenuItem\n key={item_.value}\n value={item_.value}\n style={item_.value === ConfigGeneric.NONE_VALUE ? { opacity: 0.5 } : {}}\n >\n {renderCredentialItem(\n item_,\n this.getText(item_.label, this.props.schema.noTranslation !== false),\n anyIcon,\n )}\n </MenuItem>\n ))}\n </Select>\n {this.props.schema.help ? (\n <FormHelperText>\n {this.renderHelp(\n this.props.schema.help,\n this.props.schema.helpLink,\n this.props.schema.noTranslation,\n )}\n </FormHelperText>\n ) : null}\n </FormControl>\n {canCreate ? (\n <IconButton\n size=\"small\"\n disabled={!!disabled}\n title={I18n.t('Add credential')}\n onClick={() => this.openAddDialog()}\n >\n <AddIcon />\n </IconButton>\n ) : null}\n {this.renderAddDialog()}\n </div>\n );\n }\n}\n"]}
|
package/build/types.d.ts
CHANGED
|
@@ -988,6 +988,8 @@ export interface ConfigItemCredentialSelect extends ConfigItem {
|
|
|
988
988
|
type: 'credential';
|
|
989
989
|
/** Show only credentials of this type, e.g. 'email', 'cloud', 'ai' or 'custom'. If not defined, all credentials are listed. */
|
|
990
990
|
credentialType?: 'email' | 'cloud' | 'ai' | 'custom';
|
|
991
|
+
/** Do not allow creation of credentials, just selection */
|
|
992
|
+
disableCreation?: boolean;
|
|
991
993
|
}
|
|
992
994
|
|
|
993
995
|
export interface ConfigItemLicense extends ConfigItem {
|