feat: support parent-based areaRestrictions in config for areas.json#1195
feat: support parent-based areaRestrictions in config for areas.json#1195unseenmagik wants to merge 123 commits intoWatWowMap:developfrom
Conversation
## 📝 Commit description (detailed) - Added a new optional `parent: string[]` field to `authentication.areaRestrictions` so admins can grant access by parent area name (and automatically include all child areas). - Updated areaPerms.js to: - accept `parent` in the restriction rule, - build a parent→child key map from loaded geojson, - and grant access for both the parent area and its children. - Updated GraphQL area filtering (`scanAreas` / `scanAreasMenu`) to allow permissions to match by `feature.properties.key` (in addition to `name`/`parent`) so the filtering behavior stays consistent with the new parent-based key logic. - Updated type definitions (config.d.ts) to reflect the new optional `parent` array. - Updated the example config (local.example.json) with `parent: []` entries for documentation and schema clarity.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 600eed64d7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Adjust area permission handling and grouped child-area selection. Include the parent area key in grouped toggles when present, while preserving existing single-child behavior.
|
Thanks for the updates @Mygod i will test these out. |
|
bruh why so many bugs 😠 |
|
I think I may have let my agent be too free. Steering it. (Rip my tokens.) Btw just in case you can't tell yet, I'm using your PR as a testing playground for my new skill. ;) |
|
Ok done. Please test thoroughly. Do not make any mistakes. |
There was a problem hiding this comment.
Pull request overview
This PR adds parent-based area restriction support (including multi-domain/scoped behavior) so areas.json users can grant access via parent areas while keeping scan area selection/filtering consistent across the UI and GraphQL/backend filters.
Changes:
- Extend
authentication.areaRestrictionsrules with optionalparententries and implement scoped/normalized area restriction handling (including parent→children expansion). - Update GraphQL scan area resolvers + SQL/RTREE filtering to operate on canonical area keys/features and respect unrestricted/no-access grants.
- Update the scan-area UI (map tile + drawer table) to handle parent/child selection and reset persisted scan-area selections when needed.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/store/useStorage.js | Adds persisted store versioning + migration to reset scan area selections. |
| src/features/scanArea/utils.js | Adds helper utilities for deriving selectable/valid area keys (parent/child aware). |
| src/features/scanArea/ScanAreaTile.jsx | Updates scan-area map interactions to toggle parent/child selections and search by key/name. |
| src/features/drawer/areas/Child.jsx | Updates scan-area drawer row selection logic to account for parent keys and grouped selection. |
| src/features/drawer/areas/AreaTable.jsx | Updates scan-area drawer table filtering, match counting, and grouped parent rendering. |
| src/components/auth/Local.jsx | Redirects to /blocked/... flow for blocked responses during local login. |
| server/src/utils/mergePerms.js | Minor formatting-only change. |
| server/src/utils/getServerSettings.js | Normalizes/sanitizes area restrictions before returning settings to the client. |
| server/src/utils/getAreaSql.js | Switches area SQL filtering to operate on consolidated feature geometries + unrestricted sentinel. |
| server/src/utils/getAccessibleScanAreasMenu.js | New helper to filter scan area menu output based on effective area restrictions. |
| server/src/utils/filterRTree.js | Updates in-memory RTree filtering to use consolidated feature objects + unrestricted sentinel behavior. |
| server/src/utils/consolidateAreas.js | Reworks consolidation to handle scoped grants, parent grants, multi-domain ambiguity, and feature-based filtering. |
| server/src/utils/areaPerms.js | Major expansion: encoding/decoding grants, parent grants, scopes, normalization, and public-safe restrictions. |
| server/src/services/TelegramClient.js | Adds strict group lookup option and request-scoped areaPerms serialization; adds linked-perms refresh. |
| server/src/services/logUserAuth.js | Logs only public/normalized area restrictions to avoid leaking internal sentinel/grant metadata. |
| server/src/services/LocalClient.js | Merges linked provider perms (discord/telegram) with optional live refresh + request-scoped areaPerms. |
| server/src/services/DiscordClient.js | Adds strict lookup support, request-scoped resolveAreaPerms, and linked-perms refresh. |
| server/src/services/areas.js | Builds RTree and scan-area objects across multi-domain scan areas. |
| server/src/routes/rootRouter.js | Ensures session perms use request-scoped serialized area restrictions for settings bootstrap. |
| server/src/models/Weather.js | Applies consolidated area filtering using feature geometries and unrestricted sentinel behavior. |
| server/src/middleware/apollo.js | Normalizes area restrictions into the GraphQL context (request-scoped). |
| server/src/graphql/resolvers.js | Updates scan area filtering logic and delegates menu filtering to getAccessibleScanAreasMenu. |
| packages/types/lib/config.d.ts | Updates config typings to include optional parent on area restriction rules. |
| packages/config/lib/mutations.js | Preserves parent on area restriction rules when applying config mutations. |
| config/local.example.json | Documents parent: [] in example config for clarity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return { ...rest, children, rows } | ||
| }), | ||
| [data, search], | ||
| ) |
There was a problem hiding this comment.
allRows's useMemo uses accessibleAreaKeys (e.g., for groupKey), but the dependency array is [data, search]. If permissions change, the memo can return stale rows. Include accessibleAreaKeys in the dependency list (or restructure so the memo doesn't close over it).
| const parsedStoredPerms = storedPerms | ||
| ? typeof storedPerms === 'string' | ||
| ? JSON.parse(storedPerms) | ||
| : storedPerms | ||
| : null |
There was a problem hiding this comment.
mergeLinkedPerms parses storedPerms via JSON.parse(storedPerms) without a guard. If the DB value is malformed/corrupted, this will throw and can break local login. Wrap the parse in a try/catch and fall back to null (and optionally log a warning with the user id/provider).
| const parsedStoredPerms = storedPerms | |
| ? typeof storedPerms === 'string' | |
| ? JSON.parse(storedPerms) | |
| : storedPerms | |
| : null | |
| let parsedStoredPerms = null | |
| if (storedPerms) { | |
| if (typeof storedPerms === 'string') { | |
| try { | |
| parsedStoredPerms = JSON.parse(storedPerms) | |
| } catch (error) { | |
| this.log.warn( | |
| `Failed to parse stored linked ${type} perms for id ${id}`, | |
| error, | |
| ) | |
| } | |
| } else { | |
| parsedStoredPerms = storedPerms | |
| } | |
| } |
| const safeUser = { | ||
| ...user, | ||
| perms: normalizedPerms | ||
| ? { | ||
| ...normalizedPerms, | ||
| areaRestrictions: getPublicAreaRestrictions( | ||
| normalizedPerms.areaRestrictions, | ||
| ), | ||
| } | ||
| : normalizedPerms, |
There was a problem hiding this comment.
getPublicAreaRestrictions() filters out NO_ACCESS_SENTINEL, and getServerSettings sends only these public restrictions to the client. That means a user whose resolved restrictions are [NO_ACCESS_SENTINEL] will appear to have an empty restriction list on the client (which elsewhere is treated as “unrestricted”), leading to inconsistent UI vs server behavior. Consider preserving an explicit “no access” signal for the client (e.g., keep a boolean/flag, or include a dedicated sentinel that the client understands) while still stripping scoped/grant metadata.
| export function getValidAreaKeys(features, accessibleAreaKeys = []) { | ||
| return [ | ||
| ...new Set( | ||
| features.flatMap((feature) => | ||
| getAreaKeys(features, feature, accessibleAreaKeys), | ||
| ), | ||
| ), |
There was a problem hiding this comment.
getValidAreaKeys calls getAreaKeys for every feature, and getAreaKeys may scan features again to find children. This is O(n²) and is invoked on each tap in ScanAreaTile. Consider precomputing a parentName→childKeys map once (e.g., via useMemo) and using that to build valid keys in O(n).
|
My agent has decided that this PR cannot be merged unless a major refactor is done first.
Sorry to disappoint you but the codebase at this point probably can't handle more complexity. |
|
Also rip to wasted tokens. |
📝 PR Overview
For those that still use an areas.json file (rather than Koji), you can add a new parent parameter in the features.collection which can be used to filter access to areas on ReactMap.
parent: string[]field toauthentication.areaRestrictionsso you can grant access by parent area name (and automatically include all child areas).parentin the restriction rule,scanAreas/scanAreasMenu) to allow permissions to match byfeature.properties.key(in addition toname/parent) so the filtering behavior stays consistent with the new parent-based key logic.parentarray.parent: []entries for documentation and schema clarity.