mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 04:13:17 +08:00
Replace allowlist-only gating with phone-scoped grants: global admins, DM members, per-group admins/members, pending approval via quote YES/NO or settings UI. Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
3 KiB
JavaScript
78 lines
3 KiB
JavaScript
import { validateAccessGrant, normalizeAccessGrant } from '../../../../src/channels/shared/access-grant.mjs';
|
|
|
|
export const SET_ACCESS_GRANT_ENDPOINT = 'bot.access-grant.set';
|
|
export const RESOLVE_ACCESS_PENDING_ENDPOINT = 'bot.access-pending.resolve';
|
|
|
|
export function validAccessGrantPayload(payload) {
|
|
try {
|
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)
|
|
|| !Object.hasOwn(payload, 'botId') || !Object.hasOwn(payload, 'grant')
|
|
|| typeof payload.botId !== 'string'
|
|
|| !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) return false;
|
|
validateAccessGrant(payload.grant);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function validAccessPendingResolvePayload(payload) {
|
|
try {
|
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return false;
|
|
const keys = Reflect.ownKeys(payload);
|
|
if (keys.length !== 4
|
|
|| !Object.hasOwn(payload, 'botId')
|
|
|| !Object.hasOwn(payload, 'pendingId')
|
|
|| !Object.hasOwn(payload, 'action')
|
|
|| !Object.hasOwn(payload, 'resolvedByPhone')) return false;
|
|
if (typeof payload.botId !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) {
|
|
return false;
|
|
}
|
|
if (typeof payload.pendingId !== 'string' || !/^[A-Za-z0-9_-]{6,64}$/.test(payload.pendingId)) {
|
|
return false;
|
|
}
|
|
if (payload.action !== 'approve' && payload.action !== 'deny') return false;
|
|
if (typeof payload.resolvedByPhone !== 'string' || !payload.resolvedByPhone.trim()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function publicAccessGrant(grant) {
|
|
const normalized = normalizeAccessGrant(grant);
|
|
if (!normalized) return null;
|
|
return {
|
|
version: normalized.version,
|
|
globalAdmins: [...normalized.globalAdmins],
|
|
directMembers: normalized.directMembers.map((m) => ({ ...m })),
|
|
groups: Object.fromEntries(Object.entries(normalized.groups).map(([jid, group]) => [jid, {
|
|
...(group.title ? { title: group.title } : {}),
|
|
admins: [...group.admins],
|
|
members: group.members.map((m) => ({ ...m })),
|
|
}])),
|
|
pending: normalized.pending
|
|
.filter((entry) => entry.status === 'pending')
|
|
.map((entry) => ({
|
|
id: entry.id,
|
|
kind: entry.kind,
|
|
...(entry.groupJid ? { groupJid: entry.groupJid } : {}),
|
|
phone: entry.phone,
|
|
...(entry.lid ? { lid: entry.lid } : {}),
|
|
...(entry.pushName ? { pushName: entry.pushName } : {}),
|
|
...(entry.requestText ? { requestText: entry.requestText } : {}),
|
|
createdAt: entry.createdAt,
|
|
unresolved: entry.unresolved === true,
|
|
})),
|
|
contacts: normalized.contacts.slice(0, 100).map((contact) => ({
|
|
...(contact.phone ? { phone: contact.phone } : {}),
|
|
lids: [...contact.lids],
|
|
...(contact.pushName ? { pushName: contact.pushName } : {}),
|
|
lastSeenAt: contact.lastSeenAt,
|
|
scenes: [...contact.scenes],
|
|
...(contact.groupJids ? { groupJids: [...contact.groupJids] } : {}),
|
|
})),
|
|
};
|
|
}
|