blob: 90523d5c567dbc494e4cabbba218427d01606b2f [file] [log] [blame]
gioc31bf142025-06-16 07:48:20 +00001import { z } from "zod";
2import { Node } from "@xyflow/react";
3import { Domain, ServiceType, VolumeType } from "./types.js";
4
5export const serviceAnalyzisSchema = z.object({
6 name: z.string(),
7 location: z.string(),
8 configVars: z.array(
9 z.object({
10 name: z.string(),
11 category: z.enum(["CommandLineFlag", "EnvironmentVariable"]),
12 type: z.optional(z.enum(["String", "Number", "Boolean"])),
13 semanticType: z.optional(
14 z.enum([
15 "EXPANDED_ENV_VAR",
16 "PORT",
17 "FILESYSTEM_PATH",
18 "DATABASE_URL",
19 "SQLITE_PATH",
20 "POSTGRES_URL",
21 "POSTGRES_PASSWORD",
22 "POSTGRES_USER",
23 "POSTGRES_DB",
24 "POSTGRES_PORT",
25 "POSTGRES_HOST",
26 "POSTGRES_SSL",
27 "MONGO_URL",
28 "MONGO_PASSWORD",
29 "MONGO_USER",
30 "MONGO_DB",
31 "MONGO_PORT",
32 "MONGO_HOST",
33 "MONGO_SSL",
34 ]),
35 ),
36 }),
37 ),
38});
39
40export type BoundEnvVar =
41 | {
42 id: string;
43 source: string | null;
44 }
45 | {
46 id: string;
47 source: string | null;
48 name: string;
49 isEditting: boolean;
50 }
51 | {
52 id: string;
53 source: string | null;
54 name: string;
55 alias: string;
56 isEditting: boolean;
57 }
58 | {
59 id: string;
60 source: string | null;
61 portId: string;
62 name: string;
63 alias: string;
64 isEditting: boolean;
65 };
66
67export type EnvVar = {
68 name: string;
69 value: string;
70};
71
72export type InitData = {
73 label: string;
74 envVars: BoundEnvVar[];
75 ports: Port[];
76};
77
78export type NodeData = InitData & {
79 activeField?: string | undefined;
80 state?: string | null;
81};
82
83export type PortConnectedTo = {
84 serviceId: string;
85 portId: string;
86};
87
88export type NetworkData = NodeData & {
89 domain: string;
90};
91
92export type NetworkNode = Node<NetworkData> & {
93 type: "network";
94};
95
96export type GatewayHttpsData = NodeData & {
97 readonly?: boolean;
98 network?: string;
99 subdomain?: string;
100 https?: PortConnectedTo;
101 auth?: {
102 enabled: boolean;
103 groups: string[];
104 noAuthPathPatterns: string[];
105 };
106};
107
108export type GatewayHttpsNode = Node<GatewayHttpsData> & {
109 type: "gateway-https";
110};
111
112export type GatewayTCPData = NodeData & {
113 readonly?: boolean;
114 network?: string;
115 subdomain?: string;
116 exposed: PortConnectedTo[];
117 selected?: {
118 serviceId?: string;
119 portId?: string;
120 };
121};
122
123export type GatewayTCPNode = Node<GatewayTCPData> & {
124 type: "gateway-tcp";
125};
126
127export type Port = {
128 id: string;
129 name: string;
130 value: number;
131};
132
133export type ServiceData = NodeData & {
134 type: ServiceType;
135 repository?:
136 | {
137 id: number;
138 repoNodeId: string;
139 }
140 | {
141 id: number;
142 repoNodeId: string;
143 branch: string;
144 }
145 | {
146 id: number;
147 repoNodeId: string;
148 branch: string;
149 rootDir: string;
150 };
151 env: string[];
152 volume: string[];
153 preBuildCommands: string;
154 isChoosingPortToConnect: boolean;
155 dev?:
156 | {
157 enabled: false;
158 expose?: Domain;
159 }
160 | {
161 enabled: true;
162 expose?: Domain;
163 codeServerNodeId: string;
164 sshNodeId: string;
165 };
gio69ff7592025-07-03 06:27:21 +0000166 model?: {
167 name: "gemini" | "claude";
168 apiKey?: string;
gio69148322025-06-19 23:16:12 +0400169 };
gioc31bf142025-06-16 07:48:20 +0000170 info?: z.infer<typeof serviceAnalyzisSchema>;
171};
172
173export type ServiceNode = Node<ServiceData> & {
174 type: "app";
175};
176
177export type VolumeData = NodeData & {
178 type: VolumeType;
179 size: string;
180 attachedTo: string[];
181};
182
183export type VolumeNode = Node<VolumeData> & {
184 type: "volume";
185};
186
187export type PostgreSQLData = NodeData & {
188 volumeId: string;
189};
190
191export type PostgreSQLNode = Node<PostgreSQLData> & {
192 type: "postgresql";
193};
194
195export type MongoDBData = NodeData & {
196 volumeId: string;
197};
198
199export type MongoDBNode = Node<MongoDBData> & {
200 type: "mongodb";
201};
202
203export type GithubData = NodeData & {
204 repository?: {
205 id: number;
206 sshURL: string;
207 fullName: string;
208 };
209};
210
211export type GithubNode = Node<GithubData> & {
212 type: "github";
213};
214
215export type NANode = Node<NodeData> & {
216 type: undefined;
217};
218
219export type AppNode =
220 | NetworkNode
221 | GatewayHttpsNode
222 | GatewayTCPNode
223 | ServiceNode
224 | VolumeNode
225 | PostgreSQLNode
226 | MongoDBNode
227 | GithubNode
228 | NANode;
229
230export type NodeType = Exclude<Pick<AppNode, "type">["type"], undefined>;
231
232export const networkSchema = z.object({
233 name: z.string().min(1),
234 domain: z.string().min(1),
235 hasAuth: z.boolean(),
236});
237
238export type Network = z.infer<typeof networkSchema>;
239
240export const accessSchema = z.discriminatedUnion("type", [
241 z.object({
242 type: z.literal("https"),
243 name: z.string(),
244 address: z.string(),
giocc5ce582025-06-25 07:45:21 +0400245 agentName: z.string().optional(),
gioc31bf142025-06-16 07:48:20 +0000246 }),
247 z.object({
248 type: z.literal("ssh"),
249 name: z.string(),
250 host: z.string(),
251 port: z.number(),
252 }),
253 z.object({
254 type: z.literal("tcp"),
255 name: z.string(),
256 host: z.string(),
257 port: z.number(),
258 }),
259 z.object({
260 type: z.literal("udp"),
261 name: z.string(),
262 host: z.string(),
263 port: z.number(),
264 }),
265 z.object({
266 type: z.literal("postgresql"),
267 name: z.string(),
268 host: z.string(),
269 port: z.number(),
270 database: z.string(),
271 username: z.string(),
272 password: z.string(),
273 }),
274 z.object({
275 type: z.literal("mongodb"),
276 name: z.string(),
277 host: z.string(),
278 port: z.number(),
279 database: z.string(),
280 username: z.string(),
281 password: z.string(),
282 }),
283]);
284
285export const serviceInfoSchema = z.object({
286 name: z.string(),
287 workers: z.array(
288 z.object({
289 id: z.string(),
gioa70535a2025-07-02 15:50:25 +0000290 commit: z
291 .object({
gioc31bf142025-06-16 07:48:20 +0000292 hash: z.string(),
293 message: z.string(),
gioa70535a2025-07-02 15:50:25 +0000294 })
295 .nullable()
296 .optional(),
297 commands: z
298 .array(
gioc31bf142025-06-16 07:48:20 +0000299 z.object({
300 command: z.string(),
301 state: z.string(),
302 }),
gioa70535a2025-07-02 15:50:25 +0000303 )
304 .optional(),
gioc31bf142025-06-16 07:48:20 +0000305 }),
306 ),
307});
308
309export const envSchema = z.object({
gio69ff7592025-07-03 06:27:21 +0000310 deployKeyPublic: z.string().optional(),
311 instanceId: z.string().optional(),
312 networks: z.array(
313 z.object({
314 name: z.string(),
315 domain: z.string(),
316 hasAuth: z.boolean(),
317 }),
318 ),
gioc31bf142025-06-16 07:48:20 +0000319 integrations: z.object({
320 github: z.boolean(),
gio69148322025-06-19 23:16:12 +0400321 gemini: z.boolean(),
gio69ff7592025-07-03 06:27:21 +0000322 anthropic: z.boolean(),
gioc31bf142025-06-16 07:48:20 +0000323 }),
324 services: z.array(serviceInfoSchema),
325 user: z.object({
326 id: z.string(),
327 username: z.string(),
328 }),
329 access: z.array(accessSchema),
330});
331
332export type ServiceInfo = z.infer<typeof serviceInfoSchema>;
333export type Env = z.infer<typeof envSchema>;
giocc5ce582025-06-25 07:45:21 +0400334export type Access = z.infer<typeof accessSchema>;