feat: report Docker image source in system info
This commit is contained in:
1 parent
17c8181f85
commit
10b50829ee
16 files changed
+117
-2
No files matched your search
@@ -7,6 +7,7 @@ export type Bindings = {
|
||||
storage: StorageAdapter;
|
||||
EDGE_EVER_AUTH_USERNAME?: string;
|
||||
EDGE_EVER_RUNTIME?: string;
|
||||
EDGE_EVER_CONTAINER_IMAGE?: string;
|
||||
EDGE_EVER_AUTH_PASSWORD?: string;
|
||||
EDGE_EVER_AUTH_PASSWORD_HASH?: string;
|
||||
EDGE_EVER_SESSION_TTL_DAYS?: string;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { resolveContainerImageSource } from "./container-image-source";
|
||||
|
||||
describe("container image source diagnostics", () => {
|
||||
test("recognizes official image repositories with tags or digests", () => {
|
||||
expect(resolveContainerImageSource("ghcr.io/tianma-if/edgeever")).toBe("official-ghcr");
|
||||
expect(resolveContainerImageSource("ghcr.io/tianma-if/edgeever:v1.2.3")).toBe("official-ghcr");
|
||||
expect(resolveContainerImageSource("GHCR.IO/TIANMA-IF/EDGEEVER@sha256:abc")).toBe("official-ghcr");
|
||||
expect(resolveContainerImageSource("ccr.ccs.tencentyun.com/edgeever/edgeever:latest")).toBe("official-cn-mirror");
|
||||
});
|
||||
|
||||
test("does not expose custom image references through the classification", () => {
|
||||
expect(resolveContainerImageSource("registry.internal.example/team/edgeever:latest")).toBe("custom");
|
||||
expect(resolveContainerImageSource("ghcr.io/tianma-if/edgeever-fork")).toBe("custom");
|
||||
expect(resolveContainerImageSource(" ")).toBe("unknown");
|
||||
expect(resolveContainerImageSource(undefined)).toBe("unknown");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
export type ContainerImageSource =
|
||||
| "official-ghcr"
|
||||
| "official-cn-mirror"
|
||||
| "custom"
|
||||
| "unknown";
|
||||
|
||||
const OFFICIAL_GHCR_IMAGE = "ghcr.io/tianma-if/edgeever";
|
||||
const OFFICIAL_CN_MIRROR_IMAGE = "ccr.ccs.tencentyun.com/edgeever/edgeever";
|
||||
|
||||
const matchesImageRepository = (reference: string, repository: string) =>
|
||||
reference === repository
|
||||
|| reference.startsWith(`${repository}:`)
|
||||
|| reference.startsWith(`${repository}@`);
|
||||
|
||||
export const resolveContainerImageSource = (
|
||||
imageReference: string | null | undefined,
|
||||
): ContainerImageSource => {
|
||||
const normalized = imageReference?.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/\/+$/, "") ?? "";
|
||||
if (!normalized) return "unknown";
|
||||
if (matchesImageRepository(normalized, OFFICIAL_GHCR_IMAGE)) return "official-ghcr";
|
||||
if (matchesImageRepository(normalized, OFFICIAL_CN_MIRROR_IMAGE)) return "official-cn-mirror";
|
||||
return "custom";
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from "@edgeever/shared";
|
||||
import { Hono } from "hono";
|
||||
import { cors } from "hono/cors";
|
||||
import { resolveContainerImageSource } from "./container-image-source";
|
||||
import openApiSpec from "../../../docs/openapi.json";
|
||||
import releaseSummary from "../../../release-summary.json";
|
||||
import {
|
||||
@@ -198,10 +199,15 @@ app.get("/api/health", async (c) => {
|
||||
);
|
||||
}
|
||||
|
||||
const runtime = c.env.EDGE_EVER_RUNTIME ?? "cloudflare-workers";
|
||||
|
||||
return c.json({
|
||||
ok: true,
|
||||
name: "edgeever",
|
||||
runtime: c.env.EDGE_EVER_RUNTIME ?? "cloudflare-workers",
|
||||
runtime,
|
||||
...(runtime === "self-hosted-bun"
|
||||
? { containerImageSource: resolveContainerImageSource(c.env.EDGE_EVER_CONTAINER_IMAGE) }
|
||||
: {}),
|
||||
authMode,
|
||||
build: INSTANCE_BUILD_ID.slice(0, 12),
|
||||
migration: await getAppliedMigration(c.env),
|
||||
|
||||
@@ -38,6 +38,7 @@ struct InstanceHealth: Codable, Equatable, Sendable {
|
||||
var ok: Bool
|
||||
var name: String
|
||||
var runtime: String?
|
||||
var containerImageSource: String?
|
||||
var authMode: String?
|
||||
var build: String?
|
||||
var migration: String?
|
||||
|
||||
@@ -552,6 +552,12 @@ struct SettingsView: View {
|
||||
env.preferences.t("部署平台", en: "Deployment platform"),
|
||||
deploymentPlatformLabel(instanceHealth?.runtime)
|
||||
))
|
||||
if instanceHealth?.runtime == "self-hosted-bun" {
|
||||
items.append((
|
||||
env.preferences.t("容器镜像来源", en: "Container image source"),
|
||||
containerImageSourceLabel(instanceHealth?.containerImageSource)
|
||||
))
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
@@ -606,6 +612,15 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func containerImageSourceLabel(_ source: String?) -> String {
|
||||
switch source {
|
||||
case "official-ghcr": return "GitHub Container Registry (GHCR)"
|
||||
case "official-cn-mirror": return env.preferences.t("中国大陆官方镜像", en: "Official mainland China mirror")
|
||||
case "custom": return env.preferences.t("自定义镜像", en: "Custom image")
|
||||
default: return unknownSystemInfoValue
|
||||
}
|
||||
}
|
||||
|
||||
private var systemInfoText: String {
|
||||
[
|
||||
systemInfoTextSection(env.preferences.t("云端实例", en: "Cloud instance"), items: cloudSystemInfoItems),
|
||||
|
||||
@@ -488,6 +488,7 @@ const getMobileSystemInfoText = (localePreference: MobileLocaleMode) =>
|
||||
connectionDescription: "Connection and local sync queue status.",
|
||||
connectionFailed: "Connection failed",
|
||||
connectionSection: "Connection and sync",
|
||||
containerImageSource: "Container image source",
|
||||
databaseBackend: "Database backend",
|
||||
databaseVersion: "Database version",
|
||||
description: "View cloud instance and current client diagnostics.",
|
||||
@@ -527,6 +528,7 @@ const getMobileSystemInfoText = (localePreference: MobileLocaleMode) =>
|
||||
connectionDescription: "实例连接与本地同步队列状态。",
|
||||
connectionFailed: "连接失败",
|
||||
connectionSection: "连接与同步",
|
||||
containerImageSource: "容器镜像来源",
|
||||
databaseBackend: "数据库后端",
|
||||
databaseVersion: "数据库版本",
|
||||
description: "查看云端实例与当前客户端的诊断信息。",
|
||||
@@ -561,6 +563,15 @@ const getMobileDeploymentPlatform = (runtime: string | null | undefined, english
|
||||
return english ? "Unknown" : "未知";
|
||||
};
|
||||
|
||||
const getMobileContainerImageSource = (source: string | null | undefined, english: boolean) => {
|
||||
switch (source) {
|
||||
case "official-ghcr": return "GitHub Container Registry (GHCR)";
|
||||
case "official-cn-mirror": return english ? "Official mainland China mirror" : "中国大陆官方镜像";
|
||||
case "custom": return english ? "Custom image" : "自定义镜像";
|
||||
default: return english ? "Unknown" : "未知";
|
||||
}
|
||||
};
|
||||
|
||||
const getMobileDatabaseBackend = (backend: string | null | undefined, unknown: string) => {
|
||||
if (backend === "d1") return "D1";
|
||||
if (backend === "sqlite") return "SQLite";
|
||||
@@ -627,6 +638,9 @@ const getMobileSystemInfoGroups = (
|
||||
? [{ label: copy.existingAttachments, value: copy.existingAttachmentsOriginalStorage }]
|
||||
: []),
|
||||
{ label: copy.deploymentPlatform, value: getMobileDeploymentPlatform(instance?.health.runtime, english) },
|
||||
...(instance?.health.runtime === "self-hosted-bun"
|
||||
? [{ label: copy.containerImageSource, value: getMobileContainerImageSource(instance.health.containerImageSource, english) }]
|
||||
: []),
|
||||
],
|
||||
title: copy.cloudSection,
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ services:
|
||||
environment:
|
||||
EDGE_EVER_AUTH_USERNAME: "${EDGE_EVER_AUTH_USERNAME:-admin}"
|
||||
EDGE_EVER_AUTH_PASSWORD: "${EDGE_EVER_AUTH_PASSWORD:?Set EDGE_EVER_AUTH_PASSWORD before starting EdgeEver}"
|
||||
EDGE_EVER_CONTAINER_IMAGE: "${EDGE_EVER_IMAGE:-ghcr.io/tianma-if/edgeever}"
|
||||
volumes:
|
||||
- edgeever-data:/data
|
||||
security_opt:
|
||||
|
||||
@@ -25,7 +25,7 @@ export type SystemInfoItem = {
|
||||
status?: "connected" | "connecting" | "failed" | "warning" | "error" | "default";
|
||||
};
|
||||
|
||||
type InstanceSystemDiagnostics = Pick<InstanceHealth, "build" | "migration" | "objectStorageProvider" | "storage"> & {
|
||||
type InstanceSystemDiagnostics = Pick<InstanceHealth, "build" | "containerImageSource" | "migration" | "objectStorageProvider" | "storage"> & {
|
||||
runtime?: string | null;
|
||||
};
|
||||
|
||||
@@ -62,6 +62,11 @@ const getDeploymentDescription = (t: (key: string) => string) => {
|
||||
return `${trigger} · ${method}`;
|
||||
};
|
||||
|
||||
const getContainerImageSourceTranslationKey = (source: string | null | undefined) => {
|
||||
if (source === "official-ghcr" || source === "official-cn-mirror" || source === "custom") return source;
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
const getColSpanClass = (colSpan?: SystemInfoItem["colSpan"]) => {
|
||||
if (colSpan === "full") return "sm:col-span-2 lg:col-span-3";
|
||||
if (colSpan === "two") return "sm:col-span-2 lg:col-span-2";
|
||||
@@ -129,6 +134,12 @@ const getWebSystemInfoGroups = (
|
||||
label: t("systemInfo.deploymentPlatform"),
|
||||
value: t(`systemInfo.deploymentPlatforms.${resolveDeploymentPlatform(diagnostics.instance?.runtime)}`),
|
||||
},
|
||||
...(diagnostics.instance?.runtime === "self-hosted-bun"
|
||||
? [{
|
||||
label: t("systemInfo.containerImageSource"),
|
||||
value: t(`systemInfo.containerImageSources.${getContainerImageSourceTranslationKey(diagnostics.instance.containerImageSource)}`),
|
||||
}]
|
||||
: []),
|
||||
{ label: t("systemInfo.deployment"), value: getDeploymentDescription(t), colSpan: "full" },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ services:
|
||||
environment:
|
||||
EDGE_EVER_AUTH_USERNAME: "${EDGE_EVER_AUTH_USERNAME:-admin}"
|
||||
EDGE_EVER_AUTH_PASSWORD: "${EDGE_EVER_AUTH_PASSWORD:?Set EDGE_EVER_AUTH_PASSWORD before starting EdgeEver}"
|
||||
EDGE_EVER_CONTAINER_IMAGE: "${EDGE_EVER_IMAGE:-ghcr.io/tianma-if/edgeever}"
|
||||
volumes:
|
||||
- edgeever-data:/data
|
||||
security_opt:
|
||||
|
||||
@@ -40,6 +40,11 @@
|
||||
"runtime": {
|
||||
"type": "string"
|
||||
},
|
||||
"containerImageSource": {
|
||||
"type": "string",
|
||||
"enum": ["official-ghcr", "official-cn-mirror", "custom", "unknown"],
|
||||
"description": "Current container image repository classification for Docker deployments. Custom repository addresses are not exposed."
|
||||
},
|
||||
"authMode": {
|
||||
"type": "string",
|
||||
"enum": ["required", "disabled"]
|
||||
|
||||
@@ -57,6 +57,7 @@ export type InstanceHealth = {
|
||||
ok: true;
|
||||
name: string;
|
||||
runtime?: string | null;
|
||||
containerImageSource?: "official-ghcr" | "official-cn-mirror" | "custom" | "unknown" | string | null;
|
||||
authMode?: string | null;
|
||||
build?: string | null;
|
||||
migration?: string | null;
|
||||
|
||||
@@ -668,6 +668,13 @@ export const enUS = {
|
||||
docker: "Docker",
|
||||
unknown: "Unknown",
|
||||
},
|
||||
containerImageSource: "Container image source",
|
||||
containerImageSources: {
|
||||
"official-ghcr": "GitHub Container Registry (GHCR)",
|
||||
"official-cn-mirror": "Official mainland China mirror",
|
||||
custom: "Custom image",
|
||||
unknown: "Unknown",
|
||||
},
|
||||
deployment: "Deployment",
|
||||
deploymentTriggers: {
|
||||
github_release: "GitHub Release trigger",
|
||||
|
||||
@@ -668,6 +668,13 @@ export const zhCN = {
|
||||
docker: "Docker",
|
||||
unknown: "未知",
|
||||
},
|
||||
containerImageSource: "容器镜像来源",
|
||||
containerImageSources: {
|
||||
"official-ghcr": "GitHub Container Registry(GHCR)",
|
||||
"official-cn-mirror": "中国大陆官方镜像",
|
||||
custom: "自定义镜像",
|
||||
unknown: "未知",
|
||||
},
|
||||
deployment: "自动部署方式",
|
||||
deploymentTriggers: {
|
||||
github_release: "正式 Release 触发",
|
||||
|
||||
@@ -80,6 +80,9 @@ describe("Docker release contract", () => {
|
||||
expect(compose).toContain(
|
||||
'EDGE_EVER_AUTH_PASSWORD: "${EDGE_EVER_AUTH_PASSWORD:?',
|
||||
);
|
||||
expect(compose).toContain(
|
||||
'EDGE_EVER_CONTAINER_IMAGE: "${EDGE_EVER_IMAGE:-ghcr.io/tianma-if/edgeever}"',
|
||||
);
|
||||
expect(compose).toContain("edgeever-data:/data");
|
||||
expect(compose).toContain("no-new-privileges:true");
|
||||
});
|
||||
|
||||
@@ -77,6 +77,7 @@ const env = {
|
||||
storage,
|
||||
EDGE_EVER_AUTH_USERNAME: runtimeEnvironment.EDGE_EVER_AUTH_USERNAME ?? "admin",
|
||||
EDGE_EVER_RUNTIME: "self-hosted-bun",
|
||||
EDGE_EVER_CONTAINER_IMAGE: runtimeEnvironment.EDGE_EVER_CONTAINER_IMAGE,
|
||||
EDGE_EVER_AUTH_PASSWORD: runtimeEnvironment.EDGE_EVER_AUTH_PASSWORD,
|
||||
EDGE_EVER_AUTH_PASSWORD_HASH: runtimeEnvironment.EDGE_EVER_AUTH_PASSWORD_HASH,
|
||||
EDGE_EVER_SESSION_TTL_DAYS: runtimeEnvironment.EDGE_EVER_SESSION_TTL_DAYS ?? "400",
|
||||
|
||||
Reference in new issue
Block a user