Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40cb4f838c | ||
|
|
e952a64f35 |
@@ -28,3 +28,4 @@ node_modules/
|
|||||||
design.md
|
design.md
|
||||||
agent.md
|
agent.md
|
||||||
api-backend.md
|
api-backend.md
|
||||||
|
CLAUDE.md
|
||||||
+3
-1
@@ -107,7 +107,9 @@ export const instanceApi = {
|
|||||||
modifyProxy: (instanceID, proxyInfo) =>
|
modifyProxy: (instanceID, proxyInfo) =>
|
||||||
api.post('/frpcAct/proxyMgr/modify', { instanceID, proxyInfo }),
|
api.post('/frpcAct/proxyMgr/modify', { instanceID, proxyInfo }),
|
||||||
deleteProxy: (instanceID, proxyName) =>
|
deleteProxy: (instanceID, proxyName) =>
|
||||||
api.post('/frpcAct/proxyMgr/delete', { instanceID, proxyName })
|
api.post('/frpcAct/proxyMgr/delete', { instanceID, proxyName }),
|
||||||
|
switchProxy: (instanceID, proxyName, action) =>
|
||||||
|
api.post('/frpcAct/proxyMgr/switch', { instanceID, proxyName, action })
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logApi = {
|
export const logApi = {
|
||||||
|
|||||||
@@ -172,9 +172,19 @@
|
|||||||
</button>
|
</button>
|
||||||
</h3>
|
</h3>
|
||||||
<div v-if="proxies.length > 0" class="proxy-list">
|
<div v-if="proxies.length > 0" class="proxy-list">
|
||||||
<div v-for="proxy in proxies" :key="proxy.name" class="proxy-item">
|
<div v-for="proxy in proxies" :key="proxy.name" class="proxy-item" :class="{ 'proxy-disabled': !proxy.enabled }">
|
||||||
<div class="proxy-header">
|
<div class="proxy-header">
|
||||||
|
<div class="proxy-header-left">
|
||||||
<span class="proxy-name">{{ proxy.name }}</span>
|
<span class="proxy-name">{{ proxy.name }}</span>
|
||||||
|
<label class="toggle-switch" @click.stop>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="proxy.enabled"
|
||||||
|
@change="toggleProxy(proxy)"
|
||||||
|
>
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="proxy-actions-right">
|
<div class="proxy-actions-right">
|
||||||
<span class="proxy-type">{{ proxy.type }}</span>
|
<span class="proxy-type">{{ proxy.type }}</span>
|
||||||
<div class="proxy-actions">
|
<div class="proxy-actions">
|
||||||
@@ -442,7 +452,8 @@ export default {
|
|||||||
type: proxy.type,
|
type: proxy.type,
|
||||||
localIP: proxy.localIP,
|
localIP: proxy.localIP,
|
||||||
localPort: proxy.localPort,
|
localPort: proxy.localPort,
|
||||||
remotePort: proxy.remotePort
|
remotePort: proxy.remotePort,
|
||||||
|
enabled: proxy.enabled !== undefined ? proxy.enabled : true
|
||||||
}));
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotification(error.message || 'Load proxy list failed', 'error');
|
showNotification(error.message || 'Load proxy list failed', 'error');
|
||||||
@@ -581,6 +592,17 @@ export default {
|
|||||||
showDeleteProxyModal.value = true;
|
showDeleteProxyModal.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleProxy = async (proxy) => {
|
||||||
|
const action = proxy.enabled ? 0 : 1;
|
||||||
|
try {
|
||||||
|
const result = await instanceApi.switchProxy(+instanceID.value, proxy.name, action);
|
||||||
|
showNotification(result.message || `Proxy "${proxy.name}" ${proxy.enabled ? 'disabled' : 'enabled'}`, 'success');
|
||||||
|
await loadProxies();
|
||||||
|
} catch (error) {
|
||||||
|
showNotification(error.message || 'Failed to toggle proxy', 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const closeDeleteProxyModal = () => {
|
const closeDeleteProxyModal = () => {
|
||||||
showDeleteProxyModal.value = false;
|
showDeleteProxyModal.value = false;
|
||||||
selectedProxyName.value = '';
|
selectedProxyName.value = '';
|
||||||
@@ -601,8 +623,8 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const result = await instanceApi.deleteProxy(instanceID.value, selectedProxyName.value);
|
const result = await instanceApi.deleteProxy(instanceID.value, selectedProxyName.value);
|
||||||
showNotification(result.message || 'Proxy deleted successfully', 'success');
|
showNotification(result.message || 'Proxy deleted successfully', 'success');
|
||||||
closeDeleteProxyModal();
|
|
||||||
await loadProxies();
|
await loadProxies();
|
||||||
|
closeDeleteProxyModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotification(error.message || 'Delete proxy failed', 'error');
|
showNotification(error.message || 'Delete proxy failed', 'error');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -751,6 +773,7 @@ export default {
|
|||||||
addProxy,
|
addProxy,
|
||||||
editProxy,
|
editProxy,
|
||||||
deleteProxy,
|
deleteProxy,
|
||||||
|
toggleProxy,
|
||||||
closeAddProxyModal,
|
closeAddProxyModal,
|
||||||
closeDeleteProxyModal,
|
closeDeleteProxyModal,
|
||||||
confirmDeleteProxy,
|
confirmDeleteProxy,
|
||||||
@@ -810,6 +833,12 @@ export default {
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.proxy-header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.proxy-name {
|
.proxy-name {
|
||||||
font-family: 'SF Pro Display', 'SF Pro Icons', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
font-family: 'SF Pro Display', 'SF Pro Icons', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@@ -1204,4 +1233,54 @@ export default {
|
|||||||
.section:last-child .logs-container::-webkit-scrollbar-thumb:hover {
|
.section:last-child .logs-container::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(0, 0, 0, 0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toggle-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 44px;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-slider {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: var(--surface-hover);
|
||||||
|
border-radius: 24px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-slider::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
left: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch input:checked + .toggle-slider {
|
||||||
|
background: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-switch input:checked + .toggle-slider::before {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.proxy-disabled {
|
||||||
|
opacity: 0.48;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Listen Address</label>
|
<label>Listen Address</label>
|
||||||
<input type="text" v-model="settings.listenAddr" placeholder="0.0.0.0">
|
<input type="text" v-model="settings.lstenAddr" placeholder="0.0.0.0">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Listen Port</label>
|
<label>Listen Port</label>
|
||||||
@@ -27,6 +27,11 @@
|
|||||||
<input type="text" v-model="settings.instancePath" placeholder="/etc/frpc">
|
<input type="text" v-model="settings.instancePath" placeholder="/etc/frpc">
|
||||||
<p v-pre class="field-hint">You have to fill it with absolute path but not relative path</p>
|
<p v-pre class="field-hint">You have to fill it with absolute path but not relative path</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Logs Path</label>
|
||||||
|
<input type="text" v-model="settings.logsPath" placeholder="/var/log/super-frpc">
|
||||||
|
<p v-pre class="field-hint">You have to fill it with absolute path but not relative path</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -148,6 +153,7 @@ export default {
|
|||||||
listenPort: '',
|
listenPort: '',
|
||||||
frpcPath: '',
|
frpcPath: '',
|
||||||
instancePath: '',
|
instancePath: '',
|
||||||
|
logsPath: '',
|
||||||
notificationEnabled: false,
|
notificationEnabled: false,
|
||||||
notificationMethod: 'webhook',
|
notificationMethod: 'webhook',
|
||||||
watchdogEnabled: false,
|
watchdogEnabled: false,
|
||||||
@@ -170,6 +176,7 @@ export default {
|
|||||||
settings.listenPort = data.ListenPort || '';
|
settings.listenPort = data.ListenPort || '';
|
||||||
settings.frpcPath = data.FrpcPath || '';
|
settings.frpcPath = data.FrpcPath || '';
|
||||||
settings.instancePath = data.InstancePath || '';
|
settings.instancePath = data.InstancePath || '';
|
||||||
|
settings.logsPath = data.LogsPath || '';
|
||||||
settings.notificationEnabled = data['Notification.Enabled'] || false;
|
settings.notificationEnabled = data['Notification.Enabled'] || false;
|
||||||
settings.notificationMethod = data['Notification.Method'] || 'webhook';
|
settings.notificationMethod = data['Notification.Method'] || 'webhook';
|
||||||
settings.watchdogEnabled = data['Watchdog.Enabled'] || false;
|
settings.watchdogEnabled = data['Watchdog.Enabled'] || false;
|
||||||
@@ -192,6 +199,7 @@ export default {
|
|||||||
ListenPort: settings.listenPort,
|
ListenPort: settings.listenPort,
|
||||||
FrpcPath: settings.frpcPath,
|
FrpcPath: settings.frpcPath,
|
||||||
InstancePath: settings.instancePath,
|
InstancePath: settings.instancePath,
|
||||||
|
LogsPath: settings.logsPath,
|
||||||
'Notification.Enabled': settings.notificationEnabled,
|
'Notification.Enabled': settings.notificationEnabled,
|
||||||
'Notification.Method': settings.notificationMethod,
|
'Notification.Method': settings.notificationMethod,
|
||||||
'Watchdog.Enabled': settings.watchdogEnabled,
|
'Watchdog.Enabled': settings.watchdogEnabled,
|
||||||
|
|||||||
Reference in New Issue
Block a user