Shared application
CI compiles browser files and a server renderer when code changes. Start with one main-site build and one generic WLS build if their shells differ.
Keep complete HTML pages in S3. Use a shared Angular build and a separate render worker to update the correct pages for main-site, Prateek, and James.
Proposed implementation, not a deployed change. Examples use your actual article post URL format. Backend events, queue, and page records described here must be added.
A WLS pipeline generates a separate Angular project for each creator. It places tenant configuration and some page data in TypeScript files. The prerender target then builds browser and server code and creates HTML for its route list.
| Repository evidence | Meaning for this proposal |
|---|---|
scripts/generate-whitelabel.js | Generates tenant projects, environment files, cover/sub-page constants, and deployment entries for all selected WLS creators. It currently has no creator selector for generation. |
generate-static-routes.mjs | Reads admin/posts-list-seo, builds post URLs, and writes them to the prerender route list. The WLS generator adds a creator username filter. |
prerender.routes.txt | Contains article URLs. Articles are scheduled for prerendering. This does not verify that each deployed HTML file currently exists or contains the expected content. |
post-detail-routing.module.ts | The post module already accepts :username/:type/:id. Articles are posts with type = article, not a separate /articles/:slug route. |
scripts/prefetch-marketing-pages.mjs | Generates marketing data and routes as TypeScript. A new marketing slug currently becomes a build input. |
server.ts | Uses an existing dist folder to select a project and creates global DOM objects. A shared worker needs explicit site selection and isolated render state. |
deployment_script/parallel-build-and-upload.sh | Builds each listed tenant, uploads the output, and requests distribution invalidation. Its upload rule applies a one-year immutable header to the output. |
Verified article URL: /post/<username>/article/<encoded-id>. Reuse the existing URL builder, including its original_sk || sk handling. Do not introduce a different article URL scheme.
The workspace now declares Angular 16.2 and Universal 16.2, with an upgrade in progress. Earlier discussion referred to Angular 15. Select the render API and build settings for the version finalized in the repository.
CI compiles browser files and a server renderer when code changes. Start with one main-site build and one generic WLS build if their shells differ.
A content publish or code release selects site-and-URL pairs. The queue holds these jobs and limits how many run at once.
A Node worker runs the existing server build for one site and URL, creates HTML, and publishes that page to S3.
Publish content Release application code
| |
| Compile shared application
| |
+--------------- Select affected URLs -----+
|
Render queue
|
Worker runs the built renderer
|
Validate and publish HTML
|
CloudFront serves the page
Use SQS for the queue and a Node worker on ECS or EC2 as an initial AWS design. The worker reads public, published content with backend credentials kept on the server. The visitor receives HTML with the correct site branding and content already present.
For the first implementation, keep the existing site buckets and distributions. Put common JavaScript and CSS in a shared release location. Configure shared asset routing once, rather than copying application files into each bucket for every release.
Shared application storage /releases/wls/v43/... JavaScript, CSS, server artifact /releases/main-site/v43/... Separate artifact if needed Site storage, illustrated as three existing buckets main-site → HTML, theme, public config, manifest, icons prateek → HTML, theme, public config, manifest, icons james → HTML, theme, public config, manifest, icons
Build and render are different jobs. One code build can produce HTML for many sites. If a change affects 50,000 static pages, those pages still require rendering. This design removes repeated compilation and selects the work that is needed.
Assume Prateek and James each have a WLS. Their public article posts can also appear on main-site. Whether a post belongs on a particular site is checked against publication and access rules.
As an immediate fix, add an explicit creator selector to the generator. Filter before fetching tenant pages and generating files. Emit a deployment file containing only Prateek. The upload script must process only that file. A missing or invalid creator must fail, never fall back to building every creator. Require an explicit all-sites mode for bulk work.
This is an interim improvement. After the new worker is available, a content or theme publish uses the smaller jobs below.
With the current tenant-compiled code, James still needs an initial tenant build. With the generic WLS application, provisioning needs data and page renders only. Do not copy today's main-site output into James's bucket: it contains main-site assumptions and metadata. A neutral WLS release can be reused after the refactor.
The backend validates and publishes Prateek's theme. Pages read the same six CSS variables before their first visible render. James and main-site keep their themes. This avoids page renders only after removing compiled and inline overrides that restore old colors. See the theme design for missing files, caching, and existing visitors.
On publish, the backend registers the actual site and URL, then queues one render. A new page has no rendered revision, so its first render is always required. An edit creates a new desired revision. The worker publishes the page and updates the site's sitemap and any pages that link to it.
A new slug using an existing layout needs no compilation after routes and content are loaded at runtime. A brand-new Angular layout or feature still needs a code release. Reserve application paths and existing creator routes; do not let an arbitrary marketing slug take them over.
Job 1: main-site → /post/prateek/article/<id> Job 2: prateek → /post/prateek/article/<id> No article-detail job for James's WLS.
If a prerendered homepage, feed, or related-post section shows this article, queue that page too. Do not assume an /articles listing exists. Register dependencies for the actual pages in this application. A page rendered only in the browser does not need an HTML job, though its data cache may need a refresh.
type = article. For each post, find its permitted site contexts.Prateek article A → main-site + prateek Prateek article B → main-site + prateek James article C → main-site + james
Other post types need rendering only if shared post code affects them. Article cards in other pages need rendering if the change affects those cards. When release scope is uncertain, select all public pages that could be affected. Angular cannot reliably infer the complete impact from a Git diff.
If his footer changes, select Prateek's prerendered pages that use that footer. A title change in a menu can affect several pages even though only one page was edited. Track dependencies to account for this.
Unpublishing removes the public HTML, refreshes listing pages and the sitemap, and clears the relevant edge caches. Mark the page withdrawn so an older queued job cannot publish it again. A rename publishes the new path and applies the chosen redirect or removal to the old path. Do not serve a stale full article after its access rules become private.
Add a page registry. Each record represents one URL on one site. The same Prateek article on main-site and on his WLS has two records.
siteId: prateek path: /post/prateek/article/<id> pageType: POST_DETAIL postType: article dependencies: post:101, branding:prateek, footer:prateek desiredRevision: 9 renderedRevision: 8 applicationVersion: wls-v42 publicStatus: published publishedObject: pages/article-101/revision-8/index.html
These are proposed fields, not existing backend names. The desired page revision should change when any relevant dependency changes. Store the input versions or a fingerprint of the published snapshot to avoid duplicate work.
| Trigger | Selection rule |
|---|---|
| New page | Create a record for its site and URL. No rendered revision means it needs a first render. |
| Post edit | Select direct post detail records and reverse dependencies for that post ID. |
| New article in a listing | Select pages depending on the creator's post collection, even though the new article was absent from their last render. |
| Footer or menu edit | Select records with that site-specific footer or menu dependency. |
| Code release for articles | Select post detail records with postType = article, plus other affected page types declared by the release. |
| Broad shared code change | Select all eligible records for the affected application. |
Record dependencies when a renderer or page service loads data. Also declare collection dependencies for feeds, pagination, and new or removed posts. Start with conservative rules: for example, an article publish can refresh the creator's registered public listing pages rather than attempt field-level optimization.
Initially, seed this registry from the existing generated route lists and the SEO post source. Use pagination or a complete export. A periodic reconciliation job compares published routes with the registry to repair missed events, add missing routes, and identify withdrawn pages.
Render job siteId: prateek path: /post/prateek/article/<id> targetRevision: 9 appVersion: wls-v42 reason: post-updated
renderModule-style entry point or Universal engine can return one HTML string. Bundle it with the server artifact.Do not use ng run <tenant>:prerender for these content jobs. That is the current combined pipeline. The new worker uses an already built server artifact and must not serve the old prerendered file as its rendering template.
Serialize jobs for the same site and path. Use a durable publish event, such as a transactional outbox, retries, and a failed-job queue. An atomic revision check on the published pointer prevents a slow older job from replacing a newer page. A failed render keeps the previous valid page available, except where publication or access rules require removal.
The existing global DOM and module-level tenant state require an audit. Use isolated worker processes with one render at a time initially; move to concurrent renders only after proving that tenant data cannot cross between requests.
Create a public theme resource for each site, using the six existing variables:
--configBgColor --configThemeColor --configSecondaryBgColor --configPostTextColor --configButtonTextColor --configBgTextColor
Load the theme as a normal stylesheet in the document head, in the correct cascade order. Remove competing inline values and startup code that reapplies compiled colors. Cover pages, article theme controls, and ordinary page components must follow an explicit policy: the published WLS theme is authoritative unless a permitted local preview or reading mode overrides it.
The render worker receives site configuration before producing HTML. The browser can receive that public configuration inside the HTML, so a frontend API request is not required to determine the initial tenant identity or colors. A separate theme stylesheet is still a browser asset request.
| Condition | Required behavior |
|---|---|
| Strict requirement: only that WLS's colors may appear | Embed the last published colors for that site as a fallback; fresh theme CSS overrides them. If neither a valid site theme nor its fallback is available, show a neutral unavailable shell instead of the normal WLS UI. |
| Theme asset missing | Use the same site's last valid fallback, report the failure, and retry. A generic TopFans theme does not meet the strict branding requirement. |
| Site identity missing or hostname unknown | Reject normal site rendering. Never fall back to another creator or main-site. |
| Visitor already has the page open | Next load can obtain fresh colors. Updating an open page immediately needs an SSE/WebSocket notification and a stylesheet refresh. Offline visitors cannot receive live updates. |
“Instant” needs a defined promise. A save can publish a small theme without a build, but edge invalidation and propagation are not instantaneous. For strict freshness on new requests, use a theme endpoint or behavior that bypasses caching and reads the published version. For efficient caching, use versioned theme objects with a short-lived or revalidated pointer and accept a measured propagation window.
Keep mutable theme/config resources out of stale service-worker caches. Set CloudFront minimum TTL to zero where origin revalidation rules must apply. Do not return index.html for a missing CSS or JSON resource. A raw HTTP failure is preferable to an HTML response masquerading as CSS.
With separate distributions and correctly configured site origins, /tenant-theme.css resolves to each site's own file. A shared distribution needs tenant-aware cache routing:
Prateek's verified hostname → siteId prateek /tenant-theme.css → /tenants/prateek/theme/current.css James's verified hostname → siteId james /tenant-theme.css → /tenants/james/theme/current.css
Perform the rewrite before the cache lookup so the cache uses distinct paths. Validate hostnames, overwrite any client-supplied internal tenant marker, and reject attempts to request another site's internal prefix. Hostname-based routing is not a replacement for backend access control.
CloudFront KeyValueStore can hold small routing/version mappings without a function-code deployment. It should not hold full page bodies. CloudFront Functions perform routing here; the Node worker performs Angular rendering. AWS KeyValueStore documentation.
Build the generic WLS application once. Build main-site separately if it needs a different artifact. Upload immutable versioned assets once per artifact. Route all sites' release-asset requests to that shared location.
| Strategy | Compilation | Remaining work |
|---|---|---|
| Current tenant builds | Up to 1,000 WLS builds | Prerender and upload for each WLS |
| Shared build copied to tenant buckets | One WLS build | 1,000 asset copies plus affected HTML renders |
| Recommended shared asset origin | One WLS build | One shared asset upload plus affected HTML renders and publication updates |
If all 1,000 sites have 50 affected pages, there are still 50,000 HTML renders. A queue spreads that work across measured worker capacity. Do not promise a fixed speed improvement before measuring compilation, data fetches, rendering, uploads, and cache refresh separately.
HTML rendered by v42 must reference explicit v42 asset paths. HTML rendered by v43 references v43. Do not redirect every asset request to whichever version is newest.
A shared release pointer alone cannot update already generated HTML. Choose how a release becomes visible:
For the first major shared-app migration, prefer complete site rollouts. Keep old code and page objects for rollback and old browser sessions. A feature flag can enable already released behavior for selected creators; if it affects server-rendered markup, flag changes must also queue the affected renders.
A shared SSR origin can render missing or outdated HTML when requested and cache the result. This avoids rendering unvisited pages in advance, but adds runtime infrastructure and first-request latency. An S3-only origin cannot do this by itself. Start with publish-time workers if every public page must already exist as static HTML.
Use long immutable caching for versioned assets and immutable internal HTML objects. Public navigation URLs and version pointers need a freshness policy. Invalidate changed public paths, including aliases and rewritten paths where required, or select a new immutable object via an edge mapping. Cache keys must distinguish site, route, and selected revision. AWS cache-key guidance.
Audit ngsw-config.json, offline.service.js, and the active service-worker registrations. A worker serving a cached app shell can bypass new HTML and theme policies. Public prerendered routes need network behavior compatible with independent content updates, while offline pages must remain explicitly stale.
| Step | Changes | Completion checkpoint |
|---|---|---|
| 1. Restrict today's pipeline Frontend + CI | Add creator selection and unique job output paths. Filter the deployment list. Fix broad cache headers. Remove duplicate deployment-config declarations and construct validated commands instead of evaluating command strings. | A Prateek request builds/uploads Prateek only. Invalid selection cannot trigger all sites. |
| 2. Prove one-URL rendering Frontend | Add a renderer entry point using the pinned Angular/Universal version. Use an unrendered shell and a known server artifact. Initially it may reuse existing tenant artifacts. | Render one real article URL from a saved build without invoking Angular CLI compilation. |
| 3. Supply site and page data Frontend + backend | Introduce explicit tenant context. Replace tenant environment reads and generated content dependencies. Initialize browser state from the rendered snapshot. Replace global DOM assumptions and add runtime marketing route resolution. | The generic WLS renderer produces Prateek and James pages with correct, isolated branding. Main-site still works. |
| 4. Separate colors Frontend + backend + infrastructure | Publish the six theme variables as a site resource. Remove stale inline/body overrides. Define missing-theme fallback and cache/service-worker rules. | Prateek's color publish changes his initial page appearance without a build or another site's colors. |
| 5. Add selective publishing Backend + infrastructure | Seed page registry; add dependency selection, durable publish events, SQS, worker deployment, retries, revision checks, and sitemap updates. | One Prateek article edit updates its two permitted site copies and registered dependent pages. James stays untouched unless a real dependency exists. |
| 6. Share code releases CI + infrastructure + frontend | Upload shared release assets, declare affected page types, route versioned assets, stage HTML publication, and retain rollback artifacts. | An article-layout release renders all eligible article posts without per-creator Angular builds. |
For build speed, also use a reproducible install with the committed lockfile, cache dependency downloads and Angular build cache with CI enabled, and preinstall recurring tools in the build image. Avoid rewriting generated files solely to change timestamps. Reuse published content snapshots rather than fetching the full SEO dataset separately for every tenant.
The parallel buildspec sets PARALLELISM, while its script reads MAX_JOBS. Align those settings and measure memory use. Keep the required --max_old_space_size=10048 for builds. Multiple builds or render processes can exceed host memory even when each process stays below its own heap limit.
Keep Angular upgrade work separate from this publishing refactor. A newer build system may improve compilation, but it does not supply page dependency selection or independent HTML publication automatically.
The backend trigger implementation and live CloudFront settings were not inspected. Confirm the SEO post endpoint's completeness, public visibility rules, existing event mechanism, cache rewrites, and the authoritative domain registry. Decide the acceptable theme propagation delay and whether open pages must update live. Decide whether code releases require a single version per site or permit controlled page-by-page rollout.
No CONTEXT.md was found during the discussion. This document uses the existing terms WLS, post, article, prerender, S3, and CloudFront.