Changelog
O que há de novo.
Cada melhoria para a plataforma, como ele envia.
· kamo-marketing
Aplicar um CSP, COOP e tipos confiáveis
Adiciona os três cabeçalhos da auditoria de Confiança e Segurança solicitados, mais Referência-Política e Permissões-Política. Todas as origens da política foram tomadas do que o site realmente carrega ao invés de adivinhar — o pacote de widget de chat em theme.kamocrm.com (que abre um WebSocket para mídia. e chama api. capcha.), o script de análise em analytics., a escada HLS do herói, *** final do desafio. Auditou a página construída contra a política final recurso por recurso: zero bloqueado. ON script-src 'insafe-inline' — este é um compromisso real e vale a pena afirmar Em vez de enterrar. A forma forte de um CSP é uma nonce per-response com 'strict-dynamic', e não está disponível aqui: a nonce significa ler cabeçalhos() durante a renderização, que opta pela rota de saída da geração estática. Este aplicativo tem ~ 200 rotas pré-renderizadas e o seu estado estático é o suporte de carga — uma rota dinâmica com nenhum limite de carregamento prefetches nada, então cada clique de link começa frio. Ali. também não é uma fuga baseada em hash, porque os scripts em linha são RSC do Next dados de voo, cujos conteúdos diferem por página e por compilação. Então: este CSP NÃO para um script inline injetado. O que ele faz é confinar cada script externo, busca, quadro, imagem e carga de mídia para origens conhecidas, proibir plugins, fixar o URI base para que a marcação injetada não possa re- root relativo URLs, e parar posts de formulário para terceiros. TIPOS CONFIADOS levaram três tentativas para tornar seguro, e as falhas são as parte interessante: 1. Uma linha simples <script> primeira no layout <head> não é a primeira. Reagir as etiquetas do próximo `<script assync src>` acima de qualquer coisa que o layout renderize, Assim, a política desembarcou após doze deles — e os scripts assincronizados executam o no momento em que acabam de descarregar. O tempo de execução do Turbopack está entre eles e faz `createElement("script")` então define `.src`, a pia exata que lança sem política. 2. `next/script` com estratégia="beforeInteractive" também não o moveu; Ainda faltam doze guiões. 3. O que torna seguro é que nenhuma pia pode disparar tão cedo. Todos os 78 dados de voo os impulsos estão no corpo — primeiro no deslocamento 193.072, com </cabeça> em 11.940 – por isso o React não pode começar a hidratar até bem depois da política ser instalada, e não há código de toque em <head> antes dele. Verificado em built HTML em vez de assumir. Duas coisas teriam partido sem ser grepeados. O elemento *** é um Svelte construir e cria uma política chamada `svelte-trusted-html'; um nome de política é um string runtime, então nada sobre ele aparece em tipos, lint ou na compilação, e omiti-lo da lista de permissões teria jogado e tomado o contato e demo formulários para baixo. E hls.js e o *** ambos constroem Web Workers de createObjectURL, então a política tem que passar `blob:` antes de seu teste de origem — a blob: URL não tem .origin útil e de outra forma teria falhado, tomando o O herói rebobina com ele. O createScriptURL da política é verificado contra o kamocrm.com, que é estritamente mais permissivo do que script-src, para que nunca possa recusar algo que o CSP permite. createHTML passa e NÃO é higienizado: um verdadeiro sanitiser seria tem que enviar no caminho crítico e poderia silenciosamente mutilar os blocos JSON-LD e a marcação do widget. Essa metade é um ponto de estrangulamento e um gancho de auditoria, não um filtro — e é onde endurecer a seguir. A política Cross-Origin-Resource foi escrita e depois removida. Aplicado entre `/:path*` cobre as imagens do Open Graph, que existem especificamente para ser embutido de outras origens. frame-ancestors é 'eu' para corresponder às opções X-Frame: SAMEORIGIN já sendo servido, em vez de apertar para "ninguém" e quebrar uma incorporação que ninguém lembrou. Verificado contra o servidor standalone real, não apenas os cabeçalhos config: presentes na rota pré-renderizada e na rota ***, e no activo de longa duração O Cache-Control ainda está intacto.
· kamo-internal
Criar códigos de rastreamento no lugar do substituto de referência
/comercialização/descontos foi um stub ComingSoon; é agora /comercialização/rastreamento, a espaço de trabalho, com o caminho antigo mantido como um cliente redirecionar tão existente Os favoritos ainda pousam. A página lê "Tracking Codes". Um membro menta um código e a página mostra-lhes o link que produz, construído a partir de o anfitrião realmente servindo a página — internal.acme.com produz ********************** domínio branco sem um ápice sendo codificado em qualquer lugar. A grade carrega o seis totais por código, e uma linha abre uma gaveta com o funil de conversão, o últimos trinta dias, locais de topo, dispositivos e referências, e os visitantes eles mesmos; um visitante abre seu próprio painel com origem, rede, cliente, UTM atribuição e uma linha do tempo de cada página que eles abriram. Utilização de bandeiras de país reacção-país-banco, como em qualquer outro lugar. Escopo segue os direitos: "Minha equipe" aparece para ambos os direitos cross-member, "Tudo membros" somente para VER ALL MEMBER TRACKING, e o seletor segue o escopo o SERVER diz que se aplica em vez do pedido, por isso uma deixar o membro lendo suas próprias linhas sob um rótulo "Todos os membros". Pinturas cromadas de --color-primário e --color-secundário em todo org é branco- label e o botão por omissão do MUI iria renderizar o mesmo azul para todos eles. Duas coisas que a rede precisava não são óbvias. Os cliques das linhas são filtrados em um atributo de marcador explícito em vez de pararPropagation, porque AG Grid escuta nativamente no recipiente da linha enquanto React delegados para a raiz — o React o manipulador corre em segundo lugar, então o botão de cópia e o menu de linha ambos teriam aberto A gaveta. E a pesquisa é apenas do lado do servidor: um segundo filtro rápido do lado do cliente poderia apenas subtrair de um conjunto de resultados corretos, escondendo linhas que combinassem no membro nome em um escopo sem coluna de membro. VER REFERRAL CODES e GESTÃO REFERRAL CODES deixar a lista não forçada — eles gate coisas reais agora — através de uma auditoria regenerada em vez de uma edição manual. O TS Os espelhos ganham os dois novos direitos e o tipo de serviço de comercialização em que apontam, que tinham desaparecido.
· kamo-register
Transportar a referência de comercialização para a inscrição
O site de marketing etiqueta um visitante referido com kmrt vid e kmrt src no apex compartilhado, então registre-se.<apex> pode lê-los; handleFinish envia ambos no pedido corpo, caindo para ?mrt= para o código quando o visitante chega com o link ainda na barra de endereços. Corpo, não cabeçalhos: o proxy /api tira Cookie e a entrada reescreve X-Forwarded-Host, por isso também os cookies não são httpOnly. Valores ausentes são omitidos em vez de enviados como strings vazias, uma vez que a maioria das inscrições não tem nenhum. O leitor de cookies que a página de logout tinha em linha agora é app/lib/cookies.ts e compartilhado por ambos os ouvintes, em vez de um segundo analisador sendo escrito ao lado dele.
· kamo-marketing
Honrar um link de parceiro e relatar o que trouxe
Um visitante que segue https://www.<apex>?mrt=<code> recebe dois cookies de primeira parte do middleware de borda — um GUID opaco que dura dois anos e o código Durante noventa dias — e cada página que abrirem depois é reportada uma vez, de Ocioso, por enviar Beacon. O domínio é derivado dos cabeçalhos encaminhados, não do nextUrl. Compilações seguintes URL do middleware a partir de seu próprio endereço de escuta, então nextUrl.hostname é o recipiente e o atributo Domain silenciosamente nunca apareceu: um cookie somente para host não é possível ler, o que perde a referência com Nada falha em lado nenhum. o cookie-consent apaga através do mesmo helper, assim o dois não podem voltar a separar-se. Um visitante que nunca seguiu um link parceiro nunca recebe um cookie e nunca Não há sinais. Isso, em vez de um banner alternar, é o que mantém isto proporcional: o registro por trás dos totais de um parceiro é o que esse parceiro é pago contra, e uma porta de consentimento não tornaria os números menores, faria Eles errados — as visitas ainda acontecem e as inscrições ainda se convertem, eles apenas deixar de ser atribuível. Ambos os cookies são divulgados no Essential on the Cookie Política com um controle que os desobstrui. Nossa própria análise de produtos permanece fechada. /api/mrt é um limite de confiança, não um pipe: ele liga a chave API deste site, então ele valida o GUID, o código e o URL reportado, reconstrui a carga útil de valores que possui, recusa um chamador cross-site, e encaminha apenas o conjunto de entrada IP peer — a cadeia X-Forwarded-For é controlada pelo cliente na sua extremidade esquerda e de outra forma deixaria um chamador escolher o IP os registros de relatório da comissão. Ele responde 204 antes de encaminhar, e avisa no máximo uma vez por minuto quando a montante é inacessível, porque a forma anterior tratou um 404 exatamente como sucesso. A renderização estática não é tocada: nenhum cookies() ou headers() em qualquer página, nenhum Script Tag, sem framer-motion no farol. check-mrt-cookie.ts prende as invariantes que não ter nenhum outro guarda — incluindo que o farol permanece recolhidos de um visitante não referido.
· SecurityService
Publicar inscrição e org-criado para visitantes rastreados
O site de registro agora encaminha o visitante de marketing GUID e código de rastreamento em o corpo de solicitação — não um cabeçalho, porque o proxy tira Cookie e a entrada reescreve X-Forwarded-Host — e register() passa- os para um sinal de inscrição quando Eles estão presentes. A maioria das inscrições não carregam nenhum e não publicam nada. MarketingSignalPublisher serializa com um objeto LOCALMapper, deliberadamente não O feijão partilhado. Esse feijão também escreve o mapa de sessão Redis *** e 65 chamada sites lêem IDs de volta como Número; dando-lhe um Long-> global Regra de texto transforma a resolução de org num ClassCastException que os ajudantes engolem e nula org é um 401 em cada pedido autenticado em quatro serviços. A editor é o melhor esforço, mantém uma conexão NATS opcional, e nunca lança. Ambos os sinais são registrados apósCommit com a carga útil instantâneo dentro do transação, então o consumidor não pode procurar uma linha de usuário ou organização que tenha Ainda não foi cometido. RoleRightsSyncSeedTest ganha um caso negativo: nenhum dos dois novos rastreamentos direitos podem sempre entrar NEW RIGHT SEEDS, uma vez que semeá-los iria conceder cada visibilidade existente do titular do código de referência na análise de nível de visitante dos colegas no arranque. As identificações estão em 200 e 201.
· BillingService
Publicar a conversão paga para organizações rastreadas
Ambos os caminhos que selo BillingSourceType.PAID agora publicar marketing.visitor.pay, porque gancho apenas um silenciosamente perde metade do tráfego — AccountSubscription Serviço gerencia assinatura própria do console, ConsumerCheckoutService the Stripe checkout. Três coisas que isto acerta de propósito. Publica sobre a TRANSIÇÃO, leitura a fonte anterior antes da gravação e disparo apenas quando já não estava PAGAR, então uma mudança de plano ou um "Plano de Mudança" reposicionado não pode inflar o contador. Ele instantâneo em um registro imutável dentro da transação, porque conta e targetOrganização são ambos preguiçosos e lê-los de afterCommit lances. E... a org informa é a organização TARGET, nunca do ordenante — um pai conta paga rotineiramente para uma assinatura visando uma criança org, eo funil conta a org que converteu, não a que está com o cartão. Registrado através de ************* em vez de publicado inline, que também recebe registroPaidFromCheckout right: its @Transactional joins a transação externa do webhook do Stripe, então apenas um gancho apósCommit dispara no limite real do commit.
· APIService
Encaminhar o localizador para MediaService
Clones forwardPublicWebinar em vez do genérico forward(): esse método imprime cada URI, mapa de cabeçalho e comprimento do corpo para stdout sem nível, e um visitor beacon carrega um IP e um referenciador. A chave API aqui é um limite de taxa e controle de abuso, não um limite de arrendamento. Uma o pod de marketing serve todos os hosts www.* com uma chave pública, então o org da chave é sempre o org de plataforma — a organização própria do código de rastreamentoId é o que decide Concessão, a jusante na MediaService. Esse raciocínio está em um comentário na chamada site para que ele não fique "fixado" em uma verificação org quebrado mais tarde.
· MediaService
Servir a guia de códigos de rastreamento e ingerir o farol de visitante
MediaService já apoia as outras abas de marketing, por isso leva ambas as metades de códigos de localização: o CRUD autenticado e a superfície de estatísticas em /api/media/marketing/tracking, e o público ingere trabalhador por trás do APIService Borda. O manipulador de ingestão não faz nada além de validar e enquear. Tudo real — o pesquisa de código, a pesquisa geo, a análise usuário-agente, os quatro escreve — acontece em um executor limitado fora da linha de solicitação, e a resposta é um 202 com um vazio corpo, independentemente do resultado, porque uma página de marketing nunca deve esperar na análise e um ouvinte público nunca deve saber o que correu mal. A política de rejeição é AbortarPolítica, não ChamarExunsPolítica: chamadas-correntes iria empurrar o trabalho da base de dados de volta na linha HTTP, que é a coisa exata que esta forma existe para prevenir. A A fila cheia deixa cair o sucesso e conta-o. Cada string fornecida pelo cliente é cortada para a sua largura de coluna antes de estar persistir. Hibernate não impõe @Column(comprimento) em tempo de execução, então em um público endpoint um Referer de sobre-comprimento ou Aceitar-Língua é SQLSTATE 22001 e toma todo o farol para baixo com ele — trivialmente accionável por qualquer um. Uma visita é nova quando a linha de ligação foi vista pela última vez há mais de 30 minutos, combinando a própria janela da analytics install para que os dois nunca discordem sobre "visitas". Bots são analisados, largados, e contados contra nada. Escopo é resolvido lado servidor e re-validado: "gerido" necessidades cross-member direita e sempre inclui o chamador, "todas" necessidades VEZ ALL MEMBER TRACKING e leituras por organização para que nenhuma cláusula IN possa ser entregou uma colecção vazia. Um escopo não autorizado diminui para "próprio" em vez de erro, e a resposta ecoa o escopo realmente aplicado para que o cliente possa mover seu seletor em vez de mentir sobre o que o membro está olhando. As respostas são registros com IDs de String e contadores numéricos. MediaService não tem longo-> Regra de cordas, assim que um longo cru em um mapa iria enviar um número JS-lossy sem nada para pegá-lo; RastreamentoCodeDtoWire Testa as duas metades disso. O filtro durável do consumidor de conversão é marketing.visitor.* e é fixo para sempre — uma durável mantém o filtro com que foi criado, e alargando-o mais tarde produz um SUB-90011 permanente sobre um consumidor que ainda parece saudável. Ele bate um apenas quando o carimbo condicional realmente afetou uma linha. Uma noite reconciliar recomputa todos os seis contadores das linhas que este serviço possui, por isso um O incremento cura-se.
· kamo-shared-library
Modelo de códigos de rastreamento, seus visitantes e as visitas que eles trazem
Um membro tem um código, compartilha... os seis totais na aba Códigos de Rastreamento respondem ao que esse link realmente fez: visitantes únicos, visitas, pedidos de página, inscrições, organizações criadas e pagas contas. Quatro mesas têm. COMERCIALIZAÇÃO CODES armazena a própria string de código em vez de recomputá-lo de memberId e iteração, então o público ingere caminho é uma sonda de índice único sem análise e um malformado ?mrt= é uma falha em vez de um NumberFormatException. COMERCIALIZAÇÃO VISITORES é a coluna da identidade, uma linha por GUID cookie, e seu código atribuído é escrito uma vez e nunca se moveu: com atribuição de primeiro toque os três contadores de conversão não podem visitante que mais tarde chega em um segundo código. COMERCIALIZAÇÃO VISITOR CODES é o visitante x ligação de código e a autoridade para os contadores de tráfego, e MARKETING VISIT EVENTS é a linha do tempo somente do anexo que a gaveta do visitante lê. Os contadores são mantidos, não derivados, porque a grade renderiza seis deles por linha e seis COUNT(DISTINCT) agregados sobre uma tabela apenas de apêndices em uma linha o store é uma verificação por carga de página. O que torna isso seguro é que cada solavanco é um SET atômico x = x + 1 em vez de um read-modify-write, e cada selo de conversão é um ATUALIZAÇÃO condicional fechado em IS NULL assim que uma atualização de mensagem NATS redistribuída zero linhas — a contagem de linhas é o dedupe. Ambos os testes "primeira vez visto" são nativos INSERT ... NO CONFLICT NÃO FAÇA NADA para o Pela mesma razão. A alternativa óbvia, capturando a violação única e re-leitura, não pode funcionar aqui: no YugabyteDB a violação aborta o todo transaction (25P02) e Spring marca apenas o retrocesso, então uma corrida perdida cairia todo o farol — a linha do evento, a linha do visitante e cada contador com ele. Direitos: 154/155 manter seus nomes constantes e são apenas re-labeled para "View" e "Gerenciar códigos de rastreamento", porque os direitos viajam pelo nome no token de sessão e um renomeá-los-ia de cada sessão ao vivo sem erro de compilação. Ids 200 e 201 são novos e portão de visibilidade transversal. Nem é semeada em NEW RIGHT SEEDS: antes deste recurso ninguém poderia ver o visitante de outro membro analytics em tudo, assim semeando de View REFERRAL CODES iria entregar cada existente ter uma capacidade que nunca tiveram.
· KlusterServices
PROVISÃO DE MERCADO EVENTOS para sinais de conversão de visitantes
Os códigos de rastreamento de marketing creditam um parceiro para as inscrições, organizações e contas pagas seu link produzido, e esses três momentos acontecem nos serviços que não possuem as tabelas de rastreamento: SecurityService publica marketing.visitor.signup e marketing.visitor.org-criado, Billing Serviço publica marketing.visitor.payd, e MediaService consome todos os três. Nada cria este fluxo de aplicação — os produtores apenas publicam — portanto tem que existir aqui antes de implantar, ou as primeiras conversões são publicadas em nada e os balcões silenciosamente permanecem em zero. R1 como qualquer outro fluxo neste servidor de nó único.
· kamo-internal
Encurtar a dica de descrição vazia
A coluna Descrição mostra {Sem Descrição} no lugar de "Sem Descrição .. A string foi codificada em inglês, por isso agora vem do dicionário — com as chaves aplicadas no componente, desde uma mensagem carregá-los seria analisado como um substituto da UTI.
· kamo-internal
Reordenar e renomear as páginas do documento
A faixa agora lê Originals (n) Envia, e Common troca o ícone de engrenagem para uma biblioteca agora que não Mais tempo diz "Sistema". Cada aba fixa um 'valor' explícito para que a ordem visual é livre de mudar sem re numerar o ativoTab verifica que o resto de o arquivo é construído sobre. Partilhado comigo, História, Doutores Escondidos e Lixo deixar suas etiquetas para tooltips — O histórico sai da tira principal para se juntar a eles, que também corrige seu efeito de busca: ele foi fechado na aba 5 (Uploads) enquanto o painel renderiza no 6, então as listas de histórico nunca foram carregadas.
· kamo-internal
Reconstruir a base de conhecimento como um console
As quatro telas de base de conhecimento foram as últimas no aplicativo ainda usando o antigo e três deles declararam coisas que não eram verdade. /kb mostrou "Artigos Pinados" de um endpoint que enviou linhas de pinos com o artigo retirado por @JsonIgnore — a seção nunca apareceu Alguém. Abaixo dele, "Artigos Recentes" foi os primeiros dez nós do artigo árvore, em ordem de árvore, cada estampado PUBLICADO com uma data em branco: nem recente nem necessariamente publicado. A árvore ao lado deles desabou em cada navegação, porque cada ramo manteve seu próprio usoEstado. Uma ideia carrega a reconstrução: O RAIL É O FILTRO. Contagem de azulejos e lista que abre ambos vêm de partidasFilter no modelo, então uma leitura de azulejo 4 abre exactamente aqueles 4. Não há uma segunda fila de fichas a refazer o mesmo predica em palavras diferentes, esperando discordar. Em torno dele: procurar que corresponde ao título, endereço e corpo e marcas em que bateu (a caixa antiga debounced 500ms para perguntar a um servidor que pesquisou apenas conteúdoPlainText, assim digitando o título exato de um artigo não devolveu nada), uma árvore que se lembra do que você aberto e pode ser redimensionado, faces em cada cartão do diretório membro, e um painel de Necessidades de Atenção — revisões que esperaram, rascunhos que ficaram quietos, artigos publicados ninguém tocou em um quarto. /kb/resources foi uma janela modal renderizada plana em uma rota, com open={ true} e um onClose que navegou. É uma biblioteca agora: um trilho que pesa o que a organização está armazenando e duplica como o filtro tipo, telhas com rostos reais para vídeo e arquivos em vez de uma grade de caixas cinzentas idênticas, drop- anywhere upload com o progresso real por arquivo (a busca não pode relatar progresso, então um vídeo de 40MB parecia exatamente como um ícone 4KB), e renomeando lugar em vez de um modal lançado de um rodapé. A mídia de inserção do editor O seletor é agora as mesmas peças, o mesmo painel de detalhes e o mesmo usoKbMedia gancho em um diálogo — um kit, duas superfícies, de modo que eles não podem derivar. /kb/reviews era uma lista de títulos. Um revisor podia ver que quatro coisas eram espera e nada mais — não quem os escreveu, não quanto tempo eles tinham sentado ali, não o que disseram — e decidir significava abrir cada artigo, rolar para um painel no fundo, e voltando. Pior de tudo uma busca falhada renderizado como "Sem artigos pendentes de revisão": a fila se relatou limpa em um dia pode não ter sido. Agora a fila carrega a idade de cada artigo, submissor e tamanho; o artigo fica ao lado dele; e os botões de veredicto estão ao lado do que Aplicam-se. /kb/new era um formulário que não dizia nada a um autor. O campo de lesmas disse "Auto-gerado a partir do título" e, em seguida, deixe-o salvar uma lesma que o servidor rejeita com um 400 depois que o artigo é escrito. As opções suspensas pai com dois espaços, que o HTML colapsa, então cada opção parecia de nível superior. E... todas as três caixas de visibilidade poderiam ser marcadas com treinamento de IA paradas, que Significa silenciosamente que ninguém o vê em lado nenhum. O compositor responde a esses três perguntas antes do trabalho em vez de depois da rejeição, e oferece Salvar rascunho ao lado Criar e enviar. Fixos ao longo do caminho, todos visíveis: - KbStatusBadge enviou quatro palavras em inglês codificadas e quatro hex pares – um console espanhol disse "Draft" em uma paleta cinza sem rótulo branco Podia mover-se. - KbPinButton capturado é Pinned uma vez, então uma página que aprende o seu estado pin após a primeira renderização (cada página aqui) mostrou um pino de contorno sobre uma Artigo preso para sempre; uma requisição falhada deixou o conjunto de procura do pino. - O leitor de artigos leu o artigo. preso, um campo que o KbArticle não tem. - mediaId foi declarado número entre os quatro tipos de nós editor e extrairMediaIdsFromJson, enquanto KB MEDIA.UID é um UUID e os valores têm Sempre foram cordas. Só sobreviveu porque nada fez aritmética. em um; um único Number(mediaId) teria sido um NaN silencioso. - KbMediaResource.uid tinha a mesma mentira. - Pedidos de mídia correram: uma resposta lenta de busca precoce poderia pousar após um rápido mais tarde um e repintar a grade para uma consulta que ninguém estava olhando. As regras puras vivem em app/lib/kbConsole.ts e app/lib/kbMedia.ts com 52 testes, porque aqui o vitest coleta somente .test.ts — uma regra escrita dentro de um .tsx é uma regra sem teste. O que eles codificam: um desconhecido nunca é um zero. Um tipo de data ausente dura em vez de se tornar a época, um status não reconhecido pinta neutro em vez de verde, uma fila vazia não tem item mais antigo do que um zero-dia, e um recurso cujo uso nunca conta chegou alertas em vez de prometer que não é usado.
· SecurityService
Enforce is fake, e feche os furos que permitem uma inscrição sem acompanhamento
Duas metades de um incidente. Uma conta registada, verificada num endereço descartável provedor, tomou uma sessão de auto-login e criou cinco organizações em três minutos e quarenta e dois segundos — quatro deles byte-identical "Acme Corp" linhas que diferem apenas em seus domínio. Ele produziu linhas ZERO em system access logs, então não há IP e nenhum usuário-agente Está registado. A fazer cumprir a bandeira. - Autenticação do Utilizador Serviço: `u.is fake = FALSE` entra em Um ouvinte. Todos os três identificadores de login – nome de usuário, e-mail de conta e caixa de correio principal re-searched by id — reutilizar essa projeção, então uma cláusula fecha todos os três e um quarto identificador adicionado mais tarde herda-o. Uma conta marcada é considerada inexistente e obtém o genérico "usuário não existe ou senha está incorreta"; uma mensagem que nomeia o A bandeira diria a um abusador exatamente o que mudar. - KSessionService.createSession: recusa-se totalmente. Esta é a cintura mais estreita no sistema para "que esta conta actue" — cada *** é cunhada aqui, por isso uma capa de portão login de senha, enter-as, SSO desktop, autenticação do dispositivo E o funil de registro auto-login pós-verificação, que é como a conta em questão tem uma sessão sem nunca chamar /login. Joga em vez de voltar nulo, porque trinta chamadas assumir um id não null e NPE em outro lugar inteiramente; login e a sessão automática caminho pegá-lo e moldar uma resposta limpa. - FakeAccountGuard: dois níveis de propósito. A conta única é UNCACHED (ele volta os portões rígidos, onde uma resposta defasada significa uma conta marcada ainda entra); o id define são guardados em cache por 60s (eles de volta ler filtragem, onde o pior caso é uma org sintética permanência em uma lista por menos de um minuto). Falha aberta — esconder um punhado de linhas não é vale 500 uma lista de orgs — e NÃO cache uma falha, então um blip não se torna Um minuto da bandeira silenciosamente não funciona. - OrgObjectStorageSweep: pára de fotografar orgs sintéticos. Era aqui que estava o dinheiro. instantâneos são escritos por org por dia para sempre, e cinco orgs abandonados já tinham tornar-se ~9% dessa tabela. Uma conta que não pode entrar não faz nada sobre o trabalho plataforma continua fazendo em seu nome. - FakeAccountController: flag, unflag, list — por trás MANAGEME ORGANIZAÇÕES em vez de um nova plataforma direita, porque plataforma kamo-internal do teste RightCoverage assevera o direito ajuste contra as superfícies renderizadas da consola. Revoga sessões ao vivo sobre sinalização, desde o gates estão em MINTING uma sessão, não em usar uma; recusa-se a marcar o Usuário do Sistema, que tornaria a plataforma incapaz de entrar em qualquer org infantil. Os buracos... - O registo nunca lê '***Token'. A interface de registro sempre resolveu um Capcha desafio e postou o resultado; grep para o campo em todo o Java não retornou nada. O widget estava no navegador e o endpoint estava aberto. Agora verificado, e falhar FECHADO — assimétrico com login de propósito: o login só pode verificar uma carga útil quando se está presente, porque o cliente móvel não envia nenhum e rígido-requirendo-lo lá iria bloquear usuários reais fora. O registro tem exatamente um cliente e já envia o token. - CapchaVerificationService retornou TRUE quando `capcha.service-url` foi desativada, então uma configuração a omissão era indistinguível de um desafio resolvido. Agora rejeita, para trás `capcha.allow-unconfigured` (padrão false) para corridas locais. Nenhuma alteração da produção — a propriedade está definida em kamowssecurity-config — mas o bypass latente desapareceu. - POST /org não tinha limite de taxa de qualquer tipo. Agora com o título por CONTA (não por IP — uma chave IP puniria todos por trás de uma empresa NAT). Deliberadamente generoso: um cliente real criou três orgs em cinco minutos e meio, dois com o mesmo nome e apelido, enquanto A trabalhar no feiticeiro. Um limite apertado os teria bloqueado. Conta tentativas, não sucessos, e falhas abertas em uma falha Redis. - O alias org não tinha verificação de singularidade neste caminho — apenas o domínio fez, razão pela qual quatro orgs ao vivo compartilham "acme-corp" e por que o criador variou apenas o domínio de cada vez: era o único campo que o servidor rejeitaria. Agora um 409, escopo para o provedor de segurança porque `(security provider id, alias)` é o login par resolve um host web-alias ligado. - REGISTO/REGISTRAÇÃO REJECTO / EMAIL VERIFIADO / ORG CREADO IP e usuário-agente. Todas as chaves de regras de detecção fora de eventos de login, então todo o registro O funil sentou-se fora do campo de visão do motor de detecção. Deliberadamente NÃO feito, tendo verificado: login não é feito ***-strict (quebra o celular aplicativo, precisa de uma liberação coordenada); domínios de e-mail descartáveis não são bloqueados (um real o cliente assina de proton.me, e uma regra ingênua "não um provedor mainstream" bloqueia real Não há auto-bloqueamento de conteúdo (o sinal comportamental mais forte) nomes de org duplicados criados a minutos de distância — corresponde exatamente a um cliente real). Pré-requisito do esquema já satisfeito: KamoInitializer applicated users.is fake before the compartilhamento-lib push, verificado como `boolean NOT NULL DEFAULT false` com seu índice parcial, e Re-ran idempotently sem limpar a bandeira.
· KBService
Servir o que o console de conhecimento lê, e corrigir o que nunca poderia
Quatro endpoints as necessidades da base de conhecimento reconstruída, e três fatos a API foi incapaz de declarar. GET /api/kb/pins devolveu uma Página de ROWS pin, e KbArticlePin.article is @JsonIgnore — então a resposta carregava um pin id, um member id e uma data, e nada sobre que artigo tinha sido preso. A página inicial pediu isso. lista desde que ele enviado e renderizado nada, porque um envelope de Página não tem .comprimento e o guarda na frente da seção foi uma verificação de comprimento. Pinning tem Nunca trabalhou de ponta a ponta. Agora retorna os próprios artigos, resolvidos dentro de uma transação em vez de se apoiar em open-in-view. GET /api/kb/articles/overview é novo: status conta, cada artigo é real datas, autoria, contagem de palavras e excerto, além dos guids do chamador, em Uma leitura. A página inicial usada para fingir "recente" tomando os primeiros dez nós de A árvore e a estampagem com uma data em branco. GET /api/kb/reviews/queue é novo: o que está esperando e o que foi recentemente As duas metades num pedido. Um revisor olhando para uma fila vazia pergunta a mesma pergunta de qualquer forma — não é nada esperando, ou acabei de esclarecê-la. Sua leitura decidida é uma LISTA com limite, não uma Página: dentro do somente leitura transação uma página descartada COUNT é nunca instrução # 1, então Yugabyte não pode reinicie-o e uma gravação simultânea transforma a fila em 4001. GET /api/kb/media agora envia o artigo de cada linhaCount e, para os tipos uma grade desenha, sua URL pré-assinada. O conde só foi povoado pelo Endpoint de um único recurso, então o aviso "utilizado em artigos N" da biblioteca antes uma exclusão nunca poderia disparar — um diagrama incorporado em nove artigos excluídos como Em silêncio como órfão. As URLs foram uma solicitação por miniatura após a lista já tinha aterrado. POST /api/kb/media/urls assina um lote para chamadas que Esperem aí. Cada ID de membro int64 sai como um STRING. Eles são 19 dígitos unique rowid() valores e um número JSON é um IEEE duplo no navegador — o ID do remetente na fila de revisão chegou pré-rounded, por isso nunca poderia ser corresponde ao diretório membro e um id raw onde um nome pertence. Nenhuma mudança bibliotecária compartilhada, então nenhum KamoInitializer funciona e nenhuma realocação da frota.
· InitializerService
Adicionar users.is fake, na única ordem de instrução que uma tabela povoada permite
Espelha o novo campo `User.isFake`. Quatro declarações, e a ordem é o todo migração: YugabyteDB rejeita `ADD COLUNN ... NOT NULL` em uma tabela povoada — rejeitando toda a declaração, não apenas a cláusula ofensiva — por isso a coluna chega nula, é recheado, ganha seu DEFAULT, e só então é apertado. Colapso estes em um ALTER produz uma migração que registra um aviso e deixa a coluna ausente, que é saída em forma de sucesso para um total não-op. `@Order(0)` por convenção de adição de coluna. O momento em que a entidade de compartilhamento-lib mapeia `is fake`, cada corredor posterior que carrega um `User` através do JPA emite-o em seu SELECT, assim uma migração tardia nunca seria alcançada — um corredor anterior morre em `colunn u1 0.is fake não existe` e leva a corrida para baixo primeiro. O índice é parcial (`WHERE is fake = TRUE`). A pergunta do guarda é "que identidades são sinalizado", e em uma tabela onde efetivamente cada linha é FALSE um índice completo na coluna é peso morto, enquanto uma parcial contém um punhado de entradas. O backfill's `WHERE is fake IS NULL` é suporte de carga, não decoração: KamoInitializer re-runs na íntegra cada vez, e um desnudo `UPDATE users SET is fake = FALSE` seria silenciosamente desempacotar todas as contas que um operador tinha deliberadamente escondido, em cada execução. Um teste afirma Essa cláusula ainda existe. Aplicado à produção 2026-08-19; todas as cinco declarações registradas aplicadas, SUCESSO CONSTRUTO. Coluna verificada como `boolean NOT NULL DEFAULT false` com o índice parcial presente.
· kamo-shared-library
Adicionar is fake, a bandeira que torna uma conta invisível em vez de excluída
Uma conta sintética — um artefacto de teste, um dispositivo semeado, ou um registo considerado automatizado — agora carrega `users.is fake`, e a plataforma para de reconhecê-lo. Excluir tal conta não é uma opção real: uma linha de usuário é a raiz de sete mesas de satélite, cada membro que possui e cada organização que criou, que em virar domínios próprios, recursos, modelos de email, papéis e instantâneos de armazenamento. Apagar quer dizer em cascata através de tudo isso ou deixando órfãos, e destrói as provas De tudo o que a conta fez. Um booleano é reversível e custa uma coluna. A bandeira vive no USUÁRIO, não no Membro ou na Organização, porque a propriedade é a única borda que de forma confiável atinge os dados derivados: `orgs.user id owner` e `members.user id` ambos apontam para trás aqui, então uma bandeira responde "é o originador desta linha sintético" para os três espinhos. Marcar o org em vez disso deixaria a conta capaz para entrar e criar mais. O que a bandeira alcança, e porque cada um está aqui em vez de no chamador: - `UserRepository.findSession` e a projeção de autenticação: a conta marcada resolve para zero linhas, então ele lê como inexistente em vez de como bloqueado. - `MemberRepository` / `TeamMemberRepo`: excluído de listas de membros de org e de ambas as pesquisas de diretório, ao lado do usuário do sistema que já foi filtrado lá. - Está bem. a enumeração ponto de estrangulamento nove chamada Os sites partilham. `getOrganizaçãoPor Id ` não é deliberadamente filtrado — ele volta escreve e é assim que a superfície do administrador atinge uma org marcada para desempacotá-la. - Está bem. uma org marcada pára de resolver pela máquina, por isso não pode continuar a aceitar inscrições reais sob o seu próprio subdomínio de registo. - Está filtrado na consulta, em vez de na observador, porque a varredura leva um lote fixo a cada minuto e um domínio que ninguém possui Caso contrário, manteria um desses slots para sempre, sondando o DNS de outra pessoa. Cada um destes utiliza um nome explícito para a associação esquerda. Um `o.downer.isFake` caminho dentro uma cláusula ONDE faz Hibernate adicionar uma segunda, implícita ligação INNER, que iria silenciosamente soltar cada linha cujo proprietário é nulo — e para `findByDomain` que é a resolução de login. Os localizadores acima são aditivos; os localizadores de identidade no UserRepository são deixados sem filtro de propósito. A maioria deles são suporte de carga para as verificações prévias de singularidade também quanto às pesquisas, e `USERS.EMAIL`/`ALIAS' são únicos por provedor de segurança — assim um registro duplicado-check que não poderia ver uma linha marcada iria navegar além de sua própria Guarda e morre com uma violação de restrição. Além disso, acrescenta REGISTO, REGISTO REJECTO, EMAIL VERIFIADO e ORG CREADO a AccessLogEventType. `system access logs` só realizou login, login e logout falhou, assim, uma conta que registrou, verificou, tomou uma sessão de auto-login e criou cinco organizações produziram zero linhas — nenhum evento, nenhum IP, nenhum usuário-agente — e cada detecção chaves de regra fora de eventos de login, deixando todo o funil de registro não observável. `users.is fake` já existe na produção: UserIsFakeMigration do KamoInitializer foi executado antes deste empurrão, assim nenhum serviço pode construir contra um esquema não migrado.
· kamo-internal
Reconstruir /hr/attendance como um console de folha de pagamento
Oito abas planas sobre oito painéis que cada um desenhou o seu próprio rumo, mesa, esqueleto, linha de erro e Snackbar — e tinha à deriva. A revisão é três coisas no nível da página e uma reconstrução de cada painel em cima delas. **Um resumo lido** suporta uma nova barra de comandos e cada emblema de trilho. Ele responde à pergunta que a tela não poderia: se algo está entre Aqui e folha de pagamento. Isso costumava levar três cliques — um administrador abriu o roster sem forma de saber que doze excepções de bloqueio recusariam Todas as aprovações que estavam prestes a fazer na próxima página. Bloqueamento e à espera de chips de ligação profunda ao painel que os possui. A votação está encerrada. visibilidade da página, então uma aba deixada aberta durante a noite pára de perguntar. ** Um anfitrião de torradas**, içada acima do interruptor do painel. Uma mensagem enviada por um ação que navega – "limpar a exceção de bloqueio primeiro", que envia o leitor para outro sub-tab — usado para desmontar com seu próprio painel antes Alguém o leu. ** Um mapa de tom** (attendenceConsole.ts, unit-tested) substitui três que já discordou, e carrega a regra que este módulo liga: um desconhecido é Nunca um zero. Número de homologação retido, número de minutos líquidos ausentes e período ninguém gerou todos alcançar o olho como "não nos disseram", porque renderizando qualquer um deles como 0 diz a um administrador de RH uma fila que eles não podem ver é Afastem-se. `railBadgeFor`, portanto, nunca emite um crachá zero, e `consoleVerdict` é 'desconhecido' antes da primeira leitura em vez de 'claro'. Duas lacunas fechadas que não eram cosméticas: - Uma pessoa é uma pessoa. Cinco painéis impressos com 19 dígitos brutos `unique rowid()` nas linhas decidindo se deve pagar alguém. «PersonCell» mostra o rosto e o nome, com o ID na dica; os IDs ficam no final do STRINGS para acabar e nada analisa um. `useMemberDisplayNames` agora armazena tudo pasta linha em vez do nome sozinho, então a mesma caminhada alimenta avatars e títulos de emprego em vez de precisar de um segundo. - Uma perfuração que nunca existiu. A página do Timecard de membro em / configurações é auto-somente por construção — os seus endpoints não tomam nenhum ID de membro — assim o A única rota de "o horário desta pessoa está errado" para "aqui está o soco" foi a fila de exceções, que lista o dia e não o deslocamento em torno dele. `MemberTimecardDrawer` abre o editor de período existente em outro membro; esses endpoints tomar um `subjectMemberId` e SecurityService atributos o ajuste ao ator de RH, que é o que faz a reutilização correta. Por painel: a lista ganha pesquisa, ordenar, uma carta e uma vista de tabela, a intervalo de período atual e CSV (cada valor da string do próprio servidor — um arquivo a folha de pagamento concilia contra não deve carregar um número este aplicativo inventado). As aprovações tornam-se triagem de teclado com placas claras separadas de bloqueadas e um "aprovar tudo pronto" que ainda anda na máquina do estado uma carta de cada vez e nomeia cada recusa. Excepções ganham um tipo histograma que duplica como filtro e, mais importante, um ** ação: a tela enviada oferecendo apenas Re-detect e Waive, nenhum dos que coloca um soco perdido de volta, então o caminho da menor resistência de "um dia falta-lhe o tempo-fora" foi abdicar-lhe — precisamente a decisão da fila existe para fazer alguém pensar. Períodos de pagamento ganha uma linha temporal, porque um gap ou uma sobreposição em um calendário é visível em uma faixa e invisível em uma lista. Exportar calcula a variância em relação ao período anterior em vez de aconselhar o operador para se lembrar de comparar. Perto torna-se um degrau numerado, uma vez que ordem é real: recomputação após aprovação de alterações números já acordados. Um `ReasonDialog' substitui seis laminadas à mão que tinham à deriva — alguns aparado após a verificação, então três espaços habilitaram o botão e submeteram um razão vazia que o servidor tomou como presente. Sob Anderson v. Mt. Clemens Pottery essa razão é o registro, então o cheque está no valor aparado agora. `TIMECARD SUB TAB IDS` está intocado: permanece o contrato de URL somente para apêndices e `ATENDÂNCE RAIL` reordena apenas as pílulas, então cada marcador ainda pousa. Um teste afirma que o trilho cobre cada ID exatamente uma vez.
· kamo-internal
Reconstruir a página de rede como uma consola de operações
A página foi a última tela ainda usando o olhar antigo — um gradiente plano barra, cartões brancos, um girador centralizado, alerta() para cada falha — enquanto o o restante do console passou para a linguagem instrumento-panel. Também dados jogados fora a infra- estrutura já estava enviando: a própria organização cor da marca, sua lista de domínio completo, sua política de dispositivo. Uma ideia carrega a reconstrução: os números são os filtros. A página antiga tinha uma linha de contagens e, separadamente, uma linha de fichas de filtro que reafirmam o mesmo predicados em palavras diferentes — dois controles para manter em sincronia e duas chances discordar do que significa "proprietário". Um azulejo estatístico é agora o seu próprio predicado, e tanto a contagem como a seleção vêm das partidasFilter no modelo. O que a página ganhou: procure em todos os seis campos com a correspondência marcada (um domínio secundário usado para devolver nada, mesmo enquanto o cartão mostrado "+2 mais" abaixo dele), / para focá-lo, quatro tipos, grade e densa-lista de vistas, pinning, e uma gaveta de detalhes endereçáveis como ?org=<id> — que é o as cartas ficam mais silenciosas, uma vez que a linha já não tenta mostrar o título curto, alias, ano, configuração, domínio e nome de exibição ao mesmo tempo em 0.7rem cinza. Ver, sort, pins e recentes persistem nas preferências do membro, de modo que O arranjo segue-os entre as máquinas. Cancelar a subscrição está ligada ao endpoint que agora existe, atrás de uma confirmação digitada: todos os outros controles aqui é reversível pela pessoa que o usa e que um não é. O arquivo 1341-linha é agora apenas arranjo. O que é uma organização, e cada pergunta que a tela faz sobre um, vive em redeModel.ts sob teste unitário; o que se pede ao servidor vive em ganchos/. As três visões lidas as mesmas derivações, de modo que eles não podem derivar em discordar sobre o que As organizações são acessíveis. Moção é real, mas silenciosa: entrada escalonada, refluxo de disposição no filtro e ordenar, uma lenta deriva na cabeça do mastro, e o esqueleto kmail existente da consola em vez de um gira-discos. Tudo foi fechado por um movimento reduzido. Três erros encontrados ao escrevê- lo, cada um corrigido aqui: comparando entering?.orgId to detailOrg?.id é indefinido === indefinido quando ambos são ausente, e depois lê .mode off nothing; window.open with noopener returns null, para que uma aba pré- reclamada nunca pudesse ser navegada (a referência do abridor é em vez disso, que é a mesma proteção); e um callback in a cadeia de dependência do efeito de busca poderia transformar um re-render em um refetch loop, então os ajudantes de mensagens são lidos através de um ref.
· SecurityService
Deixe um membro desistir de sua própria adesão
A página da rede oferece "Dessubscreva" desde que foi escrita. Abriu. uma janela de confirmação e depois disse que o recurso seria implementado em breve, porque não havia um ponto final por trás disso — um membro convidado para organização não tinha saída que não passou por um administrador. POST /api/security/org/{id}/leave acts only on the own caller's own member row, Por isso não tem o seu próprio direito: ninguém pode chamá-lo para remover mais ninguém. Desactiva em vez de apagar. recordState.isActive é o soft-delete bandeira, para que a linha sobrevive para auditoria e pode ser restaurado, eo emprego ciclo de vida em TeamMember.teamMembershipStatus é um eixo diferente que este Deixa-me em paz. Cada caminho de sessão ao vivo, lista e notificação Já estamos a falar sobre o termo de base. mesma bandeira, então clareá-la termina presença do chat, mencionabilidade e atribuição elegibilidade sem mais trabalho. Sessões já abertas contra o associação são revogadas a maneira de fora de bordo faz isso, então o acesso termina agora em vez de no TTL deslizante. Três membros não podem ser abandonados, cada um recusando um tipo diferente de dano: a organização que atende o pedido (deixando sessão própria da solicitação), a organização original do chamador (onde o usuário conta está ancorada, sem novo caminho de volta), e um que o chamador possui (que o deixaria sem ninguém capaz de administrá-lo).
· kamo-internal
Reconstruir /hr/observância legal como registro legal
A superfície era uma faixa de tabulação MUI sobre duas grades AG. Pacotes renderizados como sete colunas de contagens e uma célula de acções de seis botões de ícones não marcados, três de que se desactivaram sem dizer o porquê — a única maneira de aprender isso Publique precisava de um fichário para adicionar um e ver o botão acordar. - Um medidor de conformidade abre a página: cada atribuição na org como um empilhado barra sobre quatro baldes, construído a partir de sete total-somente conta porque DocsService não possui nenhum objetivo agregado. Suas linhas de legenda são os controles (a barra em si é decoração aria-escondido), e escolher um filtra as atribuições através ?bucket= — um link que um administrador pode enviar. - Pacotes se tornam um baralho de cartas carregando o que uma mesa teve que soltar: a versão Como medalhão, o verdadeiro... espinha, público, cobertura, e o conteúdo publicado impressão digital como um monograma. Cada bloqueio action permanece visível e afirma sua razão como texto, nunca como dica em uma controlo deficiente. - As atribuições permanecem uma grade, agora com uma linha ferroviária principal do Estado, o membro sobre o seu endereço, progresso da assinatura como uma barra, e um status SENTENCE no próprio estado cor em vez de um chip cinzento. - Um vocabulário de ponta a ponta (publicar / entregar / reemitir / retirar), correspondência Os verbos da própria infra-estrutura. Cor de interação é -- marca legal em todo, de acordo com a regra da casa que a marca possui cada anel de hover e contorno de foco; --legal-seal fica para identidade e estado, assim um White-label org ainda vê sua própria cor nesta página. Cada regra que a superfície desenha é uma função pura em app/lib/legalRegistry.ts com 36 testes, incluindo uma prova de que os quatro baldes cobrem as sete tarefas status exatamente uma vez — sem ele um status adicionado à infra- estrutura seria o medidor em silêncio, deixando a barra de largura completa e parte do org incontável. buildAsignmentQuery agora envia status como parâmetros REPETIDOS para que um balde inteiro seja um request; um CSV chegaria ao DocsService codificado como um token literal.
· kamo-marketing
Jogue o carretel kamo13
Aponta o herói na escada do novo carretel, retrocesso do MP4 e cartaz. A fonte é específico para aquele que substitui (H.264 720p24, 66s, ~30 MB), então Nada sobre o jogador ou a escada muda.
· KlusterServices
Troque o rolo de herói para kamo13
kamo13.mp4 carregado para o MinIO em público/kamo13.mp4 (30 MB, sha256 174aaec6..., verificado byte- idêntico à fonte local após a transferência — este link tem um História da manipulação de cópias grandes). Os antigos objetos kamo12 são deixados no lugar: nada é excluído do MinIO, e eles são o caminho de retrocesso. A nova fonte é específica da que substitui — H.264 720p24, 3.56 Mbps vídeo + 256k AAC, 66.005s - assim que a escada não precisa de ajuste. Ainda não. 1080p degrau; a fonte é 720p e um seria puro upscale. A codificação das teclas de trabalho no SHA-256 dos bytes de origem, apontando- o para um novo objeto é suficiente para torná-lo re-encode; ele vai construir public/hls/kamo13/ e publique kamo13-poster.jpg/.webp na próxima implantação. Também documenta uma ressalva de ordem na troca exposta: o passo de implantação rola o Deployment out antes de executar o trabalho de codificação, então o novo pod começa enquanto loop.mp4 ainda pertence ao rolo anterior. Apenas o canal / live/ é afetado, e apenas por jitter segmento — a /vod/ escada a homepage joga construído depois e não é afetado. Uma reinicialização de lançamento capta-o.
· kamo-internal
Reconstruir /hr/treinamento como uma consola de treino
A aba era duas grades AG sob uma barra de tabulação: precisa e muda. Agora. abre sobre toda a obrigação de formação da org e deixa um administrador agir sobre As lacunas. - Um mastro cujo trilho de coorte é a população designada — passou, atraso, em progresso, não iniciado, dispensado — tamanho proporcional e puxado em carga. Todos os segmentos de ligação profunda em atribuições pré-filtradas, então o gráfico é o caminho para o trabalho em vez de uma imagem dele. - O livro de versão: um tick sólido por versão publicada com o live um aceso, continuando em um canhoto tracejado quando o rascunho passou Ele. Um curso publica como versões e os alunos são presos a um foram enviados — que era anteriormente uma linha de texto lido uma linha em um Tempo. Carregado tanto pela galeria quanto pela mesa, então as duas visualizações compartilham um vocabulário. - Cursos se tornam uma galeria de cartões com busca, fichas de status, ordenar e cobrir arte; a grade fica atrás de uma alternância para uma varredura densa. Ambas as leituras de formaCatalog, então eles não podem discordar sobre o que combina. - As atribuições ganham busca, multi-selecionando com lembrete/extensão/desculpa/ cancelar e exportar CSV. Cada botão de volume indica quantos dos seleção que pode realmente tocar — um lembrete 40 que e-mails 31 é uma mentira O administrador então age. - Um terceiro sub-tab, Insights: pass-rate e anéis de pontuação média, o Perseguindo fila, membros, atividade recente. Tudo vem de o bloco de treinamento de /api/docs/hr/summary, que a Perspectiva da HR já chama; DocsService está intocado. - coverImgId tem estado no DTO e na carga útil salvar desde o módulo Enviado com nada que o pudesse definir e nada que o lesse. A O editor recebe o escolhidor que torna o campo real. Quatro defeitos corrigidos ao longo do caminho: - A publicação foi desactivada em todas as linhas do catálogo e sempre foi. publishBlockedReason walks thurse.items, mas list() mapeia linhas através de toDto(curso, false), que nunca os define — então o item walk viu um lista vazia e retornou 'vazio' para cada curso, incluindo aqueles que publicar perfeitamente do editor. catálogoPublicarBlockedReason gates on as contas que a lista envia; as recusas por questão permanecem na editor, onde as perguntas existem. - A mensagem de confirmação de exclusão era o corpo de estado vazio, colado, então ele leu 'Construir um curso de slides e múltipla escolha Teste-o, e depois atribua-o à sua equipa. - Estado de atribuição das fichas renderizadas status.replaceAll(' ',' ') — o bruto banco de dados enum, em inglês, em todos os 22 locais. - Duas chamadas window.prompt() coletaram um prazo de cumprimento e uma renúncia razão em uma caixa OS não-nomeada. Ambos são diálogos reais agora; a renúncia razão é o que um auditor lê. Regras ao vivo em app/lib/trainingConsole.ts sob 36 testes: vitest aqui é executado no ambiente de nó e coleta apenas .test.ts, então uma regra escrita dentro de um .tsx é uma regra sem teste. As cordas desembarcaram primeiro no dicionário kamo-tradução b5ed84af.
· kamo-marketing
Ligar o banner da oferta, corrigir mortos Saiba mais links e o campo hexagono
Cinco correções, todas aquelas coisas que pareciam presentes mas não fizeram nada. OFERTA BANNER — HeroOfferBanner era código morto; nada o importou. Restaurado, mas impulsionado por qualquer que seja o preço API realmente retorna em vez de pelo antigo PROMO CODE e PROMOÇÃO DE FALLBACK. Foi por isso que morreu. a data final passou e o componente não renderizou nada para sempre. Publicando agora uma promoção na infra- estrutura faz com que apareça no próximo revalidate o lapso remove- o, sem qualquer implantação. Promoções já voltam ao lado dos planos, então o banner não custa nenhum pedido extra. Os filtros de seleção para FLAT PER SEAT, que é um protetor de correção em vez do que uma preferência: a cópia do banner lê "Salvar $N/usuário", então uma PERCENTAGEM Promo renderia "$20/usuário" para o que é realmente 20% de desconto. Exige também real endDate — uma contagem regressiva precisa de algo para contar, e o anterior versão fingiu um com o ano 2099. SAIBA MAIS — o indicador em cada cartão de recurso de casa era um Nada. Agora é um verdadeiro Link com um esticado ::depois assim que o cartão inteiro é a área de hit, apontando para uma nova âncora por categoria em /features. Os seis as lesmas de categoria são um contrato público agora, e são comentadas como tal. HEXAGON FIELD — «.hexagon-grid» declarado «posição: relativa», enquanto o seu único o uso pede `absolute inset-0` via utilitários. Este ficheiro carrega após o Importação tailwind, então em igual especificidade a folha de estilo ganhou: o elemento colapsou para zero altura e clipped o seu próprio ::antes através de `overflow:hidden` e «conter:pintar». O campo não estava a render nada. VIDEO CUT OFF — o quadro do movimento cambaleante overhung o viewport por 1.5-5rem assim que seria Visivelmente correr fora da tela. Isso é bom para decoração e mal para um produto Demo, onde a tira cortada está contente alguém é destinado a assistir. Agora acaba. flush com a borda: ainda sem sarjeta direita, por isso continua lendo como um janela em vez de uma carta, mas o rolo inteiro é visível. Além disso: um controle de reinicialização ao lado do botão de som, e Integrações 64 -> 100+. Ambos os controles de vídeo são apenas ícones agora, que é uma correção em vez de um estilo Escolha. heroVideoMute/heroVideoUnmute faltavam na tradução dicionário, então next-intl renderizou o caminho chave bruta - HTML ao vivo estava servindo E a reconstrução do herói transformou isso em texto visível no botão. As chaves são adicionadas ao dicionário kamo-tradução (f4d2b65b); ícones mais um rótulo de aria manter uma lacuna de cópia de se tornar mobiliário. A contagem regressiva de 50ms é 20 renderiza um segundo para sempre em uma página que também é decodificação de vídeo, para que qualquer um que pedir redução de movimento recebe um relógio 1s e perde a unidade milissegundo.
· kamo-login
Reconstruir o painel de boas-vindas em torno de uma grade de status ao vivo
A coluna esquerda era um título genérico, uma platitude de segurança e uma pilha de decoração ambiente (grade ponto, esfera flutuante, traço pulsante). Substituir por um componente compartilhado WelcomePanel: um bloqueio de peso-contraste Geist, uma linha de cabelo regra, uma única linha de cópia de orientação, e um trilho monoespaçado que relata o que é realmente verdadeiro sobre o login — se a conexão é criptografada, em qual hospedeiro estás, e onde estás no fluxo. A tela de recuperação usa o mesmo painel com sua própria cópia e mapeamento passo assim Os dois ecrãs permanecem uma só superfície.
· kamo-marketing
Reconstruir o herói como uma divisão editorial com o rolo como uma abertura
O carretel era um retângulo arredondado centrado com uma sombra de gota — um objeto colocado sobre o design em vez de parte dele — e era a ÚNICA coisa no herói: ff04781 apagou o crachá, título, descrição, CTAs e estatísticas quando ele pousou O vídeo, que deixou HeroHeadline. tsx órfão e dez chaves de cópia não utilizadas. Então... a seção não teve nenhuma tese e nenhum apelo à ação, que é a maior parte do por que leu como um quadrado no meio da tela. Agora uma composição de duas colunas: sobrancelha, manchete, lede, CTA e nota livre-para sempre à esquerda; o movimento cambaleante à direita, rodando PAST à direita borda do viewport. Três coisas fazem a integração: - os cantos da moldura são chanfrados a 45°, a mesma geometria que o hexágono campo já por trás dele, em vez do raio-fronteira que cada herói SaaS usa; - um corrimão leve para baixo da sua borda esquerda carrega a azeitona do título Passagem de viagem, ligando as duas metades da divisão; - sangra fora da tela em vez de parar curto, por isso não há quarta borda para o fazer ler como um cartão. A secção está ofuscada. A borda é um gradiente de 1px mostrando através da caixa de enchimento do pai, porque Clip-path corta fronteiras no chamfer. Ambas as caixas repetem o polígono – clip-path não herda, e uma caixa interna quadrada cutuca seus cantos. Trabalhos de detalhe: o controle de som foi movido para a parte inferior esquerda, desde que o quadro a borda direita está deliberadamente fora da tela e um controle ancorado lá seria inatingível. Um trilho de progresso ao longo da parte inferior mostra posição no circuito — a única pista de que o movimento cambaleante é finito em vez de um fundo animado. As estatísticas são uma regra sob a composição, sem contagem: quatro figuras corrida sobre carga competir com o carretel no momento que deve ser Observado. "Assista Demo" é ido como um CTA porque o movimento cambaleante ao lado do botão é a demonstração; o link secundário reutiliza a própria etiqueta "Ver preços" da tira CTA e alvo para que a página nomeie a ação de uma maneira. Nenhuma cópia nova: cada string já existia em en.json. i18n/request.ts já deep-merges cada localidade com Inglês, assim que as etiquetas de estatísticas faltando de 21 locales render em inglês até que o dicionário CI alcança. Código morto removido ao longo do caminho: um div acento-glow keyed fora de um `group-hover` sem antepassado `group', um vídeo não utilizadoContainerRef, e objeto-conteúdo em um Fonte 16:9 dentro de uma moldura 16:9.
· kamo-marketing
Jogue o movimento cambaleante como uma escada adaptativa HLS em vez de um 31,8 MB MP4
O herói apontou uma tela nua <vídeo src> em um único 3,9 Mbps 720p24 MP4, assim um telefone em uma conexão fraca baixado exatamente o mesmo fluxo que um desktop em fibra, e continuou baixando depois que o visitante passou. Agora toca o 720p/540p/360p escada construída pela KlusterServices hero-hls-encode trabalho: o jogador leva o degrau sua conexão carrega e busca apenas segmentos ~4s enquanto observava. O degrau 360p é de 5,4 MB contra o original 31,8 MB. Playback sai do JSX e entra em efeito porque escolher entre HLS e o MP4 é uma decisão em tempo de execução. Safari e iOS obter a lista de reprodução nativa (iOS não tem extensões de fonte de mídia, então hls.js não é uma opção lá); todos os outros conseguem através de um hls.js importado dinamicamente que fica fora de O pacote de entrada. Um erro fatal do hls.js, um navegador não suportado ou um erro carga de bloco toda a terra no MP4 original, que ainda é a fonte da verdade. O <vídeo> deliberadamente não carrega nenhum `src' — declarando que se iniciaria o 31.8 MB download progressivo esta mudança existe para remover. hls.js torna-se uma dependência direta. Foi apenas presente transitivamente via @react- três/drei, que já não está em package.json em absoluto; npm install podou seus 67 pacotes restantes do lockfile velho. Nada na aplicação/ importações drei, três, ou reação-placas mundiais (FlagEmoji.tsx documentos substituindo o último dos de propósito). O atributo poster também começa a funcionar: ele tem referenciado public/kamo12-poster.jpg desde que este componente foi escrito, mas o objeto Nunca existiu e o herói ficou em branco até o primeiro quadro ser descodificado. A codificar trabalho agora publica-o.
· KlusterServices
Implantar um servidor de streaming e uma escada ABR para o carretel do herói
MediaMTX serve canais ao vivo sobre HLS, WebRTC, RTSP e RTMP, e loops o marketing carretel como um canal sempre disponível via sempreDisponívelFile — no ffmpeg sidecar, e qualquer editor RTMP/RTSP toma o canal de forma transparente sobre e reverte ao movimento cambaleante na desconexão. O vídeo homepage em si não é servido através do MediaMTX. MediaMTX é um live servidor sem modo VOD; looping de um arquivo em um canal ao vivo iria soltar cada visitante no meio do rolo em vez do primeiro quadro. Então... hero-hls-encode-job constrói um 720p/540p/360p Escada HLS da mesma MP4 e publica para MinIO, roteado como /vod/ no host de streaming. Medida contra a taxa de bits única de 31,8 MB original: 18,8 MB a 720p (SSIM 0,989), 9,8 MB a 540p, 5,4 MB a 360p, em ~4s segmentos que o jogador obtém apenas enquanto assiste. O trabalho é ETAG-idempotente, então ele não-ops em cada implantação que não é um vídeo swap, e também publica public/kamo12-poster.jpg — referenciado pelo homepage desde que foi escrito, mas um 404 até agora. Rotas ao vivo na transmissão.* e tema.*: tema.<domínio> já tem DNS e um certificado para cada inquilino, então isso funciona hoje, e o stream.* formulário ativa-se por conta própria uma vez que exista um registro DNS. A rota WebRTC corresponde a um prefixo. deliberadamente — MediaMTX serve WHIP na mesma porta, e um prefixo corresponderia publicar um endpoint de ingestão não autenticado para a internet. Isto toca .forgejo/, que força FULL=verdadeiro e um full-cluster reconciliar. Auditado primeiro de acordo com o procedimento documentado: kubectl diff sobre todos os 112 aplicar alvos está limpo em 104; cinco são estes novos arquivos, dois são o nats/mail bootstrap Jobs o fluxo de trabalho deliberadamente apaga e reaplica, e mail/mysql-permissions-job.yaml falha como um trabalho imutável concluído, que o O fluxo de trabalho já tolera com o eco. Cada alvo de remoção do kubectl foi confirmado ausente.
· KlusterServices
Programa de montagem do lado da estação de trabalho para a partilha do KAMOTEAM
NFSv4 sobre o NodePort, montado suave com uma automontagem systemd para um inacessível o cluster devolve um erro em vez de pendurar cada processo que toca o pasta, e assim o laptop ainda inicia normalmente quando fora da LAN.

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