Restore proxy endpoint setting

This commit is contained in:
2026-04-02 01:42:45 +03:00
parent 19ac2966c8
commit 0ae46d22d2
6 changed files with 32 additions and 7 deletions

View File

@@ -35,3 +35,4 @@ Updated: 2026-04-02
19. Added service-removal confirmation with linked-user warnings, backend cascade deletion for removed services, and migration that strips persisted legacy `admin` services from stored state.
20. Made `npm run dev` start both the Vite client and Express backend, added a Vite API proxy for local development, and restored `system` as the default panel theme so the login screen follows OS appearance.
21. Re-separated the Settings tab into distinct panel-settings and services cards so panel preferences no longer appear inside the Services section.
22. Restored editable proxy endpoint in panel settings so copied proxy URLs and displayed user endpoints can be corrected from the UI.

View File

@@ -24,7 +24,7 @@ Updated: 2026-04-02
- `src/main.tsx`: application bootstrap
- `src/App.tsx`: authenticated panel shell with API-backed login, `sessionStorage` token persistence, localized labels, early theme application, and protected panel mutations
- `src/SystemTab.tsx`: Settings tab with separate panel-settings and services cards, unified service type editing, remove confirmation, and generated config preview
- `src/SystemTab.tsx`: Settings tab with separate panel-settings and services cards, editable proxy endpoint, unified service type editing, remove confirmation, and generated config preview
- `src/App.test.tsx`: login-gate, preferences persistence, modal interaction, pause/resume, delete-confirm, and settings-save UI tests
- `src/app.css`: full panel styling
- `src/data/mockDashboard.ts`: default panel state and frontend fallback snapshot

View File

@@ -142,7 +142,7 @@ describe('App login gate', () => {
expect(screen.queryByRole('dialog', { name: /delete user/i })).not.toBeInTheDocument();
});
it('uses a combined service type field in settings and applies saved ports to the local fallback state', async () => {
it('uses a combined service type field in settings and applies saved proxy endpoint values to the local fallback state', async () => {
const user = userEvent.setup();
render(<App />);
@@ -151,12 +151,15 @@ describe('App login gate', () => {
expect(screen.getByRole('heading', { name: /panel settings/i })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: /^services$/i })).toBeInTheDocument();
expect(screen.queryByLabelText(/public host/i)).not.toBeInTheDocument();
expect(screen.getByLabelText(/proxy endpoint/i)).toBeInTheDocument();
expect(screen.queryByLabelText(/command/i)).not.toBeInTheDocument();
expect(screen.queryByLabelText(/protocol/i)).not.toBeInTheDocument();
expect(screen.getAllByLabelText(/type/i)).toHaveLength(3);
expect(screen.queryByRole('option', { name: /admin/i })).not.toBeInTheDocument();
await user.clear(screen.getByLabelText(/proxy endpoint/i));
await user.type(screen.getByLabelText(/proxy endpoint/i), 'gw.example.net');
const firstPortInput = screen.getAllByLabelText(/port/i)[0];
await user.clear(firstPortInput);
await user.type(firstPortInput, '1180');
@@ -164,7 +167,7 @@ describe('App login gate', () => {
await user.click(screen.getByRole('button', { name: /save settings/i }));
await user.click(screen.getByRole('button', { name: /users/i }));
expect(screen.getAllByText(/edge\.example\.net:1180/i).length).toBeGreaterThan(0);
expect(screen.getAllByText(/gw\.example\.net:1180/i).length).toBeGreaterThan(0);
});
it('warns before deleting a service and removes linked users after confirmation', async () => {

View File

@@ -75,7 +75,19 @@ export default function SystemTab({
<div className="card-header">
<h2>{text.settings.panelTitle}</h2>
</div>
<div className="settings-toolbar">
<div className="panel-settings-grid">
<label className="field-group panel-settings-wide">
<span>{text.settings.proxyHost}</span>
<input
value={draft.publicHost}
onChange={(event) =>
setDraft((current) => ({
...current,
publicHost: event.target.value,
}))
}
/>
</label>
<label className="field-group compact-field">
<span>{text.common.language}</span>
<select

View File

@@ -450,13 +450,19 @@ button,
color: var(--muted);
}
.settings-toolbar {
.settings-toolbar,
.panel-settings-grid {
display: flex;
align-items: end;
gap: 12px;
flex-wrap: wrap;
}
.panel-settings-wide {
min-width: 280px;
flex: 1 1 320px;
}
.compact-field {
min-width: 220px;
flex: 0 1 240px;
@@ -708,7 +714,8 @@ pre {
.shell-header,
.table-toolbar,
.settings-toolbar {
.settings-toolbar,
.panel-settings-grid {
flex-direction: column;
align-items: flex-start;
}

View File

@@ -93,6 +93,7 @@ const text = {
},
settings: {
panelTitle: 'Panel settings',
proxyHost: 'Proxy endpoint',
title: 'Services',
generatedConfig: 'Generated config',
serviceLabel: 'Service',
@@ -209,6 +210,7 @@ const text = {
},
settings: {
panelTitle: 'Настройки панели',
proxyHost: 'Точка входа для прокси',
title: 'Сервисы',
generatedConfig: 'Сгенерированный конфиг',
serviceLabel: 'Сервис',