
Egy kódbázis, két app: hogyan white-label-ezz fork nélkül
A GeekBye és a Pavleur két eltérő brandű desktop alkalmazás, amelyek egyetlen repositoryból épülnek — nincs fork, nincs duplikált kódbázis. Íme a build-időben működő gépezet, amelytől egy kódbázis két termékké fordul le, és az az egysoros hiba, amitől a második appunk rossz néven mutatkozott be.
A GeekBye egy meetingjegyzetelő. A Pavleur egy AI-alapú interjúgyakorló app. Két különböző terméknek néznek ki, mert két különböző termék is — de egy kódbázisból épülnek, fork nélkül, elsodródó branch nélkül. Ez a white-labeling mérnöki oldala: nem az, hogy „miért adj el brandelt appot" (az egy másik bejegyzés), hanem az, hogy hogyan fordítasz le ténylegesen egy repositoryt két apppá — és az a dicstelen hiba, amely minden white-label rendszerben ott lapul, amíg tudatosan nem kezded keresni.
Generálj, ne branchelj
A rossz módja annak, hogy két terméket futtass egy kódbázisból, ha if (product === 'pavleur') ellenőrzéseket szórsz szét az appban, vagy forkolsz és imádkozol, hogy a két másolat soha ne térjen el. Mindkettő gyorsan rohad.
A GeekBye megközelítése a build-idejű kódgenerálás. Minden terméknek van egy config fájlja — configs/geekbye/config.ts, configs/pavleur/config.ts —, amely az identitását tartalmazza: név, bundle id, URL protokoll, az automatikus frissítések release repositoryja, és így tovább. Build előtt lefut egy script:
node scripts/build-product.js pavleur
Ez a script beolvassa a kiválasztott termék configját, és három konkrét dolgot csinál:
- Legenerál két config modult — egyet a renderernek (
src/config/product.ts), egyet a main processnek (electron/config/product.ts) —, mindkettőt egy „NE SZERKESZD KÉZZEL — a változtatások felülíródnak" fejléccel lepecsételve. Az app többi része egyszerűen a configból importálja aproductName-et, azapplicationId-t és aprotocol-t, és soha nem gondolkodik azon, melyik brand is ő. - Csak az adott brand assetjeit másolja át — az ikonokat, a kis logót, a toast-értesítés ikonját. Egy GeekBye build fizikailag nulla Pavleur grafikát tartalmaz, és fordítva. Nincs mi kiszivárogjon.
- Átírja a
package.json-t (release buildeknél) — az app id-t, a terméknevet, az URL protokollt, a GitHub release repót, ahová publikál, és — ami külön szép — a brandelt mikrofonengedély-szöveget, hogy a macOS azt kérdezze: „A Pavleur hozzáférne a mikrofonhoz", ne a rossz brandet.
Az egész app brand-agnosztikus; a brandet build-időben injektáljuk. Nincs futásidejű termékváltó, amit el lehetne rontani, mert futásidőben mindig csak egyetlen termék van beégetve.
Egy backend, két brand
Mindkét app ugyanazzal a backenddel beszél. Honnan tudja hát a szerver, melyik termékhez — és melyik árazáshoz, promptokhoz és limitekhez — tartozik egy kérés?
Két jelzésből, mindkettő build-időben rögzül. Először is egy application id (geekbye vagy pavleur), amely a configba van égetve és a kérésekkel együtt megy, így a backend a megfelelő termék viselkedéséhez tud irányítani. Másodszor egy User-Agent, amelyet a kliens minden backend-hívásához csatol, Product/version (platform) formában — pl. Pavleur/1.2.5 (win32) vagy GeekBye/1.7.3 (darwin). Ez az egy fejléc ingyen ad a backendnek termékenkénti és operációsrendszerenkénti attribúciót, külön analitikamező nélkül. Az automatikus frissítések eközben elszigeteltek maradnak: minden termék a saját release repositoryjába publikál, így egy GeekBye-frissítést soha nem kaphat meg egy Pavleur-telepítés.
Az onboarding is termékenként eltér — a GeekBye varázslója a meetingek átiratozásáról szól, a Pavleuré az interjúgyakorlásról —, és a megfelelőt futásidőben választja ki az egyetlen beégetett application id. Ugyanaz a gépezet, két bejárati ajtó.
A hiba: amikor a Pavleur GeekBye-nak hívta magát
Íme a harctéri sztori, és ez az, amiből minden white-label rendszernek megvan a maga verziója.
A configgenerálás működött. Az ikonok cserélődtek. A mikrofonos kérdés brandelt volt. A protokollok és a bundle id-k stimmeltek. Minden látható mérce szerint egy Pavleur build Pavleur volt. És mégis, egy ideig minden Pavleur build úgy jelentkezett be a backendhez, hogy GeekBye-nak hívta magát.
A tettes egyetlen beégetett string volt, és az elképzelhető legkevésbé csillogó helyen bujkált: a User-Agent összeállítóban, egy hálózati retry segédfüggvénybe dugva. Minden felhasználó felé látszó dolgot átkötöttünk a configra, de ebben az egy mélyen fekvő csővezeték-utilityben még mindig ott volt egy `GeekBye/${version}` literál. Egy retry utilityre senki sem gondol „brandingként", így senki sem ellenőrizte. A javítás négy sor volt — importáld a dinamikus productName-et a configból, és azt interpoláld a beégetett brand helyett —, de a tanulság maga a lényeg:
A white-labeling nem az, hogy „kicserélődött-e a logó". Hanem az, hogy „minden identitás-string a configon keresztül megy-e". És az utolsó kimaradók sosem a UI-ban élnek, ahol keresnéd őket. A User-Agentben élnek, a deep-link protokollkezelőben, az engedélykérő szövegben, a hibaüzenetekben — a csővezetékben, amit senki sem sorol a „brand" címke alá. Az egyetlen megbízható védekezés, ha a brand literált aktívan vadászandó dologként kezeled: greppeld végig a kódbázist a beégetett névre, és buktasd el a buildet, ha bárhol felbukkan azon a configon kívül, amelynek hivatott definiálnia.
Három dolog, amit a white-labeling tanított nekünk
- Generálj, ne branchelj. Két apró config modul build-idejű kódgenerálása plusz szelektív assetmásolás veri a forkot is és a futásidejű termékellenőrzés-bozótot is. Az app brand-agnosztikus marad; a brand egy build-bemenet.
- Az identitás-stringek beszivárognak a csővezetékbe. A UI a könnyű 90%. A nehéz 10% a User-Agent, az URL protokoll, a mikrofonengedély-string, a hibaszövegek. Vezesd át mindet egyetlen
productName/applicationIdexporton — és greppelj a nyers brandnévre CI-kapuként, hogy egy kóbor literál soha ne kerülhessen ki. - Egy backend, sok brand, megkülönböztetés id-val és fejléccel. Egy build-idejű application id plusz egy
Product/version (platform)User-Agent termékenkénti és platformonkénti attribúciót ad nulla duplikált infrastruktúrával — tisztán szétválasztva a megváltoztathatatlan build-idejű identitást a futásidejű viselkedéstől.
A saját branded white-label jegyzetelő futtatásának termék- és adatvédelmi oldaláról lásd a white-label AI jegyzetelőt. Ez a hatodik fejezete annak a v1-történetnek, amelyből a GeekBye v2 lesz — az előző fejezetért lásd mi is valójában egy 127 commitos kiadás (v1.7.0); a teljes ívért pedig a szoftver tökéletesre szállításának anatómiája.