This commit is contained in:
ryan
2026-07-13 15:44:03 +08:00
parent 0548a8a5d4
commit 26be762c3a
102 changed files with 909 additions and 490 deletions
@@ -1,12 +1,18 @@
'use client'; 'use client';
import {Search} from 'lucide-react'; import { Search } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; import {
import type {AccessLogTab, SearchDraft} from './access-log-utils'; Select,
import {PAGE_SIZE_OPTIONS} from './access-log-utils'; SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import type { AccessLogTab, SearchDraft } from './access-log-utils';
import { PAGE_SIZE_OPTIONS } from './access-log-utils';
interface AccessLogFiltersProps { interface AccessLogFiltersProps {
tab: AccessLogTab; tab: AccessLogTab;
@@ -1,7 +1,7 @@
'use client'; 'use client';
import {useState} from 'react'; import { useState } from 'react';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
import { import {
AlertDialog, AlertDialog,
@@ -12,10 +12,16 @@ import {
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
const CLEANUP_PRESETS = [3, 7, 30]; const CLEANUP_PRESETS = [3, 7, 30];
@@ -1,16 +1,22 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {useCallback, useEffect, useState} from 'react'; import { useCallback, useEffect, useState } from 'react';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {HardDrive, RefreshCw, Trash2} from 'lucide-react'; import { HardDrive, RefreshCw, Trash2 } from 'lucide-react';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import {Button} from '@/components/ui/button'; Card,
import {Skeleton} from '@/components/ui/skeleton'; CardContent,
import {Label} from '@/components/ui/label'; CardDescription,
import {Input} from '@/components/ui/input'; CardHeader,
import {Switch} from '@/components/ui/switch'; CardTitle,
} from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { Switch } from '@/components/ui/switch';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -19,7 +25,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import type {CacheStatus} from '@/lib/services/admin'; import type { CacheStatus } from '@/lib/services/admin';
import services from '@/lib/services'; import services from '@/lib/services';
/** /**
@@ -4,13 +4,25 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Code} from 'lucide-react'; import { Code } from 'lucide-react';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import {Label} from '@/components/ui/label'; Card,
import {Input} from '@/components/ui/input'; CardContent,
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; CardDescription,
import {Checkbox} from '@/components/ui/checkbox'; CardHeader,
import {Button} from '@/components/ui/button'; CardTitle,
} from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Checkbox } from '@/components/ui/checkbox';
import { Button } from '@/components/ui/button';
export function ControlsTab() { export function ControlsTab() {
const [formData, setFormData] = React.useState({ const [formData, setFormData] = React.useState({
@@ -4,9 +4,9 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Code, Cpu, Database, HardDrive} from 'lucide-react'; import { Code, Cpu, Database, HardDrive } from 'lucide-react';
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {Progress} from '@/components/ui/progress'; import { Progress } from '@/components/ui/progress';
export function DashboardTab() { export function DashboardTab() {
return ( return (
@@ -1,8 +1,8 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {AnimatePresence, motion} from 'motion/react'; import { AnimatePresence, motion } from 'motion/react';
import { import {
Download, Download,
Eye, Eye,
@@ -17,13 +17,20 @@ import {
Upload, Upload,
X, X,
} from 'lucide-react'; } from 'lucide-react';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Badge} from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {Checkbox} from '@/components/ui/checkbox'; import { Checkbox } from '@/components/ui/checkbox';
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from '@/components/ui/table'; import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@@ -34,10 +41,16 @@ import {
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle,} from '@/components/ui/sheet'; import {
import {FileImagePreview} from '@/components/common/file-image-preview'; Sheet,
import services, {formatFileSize} from '@/lib/services'; SheetContent,
import type {Upload as UploadRecord} from '@/lib/services/upload/types'; SheetDescription,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
import { FileImagePreview } from '@/components/common/file-image-preview';
import services, { formatFileSize } from '@/lib/services';
import type { Upload as UploadRecord } from '@/lib/services/upload/types';
/* ─── 工具函数 ─────────────────────────────────────────── */ /* ─── 工具函数 ─────────────────────────────────────────── */
@@ -1,12 +1,35 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {useQuery} from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import {Database, Files, HardDrive, Info, Loader2, TrendingUp, Upload,} from 'lucide-react'; import {
import {Area, AreaChart, CartesianGrid, Cell, Pie, PieChart, XAxis, YAxis,} from 'recharts'; Database,
Files,
HardDrive,
Info,
Loader2,
TrendingUp,
Upload,
} from 'lucide-react';
import {
Area,
AreaChart,
CartesianGrid,
Cell,
Pie,
PieChart,
XAxis,
YAxis,
} from 'recharts';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { import {
ChartConfig, ChartConfig,
ChartContainer, ChartContainer,
@@ -15,7 +38,7 @@ import {
ChartTooltip, ChartTooltip,
ChartTooltipContent, ChartTooltipContent,
} from '@/components/ui/chart'; } from '@/components/ui/chart';
import services, {formatFileSize} from '@/lib/services'; import services, { formatFileSize } from '@/lib/services';
const categoryMap: Record<string, string> = { const categoryMap: Record<string, string> = {
图片: 'images', 图片: 'images',
@@ -16,19 +16,37 @@ limitations under the License.
'use client'; 'use client';
import {useCallback, useEffect, useState} from 'react'; import { useCallback, useEffect, useState } from 'react';
import {BarChart3, Globe, RefreshCw, TrendingUp, Users, XCircle,} from 'lucide-react'; import {
import {Area, AreaChart, CartesianGrid, XAxis, YAxis} from 'recharts'; BarChart3,
Globe,
RefreshCw,
TrendingUp,
Users,
XCircle,
} from 'lucide-react';
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts';
import services from '@/lib/services'; import services from '@/lib/services';
import {ErrorInline} from '@/components/layout/error'; import { ErrorInline } from '@/components/layout/error';
import {LoadingStateWithBorder} from '@/components/layout/loading'; import { LoadingStateWithBorder } from '@/components/layout/loading';
import {EmptyStateWithBorder} from '@/components/layout/empty'; import { EmptyStateWithBorder } from '@/components/layout/empty';
import {Badge} from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import {ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent,} from '@/components/ui/chart'; Card,
import {Spinner} from '@/components/ui/spinner'; CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from '@/components/ui/chart';
import { Spinner } from '@/components/ui/spinner';
interface TrendData { interface TrendData {
date: string; date: string;
@@ -16,22 +16,44 @@ limitations under the License.
'use client'; 'use client';
import {useMemo, useState} from 'react'; import { useMemo, useState } from 'react';
import {useQuery} from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {format} from 'date-fns'; import { format } from 'date-fns';
import {Activity, ChevronLeft, ChevronRight, Copy, Eye, RotateCcw, Search, XCircle,} from 'lucide-react'; import {
Activity,
ChevronLeft,
ChevronRight,
Copy,
Eye,
RotateCcw,
Search,
XCircle,
} from 'lucide-react';
import services from '@/lib/services'; import services from '@/lib/services';
import {ErrorInline} from '@/components/layout/error'; import { ErrorInline } from '@/components/layout/error';
import {LoadingStateWithBorder} from '@/components/layout/loading'; import { LoadingStateWithBorder } from '@/components/layout/loading';
import {EmptyStateWithBorder} from '@/components/layout/empty'; import { EmptyStateWithBorder } from '@/components/layout/empty';
import {Badge} from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle,} from '@/components/ui/sheet'; import {
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from '@/components/ui/table'; Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
interface AccessLog { interface AccessLog {
id: string; id: string;
@@ -16,15 +16,15 @@ limitations under the License.
'use client'; 'use client';
import {memo, useCallback, useEffect, useRef, useState} from 'react'; import { memo, useCallback, useEffect, useRef, useState } from 'react';
import {useVirtualizer} from '@tanstack/react-virtual'; import { useVirtualizer } from '@tanstack/react-virtual';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {ArrowDown, ChevronUp, Loader2} from 'lucide-react'; import { ArrowDown, ChevronUp, Loader2 } from 'lucide-react';
import services from '@/lib/services'; import services from '@/lib/services';
import {ErrorInline} from '@/components/layout/error'; import { ErrorInline } from '@/components/layout/error';
import {LoadingStateWithBorder} from '@/components/layout/loading'; import { LoadingStateWithBorder } from '@/components/layout/loading';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
interface LogEntry { interface LogEntry {
index: number; index: number;
@@ -4,18 +4,37 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {ChevronDown, Edit2, Layers, Loader2, Plus, Trash2,} from 'lucide-react'; import {
ChevronDown,
Edit2,
Layers,
Loader2,
Plus,
Trash2,
} from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Textarea} from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import {Switch} from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import {Badge} from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from '@/components/ui/table'; import {
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,} from '@/components/ui/tooltip'; Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -24,17 +43,31 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; import {
import {Checkbox} from '@/components/ui/checkbox'; Select,
import {Popover, PopoverContent, PopoverTrigger,} from '@/components/ui/popover'; SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Checkbox } from '@/components/ui/checkbox';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import {ErrorInline} from '@/components/layout/error'; import { ErrorInline } from '@/components/layout/error';
import {LoadingStateWithBorder} from '@/components/layout/loading'; import { LoadingStateWithBorder } from '@/components/layout/loading';
import {EmptyStateWithBorder} from '@/components/layout/empty'; import { EmptyStateWithBorder } from '@/components/layout/empty';
import services from '@/lib/services'; import services from '@/lib/services';
import type {CreatePushEventRequest, PushEvent, UpdatePushEventRequest,} from '@/lib/services/push'; import type {
import {PushService} from '@/lib/services/push'; CreatePushEventRequest,
PushEvent,
UpdatePushEventRequest,
} from '@/lib/services/push';
import { PushService } from '@/lib/services/push';
export function EventsTab() { export function EventsTab() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -1,13 +1,19 @@
'use client'; 'use client';
import {useEffect, useState} from 'react'; import { useEffect, useState } from 'react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
import {Input} from '@/components/ui/input'; Dialog,
import {Label} from '@/components/ui/label'; DialogContent,
import {Switch} from '@/components/ui/switch'; DialogDescription,
import {useAdminUsers} from '@/contexts/admin-users-context'; DialogHeader,
import type {CreateUserRequest} from '@/lib/services/admin'; DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { useAdminUsers } from '@/contexts/admin-users-context';
import type { CreateUserRequest } from '@/lib/services/admin';
const emptyForm: CreateUserRequest = { const emptyForm: CreateUserRequest = {
username: '', username: '',
@@ -1,14 +1,20 @@
'use client'; 'use client';
import {useEffect, useState} from 'react'; import { useEffect, useState } from 'react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
import {Input} from '@/components/ui/input'; Dialog,
import {Label} from '@/components/ui/label'; DialogContent,
import {Switch} from '@/components/ui/switch'; DialogDescription,
import {useAdminUsers} from '@/contexts/admin-users-context'; DialogHeader,
import {useAuth} from '@/components/providers/auth-provider'; DialogTitle,
import type {AdminUser} from '@/lib/services/admin'; } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { useAdminUsers } from '@/contexts/admin-users-context';
import { useAuth } from '@/components/providers/auth-provider';
import type { AdminUser } from '@/lib/services/admin';
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@@ -19,7 +25,7 @@ import {
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
interface EditUserForm { interface EditUserForm {
nickname: string; nickname: string;
@@ -1,10 +1,16 @@
'use client'; 'use client';
import {TrendChart} from '@/components/data/trend-chart'; import { TrendChart } from '@/components/data/trend-chart';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import type {CapacityTrendPoint} from '@/lib/services/openflare'; Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import type { CapacityTrendPoint } from '@/lib/services/openflare';
import {formatPercent, formatTrendHour} from './dashboard-utils'; import { formatPercent, formatTrendHour } from './dashboard-utils';
export function CapacityTrendChart({ export function CapacityTrendChart({
points, points,
@@ -1,8 +1,17 @@
'use client'; 'use client';
import {RankChart} from '@/components/data/rank-chart'; import { RankChart } from '@/components/data/rank-chart';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import type {DistributionItem, TrafficDistributions,} from '@/lib/services/openflare'; Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import type {
DistributionItem,
TrafficDistributions,
} from '@/lib/services/openflare';
function toRankItems(items: DistributionItem[]) { function toRankItems(items: DistributionItem[]) {
return items.map((item) => ({ return items.map((item) => ({
@@ -1,12 +1,18 @@
'use client'; 'use client';
import {Globe2} from 'lucide-react'; import { Globe2 } from 'lucide-react';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import {Progress} from '@/components/ui/progress'; Card,
import type {DistributionItem} from '@/lib/services/openflare'; CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Progress } from '@/components/ui/progress';
import type { DistributionItem } from '@/lib/services/openflare';
import {formatCompactNumber} from './dashboard-utils'; import { formatCompactNumber } from './dashboard-utils';
export function GeoDistributionList({ items }: { items: DistributionItem[] }) { export function GeoDistributionList({ items }: { items: DistributionItem[] }) {
const sortedItems = [...items] const sortedItems = [...items]
@@ -1,10 +1,10 @@
'use client'; 'use client';
import {useEffect} from 'react'; import { useEffect } from 'react';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {z} from 'zod'; import { z } from 'zod';
import { import {
AlertDialog, AlertDialog,
@@ -15,9 +15,9 @@ import {
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
const cleanupSchema = z.object({ const cleanupSchema = z.object({
keepCount: z.number().int().min(3, '最少保留 3 个历史快照'), keepCount: z.number().int().min(3, '最少保留 3 个历史快照'),
@@ -1,11 +1,24 @@
'use client'; 'use client';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
import {Badge} from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from '@/components/ui/table'; Dialog,
import type {ConfigDiffResult} from '@/lib/services/openflare'; DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import type { ConfigDiffResult } from '@/lib/services/openflare';
interface DiffDialogProps { interface DiffDialogProps {
open: boolean; open: boolean;
@@ -1,10 +1,10 @@
'use client'; 'use client';
import {useState} from 'react'; import { useState } from 'react';
import {useQuery} from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import {ExternalLink, Loader2} from 'lucide-react'; import { ExternalLink, Loader2 } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -13,12 +13,16 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {formatDateTime} from '@/lib/utils'; import { formatDateTime } from '@/lib/utils';
import type {NodeAgentReleaseInfo, NodeItem, ReleaseChannel,} from '@/lib/services/openflare'; import type {
import {NodeService} from '@/lib/services/openflare'; NodeAgentReleaseInfo,
NodeItem,
ReleaseChannel,
} from '@/lib/services/openflare';
import { NodeService } from '@/lib/services/openflare';
import {NodeStatusBadge} from './node-status-badge'; import { NodeStatusBadge } from './node-status-badge';
import {formatRelativeTime, getErrorMessage} from './node-utils'; import { formatRelativeTime, getErrorMessage } from './node-utils';
export function AgentUpdateDialog({ export function AgentUpdateDialog({
open, open,
@@ -1,10 +1,19 @@
'use client'; 'use client';
import {TrendChart} from '@/components/data/trend-chart'; import { TrendChart } from '@/components/data/trend-chart';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import type {DiskIOTrendPoint} from '@/lib/services/openflare'; Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import type { DiskIOTrendPoint } from '@/lib/services/openflare';
import {formatBytes, formatTrendHour,} from '../../components/dashboard/dashboard-utils'; import {
formatBytes,
formatTrendHour,
} from '../../components/dashboard/dashboard-utils';
export function DiskIOTrendChart({ export function DiskIOTrendChart({
points, points,
@@ -1,8 +1,8 @@
'use client'; 'use client';
import {Progress} from '@/components/ui/progress'; import { Progress } from '@/components/ui/progress';
import {formatCompactNumber} from '../../components/dashboard/dashboard-utils'; import { formatCompactNumber } from '../../components/dashboard/dashboard-utils';
export function DistributionList({ export function DistributionList({
items, items,
@@ -1,11 +1,21 @@
'use client'; 'use client';
import Link from 'next/link'; import Link from 'next/link';
import {useRouter} from 'next/navigation'; import { useRouter } from 'next/navigation';
import {useMutation, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import {useState} from 'react'; import { useState } from 'react';
import {Activity, Cpu, FileText, Fingerprint, Package, RefreshCw, RotateCcw, Trash2, Upload,} from 'lucide-react'; import {
import {toast} from 'sonner'; Activity,
Cpu,
FileText,
Fingerprint,
Package,
RefreshCw,
RotateCcw,
Trash2,
Upload,
} from 'lucide-react';
import { toast } from 'sonner';
import { import {
AlertDialog, AlertDialog,
@@ -17,17 +27,25 @@ import {
AlertDialogHeader, AlertDialogHeader,
AlertDialogTitle, AlertDialogTitle,
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {formatDateTime} from '@/lib/utils'; import { formatDateTime } from '@/lib/utils';
import type {NodeAgentReleaseInfo, NodeItem, ReleaseChannel,} from '@/lib/services/openflare'; import type {
import {NodeService} from '@/lib/services/openflare'; NodeAgentReleaseInfo,
NodeItem,
ReleaseChannel,
} from '@/lib/services/openflare';
import { NodeService } from '@/lib/services/openflare';
import {AgentUpdateDialog} from './agent-update-dialog'; import { AgentUpdateDialog } from './agent-update-dialog';
import {NodeDetailShell} from './node-detail-shell'; import { NodeDetailShell } from './node-detail-shell';
import {NodeErrorBanner, NodeInfoRow, NodeSectionCard,} from './node-detail-primitives'; import {
import {NodeEditorDialog} from './node-editor-dialog'; NodeErrorBanner,
import {NodeObservability} from './node-observability'; NodeInfoRow,
import {NodeStatusBadge} from './node-status-badge'; NodeSectionCard,
} from './node-detail-primitives';
import { NodeEditorDialog } from './node-editor-dialog';
import { NodeObservability } from './node-observability';
import { NodeStatusBadge } from './node-status-badge';
import { import {
formatRelativeTime, formatRelativeTime,
getApplyLabel, getApplyLabel,
@@ -1,11 +1,11 @@
'use client'; 'use client';
import {useRef, useState} from 'react'; import { useRef, useState } from 'react';
import {useMutation, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import {Loader2, UploadCloud} from 'lucide-react'; import { Loader2, UploadCloud } from 'lucide-react';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -14,13 +14,18 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Progress} from '@/components/ui/progress'; import { Progress } from '@/components/ui/progress';
import {PagesService} from '@/lib/services/openflare'; import { PagesService } from '@/lib/services/openflare';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {deploymentsQueryKey, formatBytes, projectQueryKey, projectsQueryKey,} from './pages-utils'; import {
deploymentsQueryKey,
formatBytes,
projectQueryKey,
projectsQueryKey,
} from './pages-utils';
interface DeploymentUploadDialogProps { interface DeploymentUploadDialogProps {
open: boolean; open: boolean;
@@ -1,19 +1,33 @@
'use client'; 'use client';
import {useEffect} from 'react'; import { useEffect } from 'react';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {z} from 'zod'; import { z } from 'zod';
import {Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,} from '@/components/ui/form'; import {
import {Input} from '@/components/ui/input'; Form,
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; FormControl,
import type {ProxyRouteItem} from '@/lib/services/openflare'; FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import type { ProxyRouteItem } from '@/lib/services/openflare';
import {proxyRouteFormIds} from '../helpers'; import { proxyRouteFormIds } from '../helpers';
import {useRouteSectionSave} from '../hooks/use-route-section-save'; import { useRouteSectionSave } from '../hooks/use-route-section-save';
import {SectionShell} from './section-shell'; import { SectionShell } from './section-shell';
const authSchema = z const authSchema = z
.object({ .object({
@@ -1,20 +1,37 @@
'use client'; 'use client';
import {useEffect} from 'react'; import { useEffect } from 'react';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {z} from 'zod'; import { z } from 'zod';
import {Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,} from '@/components/ui/form'; import {
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; Form,
import {Switch} from '@/components/ui/switch'; FormControl,
import {Textarea} from '@/components/ui/textarea'; FormDescription,
import type {ProxyRouteItem} from '@/lib/services/openflare'; FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import type { ProxyRouteItem } from '@/lib/services/openflare';
import {linesFromTextarea, validateCacheRules,} from '../../components/helpers'; import {
import {proxyRouteFormIds} from '../helpers'; linesFromTextarea,
import {useRouteSectionSave} from '../hooks/use-route-section-save'; validateCacheRules,
import {SectionShell} from './section-shell'; } from '../../components/helpers';
import { proxyRouteFormIds } from '../helpers';
import { useRouteSectionSave } from '../hooks/use-route-section-save';
import { SectionShell } from './section-shell';
const cacheSchema = z const cacheSchema = z
.object({ .object({
@@ -1,22 +1,30 @@
'use client'; 'use client';
import {useEffect, useMemo} from 'react'; import { useEffect, useMemo } from 'react';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useQuery} from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {z} from 'zod'; import { z } from 'zod';
import {Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,} from '@/components/ui/form'; import {
import {Input} from '@/components/ui/input'; Form,
import {Switch} from '@/components/ui/switch'; FormControl,
import type {ProxyRouteItem} from '@/lib/services/openflare'; FormDescription,
import {zoneQueryKey, ZoneService} from '@/lib/services/openflare'; FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Switch } from '@/components/ui/switch';
import type { ProxyRouteItem } from '@/lib/services/openflare';
import { zoneQueryKey, ZoneService } from '@/lib/services/openflare';
import {listAllZoneDomains} from '../../components/helpers'; import { listAllZoneDomains } from '../../components/helpers';
import {ZoneDomainSelector} from '../../components/zone-domain-selector'; import { ZoneDomainSelector } from '../../components/zone-domain-selector';
import {proxyRouteFormIds} from '../helpers'; import { proxyRouteFormIds } from '../helpers';
import {useRouteSectionSave} from '../hooks/use-route-section-save'; import { useRouteSectionSave } from '../hooks/use-route-section-save';
import {SectionShell} from './section-shell'; import { SectionShell } from './section-shell';
const domainSettingsSchema = z.object({ const domainSettingsSchema = z.object({
site_name: z site_name: z
@@ -1,8 +1,8 @@
'use client'; 'use client';
import {useEffect, useState} from 'react'; import { useEffect, useState } from 'react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -11,9 +11,9 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {Field, FieldGroup, FieldLabel} from '@/components/ui/field'; import { Field, FieldGroup, FieldLabel } from '@/components/ui/field';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Spinner} from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
interface CreateRuleDialogProps { interface CreateRuleDialogProps {
open: boolean; open: boolean;
@@ -1,6 +1,6 @@
import {describe, expect, it} from 'vitest'; import { describe, expect, it } from 'vitest';
import type {WAFRuleGraph} from '@/lib/services/openflare'; import type { WAFRuleGraph } from '@/lib/services/openflare';
import { import {
acceptedNodeChanges, acceptedNodeChanges,
@@ -1,7 +1,7 @@
import type {EdgeChange, Node, NodeChange} from '@xyflow/react'; import type { EdgeChange, Node, NodeChange } from '@xyflow/react';
import type {WAFRuleGraph, WAFRuleNode} from '@/lib/services/openflare'; import type { WAFRuleGraph, WAFRuleNode } from '@/lib/services/openflare';
import {wouldCreateCycle} from './graph-validation'; import { wouldCreateCycle } from './graph-validation';
export type GraphErrorTarget = { kind: 'node' | 'edge'; id: string }; export type GraphErrorTarget = { kind: 'node' | 'edge'; id: string };
@@ -1,23 +1,42 @@
'use client'; 'use client';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {useEffect, useState} from 'react'; import { useEffect, useState } from 'react';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {ChevronDown, Loader2} from 'lucide-react'; import { ChevronDown, Loader2 } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
import {Input} from '@/components/ui/input'; Dialog,
import {Label} from '@/components/ui/label'; DialogContent,
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; DialogDescription,
import {Switch} from '@/components/ui/switch'; DialogHeader,
import {Textarea} from '@/components/ui/textarea'; DialogTitle,
import type {TlsCertificateItem} from '@/lib/services/openflare'; } from '@/components/ui/dialog';
import {DnsAccountService, TlsCertificateService,} from '@/lib/services/openflare'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import type { TlsCertificateItem } from '@/lib/services/openflare';
import {
DnsAccountService,
TlsCertificateService,
} from '@/lib/services/openflare';
import {type AcmeApplyFormValues, acmeApplySchema, defaultAcmeApplyValues,} from './schemas'; import {
import {getErrorMessage} from './website-utils'; type AcmeApplyFormValues,
acmeApplySchema,
defaultAcmeApplyValues,
} from './schemas';
import { getErrorMessage } from './website-utils';
const certificatesQueryKey = ['openflare', 'tls-certificates']; const certificatesQueryKey = ['openflare', 'tls-certificates'];
@@ -1,10 +1,10 @@
'use client'; 'use client';
import {useQuery} from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import {Copy, Loader2} from 'lucide-react'; import { Copy, Loader2 } from 'lucide-react';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -13,14 +13,14 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {EmptyStateWithBorder} from '@/components/layout/empty'; import { EmptyStateWithBorder } from '@/components/layout/empty';
import {ErrorInline} from '@/components/layout/error'; import { ErrorInline } from '@/components/layout/error';
import {LoadingStateWithBorder} from '@/components/layout/loading'; import { LoadingStateWithBorder } from '@/components/layout/loading';
import {TlsCertificateService} from '@/lib/services/openflare'; import { TlsCertificateService } from '@/lib/services/openflare';
import {formatDateTime} from '@/lib/utils'; import { formatDateTime } from '@/lib/utils';
import {WebsiteStatusBadge} from './status-badge'; import { WebsiteStatusBadge } from './status-badge';
import {getCertificateStatus, getErrorMessage} from './website-utils'; import { getCertificateStatus, getErrorMessage } from './website-utils';
interface CertificateDetailDialogProps { interface CertificateDetailDialogProps {
certificateId: number | null; certificateId: number | null;
@@ -1,12 +1,12 @@
'use client'; 'use client';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {useEffect} from 'react'; import { useEffect } from 'react';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -15,17 +15,21 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {EmptyStateWithBorder} from '@/components/layout/empty'; import { EmptyStateWithBorder } from '@/components/layout/empty';
import {ErrorInline} from '@/components/layout/error'; import { ErrorInline } from '@/components/layout/error';
import {LoadingStateWithBorder} from '@/components/layout/loading'; import { LoadingStateWithBorder } from '@/components/layout/loading';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Textarea} from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import type {TlsCertificateItem} from '@/lib/services/openflare'; import type { TlsCertificateItem } from '@/lib/services/openflare';
import {TlsCertificateService} from '@/lib/services/openflare'; import { TlsCertificateService } from '@/lib/services/openflare';
import {defaultManualImportValues, type ManualImportFormValues, manualImportSchema,} from './schemas'; import {
import {getErrorMessage, toManualPayload} from './website-utils'; defaultManualImportValues,
type ManualImportFormValues,
manualImportSchema,
} from './schemas';
import { getErrorMessage, toManualPayload } from './website-utils';
const certificatesQueryKey = ['openflare', 'tls-certificates']; const certificatesQueryKey = ['openflare', 'tls-certificates'];
@@ -1,19 +1,25 @@
'use client'; 'use client';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useMutation, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import {type FormEvent, useEffect, useState} from 'react'; import { type FormEvent, useEffect, useState } from 'react';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
import {Input} from '@/components/ui/input'; Dialog,
import {Label} from '@/components/ui/label'; DialogContent,
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'; DialogDescription,
import {Textarea} from '@/components/ui/textarea'; DialogHeader,
import type {TlsCertificateItem} from '@/lib/services/openflare'; DialogTitle,
import {TlsCertificateService} from '@/lib/services/openflare'; } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Textarea } from '@/components/ui/textarea';
import type { TlsCertificateItem } from '@/lib/services/openflare';
import { TlsCertificateService } from '@/lib/services/openflare';
import { import {
defaultFileImportValues, defaultFileImportValues,
@@ -22,7 +28,11 @@ import {
type ManualImportFormValues, type ManualImportFormValues,
manualImportSchema, manualImportSchema,
} from './schemas'; } from './schemas';
import {getErrorMessage, toFilePayload, toManualPayload,} from './website-utils'; import {
getErrorMessage,
toFilePayload,
toManualPayload,
} from './website-utils';
const certificatesQueryKey = ['openflare', 'tls-certificates']; const certificatesQueryKey = ['openflare', 'tls-certificates'];
@@ -1,13 +1,13 @@
'use client'; 'use client';
import {useMutation, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import {zodResolver} from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import {useState} from 'react'; import { useState } from 'react';
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import {Loader2} from 'lucide-react'; import { Loader2 } from 'lucide-react';
import {z} from 'zod'; import { z } from 'zod';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -16,13 +16,19 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue,} from '@/components/ui/select'; import {
import type {DnsAccountMutationPayload} from '@/lib/services/openflare'; Select,
import {DnsAccountService} from '@/lib/services/openflare'; SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import type { DnsAccountMutationPayload } from '@/lib/services/openflare';
import { DnsAccountService } from '@/lib/services/openflare';
import {getErrorMessage} from './website-utils'; import { getErrorMessage } from './website-utils';
const dnsAccountsQueryKey = ['openflare', 'dns-accounts']; const dnsAccountsQueryKey = ['openflare', 'dns-accounts'];
@@ -1,13 +1,13 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {cva, type VariantProps} from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import { import {
Button as ButtonPrimitive, Button as ButtonPrimitive,
type ButtonProps as ButtonPrimitiveProps, type ButtonProps as ButtonPrimitiveProps,
} from '@/components/animate-ui/primitives/buttons/button'; } from '@/components/animate-ui/primitives/buttons/button';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
@@ -1,9 +1,14 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {motion, type Variants} from 'motion/react'; import { motion, type Variants } from 'motion/react';
import {getVariants, type IconProps, IconWrapper, useAnimateIconContext,} from '@/components/animate-ui/icons/icon'; import {
getVariants,
type IconProps,
IconWrapper,
useAnimateIconContext,
} from '@/components/animate-ui/icons/icon';
type CheckProps = IconProps<keyof typeof animations>; type CheckProps = IconProps<keyof typeof animations>;
@@ -1,9 +1,14 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {motion, type Variants} from 'motion/react'; import { motion, type Variants } from 'motion/react';
import {getVariants, type IconProps, IconWrapper, useAnimateIconContext,} from '@/components/animate-ui/icons/icon'; import {
getVariants,
type IconProps,
IconWrapper,
useAnimateIconContext,
} from '@/components/animate-ui/icons/icon';
type ChevronDownProps = IconProps<keyof typeof animations>; type ChevronDownProps = IconProps<keyof typeof animations>;
@@ -1,9 +1,14 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {motion, type Variants} from 'motion/react'; import { motion, type Variants } from 'motion/react';
import {getVariants, type IconProps, IconWrapper, useAnimateIconContext,} from '@/components/animate-ui/icons/icon'; import {
getVariants,
type IconProps,
IconWrapper,
useAnimateIconContext,
} from '@/components/animate-ui/icons/icon';
type ChevronLeftProps = IconProps<keyof typeof animations>; type ChevronLeftProps = IconProps<keyof typeof animations>;
@@ -1,9 +1,14 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {motion, type Variants} from 'motion/react'; import { motion, type Variants } from 'motion/react';
import {getVariants, type IconProps, IconWrapper, useAnimateIconContext,} from '@/components/animate-ui/icons/icon'; import {
getVariants,
type IconProps,
IconWrapper,
useAnimateIconContext,
} from '@/components/animate-ui/icons/icon';
type ChevronRightProps = IconProps<keyof typeof animations>; type ChevronRightProps = IconProps<keyof typeof animations>;
@@ -1,9 +1,14 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {motion, type Variants} from 'motion/react'; import { motion, type Variants } from 'motion/react';
import {getVariants, type IconProps, IconWrapper, useAnimateIconContext,} from '@/components/animate-ui/icons/icon'; import {
getVariants,
type IconProps,
IconWrapper,
useAnimateIconContext,
} from '@/components/animate-ui/icons/icon';
type CircleCheckProps = IconProps<keyof typeof animations>; type CircleCheckProps = IconProps<keyof typeof animations>;
@@ -1,9 +1,12 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {type HTMLMotionProps, motion} from 'motion/react'; import { type HTMLMotionProps, motion } from 'motion/react';
import {Slot, type WithAsChild,} from '@/components/animate-ui/primitives/animate/slot'; import {
Slot,
type WithAsChild,
} from '@/components/animate-ui/primitives/animate/slot';
type ButtonProps = WithAsChild< type ButtonProps = WithAsChild<
HTMLMotionProps<'button'> & { HTMLMotionProps<'button'> & {
@@ -1,11 +1,11 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Collapsible as CollapsiblePrimitive} from 'radix-ui'; import { Collapsible as CollapsiblePrimitive } from 'radix-ui';
import {AnimatePresence, type HTMLMotionProps, motion} from 'motion/react'; import { AnimatePresence, type HTMLMotionProps, motion } from 'motion/react';
import {getStrictContext} from '@/lib/utils/get-strict-context'; import { getStrictContext } from '@/lib/utils/get-strict-context';
import {useControlledState} from '@/hooks/use-controlled-state'; import { useControlledState } from '@/hooks/use-controlled-state';
type CollapsibleContextType = { type CollapsibleContextType = {
isOpen: boolean; isOpen: boolean;
@@ -1,9 +1,9 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {type SpringOptions, useMotionValue, useSpring} from 'motion/react'; import { type SpringOptions, useMotionValue, useSpring } from 'motion/react';
import {useIsInView, type UseIsInViewOptions} from '@/hooks/use-is-in-view'; import { useIsInView, type UseIsInViewOptions } from '@/hooks/use-is-in-view';
type CountingNumberProps = Omit<React.ComponentProps<'span'>, 'children'> & { type CountingNumberProps = Omit<React.ComponentProps<'span'>, 'children'> & {
number: number; number: number;
+4 -4
View File
@@ -1,10 +1,10 @@
'use client'; 'use client';
import type {ReactNode} from 'react'; import type { ReactNode } from 'react';
import {motion} from 'motion/react'; import { motion } from 'motion/react';
import {WavesIcon} from 'lucide-react'; import { WavesIcon } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
interface AuthShellProps { interface AuthShellProps {
children: ReactNode; children: ReactNode;
+9 -3
View File
@@ -1,8 +1,14 @@
'use client'; 'use client';
import {useEffect, useRef, useState} from 'react'; import { useEffect, useRef, useState } from 'react';
import {CheckCircle2, Loader2, ShieldAlert, ShieldCheck, ShieldQuestion,} from 'lucide-react'; import {
import {getCapToken} from '@/lib/cap-solver'; CheckCircle2,
Loader2,
ShieldAlert,
ShieldCheck,
ShieldQuestion,
} from 'lucide-react';
import { getCapToken } from '@/lib/cap-solver';
type CapStatus = 'idle' | 'solving' | 'solved' | 'error'; type CapStatus = 'idle' | 'solving' | 'solved' | 'error';
+2 -2
View File
@@ -1,5 +1,5 @@
import {type PolicySection} from './types'; import { type PolicySection } from './types';
import {CodeBlock} from '@/components/ui/code-block'; import { CodeBlock } from '@/components/ui/code-block';
import { import {
DocsTable, DocsTable,
DocsTableBody, DocsTableBody,
@@ -1,10 +1,10 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {ImageOff} from 'lucide-react'; import { ImageOff } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {getFileUrl, type ImageQuality} from '@/lib/services/upload/utils'; import { getFileUrl, type ImageQuality } from '@/lib/services/upload/utils';
type FileImagePreviewProps = { type FileImagePreviewProps = {
fileId: string | number; fileId: string | number;
@@ -2,16 +2,33 @@
import * as React from 'react'; import * as React from 'react';
import Link from 'next/link'; import Link from 'next/link';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {motion} from 'motion/react'; import { motion } from 'motion/react';
import {AlertTriangle, Check, Copy, Info, Key, Loader2, Plus, RefreshCw, Shield, Trash2,} from 'lucide-react'; import {
AlertTriangle,
Check,
Copy,
Info,
Key,
Loader2,
Plus,
RefreshCw,
Shield,
Trash2,
} from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'; import {
import {Input} from '@/components/ui/input'; Card,
import {Label} from '@/components/ui/label'; CardContent,
import {Badge} from '@/components/ui/badge'; CardDescription,
import {Switch} from '@/components/ui/switch'; CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Switch } from '@/components/ui/switch';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -28,10 +45,10 @@ import {
BreadcrumbPage, BreadcrumbPage,
BreadcrumbSeparator, BreadcrumbSeparator,
} from '@/components/ui/breadcrumb'; } from '@/components/ui/breadcrumb';
import type {CreateTokenResponse} from '@/lib/services/user'; import type { CreateTokenResponse } from '@/lib/services/user';
import {UserService} from '@/lib/services/user'; import { UserService } from '@/lib/services/user';
import {useAuth} from '@/components/providers/auth-provider'; import { useAuth } from '@/components/providers/auth-provider';
import {toast} from 'sonner'; import { toast } from 'sonner';
export function AccessTokenMain() { export function AccessTokenMain() {
const { user } = useAuth(); const { user } = useAuth();
@@ -2,12 +2,12 @@
import * as React from 'react'; import * as React from 'react';
import Link from 'next/link'; import Link from 'next/link';
import {Check, Loader2, Monitor, Moon, Sun} from 'lucide-react'; import { Check, Loader2, Monitor, Moon, Sun } from 'lucide-react';
import {useTheme} from 'next-themes'; import { useTheme } from 'next-themes';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {useCustomTheme} from '@/lib/theme'; import { useCustomTheme } from '@/lib/theme';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import { import {
Breadcrumb, Breadcrumb,
BreadcrumbItem, BreadcrumbItem,
@@ -1,14 +1,20 @@
'use client'; 'use client';
import {useEffect, useState} from 'react'; import { useEffect, useState } from 'react';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
import {Input} from '@/components/ui/input'; Dialog,
import {Label} from '@/components/ui/label'; DialogContent,
import {Switch} from '@/components/ui/switch'; DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import services from '@/lib/services'; import services from '@/lib/services';
import type {AuthSource, AuthSourceRequest} from '@/lib/services/admin'; import type { AuthSource, AuthSourceRequest } from '@/lib/services/admin';
const emptyForm: AuthSourceRequest = { const emptyForm: AuthSourceRequest = {
name: '', name: '',
@@ -1,7 +1,7 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { import {
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
@@ -19,12 +19,12 @@ import {
Trash2, Trash2,
Upload, Upload,
} from 'lucide-react'; } from 'lucide-react';
import {toast} from 'sonner'; import { toast } from 'sonner';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Input} from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import {Badge} from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import {Card, CardContent} from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@@ -36,9 +36,9 @@ import {
AlertDialogTitle, AlertDialogTitle,
} from '@/components/ui/alert-dialog'; } from '@/components/ui/alert-dialog';
import {FileImagePreview} from '@/components/common/file-image-preview'; import { FileImagePreview } from '@/components/common/file-image-preview';
import services, {formatFileSize} from '@/lib/services'; import services, { formatFileSize } from '@/lib/services';
import type {Upload as UploadRecord} from '@/lib/services/upload/types'; import type { Upload as UploadRecord } from '@/lib/services/upload/types';
/* ─── 工具函数 ─────────────────────────────────────────── */ /* ─── 工具函数 ─────────────────────────────────────────── */
@@ -1,9 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import Link from 'next/link'; import Link from 'next/link';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {motion} from 'motion/react'; import { motion } from 'motion/react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {Book, Check, Copy, Terminal} from 'lucide-react'; import { Book, Check, Copy, Terminal } from 'lucide-react';
export interface DeveloperSectionProps { export interface DeveloperSectionProps {
className?: string; className?: string;
+2 -2
View File
@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import Link from 'next/link'; import Link from 'next/link';
import {Github, LucideIcon} from 'lucide-react'; import { Github, LucideIcon } from 'lucide-react';
export interface FooterSectionProps { export interface FooterSectionProps {
className?: string; className?: string;
@@ -25,8 +25,8 @@
* ``` * ```
*/ */
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {SnowEffect} from '@/components/ui/snow-effect'; import { SnowEffect } from '@/components/ui/snow-effect';
/** /**
* 圣诞树图标组件 * 圣诞树图标组件
+3 -3
View File
@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import {FileTextIcon} from 'lucide-react'; import { FileTextIcon } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
/** /**
* 空状态展示组件属性 * 空状态展示组件属性
+3 -3
View File
@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import {AlertCircle} from 'lucide-react'; import { AlertCircle } from 'lucide-react';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
/** /**
* 错误展示组件属性 * 错误展示组件属性
@@ -1,6 +1,6 @@
'use client'; 'use client';
import {useUser} from '@/contexts/user-context'; import { useUser } from '@/contexts/user-context';
/** /**
* Auth provider bridge hook * Auth provider bridge hook
+2 -2
View File
@@ -2,9 +2,9 @@
import * as React from 'react'; import * as React from 'react';
import * as AccordionPrimitive from '@radix-ui/react-accordion'; import * as AccordionPrimitive from '@radix-ui/react-accordion';
import {ChevronDownIcon} from 'lucide-react'; import { ChevronDownIcon } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Accordion({ function Accordion({
...props ...props
+2 -2
View File
@@ -3,8 +3,8 @@
import * as React from 'react'; import * as React from 'react';
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {buttonVariants} from '@/components/ui/button'; import { buttonVariants } from '@/components/ui/button';
function AlertDialog({ function AlertDialog({
...props ...props
+2 -2
View File
@@ -1,6 +1,6 @@
'use client'; 'use client';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import React, {ReactNode, useEffect, useState} from 'react'; import React, { ReactNode, useEffect, useState } from 'react';
interface AuroraBackgroundProps extends React.HTMLProps<HTMLDivElement> { interface AuroraBackgroundProps extends React.HTMLProps<HTMLDivElement> {
children: ReactNode; children: ReactNode;
+1 -1
View File
@@ -3,7 +3,7 @@
import * as React from 'react'; import * as React from 'react';
import * as AvatarPrimitive from '@radix-ui/react-avatar'; import * as AvatarPrimitive from '@radix-ui/react-avatar';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Avatar({ function Avatar({
className, className,
+3 -3
View File
@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import {Slot} from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import {cva, type VariantProps} from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
const badgeVariants = cva( const badgeVariants = cva(
'inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', 'inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
+3 -3
View File
@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import {Slot} from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import {ChevronRight, MoreHorizontal} from 'lucide-react'; import { ChevronRight, MoreHorizontal } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) { function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
return <nav aria-label='breadcrumb' data-slot='breadcrumb' {...props} />; return <nav aria-label='breadcrumb' data-slot='breadcrumb' {...props} />;
+3 -3
View File
@@ -1,8 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import {Slot} from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import {cva, type VariantProps} from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
const buttonVariants = cva( const buttonVariants = cva(
"cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+8 -4
View File
@@ -1,11 +1,15 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon,} from 'lucide-react'; import {
import {DayButton, DayPicker, getDefaultClassNames} from 'react-day-picker'; ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from 'lucide-react';
import { DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {Button, buttonVariants} from '@/components/ui/button'; import { Button, buttonVariants } from '@/components/ui/button';
function Calendar({ function Calendar({
className, className,
+1 -1
View File
@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Card({ className, ...props }: React.ComponentProps<'div'>) { function Card({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
+6 -4
View File
@@ -1,11 +1,13 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import useEmblaCarousel, {type UseEmblaCarouselType,} from 'embla-carousel-react'; import useEmblaCarousel, {
import {ArrowLeft, ArrowRight} from 'lucide-react'; type UseEmblaCarouselType,
} from 'embla-carousel-react';
import { ArrowLeft, ArrowRight } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {Button} from '@/components/ui/button'; import { Button } from '@/components/ui/button';
type CarouselApi = UseEmblaCarouselType[1]; type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>; type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
+1 -1
View File
@@ -3,7 +3,7 @@
import * as React from 'react'; import * as React from 'react';
import * as RechartsPrimitive from 'recharts'; import * as RechartsPrimitive from 'recharts';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
// Format: { THEME_NAME: CSS_SELECTOR } // Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: '', dark: '.dark' } as const; const THEMES = { light: '', dark: '.dark' } as const;
+2 -2
View File
@@ -2,9 +2,9 @@
import * as React from 'react'; import * as React from 'react';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import {CheckIcon} from 'lucide-react'; import { CheckIcon } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Checkbox({ function Checkbox({
className, className,
+2 -2
View File
@@ -1,8 +1,8 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Check, Copy} from 'lucide-react'; import { Check, Copy } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> { interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
code: string; code: string;
+1 -1
View File
@@ -1,7 +1,7 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Collapsible as CollapsiblePrimitive} from 'radix-ui'; import { Collapsible as CollapsiblePrimitive } from 'radix-ui';
function Collapsible({ function Collapsible({
...props ...props
+11 -5
View File
@@ -1,12 +1,18 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Command as CommandPrimitive} from 'cmdk'; import { Command as CommandPrimitive } from 'cmdk';
import {SearchIcon} from 'lucide-react'; import { SearchIcon } from 'lucide-react';
import {motion} from 'motion/react'; import { motion } from 'motion/react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,} from '@/components/ui/dialog'; import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
function Command({ function Command({
className, className,
+2 -2
View File
@@ -2,9 +2,9 @@
import * as React from 'react'; import * as React from 'react';
import * as DialogPrimitive from '@radix-ui/react-dialog'; import * as DialogPrimitive from '@radix-ui/react-dialog';
import {XIcon} from 'lucide-react'; import { XIcon } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Dialog({ function Dialog({
...props ...props
+1 -1
View File
@@ -1,5 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
const DocsTable = React.forwardRef< const DocsTable = React.forwardRef<
HTMLTableElement, HTMLTableElement,
+2 -2
View File
@@ -1,9 +1,9 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {Drawer as DrawerPrimitive} from 'vaul'; import { Drawer as DrawerPrimitive } from 'vaul';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function Drawer({ function Drawer({
...props ...props
+2 -2
View File
@@ -2,9 +2,9 @@
import * as React from 'react'; import * as React from 'react';
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
import {CheckIcon, ChevronRightIcon, CircleIcon} from 'lucide-react'; import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
function DropdownMenu({ function DropdownMenu({
...props ...props
+5 -5
View File
@@ -1,11 +1,11 @@
'use client'; 'use client';
import {useMemo} from 'react'; import { useMemo } from 'react';
import {cva, type VariantProps} from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import {Separator} from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) { function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {
return ( return (
+1 -1
View File
@@ -1,6 +1,6 @@
'use client'; 'use client';
import {useEffect, useRef} from 'react'; import { useEffect, useRef } from 'react';
interface Particle { interface Particle {
x: number; x: number;
+3 -3
View File
@@ -2,7 +2,7 @@
import * as React from 'react'; import * as React from 'react';
import * as LabelPrimitive from '@radix-ui/react-label'; import * as LabelPrimitive from '@radix-ui/react-label';
import {Slot} from '@radix-ui/react-slot'; import { Slot } from '@radix-ui/react-slot';
import { import {
Controller, Controller,
type ControllerProps, type ControllerProps,
@@ -13,8 +13,8 @@ import {
useFormState, useFormState,
} from 'react-hook-form'; } from 'react-hook-form';
import {cn} from '@/lib/utils'; import { cn } from '@/lib/utils';
import {Label} from '@/components/ui/label'; import { Label } from '@/components/ui/label';
const Form = FormProvider; const Form = FormProvider;
+13 -4
View File
@@ -1,12 +1,21 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {createContext, useCallback, useContext, useRef, useState} from 'react'; import {
import {useQueryClient} from '@tanstack/react-query'; createContext,
useCallback,
useContext,
useRef,
useState,
} from 'react';
import { useQueryClient } from '@tanstack/react-query';
import type {SystemConfig, UpdateSystemConfigRequest,} from '@/lib/services/admin'; import type {
SystemConfig,
UpdateSystemConfigRequest,
} from '@/lib/services/admin';
import services from '@/lib/services'; import services from '@/lib/services';
import {handleContextError} from '@/lib/utils/error-handling'; import { handleContextError } from '@/lib/utils/error-handling';
/** Admin 上下文状态接口 */ /** Admin 上下文状态接口 */
export interface AdminContextState { export interface AdminContextState {
+15 -3
View File
@@ -1,9 +1,21 @@
'use client'; 'use client';
import * as React from 'react'; import * as React from 'react';
import {createContext, useCallback, useContext, useEffect, useRef, useState} from 'react'; import {
import {toast} from 'sonner'; createContext,
import type {AdminUser, CreateUserRequest, ListUsersRequest, UpdateUserRequest,} from '@/lib/services/admin'; useCallback,
useContext,
useEffect,
useRef,
useState,
} from 'react';
import { toast } from 'sonner';
import type {
AdminUser,
CreateUserRequest,
ListUsersRequest,
UpdateUserRequest,
} from '@/lib/services/admin';
import services from '@/lib/services'; import services from '@/lib/services';
/** 用户列表查询参数 */ /** 用户列表查询参数 */
+7 -1
View File
@@ -1,6 +1,12 @@
'use client'; 'use client';
import {createContext, useCallback, useContext, useRef, useState,} from 'react'; import {
createContext,
useCallback,
useContext,
useRef,
useState,
} from 'react';
interface BellRingContextType { interface BellRingContextType {
isRinging: boolean; isRinging: boolean;
+3 -3
View File
@@ -1,6 +1,6 @@
import {dirname} from 'path'; import { dirname } from 'path';
import {fileURLToPath} from 'url'; import { fileURLToPath } from 'url';
import {FlatCompat} from '@eslint/eslintrc'; import { FlatCompat } from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
+1 -1
View File
@@ -7,7 +7,7 @@
// ——— Challenge / Redeem API types ——— // ——— Challenge / Redeem API types ———
import {readApiData} from '@/lib/api-envelope'; import { readApiData } from '@/lib/api-envelope';
export interface ChallengeResponse { export interface ChallengeResponse {
challenge: { c: number; s: number; d: number }; challenge: { c: number; s: number; d: number };
@@ -1,5 +1,9 @@
import {BaseService} from '@/lib/services/core'; import { BaseService } from '@/lib/services/core';
import type {AuthSource, AuthSourceRequest, ToggleAuthSourceRequest,} from './types'; import type {
AuthSource,
AuthSourceRequest,
ToggleAuthSourceRequest,
} from './types';
export class AdminAuthSourceService extends BaseService { export class AdminAuthSourceService extends BaseService {
protected static readonly basePath = '/api/v1/admin'; protected static readonly basePath = '/api/v1/admin';
+2 -2
View File
@@ -1,5 +1,5 @@
import {BaseService} from '@/lib/services/core'; import { BaseService } from '@/lib/services/core';
import type {CacheConfig, CacheStatus} from './types'; import type { CacheConfig, CacheStatus } from './types';
export class AdminCacheService extends BaseService { export class AdminCacheService extends BaseService {
protected static readonly basePath = '/api/v1/admin'; protected static readonly basePath = '/api/v1/admin';
+2 -2
View File
@@ -1,5 +1,5 @@
import type {InternalAxiosRequestConfig} from 'axios'; import type { InternalAxiosRequestConfig } from 'axios';
import {BaseService} from '@/lib/services/core'; import { BaseService } from '@/lib/services/core';
import type { import type {
AuthSource, AuthSource,
ChangePasswordRequest, ChangePasswordRequest,
@@ -1,5 +1,5 @@
import {BaseService} from '@/lib/services/core'; import { BaseService } from '@/lib/services/core';
import type {PublicConfigResponse} from './types'; import type { PublicConfigResponse } from './types';
/** /**
* 配置服务 * 配置服务
+9 -4
View File
@@ -1,6 +1,11 @@
import axios, {AxiosError, AxiosResponse, CancelTokenSource, InternalAxiosRequestConfig,} from 'axios'; import axios, {
import {toast} from 'sonner'; AxiosError,
import {apiConfig, getApiBaseUrl} from './config'; AxiosResponse,
CancelTokenSource,
InternalAxiosRequestConfig,
} from 'axios';
import { toast } from 'sonner';
import { apiConfig, getApiBaseUrl } from './config';
import { import {
ApiErrorBase, ApiErrorBase,
ForbiddenError, ForbiddenError,
@@ -11,7 +16,7 @@ import {
UnauthorizedError, UnauthorizedError,
ValidationError, ValidationError,
} from './errors'; } from './errors';
import {ApiError, ApiResponse} from './types'; import { ApiError, ApiResponse } from './types';
/** /**
* API 客户端实例 * API 客户端实例
+2 -2
View File
@@ -1,6 +1,6 @@
import apiClient from './api-client'; import apiClient from './api-client';
import {ApiResponse} from './types'; import { ApiResponse } from './types';
import {InternalAxiosRequestConfig} from 'axios'; import { InternalAxiosRequestConfig } from 'axios';
/** /**
* 服务基类 * 服务基类
@@ -1,8 +1,12 @@
import type {InternalAxiosRequestConfig} from 'axios'; import type { InternalAxiosRequestConfig } from 'axios';
import apiClient from '@/lib/services/core/api-client'; import apiClient from '@/lib/services/core/api-client';
import {BaseService} from '@/lib/services/core'; import { BaseService } from '@/lib/services/core';
import type {DBOverview, ExecuteSQLResponse, TableDataResponse,} from './types'; import type {
DBOverview,
ExecuteSQLResponse,
TableDataResponse,
} from './types';
/** /**
* 数据库管理服务类 * 数据库管理服务类
@@ -1,4 +1,4 @@
import {OpenFlareBaseService} from './base.service'; import { OpenFlareBaseService } from './base.service';
import type { import type {
AccessLogCleanupPayload, AccessLogCleanupPayload,
AccessLogCleanupResult, AccessLogCleanupResult,
@@ -1,5 +1,10 @@
import {OpenFlareBaseService} from './base.service'; import { OpenFlareBaseService } from './base.service';
import type {ApplyLogCleanupPayload, ApplyLogCleanupResult, ApplyLogList, ApplyLogListQuery,} from './types'; import type {
ApplyLogCleanupPayload,
ApplyLogCleanupResult,
ApplyLogList,
ApplyLogListQuery,
} from './types';
export class ApplyLogService extends OpenFlareBaseService { export class ApplyLogService extends OpenFlareBaseService {
protected static override readonly basePath: string = '/api/v1/d/apply-logs'; protected static override readonly basePath: string = '/api/v1/d/apply-logs';
@@ -1,4 +1,4 @@
import {BaseService} from '@/lib/services/core'; import { BaseService } from '@/lib/services/core';
export class OpenFlareBaseService extends BaseService { export class OpenFlareBaseService extends BaseService {
protected static override readonly basePath: string = '/api/v1/d'; protected static override readonly basePath: string = '/api/v1/d';
@@ -1,6 +1,6 @@
import type {InternalAxiosRequestConfig} from 'axios'; import type { InternalAxiosRequestConfig } from 'axios';
import {OpenFlareBaseService} from './base.service'; import { OpenFlareBaseService } from './base.service';
import type { import type {
ConfigDiffResult, ConfigDiffResult,
ConfigPreviewResult, ConfigPreviewResult,
@@ -1,4 +1,4 @@
import {OpenFlareBaseService} from './base.service'; import { OpenFlareBaseService } from './base.service';
import type { import type {
CompactCapacityTrendPoint, CompactCapacityTrendPoint,
CompactDashboardNodeHealth, CompactDashboardNodeHealth,
@@ -1,5 +1,5 @@
import {OpenFlareBaseService} from './base.service'; import { OpenFlareBaseService } from './base.service';
import type {DnsAccountItem, DnsAccountMutationPayload} from './types'; import type { DnsAccountItem, DnsAccountMutationPayload } from './types';
export class DnsAccountService extends OpenFlareBaseService { export class DnsAccountService extends OpenFlareBaseService {
protected static override readonly basePath: string = protected static override readonly basePath: string =
@@ -1,7 +1,7 @@
import type {InternalAxiosRequestConfig} from 'axios'; import type { InternalAxiosRequestConfig } from 'axios';
import {BaseService} from '../core/base.service'; import { BaseService } from '../core/base.service';
import type {FileStatsResponse, ListUploadsResponse} from './types'; import type { FileStatsResponse, ListUploadsResponse } from './types';
export class AdminUploadService extends BaseService { export class AdminUploadService extends BaseService {
protected static readonly basePath = '/api/v1/admin/uploads'; protected static readonly basePath = '/api/v1/admin/uploads';
+1 -1
View File
@@ -1,4 +1,4 @@
import type {ThemeConfig} from './types'; import type { ThemeConfig } from './types';
/** /**
* 主题系统配置 * 主题系统配置
+11 -5
View File
@@ -1,10 +1,16 @@
'use client'; 'use client';
import React, {createContext, useCallback, useContext, useEffect, useState,} from 'react'; import React, {
import {useTheme as useNextTheme} from 'next-themes'; createContext,
import type {Theme} from './types'; useCallback,
import {getStoredThemeId, setStoredThemeId} from './storage'; useContext,
import {getAvailableThemes} from './parser'; useEffect,
useState,
} from 'react';
import { useTheme as useNextTheme } from 'next-themes';
import type { Theme } from './types';
import { getStoredThemeId, setStoredThemeId } from './storage';
import { getAvailableThemes } from './parser';
/** /**
* 主题上下文类型定义 * 主题上下文类型定义

Some files were not shown because too many files have changed in this diff Show More