mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-13 02:26:36 +08:00
prettier
This commit is contained in:
@@ -5,7 +5,7 @@ import type {
|
||||
ProxyRouteMutationPayload,
|
||||
ZoneDomainItem,
|
||||
} from '@/lib/services/openflare';
|
||||
import {ZoneService} from '@/lib/services/openflare';
|
||||
import { ZoneService } from '@/lib/services/openflare';
|
||||
|
||||
export const proxyRouteConfigSections = [
|
||||
{
|
||||
@@ -353,6 +353,8 @@ export function buildPayloadFromRoute(
|
||||
/** Load all Zone domains across registered Zones for route binding selectors. */
|
||||
export async function listAllZoneDomains(): Promise<ZoneDomainItem[]> {
|
||||
const zones = await ZoneService.list();
|
||||
const overviews = await Promise.all(zones.map((zone) => ZoneService.getOverview(zone.id)));
|
||||
const overviews = await Promise.all(
|
||||
zones.map((zone) => ZoneService.getOverview(zone.id)),
|
||||
);
|
||||
return overviews.flatMap((overview) => overview.domains ?? []);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import {useEffect} from 'react';
|
||||
import {zodResolver} from '@hookform/resolvers/zod';
|
||||
import {useQuery} from '@tanstack/react-query';
|
||||
import {useForm} from 'react-hook-form';
|
||||
import {z} from 'zod';
|
||||
import { useEffect } from 'react';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {Button} from '@/components/ui/button';
|
||||
import {Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,} from '@/components/ui/form';
|
||||
import {Input} from '@/components/ui/input';
|
||||
import {Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle,} from '@/components/ui/sheet';
|
||||
import {Switch} from '@/components/ui/switch';
|
||||
import type {ProxyRouteItem} from '@/lib/services/openflare';
|
||||
import {ProxyRouteService, ZoneService, zoneQueryKey} from '@/lib/services/openflare';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import type { ProxyRouteItem } from '@/lib/services/openflare';
|
||||
import {
|
||||
ProxyRouteService,
|
||||
ZoneService,
|
||||
zoneQueryKey,
|
||||
} from '@/lib/services/openflare';
|
||||
|
||||
import {listAllZoneDomains, parseOriginUrl} from './helpers';
|
||||
import {ZoneDomainSelector} from './zone-domain-selector';
|
||||
import { listAllZoneDomains, parseOriginUrl } from './helpers';
|
||||
import { ZoneDomainSelector } from './zone-domain-selector';
|
||||
|
||||
const createProxyRouteSchema = z
|
||||
.object({
|
||||
site_name: z.string().trim().max(255, '站点标识不能超过 255 个字符'),
|
||||
zone_domain_ids: z.array(z.number().int().positive()).min(1, '请至少选择一个域名'),
|
||||
zone_domain_ids: z
|
||||
.array(z.number().int().positive())
|
||||
.min(1, '请至少选择一个域名'),
|
||||
origin_url: z.string().trim().min(1, '请输入上游地址'),
|
||||
enabled: z.boolean(),
|
||||
})
|
||||
@@ -125,33 +146,37 @@ export function ProxyRouteCreateSheet({
|
||||
onCreated(route);
|
||||
} catch (error) {
|
||||
form.setError('root', {
|
||||
message: error instanceof Error ? error.message : '创建失败,请稍后重试',
|
||||
message:
|
||||
error instanceof Error ? error.message : '创建失败,请稍后重试',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="right" className="w-full sm:max-w-lg overflow-y-auto">
|
||||
<SheetContent side='right' className='w-full sm:max-w-lg overflow-y-auto'>
|
||||
<SheetHeader>
|
||||
<SheetTitle>新建规则</SheetTitle>
|
||||
<SheetDescription>
|
||||
从已注册的 Zone 域名中选择绑定关系,创建后可继续配置缓存和限流等高级选项。
|
||||
从已注册的 Zone
|
||||
域名中选择绑定关系,创建后可继续配置缓存和限流等高级选项。
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={handleSubmit} className="space-y-4 px-4 pb-4">
|
||||
<form onSubmit={handleSubmit} className='space-y-4 px-4 pb-4'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="site_name"
|
||||
name='site_name'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>站点标识</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="marketing-site" {...field} />
|
||||
<Input placeholder='marketing-site' {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>可选,留空时会自动使用首个域名。</FormDescription>
|
||||
<FormDescription>
|
||||
可选,留空时会自动使用首个域名。
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -159,7 +184,7 @@ export function ProxyRouteCreateSheet({
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="zone_domain_ids"
|
||||
name='zone_domain_ids'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>绑定域名</FormLabel>
|
||||
@@ -185,12 +210,12 @@ export function ProxyRouteCreateSheet({
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="origin_url"
|
||||
name='origin_url'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>上游地址</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="http://127.0.0.1:8080" {...field} />
|
||||
<Input placeholder='http://127.0.0.1:8080' {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -199,29 +224,40 @@ export function ProxyRouteCreateSheet({
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="enabled"
|
||||
name='enabled'
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center justify-between rounded-lg border p-3">
|
||||
<div className="space-y-0.5">
|
||||
<FormItem className='flex items-center justify-between rounded-lg border p-3'>
|
||||
<div className='space-y-0.5'>
|
||||
<FormLabel>启用站点</FormLabel>
|
||||
<FormDescription>关闭后会保留配置,但不会参与发布。</FormDescription>
|
||||
<FormDescription>
|
||||
关闭后会保留配置,但不会参与发布。
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.formState.errors.root ? (
|
||||
<p className="text-sm text-destructive">{form.formState.errors.root.message}</p>
|
||||
<p className='text-sm text-destructive'>
|
||||
{form.formState.errors.root.message}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<SheetFooter className="px-0">
|
||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
||||
<SheetFooter className='px-0'>
|
||||
<Button
|
||||
type='button'
|
||||
variant='outline'
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="submit" disabled={form.formState.isSubmitting}>
|
||||
<Button type='submit' disabled={form.formState.isSubmitting}>
|
||||
{form.formState.isSubmitting ? '创建中…' : '创建'}
|
||||
</Button>
|
||||
</SheetFooter>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
'use client';
|
||||
|
||||
import {useMemo, useState} from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import {ExternalLink, Plus} from 'lucide-react';
|
||||
import { ExternalLink, Plus } from 'lucide-react';
|
||||
|
||||
import {Badge} from '@/components/ui/badge';
|
||||
import {Button} from '@/components/ui/button';
|
||||
import {Checkbox} from '@/components/ui/checkbox';
|
||||
import {Input} from '@/components/ui/input';
|
||||
import {cn} from '@/lib/utils';
|
||||
import type {ZoneDomainItem, ZoneItem} from '@/lib/services/openflare';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { ZoneDomainItem, ZoneItem } from '@/lib/services/openflare';
|
||||
|
||||
import {QuickCreateZoneDomainDialog} from '../../websites/components/quick-create-zone-domain-dialog';
|
||||
import { QuickCreateZoneDomainDialog } from '../../websites/components/quick-create-zone-domain-dialog';
|
||||
|
||||
export interface ZoneDomainSelectorProps {
|
||||
value: number[];
|
||||
@@ -49,7 +49,8 @@ export function ZoneDomainSelector({
|
||||
() =>
|
||||
domains.filter(
|
||||
(domain) =>
|
||||
domain.proxy_route_id == null || domain.proxy_route_id === currentRouteId,
|
||||
domain.proxy_route_id == null ||
|
||||
domain.proxy_route_id === currentRouteId,
|
||||
),
|
||||
[currentRouteId, domains],
|
||||
);
|
||||
@@ -92,33 +93,36 @@ export function ZoneDomainSelector({
|
||||
|
||||
return (
|
||||
<div className={cn('space-y-3', className)}>
|
||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<div className='flex flex-col gap-2 sm:flex-row sm:items-center'>
|
||||
<Input
|
||||
value={keyword}
|
||||
onChange={(event) => setKeyword(event.target.value)}
|
||||
placeholder="搜索域名或 Zone…"
|
||||
placeholder='搜索域名或 Zone…'
|
||||
disabled={disabled}
|
||||
className="sm:flex-1"
|
||||
className='sm:flex-1'
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-8 shrink-0 text-xs"
|
||||
type='button'
|
||||
variant='secondary'
|
||||
size='sm'
|
||||
className='h-8 shrink-0 text-xs'
|
||||
disabled={disabled}
|
||||
onClick={() => setCreateOpen(true)}
|
||||
>
|
||||
<Plus className="mr-1 size-3.5" />
|
||||
<Plus className='mr-1 size-3.5' />
|
||||
快捷新增域名
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{filtered.length === 0 ? (
|
||||
<div className="rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground">
|
||||
<div className='rounded-lg border border-dashed px-4 py-8 text-center text-sm text-muted-foreground'>
|
||||
{domains.length === 0 ? (
|
||||
<>
|
||||
暂无可用 Zone 域名。请先在{' '}
|
||||
<Link href="/websites" className="text-primary underline-offset-4 hover:underline">
|
||||
<Link
|
||||
href='/websites'
|
||||
className='text-primary underline-offset-4 hover:underline'
|
||||
>
|
||||
网站管理
|
||||
</Link>{' '}
|
||||
中添加 FQDN,或使用「快捷新增域名」。
|
||||
@@ -130,7 +134,7 @@ export function ZoneDomainSelector({
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="max-h-72 space-y-1 overflow-y-auto rounded-lg border p-2">
|
||||
<div className='max-h-72 space-y-1 overflow-y-auto rounded-lg border p-2'>
|
||||
{filtered.map((domain) => {
|
||||
const checked = selected.has(domain.id);
|
||||
const zoneRoot = zoneMap.get(domain.zone_id);
|
||||
@@ -147,30 +151,32 @@ export function ZoneDomainSelector({
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
onCheckedChange={() => toggle(domain)}
|
||||
className="mt-0.5"
|
||||
className='mt-0.5'
|
||||
/>
|
||||
<span className="min-w-0 flex-1 space-y-1">
|
||||
<span className="flex flex-wrap items-center gap-2">
|
||||
<span className="truncate text-sm font-medium">{domain.domain}</span>
|
||||
<span className='min-w-0 flex-1 space-y-1'>
|
||||
<span className='flex flex-wrap items-center gap-2'>
|
||||
<span className='truncate text-sm font-medium'>
|
||||
{domain.domain}
|
||||
</span>
|
||||
{domain.cert_id ? (
|
||||
<Badge variant="secondary" className="text-[10px]">
|
||||
<Badge variant='secondary' className='text-[10px]'>
|
||||
证书 #{domain.cert_id}
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
<Badge variant='outline' className='text-[10px]'>
|
||||
无证书
|
||||
</Badge>
|
||||
)}
|
||||
</span>
|
||||
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<span className='flex items-center gap-1 text-xs text-muted-foreground'>
|
||||
{zoneRoot ? (
|
||||
<Link
|
||||
href={`/websites/${domain.zone_id}`}
|
||||
className="inline-flex items-center gap-0.5 hover:text-foreground"
|
||||
className='inline-flex items-center gap-0.5 hover:text-foreground'
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
Zone {zoneRoot}
|
||||
<ExternalLink className="size-3" />
|
||||
<ExternalLink className='size-3' />
|
||||
</Link>
|
||||
) : (
|
||||
<span>Zone #{domain.zone_id}</span>
|
||||
|
||||
Reference in New Issue
Block a user