Changelog
Cosa c'è di nuovo.
Ogni miglioramento alla piattaforma, come si spedisce.
· kamo-internal
Mostra ogni categoria di storage separatamente
Registrazioni di chiamate, segreteria telefonica, allegati di chat, risorse HR e marketing materiali ogni ottenere la propria linea invece di condividere uno "Documenti e immagini" filare con tutto il resto che capita di vivere nella stessa tabella. Due conseguenze per la visuale. Quattordici categorie superano tavolozza categorica in grado di codificare — passato circa otto, aggiunto hues smettere di essere separabile per i lettori a colori e diventare decorazione — così l'identità è portato dall'etichetta e la barra delle azioni utilizza una tonalità puramente come un segno di grandezza. E la tendenza è ora una singola serie totale piuttosto che uno stack di quattordici vie: la domanda una risposta di tendenza è "è il mio storage crescente", e quale categoria crebbe in è il lavoro del tavolo, che il tavolo fa esattamente.
· kamo-shared-library
Contare ogni tipo di file memorizzato come propria categoria
Registrazioni di chiamate, segreteria telefonica, allegati di chat, risorse HR, marketing materiali, registrazioni di riunione e ogni tipo di documento sono tutte le righe di imgs, distinta solo da ImageAssocType e tutti condividono un secchio, perché l'associazione è metadati piuttosto che un confine di stoccaggio. Rapporti come un singola linea era accurata e inutile: per un'organizzazione di produzione si nascose che 5.08 GB di un'impronta di 5.09 GB era allegati di chat. Ogni associazione ora appartiene a un dominio, quindi un'organizzazione può vedere cosa è consumare effettivamente il suo deposito piuttosto che un totale indifferenziato. StorageDomainCoverageTest rende un'omissione un fallimento di costruzione: ogni ImageAssocType deve essere rivendicato da un dominio esatto — mezzi non reclamati sono memorizzati e mai fatturati, rivendicato due volte significa fatturato due volte. Verificato da sonda: rimuovere HR RESOURCE dal suo dominio fallisce la costruzione con la ragione, piuttosto che passare tranquillamente. MEETING RECORDINGS riassume sia le sue fonti, la tavola meeting recordings e la Associazione MEET RECORDING. MeetRecording Il servizio scrive solo il primo, quindi non si sovrappongono.
· EmailService
Misurare le caselle di posta KamoMail di ogni org
Cammina ogni mailbox Kamo ospita e registra, al giorno di arrivo, la RFC822 lo byte tiene — intestazioni, corpo e allegati codificati, in ogni cartella compreso Trash e Junk, dal momento che un messaggio nel cestino è ancora su disco. Un viaggio rotondo per cartella: il provider Jakarta Mail IMAP espande FetchProfile. Voce.ENVELOPE a ENVELOPE INTERNALDATE RFC822.SIZE, così la dimensione e la data di arrivo arrivano insieme. Ciò che conta è deciso se Dovecot ha le credenziali per l'indirizzo, non leggendo l'impostazione del fornitore. Presenza in virtual users È la definizione di "Kamo memorizza questa casella di posta", mentre una colonna del fornitore può essere stallo, ereditata da un genitore o da una media emigrazione. Una casella di posta ospitata da Microsoft o Google cede nessuna credenziali ed è saltato, quindi la sua organizzazione non è carica nulla. Chi conta per viene da ogni casella di posta propria org id. KamoMail non è un singolo inquilino — la produzione ha 18 caselle di posta sotto una organizzazione e 41 sotto un altro, intermixato in quattro domini — quindi sommando un dominio o la maildir di un'organizzazione per la posta di un'altra. Gli indirizzi sono abbinati in minuscolo. La tabella utente di Dovecot e i percorsi maildir sono minuscolo mentre la tabella della casella di posta mantiene qualsiasi involucro la casella di posta è stata creata con, e un caso sbagliato qui ha già una volta diviso consegna e recupero attraverso due diverse maildirs. Protegge anche /api/internal nel suo complesso piuttosto che endpoint. The filtro chiamato /api/internal/calendar e /api/internal/lead-comms individualmente, così il prossimo controller montato sotto tale prefisso — questo — sarebbe stato raggiungibile senza segreto, silenziosamente e in direzione di esposizione. 6 nuovi test riguardanti l'attribuzione, la gestione dei casi e il caso non ospitato; 152 totale, tutti di passaggio.
· InitializerService
Schema per la misurazione dello storage per posta
****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** RFC822 byte che la casella di posta sta tenendo. La contabilità di stoccaggio non ha nessuna fonte onesta per byte mailbox: l'indice di ricerca copre solo messaggi indicizzati, e li copre indipendentemente da chi li memorizza. ORG ID è denormalizzato da email provider.mailboxes così per-organizzazione l'aggregazione è una somma indicizzata, e quindi l'attribuzione rimane per casella di posta — diversi le organizzazioni possono essere su KamoMail subito e un maildir di dominio può contenere cassette postali appartenenti a diverse organizzazioni. Correre contro la produzione 2026-08-08; tabella e indici verificati.
· kamo-shared-library
Misurare la posta Kamo realmente ospita, per mailbox
EMAIL è stato fonte di e-mail metadata.search index, che era sbagliato in entrambi indicazioni. Ha perso completamente KamoMail — un org su KAMO MAIL ha i suoi messaggi e gli allegati nella maildir Dovecot del cluster, 62 MB di esso in produzione, contato come zero. E avrebbe addebitato un org su Microsoft 365 o Google Spazio di lavoro per posta seduto sui dischi di quel fornitore, dove Kamo non detiene nulla ma un indice di ricerca proprio. Non e' vero. lo sostituisce: per mailbox, al giorno di arrivo, totale RFC822 byte — intestazioni, corpo e allegati codificati, che è ciò occupa la maildir. Bucketing al giorno piuttosto che per messaggio mantiene il tavolo proporzionale a giorni-con-mail, lasciando ancora una finestra di fatturazione arbitraria essere riassunto esattamente invece di arrotondato a mesi interi. L'attribuzione è per casella di posta tramite il proprio org id, mai per dominio e mai per maildir. KamoMail non è un singolo inquilino: la produzione ha già 18 cassette postali sotto un org e 41 sotto un altro, intermixato su quattro domini in una tabella. La somma di un dominio, o la maildir nel suo complesso, potrebbe fatturare un org per un altro posta. Schema verificato nella produzione prima di questo atterrare: email provider .mailbox storage days esiste con il suo vincolo di unicità dopo una corsa KI.
· kamo-internal
Dire perché la casella di posta di archiviazione può leggere zero
Mailbox bytes conta solo la posta Kamo realmente ospita. Un'organizzazione su Microsoft 365 o Google Workspace ha i suoi messaggi sui dischi di quel fornitore, quindi la sua riga Mailboxes è legittimamente 0 — che, inspiegabile, legge come un rotto numero piuttosto che corretto.
· BillingService
Lasciare che un amministratore di piattaforma eseguire la scansione istantanea su richiesta
La spazzata è ciò che rende le rimozioni di periodo e i totali di vita per il domini discontinui rispondono a tutti, quindi i giorni persi a un outage sono persi permanentemente a meno che il divario non si riempia. Attendere per il prossimo 04:20 corsa non è un Piano di recupero. La spazzata raggiunge già 14 giorni ed è idempotent per (org, giorno, dominio), le chiamate così ripetute si stabiliscono piuttosto che il doppio conteggio. Questo lo espone, dietro @PlatformAdminOnly, e restituisce il numero di riga in modo che il chiamante può vedere che ha funzionato.
· kamo-internal
Mostra l'utilizzo di storage nella scheda statistiche dell'account
La scheda ha detto "Non sono disponibili dati di utilizzo per questo periodo" per ogni organizzazione. billingApi.getCurrentUsage() è una stube che ritorna { metri: [] } e UseMeters rende il suo stato vuoto a metri zero — quindi il messaggio stava segnalando la stub, non i dati. StorageUsagePanel legge il vero endpoint BillingService e mostra i due figure che sono facili da conflate fianco a fianco: ciò che l'organizzazione tiene in questo momento, che cade quando i file vengono eliminati, e che cosa ha aggiunto oltre il suo vita, che non lo fa. Accanto a loro, che cosa ha mosso questo periodo, e un per-dominio di ripartizione tra documenti, caselle di posta, supporti di base di conoscenza, riunione registrazioni e rivelazioni. Se una cifra è un pavimento piuttosto che una misura, lo dice — una rimozione totale senza snapshot di apertura dietro di esso, o un totale di vita per un'area di archiviazione che cancella i suoi dischi. Un chip spiega la base di record: un org è contato per ogni file che detiene anche se Kamo memorizza una copia per hash. La tendenza rende solo una volta che esistono due giorni di storia; un punto non è una tendenza, e il disegno implica una storia che non è stata ancora raccolta. I colori della serie sono l'ordine categorico convalidato — peggiore CVD adiacente ΔE 9.1, peggio adiacente normale-visione ΔE 19.6 su questa superficie. Tre siedono sotto 3:1 contrasto, quindi il tavolo di rottura non è decorativo: è la lettura non colorata degli stessi dati. Byte conta le stringhe di soggiorno end to end (BillingService serializza Long via ToStringSerializer) e sono ampliati solo dove la magnitudine è provabilmente sicura.
· BillingService
Segnala cosa sta memorizzando un'organizzazione
La scheda statistiche dell'account ha sempre detto "Nessun dato di utilizzo disponibile per questo periodo", per ogni organizzazione, perché ottenereCurrentUsage() in kamo-internal è uno stub restituire zero metri e niente dietro di esso mai aggregato stoccaggio. Serve tre cifre distinte per area di stoccaggio, più un totale: - ciò che l'organizzazione detiene in questo momento, che cade quando i file vengono eliminati - quello che ha aggiunto nella sua vita, che non - cosa si è mosso durante il periodo di fatturazione corrente La rimozione è derivata — nessuna tabella di archiviazione timestamps una disattivazione, dal momento che EmbRecordState.DATE UPDATED è inseribile=falso e mai rinfrescante — come apertura + aggiunta - corrente contro l'istantanea di apertura del periodo. Senza l'istantanea di apertura ritorna ai timestamp spazzatura e si riferisce come un pavimento piuttosto che presentare un'ipotesi come misura. La vita è esatta dove righe rimosse sopravvivono e si accumula da istantanee dove non lo fanno; entrambi dicono che sono, così l'UI può anche. BillingPeriodResolver preferisce un periodo di abbonamento in tempo reale e torna a il mese del calendario nel fuso orario dell'org. Quella ricaduta è tutta la caratteristica in produzione: ogni abbonamento porta NULL current period start e corrente period end, quindi senza di esso le cifre di periodo sarebbero vuote per ogni organizzazione che esiste — la stessa classe di bug come la stub che sostituisce. StorageSnapshotSweep scrive la posizione quotidiana sulla storia di ogni org dipende, catturare fino a 14 giorni mancati e vedere una prima snapshot dal queryable vita così il tracciamento non riavvia a zero. La piattaforma di endpoint riporta fatturati contro byte fisicamente memorizzate. Clienti sono fatturati per record; img dats detiene una copia per hash tuttavia molti org hanno quel file, e le miniature e le conversioni costano byte nessuno è fatturato per. Entrambi Le lacune sono l'economia di Kamo, quindi siedono dietro @PlatformAdminOnly. 21 nuovi test, 113 totali, tutti di passaggio.
· InitializerService
Schema runner per org storage snapshots
Specchi OrgStorageSnapshot come DDL esplicito, per lo stile di casa — ddl-auto crea la tabella prima, quindi il CREATE è un fallback e i DEFAULT SET sono che cosa effettivamente rende utilizzabile da SQL normale (Hibernate emette nessun default di colonna per i valori che l'entità riempie in Java). Anche gli indici imgs su (ORG ID, DATE CREATED) e (ORG ID, DATE TRASHED). Stoccaggio l'aggregazione si legge sia su ogni richiesta sommaria che gli indici esistenti sono tutti assoc- e a forma di cartella. Correre contro la produzione 2026-08-08; org storage snapshots verificata con tutti i 16 colonne e il suo vincolo di unicità prima che l'entità condivisa-lib sia stata spinta.
· kamo-shared-library
Per-organizzazione modello di utilizzo di storage
Nulla ha mai riassunto lo stoccaggio per organizzazione. Sei entità portano un byte conteggio per file, e nessun codice aggregato uno di loro, motivo per cui l'account la scheda statistiche rende "Nessun dato di utilizzo disponibile per questo periodo" per ogni org. Aggiunge il modello condiviso che l'aggregazione ha bisogno di: - StorageDomain — le cinque aree di stoccaggio org-attribuibili, ciascuna dichiara se i suoi dischi rimossi sopravvivono. Documenti e KB media soft-delete, quindi una vita totale è una query retta; caselle di posta, registrazioni e rivelazioni hard-delete, così i loro possono accumularsi solo in avanti. - OrgStorageSnapshot — una riga per (org, giorno, dominio). Esiste per tre figure che non sono recuperabili dopo il fatto: la vita totale per domini discordanti, la posizione di apertura che le rimozioni di periodo sono differenza contro (nessun timestamp tabella una disattivazione — EmbRecordState's DATE UPDATED è inserito=falso e mai rinfrescante), e un periodo chiuso numero fissato al termine del periodo. - OrgStorageAggregator — ogni per-dominio aggregato in una classe. Totali sono riassunto sopra i record, mai sopra gli oggetti memorizzati: img dats mantiene un fisico copia per hash tuttavia molti org tengono quel file, e che il salvataggio è di Kamo. L'aggregatore non porta deliberatamente stereotipo. Ogni servizio viene eseguito @ComponentScan("com.kamo"), in modo da annotarlo avrebbe costruito in tutti ~40 di loro; BillingService dichiara il fagiolo. Aggiunge anche le query di ricerca-indice le esigenze di backfill dimensione e-mail — size bytes è stato scritto come un hardcoded 0 dal momento che l'indice è stato creato, quindi legge come zero bytes piattaforma-wide fino a quando queste righe sono riparate. Schema verificato nella produzione prima di questo atterrato: org storage snapshots esiste con tutte le 16 colonne dopo una corsa KamoInitializer.
· InitializerService
Denunciare l'app divisa
La discarica ha mai riempito una fila di piano che mancava - deliberatamente, in modo che non poteva silenziosamente spostare un livello — che ha lasciato applicazioni che non erano mai stati valutati ovunque prima li ha messi. Punto vendita e Strumenti di marketing finito su Libero in quel modo, che non è quello che Free è venduto come. Ancorato a quello che ogni piano già promette sulla pagina dei prezzi, quindi la matrice e la copia di marketing non può derivare: Free è la valutazione (CRM, chat, documenti, note, KB, calcolatore); Starter aggiunge le applicazioni a business trades with (supporto, commercio, marketing); Business aggiunge il set premium più telefonia, inventario e clubhouse; Enterprise aggiunge Legal. Email hosting e i due sistemi di origine appartengono a nessun livello — l'e-mail è prezzo per posto su qualsiasi piano, i sistemi di origine sono negoziati per creditore. A differenza del backfill questo si riconcilia piuttosto che colma le lacune, che è il punto di essere la fonte di verità: un cambio di livello appartiene qui, in recensione, non in un UI modificare la prossima corsa sarebbe tornare. Applicato al prod: 11 righe sono cambiate. POS e Marketing Free, e le quattro app non rilasciate date le tier atterreranno quando spediranno, quindi pubblicando uno è un flip di fase piuttosto che un cambiamento di dati. Un secondo run riporta zero cambiamenti.
· kamo-internal
Split Apps & Caratteristiche in Disponibile / Non disponibile / Deprecato
Una lista piatta app mista la piattaforma offre con quelli che non e quelli che si è ritirato, quindi l'operatore ha dovuto leggere ogni chip per distinguerli. I secchi sono esclusivi e la deprecazione viene testata prima: un'app in pensione è ha ritirato tutto quello che la sua bandiera di disponibilità ancora dice, quindi appare solo sotto Deprecato. Questo rende anche il chip per-row "Deprecated" e la dimming ridondante — ogni riga in quella scheda è ritirata — così entrambi sono andati. La partizione legge lo stato del server piuttosto che la bozza, quindi ticking il deprecato l'interruttore lascia la riga in posizione fino a quando il salvataggio atterra invece di tirarla fuori da sotto la modifica.
· SecurityService
Un catalogo di app leggibile dagli inquilini per i tipi Branch
Tipi di ramo -> App consentite ha iniziato a caricare la sua lista app da /platform-config/app-catalog, che richiede un proprietario di org di alto livello. La pagina ospitante è invece aperta su CONFIGURE SYSTEM, quindi per ogni inquilino il GET 403'd: l'editor ha reso zero applicazioni e il gestore di salvataggio poi ha rifiutato di salvare modello di sicurezza — bloccando il nome, le fasce di età, i ruoli e i tipi di commercio, nessuno dei quali coinvolgere il catalogo. Ha fallito per un amministratore KamoCRM senza èOrganizationOwner troppo. ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** proiezione volutamente più stretta: tipo, nome del display, descrizione, stadio, disponibile, deprecato. Nessuna nota operatore, nessun default di enum, nessuna diagnostica di diritto — quelli descrivono come la piattaforma è eseguire, e le scritture rimangono piattaforma-proprietario-solo.
· kamo-internal
Aggiungere una scheda Apps & Features e correggere l'elenco Apps consentiti
La nuova scheda, a destra delle Organizzazioni, controlla ciò che ogni organizzazione è offerto: disponibilità, fase di sviluppo, nome del display, descrizione, un nota operatore e deprecazione. La disponibilità non è più derivata dalla fase, in modo che un'applicazione può essere pubblicato mentre ancora in prova o tenuto indietro dopo che è finito; il palco ora guida solo il distintivo e la barra. Campi sovrapposti sono marcato e ritorto individualmente, e deprecazione — che nasconde un'app da ogni inquilino che lo utilizza — ha bisogno del nome dell'app digitato per confermare. App consentite nell'editor del modello di sicurezza era un TypeScript mantenuto a mano copia di ServiceType.java, con un commento chiedendo agli editor di tenerlo allineato. Si era allontanato, invisibilmente: - MLOS mancava del tutto, quindi nessun modello di sicurezza potrebbe concedere mutuo origine affatto. - LOS è stato etichettato "Mortgage Loan Origination System" - significa personale prestiti dal 2026-08-08 rinominare, così l'editore ha scritto un personale concessione per un operatore che pensavano di concedere mutuo. - POS e ESIG hanno mostrato PLANNING molto tempo dopo la spedizione. L'aggiunta di indicatori di disponibilità a tale elenco l'avrebbe fatto solo fiduciosamente sbagliato, così ora legge lo stesso catalogo la nuova scheda scrive e non può non essere d'accordo. Il risparmio viene bloccato fino al carico del catalogo: ogni appConfigs costruttore itera, così un elenco vuoto avrebbe spogliato I contributi del modello.
· SecurityService
Servire il catalogo app e leggere le funzionalità attraverso di esso
FeatureController derivato "è questa applicazione offerta" dalla fase dell'enum in quattro luoghi separati, in modo che la risposta potrebbe essere cambiata solo ridipingendo piattaforma. Ognuno di loro ora legge AppCatalogService, insieme al nome, descrizione e deprecazione. L'unica sottigliezza comportamentale è il cancello root-org. Una volta leggeva "il palco è non COMPLETATO, così i bambini o i bambini non possono abilitare questo" — il percorso di dogfooding. Si'. ora legge "la piattaforma non offre attualmente questo". Mentre non si sovrappone esiste quelle sono la stessa frase, perché la disponibilità non imposta si risolve stadio == COMPLETATO. Una volta che un operatore pubblica un'app che è ancora in TESTING, solo la nuova forma fa la cosa giusta: senza di essa l'app rimanere silenziosamente bloccato per ogni bambino org. PlatformAppCatalogController è gated ai proprietari di top-level-org, lo stesso il pubblico come le schede della piattaforma vicina, e riferisce se un disponibile app è in realtà nella matrice del piano — un'app nessun piano menziona risolve attraverso l'impostazione non aperta piuttosto che attraverso tutto ciò che l'operatore ha scelto.
· kamo-shared-library
Rendere la disponibilità delle app e la fase modificabile a runtime
Ogni attributo le impostazioni delle app rende la pagina — nome del display, descrizione, fase, deprecazione — era una costante di compilazione nel ServiceType enum, e "Disponibile" non è stato memorizzato affatto: è stato derivato come stadio == COMPLETATO nell'interfaccia utente e in tre posti lato server. Cambiare qualsiasi di esso significa modificare la biblioteca condivisa e ridipingendo la piattaforma. PLATFORM APP CATALOG contiene una riga per app con ogni colonna override nullable, e AppCatalogService risolve override?? Enum. Una riga mancante significa enum verbatim, quindi installare questo non cambia comportamento. La disponibilità diventa il suo valore, che è ciò che rende un lancio morbido (offered mentre ancora TESTING) o una app COMPLETATA non ancora valida. La sua caduta legge la fase RESOLVED piuttosto che quella dell'enum, quindi un operatore che si muove solo un app a COMPLETED ottiene la disponibilità senza anche capovolgere l'interruttore.
· InitializerService
Una riga di catalogo correttamente classificata per app, su ogni radice di diritto
Nove applicazioni — POS, Strumenti di marketing, Calcolatore, Club, Inventario, Legale, Giochi e i due sistemi di origine — non aveva alcuna rappresentanza del catalogo affatto: nessuna riga di prezzi, nessuna categoria, nessun livello. Dodici altri sono stati già descritti da una riga di marketing, in modo da legare quelli e aggiungere un parallelo La riga APP * avrebbe dato un ServiceType due righe e fatto il pianoFeatureIncluded dipende da ordine di iterazione. Il corridore ora converge il catalogo invece: - lega una riga di marketing pubblicata all'app che descrive (VIDEO CONF is Meet, MESSAGING is Chat), mantenere il suo codice, nome, categoria e valori di livello in modo che la matrice pubblicata non si muova; - crea una riga per un'app che il catalogo non descrive, in una vera e propria categoria, incluso un nuovo categoria Commercio & Origine; - promuove qualsiasi segnaposto APP * precedente alla sua identità reale ed elimina le Applicazioni piatte secchio una volta vuoto; - rimuove le righe duplicate per la stessa app, preferendo la riga di marketing. Non c'è APP fallback a sinistra: ogni applicazione ha un codice leggibile, e app il catalogo pubblicato già i nomi riutilizzano quel codice esatto in modo da un org con la propria matrice e uno senza descrivere stesso prodotto identicamente. Questo importato — sign.pink non ha matrice di marketing ed è stato lasciato con undici file che prende il nome da costanti di enum. MLOS e Prestiti Personali sono negoziati con le imprese, non per-seat: prezzo null, modello di prezzi FLAT, compatibile solo con i piani tipo CUSTOM (Enterprise), in bundle con nessun livello. Essi sono creati sul solo piattaforma org — un prodotto white-label org non li rivende, e i due creati su sign.pink da un passaggio precedente sono disattivati. Verificato contro il prod dopo l'esecuzione: 0 codici APP * lasciati, 0 righe di servizio duplicate, 21 app su ogni radice, 0 combinazioni di plan-by-app scoperte, e una ri-run è un no-op.
· kamo-internal
Consolle di organizzazioni fantasia server
La scheda ha caricato ogni organizzazione in una richiesta e ha reso una carta ciascuno, con il backend sbiadito per org, poi per conto, poi per abbonamento. Cerca, ordina, filtra e paging ora eseguire server-side contro l'intera tenuta, così il costo della pagina è piatto non importa quante organizzazioni esistono. La riga è anche l'oggetto giusto ora. Un abbonamento è HELD da un account e TARGETS un org, quindi la vecchia carta -- con chiave sul supporto -- ha dovuto stampare il il nome dell'obiettivo all'interno di una tabella nidificata, e il cliente org appariva come stringhe all'interno di altre file di orgs piuttosto che come righe. Ogni riga ora porta il diritto (inbound: che cosa governa questo org) e fatturazione tenuto (outbound: che cosa si legge per altri) come sfaccettature separate. Design: una barra di stato lungo il bordo sinistro di ogni riga e una barra di tenuta proporzionale, così una pagina legge come un istogramma di colore di salute commerciale prima che una parola è letto; numeri tabulari con zeri disattivati, dal momento che la maggior parte delle cellule su un immobile di fatturazione sono zero. Stato della tabella vive nell'URL quindi una vista filtrata è collegabile -- scritta attraverso una copia che conserva `tab`, e senza pubblicare attivoTab, che appartiene a tabSync.
· SecurityService
Fail chiuso su app un piano mai menziona
Kamo.entitlement.fail-open off ora che AppEntitlementCatalogMigration ha eseguito contro prod e ogni piano attivo su entrambe le radici di diritto (KamoCRM, Sign Pink) porta un esplicito riga per tutte le 21 applicazioni non-core — verificate zero scoperte combinazioni plan-by-app, quindi c'è niente è rimasto per un nega-by-default di catturare tranne un'applicazione autenticamente non costoso. Questo non cambia ciò che un org può utilizzare oggi: le applicazioni spedite sono esplicitamente incluse e MLOS e l'app personale-loan era già negato da esplicito èIncluded=false righe, che legano indipendentemente da questa bandiera. Ciò che chiude è un'applicazione scivolare attraverso unprezzo non appare mai in matrice di un piano. Flip back to true se viene aggiunta una nuova radice di diritto prima che la migrazione si esaurisca.
· BillingService
Servire la directory org come pagina filtrata, ordinata e impaginata
GET /orgs prende pagina/dimensione/q/sort/dir più sette filtri e restituisce la casa pagina busta con aggregati immobiliari. GET /orgs/{orgId} serve tutti i dettagli per il cassetto di perforazione della console, dove la ventola per conto è a posto perché è legato a un org. listAllOrgs() è sostituito piuttosto che mantenuto come delegato troncante: aveva esattamente un chiamante (questo controller), e la lista di frontendOrgs() aveva esattamente uno (la scheda viene riscritta), quindi uno shim di compatibilità avrebbe solo aggiunto rischio di intrusione silenziosa con nulla a seconda di esso.
· BillingService
L'idratazione della pagina a un conteggio costante delle query
Titolo principale, dominio primario, piano/mercato dietro il diritto e abbonamenti-catalogo bandiera sono ciascuno un lotto IN (:ids) su tutta la pagina, così una pagina 100-row costa le stesse sei dichiarazioni come una pagina 1-row. Il test afferma che l'uguaglianza direttamente -- l'attuazione che questo sostituisce avrebbe segnato 6 contro 600. L'abbonamento è scelto da EntitlementModeResolver, lo stesso codice che ha deciso la modalità della riga, quindi il piano mostrato appartiene sempre allo stato mostrata. Le query passano attraverso EntityManager piuttosto che nuovi metodi di repository kamo-shared-library, che ogni servizio dipende, non deve cambiare.
· BillingService
Directory riga / pagina DTO e il repository query nativo
PlatformOrgRow trasporta le due sfaccettature separatamente: diritto (sotto in entrata mirare l'org -- che cosa lo governa) e fatturazione tenuta (conti che l'org possiede -- non-zero solo per la piattaforma org e rivenditori). Gli aggregati descrivono il set intero filtrato, non la pagina, quindi la striscia KPI non cambia significato come le pagine dell'operatore. Ogni valore del driver è costretto tramite Number/Boolean piuttosto che lanciare: a carbonesce(...,0) che non corrisponde a righe restituisce Integer dove la stessa colonna con righe restituisce Long, che passerebbe in dev e gettare in prod. Il denaro attraversa il filo come una stringa decimale.
· BillingService
Org directory SQL con per-facet CTEs
Conti ceduti, abbonamenti in entrata, membri, licenziatari e bambini ciascuno aggregati nel proprio CTE prima di essere uniti a org. Unire loro in uno pass avrebbe moltiplicato le righe e gonfiato ogni SUM. Pure e unit-tested (26 test): no ':' casts, ogni valore legato, ordinare chiavi whitelist attraverso OrgSort. Due cose il pin dei test perché sono facili "tidy" in bug -- ORDER BY e i predicati derivati (mode rank, has stripe) deve nominare le colonne proiettate della base CTE, dal momento che l'alis `o` è fuori portata nella query esterna; e un processo PENDING non riporta alcuna scadenza piuttosto che la ultimo scaduto uno. Verificato contro il cluster live: entrambe le dichiarazioni eseguono, il conteggio concorda con le righe e si applicano filtri/aggregate. L'uso dell'indice NON è dimostrato -- a 12 org / 9 sub CockroachDB full-scans indipendentemente, correttamente.
· BillingService
Richiesta di directory validata, bloccata
Dimensioni morsetti a [1.100] e un tipo non riconosciuto rientra al titolo, quindi un URL a mano non può richiedere l'intera proprietà né raggiungere la SQL come testo gratuito. L'Enum OrgSort è la specie whitelist.
· BillingService
Modalità di denunce del diritto attraverso un risolutore condiviso e deterministico
TrialAdminService ha scelto il suo abbonamento di prova con findFirst() su un elenco non ordinato, quindi un org che tiene sia un processo in sospeso e attivo potrebbe rapporto. La prossima directory di organizzazione deve ordinare e filtrare su questo stato, e una query non può riprodurre una regola non deterministica. Il precedente ora vive in... prove live, vincite attive sopra le vincite in sospeso in scadenza, e la scadenza attiva segnalato è l'ultimo. stato() mantiene la sua esatta forma di risposta. Primo test in questo repo (11).
· InitializerService
Backfill app titola righe nel catalogo della piattaforma
L'iscrizione si risolve ora su una colonna di service type digitata, ma nessuna riga di catalogo ha portato una, quindi ogni piano ha ancora deciso di "tutto compreso" tramite il default non aperto. Questo semi matrix così kamo.entitlement.fail-open può alla fine essere spento senza applicazioni dark-firing. - AppEntitlementCatalogMigration (@Order(51), dopo PlatformOwnerPlanSeeder): idempotent riempimento. Crea una categoria di funzionalità 'Applicazioni' con una riga BOOLEAN per app non-core legato al suo ServiceType, rilega qualsiasi riga preesistente che ha il codice ma non serviceType, e inserisce le righe plan-to-app mancanti per livello. Inserimento solo — non cancella mai e non sovrascrive mai un operatore èIncluso, quindi un livello modificato nel UI sopravvive ai reboots. Inoltre rilega il VIDEO CONF esistente / AI ASSISTANT / HR SYSTEM / ESIGNATURES / Componenti aggiuntivi EMAIL HOSTING, che non concedevano nulla. - KamoCRMPricingLoader: stessa matrice per un cluster fresco. Le righe dell'app sono allegate all'interno upsertCaratteristiche piuttosto che scritte in un secondo passaggio, perché upsertPlanFeatures cancella il le righe del piano prima di inserire — una seconda chiamata sarebbe silenziosamente spazzare la matrice di marketing. Nota questo caricatore è disabilitato (ritorno precoce e la sua chiamata DataLoader è commentata), che è perché la migrazione sopra è il percorso di produzione.
· SecurityService
Porta ipoteca e verticale personale-loan su diritto app
Un mercato MORTGAGE è apparso su /commerce ogni volta che POS era acceso, indipendentemente dall'applicazione MLOS, e /features/enable non aveva alcun controllo di fatturazione affatto — il lucchetto nelle impostazioni UI era presentazione solo, quindi un POST diretto ha acceso un'app non collegata. - FeatureController: 403 su abilitare quando il piano non include l'app; serializzare un verdetto 'concitato' su entrambe le caratteristiche[] e disponibile[] così il lucchetto UI e il 403 sono calcolato dallo stesso servizio e non può essere disaccordo. - CommerceMarketController: GET /allowed-types per il raccoglitore di tipo di mercato (modello per la lista dei permessi intersecato con diritto); l'elenco di mercato scende verticali non collegate — nascoste, mai archiviato, in modo da ripristinare il piano riporta il mercato con i suoi dati intatti. Una guardia chiusa su ogni sub-route. Disponibilità L'intercettatore non può esprimerlo — la sua mappa prefisso è un prefisso ad uno ServiceType e /commerce-markets serve tutti e sette i verticali sotto POS. - POSController: i chiamanti pubblici del catalogo di abbonamento non vedono più righe legate ad un'app che non ha raggiunto COMPLETATO, quindi spostare un ServiceType a COMPLETATO è quello che lo pubblica a il sito di marketing. I chiamanti interni vedono tutto per valutarlo in anticipo. - MortgageVerticalResolver: la disponibilità misura solo POS, quindi un org che non ha mai acquistato MLOS ancora leggere come pronto; ora riferisce MLOS in 'missing' troppo.
· kamo-internal
Rinominare LOS a MLOS, aggiungere Personal Loan Origination System, gate commerce verticali
La scheda App mostra ora 'Commerce: Mortgage Loan Origination System' (il titolo viene servito dal backend enum, non lo specchio TS) e un nuovo 'Commerce: Personal Loan Origination System' sotto Non disponibile. I mercati MORTGAGE e PERSONAL LOANS sono depositati sulla loro app. - AppType/ServiceType mirrors: MLOS(14) + new LOS(25); fissa anche la deriva preesistente dove gli specchi non erano d'accordo con l'enum Java. - Organizzazione: isMlos aggiunto; isLos ora significa prestiti personali. isLos aveva zero consumatori. - FeaturesManager: gating legge la bandierina "azionata" fornita dal server. E 'stato ricostruito lato cliente da fatturazioneApi, il cui getCurrentSubscription getAvailablePlans restituisce [] (legacy stubs), così ogni applicazione silenziosamente risolto a 'ungated' e nessun lucchetto mai reso. - Selettori di mercato: sia le pagine di creazione che di modifica hanno tenuto il proprio array COMMERCE TYPES codificato; che ora rendono GET ******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** non selezionabile. La pagina di modifica era stata anche saltando la regola one-MORTGAGE-per-org. Continua il tipo attuale del mercato selezionabile in modo che un org lapsed possa ancora aprire e rinominarlo. - Impostazioni Mortgage superfici cancello su isMlos piuttosto che isPos.
· kamo-shared-library
Risolvere il diritto di app da ServiceType; rinominare LOS a MLOS
L'iscrizione ha risolto ServiceType.name() contro Non e' vero. I codici del catalogo sono codici di marketing (USERS, VIDEO CONF, HR SYSTEM, ...) e mai abbinati, così planFeatureIncluded's mancante-row default concesso ogni app su ogni piano — la fatturazione-app il collegamento era inerte. Le righe dei cataloghi ora portano un tipo di servizio nullable type e la risoluzione passa attraverso in modo che un'applicazione può essere rinominato senza movimento diritto. - **************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** - No, no. risoluzione di ServiceType; onori aggiuntivi tramite In precedenza illecito. dietro kamo.entitlement.fail-open (default vero fino a quando la matrice del catalogo è semenzato). - SottoscrizioneServizio: portare il servizioTipo attraverso creare/aggiornamento/DTO, e derivare feature code dal nome quando omesso — la finestra di dialogo delle impostazioni non ha mai inviato uno, quindi ogni creare dal Ho colpito il vincolo NON NULL. - CommerceType: legare ogni verticale alla sua app richiesta (MORTGAGE->MLOS, PERSONAL LOANS->LOS). - AppliedModelEnforcementService: ottenereAllowedCommerceTypes interseca il modello di sicurezza consent-list con diritto; assertCommerceTypeAllowed già copre creare e modificare. - ServiceType: LOS -> MLOS mantenendo id 14 (ORG FEATURES persiste il id, non il nome, quindi no migrazione dei dati); nuova LOS(25) per i prestiti personali — id 25 perché riutilizzare 14 sarebbe riassegnare ogni file di ipoteca esistente. - RoleRightType: MLOS * rimbalzo, ids 139-144 invariato; LOS * personale-loan aggiunto al 190-195. - PhiModule. PLOS ha aggiunto: il rinominare ha lasciato l'app personale-loan mappato al mutuo PHI modulo e ipoteca non mappato, che compilato pulito.

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