feat(group): 1st implementation of Groups
this implements first version (manageable only by admin right now)
routes:
GET /api/groups
List subject groups (paginated). Admin-only.
POST /api/groups
Create a new ReBAC subject group. Admin-only. The name must match the RFC 5321 local-part shape and be globally unique (case-insensitive).
GET /api/groups/search
Search non-virtual groups by name substring. Authenticated only (no admin role required) — backs the share-dialog recipient autocomplete.
GET /api/groups/{id}
Fetch a single group's details. Admin-only.
DELETE /api/groups/{id}
Delete a group. Cascades to `subject_group_members` (FK) and to `access_grants` rows referencing this group as a subject. Admin-only.
PATCH /api/groups/{id}
Update a group's metadata. Admin-only. v1 only persists name renames.
GET /api/groups/{id}/effective-members
List every user transitively reached through this group (members of members of members, etc.). Used by admin / audit tooling. Admin-only.
GET /api/groups/{id}/members
List the *direct* members of a group (one level only). Admin-only.
POST /api/groups/{id}/members
Add a member to a group. Exactly one of `user_id` / `group_id` must be provided. Adding a group-member runs a write-time cycle check and a nesting-depth check (max 8). Admin-only.
DELETE /api/groups/{id}/members/group/{gid}
Remove a nested group-member from a group. Admin-only.
DELETE /api/groups/{id}/members/user/{uid}
Remove a user-member from a group. Admin-only.
fix hurl
groups
round
groups
This commit is contained in:
@@ -12,12 +12,13 @@
|
||||
*/
|
||||
|
||||
import { ui } from '../../app/ui.js';
|
||||
import { MySharesList } from '../../components/mySharesList.js';
|
||||
import { collectGroupSubjectIds, MySharesList } from '../../components/mySharesList.js';
|
||||
import { shareModal } from '../../components/shareModal.js';
|
||||
import { i18n } from '../../core/i18n.js';
|
||||
import * as viewPrefs from '../../core/viewPrefs.js';
|
||||
import * as itemTooltip from '../../features/itemTooltip.js';
|
||||
import { grants } from '../../model/grants.js';
|
||||
import { groups } from '../../model/groups.js';
|
||||
|
||||
/** @import {FileItem, FolderItem} from '../../core/types.js' */
|
||||
|
||||
@@ -69,6 +70,14 @@ const mySharesView = {
|
||||
/** @type {MySharesList|null} */
|
||||
_component: null,
|
||||
|
||||
/**
|
||||
* Cumulative id→GroupItem map of every group subject seen so far in this
|
||||
* session. Each `_loadPage()` merges newly resolved entries so subsequent
|
||||
* pages don't re-resolve already-known groups.
|
||||
* @type {Record<string, import('../../core/types.js').GroupItem>}
|
||||
*/
|
||||
_knownGroupMeta: {},
|
||||
|
||||
/** @type {string} */
|
||||
_groupBy: '',
|
||||
|
||||
@@ -188,6 +197,24 @@ const mySharesView = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve full GroupItem records for any group-subject grants on
|
||||
// this page so lane headers and identity rows render the localised
|
||||
// name + virtual-aware icon. Cheap — one search call shared across
|
||||
// every group visible on the page.
|
||||
if (this._component) {
|
||||
const groupIds = collectGroupSubjectIds(data.items);
|
||||
if (groupIds.size > 0) {
|
||||
try {
|
||||
const resolved = await groups.resolveGroups(groupIds);
|
||||
const merged = { ...this._knownGroupMeta, ...resolved };
|
||||
this._component.setGroupMeta(merged);
|
||||
this._knownGroupMeta = merged;
|
||||
} catch (err) {
|
||||
console.warn('mySharesView: failed to resolve group names', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFirstPage) {
|
||||
this._component?.render(data.items, mode.viewMode);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user