mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-06 04:19:37 +00:00
feat: added SPA option for static sites
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@@ -63,10 +64,11 @@ const mySchema = z.discriminatedUnion("buildType", [
|
|||||||
publishDirectory: z.string().optional(),
|
publishDirectory: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
buildType: z.literal(BuildType.static),
|
buildType: z.literal(BuildType.railpack),
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
buildType: z.literal(BuildType.railpack),
|
buildType: z.literal(BuildType.static),
|
||||||
|
isStaticSpa: z.boolean().default(false),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ interface ApplicationData {
|
|||||||
dockerBuildStage?: string | null;
|
dockerBuildStage?: string | null;
|
||||||
herokuVersion?: string | null;
|
herokuVersion?: string | null;
|
||||||
publishDirectory?: string | null;
|
publishDirectory?: string | null;
|
||||||
|
isStaticSpa?: boolean | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidBuildType(value: string): value is BuildType {
|
function isValidBuildType(value: string): value is BuildType {
|
||||||
@@ -115,6 +118,7 @@ const resetData = (data: ApplicationData): AddTemplate => {
|
|||||||
case BuildType.static:
|
case BuildType.static:
|
||||||
return {
|
return {
|
||||||
buildType: BuildType.static,
|
buildType: BuildType.static,
|
||||||
|
isStaticSpa: data.isStaticSpa ?? false,
|
||||||
};
|
};
|
||||||
case BuildType.railpack:
|
case BuildType.railpack:
|
||||||
return {
|
return {
|
||||||
@@ -174,6 +178,8 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
|
|||||||
data.buildType === BuildType.heroku_buildpacks
|
data.buildType === BuildType.heroku_buildpacks
|
||||||
? data.herokuVersion
|
? data.herokuVersion
|
||||||
: null,
|
: null,
|
||||||
|
isStaticSpa:
|
||||||
|
data.buildType === BuildType.static ? data.isStaticSpa : null,
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Build type saved");
|
toast.success("Build type saved");
|
||||||
@@ -364,6 +370,30 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{buildType === BuildType.static && (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="isStaticSpa"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<div className="flex items-center gap-x-2 p-2">
|
||||||
|
<Checkbox
|
||||||
|
id="checkboxIsStaticSpa"
|
||||||
|
value={String(field.value)}
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<FormLabel htmlFor="checkboxIsStaticSpa">
|
||||||
|
Single Page Application (SPA)
|
||||||
|
</FormLabel>
|
||||||
|
</div>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className="flex w-full justify-end">
|
<div className="flex w-full justify-end">
|
||||||
<Button isLoading={isLoading} type="submit">
|
<Button isLoading={isLoading} type="submit">
|
||||||
Save
|
Save
|
||||||
|
|||||||
1
apps/dokploy/drizzle/0092_stiff_the_watchers.sql
Normal file
1
apps/dokploy/drizzle/0092_stiff_the_watchers.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "application" ADD COLUMN "isStaticSpa" boolean;
|
||||||
5717
apps/dokploy/drizzle/meta/0092_snapshot.json
Normal file
5717
apps/dokploy/drizzle/meta/0092_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -645,6 +645,13 @@
|
|||||||
"when": 1746518402168,
|
"when": 1746518402168,
|
||||||
"tag": "0091_spotty_kulan_gath",
|
"tag": "0091_spotty_kulan_gath",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 92,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1747713229160,
|
||||||
|
"tag": "0092_stiff_the_watchers",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -330,6 +330,7 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
dockerContextPath: input.dockerContextPath,
|
dockerContextPath: input.dockerContextPath,
|
||||||
dockerBuildStage: input.dockerBuildStage,
|
dockerBuildStage: input.dockerBuildStage,
|
||||||
herokuVersion: input.herokuVersion,
|
herokuVersion: input.herokuVersion,
|
||||||
|
isStaticSpa: input.isStaticSpa,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ export const applications = pgTable("application", {
|
|||||||
buildType: buildType("buildType").notNull().default("nixpacks"),
|
buildType: buildType("buildType").notNull().default("nixpacks"),
|
||||||
herokuVersion: text("herokuVersion").default("24"),
|
herokuVersion: text("herokuVersion").default("24"),
|
||||||
publishDirectory: text("publishDirectory"),
|
publishDirectory: text("publishDirectory"),
|
||||||
|
isStaticSpa: boolean('isStaticSpa'),
|
||||||
createdAt: text("createdAt")
|
createdAt: text("createdAt")
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => new Date().toISOString()),
|
.$defaultFn(() => new Date().toISOString()),
|
||||||
@@ -409,6 +410,7 @@ const createSchema = createInsertSchema(applications, {
|
|||||||
]),
|
]),
|
||||||
herokuVersion: z.string().optional(),
|
herokuVersion: z.string().optional(),
|
||||||
publishDirectory: z.string().optional(),
|
publishDirectory: z.string().optional(),
|
||||||
|
isStaticSpa: z.boolean().optional(),
|
||||||
owner: z.string(),
|
owner: z.string(),
|
||||||
healthCheckSwarm: HealthCheckSwarmSchema.nullable(),
|
healthCheckSwarm: HealthCheckSwarmSchema.nullable(),
|
||||||
restartPolicySwarm: RestartPolicySwarmSchema.nullable(),
|
restartPolicySwarm: RestartPolicySwarmSchema.nullable(),
|
||||||
@@ -461,7 +463,7 @@ export const apiSaveBuildType = createSchema
|
|||||||
herokuVersion: true,
|
herokuVersion: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required()
|
||||||
.merge(createSchema.pick({ publishDirectory: true }));
|
.merge(createSchema.pick({ publishDirectory: true, isStaticSpa: true }));
|
||||||
|
|
||||||
export const apiSaveGithubProvider = createSchema
|
export const apiSaveGithubProvider = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
|||||||
@@ -7,21 +7,62 @@ import type { ApplicationNested } from ".";
|
|||||||
import { createFile, getCreateFileCommand } from "../docker/utils";
|
import { createFile, getCreateFileCommand } from "../docker/utils";
|
||||||
import { getBuildAppDirectory } from "../filesystem/directory";
|
import { getBuildAppDirectory } from "../filesystem/directory";
|
||||||
|
|
||||||
|
const nginxSpaConfig = `
|
||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
location / {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html index.htm;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const buildStatic = async (
|
export const buildStatic = async (
|
||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
writeStream: WriteStream,
|
writeStream: WriteStream,
|
||||||
) => {
|
) => {
|
||||||
const { publishDirectory } = application;
|
const { publishDirectory, isStaticSpa } = application;
|
||||||
const buildAppDirectory = getBuildAppDirectory(application);
|
const buildAppDirectory = getBuildAppDirectory(application);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (isStaticSpa) {
|
||||||
|
createFile(
|
||||||
|
buildAppDirectory,
|
||||||
|
"nginx.conf",
|
||||||
|
nginxSpaConfig,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createFile(
|
||||||
|
buildAppDirectory,
|
||||||
|
".dockerignore",
|
||||||
|
[".git", ".env", "Dockerfile", ".dockerignore"].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
createFile(
|
createFile(
|
||||||
buildAppDirectory,
|
buildAppDirectory,
|
||||||
"Dockerfile",
|
"Dockerfile",
|
||||||
[
|
[
|
||||||
"FROM nginx:alpine",
|
"FROM nginx:alpine",
|
||||||
"WORKDIR /usr/share/nginx/html/",
|
"WORKDIR /usr/share/nginx/html/",
|
||||||
|
isStaticSpa ? 'COPY nginx.conf /etc/nginx/nginx.conf' : '',
|
||||||
`COPY ${publishDirectory || "."} .`,
|
`COPY ${publishDirectory || "."} .`,
|
||||||
|
'CMD ["nginx", "-g", "daemon off;"]'
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
6084
pnpm-lock.yaml
generated
6084
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user