upstream: integrar 0.9.5 en modelos y vistas medianas
chats_model: se adopta 0.9.5 y se reinyecta el replyTo de la rama (responder a mensajes). El fix del salt en joinByInvite ya venia en upstream, y el limite de mensajes pasa a ser configurable (60/h) en vez del parche que lo saltaba en movil. config-manager: version de 0.9.5 con karvanMod, los seis modulos de public y toda la normalizacion de bottomBarPins reinyectados; housingMod fuera. Entran blogsMod y pollsMod del dev. Se adopta 0.9.5 en votes_model (ya trae el margen de 2 minutos de la rama y anade codigos de error), blobHandler (superconjunto compatible: mantiene handleBlobUpload y anade subida multiple y deteccion de OGG), market_model (misma refactorizacion mas opiniones y compradores), agenda_model y agenda_view (el codigo de 0.9.5 ya degrada solo sin housingModel), opinions_view, cv_view y trending_view. peers_view y parliament_view se quedan como estan: sus celdas llevan data-label, que es lo que OasisMobile.css usa para convertir las tablas en tarjetas en movil. Verificado con la app en marcha: 20 rutas OK, interfaz intacta (10 categorias, 47 modulos, 7 accesos rapidos, 0 etiquetas vacias) y Karvan completo de punta a punta — crear sala, enviar y leer mensajes, mailbox de senalizacion WebRTC con miembros, y la pagina de sala sirviendo karvan.js con el panel de videollamada.
This commit is contained in:
parent
82b6c3ae05
commit
c662e37668
10 changed files with 310 additions and 40 deletions
|
|
@ -17,10 +17,22 @@ const normalizeTags = (raw) => {
|
|||
const INVITE_CODE_BYTES = 16
|
||||
const VALID_STATUS = ["OPEN", "INVITE-ONLY", "CLOSED"]
|
||||
|
||||
const DEFAULT_MESSAGES_PER_HOUR = 60
|
||||
|
||||
module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
||||
let ssb
|
||||
const openSsb = async () => { if (!ssb) ssb = await cooler.open(); return ssb }
|
||||
|
||||
const MESSAGES_PER_HOUR = (() => {
|
||||
try {
|
||||
const raw = getConfig()?.chats?.messagesPerHour
|
||||
const n = parseInt(raw, 10)
|
||||
return Number.isFinite(n) && n > 0 ? n : DEFAULT_MESSAGES_PER_HOUR
|
||||
} catch (_) {
|
||||
return DEFAULT_MESSAGES_PER_HOUR
|
||||
}
|
||||
})()
|
||||
|
||||
const ownCrypto = chatCrypto || tribeCrypto
|
||||
const lookupKey = (rid) => (ownCrypto && ownCrypto.getKey(rid)) || (tribeCrypto && tribeCrypto.getKey(rid)) || null
|
||||
const lookupKeys = (rid) => {
|
||||
|
|
@ -265,6 +277,7 @@ module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
|||
invites,
|
||||
author: c.author || node.author,
|
||||
createdAt: c.createdAt || new Date(node.ts).toISOString(),
|
||||
replyTo: c.replyTo || null,
|
||||
updatedAt: c.updatedAt || null,
|
||||
encrypted: !!c.encrypted,
|
||||
tribeId: c.tribeId || null,
|
||||
|
|
@ -293,8 +306,7 @@ module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
|||
text,
|
||||
image: c.image || null,
|
||||
author: c.author || node.author,
|
||||
createdAt: c.createdAt || new Date(node.ts).toISOString(),
|
||||
replyTo: c.replyTo || null
|
||||
createdAt: c.createdAt || new Date(node.ts).toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -358,6 +370,15 @@ module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
|||
return {
|
||||
type: "chat",
|
||||
|
||||
async encryptionKeyFor(chatRootId, tribeId = null) {
|
||||
if (!tribeCrypto) return null
|
||||
if (tribeId) {
|
||||
const k = await getTribeFirstKeyFor(tribeId)
|
||||
if (k) return k
|
||||
}
|
||||
return lookupKey(chatRootId) || null
|
||||
},
|
||||
|
||||
async resolveRootId(id) {
|
||||
const ssbClient = await openSsb()
|
||||
const messages = await readAll(ssbClient)
|
||||
|
|
@ -597,6 +618,18 @@ module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
|||
|
||||
let list = chatCollab.visibleThenCollapsed(collectChats(idx), uid)
|
||||
|
||||
const lastMsgAt = new Map()
|
||||
const lastMineAt = new Map()
|
||||
for (const node of idx.msgNodes.values()) {
|
||||
const cid = node.c && node.c.chatId
|
||||
if (!cid) continue
|
||||
const r = idx.rawRootOf(cid) || cid
|
||||
const t = node.ts || 0
|
||||
if (t > (lastMsgAt.get(r) || 0)) lastMsgAt.set(r, t)
|
||||
if (node.author === uid && t > (lastMineAt.get(r) || 0)) lastMineAt.set(r, t)
|
||||
}
|
||||
list = list.map(c => ({ ...c, lastMsgAt: lastMsgAt.get(c.rootId) || 0, lastMineAt: lastMineAt.get(c.rootId) || 0 }))
|
||||
|
||||
if (filter === "mine") list = list.filter(c => c.author === uid)
|
||||
else if (filter === "recent") list = list.filter(c => new Date(c.createdAt).getTime() >= now - 86400000)
|
||||
else if (filter === "open") list = list.filter(c => c.status === "OPEN" || c.status === "INVITE-ONLY")
|
||||
|
|
@ -707,7 +740,7 @@ module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
|||
let chatKey = null
|
||||
if (tribeCrypto && typeof matchedInvite === "object") {
|
||||
if (matchedInvite.ekChain) {
|
||||
const chain = tribeCrypto.decryptChainFromInvite(matchedInvite.ekChain, code, matchedInvite.salt)
|
||||
const chain = tribeCrypto.decryptChainFromInvite(matchedInvite.ekChain, code, matchedInvite.salt, 3)
|
||||
if (Array.isArray(chain) && chain.length) {
|
||||
for (const entry of chain) {
|
||||
if (Array.isArray(entry.keys) && entry.keys.length) {
|
||||
|
|
@ -809,7 +842,12 @@ module.exports = ({ cooler, tribeCrypto, chatCrypto, tribesModel }) => {
|
|||
const c = m.value?.content
|
||||
return c?.type === "chatMessage" && c?.chatId === chat.rootId && m.value?.author === userId && (m.value?.timestamp || 0) >= oneHourAgo
|
||||
}).length
|
||||
if (process.env.OASIS_MOBILE !== '1' && recentCount >= 3) throw new Error("Rate limit: max 3 messages per hour")
|
||||
if (recentCount >= MESSAGES_PER_HOUR) {
|
||||
const err = new Error(`Rate limit: max ${MESSAGES_PER_HOUR} messages per hour`)
|
||||
err.code = "CHAT_RATE_LIMIT"
|
||||
err.retryAfterMinutes = 60
|
||||
throw err
|
||||
}
|
||||
|
||||
const now = new Date().toISOString()
|
||||
let content = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue