ux: token Proxmox en deux champs séparés (ID + Secret)
Install.vue et Settings.vue : remplace le champ unique "PVEAPIToken=..." par deux inputs distincts — Token ID (ex: enzo@pam!panel) et Secret (uuid). L'assemblage PVEAPIToken=ID=Secret se fait côté frontend avant envoi. Plus besoin de connaître le format interne. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d55ecdcd97
commit
233f690214
4 changed files with 48 additions and 20 deletions
|
|
@ -33,8 +33,9 @@
|
||||||
"sshSuccess": "SSH connection successful!",
|
"sshSuccess": "SSH connection successful!",
|
||||||
"sshFailed": "SSH connection failed",
|
"sshFailed": "SSH connection failed",
|
||||||
"proxmoxUrl": "Proxmox URL",
|
"proxmoxUrl": "Proxmox URL",
|
||||||
"proxmoxToken": "Proxmox API token",
|
"proxmoxTokenId": "Token ID",
|
||||||
"proxmoxTokenHint": "Format: PVEAPIToken=user{'@'}realm!tokenid=secret",
|
"proxmoxTokenSecret": "Token secret",
|
||||||
|
"proxmoxTokenHint": "Token ID: enzo{'@'}pam!panel — Secret: UUID generated by Proxmox",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"finish": "Complete installation",
|
"finish": "Complete installation",
|
||||||
|
|
@ -115,7 +116,9 @@
|
||||||
"sshUsername": "SSH username",
|
"sshUsername": "SSH username",
|
||||||
"sshPassword": "SSH password",
|
"sshPassword": "SSH password",
|
||||||
"proxmoxUrl": "Proxmox URL",
|
"proxmoxUrl": "Proxmox URL",
|
||||||
"proxmoxToken": "Proxmox API token",
|
"proxmoxTokenId": "Proxmox Token ID",
|
||||||
|
"proxmoxTokenIdPlaceholder": "enzo@pam!panel",
|
||||||
|
"proxmoxTokenSecret": "Token secret",
|
||||||
"secretPlaceholder": "Leave empty to keep current value",
|
"secretPlaceholder": "Leave empty to keep current value",
|
||||||
"darkMode": "Dark mode",
|
"darkMode": "Dark mode",
|
||||||
"sidebarPosition": "Sidebar position",
|
"sidebarPosition": "Sidebar position",
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,9 @@
|
||||||
"sshSuccess": "Connexion SSH réussie !",
|
"sshSuccess": "Connexion SSH réussie !",
|
||||||
"sshFailed": "Connexion SSH échouée",
|
"sshFailed": "Connexion SSH échouée",
|
||||||
"proxmoxUrl": "URL Proxmox",
|
"proxmoxUrl": "URL Proxmox",
|
||||||
"proxmoxToken": "Token API Proxmox",
|
"proxmoxTokenId": "Token ID",
|
||||||
"proxmoxTokenHint": "Format : PVEAPIToken=user{'@'}realm!tokenid=secret",
|
"proxmoxTokenSecret": "Secret du token",
|
||||||
|
"proxmoxTokenHint": "Token ID : enzo{'@'}pam!panel — Secret : uuid généré par Proxmox",
|
||||||
"back": "Retour",
|
"back": "Retour",
|
||||||
"next": "Suivant",
|
"next": "Suivant",
|
||||||
"finish": "Terminer l'installation",
|
"finish": "Terminer l'installation",
|
||||||
|
|
@ -115,7 +116,9 @@
|
||||||
"sshUsername": "Utilisateur SSH",
|
"sshUsername": "Utilisateur SSH",
|
||||||
"sshPassword": "Mot de passe SSH",
|
"sshPassword": "Mot de passe SSH",
|
||||||
"proxmoxUrl": "URL Proxmox",
|
"proxmoxUrl": "URL Proxmox",
|
||||||
"proxmoxToken": "Token API Proxmox",
|
"proxmoxTokenId": "Token ID Proxmox",
|
||||||
|
"proxmoxTokenIdPlaceholder": "enzo@pam!panel",
|
||||||
|
"proxmoxTokenSecret": "Secret du token",
|
||||||
"secretPlaceholder": "Laisser vide pour ne pas modifier",
|
"secretPlaceholder": "Laisser vide pour ne pas modifier",
|
||||||
"darkMode": "Mode sombre",
|
"darkMode": "Mode sombre",
|
||||||
"sidebarPosition": "Position de la sidebar",
|
"sidebarPosition": "Position de la sidebar",
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,12 @@
|
||||||
<input v-model="form.proxmoxUrl" class="neu-input" placeholder="https://10.0.0.1:8006" />
|
<input v-model="form.proxmoxUrl" class="neu-input" placeholder="https://10.0.0.1:8006" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ t('install.proxmoxToken') }}</label>
|
<label>{{ t('install.proxmoxTokenId') }}</label>
|
||||||
<input v-model="form.proxmoxToken" class="neu-input" placeholder="PVEAPIToken=enzo@pam!panel=xxxx" />
|
<input v-model="form.proxmoxTokenId" class="neu-input" placeholder="enzo@pam!panel" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ t('install.proxmoxTokenSecret') }}</label>
|
||||||
|
<input v-model="form.proxmoxTokenSecret" type="password" class="neu-input" placeholder="ed57ea62-cadc-4ddd-..." />
|
||||||
<small>{{ t('install.proxmoxTokenHint') }}</small>
|
<small>{{ t('install.proxmoxTokenHint') }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -187,7 +191,8 @@ const form = ref({
|
||||||
sshUsername: 'enzo',
|
sshUsername: 'enzo',
|
||||||
sshPassword: '',
|
sshPassword: '',
|
||||||
proxmoxUrl: 'https://10.0.0.1:8006',
|
proxmoxUrl: 'https://10.0.0.1:8006',
|
||||||
proxmoxToken: '',
|
proxmoxTokenId: '',
|
||||||
|
proxmoxTokenSecret: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const canProceed = computed(() => {
|
const canProceed = computed(() => {
|
||||||
|
|
@ -264,7 +269,9 @@ async function finalize() {
|
||||||
ssh_username: form.value.sshUsername,
|
ssh_username: form.value.sshUsername,
|
||||||
ssh_password: form.value.sshPassword,
|
ssh_password: form.value.sshPassword,
|
||||||
proxmox_url: form.value.proxmoxUrl,
|
proxmox_url: form.value.proxmoxUrl,
|
||||||
proxmox_token: form.value.proxmoxToken,
|
proxmox_token: (form.value.proxmoxTokenId && form.value.proxmoxTokenSecret)
|
||||||
|
? `PVEAPIToken=${form.value.proxmoxTokenId}=${form.value.proxmoxTokenSecret}`
|
||||||
|
: '',
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,12 @@
|
||||||
<input v-model="settings.proxmox_url" class="neu-input" placeholder="https://10.0.0.1:8006" />
|
<input v-model="settings.proxmox_url" class="neu-input" placeholder="https://10.0.0.1:8006" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label>{{ t('settings.proxmoxToken') }}</label>
|
<label>{{ t('settings.proxmoxTokenId') }}</label>
|
||||||
<input v-model="secrets.proxmox_token" type="password" class="neu-input" :placeholder="t('settings.secretPlaceholder')" autocomplete="new-password" />
|
<input v-model="secrets.proxmox_token_id" class="neu-input" :placeholder="t('settings.proxmoxTokenIdPlaceholder')" autocomplete="off" />
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>{{ t('settings.proxmoxTokenSecret') }}</label>
|
||||||
|
<input v-model="secrets.proxmox_token_secret" type="password" class="neu-input" :placeholder="t('settings.secretPlaceholder')" autocomplete="new-password" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -184,7 +188,8 @@ const settings = ref({
|
||||||
// Champs sensibles — write-only, jamais retournés par l'API
|
// Champs sensibles — write-only, jamais retournés par l'API
|
||||||
const secrets = ref({
|
const secrets = ref({
|
||||||
ssh_password: '',
|
ssh_password: '',
|
||||||
proxmox_token: '',
|
proxmox_token_id: '',
|
||||||
|
proxmox_token_secret: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -225,13 +230,22 @@ async function saveSettings() {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
saveSuccess.value = false
|
saveSuccess.value = false
|
||||||
|
|
||||||
const allEntries: [string, string][] = [
|
const entries: [string, string][] = [...Object.entries(settings.value)]
|
||||||
...Object.entries(settings.value),
|
|
||||||
// Secrets : envoyés seulement si non-vides (le backend ignore les valeurs vides)
|
|
||||||
...Object.entries(secrets.value).filter(([, v]) => v !== ''),
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const [key, value] of allEntries) {
|
// Mot de passe SSH — envoyé seulement si non-vide
|
||||||
|
if (secrets.value.ssh_password) {
|
||||||
|
entries.push(['ssh_password', secrets.value.ssh_password])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token Proxmox — assemblé si les deux champs sont remplis
|
||||||
|
if (secrets.value.proxmox_token_id && secrets.value.proxmox_token_secret) {
|
||||||
|
entries.push([
|
||||||
|
'proxmox_token',
|
||||||
|
`PVEAPIToken=${secrets.value.proxmox_token_id}=${secrets.value.proxmox_token_secret}`,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of entries) {
|
||||||
await fetch(`/api/settings/${key}`, {
|
await fetch(`/api/settings/${key}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -244,7 +258,8 @@ async function saveSettings() {
|
||||||
|
|
||||||
// Vider les champs secrets après sauvegarde
|
// Vider les champs secrets après sauvegarde
|
||||||
secrets.value.ssh_password = ''
|
secrets.value.ssh_password = ''
|
||||||
secrets.value.proxmox_token = ''
|
secrets.value.proxmox_token_id = ''
|
||||||
|
secrets.value.proxmox_token_secret = ''
|
||||||
|
|
||||||
saving.value = false
|
saving.value = false
|
||||||
saveSuccess.value = true
|
saveSuccess.value = true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue