add build env and path ingress controls
This commit is contained in:
+118
-1
@@ -22,6 +22,7 @@ import {
|
||||
CreateRuntimeEnvVariableDto,
|
||||
CreateRuntimeIngressRuleDto,
|
||||
EnsureRuntimeNamespaceDto,
|
||||
ReplaceBuildEnvVariablesDto,
|
||||
} from './dto';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
@@ -43,6 +44,9 @@ export class AppsService {
|
||||
components: {
|
||||
orderBy: { name: 'asc' },
|
||||
include: {
|
||||
buildVariables: {
|
||||
orderBy: { key: 'asc' },
|
||||
},
|
||||
buildJobs: {
|
||||
where: { status: 'SUCCEEDED' },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
@@ -194,6 +198,56 @@ export class AppsService {
|
||||
return { component };
|
||||
}
|
||||
|
||||
async listComponentBuildEnv(componentId: string) {
|
||||
const component = await this.prisma.appComponent.findUnique({
|
||||
where: { id: componentId },
|
||||
include: {
|
||||
buildVariables: {
|
||||
orderBy: { key: 'asc' },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!component) {
|
||||
throw new NotFoundException('앱 구성요소를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
return { variables: component.buildVariables };
|
||||
}
|
||||
|
||||
async replaceComponentBuildEnv(
|
||||
componentId: string,
|
||||
body: ReplaceBuildEnvVariablesDto,
|
||||
) {
|
||||
const component = await this.prisma.appComponent.findUnique({
|
||||
where: { id: componentId },
|
||||
});
|
||||
|
||||
if (!component) {
|
||||
throw new NotFoundException('앱 구성요소를 찾을 수 없습니다.');
|
||||
}
|
||||
|
||||
const variables = normalizeBuildEnvVariables(body.variables ?? []);
|
||||
|
||||
await this.prisma.$transaction([
|
||||
this.prisma.buildEnvVariable.deleteMany({ where: { componentId } }),
|
||||
...(variables.length
|
||||
? [
|
||||
this.prisma.buildEnvVariable.createMany({
|
||||
data: variables.map((variable) => ({
|
||||
componentId,
|
||||
key: variable.key,
|
||||
value: variable.value,
|
||||
isSecret: variable.isSecret,
|
||||
})),
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]);
|
||||
|
||||
return this.listComponentBuildEnv(componentId);
|
||||
}
|
||||
|
||||
listReleases(appKey?: string) {
|
||||
return this.prisma.appRelease.findMany({
|
||||
where: appKey ? { app: { key: appKey } } : undefined,
|
||||
@@ -415,7 +469,21 @@ export class AppsService {
|
||||
if (runtimeProfile?.ingressRules.length) {
|
||||
for (const rule of runtimeProfile.ingressRules) {
|
||||
const serviceName = sanitizeK8sName(rule.component.name);
|
||||
const ingressName = sanitizeK8sName(`${rule.component.name}-${rule.host}`);
|
||||
const ingressName = sanitizeK8sName(`${rule.component.name}-${rule.host}-${rule.path}`);
|
||||
const middlewareName = rule.stripPrefix ? sanitizeK8sName(`${ingressName}-strip`) : null;
|
||||
|
||||
if (middlewareName) {
|
||||
logs.push(`[apply] middleware/${middlewareName}`);
|
||||
appendOutput(
|
||||
logs,
|
||||
await this.applyK8sManifest(createTraefikStripPrefixMiddlewareManifest({
|
||||
namespace,
|
||||
name: middlewareName,
|
||||
prefix: rule.path,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
logs.push(`[apply] ingress/${ingressName}`);
|
||||
appendOutput(
|
||||
logs,
|
||||
@@ -426,6 +494,7 @@ export class AppsService {
|
||||
path: rule.path,
|
||||
serviceName,
|
||||
servicePort: rule.servicePort,
|
||||
middlewareName,
|
||||
tlsEnabled: rule.tlsEnabled,
|
||||
tlsSecretName: rule.tlsSecretName,
|
||||
})),
|
||||
@@ -875,6 +944,7 @@ export class AppsService {
|
||||
host,
|
||||
path: pathValue,
|
||||
servicePort: body.servicePort,
|
||||
stripPrefix: Boolean(body.stripPrefix),
|
||||
tlsEnabled: Boolean(body.tlsEnabled),
|
||||
tlsSecretName: body.tlsSecretName?.trim() || null,
|
||||
},
|
||||
@@ -1367,6 +1437,24 @@ function normalizeOptionalString(value?: string | null) {
|
||||
return trimmed || undefined;
|
||||
}
|
||||
|
||||
function normalizeBuildEnvVariables(
|
||||
variables: Array<{ key?: string; value?: string; isSecret?: boolean }>,
|
||||
) {
|
||||
const byKey = new Map<string, { key: string; value: string; isSecret: boolean }>();
|
||||
|
||||
for (const variable of variables) {
|
||||
const key = variable.key?.trim();
|
||||
if (!key) continue;
|
||||
byKey.set(key, {
|
||||
key,
|
||||
value: variable.value ?? '',
|
||||
isSecret: variable.isSecret ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
return [...byKey.values()].sort((left, right) => left.key.localeCompare(right.key));
|
||||
}
|
||||
|
||||
function normalizeImageSource(value: string | null | undefined, componentType?: string) {
|
||||
const normalized = value?.trim().toUpperCase();
|
||||
if (normalized === 'EXTERNAL' || normalized === 'DATABASE') return normalized;
|
||||
@@ -1952,9 +2040,16 @@ function createIngressManifest(input: {
|
||||
path: string;
|
||||
serviceName: string;
|
||||
servicePort: number;
|
||||
middlewareName?: string | null;
|
||||
tlsEnabled: boolean;
|
||||
tlsSecretName?: string | null;
|
||||
}) {
|
||||
const annotations: Record<string, string> = {};
|
||||
|
||||
if (input.middlewareName) {
|
||||
annotations['traefik.ingress.kubernetes.io/router.middlewares'] = `${input.namespace}-${input.middlewareName}@kubernetescrd`;
|
||||
}
|
||||
|
||||
const manifest: Record<string, unknown> = {
|
||||
apiVersion: 'networking.k8s.io/v1',
|
||||
kind: 'Ingress',
|
||||
@@ -1962,6 +2057,7 @@ function createIngressManifest(input: {
|
||||
name: input.name,
|
||||
namespace: input.namespace,
|
||||
labels: getManagedLabels(input.name, 'ingress'),
|
||||
...(Object.keys(annotations).length > 0 ? { annotations } : {}),
|
||||
},
|
||||
spec: {
|
||||
rules: [
|
||||
@@ -1998,6 +2094,27 @@ function createIngressManifest(input: {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
function createTraefikStripPrefixMiddlewareManifest(input: {
|
||||
namespace: string;
|
||||
name: string;
|
||||
prefix: string;
|
||||
}) {
|
||||
return {
|
||||
apiVersion: 'traefik.io/v1alpha1',
|
||||
kind: 'Middleware',
|
||||
metadata: {
|
||||
name: input.name,
|
||||
namespace: input.namespace,
|
||||
labels: getManagedLabels(input.name, 'ingress-middleware'),
|
||||
},
|
||||
spec: {
|
||||
stripPrefix: {
|
||||
prefixes: [input.prefix],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createPostgresSecretManifest(input: {
|
||||
namespace: string;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user