Restore proxy endpoint setting
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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
|
||||
|
||||
11
src/app.css
11
src/app.css
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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: 'Сервис',
|
||||
|
||||
Reference in New Issue
Block a user