1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-17 13:46:47 +02:00
unleash.unleash/src/server-dev.ts
Nuno Góis eb238f502a
chore: unknown flags (#9837)
https://linear.app/unleash/issue/2-3406/hold-unknown-flags-in-memory-and-show-them-in-the-ui-somehow

This PR introduces a suggestion for a “unknown flags” feature.

When clients report metrics for flags that don’t exist in Unleash (e.g.
due to typos), we now track a limited set of these unknown flag names
along with the appnames that reported them. The goal is to help users
identify and clean up incorrect flag usage across their apps.

We store up to 10 unknown flag + appName combinations, keeping only the
most recent reports. Data is collected in-memory and flushed
periodically to the DB, with deduplication and merging to ensure we
don’t exceed the cap even across pods.

We were especially careful to make this implementation defensive, as
unknown flags could be reported in very high volumes. Writes are
batched, deduplicated, and hard-capped to avoid DB pressure.

No UI has been added yet — this is backend-only for now and intended as
a step toward better visibility into client misconfigurations.

I would suggest starting with a simple banner that opens a dialog
showing the list of unknown flags and which apps reported them.

<img width="497" alt="image"
src="https://github.com/user-attachments/assets/b7348e0d-0163-4be4-a7f8-c072e8464331"
/>
2025-05-07 11:48:36 +01:00

98 lines
3.9 KiB
TypeScript

import { start } from './lib/server-impl';
import { createConfig } from './lib/create-config';
import { LogLevel } from './lib/logger';
import { ApiTokenType } from './lib/types/models/api-token';
process.nextTick(async () => {
try {
await start(
createConfig({
db: process.env.DATABASE_URL
? undefined
: {
user: 'unleash_user',
password: 'password',
host: 'localhost',
port: 5432,
database:
process.env.UNLEASH_DATABASE_NAME || 'unleash',
schema: process.env.UNLEASH_DATABASE_SCHEMA,
ssl: false,
applicationName: 'unleash',
},
server: {
enableRequestLogger: true,
baseUriPath: '',
// keepAliveTimeout: 1,
gracefulShutdownEnable: true,
// cdnPrefix: 'https://cdn.getunleash.io/unleash/v4.4.1',
enableHeapSnapshotEnpoint: true,
},
logLevel: LogLevel.debug,
secureHeaders: false,
versionCheck: {
enable: false,
},
experimental: {
// externalResolver: unleash,
flags: {
embedProxy: true,
embedProxyFrontend: true,
anonymiseEventLog: false,
responseTimeWithAppNameKillSwitch: false,
outdatedSdksBanner: true,
disableShowContextFieldSelectionValues: false,
feedbackPosting: true,
manyStrategiesPagination: true,
enableLegacyVariants: false,
extendedMetrics: true,
originMiddlewareRequestLogging: true,
webhookDomainLogging: true,
releasePlans: false,
showUserDeviceCount: true,
deltaApi: true,
uniqueSdkTracking: true,
filterExistingFlagNames: true,
teamsIntegrationChangeRequests: true,
tagTypeColor: true,
newStrategyDropdown: true,
addEditStrategy: true,
flagsOverviewSearch: true,
cleanupReminder: true,
strictSchemaValidation: true,
registerFrontendClient: true,
featureLinks: true,
reportUnknownFlags: true,
},
},
authentication: {
initApiTokens: [
{
environment: '*',
project: '*',
secret: '*:*.964a287e1b728cb5f4f3e0120df92cb5',
type: ApiTokenType.ADMIN,
tokenName: 'some-user',
},
],
},
/* can be tweaked to control configuration caching for /api/client/features
clientFeatureCaching: {
enabled: true,
maxAge: 4000,
},
*/
}),
);
} catch (error) {
if (error.code === 'EADDRINUSE') {
// eslint-disable-next-line no-console
console.warn('Port in use. You might want to reload once more.');
} else {
// eslint-disable-next-line no-console
console.error(error);
process.exit();
}
}
}, 0);