feat(groups): allow renaming a group id

Add PUT /admin/api/groups/:id/rename (owner role) which re-points the group's routes, migrates the per-group webhook secret (tenant:{id}) and pending invites, and records an audit entry. The group editor's id field is now editable and the console renames first, then persists remaining edits under the new id.
This commit is contained in:
RhenCloud 2026-08-13 09:58:02 +08:00
parent 39bb639883
commit f4959eebf8
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
11 changed files with 372 additions and 53 deletions

View file

@ -120,6 +120,35 @@ export async function revokeInvite(kv: KVNamespace, token: string): Promise<void
if (raw) await removeFromIndex(kv, raw.groupId, token);
}
/**
* Re-point every pending invite of a group to its new id (group rename).
* Best-effort: a failure leaves the old invites in place (they will be
* rejected as group-missing after the rename).
*/
export async function migrateInvites(kv: KVNamespace, from: string, to: string): Promise<void> {
try {
const tokens = await readIndex(kv, from);
if (tokens.length === 0) {
await kv.delete(indexKey(from));
return;
}
const moved: string[] = [];
for (const token of tokens) {
const invite = await kv.get<Invite>(inviteKey(token), "json");
if (invite && invite.groupId === from) {
await kv.put(inviteKey(token), JSON.stringify({ ...invite, groupId: to }), {
expirationTtl: INVITE_TTL,
});
moved.push(token);
}
}
await kv.put(indexKey(to), JSON.stringify(moved));
await kv.delete(indexKey(from));
} catch (err) {
log.warn({ err, from, to }, "Failed to migrate invites on group rename");
}
}
/**
* Adds the accepting user to the invited group (or upgrades their role when
* they are already a viewer) and consumes the invite. The invited role is