Changelog
Cosa c'è di nuovo.
Ogni miglioramento alla piattaforma, come si spedisce.
· VOIPService
Persist chiama e associa chiamate, testi e segreteria
Le chiamate non sono mai state registrate da nessuna parte — VoipCallEventService ha pubblicato un evento NATS per il browser e non ha mantenuto nulla, e RECORDINGS esiste solo quando l'audio era catturato. La sincronizzazione per-instance ora tira la cronologia delle chiamate attraverso il PhoneProvider astrazione e upserts PHONE CALL LOG, in chiave su (instance, provider call id). Questa è l'unica strada che funziona per FreePBX e Microsoft Teams, né di che offre un webhook per-call utilizzabile. La spazzata è il singolo scrittore. Persistente dal webhook RingCentral pure è stato provato e rimosso: la sua notifica trasporta la telefoniaSessionId mentre la call-history API restituisce un id record diverso, così i due scrittori hanno prodotto due righe e due voci timeline per una chiamata. Le chiamate sono attribuite ad un'estensione solo quando l'estensione è effettivamente sulla Chiama. RingCentral's endpoint è estensione/~ e ignora l'argomento, quindi looping per estensione altrimenti filerebbe le chiamate di ogni membro sotto quale l'anello visitato ultimo. Gli id di registrazione vengono risolti per pagina tramite RecordingLookupService, lo stesso arricchimento la storia softphone ottiene — senza di essa la scheda conosce una registrazione esiste ma non può giocarlo. I testi si collegano a send/receive; i messaggi vocali si collegano alla sincronizzazione.
· InitializerService
Schema e punto di contatto per le comunicazioni di piombo
Hibernate crea le tre nuove tabelle delle entità condivise-lib; questo aggiunge che non può esprimere — l'indice unico parziale che mantiene al massimo uno aperto finestra a punto di contatto per (caso, slot) e indici di copertura DESC per La scansione della linea temporale. Il seme apre una prima finestra per ogni piombo che precede l'indice. No. comunicazione back-fill: nessuna chiamata storica o messaggio è associato a Qualsiasi cosa. Senza di esso un piombo preesistente non ha punti di contatto affatto e potrebbe mai corrispondere a una chiamata in arrivo, quindi ogni scheda resterebbe vuota per sempre. Funziona attraverso LeadContactPointService piuttosto che una massa INSERT..SELECT così c'è esattamente una implementazione della canonizzazione. Un gemello SQL avrebbe per la ridistribuzione delle cifre, la manipolazione NANP, il piano di estensione e l'indirizzo swrapping, e qualsiasi divergenza successiva sarebbe invisibile — le file semi sarebbero semplicemente smettere di abbinare il traffico dal vivo. Applicato contro la produzione: 66 punti di contatto aperti su 45 piste.
· kamo-shared-library
Indice punto di contatto puntuale e collegamenti di comunicazione con i lead
Aggiunge la colonna vertebrale la linguetta delle comunicazioni di piombo legge da. LEAD CONTACT POINTS registra ogni numero di telefono ed e-mail che un lead abbia mai avuto, come una finestra con attivaDa/Attivo. LEAD COMMUNICA registra il durevole link tra una guida e una chiamata, segreteria, testo o e-mail, scritta al il momento in cui avviene contro i punti di contatto vivono in quel momento — così cambiando un numero di piombo lega il traffico futuro solo e lascia la storia esattamente come era. PHONE CALL LOG è il record di chiamata che la piattaforma non ha mai avuto: RECORDINGS ha solo un riga quando l'audio è stato catturato, quindi le chiamate mancate e non registrate non hanno lasciato traccia. ContattoCanon è la sola definizione di "lo stesso numero" per l'intero piattaforma; LeadController.canonical Telefono ora delegati ad esso in modo inverso ricerca e la corrispondenza di comunicazione non può essere disaccordo. LeadContactPointService e LeadCommunicationLinker non sono deliberatamente stereotipi — un @Service condiviso-lib che necessita di un repository fallisce l'avvio del contesto in ogni servizio che non ha aggiunto il pacchetto, che ha preso la produzione tre volte. I servizi di proprietà dichiarano i fagioli; LeadService li prende @Autowired (required=false) in modo che ancora fili ovunque altro.
· MediaService
Cattura il comando demo prima della fase di prenotazione
Il modulo demo del sito di marketing ha raccolto nome, e-mail, azienda, dimensione della società, industria e un messaggio, quindi avanzato per la pianificazione senza dirlo a nessuno. La creazione di piombo viveva solo all'interno di un'area riservata. ogni visitatore che non ha completato una prenotazione — compresi tutti i visitatori mentre nessuna disponibilità è pubblicata — è stata scartata dopo aver consegnato il loro dettagli. - Si'. Gufo org proviene dal X-Public-Chat-Org-Id header APIService francobolli dal chiave validata, mai dal corpo. - findOrCreatePublicLead è ora il singolo percorso di scrittura per tutti e tre superfici pubbliche (forma di demo, programma, unisciti), quindi un visitatore produce un piombo tuttavia molti passaggi che completano. Timbra l'org "KamoCRM" / "Demo Richiesta" prodotto fornitore e prende il mercato da quel venditore, e percorsi tramite LeadService.createLead invece di salvare l'entità direttamente — il vecchio salvataggio in linea non prodotto storia fila e nessun evento LEAD CREATED, così quelli non è mai apparso in una griglia già aperta. - Le dimensioni dell'azienda, l'industria e il messaggio del visitatore sono mantenuti come una storia principale ingresso; Piombo non ha colonna per nessuno di loro e sono la parte una il venditore legge. - GET /api/public/webinar/types permette a una pagina pubblica di scoprire il tipo id piuttosto che portare una copia in codice duro, e slot / disponibile ora anche restituisce disponibiliTempi — la stessa disponibilità risolta contro il proprio org timezone in istantanei che possono essere pubblicati come un programmaStartTime.
· kamo-shared-library
Demo-richiesta pubblica primitivi cattura
Un visitatore che riempie il modulo demo del sito di marketing ma non prenota mai una volta non ha lasciato traccia: il piombo è stato scritto solo all'interno della transazione di prenotazione. Catturarlo davanti ha bisogno di tre pezzi la biblioteca non ha avuto. E' una cosa da fare. portare la stessa forma già prodotta. Passato al prodotto del fornitore piuttosto che all'e-mail da solo in modo che una forma anonima non possa mai adottare (e ri-stamp) un piombo personale creato per quell'indirizzo. - PublicWebinarDateSlotsDTO: la disponibilità si risolve in istanti assoluti. The slot memorizzato è una data locale più un tempo a parete con nessuna zona, che una il browser non può trasformarsi in un programma prenotabileStartTime. - PublicWebinarTypeDTO: un tipo webinar senza il roster del presentatore, quindi un pubblico pagina può scoprire il tipo id senza perdere gli indirizzi e-mail del presentatore.
· kamo-internal
Registrare Provider Setup in navigazione, e scrivere i consigli rapidi
La scheda Configurazione del Provider reso all'interno di ImpostazioniVisualizza ma è stata raggiunta solo da digitando l'URL — mancava da entrambi i posti che le impostazioni dell'elenco destinazioni: il menu impostazioni nav e la pagina di atterraggio delle impostazioni. Ora conduce ciascuno, corrispondente all'ordine della scheda, dal quale il fornitore ospita riunioni decide quali delle altre impostazioni sono disponibili. I consigli rapidi sulle cinque schede esistenti non sono mai stati scritti — ogni stringa era a placeholder ("Help Heading", "Bullet Lobby Title"). Ora dicono qualcosa. e coprire le impostazioni aggiunte per Zoom e Team: controlli in-meeting, codice di accesso politica, bypass della lobby e ruoli del presentatore, tiro di registrazione a distanza e trascrizioni, e gli inviti del fornitore-sent. Ogni scheda spiega anche dove i fornitori differiscono, quindi un controllo disabile ha una ragione dichiarata piuttosto che semplicemente guardando rotto.
· kamo-internal
Applicare le regole di accesso geo e IP sui carichi di pagina
checkAccess ha valutato le regole stesse e non ha abbinato nulla per tutta la sua vita: rispetto dei tipi di regola contro # Mentre l'editore # scrive i nomi di enum WHITELIST/BLACKLIST/TEMP BLOCK. Geo è stato un commento dicendo Node ha nessun database GeoLite. Entrambe le metà sono andate — ora legge il verdetto SecurityService cache in Redis e chiede che il servizio su una miss, quindi c'è una implementazione della politica e nessuna copia lasciata qui alla deriva. L'indirizzo è l'hop pubblico più a destra di X-Forwarded-For piuttosto che il più a sinistra. Proxies append, quindi l'elemento sinistro è quello che il cliente ha inviato, e la lettura sarebbe hanno lasciato un intestazione decidere quale paese il visitatore è trattato come essere in. Un visitatore bloccato ora ottiene un documento normale 403 invece di JSON crudo — questo raggiunge un browser facendo una navigazione di alto livello. Non nomina nessuna regola e nessun paese: il lettore non può agire su questo, e qualcuno che sostiene il confine non deve essere consegnato una mappa di esso. I falli si aprono su ogni sentiero. Un proxy che blocca quando Redis è brevemente irraggiungibile prende l'intero inquilino offline.
· APIService
Fornire regole di accesso inquilino al gateway
Questo filtro è inerte da quando è stato scritto. risolvereOrgId lettura org:dominio: da Redis, nulla ha mai scritto quella chiave, e il ritorno nullo è caduto per consentire; geo branch era un se-blocco vuoto con un commento deferente a SecurityService, che era Neanche forzando. Ogni richiesta è passata. Ora legge il verdetto SecurityService cache e chiede che il servizio su una miss, così il La politica vive in un posto piuttosto che essere attuata qui. Il sentiero caldo è un Redis lettura; una miss costa una chiamata HTTP a breve termine per organizzazione e indirizzo per 30s. L'indirizzo del cliente è l'hop più pubblico di X-Forwarded-For. Proxies append, così il elemento sinistro è attaccante-fornito — lettura avrebbe lasciato un intestazione scegliere quale paese da cui viene giudicata la richiesta. Esenta sonde di vita, preflight di CORS e la decisione si conclude, e mai giudici indirizzi di cluster-internal: il traffico di servizio-servizio non ha paese e bloccarlo avrebbe abbassato la piattaforma. Fail-open su ogni percorso, con un test per dipendenza. Questo siede davanti a tutti gli inquilini traffico API, quindi un filtro che blocca quando Redis è lento è un outage totale. Anche le gocce ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** ponteggio dal commit iniziale, in un pacchetto senza @SpringBootConfiguration a Trova. Non è mai passato; CI costruisce con -DskipTests così nulla notato. Rimozione rende 'mvn test' utilizzabile qui.
· SecurityService
Realmente applicare le regole di accesso geo e IP
Le regole sono state memorizzate, memorizzate in cache a Redis e mai consultate. AccessCheckController è stato l'unico chiamante della valutazione e niente chiamato AccessCheckController; APIService's filtro risolto orgs da una chiave Redis nulla ha scritto e trattato la signorina come permesso. Configurare un blocco geografico non ha fatto nulla. AccessDecisionService è ora l'unica autorità. Login lo chiama direttamente, il gateway il filtro e il proxy kamo-internal raggiungono la stessa decisione attraverso il punto finale di controllo, e la politica esiste una volta invece di una volta per strato. La copia di Node era lontana abbastanza da non corrispondere nulla: ha paragonato i tipi di regole a "whitelist"/"temp-block" mentre il editore scrive WHITELIST/TEMP BLOCK. La risoluzione GeoLite viene memorizzata in Redis per un giorno, positivamente e negativamente, e salti indirizzi privati del tutto. L'indirizzo viene da un intestazione, quindi senza una cache negativa un caller rotante bogon trasforma ogni richiesta in una sonda fresca del tavolo da 5.7M-row — la forma DoS findByIpInRange era già riscritta una volta per evitare. ClientIpResolver legge l'altoparlante pubblico più a destra di X-Forwarded-For, non il più a sinistra. L'elemento sinistro è quello che il cliente ha inviato: i proxy append, quindi "X-Forwarded-For: 8.8.8.8" più l'append di Traefik avrebbe lasciato chiunque sceglie il paese il loro blocco è giudicato contro. Tutte le regole geografiche sarebbero state un'intestazione lontana dal bypass. Fail-open in tutto, per dipendenza e affermato per dipendenza. Un controllo di accesso fallisce chiuso su un Redis wobble blocca ogni inquilino fuori dalla piattaforma, che è un peggio incidente rispetto alla finestra che chiude. Un indirizzo whitelist bypassa anche geo, quindi un amministratore che blocca il proprio paese mantiene una via di ritorno.
· kamo-internal
Scheda di registrazione della piattaforma per l'app Zoom Marketplace
Completa il percorso di connessione a un clic di Zoom. Il backend si risolve già PlatformOAuthProviderType.ZOOM, ma senza carta qui un operatore piattaforma aveva da nessuna parte per inserire le credenziali Marketplace, in modo che l'organizzazione del provider di riunione schermo ha sempre segnalato un clic collegare come non registrato. Facoltativo per il design: il percorso per-org Server-to-Server non ha bisogno di niente qui e rimane l'impostazione predefinita raccomandata, quindi una carta vuota mantiene semplicemente un clic nascosto. Le riunioni di team riutilizzano la registrazione Microsoft 365 esistente.
· kamo-internal
Microsoft Teams Fornitore di telefonia, anteriore a posteriore
Addiziona le squadre a **************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** attraverso: la selezione dei fornitori, la configurazione dei Servizi di Comunicazione Entra + Azure, consenso amministratore inquilino, il per-membro collegare, e un softphone in-browser. Il softphone è la parte che differisce strutturalmente da ogni altro fornitore. Le squadre non espongono alcun registrar SIP a terzi, quindi una linea Teams non può essere registrato con SIP.js o RingCentral WebPhone. Il percorso supportato è Azure Servizi di Comunicazione Custom Teams endpoint: il backend scambia i Token Entra del membro per un token ACS, e TeamsAcsPhoneSdk trasforma che in un call agent dietro la stessa interfaccia PhoneSdk gli adattatori SIP implementano, quindi utilizzareSoftphone e ogni componente UI rimanere fornitore-agnostico. SipCredenti guadagni campi acs* facoltativi; sipInfo è semplicemente vuoto per le squadre. Il pacchetto ACS è solo browser e importato dinamicamente dentro start(), dal momento che un import statico romperebbe il rendering del server. Due verità specifiche per le squadre sono emerse nell'interfaccia utente piuttosto che sinistra per fallire runtime: la chiamata in-browser è facoltativa (lasciare ACS vuoto e l'org ancora ottiene presenza e cronologia delle chiamate, con il dialler nascosto piuttosto che errore), e voicemail è spento per impostazione predefinita perché Teams non ha API voicemail — messaggi terra nella casella di posta di scambio di ciascun membro, quindi la lettura è un materialmente più ampia acconsente che un amministratore debba optare deliberatamente. Il membro della carta di connessione nella scheda delle impostazioni del telefono è ora provider-aware invece di solo RingCentral. Entrambi i fornitori hanno bisogno di un'autorizzazione per membro, per motivi diversi: RingCentral perché un softphone può solo registrarsi come estensione dell'account collegato, Teams perché la presenza scrive e la chiamata lo scambio di token è delegato-solo. Branding: la piastrella utilizza il marchio aziendale Microsoft e il nome del prodotto normale. Microsoft consente di nominare i team per descrivere un'integrazione ma limita i team loghi e branding a partner di team certificati, quindi nessun glifo Team viene spedito. Verificato: npm test — 40 file, 394 test, 0 guasti; tsc --noEmit clean; the i18n key guard e client-navigation guard entrambi passare. Dizionario stringhe atterrate primo in kamo-translation-dictionary 0801a17.
· VOIPService
Microsoft Teams Phone as a provider
Teams is not one API, so the provider glues three control planes together: • Graph, app-only — call history (getPstnCalls), presence reads, directory and Teams device inventory. Call records are application-permission by design; Microsoft states plainly that they have no delegated form. • Graph, delegated — presence writes (Presence.ReadWrite exists only as a delegated, self-only permission) and voicemail, which lives in the member's Exchange mailbox because Teams has no voicemail resource at all. • Azure Communication Services — the in-browser softphone on the member's own Teams Phone line, via the custom Teams endpoint token exchange. This is the only supported route: Teams exposes no SIP registrar to third parties, and the Graph calling API joins as a bot rather than as the user. Capabilities are computed from what an instance is actually configured for rather than hardcoded, so an org without ACS reports voiceCalls=false and the UI hides the dialler instead of surfacing a 501 mid-call. Deliberately absent, because no Microsoft API exists at any version: SMS from a Teams number, call queues, call forwarding, auto-answer, and starting/stopping recording. Those stay on the interface defaults that raise 501. Credentials resolve per-instance first (an org registers its own Entra app — the common enterprise shape, and it needs nothing of Kamo the platform) and fall back per-field to a MICROSOFT_TEAMS row in the platform OAuth registry. Onboarding runs in two hops because the tenant must be discovered before it can be pinned: an admin signs in, the id_token's `tid` gives the real tenant, then they are sent to that tenant's /v2.0/adminconsent. Consent is confirmed by minting an app token and calling Graph — Microsoft returns admin_consent=True even on failure, and warns that the `tenant` redirect parameter is forgeable, so neither is trusted. Both Teams flows and the per-member delegated connect multiplex onto /api/voip/oauth/callback, the one path already public at the gateway, so APIService needs no new route. Change notifications ride the existing /api/voip/webhook for the same reason. Graph's handshake differs from RingCentral's — a ?validationToken query parameter echoed as text/plain — and is answered before anything else, since Graph drops notifications for ten minutes if an endpoint is slow. Subscriptions are created unfiltered on purpose: the subscription filter still targets the legacy `participants` collection that stopped returning data in June 2026. Two pre-existing defects fixed because Teams makes them load-bearing: • CallController, CapabilitiesController and CallFeaturesController resolved the provider via getPrimaryForOrg — the org's oldest active instance of ANY type. That is only ever right for a single-provider org. Teams will usually be a second provider, so every member's history, voicemail and agent state would have been served by whichever provider was created first. They now use a member-aware getForMember, mirroring what SipController already did. • Deleting an instance left member_voip_oauth_token rows behind, since there is no FK to cascade from, and they kept reporting "connected". Verified: mvn test — 133 tests, 0 failures (49 new).
· kamo-internal
Scheda di configurazione del fornitore e impostazioni di riunione con capacità
Aggiunge la scheda Setup del Fornitore a /settings/features/meets e rende ogni altro scheda onesta circa il fornitore che sta configurando. useMeeting non costruisce più incontrare. ha fatto ogni incontro un incontro Kamo Meets indipendentemente dalla configurazione. Ora crea l'incontro attraverso il backend e utilizza gli URL che il provider restituisce, così il Centro Interazione, la finestra dello strumento di chat e la directory dei membri tutto percorso attraverso l'astrazione senza cambiamenti di loro: condividono MeetingDialog, che condivide questo gancio. - Configurazione del fornitore: schede del fornitore con una barra di copertura delle impostazioni, quindi il costo di la commutazione è visibile prima dell'interruttore piuttosto che scoperta in seguito su un scheda di comandi grigiati. I moduli Credenziali sono solo per iscritto — il server restituisce quali campi sono impostati, mai i loro valori — più un test di connessione live e istruzioni di configurazione per-provider. - Capability gating: useMeetProvider legge le impostazioni supportate del provider e MeetSettingRow disabilita il resto con un tooltip che nomina il fornitore. Non supportato le impostazioni sono mostrate piuttosto che nascoste, e sono autenticamente disabilitate piuttosto che semplicemente dimmerse, quindi non possono essere cambiate dalla tastiera. Li salva completamente. Gating non si apre — una ricerca di capacità fallita mostra tutto, dal grigiore fuori uno schermo di lavoro è peggio di mostrare un controllo che il backend avrebbe ignorato. - Nuove impostazioni il supporto degli altri fornitori: unisciti prima dell'host, chat in-meeting, screen share, dial-in, utente autenticato-solo, codice di lunghezza e incorporazione, area di bypass della lobby, controlli del presentatore e dei partecipanti, registrazione del cloud, trascrizioni, registrazione di download e inviti di provider-sent. - /meet/join/hyref> è ora provider-aware: Kamo Meets ottiene ancora una volta una chiave coniato all'ora del click, Zoom e Teams reindirizzano al proprio URL del provider. Si'. accetta un id meeting o un nome in camera, quindi gli inviti inviati prima di questo cambiamento Continua a lavorare. - Il link di invito email è costruito da un UUID-validated meeting id piuttosto che un nome di camera, mantenendo la regola esistente che un URL fornito dal cliente non è mai fidato in un'e-mail marcata org.
· kamo-shared-library
Tipo di fornitore MICROSOFT TEAMS
Aggiunge MICROSOFT TEAMS a VoipProviderType. L'enum persiste come ORDINAL su La posizione è di carico e la posizione è di carico e costante va ultimo; la classe Javadoc ora lo dice piuttosto che lasciare il prossimo persona che lo deferisca. Nessuna modifica DDL — una colonna ORDINAL non ha valore whitelist, quindi questo non ha bisogno di un KamoInitializer run. Aggiunge due query derivate su MemberVoipOAuthTokenRepository: • findByProviderAndInstanceId — mappa un'identità del fornitore in entrata (una Microsoft Entra oggetto id su un record di chiamata Teams) di nuovo a un membro Kamo. • deleteByInstanceId — queste righe non hanno FK all'istanza, quindi eliminando una il fornitore li stava lasciando dietro ancora riportando "connesso".
· APIService
Percorso del fornitore di meeting OAuth callback
Zoom e Microsoft reindirizzare il browser a questo gateway dopo un org autorizza L'app di Kamo. Il popup atterra qui senza inquilino sessione — il org viaggia in ?stato — così il callback ha bisogno del passaggio senza sessione, auth-stripping email provider callback già utilizza, non il normale /api/meet/** in avanti.
· MediaService
Astrazione del fornitore di riunioni con Zoom e Microsoft Teams
Incontri erano Kamo Meets in costruzione — MeetService perseverò in una sala Meet nome e nient'altro. Ciò introduce l'astrazione e due fornitori remoti dietro. meet/provider rispecchia lo strato del provider di posta elettronica: MeetingProviderResolver assembla un MeetingContext (crittografate credenziali, rinnovata OAuth Grant, org policy, account host lato provider) e una mappa delle capacità, quindi i richiedenti chiedono MeetingScheduler piuttosto che ramificarsi sul tipo di fornitore. Aggiungere un quarto provider significa aggiungere un fagiolo, non modificare siti di chiamata. - KamoMeetProvider: nome della camera e costruzione URL, spostato lato server. The org-namespacing che ferma due inquilini che condividono una conferenza Prosody utilizzata live nel browser; ora è bloccato da test. - ZoomProvider: REST v2, Server-to-Server OAuth o l'app Marketplace. per conto del membro dell'organizzazione, estrae registrazioni cloud e trascrizioni. - MicrosoftTeamsProvider: Graph onlineMeetings, app-only o delegato. La le sonde di prova di connessione che soddisfano la creazione, perché la politica di accesso all'applicazione Le squadre richiedono anche è la solita cosa che manca un amministratore — il permesso da solo autentica bene e poi 403 su ogni incontro. Riunione Caratteristica è il contratto con l'interfaccia utente di admin: ogni fornitore dichiara il le impostazioni possono onorare, e le schermi delle impostazioni disabilitano il resto. I reclami sono affermato in test piuttosto che fidato, dal momento che un fornitore di overclaiming produce toggles che silenziosamente non fare nulla — il fallimento questo strato esiste per prevenire. ProviderRecordingSweep estrae registrazioni remote nel proprio storage di Kamo la ritenzione, la quota e i riassunti post-meeting funzionano in modo identico su ogni fornitore. I download sono bloccati: questo servizio serve anche chat e incontri sotto un 1Gi limite, quindi una registrazione di grandi dimensioni è saltata con un avviso piuttosto che rischiare il baccello. Le credenziali sono crittografate con la chiave della piattaforma esistente, quindi nessun nuovo segreto ha da prevedere. La gestione delle sessioni condivisa dagli endpoint delle riunioni si sposta in MeetSessionResolver — deliberatamente senza ampliare il controllo dei suoi diritti.
· InitializerService
Schema per lo strato di incontro multiprovider
Crea ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** le colonne di identità del fornitore a MEETING RECORDS e MEETING RECORDING e Impostazioni zoom/teams-capable a ORG MEET CONFIG, e ricarica ogni esistente organizzazione a una riga esplicita KAMO MEET/ACTIVE. Ci hanno insegnato due cose, entrambe codificate qui. Il ddl-auto di Hibernate costruisce la SessionFactory prima di qualsiasi CommandLineRunner, quindi le dichiarazioni CREATE TABLE normalmente no-op e le colonne arrivano senza default — il backfill quindi fornisce id e timestamp in modo esplicito piuttosto che affidarsi a loro, motivo per cui la sua prima versione silenziosamente non ha inserito nulla. E l'unicità dichiarata sul entità significa che Hibernate mantiene i propri vincoli, in modo che gli indici nominati la revisione precedente aggiunto sono stati duplicati; sono caduti qui. Aggiunge com.kamo.z.shared.meet all'elenco esplicito @EnableJpaRepositories, senza che i nuovi depositi non hanno fagioli e KI aborti prima di qualsiasi fuoco corridore.
· kamo-shared-library
Incontri di modello come fornitore-agnostico
Kamo Meets era l'unico backend possibile: OrgMeetConfig era un appartamento Meet Impostazioni borsa e un MeetingRecord non ha tenuto altro che un nome di camera. Questo aggiunge il schema per eseguire un'organizzazione su Zoom o Microsoft Teams invece. - meet.provider: MeetingProviderType / MeetingProviderStatus, il per-org OrgMeetingProviderEntity (crittografato credenzialial blob, ultimo stato verificato) e OrgMeetingOAuthTokenEntity, rispecchiando il pacchetto email provider che già supporta l'astrazione del provider di posta elettronica. - MeetingRecord acquisisce l'identità del fornitore di hosting — meeting remoto id, unire e host URL, codice di accesso, account host — in modo che una riunione può essere riunita e riconciliato chi lo ospita. - MeetingRecording guadagna providerRecordingId, che mantiene il tiro di un telecomando Idempotent di registrazione cloud del fornitore. - OrgMeetConfig ottiene le impostazioni Zoom e Team supportano che un solo Meet config non aveva motivo di portare: unirsi-prima-host, codice di accesso modellare, lobby campo di bypass, controlli del presentatore e dei partecipanti, registrazione del cloud, trascrizioni, gli inviti del provider-sent, e le riunioni degli host fallback sono creati sotto quando un membro non ha un account sul fornitore. Ogni org senza riga del fornitore si risolve a KAMO MEET, quindi questo è inerte fino ad un admin sceglie diversamente. Colonne sono state applicate alla produzione da KamoInitializer prima che questo atterrasse. PlatformOAuthProviderType ottiene ZOOM(9) per l'app Zoom Marketplace; Team incontri riutilizzare MICROSOFT(3). Il suo test di guardia si muove con esso, da un enum e il test che fissa i suoi ids persistiti non può atterrare separatamente.
· kamo-internal
Paesi consenti-lista sulla carta di blocco geo
La carta potrebbe negare solo i paesi, quindi un org che serve un paese ha dovuto enumerate tutti gli altri. Ora ha entrambe le liste: una modalità di attivare le scelte che elencano il paese scelto atterra, e i due tavoli siedono fianco a fianco. Un elenco di permessi vuoto permette tutto non bloccato; un non-vuoto nega ogni paese assente da esso. Quell'inversione è abbastanza drastica da dichiarare sulla carta piuttosto che lasciare per essere scoperto, così il avviso sopra le tabelle dice quale regime è in vigore. Il vincolo unico (org, country) mantiene un paese su un unico elenco, che farebbe re-adding all'altro fallisce, quindi ogni riga porta un'azione di movimento che lo capovolge tramite PUT Invece.
· SecurityService
Paesi a lista consenti a blocchi geo
La sezione geo mai negata: un paese è stato bloccato o era assente. Orgs che Voglio servire un paese ha dovuto enumerate ogni altro, che è insostenibile e silenziosamente sbagliato nel momento in cui appare un nuovo codice paese. org geo block rules.is blocked già esisteva ed era vero, quindi un permesso la regola è che la colonna set falsa — nessun cambiamento di schema. Valutazione: un blocco esplicito vince, poi un elenco consentita nega tutto ciò che manca da esso, e un permesso lista permessi vuoti tutto non bloccato. Ciò mantiene gli org esistenti di sola battuta che si comportano esattamente come prima. pubblicareGeoRules ora spedisce entrambi i lati della regola impostata, dal momento che le regole consentono sono carico-portante una volta che uno esiste. I carichi di paga incassati scritti prima di questo cambiamento non portano campo chiuso e sopravvivere la cache TTL attraverso una distribuzione, quindi un campo mancante legge come bloccato. Anche gli ambiti setBlocked ed eliminare all'org del chiamante. eliminare precedentemente risolto una regola di UUID da solo, quindi un id di un altro inquilino sarebbe stato onorato.
· kamo-shared-library
Leggi la qualità sulle regolazioni e sulle versioni guida
Un prezzo assemblato è una cella di base PLUS le regolazioni impilate su di esso, in modo da giudicare solo la cella giudica metà del numero: un pulito-leggi 99.875 con un errore -1.250 LLPA in cima è ancora un prezzo sbagliato. MlosPriceAdjustment ora porta la qualità di lettura di quella regolazione, così il cancello di segnale può prendere il collegamento più debole attraverso tutta l'assemblea. MlosGuidelineVersion ottiene lo stesso campo per la consistenza con la colonna vertebrale del foglio di frequenza -- ha già portato fonteDocumentId, quindi questo completa la sua provenienza. Sia nulla; null significa NO SIGNALE piuttosto che buono. Colonne applicate al prod CockroachDB via KamoInitializer.
· kamo-internal
Apri i link di nuovo in questa app nella scheda che il membro è già in
Mail nel pannello di lettura apre ogni link in una nuova scheda, che è giusto per il internet intero e sbagliato per noi. Le nostre email di notifica sono quasi interamente link di nuovo nell'app, e l'invio a una scheda fresca butta via la sessione membro già ha e li fa sedere attraverso un avvio freddo per raggiungere una pagina questa scheda potrebbe rendere immediatamente. Stessa origine è l'intero test — byte-equal con la pagina che rende l'e-mail, non "si sente come un dominio Kamo" e non un suffisso sul dominio dell'org. Questo è esattamente il set di URL la sessione corrente è valida per, che è l'intero ragione di rimanere in questa scheda, ed è in-spoofable per posta: ospite di un altro inquilino, un lookalike, o userinfo che indica l'host altrove sono tutte origini diverse e Vai a una nuova scheda come qualsiasi altro estraneo. Routing piuttosto che assegnare la posizione: un documento pieno carico lacrime giù il React albero e distrugge ogni finestra di strumento aperto insieme a qualsiasi progetto insente, motivo per cui questo repo ha un cancello di costruzione contro la navigazione interna dura. Modificato e medio clic sono lasciati soli — quel gesto è l'utente che chiede deliberatamente una nuova scheda. Ascoltare dal genitore funziona perché la sandbox già mantiene consent-same-origin per la misurazione dell'altezza durante l'omissione di permessi-scritti, quindi niente dall'e-mail si corre sempre; il gestore è nostro, attaccato dall'esterno. Una trappola che vale la pena di registrare, catturata dal proprio test: restituire un percorso piuttosto che un URL è necessario ma NON sufficiente. "https://host//evil.test" è veramente stesso orientamento, e il suo nome di percorso è "//evil.test" — che il router legge come protocollo-relativo e segue off-site. Un reindirizzamento aperto e-mail che passa un controllo di origine. Tale collegamento ora passa a una nuova scheda, dove il browser risolve il reale, innocuo URL, piuttosto che essere silenziosamente riscritto da qualche parte l'e-mail non ha indicato.
· kamo-shared-library
Confidenza di estrazione per celle + comprovatanza di eleggibilità
Due lacune nel modello di fiducia, sia sul percorso del denaro. PER-CELL. Un pavimento a foglio è il grano sbagliato per giudicare un prezzo: un singolo male-read coupon trascina tutto il foglio giù e invierebbe ogni altro prezzo su di esso a un umano, mentre un piano alto non dice nulla sulla cella specifica un mutuatario è stato effettivamente citato da. E' il momento giusto. registra quanto bene Questo tasso / prezzo / riga di blocco è stato letto, quindi un coupon sbagliato costa solo il suo prezzo. PROVENZIONE ELIGIBILITA'. Ammissibilità Matrix non aveva traccia di un documento sorgente tutto -- il suo unico percorso era una linea guida NULLABLEVersionId, quindi una matrice promossa potrebbe non essere tracciato al foglio da cui è venuto. L'ammissibilità decide se un prestito è che lo rende esattamente dove deve essere spiegabile una negazione; una provenienza c'è un divario al punto che un mutuatario è detto no. Ora porta fonteDocumentId e lo stesso contratto di fiducia di estrazione come la colonna vertebrale del foglio di calcolo. Sia nulla che nullo significa NO SIGNALE piuttosto che buono. Colonne applicate al prod CockroachDB via KamoInitializer.
· kamo-signer-monorepo
Mostra il telefono dell'agente, senza pedaggio ed e-mail sulla copia del cliente
· kamo-internal
Mostra il telefono dell'agente, senza pedaggio ed e-mail in anteprima
Ognuno sulla propria linea: un run-on di due numeri e un indirizzo si legge come una stringa e si fa male.
· SecurityService
Risolvere i dati di contatto pubblicati dall'agente
L'email viene dalla casella di posta principale del membro piuttosto che TeamMember.emailWork — che colonna è testo gratuito predating mailbox provisioning e di routine non concorda con il l'indirizzo da cui realmente mandano. Resta il fallback quando nessuna casella di posta è primaria. Un membro abilitato a VOIP con un'estensione è pubblicato come la linea org più che estensione, offrendo il numero senza pedaggio anche quando l'org ha uno. Senza un'estensione, il loro telefono di lavoro — l'unico numero che tengono che è destinato a essere pubblicato. Un handicap VOIP config non pubblica nulla, perché tale estensione non risponde. Ogni ricerca è difensiva: non è possibile rendere un documento rivolto al cliente perché una casella di posta riga mancante sarebbe peggio di stampare un modo in meno per fare il contatto.
· kamo-shared-library
Pubblicare ogni modo per raggiungere l'agente, non solo un nome
Nome e titolo da solo dire a un cliente che possiede la citazione, ma non come chiedere su di esso. Il blocco ora trasporta e-mail e telefono, risolto dal chiamante — la casella di posta primaria vive nello schema del provider di posta elettronica e l'estensione nella configurazione VOIP, né raggiungibile da un helper statico. Linea principale e senza pedaggio sono campi separati in modo che il documento possa stampare entrambi: la scelta tra loro sta scegliendo che cosa la chiamata costa il cliente. lineWithExtension coppie un'estensione con la linea org che si blocca, e restituisce null se Manca la metà. Un orfano "ext. 204" non è un numero che chiunque può chiamare, e un nudo Org line avrebbe errato il cliente al centralino come se fosse l'agente.
· kamo-shared-library
Confidenza di estrazione sulla colonna vertebrale del catalogo + base di auto sign-off
Un prezzo è solo come affidabile come il foglio da cui è stato assemblato, e niente su il percorso dei prezzi ha portato quel segnale: la fiducia esisteva SOLO sulla pre-promozione righe di staging (MlosExtractedCandidate) che nessun codice di produzione legge. E' il WEAKEST per la fiducia nel campo. attraverso i candidati approvati promossi nella versione -- minimo, mai cattivo. Un foglio di 200 celle pulite e una media di 0,20 misread a 0,96 e sarebbe chiaro qualsiasi soglia, mentre quella cella è quella che misprezzi un prestito. Null significa NO SIGNAL (autorizzato a mano o promosso prima dell'esistenza della colonna) e i consumatori devono rifiutare l'approvazione automatica su di esso; la lettura come assente è come un prezzo non verificato esce inosservato. WORM-protetto come le altre colonne a prezzi -- istantanee in @PostLoad e diffed in @PreUpdate, o sarebbe un buco silenziosamente mutabile in la catena di audit. MlosPricingSignoff ottiene automatizzato / base confidence / soglia applied. Senza loro un'approvazione della macchina è indistinguibile da una persona umana tranne che da un membro id, che sul percorso del denaro è un difetto di audit. La soglia è memorizzata accanto al valore perché la configurazione si muove: 'la fiducia era 0.72' non può essere giudicata più tardi senza sapere se il bar quel giorno era 0.70 o 0.90. Colonne applicate al prod CockroachDB via KamoInitializer.
· kamo-internal
Quotazioni scheda — ogni citazione attraverso i lead del conto
A server-paged DataGrid accanto a Leads, con ricerca su numero e titolo, stato, totale gamma e gamma data creata. I filtri condividono una debunce e collassano in una singola query, quindi regolando un intervallo mentre La digitazione costa una andata e ritorno piuttosto che una per tasto per campo. Qualsiasi cambiamento di filtro reimposta a pagina 1 — rimanendo a pagina 6 di un set di risultati più stretti legge come griglia vuota. La scadenza è mostrata a parte lo stato: un preventivo SENT può essere superato la sua data di validità e ancora leggere come SENT, così quelle righe portano il proprio marcatore invece di essere silenziosamente Mislabelled. Le righe e la colonna di piombo si dirigono entrambe nella scheda delle citazioni del piombo.
· SecurityService
GET /quotes/account/{uid} — paged, filtered, ordinati
Le date arrivano come stringhe ISO e parse leniently: un filtro che il cliente invia male dovrebbe restringere nulla piuttosto che 400 l'intera griglia. Un solo appuntamento "a" copre l'intero quel giorno, altrimenti filtrare a oggi non restituisce nulla sollevato da mezzanotte.

Il tuo universo ti sta aspettando.
Aggiungi i prodotti quando sei pronto: nessuna migrazione, nessun nuovo login, nessuna richiesta di pagamento.
Gratis per sempre — nessuna carta di credito richiesta