Skip to content

A number of webapp / CI / docs fixes - #1254

Merged
peterthomassen merged 18 commits into
mainfrom
20260901_misc
Sep 4, 2026
Merged

peterthomassen merged 18 commits into
mainfrom
20260901_misc

Conversation

@peterthomassen

Copy link
Copy Markdown
Member

No description provided.

@peterthomassen peterthomassen changed the title Various small fixes A number of webapp / docs fixes Sep 1, 2026
@peterthomassen
peterthomassen marked this pull request as ready for review September 1, 2026 19:51
@peterthomassen peterthomassen changed the title A number of webapp / docs fixes A number of webapp / CI / docs fixes Sep 2, 2026
The /auth/account/ endpoint requires the perm_manage_tokens permission for
both GET and PATCH, which was not documented anywhere.
Django's duration parser only accepts hours when both minutes and seconds
follow, so "5:30" is 5 minutes 30 seconds, not 5 hours 30 seconds. The
notation is thus [DD] [[HH:]MM:]ss, not [DD] [HH:[MM:]]ss. Fix it in the API
docs and in the web UI form hint, which also gains an example.
The Content column was capped at 45vw by CSS targeting the Vuetify 2 data
table markup (.theme--light.v-data-table > .v-data-table__wrapper). Those
selectors match nothing in Vuetify 3, so the cell grew to the width of the
hidden mirror element that RecordItem uses to size its inputs, pushing TTL,
Last touched and Actions out of view for long values such as DKIM keys.

Cap the column with the data table's own maxWidth instead, which restores
scrolling inside the cell, and drop the remaining dead Vuetify 2 rules.

Fixes #1216
The rules used ::v-deep in an unscoped style block, where it ships as an
invalid selector, so none of them applied. Anchor them on the .crud-item row
class that the data table puts on every item row, restoring top-aligned cells,
the green flash after saving, the highlighted save button on dirty rows and the
white background of focused inputs in the domain and token lists. Also drop the
rules for .text-disabled, a class no longer applied anywhere, and the copies of
these rules in CrudListRecord.
Track the rows to highlight in component state and let itemClass emit the
successFade class, so the highlight survives the re-render that follows a
save, and start it once the server has confirmed the update. Highlight rows
on hover in all lists, not just where rows are clickable.
App.vue gives every field a white background, which put white boxes into
colored table rows. Make the fields transparent inside table rows, so the
dirty and error colorings, the hover tint and the save flash cover the row
evenly.
Home and End place the caret with setSelectionRange, which does not scroll the
cell that record values are scrolled in, so in a record with a single long
value, such as a DKIM key, the caret ends up outside the visible area. Measure
the text before the caret and scroll the cell accordingly, which also covers
the other places that set the caret, such as deleting across a field boundary.
The measuring mirror takes its font from the input, including the letter
spacing that Vuetify sets there, and is absolutely positioned so that it
shrinks to its text; the sizing mirror takes the same font, as it otherwise
makes the field too narrow for its text, which then scrolls its own content
beyond the reach of the cell's scroll position.
Picking a domain type validates the domain field, and a type is picked when
the page opens, so the field showed its error message before anything could be
entered. Reset the validation instead while the field is empty.

Fixes #1204
Put the captcha and its buttons at the right end of the form row, so that they
line up with the right border of the input fields, and lay the buttons out
across the captcha's width in a compact size. The solution field is centered
next to the block and takes the width the captcha leaves, which is more in
image mode than in audio mode.
Comment thread docs/auth/tokens.rst
Autofocus the first digit, so that the code can be typed as soon as the dialog
opens. In the 2FA dialog, this applies when the account has a single TOTP
token, which is preselected. With several tokens, focus stays on the dialog,
where the token is picked first.
Dependabot never grouped the api pip updates: the `pip-api` group used
`update-types`, which dependabot-core only evaluates when it knows the
dependency's current version. For a plain requirements.txt that is the
case only for `==` pins, so every `~=` requirement failed the check and
got its own PR.

Use `patterns: "*"` instead, and split the group by dependency type.
For pip, dependabot derives the type from the file name (anything
containing "dev" is a development dependency), so move the two
test-only packages to requirements-dev.txt. Django and Django REST
framework are excluded from the production group so that their updates
keep coming as separate PRs.
The npm ecosystem was dropped from the dependabot configuration in
9c67472, leaving www/webapp unwatched. Track it again, at a monthly
interval, with updates collated into one group PR for runtime
dependencies and one for development dependencies.

The `sass` ignore from 8a6bb2c is dropped: it was added because sass
warned about the then-current Vue 2 era vuetify, which no longer
applies. The `eslint` ignore stays, as eslint 9 requires migrating
.eslintrc.cjs to flat config first.
eslint 8 is out of support since October 2024, but dependabot was told
to ignore it in a75ced2 because eslint 9 has breaking changes and the
plugins were not compatible at the time. The plugins are ready now
(eslint-plugin-vue, -import, -vuetify and -vue-scoped-css all declare
eslint 9 support and ship flat configs), so the config format was the
only thing left.

Translate .eslintrc.cjs to eslint.config.js, and take the opportunity
to lint against the Vue 3 rule sets. The eslintrc `extends` referred to
`plugin:vue/essential` and `plugin:vue-scoped-css/recommended`, which
are the Vue 2 variants (58 and 4 rules) even though the webapp has been
on Vue 3 for a while; the flat config uses `flat/essential` and
`flat/recommended` instead (82 and 9 rules). The three "Preparation for
vue3" rule overrides and the deep combinator override are dropped, as
the Vue 3 sets enable all four as errors.

eslint-plugin-vue is bumped to ^10 because eslint-plugin-vuetify
depends on ^10.8 and registers it under the `vue` plugin key; with our
^9 the two copies collide as "Cannot redefine plugin". @eslint/js and
globals become direct dependencies, as flat config imports them.

`--ignore-path .gitignore` is gone in eslint 9, so the ignores move
into the config, and the lint globs lose the `json` extension: the only
JSON file under src/ sits in the ignored vendored module directory.

Two code changes fall out of the stricter setup. eslint 9 flipped the
`no-unused-vars` `caughtErrors` default from "none" to "all", which
surfaces two unused catch bindings; both catch blocks ignore the error,
so they lose the binding. And the Vue 3 scoped-css rules reject the
argument-less `::v-deep` in CrudListRecord.vue, which becomes
`:deep(...)` and compiles to the same `[data-v-*] <selector>` output.

Verified with node 24: `npm run lint` covers 73 files without findings,
`npm test` and `npm run build` pass.
`npm run lint` was not run anywhere in CI, so nothing caught the broken
lint invocation fixed in b23ee08, and nothing guards the eslint flat
config now. Run it in the test-webapp job, which already has the
dependencies installed.
Vuetify styles alerts with `flex: 1 1 0`, which makes an alert that sits
directly inside a card a shrinkable flex item. Once a dialog wants more
height than the viewport offers, the flex algorithm takes it out of the
alert, laying it out shorter than its own content: in the domain creation
confirmation, the success message's 16px padding drops to 6px on a narrow
window, and in a short window the text is clipped by the bottom edge.
DomainSetupDialog, TOTPVerifyDialog and MFA put alerts directly inside the
card and are affected; the CrudList dialogs wrap theirs in a v-form, whose
children are not flex items.
Vuetify teleports overlays into a .v-overlay-container that it appends
next to .v-application rather than inside it, so rules written as
`.v-application p` stop at the dialog boundary: paragraphs in dialogs lose
their bottom margin and run together, and `.text-primary` falls back to
vuetify's amber instead of ours. Name both containers, as the vuetify 3
migration in f8dd7bb already did for links.

With the list rules reaching into dialogs, the scoped rule for the DNSSEC
list is redundant and goes: its padding-left now comes from the app-wide
rule in both contexts, and list-style-position: outside was always the
initial value.
`limit_domains` started out as 0 and was replaced only once the request to
auth/account/ came back, so until then -- or forever, if that request
failed -- the create dialog said "You have 0 of 0 domains left", disabled
the Save button and warned that the maximum number of domains had been
reached. A user with one of one domain used reported exactly this. null is
already the component's value for "no limit applies", so starting there
costs the quota hint instead of locking the user out of domain creation,
and the server still enforces the actual limit.
Align the domain list with desecapi.permissions.WithinDomainLimit.
@peterthomassen
peterthomassen force-pushed the 20260901_misc branch 2 times, most recently from ab3b00c to 7af2137 Compare September 4, 2026 16:41
@peterthomassen
peterthomassen merged commit 7af2137 into main Sep 4, 2026
22 of 25 checks passed
@peterthomassen
peterthomassen deleted the 20260901_misc branch September 4, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants