refactor: remove unused remarks and add quick domain create

Drop remark fields from Zone, Zone domains, proxy routes, WAF rule
groups and IP groups across models, APIs, UI and DB columns (keep
certificate/origin remarks). Add quick-create domain input for short
labels, @ apex and full FQDNs when binding domains.
This commit is contained in:
ryan
2026-07-12 16:16:56 +08:00
parent 8afd103751
commit d615d85a26
43 changed files with 616 additions and 433 deletions
@@ -0,0 +1,28 @@
import {describe, expect, it} from 'vitest';
import {resolveZoneDomainInput} from '@/app/(main)/websites/components/resolve-zone-domain-input';
describe('resolveZoneDomainInput', () => {
it('maps short label and apex @ under zone root', () => {
expect(resolveZoneDomainInput('api', 'example.com')).toEqual({
domain: 'api.example.com',
});
expect(resolveZoneDomainInput('@', 'example.com')).toEqual({
domain: 'example.com',
});
});
it('accepts full FQDN under the zone', () => {
expect(resolveZoneDomainInput('www.api.example.com', 'example.com')).toEqual({
domain: 'www.api.example.com',
});
expect(resolveZoneDomainInput('example.com', 'example.com')).toEqual({
domain: 'example.com',
});
});
it('rejects foreign domains and wildcards', () => {
expect(resolveZoneDomainInput('evil.com', 'example.com').error).toBeTruthy();
expect(resolveZoneDomainInput('*.example.com', 'example.com').error).toBeTruthy();
});
});
@@ -31,7 +31,6 @@ describe('WebsitesPage', () => {
{
id: 42,
domain: 'arctel.de',
remark: '生产站点',
domain_count: 3,
created_at: '',
updated_at: '',
@@ -39,7 +38,6 @@ describe('WebsitesPage', () => {
{
id: 43,
domain: 'example.com',
remark: '',
domain_count: 0,
created_at: '',
updated_at: '',
@@ -1,3 +1,4 @@
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {describe, expect, it, vi} from 'vitest';
@@ -12,7 +13,7 @@ vi.mock('next/link', () => ({
}));
const zones: ZoneItem[] = [
{id: 1, domain: 'arctel.de', remark: '', created_at: '', updated_at: ''},
{id: 1, domain: 'arctel.de', created_at: '', updated_at: ''},
];
const domains: ZoneDomainItem[] = [
@@ -22,7 +23,6 @@ const domains: ZoneDomainItem[] = [
proxy_route_id: null,
domain: 'api.arctel.de',
cert_id: 9,
remark: '',
created_at: '',
updated_at: '',
},
@@ -32,18 +32,22 @@ const domains: ZoneDomainItem[] = [
proxy_route_id: 99,
domain: 'bound.arctel.de',
cert_id: null,
remark: '',
created_at: '',
updated_at: '',
},
];
function renderSelector(ui: React.ReactElement) {
const client = new QueryClient({defaultOptions: {queries: {retry: false}}});
return render(<QueryClientProvider client={client}>{ui}</QueryClientProvider>);
}
describe('ZoneDomainSelector', () => {
it('renders domains and toggles selection', async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(
renderSelector(
<ZoneDomainSelector
value={[7]}
onChange={onChange}
@@ -53,29 +57,41 @@ describe('ZoneDomainSelector', () => {
);
expect(screen.getByText('api.arctel.de')).toBeVisible();
expect(screen.getByText('bound.arctel.de')).toBeVisible();
expect(screen.getByText('路由 #99')).toBeVisible();
// Bound to another route — hidden by default
expect(screen.queryByText('bound.arctel.de')).not.toBeInTheDocument();
expect(screen.getByRole('button', {name: /快捷新增域名/})).toBeVisible();
// Deselect already selected domain
await user.click(screen.getByText('api.arctel.de'));
expect(onChange).toHaveBeenCalledWith([]);
});
it('does not select domains bound to another route', async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(
it('hides domains bound to another route', () => {
renderSelector(
<ZoneDomainSelector
value={[]}
onChange={onChange}
onChange={vi.fn()}
domains={domains}
zones={zones}
currentRouteId={1}
/>,
);
await user.click(screen.getByText('bound.arctel.de'));
expect(onChange).not.toHaveBeenCalled();
expect(screen.getByText('api.arctel.de')).toBeVisible();
expect(screen.queryByText('bound.arctel.de')).not.toBeInTheDocument();
});
it('still shows domains already bound to the current route', () => {
renderSelector(
<ZoneDomainSelector
value={[8]}
onChange={vi.fn()}
domains={domains}
zones={zones}
currentRouteId={99}
/>,
);
expect(screen.getByText('bound.arctel.de')).toBeVisible();
expect(screen.getByText('api.arctel.de')).toBeVisible();
});
});
+1 -1
View File
@@ -62,7 +62,7 @@ describe('ZonePageClient', () => {
it('loads the overview by stable ID and exposes all tabs including empty domains', async () => {
vi.mocked(ZoneService.getOverview).mockImplementation(async () => ({
zone: {id: 42, domain: 'arctel.de', remark: '', created_at: '', updated_at: ''},
zone: {id: 42, domain: 'arctel.de', created_at: '', updated_at: ''},
domains: [],
}));