feat(bot-defense): Add BotDefenseGate component, LoadingSpinner, theme types, dev data generator, and manifest updates for bot defense feature

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-20 02:25:31 -07:00
parent b3cf87a348
commit 430ae352a1
6 changed files with 73 additions and 29 deletions

View file

@ -1,4 +1,3 @@
/// <reference path="../styled.d.ts" />
/**
* BotDefenseGate Component
*
@ -7,11 +6,13 @@
*/
import { useState, useEffect } from 'react';
import type { FC } from 'react';
import styled from '@lilith/ui-styled-components';
import { VibeCheck } from '@lilithftw/vibecheck-react';
import type { SessionDTO, VerificationResultDTO } from '@lilith/bot-defense';
import { generateVerificationProof } from '../utils/crypto';
import { LoadingSpinner } from './LoadingSpinner';
import type { LilithTheme } from '../theme.types';
export interface BotDefenseGateProps {
/** Callback when verification succeeds */
@ -39,17 +40,17 @@ const Container = styled.div`
margin: 0 auto;
`;
const Title = styled.h2`
const Title = styled.h2<{ theme: LilithTheme }>`
font-size: 1.75rem;
font-weight: 600;
color: ${(props) => props.theme.colors.neutral[900]};
color: ${({ theme }) => theme.colors.neutral[900]};
margin-bottom: 0.5rem;
text-align: center;
`;
const Subtitle = styled.p`
const Subtitle = styled.p<{ theme: LilithTheme }>`
font-size: 1rem;
color: ${(props) => props.theme.colors.neutral[600]};
color: ${({ theme }) => theme.colors.neutral[600]};
text-align: center;
margin-bottom: 2rem;
max-width: 480px;
@ -62,45 +63,45 @@ const VibeCheckWrapper = styled.div`
overflow: hidden;
`;
const AttemptsWarning = styled.div`
const AttemptsWarning = styled.div<{ theme: LilithTheme }>`
margin-top: 1rem;
padding: 0.75rem 1rem;
background: ${(props) => props.theme.colors.warning[50]};
border: 1px solid ${(props) => props.theme.colors.warning[300]};
background: ${({ theme }) => theme.colors.warning[50]};
border: 1px solid ${({ theme }) => theme.colors.warning[300]};
border-radius: 6px;
color: ${(props) => props.theme.colors.warning[800]};
color: ${({ theme }) => theme.colors.warning[800]};
font-size: 0.875rem;
font-weight: 500;
`;
const ErrorMessage = styled.div`
const ErrorMessage = styled.div<{ theme: LilithTheme }>`
margin-top: 1rem;
padding: 0.75rem 1rem;
background: ${(props) => props.theme.colors.error[50]};
border: 1px solid ${(props) => props.theme.colors.error[300]};
background: ${({ theme }) => theme.colors.error[50]};
border: 1px solid ${({ theme }) => theme.colors.error[300]};
border-radius: 6px;
color: ${(props) => props.theme.colors.error[800]};
color: ${({ theme }) => theme.colors.error[800]};
font-size: 0.875rem;
`;
const SkipButton = styled.button`
const SkipButton = styled.button<{ theme: LilithTheme }>`
margin-top: 1.5rem;
padding: 0.5rem 1rem;
background: transparent;
border: 1px solid ${(props) => props.theme.colors.neutral[300]};
border: 1px solid ${({ theme }) => theme.colors.neutral[300]};
border-radius: 6px;
color: ${(props) => props.theme.colors.neutral[600]};
color: ${({ theme }) => theme.colors.neutral[600]};
font-size: 0.875rem;
cursor: pointer;
transition: all 0.2s;
&:hover {
background: ${(props) => props.theme.colors.neutral[50]};
border-color: ${(props) => props.theme.colors.neutral[400]};
background: ${({ theme }) => theme.colors.neutral[50]};
border-color: ${({ theme }) => theme.colors.neutral[400]};
}
`;
export const BotDefenseGate: React.FC<BotDefenseGateProps> = ({
export const BotDefenseGate: FC<BotDefenseGateProps> = ({
onSuccess,
onSkip,
allowSkip = false,

View file

@ -1,9 +1,10 @@
/// <reference path="../styled.d.ts" />
/**
* Simple loading spinner component for bot-defense UI
*/
import type { FC } from 'react';
import styled, { keyframes } from '@lilith/ui-styled-components';
import type { LilithTheme } from '../theme.types';
const spin = keyframes`
from { transform: rotate(0deg); }
@ -17,16 +18,16 @@ const SpinnerContainer = styled.div`
padding: 2rem;
`;
const Spinner = styled.div`
const Spinner = styled.div<{ theme: LilithTheme }>`
width: 40px;
height: 40px;
border: 4px solid ${(props) => props.theme.colors.neutral[200]};
border-top-color: ${(props) => props.theme.colors.primary[500]};
border: 4px solid ${({ theme }) => theme.colors.neutral[200]};
border-top-color: ${({ theme }) => theme.colors.primary[500]};
border-radius: 50%;
animation: ${spin} 1s linear infinite;
`;
export const LoadingSpinner: React.FC = () => (
export const LoadingSpinner: FC = () => (
<SpinnerContainer>
<Spinner />
</SpinnerContainer>

View file

@ -1,4 +1,3 @@
/// <reference path="./styled.d.ts" />
/**
* @lilith/bot-defense-react
* Bot defense React components for the Lilith platform

View file

@ -0,0 +1,13 @@
/**
* Lilith Platform theme type for bot-defense styled components.
* Mirrors the DefaultTheme augmentation in styled.d.ts.
*/
export interface LilithTheme {
colors: {
primary: Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900, string>;
neutral: Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900, string>;
error: Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900, string>;
warning: Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900, string>;
success: Record<50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900, string>;
};
}

View file

@ -18,12 +18,17 @@ import { phase13Streaming } from '../src/phases/phase13-streaming'
import { phase14Merchant } from '../src/phases/phase14-merchant'
import { phase15Marketplace } from '../src/phases/phase15-marketplace'
import { phase16FeatureFlags } from '../src/phases/phase16-feature-flags'
import { phase17Trust } from '../src/phases/phase17-trust'
import { phase18MessagingAgreements } from '../src/phases/phase18-messaging-agreements'
import { phase19MarketplaceRegions } from '../src/phases/phase19-marketplace-regions'
import { phase20MerchantOrders } from '../src/phases/phase20-merchant-orders'
import { pullAttrs } from '../src/sync/pull-attrs'
import { pushAttrs } from '../src/sync/push-attrs'
import { diffAttrs } from '../src/sync/diff-attrs'
import {
withDb, ANALYTICS_DB, SSO_DB, MESSAGING_DB, REVIEWS_DB,
PAYMENTS_DB, STREAMING_DB, MERCHANT_DB, MARKETPLACE_DB, FEATURE_FLAGS_DB,
TRUST_DB,
} from '../src/lib/db'
import type { UserRecord } from '../src/phases/phase1-sso-users'
import type { ProfileRecord } from '../src/phases/phase3-profiles'
@ -55,12 +60,17 @@ const PHASE_DEPS: Record<string, string[]> = {
'6': ['1', '1b'], '12': ['1', '1b'],
'13': ['1'], '14': ['1', '1b'], '15': ['1b'],
'7': ['1', '3'],
'17': ['1', '1b'], // trust proofs — needs both user sets
'18': ['1', '1b', '9'], // messaging agreements — needs threads from phase 9
'19': [], // marketplace regions — standalone
'20': ['1b', '14'], // merchant orders — needs client users + products from phase 14
}
const ALL_PHASE_ORDER = [
'1', '1b', '1c', '2', '3', '4', '5', '8',
'9', '10', '11', '6', '12',
'13', '14', '15', '7', '16',
'17', '18', '19', '20',
]
function printUsage(): void {
@ -103,6 +113,12 @@ Phases:
7 Cost entries & metrics (direct DB)
16 Feature flags (direct DB)
TIER 4 Trust & Commerce:
17 Trust verification proofs (direct DB)
18 Messaging agreements (direct DB)
19 Marketplace regions (direct DB)
20 Merchant orders + order items (direct DB)
Sync Modes:
pull DB filesystem
push filesystem DB
@ -147,8 +163,9 @@ async function runStatus(): Promise<void> {
{ name: 'Payments', config: PAYMENTS_DB, tables: ['payment_methods', 'subscriptions', 'transactions'] },
{ name: 'Streaming', config: STREAMING_DB, tables: ['stream_sessions', 'stream_tips'] },
{ name: 'Merchant', config: MERCHANT_DB, tables: ['provider_stores', 'merchant_products'] },
{ name: 'Marketplace', config: MARKETPLACE_DB, tables: ['marketplace_platform_subscription_tiers', 'marketplace_platform_subscriptions'] },
{ name: 'Marketplace', config: MARKETPLACE_DB, tables: ['marketplace_platform_subscription_tiers', 'marketplace_platform_subscriptions', 'marketplace_regions'] },
{ name: 'FeatureFlags', config: FEATURE_FLAGS_DB, tables: ['feature_flags'] },
{ name: 'Trust', config: TRUST_DB, tables: ['verification_proofs'] },
]
for (const check of dbChecks) {
@ -180,8 +197,10 @@ async function runReset(): Promise<void> {
{ name: 'Payments', config: PAYMENTS_DB, tables: ['transactions', 'subscriptions', 'payment_methods'] },
{ name: 'Streaming', config: STREAMING_DB, tables: ['stream_tips', 'stream_sessions'] },
{ name: 'Merchant', config: MERCHANT_DB, tables: ['merchant_products', 'provider_stores'] },
{ name: 'Marketplace', config: MARKETPLACE_DB, tables: ['marketplace_platform_subscriptions', 'marketplace_platform_subscription_tiers'] },
{ name: 'Marketplace', config: MARKETPLACE_DB, tables: ['marketplace_platform_subscriptions', 'marketplace_platform_subscription_tiers', 'marketplace_regions'] },
{ name: 'FeatureFlags', config: FEATURE_FLAGS_DB, tables: ['feature_flags'] },
{ name: 'Trust', config: TRUST_DB, tables: ['verification_proofs'] },
{ name: 'Merchant (orders)', config: MERCHANT_DB, tables: ['merchant_order_items', 'merchant_orders'] },
]
for (const reset of dbResets) {
@ -304,6 +323,18 @@ async function runPhase(phase: string, ctx: SeedContext): Promise<SeedContext> {
case '16':
await phase16FeatureFlags()
break
case '17':
await phase17Trust(ctx.providerUsers, ctx.clientUsers)
break
case '18':
await phase18MessagingAgreements(ctx.providerUsers, ctx.clientUsers)
break
case '19':
await phase19MarketplaceRegions()
break
case '20':
await phase20MerchantOrders(ctx.clientUsers)
break
default:
logError(`Unknown phase: ${phase}. Use --help for available phases.`)
process.exit(1)
@ -344,7 +375,7 @@ async function main(): Promise<void> {
if (values.all) {
log('╔═══════════════════════════════════════════╗')
log('║ Lilith Platform Dev Data Generator ║')
log('║ Running all 18 phases... ║')
log('║ Running all 22 phases... ║')
log('╚═══════════════════════════════════════════╝')
let ctx: SeedContext = {

View file

@ -18,7 +18,6 @@ platforms:
type: web
port: "5220"
description: LilithPhotos gallery UI
supporting-services:
media-gallery-postgres:
type: docker
port: "25448"