Changelog
O que há de novo.
Cada melhoria para a plataforma, como ele envia.
· VOIPService
Persistir chamadas e associar chamadas, textos e correio de voz com leads
As chamadas nunca foram gravadas em nenhum lugar — VoipCallEventService publicou um evento NATS para o navegador e não manteve nada, e RECORDINGS só existe quando o áudio foi capturado. A sincronização por instance agora puxa o histórico de chamadas através do PhoneProvider abstraction and upserts PHONE CALL LOG, keyed on (instance, provedor call id). Essa é a única rota que funciona para o FreePBX e Microsoft Teams, nenhum dos que oferece um webhook por chamada utilizável. A varredura é o único escritor. Persistindo do Webhook RingCentral também foi tentado e removido: sua notificação transporta telefoniaSessionId enquanto o call-history API retorna um ID de registro diferente, então os dois escritores produziram dois linhas e duas entradas de linha do tempo para uma chamada. As chamadas são atribuídas a uma extensão apenas quando essa extensão está realmente no Chama. O endpoint do RingCentral é extensão/~ e ignora o argumento, então looping por extensão seria de outra forma arquivar chamadas de cada membro sob o O ciclo foi visitado pela última vez. Os IDs de gravação são resolvidos por página através do RecordingLookupService, o mesmo enriquecimento do histórico do softphone fica — sem ele a aba sabe uma gravação existe mas não pode tocá-lo. Ligação de texto em envio/receção; ligação de mensagens de voz em sincronização.
· InitializerService
Esquema e sementes de ponto de contacto para comunicações de chumbo
Hibernate cria as três novas tabelas das entidades de compartilhamento de lib; isso adiciona o que não pode expressar — o índice parcial único que mantém no máximo um aberto janela de ponto de contacto por (líder, slot) e índice de cobertura Varredura da linha do tempo. A semente abre uma primeira janela para cada lead que precede o índice. Não um comunicação back-fill: nenhuma chamada histórica ou mensagem está associada a Qualquer coisa. Sem ela, um chumbo pré-existente não tem pontos de contacto e poderia Nunca corresponde a uma chamada recebida, por isso todas as páginas ficariam vazias para sempre. Ele é executado através LeadContactPointService em vez de um volume INSERT..SELECT assim Há precisamente uma implementação da canonicalização. Um gêmeo SQL teria para re-derivar o decapagem de dígitos, manipulação NANP, o piso de extensão e endereço Desembrulhar - se, e qualquer divergência posterior seria invisível — filas sem sementes Simplesmente pare de corresponder ao tráfego ao vivo. Aplicado contra produção: 66 pontos de contato abertos em 45 leads.
· kamo-shared-library
Índice de pontos de contacto pontuais e ligações de comunicação de chumbo
Adiciona a coluna vertebral de onde a guia de comunicação lê. LEAD CONTACT POINTS registra cada número de telefone e e-mail que uma pista já teve, como uma janela com activeFrom/activeTo. LEAD COMUNICAÇÕES registra o durável ligação entre um lead e uma chamada, correio de voz, texto ou e-mail, momento em que acontece contra os pontos de contato vivem nesse momento — assim mudando O número de uma pista liga apenas o tráfego futuro e deixa a história exactamente como era. PHONE CALL LOG é o registro de chamadas que a plataforma nunca teve: Quando o áudio foi capturado, as chamadas perdidas e não gravadas não deixaram rasto. ContactCanon é a definição única de "o mesmo número" para o conjunto plataforma; LeadController.canônico Telefone para agora delegados para ele assim pesquisa reversa e correspondência de comunicação não pode discordar. LeadContactPointService e LeadCommunicationLinker não são deliberadamente estereótipos — uma compartilhamento de lib @Service que precisa de um repositório falha na inicialização do contexto em cada serviço que não tenha adicionado o pacote, que tomou a produção para baixo três vezes. Os serviços de propriedade declaram os feijões; LeadService toma-os @Autowired(required=false) para que ainda fios em todos os outros lugares.
· MediaService
Capture o comando de demonstração antes da etapa de reserva
O formulário demo do site de marketing recolheu nome, e-mail, empresa, tamanho da empresa, indústria e uma mensagem, então avançou para agendamento sem contar a ninguém. A criação de chumbo só vivia dentro de si. todos os visitantes que não completaram uma reserva — incluindo todos os visitantes embora nenhuma disponibilidade seja publicada — foi descartada após entrega detalhes. - POST ************* escreve a pista por conta própria. Possuir org vem do cabeçalho X-Public-Chat-Org-Id APIService selos do chave validada, nunca do corpo. - findOrCreatePublicLead é agora o único caminho de escrita para todos os três superfícies públicas (formulário demo, agenda, junta), para que um visitante produza um chumbo por muitos passos que completam. Marca o "KamoCRM" da org. Solicitar" produto do fornecedor e leva o mercado desse fornecedor, e ele rotas através de LeadService.createLead em vez de salvar a entidade diretamente — o old inline save produziu nenhuma linha de histórico e nenhum evento LEAD CREED, então aqueles Os leads nunca apareceram numa grelha já aberta. - O tamanho da empresa, a indústria e a mensagem do visitante são mantidos como um histórico de chumbo entrada; Chumbo não tem coluna para nenhum deles e eles são a parte a O vendedor lê mesmo. - GET /api/public/webinar/types permite que uma página pública descubra o tipo de identificação do que carregar uma cópia codificada, e slots/disponível agora também retorna disponivelTimes — a mesma disponibilidade resolvida contra as próprias org's fuso horário em instantes que podem ser postados de volta como um programadoStartTime.
· kamo-shared-library
Primitivos de captura de chumbo demo-request públicos
Um visitante que preenche o formulário demo do site de marketing, mas nunca marca um tempo não deixou nenhum vestígio: o chumbo só foi escrito dentro da transação de reserva. Capturá-lo na frente precisa de três peças que a biblioteca não tinha. - ************* ****** corresponde a uma submissão repetida de volta para o levar a mesma forma já produzida. Âmbito de aplicação do produto vendedor em vez de para o e-mail sozinho para que um formulário anônimo nunca possa adotar (e reiniciar) um lead pessoal criado para esse endereço. - PublicWebinarDateSlotsDTO: disponibilidade resolvida em instantes absolutos. A slot armazenado é uma data local mais uma hora de relógio de parede sem zona, que O navegador não pode transformar-se num agendado agendadoStartTime. - PublicWebinarTypeDTO: um tipo webinar sem a lista do apresentador, assim um público página pode descobrir o tipo id sem vazar endereços de e-mail apresentador.
· kamo-internal
Configuração do Provedor de Registro na navegação e escreva as dicas rápidas
A página Configuração do Provedor renderizada dentro do SettingsView, mas só foi acessível por digitando o URL — faltava em ambos os lugares que listam as configurações destinos: o menu de configurações de navegação e a landing page de configurações. Ele agora leva cada um, combinando a ordem de tabulação, uma vez que qual provedor hospeda reuniões decide que das outras configurações estão ainda disponíveis. As dicas rápidas sobre as cinco páginas existentes nunca foram escritas — cada texto foi um placeholder ("Help Heading", "Bullet Lobby Title"). Eles agora dizem algo, e cobrir as configurações adicionadas para Zoom e Equipes: controles de reunião, código de acesso política, funções de desvio de lobby e apresentador, tração de registro remoto e transcrições, e convites enviados pelo prestador. Cada aba também explica onde os provedores diferem, então um controle deficiente tem uma razão declarada em vez de apenas parecer quebrado.
· kamo-internal
Aplicar regras de acesso geo e IP em cargas de página
checkAccess avaliou as próprias regras e nada correspondeu a toda a sua vida: Tipos de regra comparados contra Enquanto o editor escreve os nomes de enum WHITELIST/BLACKLIST/TEMP BLOCK. Geo foi um comentário dizendo que Node tem nenhuma base de dados GeoLite. Ambas as metades se foram — agora lê-se o veredicto Serviço de Segurança caches em Redes e pede que o serviço em uma falta, então há uma implementação do política e nenhuma cópia deixada aqui à deriva. O endereço é o lúpulo público mais à direita de X-Forwarded-For em vez do mais à esquerda. Proxies anexa, então o elemento esquerdo é o que o cliente enviou, e lê-lo seria deixar um cabeçalho decidir em que país o visitante é tratado como estando. Um visitante bloqueado agora recebe um documento 403 simples em vez de JSON bruto — isso atinge um navegador fazendo uma navegação de topo. Nomeia nenhuma regra e nenhum país: o leitor não pode Agir sobre isso, e alguém que examina o limite não deve receber um mapa dele. Falha em todos os caminhos. Um proxy que bloqueia quando o Redis é pouco acessível O inquilino todo está desligado.
· APIService
Aplicar as regras de acesso dos inquilinos no portal
Este filtro está inerte desde que foi escrito. resolveOrgId ler org: domínio:<host> de Redis, nada nunca escreveu essa chave, e o retorno nulo caiu para permitir; geo branch era um bloco if vazio com um comentário diferindo para SecurityService, que foi Não aplicar também. Todos os pedidos passaram. Ele agora lê o veredito SegurançaService caches e pede que serviço em uma falta, então o A política vive num só lugar, em vez de ser reimplementada aqui. Caminho quente é um Redis ler; uma falha custa uma chamada HTTP de curto prazo por organização e endereço por 30s. O endereço do cliente é o hop mais público do X-Forwarded-For. Proxies adicionar, assim que o O elemento esquerdo é fornecido pelo atacante — ler teria deixado um cabeçalho escolher qual país o pedido é julgado como vindo. Esvazia sondas de vida, pré-voo CORS e o próprio ponto final da decisão, e nunca juízes cluster-endereços internos: serviço-a-serviço tráfego não tem país e Bloqueá-la derrubaria a plataforma. Falha-abrir em cada caminho, com um teste por dependência. Isto fica na frente de todos os inquilinos. Tráfego de API, então um filtro que bloqueia quando Redis é lento é uma falha total. Também cai ******************* andaimes do commit inicial, num pacote sem @SpringBootConfiguration to Encontrar. Nunca passou; IC constrói com -DskipTests para que nada notado. Removendo faz 'mvn test' utilizável aqui.
· SecurityService
Aplicar regras de acesso geo e IP
As regras foram armazenadas, armazenadas na Redes e nunca consultadas. O AccessCheckController foi o único chamador da avaliação e nada chamado AccessCheckController; APIService's filtrar orgs resolvidos de uma chave Redis nada escreveu e tratou o erro como permissão. Configurar um bloco geográfico não fez nada. AccessDecisionService é agora a única autoridade. O login o chama diretamente, o gateway o filtro e o proxy kamo-internal atingem a mesma decisão através do endpoint de verificação, e a política existe uma vez em vez de uma por camada. A cópia de Node tinha ido longe suficiente para não corresponder a nada: comparou tipos de regras com "whitelist"/"temp-block" enquanto o publisher escreve WHITELIST/TEMP BLOCK. A resolução GeoLite é armazenada em um cache em Redis por um dia, positiva e negativamente, e salta Endereços privados inteiramente. O endereço vem de um cabeçalho, então sem uma cache negativa um chamador girando bogons transforma cada pedido em uma nova sonda da tabela 5.7M-row – A forma do DoS findByIpInRange já foi reescrita uma vez para evitar. ClientIpResolver lê o lúpulo mais público da direita do X-Forwarded-For, não o mais à esquerda. O elemento esquerdo é o que o cliente enviou: proxies anexá-lo, então "X-Forwarded-For: 8.8.8.8" mais o próprio anexo de Traefik teria deixado qualquer um escolher o país o seu bloco é Julgado contra. Cada regra geo seria um cabeçalho de distância do bypass. Falha-aberta em todo, por dependência e afirmada por dependência. Um controle de acesso que falhas fechadas em uma oscilação Redis bloqueia cada inquilino fora da plataforma, o que é pior incidente do que a janela que fecha. Um endereço na lista branca também ignora o geo, então um administrador que bloqueia o seu próprio país tem uma maneira de voltar.
· kamo-internal
Cartão de registro da plataforma para o aplicativo Zoom Marketplace
Completa o caminho de conexão de um clique do Zoom. A infra- estrutura já resolve PlatformOAuthProviderType.ZOOM, mas sem cartão aqui um operador de plataforma tinha nenhum lugar para entrar nas credenciais do Marketplace, então a configuração do provedor de reunião tela sempre relatou um clique de conexão como não registrado. Opcional por design: o caminho servidor-a-servidor per-org não precisa de nada aqui e permanece o padrão recomendado, então uma carta vazia simplesmente mantém um clique escondido. As reuniões de equipes reutilizam o registro Microsoft 365 existente.
· kamo-internal
Equipas Microsoft Fornecedor de telefone, de frente para trás
Adiciona Equipas a... através de: seleção do provedor, a configuração de Serviços de Comunicação Entra + Azure, consentimento de administrador do inquilino, a ligação por membro, e um softphone in-browser. O softphone é a parte que difere estruturalmente de qualquer outro provedor. Equipes não expõem registro SIP a terceiros, então uma linha de Equipes não pode ser Registrado na SIP.js ou no WebPhone da RingCentral. A rota suportada é Azure Endpoint de equipes personalizadas dos serviços de comunicação: a infraestrutura troca o token Entra de membro para um token ACS, e TeamsAcsPhoneSdk transforma isso em um chamar agente atrás da mesma interface PhoneSdk os adaptadores SIP implementar, assim useSoftphone e cada componente de UI ficar provedor-agnóstico. SipCredenciais ganha campos acs* opcionais; sipInfo está simplesmente vazio para Equipes. O pacote ACS é somente para navegador e importado dinamicamente dentro de start(), uma vez que importação estática iria quebrar renderização do servidor. Duas verdades específicas de Equipas apareceram na UI em vez de serem deixadas para falhar em tempo de execução: chamada in-browser é opcional (deixe ACS em branco e o org ainda fica presença e histórico de chamadas, com o discador escondido em vez de erro), e voicemail está desligado por padrão porque Teams não tem API de voicemail — mensagens no local na caixa de correio do Exchange de cada membro, então lê-los é materialmente mais consentir que um administrador deve optar por deliberadamente. O cartão de conexão de membro na aba de configurações de telefone está agora provedor-conhecido em vez de RingCentral. Ambos os prestadores necessitam de uma autorização por membro, para diferentes razões: RingCentral porque um softphone só pode se registrar como o extensão própria da conta conectada, equipes porque a presença escreve e a chamada A troca de fichas é apenas delegada. Marcação: o azulejo usa a marca corporativa da Microsoft e o nome do produto simples. Microsoft permite nomear equipes para descrever uma integração, mas restringe equipes Logos e branding para parceiros certificados Teams, então nenhum glifo Teams é enviado. Verificado: teste npm — 40 ficheiros, 394 testes, 0 falhas; tsc -- noEmit clean; o Guarda-chave i18n e guarda-cliente-navegação ambos passam. Correntes de dicionário aterrissadas Primeiro no dicionário kamo-tradução 0801a17.
· VOIPService
Microsoft Teams Phone como provedor
As equipes não são uma API, então o provedor cola três aviões de controle juntos: • Gráfico, somente para aplicativos — histórico de chamadas (getPstnChalls), leituras de presença, diretório e inventário de dispositivos de equipes. Os registos de chamadas são a autorização de aplicação por design; Microsoft afirma claramente que eles não têm formulário delegado. • Gráfico, delegado — escreve presença (Presença. O ReadWrite só existe como um delegada, permissão auto-somente) e correio de voz, que vive na Trocar caixa de correio porque as equipas não têm qualquer recurso de correio de voz. • Azure Communication Services — o softphone in-browser Equipes Linha de telefone, através da troca personalizada de fichas de ponto final de equipes. Este é o apenas via suportada: as equipas não expõem nenhum registo SIP a terceiros, e o Graph chamando API se junta como um bot em vez de como o usuário. As capacidades são calculadas a partir do que uma instância está configurada em vez de codificado, então uma org sem SCA relata voiceCalls=false e a IU esconde o discador em vez de aparecer no meio de uma chamada 501. Deliberadamente ausente, porque nenhuma API da Microsoft existe em qualquer versão: SMS de um número Teams, chamada filas, encaminhamento de chamadas, resposta automática e gravação inicial/parada. Aqueles ficar na interface padrão que aumenta 501. As credenciais resolvem primeiro por instance (uma org regista a sua própria aplicação Entra — a forma comum da empresa, e não precisa de nada de Kamo a plataforma) e voltar por campo para uma linha MICROSOFT TEAMS no registro OAuth da plataforma. Onboarding corre em dois saltos porque o inquilino deve ser descoberto antes que possa ser preso: um administrador entra, o `tid` do id token dá ao inquilino real, então são enviados para o inquilino /v2.0/administrador. O consentimento é confirmado por: cunhando um token de aplicativo e chamando Graph — Microsoft retorna admin consent= Verdadeiro mesmo na falha, e adverte que o parâmetro de redirecionamento `tenant` é forjable, assim nem é de confiança. Ambos os fluxos de equipes e a conexão por membro delegado multiplex no /api/voip/oauth/callback, o único caminho já público no gateway, então o APIService não precisa de nenhuma nova rota. Alterar as notificações do /api/voip/webhook existente pela mesma razão. O aperto de mão de Graph difere do RingCentral — uma validação Consulta do item parâmetro ecoou como texto/plain — e é respondido antes de qualquer outra coisa, desde O gráfico reduz as notificações por dez minutos se um ponto final for lento. Assinaturas são criados sem filtro de propósito: o filtro de assinatura ainda tem como alvo coleção legacy `participants` que deixou de devolver dados em junho de 2026. Dois defeitos pré-existentes corrigidos porque as equipas os fazem suportar carga: • CallController, CapabilitiesController e CallFeaturesController resolvido o provedor via getPrimaryForOrg — a instância ativa mais antiga da org Tipo. Isso só é certo para uma única organização provedora. As equipas normalmente vão ser um segundo provedor, então o histórico de cada membro, correio de voz e estado de agente teria sido servido por qualquer provedor foi criado primeiro. Eles agora usam um membro-consciente getForMember, espelhando o que SipController já fez. • Excluindo uma instância membro esquerdo voip oauth token linhas atrás, uma vez que há nenhum FK para cascata de, e eles continuaram relatando "conectado". Verificado: teste mvn — 133 testes, 0 falhas (49 novas).
· kamo-internal
Configurações de configuração do provedor e configurações de reunião de capacidade
Adiciona a aba Configuração do Provedor a /settings/features/meets e faz cada outra tab honesto sobre o provedor que está configurando. useMeeting já não compila meet.<domínio>/<sala> no navegador — foi isso que aconteceu fez de cada reunião uma reunião Kamo Meets independentemente da configuração. Agora. cria a reunião através da infra-estrutura e usa os URLs que o provedor retorna, assim o Centro de Interação, a janela da ferramenta de chat e o diretório de membros todos caminho através da abstração sem mudanças próprias: eles compartilham MeetingDialog, que compartilha este gancho. - Configuração do provedor: cartões de provedor com uma barra de configuração-cobertura, então o custo de a mudança é visível antes da mudança, em vez de ser descoberta posteriormente em uma separador de comandos acinzentados. Formas credenciais são somente escrita — o servidor retorna quais campos são definidos, nunca seus valores — mais um teste de conexão ao vivo e instruções de configuração por provedor. - Gating de capacidade: useMeetProvider lê as configurações suportadas pelo provedor e MeetSettingRow desabilita o resto com uma dica nomeando o provedor. Não suportado as configurações são mostradas ao invés de ocultas, e estão genuinamente desabilitadas ao invés de Simplesmente ofuscados, por isso não podem ser alterados pelo teclado. Salva-os completamente. A ligação falha aberta — uma pesquisa de capacidade falhada mostra tudo, uma vez que o cinzento uma tela de trabalho é pior do que mostrar um controle que a infra- estrutura ignoraria. - Novas configurações de suporte aos outros provedores: join-before-host, chat em reunião, compartilhamento de tela, discagem, somente usuários autenticados, comprimento do código de acesso e incorporação, lobby bypass escopo, apresentação e controles participantes, gravação em nuvem, transcrições, gravação de downloads e convites enviados pelo provedor. - /meet/join/<ref> está agora provedor-consciente: Kamo Meets ainda recebe uma chave única cunhado na hora do clique, Zoom e Equipes redirecionam para a URL do próprio provedor. Ele aceita um ID de reunião ou um nome de quarto, por isso os convites enviados antes desta alteração Continua a trabalhar. - O link de convite por email é construído a partir de um ID de reunião validado por UUID em vez de um nome de sala, mantendo a regra existente de que uma URL fornecida pelo cliente nunca é confiou em um e-mail de marca org.
· kamo-shared-library
Tipo de fornecedor MICROSOFT TEAMS
Adiciona MICROSOFT TEAMS ao VoipProviderType. A enum persiste como ORDINAL em Então, a posição é de carga e constante vai por último; a classe Javadoc agora diz isso em vez de deixar a próxima pessoa para inferi-lo. Sem alteração de DDL — uma coluna ORDINAL não tem valor Lista branca, por isso isto não precisa de um KamoInitializer. Adiciona duas consultas derivadas no Repositório do MemberVoipOAuthToken: • findByProviderAndInstanceId — mapeia uma identidade de provedor de entrada (uma Microsoft Intra objeto ID em um registro de chamada de equipes) de volta para um membro Kamo. • deleteByInstanceId — essas linhas não têm FK para a instância, então excluir um provedor estava deixando-os para trás ainda relatando "conectado".
· APIService
Roteie o provedor de reunião OAuth callback
Zoom e Microsoft redirecionam o navegador para este gateway após uma org autorizar A aplicação do Kamo. O popup pousa aqui sem nenhuma sessão de inquilino — as org viajam em ?state — por isso, o retorno precisa do passe sem sessão, autth-stripping callback do provedor de e-mail já usa, não o /api/meet/** forward.
· MediaService
Abstração do provedor de reunião com Zoom e Microsoft Teams
As reuniões foram Kamo Meets por construção — MeetService persistiu em uma sala Meet Nome e mais nada. Isso introduz a abstração e dois provedores remotos atrás dele. Meet/Provider espelha a camada do provedor de email: MeetingProviderResolver monta um MeetingContext (credenciais descriptografados, subvenção OAuth atualizada, política org, o conta host do lado do provedor) e um mapa de capacidade, por isso os MeetingScheduler em vez de ramificar no tipo de provedor. Adicionando um quarto provedor significa adicionar um feijão, não editar sites de chamada. - KamoMeetProvider: nomeação de sala e construção de URL, movido servidor-lado. A org-namespacing que impede dois inquilinos de compartilhar uma conferência Prosody usado para vive no navegador; agora está preso por testes. - ZoomProvider: REST v2, Server-to-Server OAuth ou o aplicativo Marketplace. Cria em nome do membro organizador, puxa gravações e transcrições em nuvem. - MicrosoftTeamsProvider: Graph onlineMeetings, app-only ou delegada. Sua teste de conexão sondas reunião criação, porque a política de acesso à aplicação Equipes também exigem é a coisa usual que um administrador perde — a permissão sozinho autentica multa e depois 403s em cada reunião. Reunião Característica é o contrato com o administrador UI: cada provedor declara o configurações que ele pode honrar, e as telas de configurações desativar o resto. As alegações são asseverado em testes em vez de confiável, uma vez que um provedor de cobrança excessiva produz alternâncias que silenciosamente não fazem nada — a falha que esta camada existe para prevenir. ProviderRecordingSweep puxa gravações remotas para o próprio armazenamento de Kamo A retenção, a quota e os resumos pós-sessão funcionam de forma idêntica em todos os prestadores. Os downloads estão fechados: este serviço também serve bate-papo e reuniões sob 1Gi limite, então uma gravação superdimensionada é ignorada com um aviso em vez de arriscar A cápsula. As credenciais são criptografadas com a chave de plataforma existente, então nenhum novo segredo tem A ser provido. O tratamento de sessão partilhado pelos objectivos de encontro move- se para MeetSessionResolver – deliberadamente sem ampliar sua verificação de direitos.
· InitializerService
Esquema para a camada de reunião multifornecedor
Cria ************* e org meeting outh tokens, acrescenta as colunas de identificação do prestador às RECORDAÇÕES DE REUNIÃO e RECORDAÇÕES DE REUNIÃO e Configurações de zoom/equipas com capacidade para ORG MEET CONFIG, e preenche todas as configurações existentes organização para uma linha explícita KAMO MEET/ACTIVE. Duas coisas que esta corrida nos ensinou, ambas codificadas aqui. Compilações automáticas de ddl do Hibernate o SessionFactory antes de qualquer CommandLineRunner, então as instruções CREATE TABLE normalmente no-op e as colunas chegam sem predefinições — o backfill, portanto fornece id e timestamps explicitamente em vez de confiar neles, por isso sua primeira versão silenciosamente não inseriu nada. E a singularidade declarada sobre o entidades significa Hibernate mantém suas próprias restrições, de modo que os índices nomeados revisão anterior adicionada foram duplicados; eles são deixados aqui. Adiciona com.kamo.z.shared.meet à lista explícita @EnableJpaRepositórios, sem que os novos repositórios não têm feijão e o KI aborta antes de qualquer corredor disparar.
· kamo-shared-library
Modelar reuniões como provedor-agnóstico
Kamo Meets foi o único backend possível: OrgMeetConfig foi um Meet plano saco de configurações e um MeetingRecord não tinha nada além de um nome de quarto. Isto acrescenta esquema para executar uma organização em Zoom ou Microsoft Teams. - meet.provider: MeetingProviderType / MeetingProviderStatus, o per-org OrgMeetingProviderEntity (blob credencial criptografado, último estado verificado) e OrgMeetingOAuthTokenEntity, espelhando o pacote email provider que já confirma a abstração do provedor de email. - MeetingRecord adquire a própria identidade do provedor de hospedagem — id de reunião remota, Juntar e hospedar URLs, código de acesso, conta de host — para que uma reunião possa ser reunida e conciliou quem o hospedar. - MeetingGravando ganhos provedorRecordingId, que mantém a atração de um remoto A gravação em nuvem do provedor é idempotente. - OrgMeetConfig ganha as configurações Zoom e Equipes suportam que um Meet-only config não tinha motivo para carregar: join-fore-host, passcode molding, lobby desvio de escopo, apresentação e controles participantes, gravação em nuvem, transcrições, convites enviados pelo fornecedor, e as reuniões de acolhimento quando um membro não tem conta no prestador. Cada org sem linha de provedor resolve para KAMO MEET, então isso é inerte até que um admin escolhe o contrário. Colunas foram aplicadas à produção por KamoInitializer Antes de isto aterrar. PlatformOAuthProviderType ganha ZOOM(9) para o aplicativo Zoom Marketplace; Equipes reuniões reutilizam MICROSOFT(3). Seu teste de guarda se move com ele, uma vez que um enum e o teste que fixa os seus IDs persistentes não pode aterrar separadamente.
· kamo-internal
Países da lista de licenças no cartão de bloqueio geográfico
O cartão só poderia negar países, então uma org que serve um país teve que enumerar Todos os outros. Agora tem ambas as listas: um modo alterna as opções que listam o país escolhido Aterra, e as duas mesas sentam-se lado a lado. Uma lista de licenças vazia permite que tudo não seja bloqueado; um não vazio nega todos os países ausente. Essa inversão é drástica o suficiente para afirmar no cartão em vez de deixar para seja descoberto, assim o aviso acima das tabelas diz qual regime está em vigor. A restrição única (org, país) mantém um país em exatamente uma lista, que faria re-adicionar para o outro falha, então cada linha carrega uma ação de movimento que o inverte via PUT Em vez disso.
· SecurityService
Países de lista de permissão ao lado de blocos geo
A seção geo só negou: um país foi bloqueado ou estava ausente. Orgulhos que quer servir um país teve que enumerar todos os outros, o que é ininterruptível e silenciosamente errado no momento em que um novo código de país aparece. org geo block rules.is blocked já existia e foi codificado true, então uma permissão regra é que a coluna set false — nenhuma mudança de esquema. Avaliação: um bloco explícito ganha, então uma lista de permissões nega tudo o que está ausente dela, e uma lista de permissões vazia permite tudo não está bloqueado. Isso mantém as orgs existentes apenas em bloco a comportarem-se exactamente como antes. PublicarGeoRules agora envia ambos os lados do conjunto de regras, uma vez que as regras de permissão são de carga uma vez que exista. Cargas de cache gravadas antes desta alteração não carregam campo bloqueado e Sobreviva ao cache TTL através de um implante, então um campo em falta é bloqueado. Também os escopos setBlocked e delete para a org do chamador. apagar uma regra previamente resolvida por UUID sozinho, então uma identificação de outro inquilino teria sido honrada.
· kamo-shared-library
Qualidade de leitura em ajustes e versões de diretrizes
Um preço montado é uma célula base PLUS os ajustes empilhados sobre ele, por isso julgar apenas a célula julga metade do número: um perfeitamente lido 99.875 com um mal lido -1.250 LLPA em cima ainda é um preço errado. MlosPriceAdjustment agora carrega a qualidade de leitura desse ajuste, para que o portão de sinal-off possa levar o elo mais fraco através do toda a assembleia. MlosGuidelineVersion obtém o mesmo campo de consistência com a coluna da folha de taxa -- já carregava fonteDocumentId, então isso completa sua procedência. Ambos nulláveis; null significa NÃO SINAL em vez de bom. Colunas aplicadas ao prod BarataDB via KamoInitializer.
· kamo-internal
Abrir links de volta para este aplicativo na aba que o membro já está
O e- mail no painel de leitura abre cada link em uma nova aba, que é ideal para o Internet inteira e errado para nós. Nossos e-mails de notificação são quase inteiramente links de volta para o aplicativo, e enviá-los para uma nova aba joga fora a sessão membro já tem e os faz sentar através de uma inicialização fria para alcançar uma página esta aba pode renderizar imediatamente. A mesma origem é o teste inteiro — bytes iguais com a página renderizando o email, não "parece um domínio Kamo" e não uma correspondência de sufixo no domínio do org. Isto é... exatamente o conjunto de URLs para os quais a sessão atual é válida, que é a totalidade razão para ficar nesta aba, e é un-spoofable pelo correio: outro anfitrião do inquilino, uma aparência, ou userinfo apontando o host em outro lugar são todas origens diferentes e Vá para uma nova aba como o link de qualquer outro estranho. Roteamento em vez de atribuir localização: um documento completo de carga derruba o React árvore e destrói todas as janelas de ferramentas abertas juntamente com qualquer rascunho não enviado, por isso este repo tem um portão de construção contra a navegação interna dura. Modificado e médio os cliques são deixados sozinhos — esse gesto é o usuário pedindo deliberadamente uma nova aba. Ouvir do pai funciona porque a caixa de areia já mantém a mesma origem para a medição de altura enquanto omitindo permit-scripts, então nada do email ele mesmo sempre corre; o manipulador é nosso, ligado de fora. Uma armadilha que vale a pena gravar, capturada pelo seu próprio teste: devolvendo um caminho em vez de um URL é necessário, mas NÃO é suficiente. "https://host//evil.test" é genuinamente a mesma origem, e o seu pathname é "//evil.test" — que o router lê como protocolo-relativo e segue fora do local. Um redirecionamento aberto e-mail que passa por uma verificação de origem. Tal ligação agora cai através de uma nova aba, onde o navegador resolve o URL real, inofensivo, em vez de ser silenciosamente reescrito para algum lugar o email não apontou.
· kamo-shared-library
Confiança de extração por célula + proveniência de matriz de elegibilidade
Duas lacunas no modelo de confiança, ambas no caminho do dinheiro. Per-cell. Um chão largo é o grão errado para julgar um preço: um único cupom mal lido arrasta a folha inteira para baixo e enviaria todos os outros preços sobre ele para um humano, enquanto um piso alto não diz nada sobre a célula específica um mutuário foi citado. **************************** registra quão bem Esta linha de taxa/preço/bloco foi lida, pelo que um cupão mal lido custa apenas o seu próprio preço. PROVENANÇA DE ELEGIBILIDADE. MlosEligibilidade Matrix não tinha caminho para um documento de origem em all -- sua única rota era uma diretriz NULLABLEVersionId, então uma matriz promovida poderia Não ser rastreado para o lençol de onde veio. Elegibilidade decide se um empréstimo é offerable, o que o torna exatamente onde uma negação tem que ser explicável; uma procedência Há uma lacuna no momento em que um mutuário é dito não. Agora carrega sourceDocumentId e o mesmo contrato de confiança de extração que a coluna da folha de taxa. Ambos nulláveis, e null significa NÃO SINAL em vez de bom. Colunas aplicadas para prod BarataDB via KamoInitializer.
· kamo-signer-monorepo
Mostrar o telefone do agente, livre de pedágio e e-mail na cópia do cliente
· kamo-internal
Mostrar o telefone do agente, sem pedágio e e- mail na antevisão
Cada um em sua própria linha: uma execução de dois números e um endereço lê como uma string e é discada erradamente.
· SecurityService
Resolver os contactos publicados pelo agente
E-mail vem da caixa de correio primária do membro em vez de TeamMember.emailWork — que coluna é texto livre predating caixa de correio provisionamento e rotineiramente discorda com o endereço que eles realmente enviam. Continua a ser o recuo quando nenhuma caixa de correio é primária. Um membro habilitado pelo VOIP com uma extensão é publicado como linha própria do org mais que extensão, oferecendo o número livre de portagens também quando a org tem um. Sem extensão, seu telefone de trabalho — o único número que eles têm que deve ser publicado. Um desactivado O VOIP config não publica nada, porque essa extensão não responde. Cada pesquisa é defensiva: falha em renderizar um documento voltado para o cliente porque uma caixa de correio a linha está faltando seria pior do que imprimir uma maneira a menos fazer contato.
· kamo-shared-library
Publicar todas as formas de chegar ao agente, não apenas um nome
Nome e título apenas dizer um cliente que possui a citação, mas não como perguntar sobre isso. O bloco agora carrega e-mail e telefone, resolvido pelo chamador — a caixa de correio primária vive no esquema do provedor de e-mail e na extensão na configuração VOIP, nenhum acessível a partir de um ajudante estático. Linha principal e livre de pedágio são campos separados para que o documento possa imprimir ambos: escolher entre eles é escolher o que a chamada custa ao cliente. lineWithExtension emparelha uma extensão com a linha org que desliga e retorna null se A metade está desaparecida. Um órfão "ext. 204" não é um número que ninguém pode chamar, e um nu A linha de org iria desviar o cliente para a central telefónica como se fosse do agente.
· kamo-shared-library
Confiança de extração na coluna do catálogo + auto sign-off base
Um preço é tão confiável quanto a folha que foi montada, e nada sobre o caminho de fixação de preços carregou esse sinal: a confiança existiu SOMENTE na pré-promoção encenando linhas (MlosExtractedCandidate) que nenhum código de produção leu. É a confiança mais fraca por campo através dos candidatos aprovados promovidos para a versão -- mínimo, nunca significa. Uma folha de 200 células limpas e uma média de 0,20 mal lida para 0,996 e iria limpar Qualquer limite, enquanto que uma célula é o que custa mal um empréstimo. Null significa NÃO SIGNAL (autorizada manualmente ou promovida antes da existência da coluna) e os consumidores devem recusar-se a aprová-lo automaticamente; ler ausente como bom é como um preço não verificado sai sem ser revisto. Protegido por WORM como as outras colunas de preços -- instantâneo em @PostLoad e diffed em @PreUpdate, ou seria um buraco silenciosamente mutável em A cadeia de auditoria. MlosPricingSignoff ganha automatizado / base confiança / liminar aplicado. Sem uma aprovação da máquina é indistinguível de um ser humano, exceto por um membro id, que no caminho do dinheiro é um defeito de auditoria. O limiar é armazenado ao lado do valor porque a configuração se move: 'a confiança foi 0,72' não pode ser julgada mais tarde sem saber se o bar naquele dia foi de 0,70 ou 0,90. Colunas aplicadas para prod BarataDB via KamoInitializer.
· kamo-internal
Página de citações — cada citação através dos leads da conta
Um DataGrid com páginas de servidor ao lado de Leads, com pesquisa sobre número e título, status, total intervalo e intervalo de data criado. Os filtros partilham um debonce e colapsam numa única consulta, ajustando um intervalo enquanto A digitação custa uma viagem de ida e volta em vez de uma por tecla por campo. Qualquer alteração de filtro resets para a página 1 — ficar na página 6 de um conjunto de resultados mais estreitos lê como uma grade vazia. Expiração é mostrada para além do status: uma cotação SENT pode ser passado sua data de validade e ainda lido como SENT, então essas linhas carregam seu próprio marcador em vez de ser silenciosamente Marcas erradas. Linhas e a coluna principal ambos roteiam para a aba de aspas do lead.
· SecurityService
GET /quotes/account/{uid} — paged, filtrado, ordenado
As datas chegam como cadeias ISO e analisam lenientemente: um filtro que o cliente envia mal deve nada mais estreito do que 400 toda a rede. Uma data-apenas "para" abrange todo o Nesse dia, de outra forma filtrando para hoje não retorna nada levantado desde meia-noite.

Seu universo está esperando.
Comece gratuitamente em minutos.Adicione produtos sempre que estiver pronto — sem migração, sem novos logins.
Gratuito para sempre — não é necessário cartão de crédito