blob: c36dbcf11796bbe9a41033a1b4c7aba358cc8df3 [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;
gio1dacf1c2025-07-03 16:39:04 +000065 }
66 | {
67 id: string;
68 source: null;
69 name: string;
70 value: string;
71 isEditting?: boolean;
gioc31bf142025-06-16 07:48:20 +000072 };
73
74export type EnvVar = {
75 name: string;
76 value: string;
77};
78
79export type InitData = {
80 label: string;
81 envVars: BoundEnvVar[];
82 ports: Port[];
83};
84
85export type NodeData = InitData & {
86 activeField?: string | undefined;
87 state?: string | null;
88};
89
90export type PortConnectedTo = {
91 serviceId: string;
92 portId: string;
93};
94
95export type NetworkData = NodeData & {
96 domain: string;
97};
98
99export type NetworkNode = Node<NetworkData> & {
100 type: "network";
101};
102
103export type GatewayHttpsData = NodeData & {
104 readonly?: boolean;
105 network?: string;
106 subdomain?: string;
107 https?: PortConnectedTo;
108 auth?: {
109 enabled: boolean;
110 groups: string[];
111 noAuthPathPatterns: string[];
112 };
113};
114
115export type GatewayHttpsNode = Node<GatewayHttpsData> & {
116 type: "gateway-https";
117};
118
119export type GatewayTCPData = NodeData & {
120 readonly?: boolean;
121 network?: string;
122 subdomain?: string;
123 exposed: PortConnectedTo[];
124 selected?: {
125 serviceId?: string;
126 portId?: string;
127 };
128};
129
130export type GatewayTCPNode = Node<GatewayTCPData> & {
131 type: "gateway-tcp";
132};
133
134export type Port = {
135 id: string;
136 name: string;
137 value: number;
138};
139
140export type ServiceData = NodeData & {
141 type: ServiceType;
142 repository?:
143 | {
144 id: number;
145 repoNodeId: string;
146 }
147 | {
148 id: number;
149 repoNodeId: string;
150 branch: string;
151 }
152 | {
153 id: number;
154 repoNodeId: string;
155 branch: string;
156 rootDir: string;
157 };
158 env: string[];
159 volume: string[];
160 preBuildCommands: string;
161 isChoosingPortToConnect: boolean;
162 dev?:
163 | {
164 enabled: false;
165 expose?: Domain;
166 }
167 | {
168 enabled: true;
169 expose?: Domain;
170 codeServerNodeId: string;
171 sshNodeId: string;
172 };
gio69ff7592025-07-03 06:27:21 +0000173 model?: {
174 name: "gemini" | "claude";
175 apiKey?: string;
gio69148322025-06-19 23:16:12 +0400176 };
gioc31bf142025-06-16 07:48:20 +0000177 info?: z.infer<typeof serviceAnalyzisSchema>;
178};
179
180export type ServiceNode = Node<ServiceData> & {
181 type: "app";
182};
183
184export type VolumeData = NodeData & {
185 type: VolumeType;
186 size: string;
187 attachedTo: string[];
188};
189
190export type VolumeNode = Node<VolumeData> & {
191 type: "volume";
192};
193
194export type PostgreSQLData = NodeData & {
195 volumeId: string;
196};
197
198export type PostgreSQLNode = Node<PostgreSQLData> & {
199 type: "postgresql";
200};
201
202export type MongoDBData = NodeData & {
203 volumeId: string;
204};
205
206export type MongoDBNode = Node<MongoDBData> & {
207 type: "mongodb";
208};
209
210export type GithubData = NodeData & {
211 repository?: {
212 id: number;
213 sshURL: string;
214 fullName: string;
215 };
216};
217
218export type GithubNode = Node<GithubData> & {
219 type: "github";
220};
221
222export type NANode = Node<NodeData> & {
223 type: undefined;
224};
225
226export type AppNode =
227 | NetworkNode
228 | GatewayHttpsNode
229 | GatewayTCPNode
230 | ServiceNode
231 | VolumeNode
232 | PostgreSQLNode
233 | MongoDBNode
234 | GithubNode
235 | NANode;
236
237export type NodeType = Exclude<Pick<AppNode, "type">["type"], undefined>;
238
239export const networkSchema = z.object({
240 name: z.string().min(1),
241 domain: z.string().min(1),
242 hasAuth: z.boolean(),
243});
244
245export type Network = z.infer<typeof networkSchema>;
246
247export const accessSchema = z.discriminatedUnion("type", [
248 z.object({
249 type: z.literal("https"),
250 name: z.string(),
251 address: z.string(),
giocc5ce582025-06-25 07:45:21 +0400252 agentName: z.string().optional(),
gioc31bf142025-06-16 07:48:20 +0000253 }),
254 z.object({
255 type: z.literal("ssh"),
256 name: z.string(),
257 host: z.string(),
258 port: z.number(),
259 }),
260 z.object({
261 type: z.literal("tcp"),
262 name: z.string(),
263 host: z.string(),
264 port: z.number(),
265 }),
266 z.object({
267 type: z.literal("udp"),
268 name: z.string(),
269 host: z.string(),
270 port: z.number(),
271 }),
272 z.object({
273 type: z.literal("postgresql"),
274 name: z.string(),
275 host: z.string(),
276 port: z.number(),
277 database: z.string(),
278 username: z.string(),
279 password: z.string(),
280 }),
281 z.object({
282 type: z.literal("mongodb"),
283 name: z.string(),
284 host: z.string(),
285 port: z.number(),
286 database: z.string(),
287 username: z.string(),
288 password: z.string(),
289 }),
290]);
291
292export const serviceInfoSchema = z.object({
293 name: z.string(),
294 workers: z.array(
295 z.object({
296 id: z.string(),
gioa70535a2025-07-02 15:50:25 +0000297 commit: z
298 .object({
gioc31bf142025-06-16 07:48:20 +0000299 hash: z.string(),
300 message: z.string(),
gioa70535a2025-07-02 15:50:25 +0000301 })
302 .nullable()
303 .optional(),
304 commands: z
305 .array(
gioc31bf142025-06-16 07:48:20 +0000306 z.object({
307 command: z.string(),
308 state: z.string(),
309 }),
gioa70535a2025-07-02 15:50:25 +0000310 )
311 .optional(),
gioc31bf142025-06-16 07:48:20 +0000312 }),
313 ),
314});
315
316export const envSchema = z.object({
gio69ff7592025-07-03 06:27:21 +0000317 deployKeyPublic: z.string().optional(),
318 instanceId: z.string().optional(),
319 networks: z.array(
320 z.object({
321 name: z.string(),
322 domain: z.string(),
323 hasAuth: z.boolean(),
324 }),
325 ),
gioc31bf142025-06-16 07:48:20 +0000326 integrations: z.object({
327 github: z.boolean(),
gio69148322025-06-19 23:16:12 +0400328 gemini: z.boolean(),
gio69ff7592025-07-03 06:27:21 +0000329 anthropic: z.boolean(),
gioc31bf142025-06-16 07:48:20 +0000330 }),
331 services: z.array(serviceInfoSchema),
332 user: z.object({
333 id: z.string(),
334 username: z.string(),
335 }),
336 access: z.array(accessSchema),
337});
338
339export type ServiceInfo = z.infer<typeof serviceInfoSchema>;
340export type Env = z.infer<typeof envSchema>;
giocc5ce582025-06-25 07:45:21 +0400341export type Access = z.infer<typeof accessSchema>;
gio74c6f752025-07-05 04:10:58 +0000342export type AgentAccess = Required<Extract<Access, { type: "https" }>>;