From 8674781981fef30aeefd294412f62729b7f5f448 Mon Sep 17 00:00:00 2001 From: Lilith Date: Thu, 22 Jan 2026 23:03:13 -0800 Subject: [PATCH] =?UTF-8?q?chore(feature-flags):=20=F0=9F=94=A7=20Implemen?= =?UTF-8?q?t=20feature=20flag=20system=20with=20backend=20CRUD=20APIs,=20o?= =?UTF-8?q?verride=20management,=20audit=20logging,=20health=20checks,=20a?= =?UTF-8?q?nd=20admin=20UI=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend-api/src/app.module.ts | 3 ++- .../src/dto/create-feature-flag.dto.ts | 2 +- .../src/dto/create-override.dto.ts | 2 +- .../src/dto/update-feature-flag.dto.ts | 1 + .../src/entities/feature-flag-audit.entity.ts | 2 +- .../entities/feature-flag-override.entity.ts | 3 ++- .../src/entities/feature-flag.entity.ts | 2 +- .../src/health/health.controller.ts | 4 ++-- .../feature-flags/backend-api/src/main.ts | 1 + .../src/modules/flags/flags.controller.ts | 5 ++++- .../src/modules/flags/flags.module.ts | 2 ++ .../src/modules/flags/flags.service.ts | 11 +++++----- .../feature-flags/frontend-admin/src/App.tsx | 21 +++++++++---------- .../frontend-admin/src/api/flags.ts | 4 ++-- .../src/components/FlagCard.tsx | 4 +++- 15 files changed, 39 insertions(+), 28 deletions(-) diff --git a/features/feature-flags/backend-api/src/app.module.ts b/features/feature-flags/backend-api/src/app.module.ts index 4e662d10e..8b36ba9e4 100755 --- a/features/feature-flags/backend-api/src/app.module.ts +++ b/features/feature-flags/backend-api/src/app.module.ts @@ -1,8 +1,9 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { FlagsModule } from './modules/flags'; + import { HealthController } from './health/health.controller'; +import { FlagsModule } from './modules/flags'; @Module({ imports: [ diff --git a/features/feature-flags/backend-api/src/dto/create-feature-flag.dto.ts b/features/feature-flags/backend-api/src/dto/create-feature-flag.dto.ts index 0e77e9cf0..1d64fae5b 100755 --- a/features/feature-flags/backend-api/src/dto/create-feature-flag.dto.ts +++ b/features/feature-flags/backend-api/src/dto/create-feature-flag.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; import { IsString, IsBoolean, @@ -12,7 +13,6 @@ import { Max, Matches, } from 'class-validator'; -import { Type } from 'class-transformer'; export class CreateFeatureFlagDto { @ApiProperty({ description: 'Unique key for the feature flag', example: 'trustedmeet-marketplace' }) diff --git a/features/feature-flags/backend-api/src/dto/create-override.dto.ts b/features/feature-flags/backend-api/src/dto/create-override.dto.ts index ce48520c6..bd95ac3a7 100755 --- a/features/feature-flags/backend-api/src/dto/create-override.dto.ts +++ b/features/feature-flags/backend-api/src/dto/create-override.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsString, IsBoolean, IsOptional, IsDate } from 'class-validator'; import { Type } from 'class-transformer'; +import { IsString, IsBoolean, IsOptional, IsDate } from 'class-validator'; export class CreateOverrideDto { @ApiPropertyOptional({ description: 'User ID for user-specific override' }) diff --git a/features/feature-flags/backend-api/src/dto/update-feature-flag.dto.ts b/features/feature-flags/backend-api/src/dto/update-feature-flag.dto.ts index 0ecb6c26d..e4e61465e 100755 --- a/features/feature-flags/backend-api/src/dto/update-feature-flag.dto.ts +++ b/features/feature-flags/backend-api/src/dto/update-feature-flag.dto.ts @@ -1,4 +1,5 @@ import { PartialType, OmitType } from '@nestjs/swagger'; + import { CreateFeatureFlagDto } from './create-feature-flag.dto'; export class UpdateFeatureFlagDto extends PartialType( diff --git a/features/feature-flags/backend-api/src/entities/feature-flag-audit.entity.ts b/features/feature-flags/backend-api/src/entities/feature-flag-audit.entity.ts index eec7ca825..761de69bf 100755 --- a/features/feature-flags/backend-api/src/entities/feature-flag-audit.entity.ts +++ b/features/feature-flags/backend-api/src/entities/feature-flag-audit.entity.ts @@ -1,9 +1,9 @@ +import { BaseEntity } from '@lilith/typeorm-entities'; import { Entity, Column, Index, } from 'typeorm'; -import { BaseEntity } from '@lilith/typeorm-entities'; /** * Feature Flag Audit Entity diff --git a/features/feature-flags/backend-api/src/entities/feature-flag-override.entity.ts b/features/feature-flags/backend-api/src/entities/feature-flag-override.entity.ts index b57e03dfa..febf48fcf 100755 --- a/features/feature-flags/backend-api/src/entities/feature-flag-override.entity.ts +++ b/features/feature-flags/backend-api/src/entities/feature-flag-override.entity.ts @@ -1,3 +1,4 @@ +import { AuditableEntity } from '@lilith/typeorm-entities'; import { Entity, Column, @@ -5,7 +6,7 @@ import { JoinColumn, Index, } from 'typeorm'; -import { AuditableEntity } from '@lilith/typeorm-entities'; + import { FeatureFlagEntity } from './feature-flag.entity'; /** diff --git a/features/feature-flags/backend-api/src/entities/feature-flag.entity.ts b/features/feature-flags/backend-api/src/entities/feature-flag.entity.ts index 3cc7ba972..ef12171ea 100755 --- a/features/feature-flags/backend-api/src/entities/feature-flag.entity.ts +++ b/features/feature-flags/backend-api/src/entities/feature-flag.entity.ts @@ -1,9 +1,9 @@ +import { AuditableEntity } from '@lilith/typeorm-entities'; import { Entity, Column, Index, } from 'typeorm'; -import { AuditableEntity } from '@lilith/typeorm-entities'; /** * Feature Flag Entity diff --git a/features/feature-flags/backend-api/src/health/health.controller.ts b/features/feature-flags/backend-api/src/health/health.controller.ts index 71cb8a8e5..364c701fe 100755 --- a/features/feature-flags/backend-api/src/health/health.controller.ts +++ b/features/feature-flags/backend-api/src/health/health.controller.ts @@ -1,7 +1,7 @@ -import { Controller } from '@nestjs/common'; -import { SkipThrottle } from '@nestjs/throttler'; import { BaseHealthController, DependencyHealth } from '@lilith/nestjs-health'; import { TypeOrmConnectionIndicator } from '@lilith/nestjs-health/indicators'; +import { Controller } from '@nestjs/common'; +import { SkipThrottle } from '@nestjs/throttler'; import { InjectConnection } from '@nestjs/typeorm'; import { Connection } from 'typeorm'; diff --git a/features/feature-flags/backend-api/src/main.ts b/features/feature-flags/backend-api/src/main.ts index 3a1caca8b..3cf109b05 100644 --- a/features/feature-flags/backend-api/src/main.ts +++ b/features/feature-flags/backend-api/src/main.ts @@ -1,4 +1,5 @@ import { bootstrap, presets} from '@lilith/service-nestjs-bootstrap'; + import { AppModule } from './app.module'; async function main() { diff --git a/features/feature-flags/backend-api/src/modules/flags/flags.controller.ts b/features/feature-flags/backend-api/src/modules/flags/flags.controller.ts index 514127398..bd53adfcd 100755 --- a/features/feature-flags/backend-api/src/modules/flags/flags.controller.ts +++ b/features/feature-flags/backend-api/src/modules/flags/flags.controller.ts @@ -19,8 +19,11 @@ import { ApiParam, ApiQuery, } from '@nestjs/swagger'; -import type { Request } from 'express'; + import { FlagsService } from './flags.service'; + +import type { Request } from 'express'; + import { CreateFeatureFlagDto, UpdateFeatureFlagDto, diff --git a/features/feature-flags/backend-api/src/modules/flags/flags.module.ts b/features/feature-flags/backend-api/src/modules/flags/flags.module.ts index 4ca38c938..1da9b60a2 100755 --- a/features/feature-flags/backend-api/src/modules/flags/flags.module.ts +++ b/features/feature-flags/backend-api/src/modules/flags/flags.module.ts @@ -1,7 +1,9 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; + import { FlagsController } from './flags.controller'; import { FlagsService } from './flags.service'; + import { FeatureFlagEntity, FeatureFlagOverrideEntity, diff --git a/features/feature-flags/backend-api/src/modules/flags/flags.service.ts b/features/feature-flags/backend-api/src/modules/flags/flags.service.ts index f248e0cef..48748ad93 100755 --- a/features/feature-flags/backend-api/src/modules/flags/flags.service.ts +++ b/features/feature-flags/backend-api/src/modules/flags/flags.service.ts @@ -1,17 +1,18 @@ import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { - FeatureFlagEntity, - FeatureFlagOverrideEntity, - FeatureFlagAuditEntity, -} from '@/entities'; + import { CreateFeatureFlagDto, UpdateFeatureFlagDto, CreateOverrideDto, EvaluateFlagsDto, } from '@/dto'; +import { + FeatureFlagEntity, + FeatureFlagOverrideEntity, + FeatureFlagAuditEntity, +} from '@/entities'; // Feature flag types (from @lilith/feature-flags) export type Environment = 'development' | 'staging' | 'production' | 'all'; export type UserRole = 'guest' | 'user' | 'provider' | 'client' | 'investor' | 'admin' | 'all'; diff --git a/features/feature-flags/frontend-admin/src/App.tsx b/features/feature-flags/frontend-admin/src/App.tsx index 14e58ce28..73055d514 100755 --- a/features/feature-flags/frontend-admin/src/App.tsx +++ b/features/feature-flags/frontend-admin/src/App.tsx @@ -1,13 +1,13 @@ -import { Routes, Route, Navigate } from '@lilith/ui-router'; import { DeveloperFab } from '@lilith/ui-developer-fab'; -import { FlagListPage } from './pages/FlagListPage'; -import { FlagDetailPage } from './pages/FlagDetailPage'; -import { CreateFlagPage } from './pages/CreateFlagPage'; -import { AuditLogPage } from './pages/AuditLogPage'; -import { Layout } from './components/Layout'; +import { Routes, Route, Navigate } from '@lilith/ui-router'; -function App() { - return ( +import { Layout } from './components/Layout'; +import { AuditLogPage } from './pages/AuditLogPage'; +import { CreateFlagPage } from './pages/CreateFlagPage'; +import { FlagDetailPage } from './pages/FlagDetailPage'; +import { FlagListPage } from './pages/FlagListPage'; + +const App = () => ( <> @@ -25,11 +25,10 @@ function App() { { value: 'employee', label: 'Employee' }, { value: 'admin', label: 'Admin' }, ]} - showStorage={true} + showStorage /> )} - ); -} + ) export default App; diff --git a/features/feature-flags/frontend-admin/src/api/flags.ts b/features/feature-flags/frontend-admin/src/api/flags.ts index 8c70d89df..3b6ec0061 100755 --- a/features/feature-flags/frontend-admin/src/api/flags.ts +++ b/features/feature-flags/frontend-admin/src/api/flags.ts @@ -161,8 +161,8 @@ export const flagsApi = { async getAuditLog(key?: string, limit?: number): Promise { const params = new URLSearchParams(); - if (key) params.set('key', key); - if (limit) params.set('limit', String(limit)); + if (key) {params.set('key', key);} + if (limit) {params.set('limit', String(limit));} const response = await fetch(`${API_BASE}/audit/log?${params}`); return handleResponse(response); }, diff --git a/features/feature-flags/frontend-admin/src/components/FlagCard.tsx b/features/feature-flags/frontend-admin/src/components/FlagCard.tsx index fdcb5826e..e904dcbbe 100755 --- a/features/feature-flags/frontend-admin/src/components/FlagCard.tsx +++ b/features/feature-flags/frontend-admin/src/components/FlagCard.tsx @@ -1,13 +1,15 @@ import { Link } from '@lilith/ui-router'; import clsx from 'clsx'; + import type { FeatureFlag } from '@/api/flags'; + import { useToggleFlag } from '@/hooks/useFlags'; interface FlagCardProps { flag: FeatureFlag; } -export function FlagCard({ flag }: FlagCardProps) { +export const FlagCard = ({ flag }: FlagCardProps) => { const toggleMutation = useToggleFlag(); const handleToggle = () => {