chore(feature-flags): 🔧 Implement feature flag system with backend CRUD APIs, override management, audit logging, health checks, and admin UI components
This commit is contained in:
parent
08b70d48f0
commit
8674781981
15 changed files with 39 additions and 28 deletions
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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' })
|
||||
|
|
|
|||
|
|
@ -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' })
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { PartialType, OmitType } from '@nestjs/swagger';
|
||||
|
||||
import { CreateFeatureFlagDto } from './create-feature-flag.dto';
|
||||
|
||||
export class UpdateFeatureFlagDto extends PartialType(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { bootstrap, presets} from '@lilith/service-nestjs-bootstrap';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function main() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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 = () => (
|
||||
<>
|
||||
<Layout>
|
||||
<Routes>
|
||||
|
|
@ -25,11 +25,10 @@ function App() {
|
|||
{ value: 'employee', label: 'Employee' },
|
||||
{ value: 'admin', label: 'Admin' },
|
||||
]}
|
||||
showStorage={true}
|
||||
showStorage
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
|
|
@ -161,8 +161,8 @@ export const flagsApi = {
|
|||
|
||||
async getAuditLog(key?: string, limit?: number): Promise<AuditEntry[]> {
|
||||
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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue