OverviewFilesCommitsIssuesDocsReleasesPeers

keet-cli / keet-core-4.14.4-pretty / lib / core / contract / members / index.js

414 lines · javascript ref:
const e = require("autobase"),
  t = require("b4a"),
  {
    contract: {
      hyperschema: { getEnum: i, encode: a },
      hyperdispatch: { encode: s },
    },
  } = require("../../../../schema/spec"),
  {
    createDeviceResponse: r,
    decryptDeviceRequest: n,
    verifyAttestation: o,
    verifyInvitationDeviceRequest: d,
    generateMailboxKeyPair: c,
  } = require("../../invitations"),
  m = i("@keet/room-type"),
  { deviceStats: y, memberStats: k } = require("../stats"),
  { isMemberActive: l, getMemberForDeviceKey: u } = require("../query"),
  { addEvent: w } = require("../chat"),
  { updateAvatar: v } = require("../avatars"),
  { KeetContractError: f } = require("../errors"),
  b = require("../registration"),
  { isValidBuildType: p } = require("../helpers"),
  A = ["isMuted", "isBanned", "isShadowBanned"],
  h = { IsMuted: 1, IsBanned: 2, IsShadowBanned: 3 }
async function g(e, t, i) {
  const { db: a, hostcalls: s } = i,
    [r, n] = await Promise.all([
      a.get("@keet-private-id/identity"),
      a.get("@keet/threads", { id: 0 }),
    ]),
    o = await a.get("@keet/members", { key: r.publicKey }),
    d = { ...o, status: { ...o.status } },
    c = { key: e, memberKey: r.publicKey, profile: t, status: { isActive: !0, isIndexing: !0 } }
  return (
    (o.status.isPending = !1),
    await s.addWriter(e, { indexer: !0 }),
    await a.insert("@keet/members", o),
    await k(a, n, o, d),
    await a.insert("@keet/devices", c),
    await y(a, n, c, null),
    { added: !0, device: c }
  )
}
async function R(e, t, i, a, s, { db: r, writer: n, hostcalls: d, genesis: c }) {
  const [l, u, v, b, p] = await Promise.all([
    r.get("@keet/devices", { key: t }),
    r.get("@keet/members", { key: a.memberKey }),
    r.get("@keet/member-proofs", { key: a.memberKey }),
    r.get("@keet/system-digest"),
    r.get("@keet/threads", { id: 0 }),
  ])
  if (!u || !v) throw f.ABORT()
  if (l) return { added: !1, device: l }
  const A = { ...u, status: { ...u.status } },
    h = u.status.isPending,
    g = o(a, t, v.latestReceipt)
  v.latestReceipt = g
  const R = { isActive: !0, isIndexing: !!s?.isIndexing }
  ;((u.status.isActive = !0),
    (u.status.isPending = !1),
    h && !u.status.isModerator && s?.isModerator && (u.status.isModerator = !0),
    R.isIndexing && !u.status.isAdmin && (u.status.isAdmin = !0))
  const T = { key: t, memberKey: u.key, status: R, profile: i }
  if (
    (await r.insert("@keet/members", u),
    await k(r, p, u, A),
    await r.insert("@keet/devices", T),
    await y(r, p, T, null),
    await r.insert("@keet/member-proofs", v),
    c || (await d.addWriter(t, { indexer: R.isIndexing })),
    !h || b.settings.roomType === m.Broadcast)
  )
    return { added: !0, device: T }
  if (b.abi.version >= 11 && b.settings.isMembershipEventsDisabled) return { added: !0, device: T }
  const M = { memberJoined: { memberKey: u.key } }
  return (await w(r, n, M, e.timestamp), { added: !0, device: T })
}
async function T(e, t, i, s, { db: r, blobs: n, writer: d, genesis: c }) {
  const [m, y, l] = await Promise.all([
    r.get("@keet/members", { key: i.memberKey }),
    r.get("@keet/member-proofs", { key: i.memberKey }),
    r.get("@keet/threads", { id: 0 }),
  ])
  if (m) throw f.ABORT()
  const u = a("@keet/member-profile", t),
    w = o(i, u, y?.latestReceipt)
  let b = y
  ;(b
    ? ((b.latestReceipt = w), (b.profileAttestation = i.proof))
    : (b = { key: i.memberKey, latestReceipt: w, profileAttestation: i.proof }),
    t.avatar ? (t.avatar = await v(n, m?.profile.avatar, t.avatar)) : (t.avatar = null))
  const p = {
      isAdmin: !!s.isAdmin,
      isModerator: !!s.isModerator,
      isPending: !0,
      isActive: !0,
      isMuted: !1,
      isBanned: !1,
      isShadowBanned: !1,
    },
    A = { key: i.memberKey, devices: [], status: p, profile: t }
  return (
    await r.insert("@keet/members", A),
    await k(r, l, A),
    await r.insert("@keet/member-proofs", b),
    A
  )
}
async function M(e, t, i, { db: s, blobs: r }) {
  const [n, d] = await Promise.all([
    s.get("@keet/members", { key: i.memberKey }),
    s.get("@keet/member-proofs", { key: i.memberKey }),
  ])
  if (!n) throw f.ABORT()
  const c = a("@keet/member-profile", t),
    m = o(i, c, d?.latestReceipt)
  let y = d
  ;(y
    ? ((y.latestReceipt = m), (y.profileAttestation = i.proof))
    : (y = { key: i.memberKey, latestReceipt: m, profileAttestation: i.proof }),
    n.profile.timestamp >= t.timestamp ||
      ((n.profile = t),
      t.avatar
        ? (n.profile.avatar = await v(r, n.profile.avatar, t.avatar))
        : (n.profile.avatar = null),
      await s.insert("@keet/members", n),
      await s.insert("@keet/member-proofs", y)))
}
async function B(t, i, a, n, o, d) {
  const { db: c, trace: m, hostcalls: y, dryRun: k } = d,
    l = await c.get("@keet/room-capabilities"),
    u = y.discoveryKey,
    w = y.key,
    v = n.metadata,
    b = { isAdmin: v.canAdmin, isModerator: v.canModerate || v.canAdmin },
    p = { isIndexing: v.canAdmin, isModerator: v.canModerate }
  let A = null
  if (o)
    ((A = await g(i, a.device.profile, d)),
      await c.insert("@keet-private-id/device-identities", {
        deviceKey: i,
        privateProfileIdentity: o.privateProfileIdentity,
        deviceProof: o.deviceProof,
      }))
  else {
    const e = await c.get("@keet/members", { key: a.device.attestation.memberKey })
    if (e) {
      if (e.status.isRemoved) throw new f.ABORT()
      await M(t, a.member.profile, a.member.attestation, d)
    } else await T(t, a.member.profile, a.member.attestation, b, d)
    A = await R(t, i, a.device.profile, a.device.attestation, p, d)
  }
  const { device: h, added: B } = A,
    O = a.member.profile.mailbox,
    K = l.seed,
    { blindResponse: q } = r(O, K, n),
    I = s("@keet-private-id/resolve-pending-invitation", {
      header: { timestamp: 0 },
      blindResponse: q,
    }),
    P = e.encodeValue(I, { optimistic: !0, encrypted: !0 })
  if (
    (B &&
      ((n.metadata.canAdmin = !1),
      (n.metadata.canModerate = !1),
      (h.invitation = { id: n.id, response: P }),
      await c.insert("@keet/devices", h),
      await c.insert("@keet/invitations", n)),
    m && !k)
  ) {
    const e = { roomKey: w, discoveryKey: u, request: a, response: P, invitation: n }
    await m({ type: "@keet/add-device", value: e })
  }
}
async function O({ device: e, member: t }, i, a) {
  const { db: s, hostcalls: r, writer: n } = a,
    [o, d, c] = await Promise.all([
      s.get("@keet/members", { key: e.attestation.memberKey }),
      s.get("@keet/member-proofs", { key: e.attestation.memberKey }),
      s.get("@keet/system-digest"),
    ])
  if (!o || !d) throw f.ABORT()
  const y = c.settings.roomType === m.DirectMessage
  if (c.abi.version >= 12 && !o.status.isActive && !y) throw f.ABORT()
  ;(await R(i, n.key, e.profile, e.attestation, { isActive: !0, isIndexing: !1 }, a),
    await r.ackWriter(n.key),
    t && (await M(i, t.profile, t.attestation, a)))
}
async function K(e, i, a) {
  const { db: s, hostcalls: r, writer: n } = a
  let o = null
  try {
    o = d(e)
  } catch (e) {
    throw f.ABORT()
  }
  if (!o) throw f.ABORT()
  const { id: c, request: m } = o
  if (!t.equals(m.deviceKey, n.key)) throw f.ABORT()
  const [y, k, l] = await Promise.all([
    s.get("@keet/join-requests", { deviceKey: n.key }),
    s.get("@keet/invitations", { id: c }),
    s.get("@keet-private-id/identity"),
  ])
  if (!k || y) throw f.ABORT()
  const u = k.metadata.buildType,
    w = m.device.profile.buildType
  if (!p(w, u)) throw f.INVALID_BUILD_TYPE()
  ;(await r.ackWriter(n.key),
    k.requiresConfirmation || l
      ? await s.insert("@keet/join-requests", { deviceKey: n.key, id: c, request: m })
      : await B(i, n.key, m, k, null, a))
}
async function q({ header: e, deviceKey: t, identity: i }, a) {
  const { db: s, writer: r } = a,
    [n, o] = await Promise.all([u(s, r.key), s.get("@keet/join-requests", { deviceKey: t })])
  if (!l(n) || !o) throw f.ABORT()
  const { id: d, request: c } = o,
    m = await s.get("@keet/invitations", { id: d })
  if (!m) throw f.ABORT()
  ;(await B(e, t, c, m, i, a), await s.delete("@keet/join-requests", { deviceKey: t }))
}
async function I({ header: e, deviceKey: t }, i) {
  const { db: a, writer: s } = i,
    [r, n] = await Promise.all([u(a, s.key), a.get("@keet/join-requests", { deviceKey: t })])
  if (!l(r) || !n) throw f.ABORT()
  await a.delete("@keet/join-requests", { deviceKey: t })
}
async function P({ invitation: e }, { db: t, writer: i, hostcalls: a }) {
  const [s, r, n] = await Promise.all([
    u(t, i.key),
    t.get("@keet/invitations", { id: e.id }),
    t.get("@keet/system-digest"),
  ])
  if (!l(s)) throw f.ABORT()
  if (s.status.isMuted) return
  const o = e.metadata
  if (r || n.settings.roomType === m.DirectMessage) throw f.ABORT()
  return (
    (o.buildType = n.settings.buildType),
    o.canModerate && !s.status.isModerator && (o.canModerate = !1),
    o.canAdmin && !s.status.isAdmin && (o.canAdmin = !1),
    (e.isActive = !0),
    (e.anchor = await a.createAnchor(i.key, i.seq + 1)),
    t.insert("@keet/invitations", e)
  )
}
async function x({ id: e }, { db: t, writer: i }) {
  const [a, s] = await Promise.all([u(t, i.key), t.get("@keet/invitations", { id: e })])
  if (!l(a) || !a.status.isModerator || !s) throw f.ABORT()
  ;((s.isActive = !1), await t.insert("@keet/invitations", s))
}
async function E({ deviceKey: e, fromDeviceKey: i }, { db: a, writer: s, hostcalls: r }) {
  const [n, o, d] = await Promise.all([u(a, s.key), u(a, e), u(a, i)])
  if (!l(n)) throw f.ABORT()
  if (!t.equals(n.key, o.key)) throw f.ABORT()
  if (!t.equals(n.key, d.key)) throw f.ABORT()
  const [c, m] = await Promise.all([
    a.get("@keet/devices", { key: i }),
    a.get("@keet/devices", { key: e }),
  ])
  if (!c || !m) throw f.ABORT()
  if (!c.status.isActive) throw f.ABORT()
  if (!m.status.isActive) throw f.ABORT()
  if (!c.status.isIndexing) throw f.ABORT()
  if (m.status.isIndexing) throw f.ABORT()
  if (t.equals(c.key, m.key)) throw f.ABORT()
  ;(await r.addWriter(m.key, { isIndexer: !0 }),
    await r.addWriter(c.key, { isIndexer: !1 }),
    (m.status.isIndexing = !0),
    (c.status.isIndexing = !1),
    await a.insert("@keet/devices", c),
    await a.insert("@keet/devices", m))
}
async function D(e, { db: t, writer: i, hostcalls: a }) {
  const [s, r] = await Promise.all([t.get("@keet/devices", { key: i.key }), u(t, i.key)])
  if (!s.status.isActive || !l(r) || !r.status.canAdmin) throw f.ABORT()
  ;(await a.addWriter(i.key, { indexer: !0 }),
    (s.status.isIndexing = !0),
    await t.insert("@keet/devices", s))
}
async function _(e, { db: t, writer: i, hostcalls: a }) {
  const [s, r] = await Promise.all([t.get("@keet/devices", { key: i.key }), u(t, i.key)])
  if (!s.status.isActive || !l(r) || !r.status.canAdmin) throw f.ABORT()
  ;(await a.addWriter(i.key, { indexer: !1 }),
    (s.status.isIndexing = !1),
    await t.insert("@keet/devices", s))
}
async function N({ header: e, blindRequest: t }, i) {
  const { db: a } = i,
    s = await a.get("@keet/room-capabilities")
  if (!s) throw f.ABORT()
  let r = null
  try {
    const e = c(i.hostcalls.key, s.seed)
    r = n(e, t)
  } catch (e) {
    throw f.ABORT()
  }
  if (!r) throw f.ABORT()
  const { withInvitation: o, withMemberProof: d } = r
  return o ? K(o, e, i) : d ? O(d, e, i) : void 0
}
async function S({ header: e, member: { profile: t, attestation: i } }, a) {
  return M(e, t, i, a)
}
async function W({ profile: e }, { db: i, writer: a }) {
  const [s, r] = await Promise.all([u(i, a.key), i.get("@keet/devices", { key: a.key })])
  if (!(r.status.isActive && l(s) && r && t.equals(r.key, a.key))) throw f.ABORT()
  ;((r.profile = e), await i.insert("@keet/devices", r))
}
async function j({ key: e }, { db: i, writer: a, hostcalls: s }) {
  const [r, n, o] = await Promise.all([
    u(i, a.key),
    i.get("@keet/devices", { key: e }),
    i.get("@keet/threads", { id: 0 }),
  ])
  if (!n || !l(r) || !t.equals(n.memberKey, r.key)) throw f.ABORT()
  const d = { ...n, status: { ...n.status } }
  ;((n.status.isActive = !1),
    (n.status.isIndexing = !1),
    s.removeable(n.key) && (await s.removeWriter(n.key)),
    await i.insert("@keet/devices", n),
    await y(i, o, n, d))
}
async function L({ memberKey: e, key: t, status: i }, { db: a, writer: s }) {
  const [r, n] = await Promise.all([u(a, s.key), a.get("@keet/members", { key: e })])
  if (!l(r)) throw f.INACTIVE_MEMBER()
  if (!n) throw f.TARGET_MEMBER_NOT_FOUND()
  if (!r.status.isModerator) throw f.MODERATORS_ONLY()
  if (0 === t || t > A.length) throw f.INVALID_MEMBER_STATUS()
  const o = A[t - 1]
  ;((n.status[o] = i[o]), await a.insert("@keet/members", n))
}
async function V({ key: e, isModerator: t, isAdmin: i }, { db: a, writer: s, hostcalls: r }) {
  const [n, o, d] = await Promise.all([
    u(a, s.key),
    a.get("@keet/members", { key: e }),
    a
      .find("@keet/active-devices-by-member-key", { gte: { memberKey: e }, lte: { memberKey: e } })
      .toArray(),
  ])
  if (!l(n)) throw f.INACTIVE_MEMBER()
  if (!o) throw f.TARGET_MEMBER_NOT_FOUND()
  const c = n.status.isAdmin,
    m = n.status.isModerator,
    y = o.status.isAdmin
  if (!c && (y || i)) throw f.ADMINS_ONLY()
  if (!m) throw f.MODERATORS_ONLY()
  let k = !1
  if (y && !i)
    for (const e of d) r.removeable(e.key) ? await r.addWriter(e.key, { indexer: !1 }) : (k = !0)
  ;(k || (o.status.isAdmin = i),
    (o.status.isModerator = t || i),
    await a.insert("@keet/members", o))
}
async function C({ header: e, key: i }, { db: a, writer: s, hostcalls: r }) {
  const [n, o, d, c, v] = await Promise.all([
    u(a, s.key),
    a.get("@keet/members", { key: i }),
    a.get("@keet/system-digest"),
    a
      .find("@keet/active-devices-by-member-key", { gte: { memberKey: i }, lte: { memberKey: i } })
      .toArray(),
    a.get("@keet/threads", { id: 0 }),
  ])
  if (!l(n)) throw f.INACTIVE_MEMBER()
  if (!o) throw f.TARGET_MEMBER_NOT_FOUND()
  const b = { ...o, status: { ...o.status } },
    p = t.equals(n.key, o.key),
    A = n.status.isModerator,
    h = o.status.isAdmin
  if (!p && h) throw f.ABORT()
  if (!p && !A) throw f.MODERATORS_ONLY()
  ;((o.status.isActive = !1),
    (o.status.isModerator = !1),
    (o.status.isAdmin = !1),
    (o.status.isPending = !!p),
    p || ((o.status.isRemoved = !0), await a.insert("@keet/tmp-removed-members", { key: o.key })))
  for (const e of c) {
    const t = { ...e, status: { ...e.status } }
    ;(r.removeable(e.key) &&
      (await r.removeWriter(e.key, { indexer: !1 }), (e.status.isIndexing = !1)),
      (e.status.isActive = !1),
      await a.insert("@keet/devices", e),
      await y(a, v, e, t))
  }
  if (
    (await a.insert("@keet/members", o), await k(a, v, o, b), d.settings.roomType === m.Broadcast)
  )
    return
  if (d.abi.version >= 11 && d.settings.isMembershipEventsDisabled) return
  const g = p ? { memberLeft: { memberKey: o.key } } : { memberRemoved: { memberKey: o.key } }
  await w(a, s, g, e.timestamp)
}
function U(e) {
  ;(e.add("@keet/add-device", N),
    e.add("@keet/update-device", W),
    e.add("@keet/remove-device", j),
    e.add("@keet/start-indexing", D),
    e.add("@keet/stop-indexing", _),
    e.add("@keet/accept-join-request", q),
    e.add("@keet/reject-join-request", I),
    e.add("@keet/update-member", S),
    e.add("@keet/change-member-role", V),
    e.add("@keet/change-member-status", L),
    e.add("@keet/remove-member", C),
    e.add("@keet/add-invitation", P),
    e.add("@keet/remove-invitation", x),
    e.add("@keet/move-indexer", E))
}
module.exports = { [b]: U, MemberStatus: h, addDevice: R, addMember: T }