Browse Source

change disk

master
godo 5 months ago
parent
commit
eab5cc8605
  1. 1
      frontend/components.d.ts
  2. 3
      frontend/src/components/computer/Computer.vue
  3. 221
      frontend/src/components/setting/LocalProxy.vue
  4. 204
      frontend/src/components/setting/NetProxy copy.vue
  5. 182
      frontend/src/components/setting/SetNas.vue
  6. 21
      frontend/src/hook/useContextMenu.ts
  7. 20
      frontend/src/stores/proxy.ts
  8. 25
      godo/cmd/main.go
  9. 1
      godo/files/disk.go
  10. 9
      godo/model/local_proxy.go
  11. 12
      godo/model/sys_disk.go
  12. 194
      godo/proxy/local.go

1
frontend/components.d.ts

@ -138,7 +138,6 @@ declare module 'vue' {
MusicViewer: typeof import('./src/components/builtin/MusicViewer.vue')['default']
NasClient: typeof import('./src/components/setting/NasClient.vue')['default']
NetProxy: typeof import('./src/components/setting/NetProxy.vue')['default']
'NetProxy copy': typeof import('./src/components/setting/NetProxy copy.vue')['default']
NetWork: typeof import('./src/components/taskbar/NetWork.vue')['default']
NetworkPop: typeof import('./src/components/taskbar/NetworkPop.vue')['default']
NotFound: typeof import('./src/components/window/NotFound.vue')['default']

3
frontend/src/components/computer/Computer.vue

@ -399,7 +399,8 @@ async function handleNavSearch(query: string) {
}
document.addEventListener('paste', function() {
if(router_url.value !== "/" && !router_url.value.startsWith("/B")){
const sp = router_url.value.charAt(0);
if(router_url.value !== sp && !router_url.value.startsWith(sp + "B")){
pasteFile(router_url.value)
refersh();
}

221
frontend/src/components/setting/LocalProxy.vue

@ -1,42 +1,68 @@
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { ref, computed } from "vue";
import { ref } from "vue";
import { OpenDirDialog } from "@/util/goutil";
import { getSystemConfig } from '@/system/config';
import { notifySuccess,notifyError } from '@/util/msg';
import { notifySuccess, notifyError } from '@/util/msg';
interface ProxyItem {
id: number;
port: string;
port: number;
proxyType: string;
domain: string;
path?: string;
status: boolean;
}
const proxyInit = {
id: Date.now(),
port: 8080,
proxyType: "http",
domain: "",
path : "",
status: true,
}
const config = getSystemConfig();
const proxies = ref<ProxyItem[]>([]);
const total = ref(0)
const fetchProxies = async () => {
try {
const response = await fetch(config.apiUrl + "/api/v1/proxy/list");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
const page = ref({
current: 1,
size: 10,
total: 0,
})
const fetchProxies = () => {
fetch(config.apiUrl + "/proxy/local/list").then(res => res.json()).then(res => {
if (res.code === 0) {
const data = res.data;
if (data.proxies && Array.isArray(data.proxies)) {
proxies.value = data.proxies;
page.value.total = data.total;
} else {
console.error('Invalid data format:', data);
}
}
const data = await response.json();
if (data.proxies && Array.isArray(data.proxies)) {
proxies.value = data.proxies;
total.value = data.total;
}).catch(err => {
console.error('Error fetching data:', err);
});
};
const createProxies = (data: ProxyItem) => {
fetch(config.apiUrl + '/proxy/local/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(res => {
if (!res.ok) {
notifyError('添加代理失败');
} else {
console.error('Invalid data format:', data);
notifySuccess('添加代理成功');
proxyData.value = proxyInit;
proxyDialogShow.value = false;
fetchProxies()
}
} catch (error) {
console.error('Failed to fetch proxies:', error);
}
});
};
onMounted(async () => {
await fetchProxies();
});
const saveProxies = (data: ProxyItem) => {
//localStorage.setItem(localKey, JSON.stringify(proxies));
fetch(config.apiUrl + '/api/proxy/add', {
const updateProxies = (data: ProxyItem) => {
fetch(config.apiUrl + '/proxy/local/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -45,19 +71,43 @@ const saveProxies = (data: ProxyItem) => {
}).then(res => {
if (!res.ok) {
notifyError('保存代理失败');
}else{
} else {
notifySuccess('保存代理成功');
proxyData.value = proxyInit;
proxyDialogShow.value = false;
fetchProxies()
}
});
};
// const proxies = ref<ProxyItem[]>(getProxies());
const proxyInit = {
id: Date.now(),
port: "",
proxyType: "http",
domain: "",
const DeleteProxy = (id: number) => {
fetch(config.apiUrl + '/proxy/local/delete?id=' + id).then(res => {
if (!res.ok) {
notifyError('删除代理失败');
} else {
notifySuccess('删除代理成功');
fetchProxies()
}
});
}
const SetStatus = (id: number) => {
fetch(config.apiUrl + '/proxy/local/status?id=' + id).then(res => {
if (!res.ok) {
notifyError('设置代理状态失败');
} else {
notifySuccess('设置代理状态成功');
fetchProxies()
}
});
}
const changePage = (current: number) => {
page.value.current = current;
fetchProxies();
};
onMounted(() => {
fetchProxies();
});
const proxyData = ref<ProxyItem>(proxyInit);
const types = ref([
{ label: 'HTTP', value: 'http' },
@ -68,50 +118,31 @@ const proxyDialogShow = ref(false);
const isEditing = ref(false);
const pwdRef = ref<any>(null);
const addProxy = () => {
if (pwdRef.value.validate()) {
proxies.value.push({ ...proxyData.value });
saveProxies(proxyData.value);
proxyDialogShow.value = false;
proxyData.value = proxyInit;
}
};
const editProxy = (proxy: ProxyItem) => {
proxyData.value = { ...proxy };
isEditing.value = true;
proxyDialogShow.value = true;
};
const updateProxy = () => {
if (pwdRef.value.validate()) {
const index = proxies.value.findIndex(p => p.id === proxyData.value.id);
if (index !== -1) {
proxies.value[index] = { ...proxyData.value };
saveProxies(proxies.value);
proxyDialogShow.value = false;
proxyData.value = proxyInit;
isEditing.value = false;
}
}
const addProxy = () => {
proxyData.value = proxyInit;
isEditing.value = false;
proxyDialogShow.value = true;
};
function selectFile() {
OpenDirDialog().then((res: string) => {
proxyData.value.domain = res;
});
OpenDirDialog().then((res: string) => {
proxyData.value.domain = res;
});
}
const deleteProxy = (id: number) => {
proxies.value = proxies.value.filter(p => p.id !== id);
saveProxies(proxies.value);
};
const saveProxy = () => {
pwdRef.value.validate((valid: boolean) => {
if (valid) {
proxyData.value.port = Number(proxyData.value.port)
if (isEditing.value) {
updateProxy();
updateProxies(proxyData.value)
} else {
addProxy();
createProxies(proxyData.value)
}
} else {
console.log('表单验证失败');
@ -125,52 +156,42 @@ const proxyRules = {
{ pattern: /^(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$/, message: '请输入有效的端口号(1-65535)', trigger: 'blur' }
],
domain: [
{ required: true, message: '代理域名不能为空', trigger: 'blur' },
{ pattern: /^(https?:\/\/)?(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(:\d{1,5})?(\/[^\s]*)?$/, message: '请输入有效的域名格式', trigger: 'blur' }
]
};
const pageSize = 10;
const currentPage = ref(1);
const paginatedProxies = computed(() => {
const start = (currentPage.value - 1) * pageSize;
const end = start + pageSize;
return proxies.value.slice(start, end);
});
const totalPages = computed(() => Math.ceil(proxies.value.length / pageSize));
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
{ required: true, message: '代理域名不能为空', trigger: 'blur' },
{
pattern: /^(https?:\/\/)?((?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|localhost)(:\d{1,5})?(\/[^\s]*)?$/,
message: '请输入有效的域名格式',
trigger: 'blur'
}
]
};
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
</script>
<template>
<div>
<el-row justify="end">
<el-button type="primary" :icon="Plus" circle @click="proxyDialogShow = true" />
<el-button type="primary" :icon="Plus" circle @click="addProxy" />
</el-row>
<el-table :data="paginatedProxies" style="width: 98%;border:none">
<el-table-column prop="port" label="本地端口" width="180" />
<el-table-column prop="domain" label="代理域名" width="180" />
<el-table-column prop="proxyType" label="代理类型" width="180" />
<el-table :data="proxies" style="width: 98%;border:none">
<el-table-column prop="proxyType" label="代理类型" width="80" />
<el-table-column prop="port" label="本地端口" width="80" />
<!-- <el-table-column prop="domain" label="代理域名" /> -->
<el-table-column label="状态">
<template #default="scope">
<!-- <el-switch v-model="scope.row.status" active-color="#ff4949" inactive-color="#13ce66" @change="SetStatus(scope.row.id)"></el-switch> -->
<el-button size="small" @click="SetStatus(scope.row.id)">{{scope.row.status ? '启用' : '禁用'}}</el-button>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="editProxy(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="deleteProxy(scope.row.id)">删除</el-button>
<el-button size="small" circle icon="Edit" @click="editProxy(scope.row)"></el-button>
<el-button size="small" circle icon="Delete" @click="DeleteProxy(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-if="totalPages > 1" layout="prev, pager, next" :total="getProxies().length"
:page-size="pageSize" v-model:current-page="currentPage" @next-click="nextPage" @prev-click="prevPage" />
<el-pagination v-if="page.total > page.size" layout="prev, pager, next" :total="page.total"
:page-size="page.size" v-model:current-page="page.current" @current-change="changePage" />
<el-dialog v-model="proxyDialogShow" :title="isEditing ? '编辑代理' : '添加代理'" width="400px">
<span>
<el-form :model="proxyData" :rules="proxyRules" ref="pwdRef">
@ -183,13 +204,17 @@ const prevPage = () => {
<el-form-item label="本地端口" prop="port">
<el-input v-model="proxyData.port" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch v-model="proxyData.status" active-color="#13ce66" inactive-color="#ff4949"
active-text="启用" inactive-text="禁用" />
</el-form-item>
<el-form-item label="代理域名" prop="domain" v-if="proxyData.proxyType === 'http'">
<el-input v-model="proxyData.domain" />
</el-form-item>
<el-form-item label="文件路径" prop="domain" v-if="proxyData.proxyType === 'file'">
<el-input v-model="proxyData.domain" @click="selectFile()"/>
<el-form-item label="文件路径" prop="path" v-if="proxyData.proxyType === 'file'">
<el-input v-model="proxyData.path" @click="selectFile()" />
</el-form-item>
<el-form-item label="IP+端口" prop="domain" v-if="proxyData.proxyType === 'udp'">
<el-form-item label="转发IP+端口" prop="domain" v-if="proxyData.proxyType === 'udp'">
<el-input v-model="proxyData.domain" />
</el-form-item>
<el-form-item>

204
frontend/src/components/setting/NetProxy copy.vue

@ -1,204 +0,0 @@
<script setup lang="ts">
import { notifyError } from '@/util/msg';
import { Plus } from '@element-plus/icons-vue'
import { ref, computed } from "vue";
interface ProxyItem {
id: number;
port: string;
domain: string;
type: string;
name: string;
serverAddr: string;
serverPort: string;
}
const localKey = "godoos_net_proxy"
const getProxies = (): ProxyItem[] => {
const proxies = localStorage.getItem(localKey);
return proxies ? JSON.parse(proxies) : [];
};
const saveProxies = (proxies: ProxyItem[]) => {
localStorage.setItem(localKey, JSON.stringify(proxies));
};
const proxies = ref<ProxyItem[]>(getProxies());
const proxyData = ref<ProxyItem>({
id: Date.now(),
type: "http",
name: "",
port: "",
domain: "",
serverAddr: "",
serverPort: "",
});
const proxyDialogShow = ref(false);
const isEditing = ref(false);
const pwdRef = ref<any>(null);
const types = ref([
{ label: 'HTTP', value: 'http' },
{ label: '静态文件访问', value: 'file' },
{ label: '点对点穿透', value: 'p2p' },
{ label: 'SSH', value: 'ssh' },
{ label: 'SOCKS5', value: 'socks5' },
])
const addProxy = () => {
if (pwdRef.value.validate()) {
const isNameDuplicate = proxies.value.some(p => p.name === proxyData.value.name);
if (isNameDuplicate) {
notifyError("代理名称已存在");
return;
}
proxies.value.push({ ...proxyData.value });
saveProxies(proxies.value);
proxyDialogShow.value = false;
proxyData.value = { id: Date.now(), port: "", domain: "", type: "http", name: "", serverAddr: "", serverPort: "" };
}
};
const editProxy = (proxy: ProxyItem) => {
proxyData.value = { ...proxy };
isEditing.value = true;
proxyDialogShow.value = true;
};
const updateProxy = () => {
if (pwdRef.value.validate()) {
const index = proxies.value.findIndex(p => p.id === proxyData.value.id);
if (index !== -1) {
const isNameDuplicate = proxies.value.some((p, i) => p.name === proxyData.value.name && i !== index);
if (isNameDuplicate) {
notifyError("代理名称已存在");
return;
}
proxies.value[index] = { ...proxyData.value };
saveProxies(proxies.value);
proxyDialogShow.value = false;
proxyData.value = { id: Date.now(), port: "", domain: "", type: "http", name: "", serverAddr: "", serverPort: "" };
isEditing.value = false;
}
}
};
const deleteProxy = (id: number) => {
proxies.value = proxies.value.filter(p => p.id !== id);
saveProxies(proxies.value);
};
const saveProxy = () => {
pwdRef.value.validate((valid: boolean) => {
if (valid) {
if (isEditing.value) {
updateProxy();
} else {
addProxy();
}
} else {
console.log('表单验证失败');
}
});
};
const proxyRules = {
name: [
{ required: true, message: '代理名称不能为空', trigger: 'blur' },
{ min: 1, max: 20, message: '代理名称长度在 1 到 20 个字符', trigger: 'blur' },
{ pattern: /^[a-zA-Z]+$/, message: '代理名称只能包含英文字符', trigger: 'blur' }
],
port: [
{ required: true, message: '端口不能为空', trigger: 'blur' },
{ pattern: /^(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$/, message: '请输入有效的端口号(1-65535)', trigger: 'blur' }
],
domain: [
{ required: true, message: '代理域名不能为空', trigger: 'blur' },
{ pattern: /^(https?:\/\/)?(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(:\d{1,5})?(\/[^\s]*)?$/, message: '请输入有效的域名格式', trigger: 'blur' }
],
serverAddr: [
{ required: true, message: '服务器地址不能为空', trigger: 'blur' },
{ pattern: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, message: '请输入有效的IP地址格式', trigger: 'blur' }
],
serverPort: [
{ required: true, message: '服务器端口不能为空', trigger: 'blur' },
{ pattern: /^(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$/, message: '请输入有效的服务器端口号(1-65535)', trigger: 'blur' }
]
};
const pageSize = 10;
const currentPage = ref(1);
const paginatedProxies = computed(() => {
const start = (currentPage.value - 1) * pageSize;
const end = start + pageSize;
return proxies.value.slice(start, end);
});
const totalPages = computed(() => Math.ceil(proxies.value.length / pageSize));
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
};
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
</script>
<template>
<div>
<el-row justify="end">
<el-button type="primary" :icon="Plus" circle @click="proxyDialogShow = true" />
</el-row>
<el-table :data="paginatedProxies" style="width: 98%;border:none">
<el-table-column prop="name" label="名称" width="100" />
<el-table-column prop="type" label="类型" width="80" />
<el-table-column prop="port" label="本地端口" width="80" />
<el-table-column prop="domain" label="代理域名" width="180" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="editProxy(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="deleteProxy(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-if="totalPages > 1" layout="prev, pager, next" :total="getProxies().length"
:page-size="pageSize" v-model:current-page="currentPage" @next-click="nextPage" @prev-click="prevPage" />
<el-dialog v-model="proxyDialogShow" :title="isEditing ? '编辑代理' : '添加代理'" width="400px">
<span>
<el-form :model="proxyData" :rules="proxyRules" ref="pwdRef">
<el-form-item label="代理类型" prop="type">
<el-select v-model="proxyData.type" placeholder="代理类型">
<el-option v-for="type in types" :key="type.value" :label="type.label"
:value="type.value" />
</el-select>
</el-form-item>
<el-form-item label="代理名称" prop="name">
<el-input v-model="proxyData.name" />
</el-form-item>
<el-form-item label="本地端口" prop="port">
<el-input v-model="proxyData.port" />
</el-form-item>
<el-form-item label="代理域名" prop="domain">
<el-input v-model="proxyData.domain" />
</el-form-item>
<el-form-item label="服务器地址" prop="serverAddr">
<el-input v-model="proxyData.serverAddr" />
</el-form-item>
<el-form-item label="服务器端口" prop="serverPort">
<el-input v-model="proxyData.serverPort" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveProxy" style="margin: 0 auto;">
确认
</el-button>
</el-form-item>
</el-form>
</span>
</el-dialog>
</div>
</template>

182
frontend/src/components/setting/SetNas.vue

@ -1,33 +1,153 @@
<template>
<div class="container">
<div class="nav">
<ul>
<li v-for="(item, index) in items" :key="index" @click="activeIndex = index"
:class="{ active: index === activeIndex }">
{{ item }}
</li>
</ul>
</div>
<div class="setting">
<div v-if="0 === activeIndex">
<LocalNas />
</div>
<div v-if="1 === activeIndex">
<NasClient />
</div>
<div v-if="2 === activeIndex">
<WebDavClient />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { Plus } from '@element-plus/icons-vue'
import { ref, computed } from "vue";
interface ProxyItem {
id: number;
server_url: string;
disk: string;
username: string;
password: string;
}
const localKey = "godoos_local_nasclient"
const getProxies = (): ProxyItem[] => {
const proxies = localStorage.getItem(localKey);
return proxies ? JSON.parse(proxies) : [];
};
<script lang="ts" setup>
import { ref } from "vue";
const items = ["服务端配置", "NAS客户端", "WebDav客户端"];
const activeIndex = ref(0);
</script>
const saveProxies = (proxies: ProxyItem[]) => {
localStorage.setItem(localKey, JSON.stringify(proxies));
};
const proxies = ref<ProxyItem[]>(getProxies());
const initData = {
id: Date.now(),
server_url: "",
disk: "",
username: "",
password: "",
}
const proxyData = ref<ProxyItem>(initData);
const proxyDialogShow = ref(false);
const isEditing = ref(false);
const pwdRef = ref<any>(null);
const addProxy = () => {
if (pwdRef.value.validate()) {
proxies.value.push({ ...proxyData.value });
saveProxies(proxies.value);
proxyDialogShow.value = false;
proxyData.value = initData;
}
};
const editNas = (proxy: ProxyItem) => {
proxyData.value = { ...proxy };
isEditing.value = true;
proxyDialogShow.value = true;
};
const updateProxy = () => {
if (pwdRef.value.validate()) {
const index = proxies.value.findIndex(p => p.id === proxyData.value.id);
if (index !== -1) {
proxies.value[index] = { ...proxyData.value };
saveProxies(proxies.value);
proxyDialogShow.value = false;
proxyData.value = initData;
isEditing.value = false;
}
}
};
const deleteNas = (id: number) => {
proxies.value = proxies.value.filter(p => p.id !== id);
saveProxies(proxies.value);
};
const saveNas = () => {
pwdRef.value.validate((valid: boolean) => {
if (valid) {
if (isEditing.value) {
updateProxy();
} else {
addProxy();
}
} else {
console.log('表单验证失败');
}
});
};
<style scoped>
@import "./setStyle.css";
</style>
const proxyRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度至少为6位', trigger: 'blur' }
]
};
const pageSize = 10;
const currentPage = ref(1);
const paginatedProxies = computed(() => {
const start = (currentPage.value - 1) * pageSize;
const end = start + pageSize;
return proxies.value.slice(start, end);
});
const totalPages = computed(() => Math.ceil(proxies.value.length / pageSize));
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++;
}
};
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--;
}
};
</script>
<template>
<div style="padding:15px">
<el-row justify="end">
<el-button type="primary" :icon="Plus" circle @click="proxyDialogShow = true" />
</el-row>
<el-table :data="paginatedProxies" style="width: 98%;border:none">
<el-table-column prop="disk" label="挂载盘符" width="180" />
<el-table-column prop="server_url" label="类型" width="180" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="editNas(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="deleteNas(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-if="totalPages > 1" layout="prev, pager, next" :total="getProxies().length"
:page-size="pageSize" v-model:current-page="currentPage" @next-click="nextPage" @prev-click="prevPage" />
<el-dialog v-model="proxyDialogShow" :title="isEditing ? '编辑Nas' : '添加Nas'" width="400px">
<span>
<el-form :model="proxyData" :rules="proxyRules" ref="pwdRef">
<el-form-item label="挂载" prop="disk">
<el-input v-model="proxyData.disk"/>
</el-form-item>
<el-form-item label="地址" prop="server_url">
<el-input v-model="proxyData.server_url" />
</el-form-item>
<el-form-item label="用户" prop="username">
<el-input v-model="proxyData.username" />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="proxyData.password" type="password" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveNas" style="margin: 0 auto;">
确认
</el-button>
</el-form-item>
</el-form>
</span>
</el-dialog>
</div>
</template>

21
frontend/src/hook/useContextMenu.ts

@ -217,11 +217,12 @@ function useContextMenu() {
const system = useSystem();
if (!system) return;
const filePath = file.path;
if (filePath === "/") return;
if (filePath === "/"|| filePath === "\\") return;
const vol = filePath.charAt(1);
if (vol != "B") return;
let newPath: any = file.oldPath;
const Sp = filePath.charAt(0);
//console.log(newPath)
if (await system.fs.exists(newPath)) {
@ -234,7 +235,7 @@ function useContextMenu() {
} else {
let i = 1;
let parentPath = '/B' + file.parentPath.substring(2)
let parentPath = Sp + 'B' + file.parentPath.substring(2)
while (await system.fs.exists(fspath.join(parentPath, `${file.title}(${i}).${file.ext}`))) {
i++;
}
@ -252,22 +253,24 @@ function useContextMenu() {
if (!system) return;
if (!file) return;
const filePath = file.path;
if (filePath === "/") return;
const Sp = filePath.charAt(0);
const vol = filePath.charAt(1);
if (filePath === "/" || filePath === "\\") return;
if (file.isDirectory) {
if (["/", "/B", "/C", "/D", "/E", "/F"].includes(file.path)) return;
if (file.isDirectory && filePath.length == 2) {
if (["B", "C", "D", "E", "F"].includes(vol)) return;
}
//console.log(file)
const vol = filePath.charAt(1);
//console.log(vol)
if (file && file.id && file.id < 1) {
if (["C", "D", "E"].includes(vol)) {
//console.log('/B' + filePath.substring(2))
let newPath = '/B' + filePath
let newPath = Sp + 'B' + filePath
// if (file.parentPath == "/C/Users/Desktop" && !file.isDirectory) {
// newPath = '/B/' + file.title + "." + file.ext
// }
const fileDir = '/B' + (file.isDirectory ? file.path : file.path.replace(file.name, ""))
const fileDir = Sp + 'B' + (file.isDirectory ? file.path : file.path.replace(file.name, ""))
//console.log(fileDir)
if (!(await system.fs.exists(fileDir))) {
await system.fs.mkdir(fileDir)
@ -283,7 +286,7 @@ function useContextMenu() {
} else {
let i = 1;
let parentPath = '/B' + file.parentPath
let parentPath = Sp + 'B' + file.parentPath
while (await system.fs.exists(fspath.join(parentPath, `${file.title}(${i}).${file.ext}`))) {
i++;
}

20
frontend/src/stores/proxy.ts

@ -98,7 +98,7 @@ export const useProxyStore = defineStore('proxyStore', () => {
};
const createFrpc = async () => {
const url = `${apiUrl}/frpc/create`;
const url = `${apiUrl}/proxy/frpc/create`;
const postData:any = toRaw(proxyData.value)
if(customDomains.value.length > 0){
postData.customDomains = customDomains.value.join(',');
@ -120,7 +120,7 @@ export const useProxyStore = defineStore('proxyStore', () => {
await fetchProxies()
}
const fetchProxies = async () => {
const url = `${apiUrl}/frpc/list?page=${page.value.current}&limit=${page.value.size}`;
const url = `${apiUrl}/proxy/frpc/list?page=${page.value.current}&limit=${page.value.size}`;
const response = await fetch(url);
if (!response.ok) {
@ -133,13 +133,13 @@ export const useProxyStore = defineStore('proxyStore', () => {
} else {
console.error("Failed to retrieve proxies:", res.message);
}
fetch(`${apiUrl}/frpc/status`).then(res => res.json()).then(res => {
fetch(`${apiUrl}/proxy/frpc/status`).then(res => res.json()).then(res => {
status.value = res.data
})
};
const fetchProxy = async (id: number) => {
fetch(`${apiUrl}/frpc/get?id=${id}`).then(res => res.json()).then(res => {
fetch(`${apiUrl}/proxy/frpc/get?id=${id}`).then(res => res.json()).then(res => {
if (res.code == 0) {
proxyData.value = res.data;
customDomains.value = proxyData.value.customDomains?.split(',') || [];
@ -148,7 +148,7 @@ export const useProxyStore = defineStore('proxyStore', () => {
};
const deleteProxyById = async (id: number) => {
const url = `${apiUrl}/frpc/delete?id=${id}`;
const url = `${apiUrl}/proxy/frpc/delete?id=${id}`;
const response = await fetch(url);
if (!response.ok) {
@ -165,7 +165,7 @@ export const useProxyStore = defineStore('proxyStore', () => {
};
const updateProxy = async (proxy: ProxyItem) => {
const url = `${apiUrl}/frpc/update`;
const url = `${apiUrl}/proxy/frpc/update`;
if(customDomains.value.length > 0){
proxy.customDomains = customDomains.value.join(',');
}
@ -182,7 +182,7 @@ export const useProxyStore = defineStore('proxyStore', () => {
})
};
const getConfig = async () => {
return await fetch(`${apiUrl}/frpc/getconfig`).then(res => res.json())
return await fetch(`${apiUrl}/proxy/frpc/getconfig`).then(res => res.json())
}
const setConfig = async (config: any) => {
return await fetch(`${apiUrl}/frpc/setconfig`, {
@ -194,21 +194,21 @@ export const useProxyStore = defineStore('proxyStore', () => {
}).then(res => res.json())
}
const startFrpc = async () => {
return await fetch(`${apiUrl}/frpc/start`).then(res => res.json()).then(res => {
return await fetch(`${apiUrl}/proxy/frpc/start`).then(res => res.json()).then(res => {
if (res.code == 0) {
status.value = true
}
})
}
const stopFrpc = async () => {
return await fetch(`${apiUrl}/frpc/stop`).then(res => res.json()).then(res => {
return await fetch(`${apiUrl}/proxy/frpc/stop`).then(res => res.json()).then(res => {
if (res.code == 0) {
status.value = false
}
})
}
const restartFrpc = async () => {
return await fetch(`${apiUrl}/frpc/restart`).then(res => res.json()).then(res => {
return await fetch(`${apiUrl}/proxy/frpc/restart`).then(res => res.json()).then(res => {
if (res.code == 0) {
status.value = true
}

25
godo/cmd/main.go

@ -168,20 +168,21 @@ func OsStart() {
proxyRouter.HandleFunc("/local/delete", proxy.DeleteLocalProxyHandler).Methods(http.MethodGet)
proxyRouter.HandleFunc("/local/get", proxy.GetLocalProxyHandler).Methods(http.MethodGet)
proxyRouter.HandleFunc("/local/list", proxy.GetLocalProxiesHandler).Methods(http.MethodGet)
proxyRouter.HandleFunc("/local/status", proxy.HandlerSetProxyStatus).Methods(http.MethodGet)
proxyRouter.HandleFunc("/local/update", proxy.UpdateLocalProxyHandler).Methods(http.MethodPost)
// 注册frpc路由
frpcRouter := router.PathPrefix("/frpc").Subrouter()
frpcRouter.HandleFunc("/create", proxy.CreateFrpcHandler).Methods(http.MethodPost)
frpcRouter.HandleFunc("/delete", proxy.DeleteFrpcHandler).Methods(http.MethodGet) // 删除frpc配置
frpcRouter.HandleFunc("/get", proxy.GetFrpcHandler).Methods(http.MethodGet) // 获取frpc配置
frpcRouter.HandleFunc("/list", proxy.GetFrpcListHandler).Methods(http.MethodGet) // 获取frpc代理列表
frpcRouter.HandleFunc("/update", proxy.UpdateFrpcHandler).Methods(http.MethodPost) // 更新frpc配置
frpcRouter.HandleFunc("/setconfig", proxy.UpdateFrpcConfigHandler).Methods(http.MethodPost) // 更新frpc配置
frpcRouter.HandleFunc("/getconfig", proxy.GetFrpcConfigHandler).Methods(http.MethodGet)
frpcRouter.HandleFunc("/start", proxy.StartFrpcHandler).Methods(http.MethodGet) // 启动frpc服务
frpcRouter.HandleFunc("/stop", proxy.StopFrpcHandler).Methods(http.MethodGet) // 停止frpc服务
frpcRouter.HandleFunc("/restart", proxy.RestartFrpcHandler).Methods(http.MethodGet) // 重启frpc服务
frpcRouter.HandleFunc("/status", proxy.StatusFrpcHandler).Methods(http.MethodGet)
//frpcRouter := router.PathPrefix("/frpc").Subrouter()
proxyRouter.HandleFunc("/frpc/create", proxy.CreateFrpcHandler).Methods(http.MethodPost)
proxyRouter.HandleFunc("/frpc/delete", proxy.DeleteFrpcHandler).Methods(http.MethodGet) // 删除frpc配置
proxyRouter.HandleFunc("/frpc/get", proxy.GetFrpcHandler).Methods(http.MethodGet) // 获取frpc配置
proxyRouter.HandleFunc("/frpc/list", proxy.GetFrpcListHandler).Methods(http.MethodGet) // 获取frpc代理列表
proxyRouter.HandleFunc("/frpc/update", proxy.UpdateFrpcHandler).Methods(http.MethodPost) // 更新frpc配置
proxyRouter.HandleFunc("/frpc/setconfig", proxy.UpdateFrpcConfigHandler).Methods(http.MethodPost) // 更新frpc配置
proxyRouter.HandleFunc("/frpc/getconfig", proxy.GetFrpcConfigHandler).Methods(http.MethodGet)
proxyRouter.HandleFunc("/frpc/start", proxy.StartFrpcHandler).Methods(http.MethodGet) // 启动frpc服务
proxyRouter.HandleFunc("/frpc/stop", proxy.StopFrpcHandler).Methods(http.MethodGet) // 停止frpc服务
proxyRouter.HandleFunc("/frpc/restart", proxy.RestartFrpcHandler).Methods(http.MethodGet) // 重启frpc服务
proxyRouter.HandleFunc("/frpc/status", proxy.StatusFrpcHandler).Methods(http.MethodGet)
// 注册根路径的处理函数
distFS, _ := fs.Sub(deps.Frontendassets, "dist")

1
godo/files/disk.go

@ -0,0 +1 @@
package files

9
godo/model/local_proxy.go

@ -3,13 +3,20 @@ package model
type LocalProxy struct {
BaseModel
Port uint `json:"port"`
ProxyType string `json:"proxy_type"`
ProxyType string `json:"proxyType"`
Domain string `json:"domain"`
Path string `json:"path"`
Status bool `json:"status"`
}
func (*LocalProxy) TableName() string {
return "local_proxy"
}
func GetLocalProxiesOn() ([]LocalProxy, error) {
var proxies []LocalProxy
err := Db.Where("status = ?", true).Find(&proxies).Error
return proxies, err
}
// GetLocalProxies 获取所有 LocalProxy,支持分页
func GetLocalProxies(page, limit int) ([]LocalProxy, int64, error) {

12
godo/model/sys_disk.go

@ -14,9 +14,9 @@ type SysDisk struct {
Name string `json:"name"`
Disk string `json:"disk" gorm:"unique"`
Size int64 `json:"size"`
Type uint `json:"type"` //0C-E本地 1nasserver 2nasclient 3webdavserver 4webdavclient 5F分享 6B回收站
Type uint `json:"type"` //0C-E系统 1nasserver 2nasclient 3webdavserver 4webdavclient 5F分享 9B回收站
Path string `json:"path"`
Status uint `json:"status"`
Status bool `json:"status"`
}
func (*SysDisk) TableName() string {
@ -31,10 +31,10 @@ func initSysDisk(db *gorm.DB) {
}
if count == 0 {
disks := []SysDisk{
{Disk: "B", Name: "回收站", Size: 0, Path: filepath.Join(basePath, "B"), Type: 6, Status: 1},
{Disk: "C", Name: "系统", Size: 0, Path: filepath.Join(basePath, "C"), Type: 0, Status: 1},
{Disk: "D", Name: "文档", Size: 0, Path: filepath.Join(basePath, "D"), Type: 0, Status: 1},
{Disk: "E", Name: "办公", Size: 0, Path: filepath.Join(basePath, "E"), Type: 0, Status: 1},
{Disk: "B", Name: "回收站", Size: 0, Path: filepath.Join(basePath, "B"), Type: 9, Status: true},
{Disk: "C", Name: "系统", Size: 0, Path: filepath.Join(basePath, "C"), Type: 0, Status: true},
{Disk: "D", Name: "文档", Size: 0, Path: filepath.Join(basePath, "D"), Type: 0, Status: true},
{Disk: "E", Name: "办公", Size: 0, Path: filepath.Join(basePath, "E"), Type: 0, Status: true},
}
db.Create(&disks)
fmt.Println("Initialized A-Z disks")

194
godo/proxy/local.go

@ -1,19 +1,37 @@
package proxy
import (
"context"
"encoding/json"
"fmt"
"godo/libs"
"godo/model"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"sync"
"time"
)
// 全局代理服务映射
var proxyServers sync.Map
// ProxyServer 结构体用于存储服务类型和实际服务对象
type ProxyServer struct {
Type string
Server interface{}
}
// FileServer 结构体用于存储文件静态服务的信息
type FileServer struct {
Port int
Server *http.Server
}
// 创建 LocalProxy 的 HTTP 处理函数
func CreateLocalProxyHandler(w http.ResponseWriter, r *http.Request) {
var lp model.LocalProxy
err := json.NewDecoder(r.Body).Decode(&lp)
if err != nil {
@ -27,14 +45,14 @@ func CreateLocalProxyHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(lp)
// 启动代理服务
go startProxy(lp)
libs.SuccessMsg(w, lp, "")
}
// GetLocalProxiesHandler 获取所有 LocalProxy 的 HTTP 处理函数
func GetLocalProxiesHandler(w http.ResponseWriter, r *http.Request) {
// 获取查询参数 page 和 limit
pageStr := r.URL.Query().Get("page")
limitStr := r.URL.Query().Get("limit")
@ -62,15 +80,7 @@ func GetLocalProxiesHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.Header().Set("Content-Type", "application/json")
response := ProxyResponse{
Proxies: proxies,
Total: total,
}
if err := json.NewEncoder(w).Encode(response); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
}
libs.SuccessMsg(w, ProxyResponse{Proxies: proxies, Total: total}, "")
}
// GetLocalProxyHandler 获取单个 LocalProxy 的 HTTP 处理函数
@ -88,13 +98,11 @@ func GetLocalProxyHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(proxy)
libs.SuccessMsg(w, proxy, "")
}
// UpdateLocalProxyHandler 更新 LocalProxy 的 HTTP 处理函数
func UpdateLocalProxyHandler(w http.ResponseWriter, r *http.Request) {
var lp model.LocalProxy
err := json.NewDecoder(r.Body).Decode(&lp)
if err != nil {
@ -108,8 +116,13 @@ func UpdateLocalProxyHandler(w http.ResponseWriter, r *http.Request) {
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(lp)
// 停止旧的代理服务
stopProxy(lp.ID)
// 启动新的代理服务
go startProxy(lp)
libs.SuccessMsg(w, lp, "")
}
// DeleteLocalProxyHandler 删除 LocalProxy 的 HTTP 处理函数
@ -121,13 +134,44 @@ func DeleteLocalProxyHandler(w http.ResponseWriter, r *http.Request) {
return
}
// 停止代理服务
stopProxy(uint(id))
err = model.Db.Delete(&model.LocalProxy{}, uint(id)).Error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
libs.SuccessMsg(w, nil, "delete proxy success")
}
func HandlerSetProxyStatus(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
if idStr == "" {
libs.ErrorMsg(w, "id is empty")
return
}
id, err := strconv.Atoi(idStr)
if err != nil || id == 0 {
libs.ErrorMsg(w, "id is not number")
return
}
var proxy model.LocalProxy
if err := model.Db.First(&proxy, uint(id)).Error; err != nil {
libs.ErrorMsg(w, "proxy not found")
return
}
if err := model.Db.Model(&model.LocalProxy{}).Where("id = ?", proxy.ID).Update("status", !proxy.Status).Error; err != nil {
libs.ErrorMsg(w, "update proxy status failed")
return
}
if !proxy.Status {
startProxy(proxy)
} else {
stopProxy(proxy.ID)
}
libs.SuccessMsg(w, nil, "")
}
// 初始化代理处理函数
@ -140,22 +184,74 @@ func InitProxyHandlers() {
}
for _, proxy := range proxies {
switch proxy.ProxyType {
go startProxy(proxy)
}
}
// 启动代理服务
func startProxy(proxy model.LocalProxy) {
switch proxy.ProxyType {
case "http":
go func(p model.LocalProxy) {
httpProxyHandler(p)
}(proxy)
case "udp":
go func(p model.LocalProxy) {
udpProxyHandler(p)
}(proxy)
case "file":
go func(p model.LocalProxy) {
fileServerHandler(p)
}(proxy)
default:
fmt.Printf("Unknown proxy type: %s\n", proxy.ProxyType)
}
}
// 停止代理服务
func stopProxy(id uint) {
if server, ok := proxyServers.Load(id); ok {
proxyServer, ok := server.(ProxyServer)
if !ok {
fmt.Printf("Failed to load proxy server for ID %d\n", id)
return
}
switch proxyServer.Type {
case "http":
go func(p model.LocalProxy) {
httpProxyHandler(p)
}(proxy)
httpServer, ok := proxyServer.Server.(*http.Server)
if ok {
// 创建一个上下文,用于传递给 server.Shutdown(ctx)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil {
fmt.Printf("Failed to shutdown HTTP server on port %s: %v\n", httpServer.Addr, err)
} else {
fmt.Printf("Stopped HTTP server on port %s\n", httpServer.Addr)
}
}
case "udp":
// UDP 转发的处理逻辑
go func(p model.LocalProxy) {
udpProxyHandler(p)
}(proxy)
udpConn, ok := proxyServer.Server.(*net.UDPConn)
if ok {
udpConn.Close()
fmt.Printf("Stopped UDP server on port %d\n", udpConn.LocalAddr().(*net.UDPAddr).Port)
}
case "file":
http.Handle(fmt.Sprintf("/file/%d/", proxy.Port), http.StripPrefix(fmt.Sprintf("/file/%d/", proxy.Port), http.FileServer(http.Dir(proxy.Domain))))
fmt.Printf("Setting up file server for port %d at %s\n", proxy.Port, proxy.Domain)
fileServer, ok := proxyServer.Server.(*FileServer)
if ok {
// 创建一个上下文,用于传递给 server.Shutdown(ctx)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := fileServer.Server.Shutdown(ctx); err != nil {
fmt.Printf("Failed to shutdown file server on port %d: %v\n", fileServer.Port, err)
} else {
fmt.Printf("Stopped file server on port %d\n", fileServer.Port)
}
}
default:
fmt.Printf("Unknown proxy type: %s\n", proxy.ProxyType)
fmt.Printf("Unknown proxy type: %s\n", proxyServer.Type)
}
proxyServers.Delete(id)
}
}
@ -167,23 +263,36 @@ func httpProxyHandler(proxy model.LocalProxy) {
return
}
if remote.Scheme == "" {
fmt.Printf("Remote URL for port %d does not contain a scheme (http/https): %s\n", proxy.Port, proxy.Domain)
return
}
reverseProxy := httputil.NewSingleHostReverseProxy(remote)
// 设置请求头
// reverseProxy.Director = func(req *http.Request) {
// req.Header.Add("X-Forwarded-For", req.RemoteAddr)
// req.Header.Add("X-Real-IP", req.RemoteAddr)
// req.Host = remote.Host
// }
// 启动 HTTP 服务器并监听指定端口
server := &http.Server{
Addr: fmt.Sprintf(":%d", proxy.Port),
Handler: reverseProxy,
}
proxyServers.Store(proxy.ID, ProxyServer{Type: "http", Server: server})
fmt.Printf("Starting HTTP proxy on port %d and forwarding to %s:%d\n", proxy.Port, proxy.Domain, proxy.Port)
if err := server.ListenAndServe(); err != nil {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("Failed to start HTTP proxy on port %d: %v\n", proxy.Port, err)
}
}
// UDP 代理处理函数
func udpProxyHandler(proxy model.LocalProxy) {
localAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", proxy.Port))
if err != nil {
fmt.Printf("Failed to resolve local UDP address for port %d: %v\n", proxy.Port, err)
@ -203,6 +312,8 @@ func udpProxyHandler(proxy model.LocalProxy) {
}
defer conn.Close()
proxyServers.Store(proxy.ID, ProxyServer{Type: "udp", Server: conn})
buffer := make([]byte, 1024)
for {
n, addr, err := conn.ReadFromUDP(buffer)
@ -237,3 +348,22 @@ func udpProxyHandler(proxy model.LocalProxy) {
}
}
}
// 文件静态服务处理函数
func fileServerHandler(proxy model.LocalProxy) {
// 创建文件服务器的 HTTP 处理函数
fileHandler := http.FileServer(http.Dir(proxy.Path))
// 启动 HTTP 服务器并监听指定端口
server := &http.Server{
Addr: fmt.Sprintf(":%d", proxy.Port),
Handler: fileHandler,
}
proxyServers.Store(proxy.ID, ProxyServer{Type: "file", Server: &FileServer{Port: int(proxy.Port), Server: server}})
fmt.Printf("Starting file server on port %d serving files from %s\n", proxy.Port, proxy.Path)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("Failed to start file server on port %d: %v\n", proxy.Port, err)
}
}

Loading…
Cancel
Save