Files
bambuddy/frontend
maziggy 812a70f326 fix(backup): stop overwrite writing a spool's other tag key (#2656)
Four of the review's smaller items.

    E6, the substantive one. tag_uid and tray_uuid are both in the overwrite setattr
    loop, so a spool matched on one key got the backup's *other* key written onto it.
    Neither column has a unique constraint (models/spool.py, and no unique index in
    the migrations), so nothing errors — a duplicate tag simply appears, after which
    _find_spool's .scalars().first() is non-deterministic and an AMS tag lookup
    resolves to an arbitrary one of the two spools. The same loop could also clear a
    tag the user had scanned since the backup was taken, when the backup entry held
    None.

    _find_spool now reports which key matched, and _guard_tag_overwrite drops a tag
    column from the write when the incoming value is empty and the local row has one
    (the backup predates the scan, so the local tag is the newer fact) or when
    another local spool already holds it. Announced in the tally the way the archive
    un-delete case already announces itself, rather than done silently — the
    spoolTagKept locale key landed with the rest of the i18n block last commit.

    E5. The Restore button is hidden without github:restore. All three endpoints are
    gated on it server-side, so the modal 403s on its first preview; offering the
    button is offering an action that cannot work. Button only — the card stays
    visible, since configuring backups is a separate permission — and hasPermission
    returns true with auth off, so a single-user instance is unaffected.

    E3. models/github_backup.py: the trigger comment said manual/scheduled; this PR
    added a third value.

    E4. ha_token_from_env: recommending no change, with the reasoning recorded as a
    test rather than left in a review thread. It is built only in the settings GET
    response, is absent from AppSettingsUpdate, and is therefore never a Settings
    row — it cannot reach a backup, so an allowlist entry would be dead code. Worse,
    a name-shaped exception to a belt-and-braces denylist is a live hole: an
    attacker-authored settings/app_settings.json could get a *token*-named row
    written by choosing that name.

    4 unit tests and 1 frontend test that fail against this commit's parent, plus 4
    controls: a free tag is still written, an unchanged tag is not reported as kept,
    an insert is unaffected, and the button still shows with auth disabled.
2026-08-15 14:13:35 +02:00
..
…
…
…

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...

      // Remove tseslint.configs.recommended and replace with this
      tseslint.configs.recommendedTypeChecked,
      // Alternatively, use this for stricter rules
      tseslint.configs.strictTypeChecked,
      // Optionally, add this for stylistic rules
      tseslint.configs.stylisticTypeChecked,

      // Other configs...
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...
      // Enable lint rules for React
      reactX.configs['recommended-typescript'],
      // Enable lint rules for React DOM
      reactDom.configs.recommended,
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])