Changelog
Cosa c'è di nuovo.
Ogni miglioramento alla piattaforma, come si spedisce.
· kamo-shared-library
Aggiungere un controllo globale dell'alias-uniqueness
E questo e'... era giusto mentre un alias era stato risolto solo (security provider id, alias) — la coppia dietro un <alias>. ospite. Due org sotto diversi fornitori potrebbero condividere uno senza ambiguità. Un'organizzazione può ora essere nominata direttamente dal suo alias, con ?org=, e che la ricerca non ha alcun fornitore di scopo. Un alias ripetuto fa riferimento ambiguo piattaforma-wide, quindi OrgResolutionService lo rifiuta piuttosto che scegliere uno — che lascia le organizzazioni BOTH irraggiungibili Alias. La tenuta dal vivo contiene già ripetizioni (quattro org condividono "acme-corp", due parti "kia-kaha"), motivo per cui il risolutore rifiuta; questo è ciò che smette di essere creato. escludereId permette ad un'organizzazione di mantenere il proprio alias mentre altri campi sono salvato; passare null quando si crea. 1597 passano i test.
· KlusterServices
Invia un bug report nel codice VS aperto di salvia
URI handler che crea una conversazione in una finestra RUNNING e pre-fill l'ingresso, che è l'unico modo per raggiungere il saggio dell'editor ha già aperto invece di iniziare un secondo accanto a esso. L'URI porta un prompt e nient'altro, quindi modello e sforzo passano attraverso coderProcessWrapper — un wrapper che mappa un enum convalidato a bandiere fisse e non prende mai argv dal file, quindi un file forgiato può scegliere un diverso modello e niente di più. L'URI pre-riempimento ma non si presenta, quindi la presentazione è un ritorno sintetico e l'agente quindi aspetta una trascrizione prima di segnalare il successo. The ago che cerca è JSON-escaped: trascrizioni sono JSONL, e la ricerca per le novità crude segnare ogni messaggio consegnato come fallito.
· InitializerService
Creare le tabelle System Bugs
Nessun vincolo CHECK sulle colonne ordinali: support ticket status check is che cosa succede quando Hibernate congela uno alla dimensione di un enum e l'enum allora cresce, e SystemBugStatusType si aspetta di crescere. Applicato a yb-tserver:5433 a mano pure, dal momento che questo servizio non ha CI.
· kamo-internal
Gestire un dominio personalizzato dalle impostazioni aziendali
Un dominio usato per essere la porta d'ingresso in un'organizzazione: DNS doveva essere configurato prima che qualcuno potesse firmare a tutti, motivo per cui /setup/dns era raggiunto dal wizard org-creation e da un banner "necess setup" piuttosto che dalle impostazioni. Ora è facoltativo, quindi appartiene al resto del bianco-labelling. Aggiunge una sezione Dominio personalizzato a Impostazioni → Essenziale → Comportamento & Etichettazione, tra Informazioni Aziendali e Branding. Mostra il dominio corrente dell'org, o dice chiaramente che non c'è uno, e si apre /setup/dns per aggiungere o cambiare Ecco. Il pulsante legge "Set up a domain" o "Manage domain" a seconda del che di quelli è. La copia conduce con lo spazio di lavoro già raggiungibile a kamocrm.com, così un proprietario che non vuole il proprio indirizzo sa che questo è qualcosa che può ignorare. Utilizza orgIdStr piuttosto che organizzati.id: quel campo è un numero JS e int64 org ids passato MAX SAFE INTEGER, che aprirebbe il DNS pagina per un'organizzazione diversa. Il pulsante è disattivato fino a quando non è risolve piuttosto che navigare con un id sbagliato. Copia atterrata in kamo-traduzione-dizionario prima (98eee7cb6) — CI clona non piegato a immagine-build time, quindi app-first è una costruzione rossa. 2921 test e tutte le 8 guardie passano, i18n incluso.
· SecurityService
Invia l'albero anche sul punto finale dell'app-catalogo
Branch Types → App consentite costruisce il suo editor da /features/app-catalog, non dalle caratteristiche inquilino, quindi era l'unica superficie che poteva non vedere quali app sono bambini di cui. Un genitore decide cosa ne è i bambini possono anche essere offerti, così l'editor ha bisogno della stessa forma. 947 test verdi.
· kamo-internal
Render l'albero dell'app, e confermare ciò che un genitore passa
I bambini ora siedono sotto il loro genitore nella lista Apps & Features, ciascuno con il proprio interruttore. La nidificazione è espressa da ORDER più un indent piuttosto che da un contenitore nidificato, in modo che la scheda esistente, il movimento e il codice gating è intoccato — e un bambino il cui genitore è filtrato dalla scheda corrente rende ancora al livello superiore piuttosto che svanire. Due comportamenti che la cascata ha reso necessario: Toggling ora applica l'intera lista di funzionalità del server invece di flipping la riga cliccata. Abilitare un bambino accendere e disabilitare un genitore il genitore cambia ogni bambino fuori, quindi un aggiornamento locale single-row lascerebbe l'interfaccia utente in disaccordo con il server su tutto il resto la cascata toccata. Entrambi gli endpoint hanno già restituito l'elenco completo; niente di nuovo era necessario lato server. Il vecchio sentiero single-row rimane come un fallback per un Sicurezza Servizio che non invia nessuna lista. Cambiare un genitore fuori chiede in primo luogo, nominando i bambini che prenderà con E' cosi'. Questa e' un'azione piu' grande di quella che sembra. L'albero proviene dalle caratteristiche endpoint piuttosto che da uno specchio TypeScript di ServiceType — quelli hanno già derivato da Java due volte, e server è l'unico posto che conosce il catalogo dell'operatore. 203 file di prova / 2921 test verdi, costruire pulito.
· InitializerService
Prezzo il commercio bambini e la EHR, con una guardia totalitaria
Né la mappa dei prezzi ha avuto un controllo della totalità, e le due modalità di fallimento sono diverso ed entrambi silenziosi. Mancando da CATALOG, un'app non atterra da nessuna parte la matrice dei prezzi. Mancando da TIERS, ogni tier scrive is included=false — un DENIALE esplicito che il diritto fallito non è aperto NON salvare, perché fail-open si applica solo quando un piano non ha fila affatto e questo ne scrive uno. Quindi un'app dimenticata è o dato via a tutti o lucchetto per tutti, e che uno dipende da un dettaglio nessuno si ricorda all'epoca. I cinque verticali di commercio siedono esattamente sulla rung di POS. Loro non erano purchasable separatamente prima dell'albero dell'app — qualsiasi cosa un org che tiene POS potrebbe fare, potrebbe fare — così mettendoli altrove sarebbe retroattivamente carica per la capacità che le persone hanno già. Un perno di prova che a POS piuttosto che a un POS letterale, così commovente li muove. EHR è negoziata come i sistemi di provenienza: no list price, CUSTOM pianifica solo, assente da ogni livello. Imbarcazione di un cliente PHI significa una BAA e una revisione di conformità, non un checkout self-serve. 159 test verdi.
· kamo-marketing
Smettere di contare le visite di un membro al proprio link
Un membro che controlla che il proprio link di tracciamento funziona è il primo più comune visitare qualsiasi codice mai ottiene, ed è peggio che inutile: gonfia il numero sono misurati su, è indistinguibile da una prospettiva reale nel visitatore cassetto, e li lascia in possesso di un cookie visitatore permanente così ogni visita successiva conta di nuovo. Un codice che mostra tre visite, tutte la persona che l'ha coniato, è un record di pagamento con una bugia in esso. Questo sito è l'unica origine che può rispondere alla domanda — non ha sessione e nessun membro, e la console interna non può impostare un cookie su www. <apex> da interno. Che cosa attraversa quel divario è un collegamento: arrivare con ?mrt self=1 mints l'esclusione, e da allora sul browser è escluso su ogni visita se o non c'è il parametro. La scheda Codici di monitoraggio interno aumenta il controllo che produce quel link. Non è sufficiente la coniazione, quindi una visita esclusa DELETES il visitatore e la fonte Anche i biscotti. Un membro che ha previsto il loro collegamento prima di questo spedito, o che ha optato fuori su un browser già contato, sta ancora portando un GUID — e il beacon incendi per chiunque ne abbia uno. Senza l'eliminazione l'opt-out avrebbe mai funzionato per le persone che non ne avevano bisogno. L'esclusione non è volutamente in TRACKING COOKIES: questo set è ciò che il cookie policy's delete control clears, e spazzare un opt-OUT via con gli identificatori sarebbe silenziosamente re-enrol l'unico visitatore che ha chiesto di non essere contato — sul controllo molto probabilmente essere premuto da esattamente quella persona. check-discount-cookie cresce da 50 assegni a 72, coprendo il valore corrispondente, il ortografia dei parametri, l'esclusione di delete-set e i due invarianti di sorgente (middleware deve eliminare l'identità, il faro deve cauzione localmente perché middleware funziona su richieste di documenti mentre il faro funziona su ogni lato client navigazione).
· kamo-internal
Il widget della filiale vendite Scoreboard
Una colonna di griglia larga e alta due righe: le dieci persone nell'organizzazione portare di più attraverso i loro collegamenti di tracciamento, oggi, questo mese, questo anno o tutto il tempo, apertura su visitatori questo mese. Il riordine è la funzione. Ogni altro modo di mostrare quattro metriche su quattro finestre sono sedici liste separate; questa è una lista che si riorganizza. Righe sono keyed da membro e portare un'animazione di layout, in modo da commutare le diapositive di categoria le stesse persone si passano l'un l'altro piuttosto che svuotare il pannello — e il gancio mantiene ogni scheda che ha recuperato, quindi la stampa che attiva l'animazione ha dati da animare sulla stessa cornice. I selettori sono memorizzati contro il PROFILE del membro, non localStorage, quindi il bordo qualcuno allestito alla loro scrivania è il bordo che ottengono sul loro computer portatile. Entrambi i lettori degradano un valore non riconosciuto al default piuttosto che rifiutarlo — il server applica la regola identica e riecheggia quello che ha usato, quindi un la preferenza scritta da un vecchio edificio non può mai lasciare un membro su un pannello vuoto. Un membro al di fuori della top ten ottiene la propria linea pinned sotto la lista, e un membro con nulla lo ottiene ancora al rango zero. Una tavola che può solo dire "sei non su di esso" è l'unica cosa che un tabellone non deve fare alla gente che esiste motivare. Anche qui, perché è lo stesso problema dall'altra parte: un controllo "preview" sul Tracking Codes scheda che apre un link SENZA contare la visita. Controllo dove il tuo link terra è il più comune prima visita qualsiasi codice mai ottiene, e contare che mette i propri clic del membro nel record su cui vengono misurati. costruireTracking PreviewLink è il vero link più il marcatore del sito di marketing legge — un contratto cross-repo, quindi il test indica il nome del parametro.
· MediaService
Il punto finale delle vendite di affiliazione
Risponde a questo. L'organizzazione e' la top ten per una metrica su una finestra, piu' il chiamante e' proprio. in piedi quando non ci sono sopra. I contatori su MARKETING TRACKING CODES non possono sostenere questo: sono tutti i tempi roll-up senza dimensione del tempo, e non c'è nulla in un totale in esecuzione che dice quando è successo qualcosa. La verità vetrata è nelle file — Per l'acquisizione, e ogni conversione e' propria. * A timbro su MARKETING VISITORS. Ed è anche per questo che All Time concorda con il codice griglia: stessi dati, contati allo stesso modo, non una seconda contabilità. Quattro decisioni degne di nota: - L'acquisizione è datata dal contatto PRIMO, non dall'attività. Contare i visitatori che erano semplicemente attivi nella finestra ri-crediterebbe la stessa persona ogni mese, così il lavoro fatto una volta che il lavoro out-rank fatto ora, per sempre. - Le conversioni sono attribuite solo aTrackingCodeId. Quella colonna congela la momento in cui esiste un account; lastTrackingCodeId continua a muoversi, e la classifica fuori di esso avrebbe lasciato un membro prendere la conversione chiusa di un altro con un clic aggiuntivo. - La finestra è tagliata nel calendario di MEMBER e confrontata in UTC. "Questo mese" differisce fino a un giorno a seconda del cui calendario viene chiesto, e ogni colonna in questo schema è un localDateTime senza offset che significa UTC. - Tutte e quattro le metriche sono calcolate a richiesta, quindi la categoria di commutazione è un istante re-sort di righe già sullo schermo piuttosto che un girante. Gated su AFFILIATE SCOREBOARD WIDGET e nient'altro — vedere il proprio diritto Javadoc. Non acquista alcun trapano: non c'è nessun codice id, nessun id visitatori e nessun membro parametro sul endpoint, e ogni endpoint analytics by-id continua a passare MarketingTrackingScopeResolver invariato. Le quattro aggregazioni usano la virgola-join piuttosto che JOIN.ON perché né chiave estera è un'associazione JPA e la forma di virgola parses su ogni Hibernate versione la flotta corre — MarketingTrackingHqlTest ora parses tutti e quattro contro modello di entità reale su 6.2.13, e pin l'acquisizione, attribuzione e inquilino predica che falliscono silenziosamente se si allontanano.
· kamo-shared-library
Aggiungi i diritti dei widget della filiale Vendite Scoreboard
Cinque diritti, 234-238, nel gruppo MARKETING e appeso fuori VIEW REFERRAL CODES: una classifica dei numeri di riferimento è dati di codice di monitoraggio e un membro che può non vedere il Tracking Codes scheda non ha alcun business su di esso. Il diritto del widget — non VIEW ALL MEMBER TRACKING — è ciò che rivela altri Il totale dei membri e' deliberato. 200/201 cancello gli ANALITICI: il cui visitatori, i cui indirizzi, i cui termini, risolti per membro di destinazione oggetto risolutore. Un tabellone rivela un nome, un grado e un numero, a tutti insieme. Richiedere il diritto di analisi a livello org lo lascerebbe visibile solo agli amministratori che ne tengono uno, che è un rapporto piuttosto che una scheda. Deliberatamente NON seme, per lo stesso motivo 200 e 201 non sono: la semina sarebbe pubblicare tutti i totali di riferimento dei titolari esistenti ai loro colleghi sul boot che lo ha spedito, che è una decisione di divulgazione presa da un dispiegamento piuttosto che da un amministratore. 229/166/32/31 nella forma controllata — tutti e cinque sono bambini e VIEW REFERRAL CODES è di per sé un bambino di VIEW MARKETING, così né secchio radice si muove.
· kamo-login
Marca e applica la schermata di accesso con ?org=
login.kamocrm.com è lo schermo di accesso per ogni organizzazione che non ha l'ospite del proprio, così il nome host non può dire quale si sta firmando. Traduzione: fa: nomina l'organizzazione da id o alias, seleziona il branding a dipingere, e viaggiare con le credenziali come l'organizzazione sono contro. Un suggerimento, non una credenziale. L'appartenenza è comprovata lato server, quindi nominare un'organizzazione che non appartieni a ottenere lo stesso rifiuto di nominare Una finzione. Il peggio un riferimento sbagliato raggiunge è il logo sbagliato su una pagina pubblica. Branding proviene da SecurityService's /org/public/{ref}, relè piuttosto che reimplementato in modo che la regola su ciò che un riferimento indovinabile può esporre rimane in un posto. La base tematica segue quindi l'organizzazione invece di l'host, usato come indicato: lasciare la sua prima etichetta come il percorso host fa trasformerebbe acme.kamocrm.com in kamocrm.com e dipingere un inquilino in il branding della piattaforma. La prima vernice utilizza necessariamente il branding dell'ospite, perché il riferimento non è stato ancora risolto; KAMO THEME REAPPLY si rivernicia una volta che lo è. Una visita che non nomina alcuna organizzazione cancella qualsiasi riferimento una precedente visita registrata in questa scheda, così lo schermo della piattaforma non tiene qualcun altro logo.
· SecurityService
Servire il marchio di un org a uno schermo senza sessione
Login, registrazione e la sala riunioni dipingono i colori di un'organizzazione prima che esista una sessione. Sapevano quale dal nome dell'ospite, che è esattamente ciò che un'organizzazione senza un nome host del proprio non può fornire. GET /api/security/org/public/{ref} trasforma un esplicito riferimento ?org= l'id come cifre, o l'alias -- in qualcosa da dipingere. Marca SOLO: nome, logo, colori, locale, tema host. Non caratteristiche, non diritti, non domini, non membri. Il primo tentativo di questo riusò il record di organizzazione proiettato e il ratchet endpoint lo ha rifiutato, correttamente -- gli alias sono indovinabili, quindi un record completo dietro un indovinabile riferimento enumera la configurazione di ogni organizzazione. Ciò che rimane è quello che qualcuno che apre la pagina di firma di quell'organizzazione vede già, e testare i campi esclusi individualmente in modo da rimettere uno indietro deve essere deliberato. Solo per leggere. L'applicazione sibling /org/domain/{host} auto-provisions come una effetto collaterale; un endpoint raggiungibile indovinando non deve scrivere. Un riferimento sconosciuto 404s, e così fa un alias ambiguo -- diPublicRef già rifiuta una denominazione diversi org, e questo indica che il rifiuto raggiunge il chiamante come "nessuna tale organizzazione" piuttosto che come qualcun altro branding. Il chiamante torna alla piattaforma. temaDomain porta lo stesso valore la shell e l'uso del provvisto di tema, così una schermata di pre-sessione legge il marchio di un org dalla cartella era effettivamente scritto a -- {alias}.kamocrm.com per un org senza dominio. Passo 947 test.
· MediaService
Rifiutare la registrazione di riunione per un inquilino che gestisce PHI
La capacità di divisione ha permesso ServiceType.MEET — una conferenza dal vivo su self-hosted Meet non lascia mai il cluster — mentre MEETING RECORDING rimasto bloccato, perché questa pipeline posta l'intera trascrizione al router AI multiprovider e poi e-mail il risultato ad ogni partecipante compresi gli ospiti esterni. E' una copia di terze parti inaffidabile. La guardia si trova nell'ingestione privata() entrambi i punti di ingresso pubblici imbuto attraverso, così si rifiuta prima che qualsiasi byte raggiunga MinIO, e ancora in cima di runSummaryPipeline, che è pubblico e @Async. 200 test verdi.
· VOIPService
La capacità bloccata ora supera la destra e la modalità dio
PhiPlaybackAuthorizer ha portato la riproduzione delle chiamate PHI su VIEW CALL LOGS. Quello era l'intero controllo perché ServiceType.VOIP mappato al bloccato Modulo VOIP RECORDING, quindi un inquilino PHI non poteva avere il sistema telefonico al Tutto e questa classe non ha mai corso per uno. La capacità divisa ha permesso VOIP stesso — la gestione delle chiamate è incluster — mentre VOIP RECORDING è rimasto BLOCKED PENDING WORK per motivi che non hanno cambiato: la riproduzione non esegue alcun controllo di sdoganamento, trascrizioni cavalcare in metadati, e non c'è traccia di ritenzione o cancellazione. Da solo che sarebbe stato un indebolimento diritto: un inquilino PHI potrebbe consentire sistema telefonico e, con un diritto concesso, giocare tutto. Così PhiCapability La Guardia gestisce il PRIMO e il suo rifiuto è definitivo. Non ci vuole parametro chiamante, quindi per un inquilino PHI nessun diritto lo solleva — e né fa una sessione di dio attiva, che è la proprietà che più conta. E' ora di andare. E' il momento giusto. Tre test hanno codificato il vecchio contratto e sono riscritti piuttosto che cancellato, ogni registrazione perché l'aspettativa ha capovolto. Il messaggio di rifiuto della segreteria non incolpa più il diritto mancante, che di solito non è più la ragione. 152 test verdi.
· SecurityService
Servire l'albero dell'app e fissare l'auto non custodito
FeatureController ora invia genitore/bambini/isParent su ogni serializzato app in modo che il cliente possa rendere i bambini nidificato sotto il loro genitore. Servito piuttosto che rispecchiare in TypeScript di proposito: gli specchi ServiceType hanno derivato da Java due volte già e una seconda copia dell'albero sarebbe essere un'altra cosa alla deriva. Nessuna nuova forma di risposta era necessaria per la cascata — /enable e /disable già ri-serializzare l'intera lista di funzionalità, così i cambiamenti di colpo arrivano senza che il cliente riconciliasse qualcosa. Entrambi i loop di auto-provisione ora saltare un bambino il cui genitore la piattaforma fa non offrire, dal momento che abilita le cascate della natura il genitore su e altrimenti accendere un genitore nessuno pubblicato. Il loop di OrganizationController non aveva controllo PHI mentre FeatureController's l'equivalente ha fatto, su un punto finale pubblico non autorizzato. Per un inquilino PHI primo modulo bloccato lanciato, la cattura circostante ha registrato un avviso, e efficaciCaratteristiche silenziosamente cadde a file grezze senza modello applicato proiezione. Ora chiede piuttosto che cattura, esattamente come le caratteristiche La lista lo fa. /api/security/ehr registrato. I prefissi commerciali rimangono mappati a POS: Il genitore e' il cancello piu' grosso e... già perfeziona per mercato. 938 test verdi.
· kamo-internal
Specchio le sei app per bambini di commercio, e fermare gli specchi alla deriva
Entrambi gli specchi TS sono ora GENERATED di ServiceType.java piuttosto che mantenute a mano. Si erano allontanati male da Java e l'uno dall'altro: stale "integrazione RAG" e "Ritorna all'organizzazione madre SMTP" copia che Java rimosso nel 2026-08, POS contrassegnato PLANNING in ServiceType.ts mentre AppType.ts ha detto COMPLETATO, e descrizioni diverse per lo stesso app in ogni file. Vale la pena sapere prima di fidarsi di loro: displayName e descrizione qui sono letto da NOTHING. Ogni consumatore utilizza queste costanti solo per l'identità — haFeature() partite su id, appServiceIcons chiavi sul nome. Copia viene da GET /api/security/features, che onora i override PLATFORM APP CATALOG gli specchi non possono vedere. Le stringhe sono sincronizzate in modo che un lettore non sia ingannato, e l'intestazione del file dice di non iniziare a renderli. Il genitore/albero del bambino non è volutamente rispecchiato — le caratteristiche endpoint invia genitori/bambini/isParent per app, e una seconda copia della la forma sarebbe un'altra cosa da vagare. Inoltre: un'icona per nuova verticale piuttosto che prendere in prestito il genitore storefront, e OMS, tra cui Ehr.
· kamo-internal
Una barra di scorrimento, tre volte più ampia, giù ogni conversazione
La barra accanto ai messaggi era 8px nella chat dei membri e SMS, 6px nei biglietti di supporto e social, e nella chat AI non era affatto nulla: quella lista ha dichiarato `scrollbar-width` inline accanto alla sua:-webkit-scrollbar regole, e da Chrome 121 un `scrollbar-width` non-iniziale `scrollbar-color` rende il browser scartare l'intero -webkit- blocco — così ha chiesto 12px e Ho la linea di capelli della piattaforma. globals.css documenti che regolano per lo scorrimento della pagina; niente ha fatto rispettare, e la chat AI era stato tranquillamente sul lato sbagliato di esso. Tutti e cinque ora leggono una costante di CHAT SCROLLBAR: un 24px gutter — tre volte la chat dei membri larghezza vecchia — con il pollice inset 6px sia lato di un bordo trasparente, quindi una barra che larga ancora legge come una pillola in una tosse piuttosto che una lastra saldata al bordo della finestra. La chat di AI le proprietà standard si spostano in `@supports non selettore(:-webkit-scrollbar)`, che è dove globals.css li mette e l'unico posto Firefox li legge senza costare Chrome le sue regole. Il test è a livello sorgente perché il soggetto è un foglio di stile e jsdom non implementa barre di scorrimento: pins che nessuna finestra dimensiona la sua barra da un letterale, e che nessuno di loro imposta lo standard proprietà non custodita di nuovo.
· InitializerService
Estendere il nonno di mercato nel backfill dell'app-tree
nonnoEsistere GatedMarkets ha già camminato i mercati attivi di ogni org, risolto getRequiredService() e abilitato l'app mancante — era solo ristretto ad isGated() verticali, cioè MORTGAGE e PERSONAL LOANS. Ora che ogni CommerceType nomina la propria app bambino la restrizione è scomparsa, così gli stessi semi di loop RETAIL / RENTAL / SERVICE / SUBSCRIPTION / Remi di PROCURAZIONE. Non era necessaria nessuna nuova migrazione. Anche semi RETAIL per org che tengono POS senza mercati affatto, che aveva il commercio di lavoro prima dell'albero perché la vendita al dettaglio non aveva bisogno di app propria. Gli org di livello superiore non sono più saltati, e questa è la soluzione che conta. L'esenzione era giusta quando questo ha concesso solo due app di prestito — una piattaforma-operatore org non dovrebbe acquisire silenziosamente MLOS — ma è sbagliato per l'albero dell'app, perché èAppEffectivelyEnabled richiede un OrgFeature ROW e non ha esenzioni di alto livello. Il primo giro ha segnalato il successo mentre lasciando KamoCRM, che gestisce i mercati attivi di RETAIL e SUBSCRIPTION, con nessuna riga per entrambi; avrebbe perso sia da /commerce sul successivo Sicurezza Servizio di distribuzione. Catturato interrogando il database, non leggendo il registro. consentireFeature ora cascata un bambino al suo genitore, in modo da vedere un bambino anche garantisce la riga POS. RUN contro il prod 2026-08-24. Verificato: ogni riga di commercio attivo markets ora ha un bambino attivo OrgFeature fila, zero mancante.
· SecurityService
Inserimento-controllare ogni verticale, non solo il gated due
Ogni CommerceType ora nomina la propria app per bambini sotto POS, quindi la isGated() cortocircuito che ha waved retail, affitti, servizio, abbonamenti e l'approvvigionamento subito è andato. Questi mercati sono ora controllati contro la propria app — che è il punto: un verticale può essere venduto, e spento, da solo. Dipende dal KamoInitializer backfill dopo aver eseguito.
· kamo-internal
La ricerca della barra degli strumenti è un glifo che si apre in un campo
La chat dei membri ha portato una casella di ricerca permanente nella parte superiore della finestra — la larghezza della intera barra degli strumenti, speso su un controllo che è vuoto quasi tutto il tempo e utilizzato in esplosioni. Si'. è ora un ingrandimento 28px che si apre nel campo su clic, e la barra degli strumenti legge come quattro glifi e aria. L'idea è costruita intorno: il lente non si muove mai e non viene mai spazzato via. Collasso, il pillola è 28px e l'icona si trova al suo centro, 14px dal bordo sinistro; aperto, la pillola ha 6px di imbottitura prima di un'icona 16px, che mette il centro dell'icona a 14px di nuovo. La pillola cresce quindi fuori da dietro un glifo che tiene fermo, e il controllo legge come un oggetto apertura piuttosto che due stati attraversano. La larghezza molle con un po' di overshoot sulla strada aperta e nessuna sulla strada chiusa; il segnaposto percorre la crescita di 60ms così il testo arriva in una scatola che già ha spazio per esso; il controllo chiaro molle in solo una volta c'è qualcosa da chiarire. La chiusura cancella sempre la query, e il campo non può essere messo via mentre ne tiene una: a l'ingrandimento crollato ancora filtrare la conversazione sarebbe la finestra mentire su quello che è mostra. Blur lo lascia aperto con il testo in esso, e un clic randagio sul glifo focalizza il campo piuttosto che scartare la ricerca. Il movimento ridotto è onorato in tutto, il glifo porta l'anello di messa a fuoco per una scheda così come un click, e il 50px distanziatore la vecchia scatola necessaria è andato con esso.
· kamo-shared-library
Ogni verticale nomina la propria app per bambini
richiestoServizio diventa totale ora che ogni verticale ha un'app per bambini sotto ServiceType.POS, che elimina il "null significa POS da solo è sufficiente" caso speciale e isGated() con esso. Ogni verticale può ora essere acceso, licenziata e classificata indipendentemente — che è ciò che lascia un medico pratica utilizzare il commercio a tutti, dal momento che RETAIL rimane bloccato per un inquilino PHI mentre il resto del commercio non lo fa. HEALTHCARE (ServiceType.EHR) è ciò che fa apparire la EHR # When the app is on, with no new gating # codice: assertCommerceTypeAllowed, getAllowedCommerceTypes, the endpoint di tipo permesso dietro entrambi i raccoglitori di mercato, E la lista di mercato filtra tutto. già risolvi tramite getRequiredService(). NON PUSHED fino a quando il KamoInitializer backfill è in esecuzione. assertCommerceTypeAllowed legge getRequiredService() direttamente, così il momento SecurityService vede questo ogni org con un mercato di vendita al dettaglio o noleggio ha bisogno di un bambino OrgFeature fila fa Non l'ho ancora fatto.
· MediaService
Lasciate che la configurazione pubblica sia indirizzata da org id, non solo da host
Il sidecar dell'incontro cerca una configurazione di org per decidere se un ospite ha bisogno di una password di riunione. Ha chiesto da hostname, che sull'incontro condiviso host si risolve all'organizzazione della piattaforma -- quindi un inquilino che richiede un password avrebbe avuto quel requisito silenziosamente caduto per ogni ospite unire tramite link. Un ospite non ha sessione, quindi l'unica cosa che nomina il loro inquilino è la stanza, che ora porta un org token. Pertanto, entrambi gli endpoint pubblici accettano un esplicita orgId e tornare alla ricerca di dominio quando nessuno è dato, quindi nulla che chiede da ospite oggi cambia. Ids arrivano come cifre e sono parsed come un lungo: sono passati 2^53, quindi uno ha percorso attraverso un doppio nome un'organizzazione diversa. 200 test passano.
· kamo-shared-library
Funzione Cascade abilita/disattiva dall'albero app
Abilitare un bambino abilita il genitore; disabilitare un genitore disabilita ogni bambino; disabilitare l'ultimo bambino disabilita il genitore. Abilitare un genitore lascia i bambini da soli, quindi un genitore acceso deliberatamente senza i bambini rimangono. La cascata è guidata da eventi e non c'è volutamente alcuna riconciliazione spazzare. Questa è una proprietà di sicurezza: un "genitore" continuamente rinforzato nessun bambino abilitato è spento" invariante avrebbe spento il commercio per ogni esistente org il momento in cui questo spedito, perché non esistono file di bambini fino a il KamoInitializer funziona. Un bambino afferma la guardia PHI sul modulo del suo genitore così come il suo, prima di qualsiasi scrittura — altrimenti un bambino permesso sotto un genitore bloccato o sarebbe bloccato o sarebbe silenziosamente attivare il genitore bloccato.
· kamo-shared-library
Aggiungi PhiCapabilityGuard per i controlli delle capacità di chiamata
PhiTenantGuard corre a tempo reale e nessun altro — tre chiamate siti, tutti in FeatureService. Ora che PhiModule è dotato di capacità, un app il cui nucleo è consentito può ancora contenere una capacità che non è, e il controllo del tempo di attivazione non può vedere la differenza. Stessa firma deliberata di PhiTenantGuard: una pura funzione (status, modulo) senza caller, giusto o parametro God-mode, quindi c'è niente da elevare. Classe di pianura piuttosto che uno stereotipo — un condiviso-lib @Component bisogno di un repository non registrato ha crash-looped servizi quattro tempi; ogni servizio di consumo dichiara il proprio @Bean. NOTA: MEETING RECORDING e VOIP RECORDING sono ora incompleti da qualsiasi ServiceType ma non ancora sorvegliato nei loro siti di chiamata — questo è il prossimo commit toccare MediaService e VOIPService. Questo è inerte nella produzione nel frattempo perché ogni org attualmente ha manigliePhi=false, ma la chiamata i siti devono atterrare prima che qualsiasi inquilino sia segnalato.
· kamo-shared-library
Split PhiModule da app-grained a funzionalità-grained
POS mappato a ECOMMERCE SYNC (BLOCKED NO BAA) e all'intero commercio superficie appende fuori POS, quindi un org con maniciPhi=true non poteva cambiare commercio su tutti — nessun mercato, no /commerce, e nessun verticale clinica costruito su di esso. I dieci adattatori di sincronizzazione al dettaglio sono la cosa che lascia la confine, non la spina dorsale del commercio. POS ora mappa a COMMERCE CORE (permesso) e ECOMMERCE SYNC spazi in basso al bambino RETAIL. Stessa correzione per MEET (conferenza live vs pipeline di sintesi che le mail trascritte a inferenza esterna) e VOIP (telefonia vs registrazioni memorizzate e trascrizioni). MEETING RECORDING e VOIP RECORDING ora non ha alcun ServiceType affatto: sono capacità raggiungibile solo attraverso il codice, e il javadoc di PhiModule ora afferma la regola che tale costante MUST essere controllato da PhiCapabilityGuard alla sua chiamata sito. Riclassificare un'app come consentito senza proteggere la capacità all'interno si legge come compiacente mentre non rafforza nulla. AI ASSISTANT e RAG SEARCH deliberatamente rimangono bloccati: KlusterServices ha embeddings, sussurro asr e traduzione ma nessun LLM generativo, e RAG condivide una collezione Qdrant isolata da un filtro payload. Spalato Sarebbe un cambio di carta. Due test hanno codificato il modello inserito nell'app e sono aggiornati piuttosto che esteso: PhiServiceTypeMapping L'affermazione del test e' bloccata. FeatureServicePhiGateTest, il cui esemplare bloccato si sposta da VOIP a RETAIL — Il VOIP non è più confusabile al momento dell'attivazione.
· kamo-internal
Terreni all'interno di un org appena creato anziché in configurazione DNS
Creare un org ha indirizzato il creatore a /setup/dns, e la ragione era reale: entrare significava un reindirizzamento di OTK all'interno. org non ha DNS, in modo che l'host non fosse raggiungibile. Configurare DNS era quindi il prezzo di aprire la cosa che avevi appena fatto -- il muro esatto questo il lavoro rimuove. La chiamata di creazione già restituisce una chiave di una volta; il mago non aveva solo nulla per spenderla. Ora lo spende sull'origine attuale, dove la sessione chiama l'org, quindi non c'è un ospite da aspettare. Un dominio vale ancora la pena avere e Impostazioni è dove vive ora: un passo in bianco opzionale piuttosto che una porta. Avvertenze di marca sono trasportati in entrambi i modi, e se nessuna chiave ritorna l'org esiste ancora, così il mago atterra sulla vista immobiliare piuttosto che fallire. check-client-navigation correttamente contrassegnato il window.location.href. La sua nomi docstring /validate con una chiave di una volta come un autentico sessione-boundary salti, quindi questo è ammissibile con quella ragione piuttosto che lavorare intorno -- il router avrebbe mantenuto la sessione OLD del creatore, che è precisamente quello viene sostituito. 2857 test e tutte le 7 guardie passano.
· kamo-shared-library
Dare a ServiceType un genitore di due livelli / albero bambino
Sei bambini di commercio al nuovo ids 26-31, cablati a POS in un blocco statico su il modello RoleRightType (una costante di enum non può fare riferimento a un altro da i propri argomenti costruttori). MLOS e LOS reparent senza cambiare id, perché ORG FEATURES persiste il id tramite ServiceTypeConverter e in movimento si riassegna ogni riga esistente. La copia di POS diventa "Linee di Business" ora che i genitori prestano e record del paziente; il nome costante rimane POS perché E' il momento giusto. persiste i nomi come stringhe. Il vecchio descrizione si sposta a RETAIL, dove è accurato e già vetted da ServiceTypeCustomerCopyTest. Nuove costanti nave PLANNING/disabled così FeatureController.list ciclo di auto-provisione non può accenderli per ogni org. PhiServiceTypeMapping Il test fallisce dal design fino a quando la prossima commit mappa nuove costanti; entrambe la terra in una spinta.
· kamo-internal
Fare un org senza un dominio inseribile
Inserire un org significava passare a... Con nessun dominio che l'URL ha reso come E' il momento giusto. così Enter è stato disabilitato e proprietari sono stati indirizzati a /setup/dns invece -- DNS era la porta, non una opzione white-label. Lo spazio di lavoro si apre ora sull'origine CURRENT quando non c'è una consuetudine dal vivo host, e la sessione nomina l'org. Un inquilino con un dominio personalizzato funzionante si apre ancora sul proprio ospite, così nulla cambia per chiunque bianco-labelled. Un dominio personalizzato non finito viene trattato come nessun dominio: che HOST non può servire ancora, che non è lo stesso come l'org essere irraggiungibile. puòEnterOrg quindi chiavi sulla cosa che era sempre il reale precondizione -- un'iscrizione utilizzabile - piuttosto che su un dominio. necessitàDomainSetup e orgStatus deliberatamente continuano a segnalare un non finito dominio: non è più una barriera, ma è ancora uno straordinario compito, e la vista immobiliare è dove viene notato. Il guscio ha anche dovuto smettere di chiedere il nome host che org è in. La radice layout spogliato "interno" e ha guardato l'host up, che risponde "chi possiede questo nome" -- la stessa domanda di "che spazio di lavoro sono in" solo mentre ogni inquilino aveva un ospite a se stesso. Sull'host della piattaforma restituisce il proprietario della piattaforma, quindi un membro che entra in un org senza dominio sarebbe stato mostrato KamoCRM Inc. branding, caratteristiche e diritti. Ora chiede la prima sessione via /api/org/current e rientra all'host quando non c'è sessione, che mantiene il rendering della shell firmata. *** cookie è ciò che rende che raggiungibile a tutti -- sessioneStorage è invisibile a una richiesta di documento. 2857 test e tutti i 7 script di guardia passano.
· SecurityService
Lascia che il guscio prenda il suo org dalla sessione, non l'host
il layout radice di kamo-internal legge l'intestazione di Host, strisce "interne". fetches /org/domain/ <that>. Su un ospite che serve un inquilino che è lo stesso domanda come "che org sono"; su un host condiviso è una domanda diversa con una risposta sbagliata -- restituisce chi possiede l'host, quindi un membro che inserito un org senza domini sarebbe consegnato il marchio di KamoCRM Inc., caratteristiche e bandiere. Aggiunge GET /api/security/org/current, riportando il record proiettato per organizzazione la SESSIONE del chiamante è dentro. Il layout può raggiungerlo perché già inoltra l'intestazione Cookie e un *** cookie esiste, quindi il server può vedere una sessione anche se la sessioneStorage è invisibile a un documento richiesta. Non prende nessun parametro, deliberatamente. Il primo progetto ha accettato un id o un alias E poi... correttamente rifiutata: quella forma rende ogni record di org enumerabile indovinando un alias, e gli alias sono Indovina. La sessione sa già in che direzione si trova, quindi un parametro potrebbe chiederne solo una diversa. Con la sessione risolta ratchet passa da solo piuttosto che bisogno di una voce di base. La proiezione viene estratta piuttosto che copiata. Due endpoint che ritornano due forme dello stesso record è il fallimento che vale la pena prevenire -- un cliente non si può dire quale detiene, e si allontana la prima volta solo uno è aggiornato. Passo 928 test.

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