feat(ce): tear down BE edition-gating engine
Delete the feature-flags edition machinery (isBE, init/selectShow/ isLimitedToBE, FeatureId/Edition/FeatureState enums, BEFeatureIndicator, BEOverlay, BETeaserButton, withEdition, useLimitToBE, limitedFeatureDir) now that all consumers are gone, drop the initFeatureService bootstrap, and update tests/stories to assert CE-only behaviour. Mechanism B (runtime FeatureFlag) and withHideOnExtension are left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,6 @@ import './i18n';
|
||||
import angular from 'angular';
|
||||
import { UI_ROUTER_REACT_HYBRID } from '@uirouter/react-hybrid';
|
||||
|
||||
import { Edition } from '@/react/portainer/feature-flags/enums';
|
||||
import { init as initFeatureService } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
|
||||
import './agent';
|
||||
import { azureModule } from './azure';
|
||||
import './docker/__module';
|
||||
@@ -30,8 +27,6 @@ if (window.origin == 'http://localhost:49000') {
|
||||
document.getElementById('base').href = basePath;
|
||||
}
|
||||
|
||||
initFeatureService(Edition[process.env.PORTAINER_EDITION]);
|
||||
|
||||
angular
|
||||
.module('portainer', [
|
||||
'ui.bootstrap',
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
export enum Edition {
|
||||
CE,
|
||||
BE,
|
||||
}
|
||||
|
||||
export enum FeatureState {
|
||||
HIDDEN,
|
||||
VISIBLE,
|
||||
LIMITED_BE,
|
||||
}
|
||||
|
||||
export enum FeatureId {
|
||||
K8S_RESOURCE_POOL_LB_QUOTA = 'k8s-resourcepool-Ibquota',
|
||||
K8S_RESOURCE_POOL_STORAGE_QUOTA = 'k8s-resourcepool-storagequota',
|
||||
K8S_CREATE_FROM_KUBECONFIG = 'k8s-create-from-kubeconfig',
|
||||
K8S_EDIT_YAML = 'k8s-edit-yaml',
|
||||
KAAS_PROVISIONING = 'kaas-provisioning',
|
||||
RBAC_ROLES = 'rbac-roles',
|
||||
REGISTRY_MANAGEMENT = 'registry-management',
|
||||
K8S_SETUP_DEFAULT = 'k8s-setup-default',
|
||||
S3_BACKUP_SETTING = 's3-backup-setting',
|
||||
S3_RESTORE = 'restore-s3-form',
|
||||
HIDE_INTERNAL_AUTHENTICATION_PROMPT = 'hide-internal-authentication-prompt',
|
||||
TEAM_MEMBERSHIP = 'team-membership',
|
||||
HIDE_INTERNAL_AUTH = 'hide-internal-auth',
|
||||
EXTERNAL_AUTH_LDAP = 'external-auth-ldap',
|
||||
ACTIVITY_AUDIT = 'activity-audit',
|
||||
FORCE_REDEPLOYMENT = 'force-redeployment',
|
||||
HIDE_AUTO_UPDATE_WINDOW = 'hide-auto-update-window',
|
||||
IMAGE_UP_TO_DATE_INDICATOR = 'image-up-to-date-indicator',
|
||||
STACK_PULL_IMAGE = 'stack-pull-image',
|
||||
STACK_WEBHOOK = 'stack-webhook',
|
||||
CONTAINER_WEBHOOK = 'container-webhook',
|
||||
POD_SECURITY_POLICY_CONSTRAINT = 'pod-security-policy-constraint',
|
||||
HIDE_DOCKER_HUB_ANONYMOUS = 'hide-docker-hub-anonymous',
|
||||
CUSTOM_LOGIN_BANNER = 'custom-login-banner',
|
||||
ENFORCE_DEPLOYMENT_OPTIONS = 'k8s-enforce-deployment-options',
|
||||
K8S_ADM_ONLY_USR_INGRESS_DEPLY = 'k8s-admin-only-ingress-deploy',
|
||||
K8S_ADM_ONLY_SECRETS = 'k8s-admin-only-secrets',
|
||||
K8S_ROLLING_RESTART = 'k8s-rolling-restart',
|
||||
K8SINSTALL = 'k8s-install',
|
||||
KUBESOLO = 'kubesolo',
|
||||
K8S_ANNOTATIONS = 'k8s-annotations',
|
||||
CA_FILE = 'ca-file',
|
||||
K8S_REQUIRE_NOTE_ON_APPLICATIONS = 'k8s-note-on-applications',
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
.form-control.limited-be {
|
||||
border-color: var(--BE-only);
|
||||
}
|
||||
|
||||
.form-control.limited-be.no-border {
|
||||
border-color: var(--border-form-control-color);
|
||||
}
|
||||
|
||||
button.limited-be,
|
||||
button[disabled].limited-be.oauth-save-settings-button {
|
||||
background-color: var(--BE-only);
|
||||
border-color: var(--BE-only);
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
button.limited-be.oauth-save-settings-button {
|
||||
background-color: var(--blue-2);
|
||||
border-color: transparent;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
ng-form.limited-be,
|
||||
form.limited-be,
|
||||
div.limited-be {
|
||||
border: solid 1px var(--BE-only);
|
||||
border-radius: 8px;
|
||||
pointer-events: none;
|
||||
touch-action: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.limited-be-content {
|
||||
@apply border-gray-6 p-2.5 opacity-50;
|
||||
}
|
||||
|
||||
.limited-be-link {
|
||||
padding: 10px;
|
||||
width: inherit;
|
||||
z-index: 5;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
float: right;
|
||||
border-top-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
touch-action: auto;
|
||||
cursor: hand;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.limited-be-link a {
|
||||
@apply text-gray-6;
|
||||
}
|
||||
|
||||
.limited-be-link a:hover {
|
||||
@apply underline;
|
||||
@apply border-blue-9 text-blue-9;
|
||||
}
|
||||
|
||||
.form-control.limited-be[disabled] {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import { Edition, FeatureId, FeatureState } from './enums';
|
||||
|
||||
export const isBE = process.env.PORTAINER_EDITION === 'BE';
|
||||
interface ServiceState {
|
||||
currentEdition: Edition;
|
||||
features: Record<FeatureId, Edition>;
|
||||
}
|
||||
|
||||
const state: ServiceState = {
|
||||
currentEdition: Edition.CE,
|
||||
features: <Record<FeatureId, Edition>>{},
|
||||
};
|
||||
|
||||
export async function init(edition: Edition) {
|
||||
// will be loaded on runtime
|
||||
const currentEdition = edition;
|
||||
const features = {
|
||||
[FeatureId.K8S_RESOURCE_POOL_LB_QUOTA]: Edition.BE,
|
||||
[FeatureId.K8S_RESOURCE_POOL_STORAGE_QUOTA]: Edition.BE,
|
||||
[FeatureId.K8S_CREATE_FROM_KUBECONFIG]: Edition.BE,
|
||||
[FeatureId.KAAS_PROVISIONING]: Edition.BE,
|
||||
[FeatureId.K8SINSTALL]: Edition.BE,
|
||||
[FeatureId.KUBESOLO]: Edition.BE,
|
||||
[FeatureId.ACTIVITY_AUDIT]: Edition.BE,
|
||||
[FeatureId.EXTERNAL_AUTH_LDAP]: Edition.BE,
|
||||
[FeatureId.HIDE_INTERNAL_AUTH]: Edition.BE,
|
||||
[FeatureId.HIDE_INTERNAL_AUTHENTICATION_PROMPT]: Edition.BE,
|
||||
[FeatureId.K8S_SETUP_DEFAULT]: Edition.BE,
|
||||
[FeatureId.RBAC_ROLES]: Edition.BE,
|
||||
[FeatureId.REGISTRY_MANAGEMENT]: Edition.BE,
|
||||
[FeatureId.S3_BACKUP_SETTING]: Edition.BE,
|
||||
[FeatureId.S3_RESTORE]: Edition.BE,
|
||||
[FeatureId.TEAM_MEMBERSHIP]: Edition.BE,
|
||||
[FeatureId.FORCE_REDEPLOYMENT]: Edition.BE,
|
||||
[FeatureId.HIDE_AUTO_UPDATE_WINDOW]: Edition.BE,
|
||||
[FeatureId.IMAGE_UP_TO_DATE_INDICATOR]: Edition.BE,
|
||||
[FeatureId.STACK_PULL_IMAGE]: Edition.BE,
|
||||
[FeatureId.STACK_WEBHOOK]: Edition.BE,
|
||||
[FeatureId.CONTAINER_WEBHOOK]: Edition.BE,
|
||||
[FeatureId.POD_SECURITY_POLICY_CONSTRAINT]: Edition.BE,
|
||||
[FeatureId.HIDE_DOCKER_HUB_ANONYMOUS]: Edition.BE,
|
||||
[FeatureId.CUSTOM_LOGIN_BANNER]: Edition.BE,
|
||||
[FeatureId.K8S_EDIT_YAML]: Edition.BE,
|
||||
[FeatureId.ENFORCE_DEPLOYMENT_OPTIONS]: Edition.BE,
|
||||
[FeatureId.K8S_ADM_ONLY_USR_INGRESS_DEPLY]: Edition.BE,
|
||||
[FeatureId.K8S_ADM_ONLY_SECRETS]: Edition.BE,
|
||||
[FeatureId.K8S_ROLLING_RESTART]: Edition.BE,
|
||||
[FeatureId.K8S_ANNOTATIONS]: Edition.BE,
|
||||
[FeatureId.CA_FILE]: Edition.BE,
|
||||
[FeatureId.K8S_REQUIRE_NOTE_ON_APPLICATIONS]: Edition.BE,
|
||||
};
|
||||
|
||||
state.currentEdition = currentEdition;
|
||||
state.features = features;
|
||||
}
|
||||
|
||||
export function selectShow(featureId?: FeatureId) {
|
||||
if (!featureId) {
|
||||
return FeatureState.VISIBLE;
|
||||
}
|
||||
|
||||
if (!state.features[featureId]) {
|
||||
return FeatureState.HIDDEN;
|
||||
}
|
||||
|
||||
if (state.features[featureId] <= state.currentEdition) {
|
||||
return FeatureState.VISIBLE;
|
||||
}
|
||||
|
||||
if (state.features[featureId] === Edition.BE) {
|
||||
return FeatureState.LIMITED_BE;
|
||||
}
|
||||
|
||||
return FeatureState.HIDDEN;
|
||||
}
|
||||
|
||||
export function isLimitedToBE(featureId?: FeatureId) {
|
||||
return selectShow(featureId) === FeatureState.LIMITED_BE;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import angular from 'angular';
|
||||
|
||||
import { limitedFeatureDirective } from './limited-feature.directive';
|
||||
import './feature-flags.css';
|
||||
|
||||
export default angular
|
||||
.module('portainer.feature-flags', [])
|
||||
.directive('limitedFeatureDir', limitedFeatureDirective).name;
|
||||
@@ -1,44 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import { IAttributes, IDirective, IScope } from 'angular';
|
||||
|
||||
import { FeatureState } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { selectShow } from './feature-flags.service';
|
||||
|
||||
const BASENAME = 'limitedFeature';
|
||||
|
||||
/* @ngInject */
|
||||
export function limitedFeatureDirective(): IDirective {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link,
|
||||
};
|
||||
|
||||
function link(scope: IScope, elem: JQLite, attrs: IAttributes) {
|
||||
const { limitedFeatureDir: featureId } = attrs;
|
||||
|
||||
if (!featureId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limitedFeatureAttrs = Object.keys(attrs)
|
||||
.filter((attr) => attr.startsWith(BASENAME) && attr !== `${BASENAME}Dir`)
|
||||
.map((attr) => [_.kebabCase(attr.replace(BASENAME, '')), attrs[attr]]);
|
||||
|
||||
const state = selectShow(featureId);
|
||||
|
||||
if (state === FeatureState.HIDDEN) {
|
||||
elem.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (state === FeatureState.VISIBLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
limitedFeatureAttrs.forEach(([attr, value = attr]) => {
|
||||
const currentValue = elem.attr(attr) || '';
|
||||
elem.attr(attr, `${currentValue} ${value}`.trim());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
export function withEdition<T>(
|
||||
WrappedComponent: ComponentType<T>,
|
||||
edition: 'BE' | 'CE'
|
||||
): ComponentType<T> {
|
||||
// Try to create a nice displayName for React Dev Tools.
|
||||
const displayName =
|
||||
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||
|
||||
function WrapperComponent(props: T & JSX.IntrinsicAttributes) {
|
||||
if (process.env.PORTAINER_EDITION !== edition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <WrappedComponent {...props} />;
|
||||
}
|
||||
|
||||
WrapperComponent.displayName = `with${edition}Edition(${displayName})`;
|
||||
|
||||
return WrapperComponent;
|
||||
}
|
||||
Reference in New Issue
Block a user