✨ feat(view): Add AnyTLS protocol support and enhance node configuration options
This commit is contained in:
@@ -136,14 +136,14 @@ export function Register() {
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<TableRow className='flex flex-col'>
|
||||
<TableCell className='w-full'>
|
||||
<Label>{t('trialSubscribePlan')}</Label>
|
||||
<p className='text-muted-foreground text-xs'>
|
||||
{t('trialSubscribePlanDescription')}
|
||||
</p>
|
||||
</TableCell>
|
||||
<TableCell className='text-right'>
|
||||
<TableCell className='max-w-96'>
|
||||
<EnhancedInput
|
||||
placeholder={t('trialDuration')}
|
||||
type='number'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const protocols = ['shadowsocks', 'vmess', 'vless', 'trojan', 'hysteria2', 'tuic'];
|
||||
export const protocols = ['shadowsocks', 'vmess', 'vless', 'trojan', 'hysteria2', 'tuic', 'anytls'];
|
||||
|
||||
const nullableString = z.string().nullish();
|
||||
const portSchema = z.number().max(65535).nullish();
|
||||
@@ -58,7 +58,15 @@ const hysteria2Schema = z.object({
|
||||
|
||||
const tuicSchema = z.object({
|
||||
port: portSchema,
|
||||
security: z.string(),
|
||||
disable_sni: z.boolean().default(false),
|
||||
reduce_rtt: z.boolean().default(false),
|
||||
udp_relay_mode: z.string().default('native'),
|
||||
congestion_controller: z.string().default('bbr'),
|
||||
security_config: securityConfigSchema,
|
||||
});
|
||||
|
||||
const anytlsSchema = z.object({
|
||||
port: portSchema,
|
||||
security_config: securityConfigSchema,
|
||||
});
|
||||
|
||||
@@ -87,6 +95,10 @@ const protocolConfigSchema = z.discriminatedUnion('protocol', [
|
||||
protocol: z.literal('tuic'),
|
||||
config: tuicSchema,
|
||||
}),
|
||||
z.object({
|
||||
protocol: z.literal('anytls'),
|
||||
config: anytlsSchema,
|
||||
}),
|
||||
]);
|
||||
|
||||
const baseFormSchema = z.object({
|
||||
|
||||
@@ -110,7 +110,7 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
||||
{trigger}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className='w-[520px] max-w-full md:max-w-screen-md'>
|
||||
<SheetContent className='w-[580px] max-w-full md:max-w-screen-md'>
|
||||
<SheetHeader>
|
||||
<SheetTitle>{title}</SheetTitle>
|
||||
</SheetHeader>
|
||||
@@ -290,7 +290,7 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
if (['trojan', 'hysteria2', 'tuic'].includes(value)) {
|
||||
if (['trojan', 'hysteria2', 'tuic', 'anytls'].includes(value)) {
|
||||
form.setValue('config.security', 'tls');
|
||||
}
|
||||
}}
|
||||
@@ -402,11 +402,11 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{['vmess', 'vless', 'trojan', 'hysteria2', 'tuic'].includes(protocol) && (
|
||||
{['vmess', 'vless', 'trojan', 'hysteria2', 'tuic', 'anytls'].includes(protocol) && (
|
||||
<div className='grid gap-4'>
|
||||
<div
|
||||
className={cn('flex gap-4 *:flex-1', {
|
||||
'grid grid-cols-2': ['hysteria2'].includes(protocol),
|
||||
'grid grid-cols-2': ['hysteria2', 'tuic'].includes(protocol),
|
||||
})}
|
||||
>
|
||||
<FormField
|
||||
@@ -524,6 +524,109 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{protocol === 'tuic' && (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.udp_relay_mode'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.udpRelayMode')}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t('form.pleaseSelect')} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value='native'>Native</SelectItem>
|
||||
<SelectItem value='quic'>QUIC</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.congestion_controller'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.congestionController')}</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t('form.pleaseSelect')} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value='bbr'>BBR</SelectItem>
|
||||
<SelectItem value='cubic'>Cubic</SelectItem>
|
||||
<SelectItem value='reno'>Reno</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className='flex gap-2'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.disable_sni'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.disableSni')}</FormLabel>
|
||||
<FormControl>
|
||||
<div className='pt-2'>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setValue(field.name, checked);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.reduce_rtt'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.reduceRtt')}</FormLabel>
|
||||
<FormControl>
|
||||
<div className='pt-2'>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setValue(field.name, checked);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{['vmess', 'vless', 'trojan'].includes(protocol) && (
|
||||
<Card>
|
||||
@@ -630,171 +733,17 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
<Card>
|
||||
<CardHeader className='flex flex-row items-center justify-between p-3'>
|
||||
<CardTitle>{t('form.securityConfig')}</CardTitle>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security'
|
||||
render={({ field }) => (
|
||||
<FormItem className='!mt-0 min-w-32'>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t('form.pleaseSelect')} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{['vmess', 'vless'].includes(protocol) && (
|
||||
<SelectItem value='none'>NONE</SelectItem>
|
||||
)}
|
||||
<SelectItem value='tls'>TLS</SelectItem>
|
||||
{protocol === 'vless' && (
|
||||
<SelectItem value='reality'>Reality</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</CardHeader>
|
||||
{security !== 'none' && (
|
||||
<CardContent className='grid grid-cols-2 gap-4 p-3'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.sni'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Server Name(SNI)</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{protocol === 'vless' && security === 'reality' && (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_server_addr'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.serverAddress')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t(
|
||||
'form.security_config.serverAddressPlaceholder',
|
||||
)}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_server_port'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.serverPort')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
type='number'
|
||||
min={1}
|
||||
max={65535}
|
||||
placeholder={t('form.security_config.serverPortPlaceholder')}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_private_key'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.privateKey')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t('form.security_config.privateKeyPlaceholder')}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_public_key'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.publicKey')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t('form.security_config.publicKeyPlaceholder')}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_short_id'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.shortId')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t('form.security_config.shortIdPlaceholder')}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{protocol === 'vless' && (
|
||||
{(['vmess', 'vless', 'trojan'].includes(protocol) ||
|
||||
['anytls', 'tuic', 'hysteria2'].includes(protocol)) && (
|
||||
<Card>
|
||||
<CardHeader className='flex flex-row items-center justify-between p-3'>
|
||||
<CardTitle>{t('form.securityConfig')}</CardTitle>
|
||||
{['vmess', 'vless', 'trojan'].includes(protocol) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.fingerprint'
|
||||
name='config.security'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.fingerprint')}</FormLabel>
|
||||
<FormItem className='!mt-0 min-w-32'>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
@@ -807,45 +756,213 @@ export default function NodeForm<T extends { [x: string]: any }>({
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value='chrome'>Chrome</SelectItem>
|
||||
<SelectItem value='firefox'>Firefox</SelectItem>
|
||||
<SelectItem value='safari'>Safari</SelectItem>
|
||||
<SelectItem value='ios'>IOS</SelectItem>
|
||||
<SelectItem value='android'>Android</SelectItem>
|
||||
<SelectItem value='edge'>edge</SelectItem>
|
||||
<SelectItem value='360'>360</SelectItem>
|
||||
<SelectItem value='qq'>QQ</SelectItem>
|
||||
{['vmess', 'vless'].includes(protocol) && (
|
||||
<SelectItem value='none'>NONE</SelectItem>
|
||||
)}
|
||||
<SelectItem value='tls'>TLS</SelectItem>
|
||||
{protocol === 'vless' && (
|
||||
<SelectItem value='reality'>Reality</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.allow_insecure'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Allow Insecure</FormLabel>
|
||||
<FormControl>
|
||||
<div className='pt-2'>
|
||||
<Switch
|
||||
checked={!!field.value}
|
||||
onCheckedChange={(value) => {
|
||||
</CardHeader>
|
||||
{(['anytls', 'tuic', 'hysteria2'].includes(protocol) ||
|
||||
(['vmess', 'vless', 'trojan'].includes(protocol) &&
|
||||
security !== 'none')) && (
|
||||
<CardContent className='grid grid-cols-2 gap-4 p-3'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.sni'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Server Name(SNI)</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Reality 特殊配置只在 vless + reality 时显示 */}
|
||||
{protocol === 'vless' && security === 'reality' && (
|
||||
<>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_server_addr'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.serverAddress')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t(
|
||||
'form.security_config.serverAddressPlaceholder',
|
||||
)}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_server_port'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.serverPort')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
type='number'
|
||||
min={1}
|
||||
max={65535}
|
||||
placeholder={t(
|
||||
'form.security_config.serverPortPlaceholder',
|
||||
)}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_private_key'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.privateKey')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t(
|
||||
'form.security_config.privateKeyPlaceholder',
|
||||
)}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_public_key'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.publicKey')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t('form.security_config.publicKeyPlaceholder')}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.reality_short_id'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.shortId')}</FormLabel>
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
{...field}
|
||||
placeholder={t('form.security_config.shortIdPlaceholder')}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{protocol === 'vless' && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.fingerprint'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('form.security_config.fingerprint')}</FormLabel>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
form.setValue(field.name, value);
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t('form.pleaseSelect')} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value='chrome'>Chrome</SelectItem>
|
||||
<SelectItem value='firefox'>Firefox</SelectItem>
|
||||
<SelectItem value='safari'>Safari</SelectItem>
|
||||
<SelectItem value='ios'>IOS</SelectItem>
|
||||
<SelectItem value='android'>Android</SelectItem>
|
||||
<SelectItem value='edge'>edge</SelectItem>
|
||||
<SelectItem value='360'>360</SelectItem>
|
||||
<SelectItem value='qq'>QQ</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='config.security_config.allow_insecure'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Allow Insecure</FormLabel>
|
||||
<FormControl>
|
||||
<div className='pt-2'>
|
||||
<Switch
|
||||
checked={!!field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setValue(field.name, checked);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ export default function NodeTable() {
|
||||
'bg-yellow-500': row.original.protocol === 'trojan',
|
||||
'bg-purple-500': row.original.protocol === 'hysteria2',
|
||||
'bg-cyan-500': row.original.protocol === 'tuic',
|
||||
'bg-gray-500': row.original.protocol === 'anytls',
|
||||
})}
|
||||
>
|
||||
{row.getValue('id')}
|
||||
|
||||
+11
-10
@@ -9,20 +9,20 @@ import { NextIntlClientProvider } from 'next-intl';
|
||||
import { getLocale, getMessages } from 'next-intl/server';
|
||||
import { PublicEnvScript } from 'next-runtime-env';
|
||||
import { unstable_noStore as noStore } from 'next/cache';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
// import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import { cookies } from 'next/headers';
|
||||
import NextTopLoader from 'nextjs-toploader';
|
||||
import React from 'react';
|
||||
|
||||
const fontSans = Geist({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-sans',
|
||||
});
|
||||
// const fontSans = Geist({
|
||||
// subsets: ['latin'],
|
||||
// variable: '--font-sans',
|
||||
// });
|
||||
|
||||
const fontMono = Geist_Mono({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-mono',
|
||||
});
|
||||
// const fontMono = Geist_Mono({
|
||||
// subsets: ['latin'],
|
||||
// variable: '--font-mono',
|
||||
// });
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
noStore();
|
||||
@@ -111,7 +111,8 @@ export default async function RootLayout({
|
||||
</head>
|
||||
<body
|
||||
suppressHydrationWarning
|
||||
className={`${fontSans.variable} ${fontMono.variable} size-full min-h-[calc(100dvh-env(safe-area-inset-top))] font-sans antialiased`}
|
||||
// ${fontSans.variable} ${fontMono.variable}
|
||||
className={`size-full min-h-[calc(100dvh-env(safe-area-inset-top))] font-sans antialiased`}
|
||||
>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<NextTopLoader showSpinner={false} />
|
||||
|
||||
@@ -68,11 +68,14 @@
|
||||
"cancel": "Cancel",
|
||||
"city": "City",
|
||||
"confirm": "Confirm",
|
||||
"congestionController": "Congestion Controller",
|
||||
"country": "Country",
|
||||
"disableSni": "Disable SNI",
|
||||
"edit": "Edit",
|
||||
"editSecurity": "Edit Security Configuration",
|
||||
"enableTLS": "Enable TLS",
|
||||
"encryptionMethod": "Encryption Method",
|
||||
"fingerprint": "Fingerprint",
|
||||
"flow": "Flow Control Algorithm",
|
||||
"groupId": "Node Group",
|
||||
"hopInterval": "Hop Interval",
|
||||
@@ -85,6 +88,7 @@
|
||||
"pleaseSelect": "Please Select",
|
||||
"port": "Server Port",
|
||||
"protocol": "Protocol",
|
||||
"reduceRtt": "Reduce RTT",
|
||||
"relayHost": "Relay Host",
|
||||
"relayMode": "Relay Mode",
|
||||
"relayModeOptions": {
|
||||
@@ -128,6 +132,7 @@
|
||||
"transport": "Transport Protocol Configuration",
|
||||
"transportConfig": "Transport Protocol Configuration",
|
||||
"transportHost": "Transport Server Address",
|
||||
"udpRelayMode": "UDP Relay Mode",
|
||||
"transportPath": "Transport Path",
|
||||
"transportServerName": "Transport Server Name"
|
||||
},
|
||||
|
||||
@@ -68,11 +68,14 @@
|
||||
"cancel": "取消",
|
||||
"city": "城市",
|
||||
"confirm": "确认",
|
||||
"congestionController": "拥塞控制",
|
||||
"country": "国家",
|
||||
"disableSni": "禁用SNI",
|
||||
"edit": "编辑",
|
||||
"editSecurity": "编辑安全性配置",
|
||||
"enableTLS": "启用TLS",
|
||||
"encryptionMethod": "加密方法",
|
||||
"fingerprint": "指纹",
|
||||
"flow": "流控算法",
|
||||
"groupId": "节点组",
|
||||
"hopInterval": "跳跃间隔",
|
||||
@@ -85,6 +88,7 @@
|
||||
"pleaseSelect": "请选择",
|
||||
"port": "服务端口",
|
||||
"protocol": "协议",
|
||||
"reduceRtt": "减少RTT",
|
||||
"relayHost": "中转地址",
|
||||
"relayMode": "中转模式",
|
||||
"relayModeOptions": {
|
||||
@@ -128,6 +132,7 @@
|
||||
"transport": "传输协议",
|
||||
"transportConfig": "传输协议配置",
|
||||
"transportHost": "传输服务地址",
|
||||
"udpRelayMode": "UDP中继模式",
|
||||
"transportPath": "传输路径",
|
||||
"transportServerName": "传输服务名称"
|
||||
},
|
||||
|
||||
+51
-3
@@ -28,6 +28,11 @@ declare namespace API {
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type AnyTLS = {
|
||||
port: number;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
type Application = {
|
||||
id: number;
|
||||
icon: string;
|
||||
@@ -75,6 +80,40 @@ declare namespace API {
|
||||
is_default: boolean;
|
||||
};
|
||||
|
||||
type AppUserSubcbribe = {
|
||||
id: number;
|
||||
name: string;
|
||||
upload: number;
|
||||
traffic: number;
|
||||
download: number;
|
||||
device_limit: number;
|
||||
start_time: string;
|
||||
expire_time: string;
|
||||
list: AppUserSubscbribeNode[];
|
||||
};
|
||||
|
||||
type AppUserSubscbribeNode = {
|
||||
id: number;
|
||||
name: string;
|
||||
uuid: string;
|
||||
protocol: string;
|
||||
relay_mode: string;
|
||||
relay_node: string;
|
||||
server_addr: string;
|
||||
speed_limit: number;
|
||||
tags: string[];
|
||||
traffic: number;
|
||||
traffic_ratio: number;
|
||||
upload: number;
|
||||
config: string;
|
||||
country: string;
|
||||
city: string;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
created_at: number;
|
||||
download: number;
|
||||
};
|
||||
|
||||
type AuthConfig = {
|
||||
mobile: MobileAuthenticateConfig;
|
||||
email: EmailAuthticateConfig;
|
||||
@@ -598,7 +637,7 @@ declare namespace API {
|
||||
type GetNodeListParams = {
|
||||
page: number;
|
||||
size: number;
|
||||
tag?: string;
|
||||
tags?: string;
|
||||
group_id?: number;
|
||||
search?: string;
|
||||
};
|
||||
@@ -610,7 +649,7 @@ declare namespace API {
|
||||
type GetNodeServerListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
tag?: string;
|
||||
tags?: string;
|
||||
group_id?: number;
|
||||
search?: string;
|
||||
};
|
||||
@@ -1077,7 +1116,7 @@ declare namespace API {
|
||||
type PurchaseOrderRequest = {
|
||||
subscribe_id: number;
|
||||
quantity: number;
|
||||
payment: number;
|
||||
payment?: number;
|
||||
coupon?: string;
|
||||
};
|
||||
|
||||
@@ -1130,6 +1169,11 @@ declare namespace API {
|
||||
total: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateCountResponse = {
|
||||
registers: number;
|
||||
total_commission: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
@@ -1455,6 +1499,10 @@ declare namespace API {
|
||||
|
||||
type Tuic = {
|
||||
port: number;
|
||||
disable_sni: boolean;
|
||||
reduce_rtt: boolean;
|
||||
udp_relay_mode: string;
|
||||
congestion_controller: string;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
/* eslint-disable */
|
||||
|
||||
import request from '@/utils/request';
|
||||
|
||||
/** Apple Login Callback POST /v1/auth/oauth/callback/apple */
|
||||
|
||||
+49
-1
@@ -28,6 +28,11 @@ declare namespace API {
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type AnyTLS = {
|
||||
port: number;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
type AppleLoginCallbackRequest = {
|
||||
code: string;
|
||||
id_token: string;
|
||||
@@ -81,6 +86,40 @@ declare namespace API {
|
||||
is_default: boolean;
|
||||
};
|
||||
|
||||
type AppUserSubcbribe = {
|
||||
id: number;
|
||||
name: string;
|
||||
upload: number;
|
||||
traffic: number;
|
||||
download: number;
|
||||
device_limit: number;
|
||||
start_time: string;
|
||||
expire_time: string;
|
||||
list: AppUserSubscbribeNode[];
|
||||
};
|
||||
|
||||
type AppUserSubscbribeNode = {
|
||||
id: number;
|
||||
name: string;
|
||||
uuid: string;
|
||||
protocol: string;
|
||||
relay_mode: string;
|
||||
relay_node: string;
|
||||
server_addr: string;
|
||||
speed_limit: number;
|
||||
tags: string[];
|
||||
traffic: number;
|
||||
traffic_ratio: number;
|
||||
upload: number;
|
||||
config: string;
|
||||
country: string;
|
||||
city: string;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
created_at: number;
|
||||
download: number;
|
||||
};
|
||||
|
||||
type AuthConfig = {
|
||||
mobile: MobileAuthenticateConfig;
|
||||
email: EmailAuthticateConfig;
|
||||
@@ -473,7 +512,7 @@ declare namespace API {
|
||||
type PurchaseOrderRequest = {
|
||||
subscribe_id: number;
|
||||
quantity: number;
|
||||
payment: number;
|
||||
payment?: number;
|
||||
coupon?: string;
|
||||
};
|
||||
|
||||
@@ -526,6 +565,11 @@ declare namespace API {
|
||||
total: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateCountResponse = {
|
||||
registers: number;
|
||||
total_commission: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
@@ -830,6 +874,10 @@ declare namespace API {
|
||||
|
||||
type Tuic = {
|
||||
port: number;
|
||||
disable_sni: boolean;
|
||||
reduce_rtt: boolean;
|
||||
udp_relay_mode: string;
|
||||
congestion_controller: string;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
|
||||
+11
-10
@@ -8,21 +8,21 @@ import { NextIntlClientProvider } from 'next-intl';
|
||||
import { getLocale, getMessages } from 'next-intl/server';
|
||||
import { PublicEnvScript } from 'next-runtime-env';
|
||||
import { unstable_noStore as noStore } from 'next/cache';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
// import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import { cookies } from 'next/headers';
|
||||
import { Metadata, Viewport } from 'next/types';
|
||||
import NextTopLoader from 'nextjs-toploader';
|
||||
import React from 'react';
|
||||
|
||||
const fontSans = Geist({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-sans',
|
||||
});
|
||||
// const fontSans = Geist({
|
||||
// subsets: ['latin'],
|
||||
// variable: '--font-sans',
|
||||
// });
|
||||
|
||||
const fontMono = Geist_Mono({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-mono',
|
||||
});
|
||||
// const fontMono = Geist_Mono({
|
||||
// subsets: ['latin'],
|
||||
// variable: '--font-mono',
|
||||
// });
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
noStore();
|
||||
@@ -106,7 +106,8 @@ export default async function RootLayout({
|
||||
</head>
|
||||
<body
|
||||
suppressHydrationWarning
|
||||
className={`${fontSans.variable} ${fontMono.variable} size-full min-h-[calc(100dvh-env(safe-area-inset-top))] font-sans antialiased`}
|
||||
// ${fontSans.variable} ${fontMono.variable}
|
||||
className={`size-full min-h-[calc(100dvh-env(safe-area-inset-top))] font-sans antialiased`}
|
||||
>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<NextTopLoader showSpinner={false} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-ignore
|
||||
/* eslint-disable */
|
||||
|
||||
import request from '@/utils/request';
|
||||
|
||||
/** Apple Login Callback POST /v1/auth/oauth/callback/apple */
|
||||
|
||||
+49
-1
@@ -28,6 +28,11 @@ declare namespace API {
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type AnyTLS = {
|
||||
port: number;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
type AppleLoginCallbackRequest = {
|
||||
code: string;
|
||||
id_token: string;
|
||||
@@ -81,6 +86,40 @@ declare namespace API {
|
||||
is_default: boolean;
|
||||
};
|
||||
|
||||
type AppUserSubcbribe = {
|
||||
id: number;
|
||||
name: string;
|
||||
upload: number;
|
||||
traffic: number;
|
||||
download: number;
|
||||
device_limit: number;
|
||||
start_time: string;
|
||||
expire_time: string;
|
||||
list: AppUserSubscbribeNode[];
|
||||
};
|
||||
|
||||
type AppUserSubscbribeNode = {
|
||||
id: number;
|
||||
name: string;
|
||||
uuid: string;
|
||||
protocol: string;
|
||||
relay_mode: string;
|
||||
relay_node: string;
|
||||
server_addr: string;
|
||||
speed_limit: number;
|
||||
tags: string[];
|
||||
traffic: number;
|
||||
traffic_ratio: number;
|
||||
upload: number;
|
||||
config: string;
|
||||
country: string;
|
||||
city: string;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
created_at: number;
|
||||
download: number;
|
||||
};
|
||||
|
||||
type AuthConfig = {
|
||||
mobile: MobileAuthenticateConfig;
|
||||
email: EmailAuthticateConfig;
|
||||
@@ -473,7 +512,7 @@ declare namespace API {
|
||||
type PurchaseOrderRequest = {
|
||||
subscribe_id: number;
|
||||
quantity: number;
|
||||
payment: number;
|
||||
payment?: number;
|
||||
coupon?: string;
|
||||
};
|
||||
|
||||
@@ -526,6 +565,11 @@ declare namespace API {
|
||||
total: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateCountResponse = {
|
||||
registers: number;
|
||||
total_commission: number;
|
||||
};
|
||||
|
||||
type QueryUserAffiliateListRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
@@ -830,6 +874,10 @@ declare namespace API {
|
||||
|
||||
type Tuic = {
|
||||
port: number;
|
||||
disable_sni: boolean;
|
||||
reduce_rtt: boolean;
|
||||
udp_relay_mode: string;
|
||||
congestion_controller: string;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
|
||||
Vendored
+46
-2
@@ -28,6 +28,11 @@ declare namespace API {
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
type AnyTLS = {
|
||||
port: number;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
type Application = {
|
||||
id: number;
|
||||
icon: string;
|
||||
@@ -75,6 +80,40 @@ declare namespace API {
|
||||
is_default: boolean;
|
||||
};
|
||||
|
||||
type AppUserSubcbribe = {
|
||||
id: number;
|
||||
name: string;
|
||||
upload: number;
|
||||
traffic: number;
|
||||
download: number;
|
||||
device_limit: number;
|
||||
start_time: string;
|
||||
expire_time: string;
|
||||
list: AppUserSubscbribeNode[];
|
||||
};
|
||||
|
||||
type AppUserSubscbribeNode = {
|
||||
id: number;
|
||||
name: string;
|
||||
uuid: string;
|
||||
protocol: string;
|
||||
relay_mode: string;
|
||||
relay_node: string;
|
||||
server_addr: string;
|
||||
speed_limit: number;
|
||||
tags: string[];
|
||||
traffic: number;
|
||||
traffic_ratio: number;
|
||||
upload: number;
|
||||
config: string;
|
||||
country: string;
|
||||
city: string;
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
created_at: number;
|
||||
download: number;
|
||||
};
|
||||
|
||||
type AuthConfig = {
|
||||
mobile: MobileAuthenticateConfig;
|
||||
email: EmailAuthticateConfig;
|
||||
@@ -448,6 +487,7 @@ declare namespace API {
|
||||
subscribe_id: number;
|
||||
quantity: number;
|
||||
coupon?: string;
|
||||
invite_code?: string;
|
||||
turnstile_token?: string;
|
||||
};
|
||||
|
||||
@@ -466,7 +506,7 @@ declare namespace API {
|
||||
};
|
||||
|
||||
type PrePurchaseOrderRequest = {
|
||||
payment: number;
|
||||
payment?: number;
|
||||
subscribe_id: number;
|
||||
quantity: number;
|
||||
coupon?: string;
|
||||
@@ -511,7 +551,7 @@ declare namespace API {
|
||||
type PurchaseOrderRequest = {
|
||||
subscribe_id: number;
|
||||
quantity: number;
|
||||
payment: number;
|
||||
payment?: number;
|
||||
coupon?: string;
|
||||
};
|
||||
|
||||
@@ -899,6 +939,10 @@ declare namespace API {
|
||||
|
||||
type Tuic = {
|
||||
port: number;
|
||||
disable_sni: boolean;
|
||||
reduce_rtt: boolean;
|
||||
udp_relay_mode: string;
|
||||
congestion_controller: string;
|
||||
security_config: SecurityConfig;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user