|
1 | 1 | import { z } from "zod"; |
| 2 | +import { NO_SEP_CHAR_REGEX, UNIT_SEP_CHAR } from "./common"; |
2 | 3 |
|
3 | 4 | export const StatusSchema = z.discriminatedUnion("type", [ |
4 | | - z.object({ type: z.literal("starting"), data: z.object({}) }), |
5 | | - z.object({ type: z.literal("running"), data: z.object({}) }), |
6 | | - z.object({ type: z.literal("finishing"), data: z.object({}) }), |
7 | | - z.object({ type: z.literal("converting"), data: z.object({}) }), |
8 | | - z.object({ type: z.literal("uploading"), data: z.object({}) }), |
9 | | - z.object({ type: z.literal("failure"), data: z.object({ reason: z.string() }) }), |
10 | | - z.object({ type: z.literal("success"), data: z.object({ buildId: z.string() }) }), |
| 5 | + z.object({ type: z.literal("starting"), data: z.object({}) }), |
| 6 | + z.object({ type: z.literal("running"), data: z.object({}) }), |
| 7 | + z.object({ type: z.literal("finishing"), data: z.object({}) }), |
| 8 | + z.object({ type: z.literal("converting"), data: z.object({}) }), |
| 9 | + z.object({ type: z.literal("uploading"), data: z.object({}) }), |
| 10 | + z.object({ type: z.literal("failure"), data: z.object({ reason: z.string() }) }), |
| 11 | + z.object({ type: z.literal("success"), data: z.object({ buildId: z.string() }) }), |
11 | 12 | ]); |
12 | 13 |
|
13 | 14 | export type Status = z.infer<typeof StatusSchema>; |
14 | 15 |
|
| 16 | +const ILLEGAL_BUILD_ARG_KEY = /[\s'"\\]/g; |
| 17 | +const BuildArgsSchema = z.string() |
| 18 | + .transform((str) => JSON.parse(str)) |
| 19 | + .pipe(z.array(z.string())) |
| 20 | + .refine((arr) => { |
| 21 | + // Check each key=value pair to ensure keys have no spaces |
| 22 | + return arr.every(item => { |
| 23 | + const [key] = item.split('='); |
| 24 | + if (!key) return false; |
| 25 | + if (ILLEGAL_BUILD_ARG_KEY.test(key)) return false; |
| 26 | + if (item.includes(UNIT_SEP_CHAR)) return false; |
| 27 | + return true; |
| 28 | + }); |
| 29 | + }, { message: "Argument key/value contains invalid character" }) |
| 30 | + .transform((arr) => { |
| 31 | + const result: Record<string, string> = Object.create(null); |
| 32 | + // Convert array of strings to an object |
| 33 | + for (const item of arr) { |
| 34 | + const [key, ...valueParts] = item.split('='); |
| 35 | + const value = valueParts.join('='); |
| 36 | + |
| 37 | + if (key && value !== undefined) { |
| 38 | + result[key] = value; |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + return result; |
| 43 | + }); |
| 44 | + |
15 | 45 | export const BuildRequestSchema = z.object({ |
16 | | - buildName: z.string(), |
17 | | - dockerfilePath: z.string(), |
18 | | - environmentId: z.string(), |
| 46 | + buildName: z.string() |
| 47 | + .regex(NO_SEP_CHAR_REGEX, "buildName cannot contain special characters"), |
| 48 | + dockerfilePath: z.string() |
| 49 | + .regex(NO_SEP_CHAR_REGEX, "dockerfilePath cannot contain special characters"), |
| 50 | + environmentId: z.string() |
| 51 | + .regex(NO_SEP_CHAR_REGEX, "environmentId cannot contain special characters"), |
| 52 | + buildArgs: BuildArgsSchema, |
| 53 | + buildTarget: z.string() |
| 54 | + .regex(NO_SEP_CHAR_REGEX, "buildTarget cannot contain special characters") |
| 55 | + .optional(), |
| 56 | + context: z.instanceof(File) |
19 | 57 | }); |
20 | 58 |
|
21 | 59 | export type BuildRequest = z.infer<typeof BuildRequestSchema>; |
22 | 60 |
|
23 | 61 | export const BuildEventSchema = z.discriminatedUnion("type", [ |
24 | | - z.object({ type: z.literal("status"), data: StatusSchema }), |
25 | | - z.object({ type: z.literal("log"), data: z.object({ line: z.string() }) }), |
| 62 | + z.object({ type: z.literal("status"), data: StatusSchema }), |
| 63 | + z.object({ type: z.literal("log"), data: z.object({ line: z.string() }) }), |
26 | 64 | ]); |
27 | 65 |
|
28 | 66 | export type BuildEvent = z.infer<typeof BuildEventSchema>; |
29 | 67 |
|
30 | 68 | export interface BuildInfo { |
31 | | - id: string; |
32 | | - status: Status; |
33 | | - buildName?: string; |
34 | | - dockerfilePath?: string; |
35 | | - environmentId?: string; |
36 | | - contextPath?: string; |
37 | | - outputPath?: string; |
38 | | - events: BuildEvent[]; |
39 | | - containerProcess?: any; |
40 | | - createdAt: Date; |
41 | | - downloadedAt?: Date; |
42 | | - cleanupTimeout?: NodeJS.Timeout; |
| 69 | + id: string; |
| 70 | + status: Status; |
| 71 | + buildName: string; |
| 72 | + dockerfilePath: string; |
| 73 | + environmentId: string; |
| 74 | + contextPath: string; |
| 75 | + buildArgs: Record<string, string>; |
| 76 | + buildTarget?: string; |
| 77 | + outputPath: string; |
| 78 | + events: BuildEvent[]; |
| 79 | + containerProcess?: any; |
| 80 | + createdAt: Date; |
| 81 | + downloadedAt?: Date; |
| 82 | + cleanupTimeout?: NodeJS.Timeout; |
43 | 83 | } |
0 commit comments