Skip to content

Commit f01a901

Browse files
committed
feat(ui): Add multi-domain and multi-database support in compose mode
- Add DatabaseConfig interface to types - Add advanced options in Compose Mode for multi-domain/database - Add dynamic wizard steps based on advanced options selection - Add Domains step for routing domains to different services - Add multi-database UI in Database step with add/remove support - Display database configs in DeploymentDetailView - Update handleCreate to send domains and databases arrays - Add comprehensive type tests for new interfaces Signed-off-by: nfebe <fenn25.fn@gmail.com>
1 parent 0317e4a commit f01a901

4 files changed

Lines changed: 1144 additions & 14 deletions

File tree

src/__tests__/types.test.ts

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
import { describe, it, expect } from "vitest";
2+
import type { DatabaseConfig, DomainConfig, ServiceMetadata } from "@/types";
3+
4+
describe("DatabaseConfig type", () => {
5+
it("should have all required fields", () => {
6+
const db: DatabaseConfig = {
7+
id: "test-db",
8+
alias: "primary",
9+
type: "mysql",
10+
mode: "shared",
11+
};
12+
13+
expect(db.id).toBe("test-db");
14+
expect(db.alias).toBe("primary");
15+
expect(db.type).toBe("mysql");
16+
expect(db.mode).toBe("shared");
17+
});
18+
19+
it("should support all database types", () => {
20+
const types: DatabaseConfig["type"][] = ["mysql", "postgres", "mariadb", "mongodb", "redis"];
21+
22+
types.forEach((type) => {
23+
const db: DatabaseConfig = {
24+
id: `test-${type}`,
25+
alias: "test",
26+
type,
27+
mode: "shared",
28+
};
29+
expect(db.type).toBe(type);
30+
});
31+
});
32+
33+
it("should support all connection modes", () => {
34+
const modes: DatabaseConfig["mode"][] = ["shared", "create", "existing", "external"];
35+
36+
modes.forEach((mode) => {
37+
const db: DatabaseConfig = {
38+
id: `test-${mode}`,
39+
alias: "test",
40+
type: "mysql",
41+
mode,
42+
};
43+
expect(db.mode).toBe(mode);
44+
});
45+
});
46+
47+
it("should support optional fields", () => {
48+
const db: DatabaseConfig = {
49+
id: "full-config",
50+
alias: "analytics",
51+
type: "postgres",
52+
mode: "external",
53+
service: "backend",
54+
host: "db.example.com",
55+
port: 5432,
56+
container: "postgres-container",
57+
database_name: "analytics_db",
58+
username: "analyst",
59+
env_prefix: "ANALYTICS",
60+
is_shared: false,
61+
};
62+
63+
expect(db.service).toBe("backend");
64+
expect(db.host).toBe("db.example.com");
65+
expect(db.port).toBe(5432);
66+
expect(db.database_name).toBe("analytics_db");
67+
expect(db.env_prefix).toBe("ANALYTICS");
68+
expect(db.is_shared).toBe(false);
69+
});
70+
});
71+
72+
describe("DomainConfig type", () => {
73+
it("should have all required fields", () => {
74+
const domain: DomainConfig = {
75+
id: "test-domain",
76+
service: "frontend",
77+
container_port: 80,
78+
domain: "example.com",
79+
ssl: {
80+
enabled: true,
81+
auto_cert: true,
82+
},
83+
};
84+
85+
expect(domain.id).toBe("test-domain");
86+
expect(domain.service).toBe("frontend");
87+
expect(domain.container_port).toBe(80);
88+
expect(domain.domain).toBe("example.com");
89+
expect(domain.ssl.enabled).toBe(true);
90+
});
91+
92+
it("should support optional fields", () => {
93+
const domain: DomainConfig = {
94+
id: "api-domain",
95+
service: "api",
96+
container_port: 8080,
97+
domain: "api.example.com",
98+
path_prefix: "/v1",
99+
strip_prefix: true,
100+
ssl: {
101+
enabled: true,
102+
auto_cert: true,
103+
},
104+
aliases: ["api-v1.example.com"],
105+
};
106+
107+
expect(domain.path_prefix).toBe("/v1");
108+
expect(domain.strip_prefix).toBe(true);
109+
expect(domain.aliases).toContain("api-v1.example.com");
110+
});
111+
});
112+
113+
describe("ServiceMetadata with databases", () => {
114+
it("should support empty databases array", () => {
115+
const metadata: ServiceMetadata = {
116+
name: "test-app",
117+
type: "web",
118+
networking: {
119+
expose: true,
120+
domain: "test.example.com",
121+
container_port: 80,
122+
protocol: "http",
123+
},
124+
ssl: {
125+
enabled: true,
126+
auto_cert: true,
127+
},
128+
healthcheck: {
129+
path: "/health",
130+
interval: "30s",
131+
},
132+
};
133+
134+
expect(metadata.databases).toBeUndefined();
135+
});
136+
137+
it("should support single database", () => {
138+
const metadata: ServiceMetadata = {
139+
name: "test-app",
140+
type: "web",
141+
networking: {
142+
expose: true,
143+
domain: "test.example.com",
144+
container_port: 80,
145+
protocol: "http",
146+
},
147+
ssl: {
148+
enabled: true,
149+
auto_cert: true,
150+
},
151+
healthcheck: {
152+
path: "/health",
153+
interval: "30s",
154+
},
155+
databases: [
156+
{
157+
id: "primary",
158+
alias: "primary",
159+
type: "mysql",
160+
mode: "shared",
161+
is_shared: true,
162+
},
163+
],
164+
};
165+
166+
expect(metadata.databases).toHaveLength(1);
167+
expect(metadata.databases![0].alias).toBe("primary");
168+
});
169+
170+
it("should support multiple databases", () => {
171+
const metadata: ServiceMetadata = {
172+
name: "complex-app",
173+
type: "web",
174+
networking: {
175+
expose: true,
176+
domain: "app.example.com",
177+
container_port: 80,
178+
protocol: "http",
179+
},
180+
ssl: {
181+
enabled: true,
182+
auto_cert: true,
183+
},
184+
healthcheck: {
185+
path: "/health",
186+
interval: "30s",
187+
},
188+
databases: [
189+
{
190+
id: "primary",
191+
alias: "primary",
192+
type: "mysql",
193+
mode: "shared",
194+
is_shared: true,
195+
},
196+
{
197+
id: "cache",
198+
alias: "cache",
199+
type: "redis",
200+
mode: "existing",
201+
container: "redis-server",
202+
},
203+
{
204+
id: "analytics",
205+
alias: "analytics",
206+
type: "postgres",
207+
mode: "external",
208+
host: "analytics.db.example.com",
209+
port: 5432,
210+
},
211+
],
212+
};
213+
214+
expect(metadata.databases).toHaveLength(3);
215+
expect(metadata.databases![0].type).toBe("mysql");
216+
expect(metadata.databases![1].type).toBe("redis");
217+
expect(metadata.databases![2].type).toBe("postgres");
218+
});
219+
});
220+
221+
describe("ServiceMetadata with domains", () => {
222+
it("should support multiple domains", () => {
223+
const metadata: ServiceMetadata = {
224+
name: "multi-domain-app",
225+
type: "web",
226+
networking: {
227+
expose: true,
228+
domain: "app.example.com",
229+
container_port: 80,
230+
protocol: "http",
231+
},
232+
ssl: {
233+
enabled: true,
234+
auto_cert: true,
235+
},
236+
healthcheck: {
237+
path: "/health",
238+
interval: "30s",
239+
},
240+
domains: [
241+
{
242+
id: "primary",
243+
service: "frontend",
244+
container_port: 80,
245+
domain: "app.example.com",
246+
ssl: { enabled: true, auto_cert: true },
247+
},
248+
{
249+
id: "api",
250+
service: "api",
251+
container_port: 8080,
252+
domain: "api.example.com",
253+
path_prefix: "/v1",
254+
ssl: { enabled: true, auto_cert: true },
255+
},
256+
{
257+
id: "admin",
258+
service: "admin",
259+
container_port: 3000,
260+
domain: "admin.example.com",
261+
ssl: { enabled: true, auto_cert: true },
262+
},
263+
],
264+
};
265+
266+
expect(metadata.domains).toHaveLength(3);
267+
expect(metadata.domains![0].domain).toBe("app.example.com");
268+
expect(metadata.domains![1].domain).toBe("api.example.com");
269+
expect(metadata.domains![2].domain).toBe("admin.example.com");
270+
});
271+
});

0 commit comments

Comments
 (0)