unleash-server
Version:
Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.
228 lines • 13.2 kB
JavaScript
import stoppable from 'stoppable';
import { promisify } from 'util';
import version from './util/version.js';
import { migrateDb, requiresMigration, resetDb } from '../migrator.js';
import getApp from './app.js';
import { createMetricsMonitor } from './metrics.js';
import { createStores } from './db/index.js';
import { createConfig } from './create-config.js';
import registerGracefulShutdown from './util/graceful-shutdown.js';
import { createDb } from './db/db-pool.js';
import sessionDb from './middleware/session-db.js';
import { ApiTokenType } from './types/index.js'; // Not really a type. It's an enum so we need the value
import { createServices, } from './services/index.js';
import { defaultLockKey, defaultTimeout, withDbLock } from './util/db-lock.js';
import { scheduleServices } from './features/scheduler/schedule-services.js';
import { compareAndLogPostgresVersion } from './util/postgres-version-checker.js';
import { createKnexTransactionStarter, withFakeTransactional, withRollbackTransaction, withTransactional, } from './db/transaction.js';
import Controller from './routes/controller.js';
import { createClientFeatureToggleDelta } from './features/client-feature-toggles/delta/createClientFeatureToggleDelta.js';
import { CRUDStore } from './db/crud/crud-store.js';
import { LogLevel } from './logger.js';
import { ImportTogglesStore } from './features/export-import-toggles/import-toggles-store.js';
import { ALL_PROJECTS, CUSTOM_ROOT_ROLE_TYPE } from './util/index.js';
import { extractAuditInfoFromUser, getVariantValue, isDefined, parseEnvVarBoolean, randomId, } from './util/index.js';
import { createTestConfig } from '../test/config/test-config.js';
import NoAuthUser from './types/no-auth-user.js';
import { ALL, isAllProjects } from './types/models/api-token.js';
import { defaultFromRow, defaultToRow } from './db/crud/default-mappings.js';
import { querySchema } from './schema/feature-schema.js';
import { basePaginationParameters, } from './openapi/spec/base-pagination-parameters.js';
import { flattenPayload } from './util/flattenPayload.js';
import { UPDATE_DELTA, } from './features/client-feature-toggles/delta/client-feature-toggle-delta.js';
import { applyGenericQueryParams, normalizeQueryParams, parseSearchOperatorValue, } from './features/feature-search/search-utils.js';
import { createAccessService, createChangeRequestAccessReadModel, createContextService, createEventsService, createFakeInstanceStatsService, createFakeProjectService, createFeatureToggleService, createInstanceStatsService, createProjectService, createTagTypeService, createExportImportTogglesService, DB_TIME, findParam, conditionalMiddleware, createPlaygroundService, createSegmentService, createFakeEventsService, createFakeFeatureToggleService, createFakeSegmentService, createDependentFeaturesService, createFakeDependentFeaturesService, createFakeAccessService, corsOriginMiddleware, impactRegister, } from './internals.js';
import SessionStore from './db/session-store.js';
import metricsHelper from './util/metrics-helper.js';
import { EventStore } from './db/event-store.js';
import RoleStore from './db/role-store.js';
import { AccessStore } from './db/access-store.js';
import { addAjvSchema, validateSchema, } from './openapi/validate.js';
import { createCounter, createGauge } from './util/metrics/index.js';
import FakeEventStore from '../test/fixtures/fake-event-store.js';
import { UserSubscriptionsReadModel } from './features/user-subscriptions/user-subscriptions-read-model.js';
import { FakeUserSubscriptionsReadModel } from './features/user-subscriptions/fake-user-subscriptions-read-model.js';
import { FakePrivateProjectChecker } from './features/private-project/fakePrivateProjectChecker.js';
import { createFakeProjectReadModel, createProjectReadModel, } from './features/project/createProjectReadModel.js';
import { createFakePrivateProjectChecker, createPrivateProjectChecker, } from './features/private-project/createPrivateProjectChecker.js';
import { SegmentReadModel } from './features/segment/segment-read-model.js';
import { FeatureEnvironmentStore } from './db/feature-environment-store.js';
import StrategyStore from './db/strategy-store.js';
import { ChangeRequestAccessReadModel } from './features/change-request-access-service/sql-change-request-access-read-model.js';
import { ImportPermissionsService } from './features/export-import-toggles/import-permissions-service.js';
import FeatureStrategiesStore from './features/feature-toggle/feature-toggle-strategies-store.js';
import TagStore from './db/tag-store.js';
import FeatureToggleStore from './features/feature-toggle/feature-toggle-store.js';
import FeatureTagStore from './db/feature-tag-store.js';
import ExportImportController from './features/export-import-toggles/export-import-controller.js';
import { DELTA_EVENT_TYPES } from './features/client-feature-toggles/delta/client-feature-toggle-delta-types.js';
import { advancedPlaygroundViewModel } from './features/playground/playground-view-model.js';
import { createAccessReadModel, createFakeAccessReadModel, } from './features/access/createAccessReadModel.js';
import { getDefaultStrategy, getProjectDefaultStrategy, } from './features/playground/feature-evaluator/helpers.js';
import { getDbConfig } from '../test/e2e/helpers/database-config.js';
import { testDbPrefix } from '../test/e2e/helpers/database-init.js';
import { UPDATE_REVISION } from './features/feature-toggle/configuration-revision-service.js';
import { defineImpactMetrics } from './features/metrics/impact/define-impact-metrics.js';
export async function initialServiceSetup({ authentication }, { userService, apiTokenService, }) {
await userService.initAdminUser(authentication);
if (authentication.initApiTokens.length > 0) {
await apiTokenService.initApiTokens(authentication.initApiTokens);
}
}
export async function createApp(config, startApp, fm = {
createDb,
createStores,
createServices,
createSessionDb: sessionDb,
createMetricsMonitor,
}) {
// Database dependencies (stateful)
const logger = config.getLogger('server-impl.js');
const serverVersion = config.enterpriseVersion ?? version;
const db = fm.createDb(config);
const stores = fm.createStores(config, db);
await compareAndLogPostgresVersion(config, stores.settingStore);
const services = fm.createServices(stores, config, db);
await initialServiceSetup(config, services);
if (!config.disableScheduler) {
scheduleServices(services, config);
}
defineImpactMetrics(config.flagResolver);
const metricsMonitor = fm.createMetricsMonitor();
const unleashSession = fm.createSessionDb(config, db);
const stopUnleash = async (server) => {
logger.info('Shutting down Unleash...');
if (server) {
const stopServer = promisify(server.stop);
await stopServer();
}
if (typeof config.shutdownHook === 'function') {
try {
await config.shutdownHook();
}
catch (e) {
logger.error('Failure when executing shutdown hook', e);
}
}
services.schedulerService.stop();
services.addonService.destroy();
await db.destroy();
};
if (!config.server.secret) {
const secret = await stores.settingStore.get('unleash.secret');
config.server.secret = secret;
}
const app = await getApp(config, stores, services, unleashSession, db);
await metricsMonitor.startMonitoring(config, stores, serverVersion, config.eventBus, services.instanceStatsService, services.schedulerService, db);
const unleash = {
stores,
eventBus: config.eventBus,
services,
app,
config,
version: serverVersion,
};
if (config.import.file) {
await services.importService.importFromFile(config.import.file, config.import.project, config.import.environment);
}
if (config.environmentEnableOverrides &&
config.environmentEnableOverrides?.length > 0) {
await services.environmentService.overrideEnabledProjects(config.environmentEnableOverrides);
}
return new Promise((resolve, reject) => {
if (startApp) {
const server = stoppable(app.listen(config.listen, () => logger.info('Unleash has started.', server.address())), config.server.gracefulShutdownTimeout);
server.keepAliveTimeout = config.server.keepAliveTimeout;
server.headersTimeout = config.server.headersTimeout;
server.on('listening', () => {
resolve({
...unleash,
server,
stop: () => stopUnleash(server),
});
});
server.on('error', reject);
}
else {
resolve({ ...unleash, stop: stopUnleash });
}
});
}
async function start(opts = {}, fm = {
createDb,
createStores,
createServices,
createSessionDb: sessionDb,
createMetricsMonitor,
}) {
const config = createConfig(opts);
const logger = config.getLogger('server-impl.js');
try {
if (config.db.disableMigration) {
logger.info('DB migration: disabled');
}
else {
if (await requiresMigration(config)) {
logger.info('DB migration: start');
if (config.flagResolver.isEnabled('migrationLock')) {
logger.info('Running migration with lock');
const lock = withDbLock(config.db, {
lockKey: defaultLockKey,
timeout: defaultTimeout,
logger,
});
await lock(migrateDb)(config);
}
else {
logger.info('Running migration without lock');
await migrateDb(config);
}
logger.info('DB migration: end');
}
else {
logger.info('DB migration: no migration needed');
}
}
}
catch (err) {
logger.error('Failed to migrate db', err);
throw err;
}
const unleash = await createApp(config, true, fm);
if (config.server.gracefulShutdownEnable) {
registerGracefulShutdown(unleash, logger);
}
return unleash;
}
async function create(opts, fm = {
createDb,
createStores,
createServices,
createSessionDb: sessionDb,
createMetricsMonitor,
}) {
const config = createConfig(opts);
const logger = config.getLogger('server-impl.js');
try {
if (config.db.disableMigration) {
logger.info('DB migrations disabled');
}
else {
await migrateDb(config);
}
}
catch (err) {
logger.error('Failed to migrate db', err);
throw err;
}
return createApp(config, false, fm);
}
export { start, create, scheduleServices, createDb, resetDb, getDbConfig, testDbPrefix, Controller, LogLevel, withRollbackTransaction, withTransactional, createClientFeatureToggleDelta, CRUDStore, ImportTogglesStore, ALL_PROJECTS, ALL, isAllProjects, extractAuditInfoFromUser, createTestConfig, NoAuthUser, defaultFromRow, defaultToRow, isDefined, parseEnvVarBoolean, querySchema, basePaginationParameters, migrateDb, flattenPayload, randomId, CUSTOM_ROOT_ROLE_TYPE, getVariantValue, UPDATE_DELTA, UPDATE_REVISION, applyGenericQueryParams, normalizeQueryParams, parseSearchOperatorValue, createEventsService, SessionStore, createAccessService, metricsHelper, DB_TIME, EventStore, FakeEventStore, createChangeRequestAccessReadModel, createFeatureToggleService, createProjectService, createFakeProjectService, findParam, RoleStore, AccessStore, validateSchema, addAjvSchema, createCounter, createGauge, UserSubscriptionsReadModel, FakeUserSubscriptionsReadModel, FakePrivateProjectChecker, createFakeProjectReadModel, createFakePrivateProjectChecker, createProjectReadModel, createPrivateProjectChecker, createFakeInstanceStatsService, createInstanceStatsService, SegmentReadModel, FeatureEnvironmentStore, ChangeRequestAccessReadModel, StrategyStore, ImportPermissionsService, FeatureStrategiesStore, TagStore, FeatureToggleStore, createTagTypeService, createContextService, createExportImportTogglesService, FeatureTagStore, ExportImportController, conditionalMiddleware, DELTA_EVENT_TYPES, createKnexTransactionStarter, createPlaygroundService, advancedPlaygroundViewModel, withFakeTransactional, createAccessReadModel, createFakeAccessService, createFakeAccessReadModel, createFakeSegmentService, createSegmentService, createFakeFeatureToggleService, createFakeEventsService, createDependentFeaturesService, createFakeDependentFeaturesService, getProjectDefaultStrategy, getDefaultStrategy, corsOriginMiddleware, ApiTokenType, impactRegister, };
export * from './openapi/index.js';
export * from './types/index.js';
export * from './error/index.js';
export * from './util/index.js';
export * from './services/index.js';
export * as eventtypes from './events/index.js';
export * as interfaces from './interfaces/index.js';
//# sourceMappingURL=server-impl.js.map