init: init proj

This commit is contained in:
2026-08-25 11:35:05 +08:00
commit bf7f2df67e
80 changed files with 9936 additions and 0 deletions

View File

@@ -0,0 +1,184 @@
<script setup lang="ts">
import { computed, reactive, watch } from 'vue';
import {
NModal,
NForm,
NFormItem,
NInput,
NInputNumber,
NSelect,
NSwitch,
NButton,
NSpace,
useMessage,
} from 'naive-ui';
import { nodeInputSchema, PROTOCOLS, type NodeDto, type NodeInput } from '@proxy-station/shared';
const props = defineProps<{ show: boolean; node?: NodeDto | null }>();
const emit = defineEmits<{
(e: 'update:show', v: boolean): void;
(e: 'submit', input: NodeInput, id?: string): void;
(e: 'delete', id: string): void;
}>();
const message = useMessage();
const blank = (): NodeInput => ({
name: '',
protocol: 'vmess',
server: '',
port: 443,
network: 'ws',
wsPath: '/',
wsHost: null,
tls: true,
sni: null,
skipCertVerify: false,
uuid: null,
alterId: 0,
security: 'auto',
password: null,
method: 'chacha20-poly1305',
obfsType: null,
obfsPassword: null,
region: null,
tags: [],
enabled: true,
sortOrder: 0,
});
const form = reactive<NodeInput>(blank());
watch(
() => props.show,
(show) => {
if (!show) return;
Object.assign(form, blank(), props.node ?? {});
}
);
const isEdit = computed(() => !!props.node);
const protocolOptions = PROTOCOLS.map((p) => ({ label: p, value: p }));
const tagOptions = [
{ label: 'direct直连出口', value: 'direct' },
{ label: 'warpWARP 出口)', value: 'warp' },
];
const showWs = computed(() => form.protocol !== 'hysteria2');
const showVmess = computed(() => form.protocol === 'vmess');
const showPassword = computed(() => form.protocol !== 'vmess');
const showSs = computed(() => form.protocol === 'shadowsocks');
const showHy2 = computed(() => form.protocol === 'hysteria2');
function submit() {
const result = nodeInputSchema.safeParse({ ...form });
if (!result.success) {
message.error(result.error.issues[0]?.message || '表单校验失败');
return;
}
emit('submit', { ...form }, props.node?.id);
}
</script>
<template>
<NModal
:show="show"
preset="card"
:title="isEdit ? `编辑节点 · ${node?.name}` : '添加节点'"
style="max-width: 560px"
@update:show="emit('update:show', $event)"
>
<NForm label-placement="left" label-width="92">
<NFormItem label="名称" required>
<NInput v-model:value="form.name" placeholder="如 SG-vmess-warp" />
</NFormItem>
<NFormItem label="协议" required>
<NSelect v-model:value="form.protocol" :options="protocolOptions" :disabled="isEdit" />
</NFormItem>
<NFormItem label="服务器" required>
<NInput v-model:value="form.server" placeholder="域名或 IP" class="mono" />
</NFormItem>
<NFormItem label="端口" required>
<NInputNumber v-model:value="form.port" :min="1" :max="65535" style="width: 100%" />
</NFormItem>
<template v-if="showVmess">
<NFormItem label="UUID" required>
<NInput v-model:value="form.uuid" class="mono" />
</NFormItem>
</template>
<template v-if="showPassword">
<NFormItem label="密码" required>
<NInput v-model:value="form.password" type="password" show-password-on="click" class="mono" />
</NFormItem>
</template>
<template v-if="showSs">
<NFormItem label="加密方式" required>
<NInput v-model:value="form.method" placeholder="chacha20-poly1305" class="mono" />
</NFormItem>
</template>
<template v-if="showHy2">
<NFormItem label="混淆">
<NSelect
v-model:value="form.obfsType"
:options="[
{ label: '无', value: null as any },
{ label: 'salamander', value: 'salamander' },
]"
/>
</NFormItem>
<NFormItem v-if="form.obfsType === 'salamander'" label="混淆密码" required>
<NInput v-model:value="form.obfsPassword" type="password" show-password-on="click" class="mono" />
</NFormItem>
</template>
<template v-if="showWs">
<NFormItem label="传输">
<NSelect
v-model:value="form.network"
:options="[
{ label: 'TCP', value: 'tcp' },
{ label: 'WebSocket', value: 'ws' },
]"
/>
</NFormItem>
<template v-if="form.network === 'ws'">
<NFormItem label="WS 路径" required>
<NInput v-model:value="form.wsPath" placeholder="/xxxxxx" class="mono" />
</NFormItem>
<NFormItem label="WS Host">
<NInput v-model:value="form.wsHost" placeholder="默认与服务器相同" class="mono" />
</NFormItem>
</template>
</template>
<NFormItem label="TLS">
<NSwitch v-model:value="form.tls" :disabled="form.protocol === 'hysteria2'" />
</NFormItem>
<NFormItem v-if="form.tls" label="SNI">
<NInput v-model:value="form.sni" placeholder="默认与服务器相同" class="mono" />
</NFormItem>
<NFormItem label="出口标签">
<NSelect v-model:value="form.tags" multiple :options="tagOptions" placeholder="direct / warp用于线路矩阵归位" />
</NFormItem>
<NFormItem label="站点代号">
<NInput v-model:value="form.region" placeholder="如 SGLARN留空则按域名推断" />
</NFormItem>
<NFormItem label="启用">
<NSwitch v-model:value="form.enabled" />
</NFormItem>
</NForm>
<template #footer>
<NSpace justify="space-between">
<NButton v-if="isEdit" type="error" quaternary @click="emit('delete', node!.id)">删除节点</NButton>
<span v-else />
<NSpace>
<NButton @click="emit('update:show', false)">取消</NButton>
<NButton type="primary" @click="submit">{{ isEdit ? '保存' : '添加' }}</NButton>
</NSpace>
</NSpace>
</template>
</NModal>
</template>