mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
fix password input for backups and add adjective noun randomizer
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core'
|
||||
import { FormsModule } from '@angular/forms'
|
||||
import { TuiAutoFocus } from '@taiga-ui/cdk'
|
||||
import { TuiButton, TuiDialogContext, TuiInput } from '@taiga-ui/core'
|
||||
import { TuiButton, TuiDialogContext, TuiIcon, TuiInput } from '@taiga-ui/core'
|
||||
import { TuiPassword } from '@taiga-ui/kit'
|
||||
import { injectContext, PolymorpheusComponent } from '@taiga-ui/polymorpheus'
|
||||
import { i18nPipe } from '../i18n/i18n.pipe'
|
||||
import { i18nKey } from '../i18n/i18n.providers'
|
||||
@@ -27,22 +28,12 @@ import { i18nKey } from '../i18n/i18n.providers'
|
||||
tuiAutoFocus
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[(ngModel)]="value"
|
||||
[class.masked]="options.useMask && masked && value"
|
||||
[placeholder]="options.placeholder || ''"
|
||||
[type]="options.useMask ? 'password' : 'text'"
|
||||
[autocomplete]="options.useMask ? 'off' : ''"
|
||||
/>
|
||||
@if (options.useMask) {
|
||||
<button
|
||||
tuiIconButton
|
||||
type="button"
|
||||
appearance="icon"
|
||||
title="Toggle masking"
|
||||
size="xs"
|
||||
class="button"
|
||||
[iconStart]="masked ? '@tui.eye' : '@tui.eye-off'"
|
||||
(click)="masked = !masked"
|
||||
>
|
||||
{{ 'Reveal/Hide' | i18n }}
|
||||
</button>
|
||||
<tui-icon tuiPassword />
|
||||
}
|
||||
</tui-textfield>
|
||||
@if (error) {
|
||||
@@ -71,24 +62,22 @@ import { i18nKey } from '../i18n/i18n.providers'
|
||||
.error {
|
||||
color: var(--tui-status-negative);
|
||||
}
|
||||
|
||||
.button {
|
||||
pointer-events: auto;
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
||||
.masked {
|
||||
-webkit-text-security: disc;
|
||||
}
|
||||
`,
|
||||
imports: [FormsModule, TuiButton, TuiInput, TuiAutoFocus, i18nPipe],
|
||||
imports: [
|
||||
FormsModule,
|
||||
TuiButton,
|
||||
TuiIcon,
|
||||
TuiInput,
|
||||
TuiPassword,
|
||||
TuiAutoFocus,
|
||||
i18nPipe,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PromptModal {
|
||||
private readonly context =
|
||||
injectContext<TuiDialogContext<string, PromptOptions>>()
|
||||
|
||||
masked = this.options.useMask
|
||||
value = this.options.initialValue || ''
|
||||
error = ''
|
||||
|
||||
|
||||
@@ -57,3 +57,5 @@ export * from './util/unused'
|
||||
export * from './util/keyboards'
|
||||
export * from './util/languages'
|
||||
export * from './util/hostname'
|
||||
export * from './util/random-server-name'
|
||||
export * from './util/server-name-validator'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* TS port of the Rust `normalize()` function from core/src/hostname.rs.
|
||||
* Converts a free-text name into a valid hostname.
|
||||
* Strips non-alphanumeric characters and produces a raw hostname string.
|
||||
*/
|
||||
export function normalizeHostname(name: string): string {
|
||||
export function normalizeHostnameRaw(name: string): string {
|
||||
let prevWasDash = true
|
||||
let normalized = ''
|
||||
|
||||
@@ -20,5 +20,12 @@ export function normalizeHostname(name: string): string {
|
||||
normalized = normalized.slice(0, -1)
|
||||
}
|
||||
|
||||
return normalized || 'start9'
|
||||
return normalized
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a free-text name into a valid hostname, with 'start9' fallback.
|
||||
*/
|
||||
export function normalizeHostname(name: string): string {
|
||||
return normalizeHostnameRaw(name) || 'start9'
|
||||
}
|
||||
|
||||
16
web/projects/shared/src/util/random-server-name.ts
Normal file
16
web/projects/shared/src/util/random-server-name.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ADJECTIVES, NOUNS } from './server-name-words'
|
||||
|
||||
/**
|
||||
* Generates a random server name in "Adjective Noun" format.
|
||||
* Uses the same word lists as the Rust backend (core/src/assets/).
|
||||
*/
|
||||
export function randomServerName(): string {
|
||||
const adj = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)]!
|
||||
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)]!
|
||||
|
||||
return `${capitalize(adj)} ${capitalize(noun)}`
|
||||
}
|
||||
|
||||
function capitalize(word: string): string {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1)
|
||||
}
|
||||
26
web/projects/shared/src/util/server-name-validator.ts
Normal file
26
web/projects/shared/src/util/server-name-validator.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { AbstractControl, ValidationErrors } from '@angular/forms'
|
||||
import { normalizeHostnameRaw } from './hostname'
|
||||
|
||||
// Matches backend normalize() threshold (core/src/hostname.rs:109)
|
||||
const HOSTNAME_MIN_LENGTH = 4
|
||||
// DNS label limit
|
||||
const HOSTNAME_MAX_LENGTH = 63
|
||||
|
||||
export function serverNameValidator(
|
||||
control: AbstractControl,
|
||||
): ValidationErrors | null {
|
||||
const name = (control.value || '').trim()
|
||||
if (!name) return null
|
||||
|
||||
const hostname = normalizeHostnameRaw(name)
|
||||
|
||||
if (hostname.length < HOSTNAME_MIN_LENGTH) {
|
||||
return { hostnameMinLength: true }
|
||||
}
|
||||
|
||||
if (hostname.length > HOSTNAME_MAX_LENGTH) {
|
||||
return { hostnameMaxLength: true }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
8948
web/projects/shared/src/util/server-name-words.ts
Normal file
8948
web/projects/shared/src/util/server-name-words.ts
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user