Merge pull request #1617 from Dokploy/fix/cover-edge-cases-processor-template

fix(templates): add optional chaining to prevent errors when accessin…
This commit is contained in:
Mauricio Siu 2025-04-03 00:22:44 -06:00 committed by GitHub
commit 131a1acbbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 50 additions and 24 deletions

View File

@ -307,7 +307,7 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
> >
{templates?.map((template) => ( {templates?.map((template) => (
<div <div
key={template.id} key={template?.id}
className={cn( className={cn(
"flex flex-col border rounded-lg overflow-hidden relative", "flex flex-col border rounded-lg overflow-hidden relative",
viewMode === "icon" && "h-[200px]", viewMode === "icon" && "h-[200px]",
@ -315,7 +315,7 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
)} )}
> >
<Badge className="absolute top-2 right-2" variant="blue"> <Badge className="absolute top-2 right-2" variant="blue">
{template.version} {template?.version}
</Badge> </Badge>
<div <div
className={cn( className={cn(
@ -324,21 +324,21 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
)} )}
> >
<img <img
src={`${customBaseUrl || "https://templates.dokploy.com/"}/blueprints/${template.id}/${template.logo}`} src={`${customBaseUrl || "https://templates.dokploy.com/"}/blueprints/${template?.id}/${template?.logo}`}
className={cn( className={cn(
"object-contain", "object-contain",
viewMode === "detailed" ? "size-24" : "size-16", viewMode === "detailed" ? "size-24" : "size-16",
)} )}
alt={template.name} alt={template?.name}
/> />
<div className="flex flex-col items-center gap-2"> <div className="flex flex-col items-center gap-2">
<span className="text-sm font-medium line-clamp-1"> <span className="text-sm font-medium line-clamp-1">
{template.name} {template?.name}
</span> </span>
{viewMode === "detailed" && {viewMode === "detailed" &&
template.tags.length > 0 && ( template?.tags?.length > 0 && (
<div className="flex flex-wrap justify-center gap-1.5"> <div className="flex flex-wrap justify-center gap-1.5">
{template.tags.map((tag) => ( {template?.tags?.map((tag) => (
<Badge <Badge
key={tag} key={tag}
variant="green" variant="green"
@ -356,7 +356,7 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
{viewMode === "detailed" && ( {viewMode === "detailed" && (
<ScrollArea className="flex-1 p-6"> <ScrollArea className="flex-1 p-6">
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">
{template.description} {template?.description}
</div> </div>
</ScrollArea> </ScrollArea>
)} )}
@ -372,25 +372,27 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
> >
{viewMode === "detailed" && ( {viewMode === "detailed" && (
<div className="flex gap-2"> <div className="flex gap-2">
<Link {template?.links?.github && (
href={template.links.github}
target="_blank"
className="text-muted-foreground hover:text-foreground transition-colors"
>
<GithubIcon className="size-5" />
</Link>
{template.links.website && (
<Link <Link
href={template.links.website} href={template?.links?.github}
target="_blank"
className="text-muted-foreground hover:text-foreground transition-colors"
>
<GithubIcon className="size-5" />
</Link>
)}
{template?.links?.website && (
<Link
href={template?.links?.website}
target="_blank" target="_blank"
className="text-muted-foreground hover:text-foreground transition-colors" className="text-muted-foreground hover:text-foreground transition-colors"
> >
<Globe className="size-5" /> <Globe className="size-5" />
</Link> </Link>
)} )}
{template.links.docs && ( {template?.links?.docs && (
<Link <Link
href={template.links.docs} href={template?.links?.docs}
target="_blank" target="_blank"
className="text-muted-foreground hover:text-foreground transition-colors" className="text-muted-foreground hover:text-foreground transition-colors"
> >
@ -419,7 +421,7 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription> <AlertDialogDescription>
This will create an application from the{" "} This will create an application from the{" "}
{template.name} template and add it to your {template?.name} template and add it to your
project. project.
</AlertDialogDescription> </AlertDialogDescription>

View File

@ -1,3 +1,4 @@
import { faker } from "@faker-js/faker";
import type { Schema } from "./index"; import type { Schema } from "./index";
import { import {
generateBase64, generateBase64,
@ -70,7 +71,7 @@ function processValue(
schema: Schema, schema: Schema,
): string { ): string {
// First replace utility functions // First replace utility functions
let processedValue = value.replace(/\${([^}]+)}/g, (match, varName) => { let processedValue = value?.replace(/\${([^}]+)}/g, (match, varName) => {
// Handle utility functions // Handle utility functions
if (varName === "domain") { if (varName === "domain") {
return generateRandomDomain(schema); return generateRandomDomain(schema);
@ -117,6 +118,14 @@ function processValue(
return generateJwt(length); return generateJwt(length);
} }
if (varName === "username") {
return faker.internet.userName().toLowerCase();
}
if (varName === "email") {
return faker.internet.email().toLowerCase();
}
// If not a utility function, try to get from variables // If not a utility function, try to get from variables
return variables[varName] || match; return variables[varName] || match;
}); });
@ -177,7 +186,14 @@ export function processDomains(
variables: Record<string, string>, variables: Record<string, string>,
schema: Schema, schema: Schema,
): Template["domains"] { ): Template["domains"] {
if (!template?.config?.domains) return []; if (
!template?.config?.domains ||
template.config.domains.length === 0 ||
template.config.domains.every((domain) => !domain.serviceName)
) {
return [];
}
return template?.config?.domains?.map((domain: DomainConfig) => ({ return template?.config?.domains?.map((domain: DomainConfig) => ({
...domain, ...domain,
host: domain.host host: domain.host
@ -194,7 +210,9 @@ export function processEnvVars(
variables: Record<string, string>, variables: Record<string, string>,
schema: Schema, schema: Schema,
): Template["envs"] { ): Template["envs"] {
if (!template?.config?.env) return []; if (!template?.config?.env || Object.keys(template.config.env).length === 0) {
return [];
}
// Handle array of env vars // Handle array of env vars
if (Array.isArray(template.config.env)) { if (Array.isArray(template.config.env)) {
@ -233,7 +251,13 @@ export function processMounts(
variables: Record<string, string>, variables: Record<string, string>,
schema: Schema, schema: Schema,
): Template["mounts"] { ): Template["mounts"] {
if (!template?.config?.mounts) return []; if (
!template?.config?.mounts ||
template.config.mounts.length === 0 ||
template.config.mounts.every((mount) => !mount.filePath && !mount.content)
) {
return [];
}
return template?.config?.mounts?.map((mount: MountConfig) => ({ return template?.config?.mounts?.map((mount: MountConfig) => ({
filePath: processValue(mount.filePath, variables, schema), filePath: processValue(mount.filePath, variables, schema),