Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"dotenv": "^17.2.3",
"lucide-react": "^0.465.0",
"moment": "^2.30.1",
"next": "14.2.15",
"next": "14.2.35",
"next-auth": "^4.24.8",
"next-themes": "^0.3.0",
"node-schedule": "^2.1.1",
Expand Down
42 changes: 42 additions & 0 deletions prisma/migrations/20260106154856_migration/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_App" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"appType" TEXT NOT NULL DEFAULT 'APP',
"projectId" TEXT NOT NULL,
"sourceType" TEXT NOT NULL DEFAULT 'GIT',
"containerImageSource" TEXT,
"containerRegistryUsername" TEXT,
"containerRegistryPassword" TEXT,
"gitUrl" TEXT,
"gitBranch" TEXT,
"gitUsername" TEXT,
"gitToken" TEXT,
"dockerfilePath" TEXT NOT NULL DEFAULT './Dockerfile',
"replicas" INTEGER NOT NULL DEFAULT 1,
"envVars" TEXT NOT NULL DEFAULT '',
"memoryReservation" INTEGER,
"memoryLimit" INTEGER,
"cpuReservation" INTEGER,
"cpuLimit" INTEGER,
"webhookId" TEXT,
"ingressNetworkPolicy" TEXT NOT NULL DEFAULT 'ALLOW_ALL',
"egressNetworkPolicy" TEXT NOT NULL DEFAULT 'ALLOW_ALL',
"useNetworkPolicy" BOOLEAN NOT NULL DEFAULT true,
"healthChechHttpGetPath" TEXT,
"healthCheckHttpScheme" TEXT,
"healthCheckHttpHeadersJson" TEXT,
"healthCheckHttpPort" INTEGER,
"healthCheckPeriodSeconds" INTEGER NOT NULL DEFAULT 10,
"healthCheckTimeoutSeconds" INTEGER NOT NULL DEFAULT 5,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "App_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_App" ("appType", "containerImageSource", "containerRegistryPassword", "containerRegistryUsername", "cpuLimit", "cpuReservation", "createdAt", "dockerfilePath", "egressNetworkPolicy", "envVars", "gitBranch", "gitToken", "gitUrl", "gitUsername", "id", "ingressNetworkPolicy", "memoryLimit", "memoryReservation", "name", "projectId", "replicas", "sourceType", "updatedAt", "useNetworkPolicy", "webhookId") SELECT "appType", "containerImageSource", "containerRegistryPassword", "containerRegistryUsername", "cpuLimit", "cpuReservation", "createdAt", "dockerfilePath", "egressNetworkPolicy", "envVars", "gitBranch", "gitToken", "gitUrl", "gitUsername", "id", "ingressNetworkPolicy", "memoryLimit", "memoryReservation", "name", "projectId", "replicas", "sourceType", "updatedAt", "useNetworkPolicy", "webhookId" FROM "App";
DROP TABLE "App";
ALTER TABLE "new_App" RENAME TO "App";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
2 changes: 2 additions & 0 deletions prisma/migrations/20260108164941_migration/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "App" ADD COLUMN "healthCheckTcpPort" INTEGER;
44 changes: 44 additions & 0 deletions prisma/migrations/20260130074309_migration/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_App" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"appType" TEXT NOT NULL DEFAULT 'APP',
"projectId" TEXT NOT NULL,
"sourceType" TEXT NOT NULL DEFAULT 'GIT',
"containerImageSource" TEXT,
"containerRegistryUsername" TEXT,
"containerRegistryPassword" TEXT,
"gitUrl" TEXT,
"gitBranch" TEXT,
"gitUsername" TEXT,
"gitToken" TEXT,
"dockerfilePath" TEXT NOT NULL DEFAULT './Dockerfile',
"replicas" INTEGER NOT NULL DEFAULT 1,
"envVars" TEXT NOT NULL DEFAULT '',
"memoryReservation" INTEGER,
"memoryLimit" INTEGER,
"cpuReservation" INTEGER,
"cpuLimit" INTEGER,
"webhookId" TEXT,
"ingressNetworkPolicy" TEXT NOT NULL DEFAULT 'ALLOW_ALL',
"egressNetworkPolicy" TEXT NOT NULL DEFAULT 'ALLOW_ALL',
"useNetworkPolicy" BOOLEAN NOT NULL DEFAULT true,
"healthChechHttpGetPath" TEXT,
"healthCheckHttpScheme" TEXT,
"healthCheckHttpHeadersJson" TEXT,
"healthCheckHttpPort" INTEGER,
"healthCheckPeriodSeconds" INTEGER NOT NULL DEFAULT 15,
"healthCheckTimeoutSeconds" INTEGER NOT NULL DEFAULT 5,
"healthCheckFailureThreshold" INTEGER NOT NULL DEFAULT 3,
"healthCheckTcpPort" INTEGER,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "App_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_App" ("appType", "containerImageSource", "containerRegistryPassword", "containerRegistryUsername", "cpuLimit", "cpuReservation", "createdAt", "dockerfilePath", "egressNetworkPolicy", "envVars", "gitBranch", "gitToken", "gitUrl", "gitUsername", "healthChechHttpGetPath", "healthCheckHttpHeadersJson", "healthCheckHttpPort", "healthCheckHttpScheme", "healthCheckPeriodSeconds", "healthCheckTcpPort", "healthCheckTimeoutSeconds", "id", "ingressNetworkPolicy", "memoryLimit", "memoryReservation", "name", "projectId", "replicas", "sourceType", "updatedAt", "useNetworkPolicy", "webhookId") SELECT "appType", "containerImageSource", "containerRegistryPassword", "containerRegistryUsername", "cpuLimit", "cpuReservation", "createdAt", "dockerfilePath", "egressNetworkPolicy", "envVars", "gitBranch", "gitToken", "gitUrl", "gitUsername", "healthChechHttpGetPath", "healthCheckHttpHeadersJson", "healthCheckHttpPort", "healthCheckHttpScheme", "healthCheckPeriodSeconds", "healthCheckTcpPort", "healthCheckTimeoutSeconds", "id", "ingressNetworkPolicy", "memoryLimit", "memoryReservation", "name", "projectId", "replicas", "sourceType", "updatedAt", "useNetworkPolicy", "webhookId" FROM "App";
DROP TABLE "App";
ALTER TABLE "new_App" RENAME TO "App";
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
11 changes: 11 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ model App {
egressNetworkPolicy String @default("ALLOW_ALL") // ALLOW_ALL, NAMESPACE_ONLY, DENY_ALL, INTERNET_ONLY
useNetworkPolicy Boolean @default(true)

// healthCheck startupProbe, readinessProbe, livenessProbe
healthChechHttpGetPath String?
healthCheckHttpScheme String? // HTTP, HTTPS
healthCheckHttpHeadersJson String? // JSON stringified key-value pairs
healthCheckHttpPort Int?
healthCheckPeriodSeconds Int @default(15)
healthCheckTimeoutSeconds Int @default(5)
healthCheckFailureThreshold Int @default(3)

healthCheckTcpPort Int?

appDomains AppDomain[]
appPorts AppPort[]
appVolumes AppVolume[]
Expand Down
26 changes: 23 additions & 3 deletions src/app/project/[projectId]/project-network-graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import React, { useMemo } from 'react';
import { ReactFlow, Background, Controls, Node, Edge, MarkerType, Handle, Position } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { App, AppDomain, AppPort } from '@prisma/client';
import { Globe, Network, Lock, Cloud, Shield, ArrowDown } from 'lucide-react';
import { Globe, Network, Lock, Cloud, Shield, ArrowDown, HeartPulse } from 'lucide-react';
import PodStatusIndicator from '@/components/custom/pod-status-indicator';
import { useRouter } from 'next/navigation';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

interface AppWithRelations extends App {
appPorts: AppPort[];
Expand Down Expand Up @@ -58,7 +59,16 @@ const PolicyIcon = ({ policy, type, ports, useNetworkPolicy }: { policy: string,
);
};

const AppNode = ({ data }: { data: any }) => {
const AppNode = ({ data }: {
data: {
label: string;
ingressPolicy: string;
egressPolicy: string;
appId: string;
app: AppWithRelations;
ports: string;
}
}) => {
return (
<div className="relative bg-white border border-slate-300 rounded-md p-4 min-w-[150px] shadow-sm text-center cursor-pointer hover:border-slate-400 transition-colors">
<Handle type="target" position={Position.Top} className="!bg-transparent !border-0" />
Expand All @@ -67,8 +77,18 @@ const AppNode = ({ data }: { data: any }) => {
<PolicyIcon policy={data.ingressPolicy} ports={data.ports} useNetworkPolicy={data.app.useNetworkPolicy} type="ingress" />
</div>

<div className="font-semibold text-sm mt-2 mb-2 flex gap-3 items-center justify-center">
<div className="font-semibold text-sm mt-2 mb-2 flex gap-2 items-center justify-center">
<PodStatusIndicator appId={data.appId} /> <p>{data.label}</p>
{(!!data.app.healthChechHttpGetPath || !!data.app.healthCheckTcpPort) && <TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<HeartPulse size={16} className="text-blue-500" />
</TooltipTrigger>
<TooltipContent>
<p>Healthchecks enabled for this App</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>}
</div>

<div className="absolute -bottom-3 left-1/2 -translate-x-1/2 z-10">
Expand Down
57 changes: 57 additions & 0 deletions src/app/project/app/[appId]/advanced/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import appService from "@/server/services/app.service";
import { isAuthorizedWriteForApp, saveFormAction, simpleAction } from "@/server/utils/action-wrapper.utils";
import { BasicAuthEditModel, basicAuthEditZodModel } from "@/shared/model/basic-auth-edit.model";
import { appNetworkPolicy } from "@/shared/model/network-policy.model";
import { HealthCheckModel, healthCheckZodModel } from "./health-check.model";


export const saveBasicAuth = async (prevState: any, inputData: BasicAuthEditModel) =>
Expand Down Expand Up @@ -43,3 +44,59 @@ export const saveNetworkPolicy = async (appId: string, ingressPolicy: string, eg
});
return new SuccessActionResult(undefined, 'Network policy saved');
});

export const saveHealthCheck = async (prevState: any, inputData: HealthCheckModel) =>
saveFormAction(inputData, healthCheckZodModel, async (validatedData) => {
await isAuthorizedWriteForApp(validatedData.appId);

const app = await appService.getById(validatedData.appId);

// Prepare update data
let updateData: Partial<typeof app> = {
healthCheckPeriodSeconds: validatedData.periodSeconds,
healthCheckTimeoutSeconds: validatedData.timeoutSeconds,
healthCheckFailureThreshold: validatedData.failureThreshold,
};

if (validatedData.enabled) {
if (validatedData.probeType === 'HTTP') {
updateData = {
...updateData,
healthChechHttpGetPath: validatedData.path || null,
healthCheckHttpPort: validatedData.httpPort || null,
healthCheckHttpScheme: validatedData.scheme || null,
healthCheckHttpHeadersJson: validatedData.headers && validatedData.headers.length > 0
? JSON.stringify(validatedData.headers)
: null,
healthCheckTcpPort: null // Clear TCP when using HTTP
};
} else if (validatedData.probeType === 'TCP') {
updateData = {
...updateData,
healthCheckTcpPort: validatedData.tcpPort || null,
// Clear HTTP fields when using TCP
healthChechHttpGetPath: null,
healthCheckHttpPort: null,
healthCheckHttpScheme: null,
healthCheckHttpHeadersJson: null
};
}
} else {
// Clear all probe fields when disabled
updateData = {
...updateData,
healthChechHttpGetPath: null,
healthCheckHttpPort: null,
healthCheckHttpScheme: null,
healthCheckHttpHeadersJson: null,
healthCheckTcpPort: null
};
}

await appService.save({
...app,
...updateData
});

return new SuccessActionResult(undefined, 'Health check settings saved');
});
Loading