Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2bfee1f31 | |||
| 32fd181b52 | |||
| 5816dd5198 | |||
| 92665293ec | |||
| ec1e402419 | |||
| 4828700776 |
@@ -1,6 +1,22 @@
|
||||
<a name="readme-top"></a>
|
||||
# Changelog
|
||||
|
||||
## [1.5.2](https://github.com/perfect-panel/ppanel-web/compare/v1.5.1...v1.5.2) (2025-09-29)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* Add step attribute to datetime-local inputs for precise time selection in forms ([32fd181](https://github.com/perfect-panel/ppanel-web/commit/32fd181))
|
||||
* Rename 'hysteria2' to 'hysteria' across protocol definitions and schemas for consistency ([5816dd5](https://github.com/perfect-panel/ppanel-web/commit/5816dd5))
|
||||
* Update protocol options in ServerConfig for accuracy and consistency ([9266529](https://github.com/perfect-panel/ppanel-web/commit/9266529))
|
||||
|
||||
## [1.5.1](https://github.com/perfect-panel/ppanel-web/compare/v1.5.0...v1.5.1) (2025-09-28)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* Simplify protocol enable checks by removing unnecessary false comparisons ([4828700](https://github.com/perfect-panel/ppanel-web/commit/4828700))
|
||||
|
||||
# [1.5.0](https://github.com/perfect-panel/ppanel-web/compare/v1.4.8...v1.5.0) (2025-09-28)
|
||||
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ export default function AdsForm<T extends Record<string, any>>({
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
placeholder={t('form.enterStartTime')}
|
||||
value={field.value ? new Date(field.value).toISOString().slice(0, 16) : ''}
|
||||
min={Number(new Date().toISOString().slice(0, 16))}
|
||||
@@ -253,6 +254,7 @@ export default function AdsForm<T extends Record<string, any>>({
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
placeholder={t('form.enterEndTime')}
|
||||
value={
|
||||
field.value ? new Date(field.value).toISOString().slice(0, 16) : ''
|
||||
|
||||
@@ -366,6 +366,7 @@ export default function EmailBroadcastForm() {
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
disabled={form.watch('scope') === 5} // ScopeSkip
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
@@ -384,6 +385,7 @@ export default function EmailBroadcastForm() {
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
disabled={form.watch('scope') === 5} // ScopeSkip
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
@@ -425,6 +427,7 @@ export default function EmailBroadcastForm() {
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
placeholder={t('leaveEmptyForImmediateSend')}
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
|
||||
@@ -269,6 +269,7 @@ export default function QuotaBroadcastForm() {
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
/>
|
||||
@@ -286,6 +287,7 @@ export default function QuotaBroadcastForm() {
|
||||
<FormControl>
|
||||
<EnhancedInput
|
||||
type='datetime-local'
|
||||
step='1'
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
/>
|
||||
|
||||
@@ -36,9 +36,13 @@ export type ProtocolName =
|
||||
| 'vmess'
|
||||
| 'vless'
|
||||
| 'trojan'
|
||||
| 'hysteria2'
|
||||
| 'hysteria'
|
||||
| 'tuic'
|
||||
| 'anytls';
|
||||
| 'anytls'
|
||||
| 'naive'
|
||||
| 'http'
|
||||
| 'socks'
|
||||
| 'mieru';
|
||||
|
||||
const buildSchema = (t: ReturnType<typeof useTranslations>) =>
|
||||
z.object({
|
||||
|
||||
@@ -79,8 +79,18 @@ export default function DynamicMultiplier() {
|
||||
<div className='space-y-4 pt-4'>
|
||||
<ArrayInput<API.TimePeriod>
|
||||
fields={[
|
||||
{ name: 'start_time', prefix: t('server_config.fields.start_time'), type: 'time' },
|
||||
{ name: 'end_time', prefix: t('server_config.fields.end_time'), type: 'time' },
|
||||
{
|
||||
name: 'start_time',
|
||||
prefix: t('server_config.fields.start_time'),
|
||||
type: 'time',
|
||||
step: '1',
|
||||
},
|
||||
{
|
||||
name: 'end_time',
|
||||
prefix: t('server_config.fields.end_time'),
|
||||
type: 'time',
|
||||
step: '1',
|
||||
},
|
||||
{
|
||||
name: 'multiplier',
|
||||
prefix: t('server_config.fields.multiplier'),
|
||||
|
||||
@@ -3,7 +3,7 @@ export const protocols = [
|
||||
'vmess',
|
||||
'vless',
|
||||
'trojan',
|
||||
'hysteria2',
|
||||
'hysteria',
|
||||
'tuic',
|
||||
'anytls',
|
||||
'socks',
|
||||
@@ -64,7 +64,7 @@ export const SECURITY = {
|
||||
vmess: ['none', 'tls'] as const,
|
||||
vless: ['none', 'tls', 'reality'] as const,
|
||||
trojan: ['tls'] as const,
|
||||
hysteria2: ['tls'] as const,
|
||||
hysteria: ['tls'] as const,
|
||||
tuic: ['tls'] as const,
|
||||
anytls: ['tls'] as const,
|
||||
naive: ['none', 'tls'] as const,
|
||||
|
||||
@@ -90,9 +90,9 @@ export function getProtocolDefaultConfig(proto: ProtocolType) {
|
||||
cert_dns_env: null,
|
||||
ratio: 1,
|
||||
} as any;
|
||||
case 'hysteria2':
|
||||
case 'hysteria':
|
||||
return {
|
||||
type: 'hysteria2',
|
||||
type: 'hysteria',
|
||||
enable: false,
|
||||
port: null,
|
||||
hop_ports: null,
|
||||
|
||||
@@ -603,7 +603,7 @@ export const PROTOCOL_FIELDS: Record<string, FieldConfig[]> = {
|
||||
condition: (p) => p.security === 'tls' && p.cert_mode === 'dns',
|
||||
},
|
||||
],
|
||||
hysteria2: [
|
||||
hysteria: [
|
||||
{
|
||||
name: 'ratio',
|
||||
type: 'number',
|
||||
|
||||
@@ -104,16 +104,16 @@ const trojan = z.object({
|
||||
cert_dns_env: nullableString,
|
||||
});
|
||||
|
||||
const hysteria2 = z.object({
|
||||
const hysteria = z.object({
|
||||
ratio: nullableRatio,
|
||||
type: z.literal('hysteria2'),
|
||||
type: z.literal('hysteria'),
|
||||
enable: nullableBool,
|
||||
hop_ports: nullableString,
|
||||
hop_interval: z.number().nullish(),
|
||||
obfs_password: nullableString,
|
||||
obfs: z.enum(['none', 'salamander'] as const).nullish(),
|
||||
port: nullablePort,
|
||||
security: z.enum(SECURITY.hysteria2).nullish(),
|
||||
security: z.enum(SECURITY.hysteria).nullish(),
|
||||
sni: nullableString,
|
||||
allow_insecure: nullableBool,
|
||||
fingerprint: nullableString,
|
||||
@@ -207,7 +207,7 @@ export const protocolApiScheme = z.discriminatedUnion('type', [
|
||||
vmess,
|
||||
vless,
|
||||
trojan,
|
||||
hysteria2,
|
||||
hysteria,
|
||||
tuic,
|
||||
anytls,
|
||||
socks,
|
||||
|
||||
@@ -155,9 +155,7 @@ export default function ServersPage() {
|
||||
accessorKey: 'protocols',
|
||||
header: t('protocols'),
|
||||
cell: ({ row }) => {
|
||||
const list = row.original.protocols.filter(
|
||||
(p) => p.enable !== false,
|
||||
) as API.Protocol[];
|
||||
const list = row.original.protocols.filter((p) => p.enable) as API.Protocol[];
|
||||
if (!list.length) return '—';
|
||||
return (
|
||||
<div className='flex flex-col gap-1'>
|
||||
|
||||
@@ -53,23 +53,6 @@ const dnsConfigSchema = z.object({
|
||||
const outboundConfigSchema = z.object({
|
||||
name: z.string(),
|
||||
protocol: z.string(),
|
||||
// z.enum([
|
||||
// 'http',
|
||||
// 'https',
|
||||
// 'socks5',
|
||||
// 'shadowsocks',
|
||||
// 'vmess',
|
||||
// 'vless',
|
||||
// 'trojan',
|
||||
// 'hysteria2',
|
||||
// 'tuic',
|
||||
// 'naive',
|
||||
// 'brook',
|
||||
// 'snell',
|
||||
// 'wireguard',
|
||||
// 'direct',
|
||||
// 'reject',
|
||||
// ]),
|
||||
address: z.string(),
|
||||
port: z.number(),
|
||||
password: z.string().optional(),
|
||||
@@ -401,18 +384,18 @@ export default function ServerConfig() {
|
||||
),
|
||||
options: [
|
||||
{ label: 'HTTP', value: 'http' },
|
||||
{ label: 'HTTPS', value: 'https' },
|
||||
{ label: 'SOCKS5', value: 'socks5' },
|
||||
{ label: 'SOCKS', value: 'socks' },
|
||||
{ label: 'Shadowsocks', value: 'shadowsocks' },
|
||||
{ label: 'Brook', value: 'brook' },
|
||||
{ label: 'Snell', value: 'snell' },
|
||||
{ label: 'VMess', value: 'vmess' },
|
||||
{ label: 'VLESS', value: 'vless' },
|
||||
{ label: 'Trojan', value: 'trojan' },
|
||||
{ label: 'Hysteria2', value: 'hysteria2' },
|
||||
{ label: 'TUIC', value: 'tuic' },
|
||||
{ label: 'NaiveProxy', value: 'naive' },
|
||||
{ label: 'Brook', value: 'brook' },
|
||||
{ label: 'Snell', value: 'snell' },
|
||||
{ label: 'WireGuard', value: 'wireguard' },
|
||||
{ label: 'Hysteria', value: 'hysteria' },
|
||||
{ label: 'TUIC', value: 'tuic' },
|
||||
{ label: 'AnyTLS', value: 'anytls' },
|
||||
{ label: 'Naive', value: 'naive' },
|
||||
{ label: 'Direct', value: 'direct' },
|
||||
{ label: 'Reject', value: 'reject' },
|
||||
],
|
||||
|
||||
@@ -492,7 +492,7 @@ export default function ServerForm(props: {
|
||||
PROTOCOLS.findIndex((t) => t === type),
|
||||
);
|
||||
const current = (protocolsValues[i] || {}) as Record<string, any>;
|
||||
const isEnabled = current?.enable !== false;
|
||||
const isEnabled = current?.enable;
|
||||
const fields = PROTOCOL_FIELDS[type] || [];
|
||||
return (
|
||||
<AccordionItem key={type} value={type} className='mb-2 rounded-lg border'>
|
||||
@@ -529,7 +529,8 @@ export default function ServerForm(props: {
|
||||
checked={!!isEnabled}
|
||||
disabled={Boolean(
|
||||
initialValues?.id &&
|
||||
isProtocolUsedInNodes(initialValues?.id || 0, type),
|
||||
isProtocolUsedInNodes(initialValues?.id || 0, type) &&
|
||||
isEnabled,
|
||||
)}
|
||||
onCheckedChange={(checked) => {
|
||||
form.setValue(`protocols.${i}.enable`, checked);
|
||||
|
||||
+1
-1
@@ -1272,7 +1272,7 @@ declare namespace API {
|
||||
has_migrate: boolean;
|
||||
};
|
||||
|
||||
type Hysteria2 = {
|
||||
type Hysteria = {
|
||||
port: number;
|
||||
hop_ports: string;
|
||||
hop_interval: number;
|
||||
|
||||
+1
-1
@@ -324,7 +324,7 @@ declare namespace API {
|
||||
state: string;
|
||||
};
|
||||
|
||||
type Hysteria2 = {
|
||||
type Hysteria = {
|
||||
port: number;
|
||||
hop_ports: string;
|
||||
hop_interval: number;
|
||||
|
||||
@@ -65,7 +65,7 @@ export const useServerStore = create<ServerState>((set, get) => ({
|
||||
|
||||
getServerEnabledProtocols: (serverId: number) => {
|
||||
const server = get().servers.find((s) => s.id === serverId);
|
||||
return server?.protocols?.filter((p) => p.enable !== false) || [];
|
||||
return server?.protocols?.filter((p) => p.enable) || [];
|
||||
},
|
||||
|
||||
getProtocolPort: (serverId?: number, protocol?: string) => {
|
||||
|
||||
+1
-1
@@ -324,7 +324,7 @@ declare namespace API {
|
||||
state: string;
|
||||
};
|
||||
|
||||
type Hysteria2 = {
|
||||
type Hysteria = {
|
||||
port: number;
|
||||
hop_ports: string;
|
||||
hop_interval: number;
|
||||
|
||||
Vendored
+1
-1
@@ -343,7 +343,7 @@ declare namespace API {
|
||||
list: Ticket[];
|
||||
};
|
||||
|
||||
type Hysteria2 = {
|
||||
type Hysteria = {
|
||||
port: number;
|
||||
hop_ports: string;
|
||||
hop_interval: number;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ppanel-web",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.2",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/perfect-panel/ppanel-web",
|
||||
"bugs": {
|
||||
|
||||
Reference in New Issue
Block a user