♻️ refactor: Remove application management forms and related configurations

This commit is contained in:
web 2025-08-18 02:55:37 -07:00
parent a7936de6dc
commit 0c43844a6f
20 changed files with 26 additions and 1090 deletions

View File

@ -1,39 +1,37 @@
<a name="readme-top"></a>
# Changelog
# [1.3.0](https://github.com/perfect-panel/ppanel-web/compare/v1.2.0...v1.3.0) (2025-08-15)
### ♻ Code Refactoring
* Refactoring and adding multiple features ([65c9b9f](https://github.com/perfect-panel/ppanel-web/commit/65c9b9f))
- Refactoring and adding multiple features ([65c9b9f](https://github.com/perfect-panel/ppanel-web/commit/65c9b9f))
### ✨ Features
* **api**: Add getClient API endpoint to retrieve subscription applications ([7a279e6](https://github.com/perfect-panel/ppanel-web/commit/7a279e6))
* **marketing**: Add marketing management features and localization updates ([ea08de0](https://github.com/perfect-panel/ppanel-web/commit/ea08de0))
* **protocol**: Add template preview functionality with localization support ([0448d21](https://github.com/perfect-panel/ppanel-web/commit/0448d21))
* **subscribe**: Update subscription management localization and add new fields ([1d9b0a4](https://github.com/perfect-panel/ppanel-web/commit/1d9b0a4))
- **api**: Add getClient API endpoint to retrieve subscription applications ([7a279e6](https://github.com/perfect-panel/ppanel-web/commit/7a279e6))
- **marketing**: Add marketing management features and localization updates ([ea08de0](https://github.com/perfect-panel/ppanel-web/commit/ea08de0))
- **protocol**: Add template preview functionality with localization support ([0448d21](https://github.com/perfect-panel/ppanel-web/commit/0448d21))
- **subscribe**: Update subscription management localization and add new fields ([1d9b0a4](https://github.com/perfect-panel/ppanel-web/commit/1d9b0a4))
### 🐛 Bug Fixes
* **bun**: Update bun.lockb to reflect dependency changes ([bbcd018](https://github.com/perfect-panel/ppanel-web/commit/bbcd018))
* **editor**: Add Go template editor component and update related schemas ([9d9c3cd](https://github.com/perfect-panel/ppanel-web/commit/9d9c3cd))
* **editor**: Enhance Go Template Editor to support trimmed template tags and improve range/end matching ([641ed5e](https://github.com/perfect-panel/ppanel-web/commit/641ed5e))
* **editor**: Enhance Go Template Editor with schema support and improved completion ([5b21d8a](https://github.com/perfect-panel/ppanel-web/commit/5b21d8a))
* **enhaced-input**: Disable autocomplete for EnhancedInput component ([f190c68](https://github.com/perfect-panel/ppanel-web/commit/f190c68))
* **global**: Add user agent limit settings to subscription configuration ([822416d](https://github.com/perfect-panel/ppanel-web/commit/822416d))
* **locales**: Update 'conf' output format to use uppercase 'CONF' ([fce9119](https://github.com/perfect-panel/ppanel-web/commit/fce9119))
* **locales**: Update "userAccount" label to "user" in multiple localization files ([48415e9](https://github.com/perfect-panel/ppanel-web/commit/48415e9))
* **protocol-form**: Swap 'description' and 'user_agent' columns for improved clarity in the table ([72a4106](https://github.com/perfect-panel/ppanel-web/commit/72a4106))
* **protocol-form**: Update protocol options descriptions for clarity and add new security and transport options ([e5d4deb](https://github.com/perfect-panel/ppanel-web/commit/e5d4deb))
* **protocol**: Add 'conf' output format option and update translations ([292efdf](https://github.com/perfect-panel/ppanel-web/commit/292efdf))
* **protpcp-form**: Rename 'schema' to 'scheme' for consistency across the application ([6ab2ba9](https://github.com/perfect-panel/ppanel-web/commit/6ab2ba9))
* **register**: Update localization files to include trial subscription settings and descriptions ([33daa1f](https://github.com/perfect-panel/ppanel-web/commit/33daa1f))
* **system**: Add time unit translations for user registration settings in multiple languages ([296a6c1](https://github.com/perfect-panel/ppanel-web/commit/296a6c1))
* Update privacy policy and terms of service schemas to use correct field names ([0e6ba5b](https://github.com/perfect-panel/ppanel-web/commit/0e6ba5b))
- **bun**: Update bun.lockb to reflect dependency changes ([bbcd018](https://github.com/perfect-panel/ppanel-web/commit/bbcd018))
- **editor**: Add Go template editor component and update related schemas ([9d9c3cd](https://github.com/perfect-panel/ppanel-web/commit/9d9c3cd))
- **editor**: Enhance Go Template Editor to support trimmed template tags and improve range/end matching ([641ed5e](https://github.com/perfect-panel/ppanel-web/commit/641ed5e))
- **editor**: Enhance Go Template Editor with schema support and improved completion ([5b21d8a](https://github.com/perfect-panel/ppanel-web/commit/5b21d8a))
- **enhaced-input**: Disable autocomplete for EnhancedInput component ([f190c68](https://github.com/perfect-panel/ppanel-web/commit/f190c68))
- **global**: Add user agent limit settings to subscription configuration ([822416d](https://github.com/perfect-panel/ppanel-web/commit/822416d))
- **locales**: Update 'conf' output format to use uppercase 'CONF' ([fce9119](https://github.com/perfect-panel/ppanel-web/commit/fce9119))
- **locales**: Update "userAccount" label to "user" in multiple localization files ([48415e9](https://github.com/perfect-panel/ppanel-web/commit/48415e9))
- **protocol-form**: Swap 'description' and 'user_agent' columns for improved clarity in the table ([72a4106](https://github.com/perfect-panel/ppanel-web/commit/72a4106))
- **protocol-form**: Update protocol options descriptions for clarity and add new security and transport options ([e5d4deb](https://github.com/perfect-panel/ppanel-web/commit/e5d4deb))
- **protocol**: Add 'conf' output format option and update translations ([292efdf](https://github.com/perfect-panel/ppanel-web/commit/292efdf))
- **protpcp-form**: Rename 'schema' to 'scheme' for consistency across the application ([6ab2ba9](https://github.com/perfect-panel/ppanel-web/commit/6ab2ba9))
- **register**: Update localization files to include trial subscription settings and descriptions ([33daa1f](https://github.com/perfect-panel/ppanel-web/commit/33daa1f))
- **system**: Add time unit translations for user registration settings in multiple languages ([296a6c1](https://github.com/perfect-panel/ppanel-web/commit/296a6c1))
- Update privacy policy and terms of service schemas to use correct field names ([0e6ba5b](https://github.com/perfect-panel/ppanel-web/commit/0e6ba5b))
<a name="readme-top"></a>

View File

@ -1,282 +0,0 @@
'use client';
import {
getApplication,
getApplicationConfig,
updateApplicationConfig,
} from '@/services/admin/system';
import { zodResolver } from '@hookform/resolvers/zod';
import { Icon } from '@iconify/react';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@workspace/ui/components/form';
import { ScrollArea } from '@workspace/ui/components/scroll-area';
import {
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@workspace/ui/components/sheet';
import { Textarea } from '@workspace/ui/components/textarea';
import { Combobox } from '@workspace/ui/custom-components/combobox';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
import { DicesIcon } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { uid } from 'radash';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast } from 'sonner';
import { z } from 'zod';
const formSchema = z.object({
app_id: z.number().optional(),
encryption_key: z.string().optional(),
encryption_method: z.string().optional(),
startup_picture: z.string().optional(),
startup_picture_skip_time: z.number().optional(),
domains: z.array(z.string()).optional(),
});
type FormSchema = z.infer<typeof formSchema>;
export default function ConfigForm() {
const t = useTranslations('product.app');
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const form = useForm<FormSchema>({
resolver: zodResolver(formSchema),
defaultValues: {
app_id: 0,
encryption_key: '',
encryption_method: '',
startup_picture: '',
startup_picture_skip_time: 0,
domains: [],
},
});
const { data, refetch } = useQuery({
queryKey: ['getApplicationConfig'],
queryFn: async () => {
const { data } = await getApplicationConfig();
return data.data;
},
});
const { data: applications } = useQuery({
queryKey: ['getApplication'],
queryFn: async () => {
const { data } = await getApplication();
return data.data?.applications || [];
},
});
useEffect(() => {
if (data) {
form.reset(data);
}
}, [data, form]);
async function onSubmit(values: FormSchema) {
setLoading(true);
try {
await updateApplicationConfig({
...values,
domains: values.domains?.filter((domain) => domain),
} as API.ApplicationConfig);
toast.success(t('updateSuccess'));
refetch();
setOpen(false);
} catch (error) {
/* empty */
} finally {
setLoading(false);
}
}
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant='outline'>
<Icon icon='mdi:cog' className='mr-2' />
{t('config')}
</Button>
</SheetTrigger>
<SheetContent className='w-[520px] max-w-full md:max-w-screen-md'>
<SheetHeader>
<SheetTitle>{t('configApp')}</SheetTitle>
</SheetHeader>
<ScrollArea className='h-[calc(100dvh-48px-36px-36px)]'>
<Form {...form}>
<form className='space-y-4 py-4'>
<FormField
control={form.control}
name='app_id'
render={({ field }) => (
<FormItem>
<FormLabel>{t('selectApp')}</FormLabel>
<FormDescription>{t('selectAppDescription')}</FormDescription>
<FormControl>
<Combobox
{...field}
options={
applications?.map((app) => ({
label: app.name,
value: app.id,
})) || []
}
value={field.value}
onChange={(value) => form.setValue(field.name, value)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='encryption_key'
render={({ field }) => (
<FormItem>
<FormLabel>{t('communicationKey')}</FormLabel>
<FormDescription>{t('communicationKeyDescription')}</FormDescription>
<FormControl>
<EnhancedInput
value={field.value}
onValueChange={(value) => form.setValue(field.name, value as string)}
suffix={
<div className='bg-muted flex h-9 items-center text-nowrap px-3'>
<DicesIcon
onClick={() => {
const id = uid(32).toLowerCase();
const formatted = `${id.slice(0, 8)}-${id.slice(8, 12)}-${id.slice(12, 16)}-${id.slice(16, 20)}-${id.slice(20)}`;
form.setValue(field.name, formatted);
}}
className='cursor-pointer'
/>
</div>
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='encryption_method'
render={({ field }) => (
<FormItem>
<FormLabel>{t('encryption')}</FormLabel>
<FormDescription>{t('encryptionDescription')}</FormDescription>
<FormControl>
<Combobox
options={[
{ label: 'none', value: 'none' },
{ label: 'AES', value: 'aes' },
]}
value={field.value}
onChange={(value) => form.setValue(field.name, value)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='startup_picture'
render={({ field }) => (
<FormItem>
<FormLabel>{t('startupPicture')}</FormLabel>
<FormDescription>{t('startupPictureDescription')}</FormDescription>
<FormControl>
<EnhancedInput
value={field.value}
onValueChange={(value) => form.setValue(field.name, value as string)}
suffix={
<UploadImage
className='bg-muted h-9 rounded-none border-none px-2'
onChange={(value) => form.setValue('startup_picture', value as string)}
/>
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='startup_picture_skip_time'
render={({ field }) => (
<FormItem>
<FormLabel>{t('startupPictureSkip')}</FormLabel>
<FormDescription>{t('startupPictureSkipDescription')}</FormDescription>
<FormControl>
<EnhancedInput
{...field}
type='number'
min={0}
suffix='S'
value={field.value}
onValueChange={(value) =>
form.setValue('startup_picture_skip_time', Number(value))
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='domains'
render={({ field }) => (
<FormItem className='px-1'>
<FormLabel>{t('backupDomains')}</FormLabel>
<FormDescription>{t('backupDomainsDescription')}</FormDescription>
<FormControl>
<Textarea
className='h-28'
placeholder='example.com'
value={field.value?.join('\n')}
onChange={(e) => {
form.setValue(field.name, e.target.value.split('\n'));
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
</ScrollArea>
<SheetFooter className='flex-row justify-end gap-2 pt-3'>
<Button variant='outline' onClick={() => setOpen(false)}>
{t('cancel')}
</Button>
<Button onClick={form.handleSubmit(onSubmit)} disabled={loading}>
{loading && <Icon icon='mdi:loading' className='mr-2 animate-spin' />}
{t('confirm')}
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}

View File

@ -1,303 +0,0 @@
'use client';
import { getSubscribeType } from '@/services/admin/system';
import { zodResolver } from '@hookform/resolvers/zod';
import { Icon } from '@iconify/react';
import { useQuery } from '@tanstack/react-query';
import { Button } from '@workspace/ui/components/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@workspace/ui/components/form';
import { ScrollArea } from '@workspace/ui/components/scroll-area';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@workspace/ui/components/select';
import {
Sheet,
SheetContent,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@workspace/ui/components/sheet';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@workspace/ui/components/tabs';
import { ArrayInput } from '@workspace/ui/custom-components/dynamic-Inputs';
import { EnhancedInput } from '@workspace/ui/custom-components/enhanced-input';
import { UploadImage } from '@workspace/ui/custom-components/upload-image';
import { useTranslations } from 'next-intl';
import { assign, shake } from 'radash';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
const platforms = ['windows', 'macos', 'linux', 'android', 'ios', 'harmony'];
const defaultValues = {
subscribe_type: 'Clash',
name: '',
icon: '',
url: '',
};
const versionSchema = z.object({
url: z.string(),
version: z.string().optional(),
description: z.string().optional(),
is_default: z.boolean().optional(),
});
const formSchema = z.object({
icon: z.string(),
name: z.string(),
subscribe_type: z.string(),
platform: z.object({
windows: z.array(versionSchema).optional(),
macos: z.array(versionSchema).optional(),
linux: z.array(versionSchema).optional(),
android: z.array(versionSchema).optional(),
ios: z.array(versionSchema).optional(),
harmony: z.array(versionSchema).optional(),
}),
});
interface FormProps<T> {
trigger: React.ReactNode | string;
title: string;
initialValues?: Partial<T>;
onSubmit: (values: T) => Promise<boolean>;
loading?: boolean;
}
export default function SubscribeAppForm<
T extends API.CreateApplicationRequest | API.UpdateApplicationRequest,
>({ trigger, title, loading, initialValues, onSubmit }: FormProps<T>) {
const t = useTranslations('product.app');
const [open, setOpen] = useState(false);
type FormSchema = z.infer<typeof formSchema>;
const form = useForm<FormSchema>({
resolver: zodResolver(formSchema),
defaultValues: assign(
defaultValues,
shake(initialValues, (value) => value === null),
),
});
useEffect(() => {
form.reset(
assign(
defaultValues,
shake(initialValues, (value) => value === null),
),
);
}, [form, initialValues]);
const { data: subscribe_types } = useQuery<string[]>({
queryKey: ['getSubscribeType'],
queryFn: async () => {
const { data } = await getSubscribeType();
return data.data?.subscribe_types || [];
},
});
return (
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
{typeof trigger === 'string' ? <Button>{trigger}</Button> : trigger}
</SheetTrigger>
<SheetContent className='w-[520px] max-w-full md:max-w-screen-md'>
<SheetHeader>
<SheetTitle>{title}</SheetTitle>
</SheetHeader>
<ScrollArea className='h-[calc(100dvh-48px-36px-36px)]'>
<Form {...form}>
<form className='space-y-4 py-4'>
<div className='grid grid-cols-2 gap-4'>
<FormField
control={form.control}
name='icon'
render={({ field }) => (
<FormItem className='col-span-2'>
<FormLabel>{t('appIcon')}</FormLabel>
<FormControl>
<EnhancedInput
required
suffix={
<UploadImage
className='bg-muted h-9 rounded-none border-none px-2'
onChange={(value) => {
form.setValue(field.name, value as string);
}}
/>
}
value={field.value}
onValueChange={(value) => {
form.setValue(field.name, value as string);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='name'
render={({ field }) => (
<FormItem>
<FormLabel>{t('appName')}</FormLabel>
<FormControl>
<EnhancedInput
required
type='text'
value={field.value}
onValueChange={(value) => {
form.setValue(field.name, value as string);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='subscribe_type'
render={({ field }) => (
<FormItem>
<FormLabel>{t('subscriptionProtocol')}</FormLabel>
<FormControl>
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder={t('subscriptionProtocol')} />
</SelectTrigger>
<SelectContent>
{subscribe_types?.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormItem>
<FormLabel>{t('platform')}</FormLabel>
<Tabs defaultValue={platforms[0]}>
<TabsList>
{platforms.map((platform) => (
<TabsTrigger key={platform} value={platform} className='uppercase'>
{platform}
</TabsTrigger>
))}
</TabsList>
{platforms.map((platform) => (
<TabsContent key={platform} value={platform}>
<FormField
control={form.control}
name={`platform.${platform as keyof FormSchema['platform']}`}
render={({ field }) => (
<FormItem>
<FormControl>
<ArrayInput
isReverse
className='grid grid-cols-3 gap-4'
fields={[
{
name: 'version',
type: 'text',
placeholder: t('version'),
defaultValue: '1.0.0',
},
{
name: 'description',
type: 'text',
placeholder: t('description'),
},
{
name: 'is_default',
type: 'boolean',
placeholder: t('defaultVersion'),
},
{
name: 'url',
type: 'text',
placeholder: t('downloadLink'),
className: 'col-span-3',
},
]}
value={field.value}
onChange={(value) => {
const filteredValue = value.filter((item) => item.url);
const newDefaultIndex = value.findIndex(
(item, idx) =>
item.is_default &&
(!field.value?.[idx] || !field.value[idx].is_default),
);
let finalValue = filteredValue;
if (newDefaultIndex >= 0 && filteredValue[newDefaultIndex]) {
finalValue = filteredValue.map((item, index) => ({
...item,
is_default: index === newDefaultIndex,
}));
} else if (
!filteredValue.some((item) => item.is_default) &&
filteredValue.length > 0
) {
finalValue = filteredValue.map((item, index) => ({
...item,
is_default: index === 0,
}));
}
form.setValue(field.name, finalValue as any);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</TabsContent>
))}
</Tabs>
</FormItem>
</form>
</Form>
</ScrollArea>
<SheetFooter className='flex-row justify-end gap-2 pt-3'>
<Button variant='outline' onClick={() => setOpen(false)}>
{t('cancel')}
</Button>
<Button
onClick={form.handleSubmit(async (values) => {
const success = await onSubmit(values as unknown as T);
if (success) setOpen(false);
})}
disabled={loading}
>
{loading && <Icon icon='mdi:loading' className='mr-2 animate-spin' />}
{t('confirm')}
</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}

View File

@ -1,143 +0,0 @@
'use client';
import { ProTable, ProTableActions } from '@/components/pro-table';
import {
createApplication,
deleteApplication,
getApplication,
updateApplication,
} from '@/services/admin/system';
import { Button } from '@workspace/ui/components/button';
import { ConfirmButton } from '@workspace/ui/custom-components/confirm-button';
import { useTranslations } from 'next-intl';
import Image from 'next/legacy/image';
import { useRef, useState } from 'react';
import { toast } from 'sonner';
import ConfigForm from './config';
import SubscribeAppForm from './form';
export default function SubscribeApp() {
const t = useTranslations('product.app');
const [loading, setLoading] = useState(false);
const ref = useRef<ProTableActions>(null);
return (
<ProTable<API.ApplicationResponseInfo, Record<string, unknown>>
action={ref}
header={{
title: t('appList'),
toolbar: (
<div className='flex items-center gap-2'>
<ConfigForm />
<SubscribeAppForm<API.CreateApplicationRequest>
trigger={t('create')}
title={t('createApp')}
loading={loading}
onSubmit={async (values) => {
setLoading(true);
try {
await createApplication(values);
toast.success(t('createSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>
</div>
),
}}
request={async (_pagination, filters) => {
const { data } = await getApplication();
return {
list: data.data?.applications || [],
total: 0,
};
}}
columns={[
{
accessorKey: 'icon',
header: t('appIcon'),
cell: ({ row }) => (
<Image
src={row.getValue('icon')}
alt={row.getValue('name')}
className='h-8 w-8 rounded-md'
width={32}
height={32}
/>
),
},
{
accessorKey: 'name',
header: t('appName'),
},
{
accessorKey: 'subscribe_type',
header: t('subscriptionProtocol'),
cell: ({ row }) => row.getValue('subscribe_type'),
},
]}
actions={{
render: (row) => [
<SubscribeAppForm<API.UpdateApplicationRequest>
key='edit'
trigger={<Button>{t('edit')}</Button>}
title={t('editApp')}
loading={loading}
initialValues={{
...row,
}}
onSubmit={async (values) => {
setLoading(true);
try {
await updateApplication({
...values,
id: row.id,
});
toast.success(t('updateSuccess'));
ref.current?.refresh();
setLoading(false);
return true;
} catch (error) {
setLoading(false);
return false;
}
}}
/>,
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('delete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
await deleteApplication({ id: row.id! });
toast.success(t('deleteSuccess'));
ref.current?.refresh();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
],
batchRender: (rows) => [
<ConfirmButton
key='delete'
trigger={<Button variant='destructive'>{t('batchDelete')}</Button>}
title={t('confirmDelete')}
description={t('deleteWarning')}
onConfirm={async () => {
await Promise.all(rows.map((row) => deleteApplication({ id: row.id! })));
toast.success(t('deleteSuccess'));
ref.current?.reset();
}}
cancelText={t('cancel')}
confirmText={t('confirm')}
/>,
],
}}
/>
);
}

View File

@ -138,7 +138,6 @@ export function ProtocolForm() {
onCheckedChange={async (checked) => {
await updateSubscribeApplication({
...row.original,
proxy_template: '',
is_default: checked,
});
tableRef.current?.refresh();
@ -289,7 +288,6 @@ export function ProtocolForm() {
if (editingClient) {
await updateSubscribeApplication({
...data,
proxy_template: '',
is_default: editingClient.is_default,
id: editingClient.id,
});

View File

@ -53,11 +53,6 @@ export const navs = [
url: '/dashboard/product',
icon: 'flat-color-icons:shop',
},
{
title: 'Application Management',
url: '/dashboard/application',
icon: 'flat-color-icons:touchscreen-smartphone',
},
],
},
{

View File

@ -1,46 +1,5 @@
{
"actions": "Actions",
"app": {
"appDownloadURL": "App Download URL",
"appIcon": "App Icon",
"appList": "App List",
"appName": "App Name",
"backupDomains": "Backup Domain List",
"backupDomainsDescription": "Backup domain list for domain resolution, one domain per line",
"batchDelete": "Batch Delete",
"cancel": "Cancel",
"communicationKey": "Communication Key",
"communicationKeyDescription": "Key used for client communication",
"config": "Config",
"configApp": "App Configuration",
"confirm": "Confirm",
"confirmDelete": "Are you sure you want to delete?",
"create": "Create",
"createApp": "Create App",
"createSuccess": "Created successfully",
"defaultVersion": "Default",
"delete": "Delete",
"deleteWarning": "Data cannot be recovered after deletion. Please proceed with caution.",
"describeDescription": "Used to describe the application, displayed in the app list",
"description": "Description",
"downloadLink": "Download Link",
"edit": "Edit",
"editApp": "Edit App",
"encryption": "Encryption Method",
"encryptionDescription": "Choose the encryption method for client communication. If selected, the client will use this method to communicate with the server",
"nameDescription": "Application name, displayed in the app list",
"platform": "Platform",
"selectApp": "Select App",
"selectAppDescription": "Select the app to configure, all settings will apply to the selected app",
"startupPicture": "Startup Picture",
"startupPictureDescription": "Startup picture, supports network and local images. For network images, please enter the complete image URL",
"startupPicturePreview": "Startup Picture Preview",
"startupPictureSkip": "Startup Picture Skip Time",
"startupPictureSkipDescription": "Startup picture display time in seconds, enter 0 to not display",
"subscriptionProtocol": "Subscription Protocol",
"updateSuccess": "Updated successfully",
"version": "Version"
},
"cancel": "Cancel",
"confirm": "Confirm",
"confirmDelete": "Are you sure you want to delete?",

View File

@ -1,46 +1,5 @@
{
"actions": "操作",
"app": {
"appDownloadURL": "应用下载链接",
"appIcon": "应用图标",
"appList": "应用列表",
"appName": "应用名称",
"backupDomains": "备用域名列表",
"backupDomainsDescription": "用于域名解析的备用域名列表,每行一个域名",
"batchDelete": "批量删除",
"cancel": "取消",
"communicationKey": "通信密钥",
"communicationKeyDescription": "用于客户端通信的密钥",
"config": "配置",
"configApp": "应用配置",
"confirm": "确认",
"confirmDelete": "确认删除",
"create": "创建",
"createApp": "创建应用",
"createSuccess": "创建成功",
"defaultVersion": "默认",
"delete": "删除",
"deleteWarning": "此操作无法撤销",
"describeDescription": "用于描述应用程序,显示在应用列表中",
"description": "描述",
"downloadLink": "下载链接",
"edit": "编辑",
"editApp": "编辑应用",
"encryption": "加密方法",
"encryptionDescription": "选择用于客户端通信的加密方法。如果选择,客户端将使用此方法与服务器通信",
"nameDescription": "应用程序名称,显示在应用列表中",
"platform": "平台",
"selectApp": "选择应用",
"selectAppDescription": "选择要配置的应用,所有设置将应用于所选应用",
"startupPicture": "启动图片",
"startupPictureDescription": "启动图片支持网络和本地图片。对于网络图片请输入完整的图片URL",
"startupPicturePreview": "启动图片预览",
"startupPictureSkip": "启动图片跳过时间",
"startupPictureSkipDescription": "启动图片显示时间输入0表示不显示",
"subscriptionProtocol": "订阅协议",
"updateSuccess": "更新成功",
"version": "版本"
},
"cancel": "取消",
"confirm": "确认",
"confirmDelete": "确定删除吗?",

View File

@ -1,5 +1,5 @@
// @ts-ignore
// API 更新时间:
// API 唯一标识:
import * as ads from './ads';

View File

@ -2,133 +2,6 @@
/* eslint-disable */
import request from '@/utils/request';
/** Get application GET /v1/admin/system/application */
export async function getApplication(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ApplicationResponse }>(
'/v1/admin/system/application',
{
method: 'GET',
...(options || {}),
},
);
}
/** Update application PUT /v1/admin/system/application */
export async function updateApplication(
body: API.UpdateApplicationRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Create application POST /v1/admin/system/application */
export async function createApplication(
body: API.CreateApplicationRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Delete application DELETE /v1/admin/system/application */
export async function deleteApplication(
body: API.DeleteApplicationRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** get application config GET /v1/admin/system/application_config */
export async function getApplicationConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ApplicationConfig }>(
'/v1/admin/system/application_config',
{
method: 'GET',
...(options || {}),
},
);
}
/** update application config PUT /v1/admin/system/application_config */
export async function updateApplicationConfig(
body: API.ApplicationConfig,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application_config', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Update application version PUT /v1/admin/system/application_version */
export async function updateApplicationVersion(
body: API.UpdateApplicationVersionRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application_version', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Create application version POST /v1/admin/system/application_version */
export async function createApplicationVersion(
body: API.CreateApplicationVersionRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application_version', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Delete application DELETE /v1/admin/system/application_version */
export async function deleteApplicationVersion(
body: API.DeleteApplicationVersionRequest,
options?: { [key: string]: any },
) {
return request<API.Response & { data?: any }>('/v1/admin/system/application_version', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** Get Currency Config GET /v1/admin/system/currency_config */
export async function getCurrencyConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.CurrencyConfig }>('/v1/admin/system/currency_config', {

View File

@ -41,15 +41,6 @@ declare namespace API {
subscribe_type: string;
};
type ApplicationConfig = {
app_id: number;
encryption_key: string;
encryption_method: string;
domains: string[];
startup_picture: string;
startup_picture_skip_time: number;
};
type ApplicationPlatform = {
ios?: ApplicationVersion[];
macos?: ApplicationVersion[];
@ -223,23 +214,6 @@ declare namespace API {
content: string;
};
type CreateApplicationRequest = {
icon: string;
name: string;
description: string;
subscribe_type: string;
platform: ApplicationPlatform;
};
type CreateApplicationVersionRequest = {
url: string;
version: string;
description: string;
platform: 'windows' | 'mac' | 'linux' | 'android' | 'ios' | 'harmony';
is_default: boolean;
application_id: number;
};
type CreateBatchSendEmailTaskRequest = {
subject: string;
content: string;
@ -424,14 +398,6 @@ declare namespace API {
id: number;
};
type DeleteApplicationRequest = {
id: number;
};
type DeleteApplicationVersionRequest = {
id: number;
};
type DeleteCouponRequest = {
id: number;
};
@ -1682,25 +1648,6 @@ declare namespace API {
popup: boolean;
};
type UpdateApplicationRequest = {
id: number;
icon: string;
name: string;
description: string;
subscribe_type: string;
platform: ApplicationPlatform;
};
type UpdateApplicationVersionRequest = {
id: number;
url: string;
version: string;
description: string;
platform: 'windows' | 'mac' | 'linux' | 'android' | 'ios' | 'harmony';
is_default: boolean;
application_id: number;
};
type UpdateAuthMethodConfigRequest = {
id: number;
method: string;
@ -1795,7 +1742,6 @@ declare namespace API {
scheme?: string;
user_agent: string;
is_default: boolean;
proxy_template: string;
template: string;
output_format: string;
download_link?: DownloadLink;

View File

@ -17,14 +17,6 @@ export async function getAds(
});
}
/** Get Tos Content GET /v1/common/application */
export async function getApplication(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetAppcationResponse }>('/v1/common/application', {
method: 'GET',
...(options || {}),
});
}
/** Check verification code POST /v1/common/check_verification_code */
export async function checkVerificationCode(
body: API.CheckVerificationCodeRequest,

View File

@ -1,5 +1,5 @@
// @ts-ignore
// API 更新时间:
// API 唯一标识:
import * as auth from './auth';

View File

@ -47,15 +47,6 @@ declare namespace API {
subscribe_type: string;
};
type ApplicationConfig = {
app_id: number;
encryption_key: string;
encryption_method: string;
domains: string[];
startup_picture: string;
startup_picture_skip_time: number;
};
type ApplicationPlatform = {
ios?: ApplicationVersion[];
macos?: ApplicationVersion[];
@ -265,11 +256,6 @@ declare namespace API {
list: Ads[];
};
type GetAppcationResponse = {
config: ApplicationConfig;
applications: ApplicationResponseInfo[];
};
type GetAvailablePaymentMethodsResponse = {
list: PaymentMethod[];
};

View File

@ -1,5 +1,5 @@
// @ts-ignore
/* eslint-disable */
import request from '@/utils/request';
/** Get Ads GET /v1/common/ads */
@ -17,14 +17,6 @@ export async function getAds(
});
}
/** Get Tos Content GET /v1/common/application */
export async function getApplication(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.GetAppcationResponse }>('/v1/common/application', {
method: 'GET',
...(options || {}),
});
}
/** Check verification code POST /v1/common/check_verification_code */
export async function checkVerificationCode(
body: API.CheckVerificationCodeRequest,

View File

@ -1,5 +1,5 @@
// @ts-ignore
// API 更新时间:
// API 唯一标识:
import * as auth from './auth';

View File

@ -47,15 +47,6 @@ declare namespace API {
subscribe_type: string;
};
type ApplicationConfig = {
app_id: number;
encryption_key: string;
encryption_method: string;
domains: string[];
startup_picture: string;
startup_picture_skip_time: number;
};
type ApplicationPlatform = {
ios?: ApplicationVersion[];
macos?: ApplicationVersion[];
@ -265,11 +256,6 @@ declare namespace API {
list: Ads[];
};
type GetAppcationResponse = {
config: ApplicationConfig;
applications: ApplicationResponseInfo[];
};
type GetAvailablePaymentMethodsResponse = {
list: PaymentMethod[];
};

View File

@ -1,5 +1,5 @@
// @ts-ignore
// API 更新时间:
// API 唯一标识:
import * as announcement from './announcement';

View File

@ -2,17 +2,6 @@
/* eslint-disable */
import request from '@/utils/request';
/** Get application config GET /v1/public/subscribe/application/config */
export async function queryApplicationConfig(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.ApplicationResponse }>(
'/v1/public/subscribe/application/config',
{
method: 'GET',
...(options || {}),
},
);
}
/** Get subscribe group list GET /v1/public/subscribe/group/list */
export async function querySubscribeGroupList(options?: { [key: string]: any }) {
return request<API.Response & { data?: API.QuerySubscribeGroupListResponse }>(

View File

@ -41,15 +41,6 @@ declare namespace API {
subscribe_type: string;
};
type ApplicationConfig = {
app_id: number;
encryption_key: string;
encryption_method: string;
domains: string[];
startup_picture: string;
startup_picture_skip_time: number;
};
type ApplicationPlatform = {
ios?: ApplicationVersion[];
macos?: ApplicationVersion[];