mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-05-24 02:02:23 -04:00
Use password and select config fields
This commit is contained in:
parent
e14f508327
commit
a3eed49638
@ -35,6 +35,7 @@
|
|||||||
@case (ConfigOptionType.String) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
|
@case (ConfigOptionType.String) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
|
||||||
@case (ConfigOptionType.JSON) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
|
@case (ConfigOptionType.JSON) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
|
||||||
@case (ConfigOptionType.File) { <pngx-input-file [formControlName]="option.key" (upload)="uploadFile($event, option.key)" [error]="errors[option.key]"></pngx-input-file> }
|
@case (ConfigOptionType.File) { <pngx-input-file [formControlName]="option.key" (upload)="uploadFile($event, option.key)" [error]="errors[option.key]"></pngx-input-file> }
|
||||||
|
@case (ConfigOptionType.Password) { <pngx-input-password [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-password> }
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +29,7 @@ import { SettingsService } from 'src/app/services/settings.service'
|
|||||||
import { ToastService } from 'src/app/services/toast.service'
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
import { FileComponent } from '../../common/input/file/file.component'
|
import { FileComponent } from '../../common/input/file/file.component'
|
||||||
import { NumberComponent } from '../../common/input/number/number.component'
|
import { NumberComponent } from '../../common/input/number/number.component'
|
||||||
|
import { PasswordComponent } from '../../common/input/password/password.component'
|
||||||
import { SelectComponent } from '../../common/input/select/select.component'
|
import { SelectComponent } from '../../common/input/select/select.component'
|
||||||
import { SwitchComponent } from '../../common/input/switch/switch.component'
|
import { SwitchComponent } from '../../common/input/switch/switch.component'
|
||||||
import { TextComponent } from '../../common/input/text/text.component'
|
import { TextComponent } from '../../common/input/text/text.component'
|
||||||
@ -46,6 +47,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
|||||||
TextComponent,
|
TextComponent,
|
||||||
NumberComponent,
|
NumberComponent,
|
||||||
FileComponent,
|
FileComponent,
|
||||||
|
PasswordComponent,
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
NgbNavModule,
|
NgbNavModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<div class="mb-3">
|
<div class="mb-3" [class.pb-3]="error">
|
||||||
<label class="form-label" [for]="inputId">{{title}}</label>
|
<div class="row">
|
||||||
|
<div class="d-flex align-items-center position-relative hidden-button-container" [class.col-md-3]="horizontal">
|
||||||
|
@if (title) {
|
||||||
|
<label class="form-label" [class.mb-md-0]="horizontal" [for]="inputId">{{title}}</label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="position-relative" [class.col-md-9]="horizontal">
|
||||||
<div class="input-group" [class.is-invalid]="error">
|
<div class="input-group" [class.is-invalid]="error">
|
||||||
<input #inputField [type]="showReveal && textVisible ? 'text' : 'password'" class="form-control" [class.is-invalid]="error" [id]="inputId" [(ngModel)]="value" (focus)="onFocus()" (focusout)="onFocusOut()" (change)="onChange(value)" [disabled]="disabled" [autocomplete]="autocomplete">
|
<input #inputField [type]="showReveal && textVisible ? 'text' : 'password'" class="form-control" [class.is-invalid]="error" [id]="inputId" [(ngModel)]="value" (focus)="onFocus()" (focusout)="onFocusOut()" (change)="onChange(value)" [disabled]="disabled" [autocomplete]="autocomplete">
|
||||||
@if (showReveal) {
|
@if (showReveal) {
|
||||||
@ -15,3 +21,4 @@
|
|||||||
<small class="form-text text-muted" [innerHTML]="hint | safeHtml"></small>
|
<small class="form-text text-muted" [innerHTML]="hint | safeHtml"></small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@ -44,6 +44,7 @@ export enum ConfigOptionType {
|
|||||||
Boolean = 'boolean',
|
Boolean = 'boolean',
|
||||||
JSON = 'json',
|
JSON = 'json',
|
||||||
File = 'file',
|
File = 'file',
|
||||||
|
Password = 'password',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConfigCategory = {
|
export const ConfigCategory = {
|
||||||
@ -52,6 +53,11 @@ export const ConfigCategory = {
|
|||||||
AI: $localize`AI Settings`,
|
AI: $localize`AI Settings`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const LLMBackendConfig = {
|
||||||
|
OPENAI: 'openai',
|
||||||
|
OLLAMA: 'ollama',
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConfigOption {
|
export interface ConfigOption {
|
||||||
key: string
|
key: string
|
||||||
title: string
|
title: string
|
||||||
@ -191,7 +197,8 @@ export const PaperlessConfigOptions: ConfigOption[] = [
|
|||||||
{
|
{
|
||||||
key: 'llm_backend',
|
key: 'llm_backend',
|
||||||
title: $localize`LLM Backend`,
|
title: $localize`LLM Backend`,
|
||||||
type: ConfigOptionType.String,
|
type: ConfigOptionType.Select,
|
||||||
|
choices: mapToItems(LLMBackendConfig),
|
||||||
config_key: 'PAPERLESS_LLM_BACKEND',
|
config_key: 'PAPERLESS_LLM_BACKEND',
|
||||||
category: ConfigCategory.AI,
|
category: ConfigCategory.AI,
|
||||||
},
|
},
|
||||||
@ -205,7 +212,7 @@ export const PaperlessConfigOptions: ConfigOption[] = [
|
|||||||
{
|
{
|
||||||
key: 'llm_api_key',
|
key: 'llm_api_key',
|
||||||
title: $localize`LLM API Key`,
|
title: $localize`LLM API Key`,
|
||||||
type: ConfigOptionType.String,
|
type: ConfigOptionType.Password,
|
||||||
config_key: 'PAPERLESS_LLM_API_KEY',
|
config_key: 'PAPERLESS_LLM_API_KEY',
|
||||||
category: ConfigCategory.AI,
|
category: ConfigCategory.AI,
|
||||||
},
|
},
|
||||||
|
@ -32,9 +32,8 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
self.assertEqual(
|
self.assertDictEqual(
|
||||||
json.dumps(response.data[0]),
|
response.data[0],
|
||||||
json.dumps(
|
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"user_args": None,
|
"user_args": None,
|
||||||
@ -58,7 +57,6 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
|||||||
"llm_api_key": None,
|
"llm_api_key": None,
|
||||||
"llm_url": None,
|
"llm_url": None,
|
||||||
},
|
},
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_api_get_ui_settings_with_config(self):
|
def test_api_get_ui_settings_with_config(self):
|
||||||
|
@ -185,6 +185,10 @@ class ProfileSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class ApplicationConfigurationSerializer(serializers.ModelSerializer):
|
class ApplicationConfigurationSerializer(serializers.ModelSerializer):
|
||||||
user_args = serializers.JSONField(binary=True, allow_null=True)
|
user_args = serializers.JSONField(binary=True, allow_null=True)
|
||||||
|
llm_api_key = ObfuscatedPasswordField(
|
||||||
|
required=False,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
|
||||||
def run_validation(self, data):
|
def run_validation(self, data):
|
||||||
# Empty strings treated as None to avoid unexpected behavior
|
# Empty strings treated as None to avoid unexpected behavior
|
||||||
|
Loading…
x
Reference in New Issue
Block a user