2026-01-16 12:32:36 +07:00
|
|
|
import type * as k8s from "@kubernetes/client-node";
|
|
|
|
|
import type { ReverseProxyServerType } from "@minikura/db";
|
|
|
|
|
import { LABEL_PREFIX } from "../config/constants";
|
|
|
|
|
import { calculateJavaMemory, convertToK8sFormat } from "../utils/memory";
|
|
|
|
|
import type { ReverseProxyConfig } from "../types";
|
2025-05-12 12:34:09 +07:00
|
|
|
|
|
|
|
|
export async function createReverseProxyServer(
|
|
|
|
|
server: ReverseProxyConfig,
|
|
|
|
|
appsApi: k8s.AppsV1Api,
|
|
|
|
|
coreApi: k8s.CoreV1Api,
|
2026-02-13 15:52:13 +07:00
|
|
|
_networkingApi: k8s.NetworkingV1Api,
|
2025-05-12 12:34:09 +07:00
|
|
|
namespace: string
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
console.log(`Creating reverse proxy server ${server.id} in namespace '${namespace}'`);
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
const serverType = server.type.toLowerCase();
|
|
|
|
|
const serverName = `${serverType}-${server.id}`;
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
const configMap = {
|
2026-01-16 12:32:36 +07:00
|
|
|
apiVersion: "v1",
|
|
|
|
|
kind: "ConfigMap",
|
2025-05-12 12:34:09 +07:00
|
|
|
metadata: {
|
|
|
|
|
name: `${serverName}-config`,
|
|
|
|
|
namespace: namespace,
|
|
|
|
|
labels: {
|
|
|
|
|
app: serverName,
|
|
|
|
|
[`${LABEL_PREFIX}/server-type`]: serverType,
|
|
|
|
|
[`${LABEL_PREFIX}/proxy-id`]: server.id,
|
2026-01-16 12:32:36 +07:00
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
data: {
|
2026-01-16 12:32:36 +07:00
|
|
|
"minikura-api-key": server.apiKey,
|
|
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
};
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
try {
|
2026-02-13 15:52:13 +07:00
|
|
|
await coreApi.createNamespacedConfigMap({ namespace, body: configMap });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Created ConfigMap for reverse proxy server ${server.id}`);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error.response?.statusCode === 409) {
|
2026-02-13 15:52:13 +07:00
|
|
|
await coreApi.replaceNamespacedConfigMap({ name: `${serverName}-config`, namespace, body: configMap });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Updated ConfigMap for reverse proxy server ${server.id}`);
|
|
|
|
|
} else {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
const service = {
|
2026-01-16 12:32:36 +07:00
|
|
|
apiVersion: "v1",
|
|
|
|
|
kind: "Service",
|
2025-05-12 12:34:09 +07:00
|
|
|
metadata: {
|
|
|
|
|
name: serverName,
|
|
|
|
|
namespace: namespace,
|
|
|
|
|
labels: {
|
|
|
|
|
app: serverName,
|
|
|
|
|
[`${LABEL_PREFIX}/server-type`]: serverType,
|
|
|
|
|
[`${LABEL_PREFIX}/proxy-id`]: server.id,
|
2026-01-16 12:32:36 +07:00
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
spec: {
|
|
|
|
|
selector: {
|
|
|
|
|
app: serverName,
|
|
|
|
|
},
|
|
|
|
|
ports: [
|
|
|
|
|
{
|
|
|
|
|
port: server.external_port,
|
|
|
|
|
targetPort: server.listen_port,
|
2026-01-16 12:32:36 +07:00
|
|
|
protocol: "TCP",
|
|
|
|
|
name: "minecraft",
|
|
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
],
|
2026-01-16 12:32:36 +07:00
|
|
|
type: "LoadBalancer",
|
|
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
};
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
try {
|
2026-02-13 15:52:13 +07:00
|
|
|
await coreApi.createNamespacedService({ namespace, body: service });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Created Service for reverse proxy server ${server.id}`);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error.response?.statusCode === 409) {
|
2026-02-13 15:52:13 +07:00
|
|
|
await coreApi.replaceNamespacedService({ name: serverName, namespace, body: service });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Updated Service for reverse proxy server ${server.id}`);
|
|
|
|
|
} else {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
const deployment = {
|
2026-01-16 12:32:36 +07:00
|
|
|
apiVersion: "apps/v1",
|
|
|
|
|
kind: "Deployment",
|
2025-05-12 12:34:09 +07:00
|
|
|
metadata: {
|
|
|
|
|
name: serverName,
|
|
|
|
|
namespace: namespace,
|
|
|
|
|
labels: {
|
|
|
|
|
app: serverName,
|
|
|
|
|
[`${LABEL_PREFIX}/server-type`]: serverType,
|
|
|
|
|
[`${LABEL_PREFIX}/proxy-id`]: server.id,
|
2026-01-16 12:32:36 +07:00
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
spec: {
|
|
|
|
|
replicas: 1,
|
|
|
|
|
selector: {
|
|
|
|
|
matchLabels: {
|
|
|
|
|
app: serverName,
|
2026-01-16 12:32:36 +07:00
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
template: {
|
|
|
|
|
metadata: {
|
|
|
|
|
labels: {
|
|
|
|
|
app: serverName,
|
|
|
|
|
[`${LABEL_PREFIX}/server-type`]: serverType,
|
|
|
|
|
[`${LABEL_PREFIX}/proxy-id`]: server.id,
|
2026-01-16 12:32:36 +07:00
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
spec: {
|
|
|
|
|
containers: [
|
|
|
|
|
{
|
|
|
|
|
name: serverType,
|
2026-01-16 12:32:36 +07:00
|
|
|
image: "itzg/mc-proxy:latest",
|
2025-05-12 12:34:09 +07:00
|
|
|
ports: [
|
|
|
|
|
{
|
|
|
|
|
containerPort: server.listen_port,
|
2026-01-16 12:32:36 +07:00
|
|
|
name: "minecraft",
|
|
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
],
|
|
|
|
|
env: [
|
|
|
|
|
{
|
2026-01-16 12:32:36 +07:00
|
|
|
name: "TYPE",
|
2025-05-12 12:34:09 +07:00
|
|
|
value: server.type,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-01-16 12:32:36 +07:00
|
|
|
name: "NETWORKADDRESS_CACHE_TTL",
|
|
|
|
|
value: "30",
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
{
|
2026-01-16 12:32:36 +07:00
|
|
|
name: "MEMORY",
|
|
|
|
|
value: calculateJavaMemory(server.memory || "512M", 0.8),
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
2026-01-16 12:32:36 +07:00
|
|
|
...(server.env_variables || []).map((ev) => ({
|
2025-05-12 12:34:09 +07:00
|
|
|
name: ev.key,
|
|
|
|
|
value: ev.value,
|
|
|
|
|
})),
|
|
|
|
|
],
|
|
|
|
|
readinessProbe: {
|
|
|
|
|
tcpSocket: {
|
2026-01-16 12:32:36 +07:00
|
|
|
port: server.listen_port,
|
2025-05-12 12:34:09 +07:00
|
|
|
},
|
|
|
|
|
initialDelaySeconds: 30,
|
|
|
|
|
periodSeconds: 10,
|
|
|
|
|
},
|
|
|
|
|
resources: {
|
|
|
|
|
requests: {
|
2026-01-16 12:32:36 +07:00
|
|
|
memory: convertToK8sFormat(server.memory || "512M"),
|
2025-05-12 12:34:09 +07:00
|
|
|
cpu: "250m",
|
|
|
|
|
},
|
|
|
|
|
limits: {
|
2026-01-16 12:32:36 +07:00
|
|
|
memory: convertToK8sFormat(server.memory || "512M"),
|
2025-05-12 12:34:09 +07:00
|
|
|
cpu: "500m",
|
2026-01-16 12:32:36 +07:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-05-12 12:34:09 +07:00
|
|
|
};
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
try {
|
2026-02-13 15:52:13 +07:00
|
|
|
await appsApi.createNamespacedDeployment({ namespace, body: deployment });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Created Deployment for reverse proxy server ${server.id}`);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error.response?.statusCode === 409) {
|
2026-02-13 15:52:13 +07:00
|
|
|
await appsApi.replaceNamespacedDeployment({ name: serverName, namespace, body: deployment });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Updated Deployment for reverse proxy server ${server.id}`);
|
|
|
|
|
} else {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function deleteReverseProxyServer(
|
|
|
|
|
proxyId: string,
|
|
|
|
|
proxyType: ReverseProxyServerType,
|
|
|
|
|
appsApi: k8s.AppsV1Api,
|
|
|
|
|
coreApi: k8s.CoreV1Api,
|
|
|
|
|
namespace: string
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
const serverType = proxyType.toLowerCase();
|
|
|
|
|
const name = `${serverType}-${proxyId}`;
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
try {
|
2026-02-13 15:52:13 +07:00
|
|
|
await appsApi.deleteNamespacedDeployment({ name, namespace });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Deleted Deployment for reverse proxy server ${proxyId}`);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error.response?.statusCode !== 404) {
|
|
|
|
|
console.error(`Error deleting Deployment for reverse proxy server ${proxyId}:`, error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
try {
|
2026-02-13 15:52:13 +07:00
|
|
|
await coreApi.deleteNamespacedService({ name, namespace });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Deleted Service for reverse proxy server ${proxyId}`);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error.response?.statusCode !== 404) {
|
|
|
|
|
console.error(`Error deleting Service for reverse proxy server ${proxyId}:`, error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-16 12:32:36 +07:00
|
|
|
|
2025-05-12 12:34:09 +07:00
|
|
|
try {
|
2026-02-13 15:52:13 +07:00
|
|
|
await coreApi.deleteNamespacedConfigMap({ name: `${name}-config`, namespace });
|
2025-05-12 12:34:09 +07:00
|
|
|
console.log(`Deleted ConfigMap for reverse proxy server ${proxyId}`);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
if (error.response?.statusCode !== 404) {
|
|
|
|
|
console.error(`Error deleting ConfigMap for reverse proxy server ${proxyId}:`, error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-16 12:32:36 +07:00
|
|
|
}
|