Panneau d'administration

hz_lib embarque un panneau d'administration centralise dans lequel chaque module HZ-Script se branche. Au lieu que chaque script fournisse sa propre commande + interface pour la config, tout est regroupe derrière un seul /hzpanel accessible aux admins. Le panneau construit automatiquement un éditeur type pour chaque champ de config déclare dans le bridge_schema.lua de chaque module.

Disponible depuis hz_lib v1.1.0. Les versions antérieures ne fournissaient que le bridge framework / inventaire.

Ouvrir le panneau

En jeu (chat, console F8 ou raccourci clavier) :

/hzpanel

  • Console seulement (F8 serveur) — /hzpanel depuis la console serveur n'ouvre pas le NUI. Tapez-le depuis le jeu.
  • Nécessite la permission ACE configurée sous HZBridge.Panel.AcePermission (défaut admin) — voir Configuration des permissions ci-dessous.
  • Appuyez sur Escape ou cliquez sur le bouton fermer pour quitter. Les sessions sont protégées par token et expirent après HZBridge.Panel.SessionTokenTtlMs (5 min par défaut) — rouvrez pour rafraîchir.
Vous pouvez aussi ouvrir directement sur l'onglet d'un module spécifique depuis un autre script :

-- server export — ouvre le panneau pour `src` et preselectionne le module nomme
exports['hz_lib']:OpenPanel(src, 'hz_weather')


Configuration des permissions

Le panneau est protégé par le noeud ACE canonique admin de FXServer. Deux lignes dans server.cfg sont requises :

add_principal identifier.license:YOUR_LICENSE group.admin
add_ace group.admin admin allow

Remplacez YOUR_LICENSE par la licence du joueur de votre serveur (visible en tapant /hzpanel une fois — la sortie console de l'échec affiche tous vos identifiants + un bloc prêt à copier-coller avec votre licence déjà interpolée).

Vous pouvez coller les deux lignes directement dans la console serveur pour corriger immédiatement — aucun redémarrage requis. Si vous les mettez dans server.cfg, redémarrez FXServer (ou relancez exec server.cfg) pour les charger.

Piège du boilerplate qb-core : un qb-core fraîchement installe livre add_ace group.admin command allow dans server.cfg mais pas add_ace group.admin admin allow. Le menu admin de qb-core utilise sa table DB qbcore_admins plutôt que le noeud ACE canonique admin, donc la ligne est absente. Presque chaque ticket de permission hz_lib vient de la — ajoutez la seconde ligne et c'est règle.

Le probe framework se rabat sur les tables admin de qb-core / qbx_core / ESX / ox_core si la vérification ACE échoue. Donc un joueur marque comme admin dans votre table qbcore_admins peut ouvrir le panneau même sans la configuration ACE. Mais ACE est la méthode recommandée car elle est agnostique au framework et auditable en un seul endroit.

Si la vérification échoue, la console serveur affiché :

  • Le noeud ACE exact vérifie
  • Tous vos identifiants joueur
  • Une matrice de probe ACE (quels noeuds sont accordes, lesquels ne le sont pas)
  • Les deux lignes exactes add_principal + add_ace à copier-coller, avec votre licence déjà interpolée
Regardez l'erreur rouge dans votre console — la correction est juste la. Disponible depuis v1.1.1.
command.hzpanel n'accorde PAS le panneau. Une erreur courante est d'essayer add_ace identifier.X command.hzpanel allow. Ce noeud n'est pas utilise par hz_lib — la vérification porte sur l'AcePermission configure (défaut admin), pas sur le nom de la commande.

Noeud ACE personnalise

Si vous ne voulez pas utiliser le noeud canonique admin (par ex. vous voulez qu'un sous-ensemble spécifique du staff ouvre le panneau sans leur donner toutes les autres commandes admin), configurez un noeud personnalise dans config.lua :

HZBridge.Panel.AcePermission = 'hzbridge.panel.admin'

Puis dans server.cfg :

add_principal identifier.license:YOUR_LICENSE group.hzbridge.staff
add_ace group.hzbridge.staff hzbridge.panel.admin allow

Le fallback (quand AcePermission est nil ou '') est 'admin' — aligne sur le défaut de config depuis v1.1.1. Les versions antérieures utilisaient 'hz_lib.admin' comme fallback, ce qui était un écart documente et bloquait les clients qui n'avaient que l'ACE canonique admin accorde. Mettez à jour vers v1.1.1 si vous êtes concerne.


Ce que vous pouvez modifier

Le panneau découvre automatiquement chaque ressource qui embarque un fichier bridge_schema.lua a sa racine. À partir de hz_lib v1.1.0, les modules suivants fournissent un schéma et s'integrent au panneau :

ModuleCe que vous pouvez ajuster
hz_libThème global — accent, surfaces, mode gradient, échelle arrondis
HZ-WeatherZones (éditeur + carte), prévisions, heure, saisons, événements, config
HZ-TelevisionChaînes, valeurs par défaut, options de performance
HZ-AudioMixerVolumes par défaut par catégorie, overrides de profil par joueur
Les scripts tiers peuvent ajouter leur propre onglet simplement en fournissant un bridge_schema.lua — voir la section DSL de schéma ci-dessous.

Le DSL de schéma

Le bridge_schema.lua de chaque module est un petit DSL Lua qui déclare la config que le panneau exposé. Le DSL s'exécute dans un environnement sandbox (pas de _G, pas de io brut, sous-ensembles restreints de string / table / math) donc un schéma buggue ou malveillant ne peut pas casser le serveur.

Exemple minimal

-- resources/your-script/bridge_schema.lua
module('your-script', {
    schemaVersion = 1,
    label = { en = 'Your Script', fr = 'Ton script' },
}, function()
    section('general', {
        label = { en = 'General', fr = 'Général' },
    }, function()
        field('Enabled', 'boolean', {
            default = true,
            label = { en = 'Enabled', fr = 'Activé' },
            description = {
                en = 'Master on/off switch for the script.',
                fr = 'Interrupteur global du script.',
            },
        })

        field('PlayerLimit', 'number', {
            default = 32, min = 1, max = 256, step = 1,
            label = { en = 'Max concurrent players', fr = 'Joueurs simultanés max' },
        })
    end)
end)

C'est tout — ouvrez /hzpanel, l'onglet "Your Script" apparait avec deux champs types, les changements de valeur sont écrits sur disque et diffuses à chaque client abonne.

Types de champ

TypeRenduNotes
booleanInterrupteur toggle
numberSaisie numérique avec min / max / stepVariante curseur quand step <= 1 et plage bornée
stringSaisie texte, pattern + maxLength optionnels
enumListe déroulante (choix unique)Choix via choices = { 'a', 'b', 'c' }
enum-multiMulti-selection contrainte (chips toggle)Nouveau en v1.1.0 — stocke en list<string>
colorSélecteur de couleur hexValide en #RRGGBB
keybindCapture de raccourci (touche + modificateurs)
list<string>Saisie de tags (ajout/suppression libre)
list<object>Éditeur de table inline avec colonnes itemSchema
vehicle-listGrille de sélection de véhiculesRemplie automatiquement depuis GetAllVehicleModels()
weapon-listGrille de sélection d'armesCatalogue statique, fourni sous web/src/data/weapons.ts
actionBouton qui declenche un événement serveurUtilisez pour des déclencheurs type "Appliquer preset" / "Reset"
enum-actionListe déroulante d'actions
viewComposant React personnalise pleine largeurRegistre sous web/src/views/<resource>/

Sections, groupes, onglets

  • section(id, { label, tab, dependsOn }, body) — regroupement visuel dans un onglet de module. tab est l'id du sous-onglet (déclare via module(name, { tabs = { ... } })). dependsOn = { field = 'X', equals = true } replie la section quand la condition parente est fausse.
  • group(id, { label }, body) — groupe repliable inline dans une section.
  • field(id, type, opts) — un seul champ. opts supporte default, description, requiresRestart, deprecated, hidden, dependsOn, plus les extras spécifiques au type.
  • invariant(sectionId, fn) — validation inter-champs. S'exécute à chaque tentative de sauvegarde. Retourne nil (ok) ou une chaîne (message d'erreur).
  • migrations({ [N] = function(values) ... end }) — incrémentez schemaVersion et fournissez une migration pour réécrire les anciennes valeurs sauvegardées.

Vues personnalisées

Si vous avez besoin d'une interface plus spécifique que ce que le renderer de formulaire type peut produire (un éditeur de carte, un graphique, un calendrier...), déclarez un champ view et enregistrez le composant React :

-- bridge_schema.lua
field('ZonesEditor', 'view', {
    component = 'hz_weather:ZoneEditor',
    events = {
        upsertZone = 'hz_weather:admin:upsertZone',
        deleteZone = 'hz_weather:admin:deleteZone',
    },
})

// web/src/views/hz_weather/index.tsx
import { registerView } from '../registry'
import { ZoneEditor } from './ZoneEditor'
registerView('hz_weather:ZoneEditor', ZoneEditor)

HZ-Weather fournit six vues comme implémentations de référence : Map, ZoneEditor, Events, Forecast, SeasonsPanel, TimePanel.


Cascade de thème (Apparence)

L'onglet Apparence dans hz_lib centralise les tokens visuels que chaque NUI HZ partage. Un seul changement retinte HZ-Weather, HZ-AudioMixer, HZ-Television en direct — sans redémarrage. Les nouveaux scripts qui se branchent héritent automatiquement de la cascade tant que leur NUI lit les variables CSS canoniques --hz-*.

Axes que vous contrôlez

AxeClésEffet
Accentaccent / accentHi / goldTrio signature Vice (rose / rose vif / doré). Presets : Vice, Industrial.
Surfacessurface / subpanel / tintPanneau extérieur / sous-panneau intérieur / teinte hover. RGB seulement — l'opacité est décidée par chaque consommateur. Presets : Surfaces Vice, Surfaces Neutres.
Mode gradientgradientMode = 'gradient' \| 'solid'solid aplatit chaque gradient d'accent en remplissage plat — rendu plus calme / style outil bureautique.
ArrondisradiusNone / Xs / Sm / Md / Lg / XlÉchelle de coins à six niveaux. Presets : Sharp (0/2/4/6/8/10), Soft (0/3/6/8/12/16), Round (0/4/8/12/16/20), Industrial (0/0/2/2/4/4).

Comment les modules le consomment

Le payload complet est diffusé à chaque fois que l'admin sauvegarde :

-- server (any resource)
AddEventHandler('hz_lib:theme:updated', function(theme)
    -- theme: { preset, accent, accentHi, gold, surface, subpanel, tint,
    --         gradientMode, radiusNone, radiusXs, radiusSm, ..., radiusXl }
end)

-- client
RegisterNetEvent('hz_lib:theme:updated', function(theme)
    SendNUIMessage({ action = 'theme', data = theme })
end)

Dans votre NUI, écrivez chaque clé comme variable CSS sur :root (par ex. --hz-accent-rgb, --hz-surface-rgb, --hz-radius-md). Les composants référencent les variables via rgb(var(--hz-accent-rgb) / 0.45) / var(--hz-radius-md) donc la cascade est automatique.

Obtenez le thème actuel de manière synchrone au démarrage :

local theme = exports['hz_lib']:GetTheme()

Ou demandez-le via TriggerServerEvent('hz_lib:theme:request') — le serveur répond avec TriggerClientEvent('hz_lib:theme:updated', src, theme).

Une référence complète copier-coller (forwarder Lua + TypeScript + applier vanilla JS + défauts CSS + binding Tailwind + pattern hide-with-HUD) est fournie dans le skill Claude hz-fivem-ui sous examples/theme-cascade-boilerplate.md.

Sécurité + audit

  • Schémas sandbox — Le DSL s'exécute sans io, os, debug bruts, et avec une bibliothèque standard restreinte. Un bridge_schema.lua buggue peut échouer au chargement (logue) mais ne peut pas crasher le serveur.
  • Écritures rate-limitees — défaut 10 ecritures par 5s par source. Configurable via HZBridge.Panel.RateLimitWrites / RateLimitWindowMs.
  • Tokens de session — chaque ouverture du panneau émet un token ephemere ; les tokens expires reçoivent un nouveau bootstrap à la prochaine requête.
  • Pipeline de validation — chaque sauvegarde passe par la validation de type de champ, les vérifications optionnelles pattern / min / max, puis les fonctions invariant au niveau section avant que la valeur n'atterrisse sur disque.
  • Log d'audit — chaque sauvegarde acceptée écrit une ligne dans le fichier d'audit rotatif (data/audit-YYYY-MM-DD.log). Les fichiers rotatifs plus anciens que HZBridge.Panel.AuditRetentionDays (défaut 90) sont supprimés au démarrage. Passez à 365 si vous avez besoin de rétention RGPD.
  • Écritures atomiques — la config effective par module est écrite dans data/<module>.json via tmpfile + rename. Les écritures en attente utilisent data/<module>.pending.json et sont commitees de manière atomique lors d'une sauvegarde réussie.
  • Vérification ACE par source à chaque action — pas seulement à l'ouverture du panneau. Un admin dont les droits sont révoqués ne peut pas continuer à éditer sur un panneau déjà ouvert.

Référence