Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
974b252637 |
@@ -172,7 +172,7 @@ The JSON API is session-authenticated unless noted:
|
|||||||
- `POST /api/telegram-link/unlink`
|
- `POST /api/telegram-link/unlink`
|
||||||
- `POST /api/vpn-clients`
|
- `POST /api/vpn-clients`
|
||||||
- `POST /api/vpn-clients/{id}/enabled`
|
- `POST /api/vpn-clients/{id}/enabled`
|
||||||
- `POST /api/vpn-clients/{id}/group` changes the group and rotates the client key pair
|
- `POST /api/vpn-clients/{id}/group` changes the server-side connectivity group without changing the client config
|
||||||
- `DELETE /api/vpn-clients/{id}`
|
- `DELETE /api/vpn-clients/{id}`
|
||||||
- `GET /api/vpn-clients/{id}/config` returns `servers[]` with one raw AWG config and one Amnezia `vpn://` import link per registered endpoint
|
- `GET /api/vpn-clients/{id}/config` returns `servers[]` with one raw AWG config and one Amnezia `vpn://` import link per registered endpoint
|
||||||
- `POST /api/vpn-clients/sync`
|
- `POST /api/vpn-clients/sync`
|
||||||
|
|||||||
+1
-4
@@ -533,10 +533,7 @@ async fn set_vpn_client_group_handler(
|
|||||||
return Ok(json_error(cot::http::StatusCode::NOT_FOUND, "not found"));
|
return Ok(json_error(cot::http::StatusCode::NOT_FOUND, "not found"));
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = client
|
if let Err(e) = client.set_group(&db, request.group_name.as_deref()).await {
|
||||||
.set_group_and_rotate_keys(&db, request.group_name.as_deref())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
return Ok(json_error_typed(
|
return Ok(json_error_typed(
|
||||||
cot::http::StatusCode::BAD_REQUEST,
|
cot::http::StatusCode::BAD_REQUEST,
|
||||||
"client_group_update_failed",
|
"client_group_update_failed",
|
||||||
|
|||||||
+1
-3
@@ -148,9 +148,7 @@ translations! {
|
|||||||
configs_group_placeholder: "Leave empty for isolation" , "Оставьте пустым для изоляции";
|
configs_group_placeholder: "Leave empty for isolation" , "Оставьте пустым для изоляции";
|
||||||
configs_group_hint: "Clients with the same group name can connect to each other on the same VPN server." , "Клиенты с одинаковой группой могут обращаться друг к другу на одном VPN-сервере.";
|
configs_group_hint: "Clients with the same group name can connect to each other on the same VPN server." , "Клиенты с одинаковой группой могут обращаться друг к другу на одном VPN-сервере.";
|
||||||
configs_change_group: "Change group" , "Сменить группу";
|
configs_change_group: "Change group" , "Сменить группу";
|
||||||
configs_group_rotation_warning: "Changing the group rotates this client's keys. The old config will stop working; download or import a new one." , "При смене группы ключи клиента будут заменены. Старый конфиг перестанет работать — скачайте или импортируйте новый.";
|
configs_group_changed: "Group changed. The existing client config remains valid." , "Группа изменена. Текущий конфиг клиента остаётся действительным.";
|
||||||
configs_group_rotation_confirm: "Change the group and invalidate the old client config?" , "Сменить группу и сделать старый конфиг клиента недействительным?";
|
|
||||||
configs_group_changed: "Group changed. Update the client config." , "Группа изменена. Обновите конфиг клиента.";
|
|
||||||
configs_empty_title: "Create your first key" , "Создайте первый ключ";
|
configs_empty_title: "Create your first key" , "Создайте первый ключ";
|
||||||
configs_empty_hint: "It will appear here after creation." , "После создания он появится здесь.";
|
configs_empty_hint: "It will appear here after creation." , "После создания он появится здесь.";
|
||||||
configs_enabled_state: "active" , "активен";
|
configs_enabled_state: "active" , "активен";
|
||||||
|
|||||||
+1
-4
@@ -122,7 +122,7 @@ impl VpnClient {
|
|||||||
self.save(db).await
|
self.save(db).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_group_and_rotate_keys(
|
pub async fn set_group(
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &Database,
|
db: &Database,
|
||||||
group_name: Option<&str>,
|
group_name: Option<&str>,
|
||||||
@@ -132,9 +132,6 @@ impl VpnClient {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let keypair = generate_keypair().map_err(db_custom_error)?;
|
|
||||||
self.public_key = LimitedString::new(keypair.public_key).unwrap();
|
|
||||||
self.private_key = LimitedString::new(keypair.private_key).unwrap();
|
|
||||||
self.group_name = group_name;
|
self.group_name = group_name;
|
||||||
let now = now_timestamp();
|
let now = now_timestamp();
|
||||||
self.updated_at = LimitedString::new(now).unwrap();
|
self.updated_at = LimitedString::new(now).unwrap();
|
||||||
|
|||||||
@@ -118,7 +118,6 @@
|
|||||||
.telegram-copy { color: #53616c; line-height: 1.45; margin: 0; }
|
.telegram-copy { color: #53616c; line-height: 1.45; margin: 0; }
|
||||||
.form-stack { display: grid; gap: .8rem; }
|
.form-stack { display: grid; gap: .8rem; }
|
||||||
.field-hint { margin: 0; color: #69777f; font-size: .82rem; line-height: 1.4; }
|
.field-hint { margin: 0; color: #69777f; font-size: .82rem; line-height: 1.4; }
|
||||||
.warning-box { border: 1px solid #e5c76c; border-radius: 8px; background: #fff8df; color: #5f4911; padding: .7rem; font-size: .86rem; line-height: 1.4; }
|
|
||||||
.guide-list { margin: 0; padding-left: 1.2rem; color: #34424b; display: grid; gap: .45rem; line-height: 1.4; }
|
.guide-list { margin: 0; padding-left: 1.2rem; color: #34424b; display: grid; gap: .45rem; line-height: 1.4; }
|
||||||
.guide-list a { color: #18342f; font-weight: 850; }
|
.guide-list a { color: #18342f; font-weight: 850; }
|
||||||
.guide-list a.disabled { color: #69777f; pointer-events: none; text-decoration: none; }
|
.guide-list a.disabled { color: #69777f; pointer-events: none; text-decoration: none; }
|
||||||
@@ -312,9 +311,6 @@
|
|||||||
</datalist>
|
</datalist>
|
||||||
<p class="field-hint">{{ t.configs_group_hint }}</p>
|
<p class="field-hint">{{ t.configs_group_hint }}</p>
|
||||||
</div>
|
</div>
|
||||||
<template x-if="clientModal.mode === 'group'">
|
|
||||||
<div class="warning-box">{{ t.configs_group_rotation_warning }}</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button type="button" class="secondary" @click="closeClientModal()">{{ t.admin_close }}</button>
|
<button type="button" class="secondary" @click="closeClientModal()">{{ t.admin_close }}</button>
|
||||||
@@ -777,8 +773,6 @@ function clientPortal() {
|
|||||||
this.clientModal = null;
|
this.clientModal = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!confirm('{{ t.configs_group_rotation_confirm }}')) return;
|
|
||||||
|
|
||||||
this.clearNotice();
|
this.clearNotice();
|
||||||
this.busy = true;
|
this.busy = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -364,11 +364,6 @@
|
|||||||
</datalist>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template x-if="clientModal.mode === 'group'">
|
|
||||||
<div class="notice tone-warning">
|
|
||||||
<span>{{ t.configs_group_rotation_warning }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button class="secondary" @click="closeClientModal()">{{ t.admin_close }}</button>
|
<button class="secondary" @click="closeClientModal()">{{ t.admin_close }}</button>
|
||||||
<button @click="clientModal.mode === 'create' ? createClient() : changeClientGroup()" :disabled="busy">{{ t.settings_save }}</button>
|
<button @click="clientModal.mode === 'create' ? createClient() : changeClientGroup()" :disabled="busy">{{ t.settings_save }}</button>
|
||||||
@@ -842,8 +837,6 @@ function configsPage() {
|
|||||||
this.clientModal = null;
|
this.clientModal = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!confirm('{{ t.configs_group_rotation_confirm }}')) return;
|
|
||||||
|
|
||||||
this.clearNotice();
|
this.clearNotice();
|
||||||
this.busy = true;
|
this.busy = true;
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user