refactor(backend): collapse entitlement provider abstraction back to LicenseService (#889)

Removes backend/src/entitlements/ (registry, loadProvider,
CommunityEntitlementProvider, types, headers, normalize) and the two
abstraction-only tests. Relocates headers/normalize/types to
services/license-*.ts. Swaps 22 consumer call sites from
getEntitlementProvider() to LicenseService.getInstance(). Drops the
Dockerfile install step plus PRO_PACKAGE_VERSION build-arg and
github_token BuildKit secret in docker-publish.yml. Removes the now
stale no-restricted-imports rule in backend/eslint.config.mjs.

Net: 37 files changed, ~700 lines removed, no behavior change. Local
dev no longer requires GitHub Packages auth to start the backend.

Rationale and revisit conditions in
docs/internal/adrs/2026-05-02-collapse-entitlement-provider.md.
This commit is contained in:
Anso
2026-05-02 23:45:44 -04:00
committed by GitHub
parent 6929dad540
commit e5b1c7b22b
39 changed files with 150 additions and 735 deletions
+9 -9
View File
@@ -1,5 +1,5 @@
import { Router, type Request, type Response } from 'express';
import { getEntitlementProvider } from '../entitlements/registry';
import { LicenseService } from '../services/LicenseService';
import SelfUpdateService from '../services/SelfUpdateService';
import { requireAdmin } from '../middleware/tierGates';
import { rejectApiTokenScope } from '../middleware/apiTokenScope';
@@ -10,7 +10,7 @@ export const licenseRouter = Router();
licenseRouter.get('/', (_req: Request, res: Response): void => {
try {
const info = getEntitlementProvider().getLicenseInfo();
const info = LicenseService.getInstance().getLicenseInfo();
res.json(info);
} catch (error) {
console.error('[License] Error getting license info:', error);
@@ -27,9 +27,9 @@ licenseRouter.post('/activate', async (req: Request, res: Response): Promise<voi
res.status(400).json({ error: 'A valid license key is required' });
return;
}
const result = await getEntitlementProvider().activate(license_key.trim());
const result = await LicenseService.getInstance().activate(license_key.trim());
if (result.success) {
res.json({ success: true, license: getEntitlementProvider().getLicenseInfo() });
res.json({ success: true, license: LicenseService.getInstance().getLicenseInfo() });
} else {
res.status(400).json({ error: result.error });
}
@@ -43,9 +43,9 @@ licenseRouter.post('/deactivate', async (req: Request, res: Response): Promise<v
if (rejectApiTokenScope(req, res, LICENSE_SCOPE_MESSAGE)) return;
if (!requireAdmin(req, res)) return;
try {
const result = await getEntitlementProvider().deactivate();
const result = await LicenseService.getInstance().deactivate();
if (result.success) {
res.json({ success: true, license: getEntitlementProvider().getLicenseInfo() });
res.json({ success: true, license: LicenseService.getInstance().getLicenseInfo() });
} else {
res.status(500).json({ error: result.error });
}
@@ -57,8 +57,8 @@ licenseRouter.post('/deactivate', async (req: Request, res: Response): Promise<v
licenseRouter.post('/validate', async (_req: Request, res: Response): Promise<void> => {
try {
const result = await getEntitlementProvider().validate();
res.json({ ...result, license: getEntitlementProvider().getLicenseInfo() });
const result = await LicenseService.getInstance().validate();
res.json({ ...result, license: LicenseService.getInstance().getLicenseInfo() });
} catch (error) {
console.error('[License] Validation error:', error);
res.status(500).json({ error: 'License validation failed' });
@@ -67,7 +67,7 @@ licenseRouter.post('/validate', async (_req: Request, res: Response): Promise<vo
licenseRouter.get('/billing-portal', async (_req: Request, res: Response): Promise<void> => {
try {
const result = await getEntitlementProvider().getBillingPortalUrl();
const result = await LicenseService.getInstance().getBillingPortalUrl();
if ('error' in result) {
res.status(404).json({ error: result.error });
return;