Add unit and E2E tests for release channel availability and switched-notice functionality. Configure runtime mocks and implement Vue components for channel-specific handling.
This commit is contained in:
+45
-24
@@ -1,11 +1,17 @@
|
||||
<script setup>
|
||||
import { computed, defineAsyncComponent, onMounted, watch } from "vue";
|
||||
import { computed, defineAsyncComponent, onBeforeUnmount, onMounted, watch } from "vue";
|
||||
// Set the favicon to the /favicon.ico file
|
||||
import { useHead } from "@vueuse/head";
|
||||
import favicon from '@/assets/favicon.ico';
|
||||
import {useRoute} from "vue-router";
|
||||
import favicon from "@/assets/favicon.ico";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { hasStoredSessionToken } from "@/services/sessionStorage.js";
|
||||
import {
|
||||
releaseChannelSwitchedStatus,
|
||||
releaseChannelUnavailableStatus,
|
||||
startReleaseChannelAvailabilityClock,
|
||||
stopReleaseChannelAvailabilityClock,
|
||||
} from "@/services/releaseChannelAvailability.js";
|
||||
|
||||
const route = useRoute();
|
||||
const { t, te, locale } = useI18n({ useScope: "global" });
|
||||
@@ -15,28 +21,32 @@ const DefaultPageWrapper = defineAsyncComponent(() => import("@/components/page/
|
||||
const VersionCheck = defineAsyncComponent(() => import("@/components/global/VersionCheck.vue"));
|
||||
const RequestQueueProgress = defineAsyncComponent(() => import("@/components/global/RequestQueueProgress.vue"));
|
||||
const ErrorReportLauncher = defineAsyncComponent(() => import("@/components/global/ErrorReportLauncher.vue"));
|
||||
const ReleaseChannelUnavailable = defineAsyncComponent(() =>
|
||||
import("@/components/release/ReleaseChannelUnavailable.vue")
|
||||
);
|
||||
const ReleaseChannelSwitched = defineAsyncComponent(() => import("@/components/release/ReleaseChannelSwitched.vue"));
|
||||
|
||||
const routeTitleI18nKeys = {
|
||||
"about-us": ["header.about_us"],
|
||||
"book-a-demo": ["about_us.solutions.operator.book_demo"],
|
||||
"customer-creation": ["header.customer_creation"],
|
||||
"login": ["header.customer_login"],
|
||||
"subuserlogin": ["header.customer_login"],
|
||||
"adminlogin": ["header.operator_login"],
|
||||
"guestHome": ["guest.home.title"],
|
||||
"PwaDownload": ["guest.pwa.title"],
|
||||
login: ["header.customer_login"],
|
||||
subuserlogin: ["header.customer_login"],
|
||||
adminlogin: ["header.operator_login"],
|
||||
guestHome: ["guest.home.title"],
|
||||
PwaDownload: ["guest.pwa.title"],
|
||||
"connectivity-issue": ["connectivity.title"],
|
||||
"selfservestudio": ["admin.self_serve.title"]
|
||||
selfservestudio: ["admin.self_serve.title"],
|
||||
};
|
||||
|
||||
const toReadableTitle = (value) => {
|
||||
if (!value) return "";
|
||||
return value
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.replace(/[-_]+/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.replace(/[-_]+/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||||
};
|
||||
|
||||
const resolveRouteTitle = () => {
|
||||
@@ -50,7 +60,7 @@ const resolveRouteTitle = () => {
|
||||
}
|
||||
|
||||
const routeName = typeof route.name === "string" ? route.name : "";
|
||||
const knownKeys = routeName ? (routeTitleI18nKeys[routeName] || []) : [];
|
||||
const knownKeys = routeName ? routeTitleI18nKeys[routeName] || [] : [];
|
||||
for (const key of knownKeys) {
|
||||
if (te(key)) return t(key);
|
||||
}
|
||||
@@ -64,6 +74,13 @@ const updateDocumentTitle = () => {
|
||||
};
|
||||
|
||||
const shouldRenderRequestQueueProgress = computed(() => hasStoredSessionToken());
|
||||
const shouldRenderReleaseChannelUnavailable = computed(() => releaseChannelUnavailableStatus.value.shouldBlock);
|
||||
const shouldRenderReleaseChannelSwitched = computed(
|
||||
() => !shouldRenderReleaseChannelUnavailable.value && releaseChannelSwitchedStatus.value.shouldShow
|
||||
);
|
||||
const shouldHideGlobalOverlays = computed(
|
||||
() => shouldRenderReleaseChannelUnavailable.value || shouldRenderReleaseChannelSwitched.value
|
||||
);
|
||||
|
||||
const initiateStoredSession = async () => {
|
||||
if (!hasStoredSessionToken()) {
|
||||
@@ -79,19 +96,24 @@ const initiateStoredSession = async () => {
|
||||
};
|
||||
|
||||
useHead({
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/x-icon', href: favicon }
|
||||
]
|
||||
link: [{ rel: "icon", type: "image/x-icon", href: favicon }],
|
||||
});
|
||||
onMounted(() => {
|
||||
startReleaseChannelAvailabilityClock();
|
||||
void initiateStoredSession();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopReleaseChannelAvailabilityClock();
|
||||
});
|
||||
|
||||
watch([() => route.fullPath, locale], updateDocumentTitle, { immediate: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LayoutV2 v-if="route.meta?.template === 'with-header'">
|
||||
<ReleaseChannelUnavailable v-if="shouldRenderReleaseChannelUnavailable" />
|
||||
<ReleaseChannelSwitched v-else-if="shouldRenderReleaseChannelSwitched" />
|
||||
<LayoutV2 v-else-if="route.meta?.template === 'with-header'">
|
||||
<router-view />
|
||||
</LayoutV2>
|
||||
<main v-else-if="route.meta?.template === 'clear-main'">
|
||||
@@ -101,14 +123,13 @@ watch([() => route.fullPath, locale], updateDocumentTitle, { immediate: true });
|
||||
<header></header>
|
||||
<main>
|
||||
<DefaultPageWrapper>
|
||||
<VersionCheck/>
|
||||
<VersionCheck />
|
||||
<router-view />
|
||||
</DefaultPageWrapper>
|
||||
</main>
|
||||
</template>
|
||||
<RequestQueueProgress v-if="shouldRenderRequestQueueProgress" />
|
||||
<ErrorReportLauncher v-if="shouldRenderRequestQueueProgress" />
|
||||
<RequestQueueProgress v-if="shouldRenderRequestQueueProgress && !shouldHideGlobalOverlays" />
|
||||
<ErrorReportLauncher v-if="shouldRenderRequestQueueProgress && !shouldHideGlobalOverlays" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -9,9 +9,13 @@ const props = defineProps({
|
||||
subtitle: String,
|
||||
icon: String,
|
||||
isSkeleton: Boolean,
|
||||
defaultExpanded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const isExpanded = ref(false);
|
||||
const isExpanded = ref(props.defaultExpanded);
|
||||
const skeletonSpaces = (length) => {
|
||||
// Create a string with non-breaking spaces
|
||||
return Array.from({ length }, () => '\u00A0').join('');
|
||||
@@ -58,4 +62,4 @@ const skeletonSpaces = (length) => {
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -56,6 +56,19 @@ const isValid = computed(() => (
|
||||
|
||||
const recentRequestErrors = computed(() => Array.from(requestQueueState.errorRequests || []).slice(0, 25));
|
||||
const recentVueErrors = computed(() => getRecentFrontendFailureEvents().slice(0, 25));
|
||||
let html2canvasPromise = null;
|
||||
|
||||
const loadHtml2Canvas = () => {
|
||||
if (!html2canvasPromise) {
|
||||
html2canvasPromise = import("html2canvas")
|
||||
.then((module) => module.default)
|
||||
.catch((error) => {
|
||||
html2canvasPromise = null;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return html2canvasPromise;
|
||||
};
|
||||
|
||||
const redactCurrentUrl = () => {
|
||||
if (typeof window === "undefined") {
|
||||
@@ -80,6 +93,7 @@ const open = () => {
|
||||
submitError.value = "";
|
||||
submitted.value = false;
|
||||
isOpen.value = true;
|
||||
void loadHtml2Canvas().catch(() => {});
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
@@ -105,17 +119,76 @@ const canvasToDataUrl = (canvas) => {
|
||||
return canvas.toDataURL("image/png");
|
||||
};
|
||||
|
||||
const captureScreenshot = async () => {
|
||||
await nextTick();
|
||||
const html2canvas = (await import("html2canvas")).default;
|
||||
const target = document.querySelector("#app") || document.body;
|
||||
const canvas = await html2canvas(target, {
|
||||
const getViewportCaptureOptions = (overrides = {}) => {
|
||||
const scrollX = window.scrollX || window.pageXOffset || 0;
|
||||
const scrollY = window.scrollY || window.pageYOffset || 0;
|
||||
const width = Math.max(1, window.innerWidth || document.documentElement.clientWidth || 1);
|
||||
const height = Math.max(1, window.innerHeight || document.documentElement.clientHeight || 1);
|
||||
|
||||
return {
|
||||
backgroundColor: "#ffffff",
|
||||
scale: Math.min(window.devicePixelRatio || 1, 2),
|
||||
useCORS: true,
|
||||
allowTaint: false,
|
||||
imageTimeout: 3000,
|
||||
logging: false,
|
||||
width,
|
||||
height,
|
||||
x: scrollX,
|
||||
y: scrollY,
|
||||
scrollX: -scrollX,
|
||||
scrollY: -scrollY,
|
||||
windowWidth: width,
|
||||
windowHeight: height,
|
||||
ignoreElements: (element) => Boolean(element.closest?.("[data-error-report-exclude]")),
|
||||
onclone: (clonedDocument) => {
|
||||
clonedDocument.querySelectorAll("[data-error-report-exclude]").forEach((element) => element.remove());
|
||||
clonedDocument.querySelectorAll("img").forEach((image) => {
|
||||
const source = image.currentSrc || image.src || "";
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(source, window.location.href);
|
||||
if (url.protocol !== "data:" && url.protocol !== "blob:" && url.origin !== window.location.origin) {
|
||||
image.removeAttribute("src");
|
||||
image.style.visibility = "hidden";
|
||||
}
|
||||
} catch {
|
||||
image.removeAttribute("src");
|
||||
image.style.visibility = "hidden";
|
||||
}
|
||||
});
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
const hideCaptureExcludedElements = () => {
|
||||
const originals = [];
|
||||
document.querySelectorAll("[data-error-report-exclude]").forEach((element) => {
|
||||
originals.push({
|
||||
element,
|
||||
display: element.style.display,
|
||||
priority: element.style.getPropertyPriority("display"),
|
||||
});
|
||||
element.style.setProperty("display", "none", "important");
|
||||
});
|
||||
|
||||
return () => {
|
||||
originals.forEach(({ element, display, priority }) => {
|
||||
if (display) {
|
||||
element.style.setProperty("display", display, priority);
|
||||
return;
|
||||
}
|
||||
element.style.removeProperty("display");
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const captureWithOptions = async (html2canvas, target, options) => {
|
||||
const canvas = await html2canvas(target, options);
|
||||
const dataUrl = canvasToDataUrl(canvas);
|
||||
if (!dataUrl.startsWith("data:image/")) {
|
||||
throw new Error("Screenshot capture failed.");
|
||||
@@ -123,6 +196,30 @@ const captureScreenshot = async () => {
|
||||
return dataUrl;
|
||||
};
|
||||
|
||||
const captureScreenshot = async () => {
|
||||
await nextTick();
|
||||
const html2canvas = await loadHtml2Canvas();
|
||||
const restoreExcludedElements = hideCaptureExcludedElements();
|
||||
|
||||
try {
|
||||
await nextTick();
|
||||
const target = document.body || document.documentElement;
|
||||
try {
|
||||
return await captureWithOptions(html2canvas, target, getViewportCaptureOptions());
|
||||
} catch (firstError) {
|
||||
return await captureWithOptions(
|
||||
html2canvas,
|
||||
document.documentElement || target,
|
||||
getViewportCaptureOptions({ foreignObjectRendering: true })
|
||||
).catch(() => {
|
||||
throw firstError;
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
restoreExcludedElements();
|
||||
}
|
||||
};
|
||||
|
||||
const buildContext = () => ({
|
||||
route_path: route.path || window.location?.pathname || null,
|
||||
page_url: redactCurrentUrl(),
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {
|
||||
acknowledgeReleaseChannelSwitch,
|
||||
redirectToConfiguredReleaseFrontend,
|
||||
releaseChannelSwitchedStatus,
|
||||
} from "@/services/releaseChannelAvailability.js";
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
const tr = (key, params = {}) => t(`configuration.release_manager.channel_switched.${key}`, params);
|
||||
const status = computed(() => releaseChannelSwitchedStatus.value);
|
||||
const frontendLabel = computed(() => status.value.frontendBaseUrl || tr("current_app_image"));
|
||||
const apiLabel = computed(() => status.value.apiBaseUrl || tr("current_api"));
|
||||
const frontendVersion = computed(() => status.value.versions?.frontend || null);
|
||||
const apiVersion = computed(() => status.value.versions?.api || null);
|
||||
|
||||
const versionLabel = (version, fallback) => {
|
||||
return version?.version_label || version?.commit_sha || fallback;
|
||||
};
|
||||
|
||||
const continueToApp = () => {
|
||||
acknowledgeReleaseChannelSwitch(status.value.channel, status.value.principalKey);
|
||||
redirectToConfiguredReleaseFrontend();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="release-switch-page" data-testid="release-channel-switched-page">
|
||||
<section class="release-switch-shell">
|
||||
<div class="release-switch-graphic" aria-hidden="true">
|
||||
<div class="release-track">
|
||||
<span class="release-node release-node--base"></span>
|
||||
<span class="release-line"></span>
|
||||
<span class="release-node release-node--channel"></span>
|
||||
<span class="release-line release-line--ready"></span>
|
||||
<span class="release-node release-node--ready"></span>
|
||||
</div>
|
||||
<div class="release-card release-card--base">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
<span>{{ tr("base_image") }}</span>
|
||||
</div>
|
||||
<div class="release-card release-card--channel">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
<span>{{ status.channelSlug || "channel" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="release-switch-copy">
|
||||
<p class="release-kicker">{{ tr("kicker") }}</p>
|
||||
<h1>{{ tr("title", { channel: status.channelName }) }}</h1>
|
||||
<p class="release-summary">
|
||||
{{ tr("summary_prefix") }} <strong>{{ status.channelName }}</strong> {{ tr("summary_suffix") }}
|
||||
</p>
|
||||
<p v-if="status.description" class="release-description">
|
||||
{{ status.description }}
|
||||
</p>
|
||||
|
||||
<div class="release-details" data-testid="release-channel-switched-details">
|
||||
<span><strong>{{ tr("frontend") }}</strong>{{ frontendLabel }}</span>
|
||||
<span><strong>{{ tr("api") }}</strong>{{ apiLabel }}</span>
|
||||
<span><strong>{{ tr("frontend_version") }}</strong>{{ versionLabel(frontendVersion, tr("assigned_channel")) }}</span>
|
||||
<span><strong>{{ tr("api_version") }}</strong>{{ versionLabel(apiVersion, tr("assigned_channel")) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="release-actions">
|
||||
<b-button
|
||||
type="is-dark"
|
||||
icon-left="arrow-right"
|
||||
icon-pack="fas"
|
||||
data-testid="release-channel-switched-continue"
|
||||
@click="continueToApp"
|
||||
>
|
||||
{{ tr("continue") }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.release-switch-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32px;
|
||||
background: linear-gradient(135deg, rgba(4, 31, 68, 0.08), rgba(235, 248, 255, 0.72)), #f7fafc;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.release-switch-shell {
|
||||
width: min(1080px, 100%);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 0.9fr) minmax(320px, 1.1fr);
|
||||
gap: 48px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.release-switch-graphic {
|
||||
position: relative;
|
||||
min-height: 360px;
|
||||
border: 1px solid #d9e4f2;
|
||||
background: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.release-track {
|
||||
position: absolute;
|
||||
top: 112px;
|
||||
left: 44px;
|
||||
right: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.release-node {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 4px solid #ffffff;
|
||||
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.16);
|
||||
border-radius: 50%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.release-node--base {
|
||||
background: #0f2a4a;
|
||||
}
|
||||
|
||||
.release-node--channel {
|
||||
background: #005486;
|
||||
}
|
||||
|
||||
.release-node--ready {
|
||||
background: #0f766e;
|
||||
}
|
||||
|
||||
.release-line {
|
||||
height: 4px;
|
||||
flex: 1;
|
||||
background: #005486;
|
||||
}
|
||||
|
||||
.release-line--ready {
|
||||
background: #0f766e;
|
||||
}
|
||||
|
||||
.release-card {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 180px;
|
||||
padding: 16px;
|
||||
border: 1px solid #d9e4f2;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.12);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.release-card i {
|
||||
color: #005486;
|
||||
}
|
||||
|
||||
.release-card--base {
|
||||
top: 168px;
|
||||
left: 58px;
|
||||
}
|
||||
|
||||
.release-card--channel {
|
||||
right: 58px;
|
||||
bottom: 72px;
|
||||
}
|
||||
|
||||
.release-switch-copy {
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.release-kicker {
|
||||
margin: 0 0 10px;
|
||||
color: #005486;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.release-switch-copy h1 {
|
||||
margin: 0;
|
||||
color: #101827;
|
||||
font-size: 2.35rem;
|
||||
line-height: 1.08;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.release-summary,
|
||||
.release-description {
|
||||
margin: 16px 0 0;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.release-summary {
|
||||
font-size: 1.06rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.release-description {
|
||||
color: #5b6677;
|
||||
}
|
||||
|
||||
.release-details {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.release-details span {
|
||||
min-width: 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #cfe0f2;
|
||||
background: #ffffff;
|
||||
color: #334155;
|
||||
font-size: 0.86rem;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.release-details strong {
|
||||
display: block;
|
||||
margin-bottom: 3px;
|
||||
color: #005486;
|
||||
font-size: 0.74rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.release-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.release-switch-page {
|
||||
padding: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.release-switch-shell {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.release-switch-graphic {
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
.release-switch-copy h1 {
|
||||
font-size: 1.85rem;
|
||||
}
|
||||
|
||||
.release-details {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,329 @@
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||
import {
|
||||
ignoreUnavailableReleaseChannel,
|
||||
redirectToConfiguredReleaseFrontend,
|
||||
releaseChannelUnavailableStatus,
|
||||
RELEASE_CHANNEL_CHECK_INTERVAL_MS,
|
||||
} from "@/services/releaseChannelAvailability.js";
|
||||
|
||||
const router = useRouter();
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
const tr = (key, params = {}) => t(`configuration.release_manager.channel_unavailable.${key}`, params);
|
||||
const checking = ref(false);
|
||||
const checkError = ref("");
|
||||
const secondsUntilNextCheck = ref(RELEASE_CHANNEL_CHECK_INTERVAL_MS / 1000);
|
||||
|
||||
let pollTimer = null;
|
||||
let countdownTimer = null;
|
||||
|
||||
const status = computed(() => releaseChannelUnavailableStatus.value);
|
||||
const missingLabels = computed(() =>
|
||||
status.value.missing.map((key) =>
|
||||
key === "frontend_base_url" ? tr("frontend_url") : key === "api_base_url" ? tr("api_url") : key
|
||||
)
|
||||
);
|
||||
|
||||
const resetCountdown = () => {
|
||||
secondsUntilNextCheck.value = RELEASE_CHANNEL_CHECK_INTERVAL_MS / 1000;
|
||||
};
|
||||
|
||||
const checkAgain = async () => {
|
||||
if (checking.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
checking.value = true;
|
||||
checkError.value = "";
|
||||
try {
|
||||
await SessionUser.refreshReleaseRuntime();
|
||||
redirectToConfiguredReleaseFrontend();
|
||||
} catch (error) {
|
||||
checkError.value = tr("refresh_error");
|
||||
} finally {
|
||||
checking.value = false;
|
||||
resetCountdown();
|
||||
}
|
||||
};
|
||||
|
||||
const ignoreForFiveMinutes = () => {
|
||||
ignoreUnavailableReleaseChannel(status.value.channel);
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
await SessionUser.auth.logout();
|
||||
router.push({ name: "login" });
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
resetCountdown();
|
||||
pollTimer = window.setInterval(() => {
|
||||
void checkAgain();
|
||||
}, RELEASE_CHANNEL_CHECK_INTERVAL_MS);
|
||||
countdownTimer = window.setInterval(() => {
|
||||
secondsUntilNextCheck.value = secondsUntilNextCheck.value > 1 ? secondsUntilNextCheck.value - 1 : 1;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (pollTimer !== null) {
|
||||
window.clearInterval(pollTimer);
|
||||
}
|
||||
if (countdownTimer !== null) {
|
||||
window.clearInterval(countdownTimer);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="release-unavailable-page" data-testid="release-channel-unavailable-page">
|
||||
<section class="release-unavailable-shell">
|
||||
<div class="release-unavailable-graphic" aria-hidden="true">
|
||||
<div class="release-track">
|
||||
<span class="release-node release-node--ready"></span>
|
||||
<span class="release-line"></span>
|
||||
<span class="release-node release-node--waiting"></span>
|
||||
<span class="release-line release-line--muted"></span>
|
||||
<span class="release-node release-node--base"></span>
|
||||
</div>
|
||||
<div class="release-card release-card--channel">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
<span>{{ status.channelSlug || "channel" }}</span>
|
||||
</div>
|
||||
<div class="release-card release-card--base">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
<span>{{ tr("base_image") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="release-unavailable-copy">
|
||||
<p class="release-kicker">{{ tr("kicker") }}</p>
|
||||
<h1>{{ tr("title") }}</h1>
|
||||
<p class="release-summary">
|
||||
{{ tr("summary_prefix") }} <strong>{{ status.channelName }}</strong
|
||||
>{{ tr("summary_suffix") }}
|
||||
</p>
|
||||
<p v-if="status.description" class="release-description">
|
||||
{{ status.description }}
|
||||
</p>
|
||||
<div class="release-missing" data-testid="release-channel-missing">
|
||||
<span v-for="label in missingLabels" :key="label">{{ label }}</span>
|
||||
</div>
|
||||
<p class="release-check-status" data-testid="release-channel-next-check">
|
||||
{{ tr("checking_again", { seconds: secondsUntilNextCheck }) }}
|
||||
</p>
|
||||
<p v-if="checkError" class="release-error" role="alert">{{ checkError }}</p>
|
||||
|
||||
<div class="release-actions">
|
||||
<b-button
|
||||
type="is-dark"
|
||||
icon-left="clock"
|
||||
icon-pack="fas"
|
||||
data-testid="release-channel-ignore"
|
||||
@click="ignoreForFiveMinutes"
|
||||
>
|
||||
{{ tr("ignore") }}
|
||||
</b-button>
|
||||
<b-button
|
||||
type="is-light"
|
||||
icon-left="sync-alt"
|
||||
icon-pack="fas"
|
||||
:loading="checking"
|
||||
data-testid="release-channel-check-again"
|
||||
@click="checkAgain"
|
||||
>
|
||||
{{ tr("check_again") }}
|
||||
</b-button>
|
||||
<b-button type="is-danger is-light" icon-left="sign-out-alt" icon-pack="fas" @click="logout">
|
||||
{{ tr("logout") }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.release-unavailable-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32px;
|
||||
background: linear-gradient(135deg, rgba(4, 31, 68, 0.08), rgba(235, 248, 255, 0.72)), #f7fafc;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.release-unavailable-shell {
|
||||
width: min(1080px, 100%);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 0.9fr) minmax(320px, 1.1fr);
|
||||
gap: 48px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.release-unavailable-graphic {
|
||||
position: relative;
|
||||
min-height: 360px;
|
||||
border: 1px solid #d9e4f2;
|
||||
background: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.release-track {
|
||||
position: absolute;
|
||||
top: 112px;
|
||||
left: 44px;
|
||||
right: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.release-node {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 4px solid #ffffff;
|
||||
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.16);
|
||||
border-radius: 50%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.release-node--ready {
|
||||
background: #0f766e;
|
||||
}
|
||||
|
||||
.release-node--waiting {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.release-node--base {
|
||||
background: #0f2a4a;
|
||||
}
|
||||
|
||||
.release-line {
|
||||
height: 4px;
|
||||
flex: 1;
|
||||
background: #0f766e;
|
||||
}
|
||||
|
||||
.release-line--muted {
|
||||
background: repeating-linear-gradient(90deg, #d1d5db 0 12px, transparent 12px 22px);
|
||||
}
|
||||
|
||||
.release-card {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 180px;
|
||||
padding: 16px;
|
||||
border: 1px solid #d9e4f2;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.12);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.release-card i {
|
||||
color: #005486;
|
||||
}
|
||||
|
||||
.release-card--channel {
|
||||
top: 168px;
|
||||
left: 58px;
|
||||
}
|
||||
|
||||
.release-card--base {
|
||||
right: 58px;
|
||||
bottom: 72px;
|
||||
}
|
||||
|
||||
.release-unavailable-copy {
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.release-kicker {
|
||||
margin: 0 0 10px;
|
||||
color: #005486;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.release-unavailable-copy h1 {
|
||||
margin: 0;
|
||||
color: #101827;
|
||||
font-size: 2.35rem;
|
||||
line-height: 1.08;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.release-summary,
|
||||
.release-description,
|
||||
.release-check-status,
|
||||
.release-error {
|
||||
margin: 16px 0 0;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.release-summary {
|
||||
font-size: 1.06rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.release-description,
|
||||
.release-check-status {
|
||||
color: #5b6677;
|
||||
}
|
||||
|
||||
.release-missing {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.release-missing span {
|
||||
padding: 7px 10px;
|
||||
border: 1px solid #f6c56b;
|
||||
background: #fff7e6;
|
||||
color: #704600;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.release-error {
|
||||
color: #b42318;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.release-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.release-unavailable-page {
|
||||
padding: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.release-unavailable-shell {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.release-unavailable-graphic {
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
.release-unavailable-copy h1 {
|
||||
font-size: 1.85rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -12,19 +12,6 @@ const getSelectedCustomerNumber = () => {
|
||||
return stored ? parseInt(stored) : null;
|
||||
};
|
||||
|
||||
const isSuppressedLicensePlateExtractionError = (url, error) => {
|
||||
if (url !== '/modules/scanner/lpr') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const responseMessage = error?.response?.data?.data?.message ?? error?.response?.data?.message;
|
||||
if (typeof responseMessage !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return responseMessage.toLowerCase().includes('license plate extraction failed');
|
||||
};
|
||||
|
||||
export const authenticatedRequest = (url, method, data, catchCallable = null, thenCallable = null) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
@@ -59,25 +46,10 @@ export const authenticatedRequest = (url, method, data, catchCallable = null, th
|
||||
params: method === 'GET' ? data : null,
|
||||
data: method === 'GET' ? null : data,
|
||||
headers,
|
||||
},
|
||||
shouldRetry: (error) => !isSuppressedLicensePlateExtractionError(url, error),
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch((error) => {
|
||||
if (isSuppressedLicensePlateExtractionError(url, error)) {
|
||||
return error?.response ?? {
|
||||
status: 500,
|
||||
data: {
|
||||
success: false,
|
||||
data: {
|
||||
message: 'Internal server error: License plate extraction failed.'
|
||||
},
|
||||
meta: [],
|
||||
includes: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (catchCallable) {
|
||||
// Call the catch callable
|
||||
catchCallable(error);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+32
-2
@@ -1,8 +1,38 @@
|
||||
export const IS_DEV = import.meta.env.VITE_IS_DEV; // Set to false in production
|
||||
const parseBoolean = (value, fallback = false) => {
|
||||
if (typeof value === "boolean") {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (["1", "true", "yes", "on"].includes(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (["0", "false", "no", "off"].includes(normalized)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const normalizeApiUrl = (value) => String(value || "").replace(/\/+$/, "");
|
||||
|
||||
export const IS_DEV = parseBoolean(import.meta.env.VITE_IS_DEV, import.meta.env.MODE !== "production");
|
||||
|
||||
// Development mode
|
||||
export const POS_STEP_1_VERSION = 2; //(IS_DEV ? 2 : 1);
|
||||
export const API_URL = IS_DEV ? "https://api.truckwash.io:4433" : "https://api.truckwash.io";
|
||||
export const API_URL = normalizeApiUrl(
|
||||
import.meta.env.VITE_API_URL || (IS_DEV ? "https://api.truckwash.io:4433" : "https://api.truckwash.io")
|
||||
);
|
||||
export const RELEASE_MANAGER_CONTROL_API_URL = normalizeApiUrl(
|
||||
import.meta.env.VITE_RELEASE_MANAGER_CONTROL_API_URL || API_URL
|
||||
);
|
||||
export const RELEASE_MANAGER_CONTROL_API_FALLBACK_URLS = String(
|
||||
import.meta.env.VITE_RELEASE_MANAGER_CONTROL_API_FALLBACK_URLS ||
|
||||
(IS_DEV ? "https://api.truckwash.io:4433,https://api.truckwash.io" : "")
|
||||
)
|
||||
.split(",")
|
||||
.map((url) => normalizeApiUrl(url.trim()))
|
||||
.filter(Boolean);
|
||||
|
||||
// Allowed origins
|
||||
export const ALLOWED_ORIGINS = [
|
||||
|
||||
@@ -2437,6 +2437,412 @@
|
||||
"subtitle": "@.capitalize:{'words.generated.konfiguration'} @:{'words.generated.af'} @:{'words.generated.xlvask'} @:{'words.generated.integrationen'}",
|
||||
"title": "@:{'words.generated.xlvask'} @:{'words.generated.konfiguration'}",
|
||||
"username_desc": "@.capitalize:{'words.generated.brugernavnet'} @:{'words.generated.bruges'} @:{'words.generated.til'} @:{'words.generated.at'} @:{'words.generated.autentificere'} @:{'words.generated.api'} @:{'words.generated.forbindelsen'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvise API- og Front-End-udgivelser",
|
||||
"control_api": {
|
||||
"title": "Kontrol-API",
|
||||
"tooltip": "Release Manager-handlinger sendes til denne API. Brug produktions-API'en, medmindre du tester en staging-backend.",
|
||||
"example": "Eksempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endpoint",
|
||||
"endpoint_message": "Skift kun dette, n?r Release Manager-endpoints er tilg?ngelige p? m?l-API'en.",
|
||||
"endpoint_aria": "Release Manager kontrol-API URL",
|
||||
"use_tooltip": "Indl?s release-data fra denne API",
|
||||
"use": "Brug",
|
||||
"reset_tooltip": "Vend tilbage til standard kontrol-API",
|
||||
"reset": "Nulstil",
|
||||
"known_endpoints": "Kendte kontrol-API-endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Oversigt",
|
||||
"description": "Kanalstatus, ops?tningsfremdrift og seneste release-tilstand."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Opret stabile, canary- og m?lrettede kanaler med rollout-gr?nser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tildelinger",
|
||||
"description": "Fastg?r brugere, subbrugere eller kunder til en bestemt release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Udrulninger",
|
||||
"description": "Start udrulninger og promover vellykkede builds."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktiv?r m?lrettet opsamling og s?g i release-tidslinjeh?ndelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationer",
|
||||
"description": "Forbind GitHub-repositories, branches og Coolify-services."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Indstillinger",
|
||||
"description": "Konfigurer Release Managers GitHub-adgang og webhook-indstillinger."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- og Coolify-m?l",
|
||||
"assignments": "Pilot-tildelinger",
|
||||
"deployments": "F?rste udrulning",
|
||||
"replay": "Replay-opsamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Udrulninger",
|
||||
"timeline_events": "Tidslinjeh?ndelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Oversigt",
|
||||
"subtitle": "Aktuel kanalstatus, release-aktivitet og modulstatus.",
|
||||
"guided_setup": "Guidet ops?tning",
|
||||
"next_step": "N?ste: {step}",
|
||||
"ready": "Release Manager er klar til daglig drift.",
|
||||
"add_suggested_channel": "Tilføj foreslået kanal",
|
||||
"module_health_empty": "Modul-health snapshots vises, når probes er blevet registreret."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-indstillinger",
|
||||
"subtitle": "Konfigurer GitHub-tokenet, der bruges til private repositories og branch-opslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemmelighed",
|
||||
"github_webhook_secret": "GitHub webhook-hemmelighed",
|
||||
"configured": "Konfigureret",
|
||||
"not_configured": "Ikke konfigureret",
|
||||
"loaded_from": "Indl?st fra {variable}",
|
||||
"set_below": "Angiv {variable} nedenfor",
|
||||
"private_repositories_prefix": "Private repositories l?ses med serverens milj?variabel",
|
||||
"private_repositories_or": "eller modulets konfigurationsvariabel",
|
||||
"github_token_message": "Eksempel: github_pat_... med adgang til de private repositories, Release Manager udruller.",
|
||||
"github_token_placeholder": "Lad feltet v?re tomt for at beholde det eksisterende token",
|
||||
"github_api_url_message": "Konfigureret i ReleaseManager.github_api_url. Brug https://api.github.com medmindre GitHub Enterprise bruges.",
|
||||
"webhook_secret_message": "Valgfrit; lad feltet v?re tomt for at beholde den eksisterende hemmelighed.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemmelighed",
|
||||
"save": "Gem indstillinger",
|
||||
"back_to_integrations": "Tilbage til integrationer",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Tilføj GitHub-token",
|
||||
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
|
||||
"configure_this_in": "Konfigurer dette i",
|
||||
"server_environment_variable": "Serverens miljøvariabel",
|
||||
"takes_precedence": "har forrang, når den er sat.",
|
||||
"recommended_access": "Anbefalet adgang",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
|
||||
"github_api_url_title": "Tilføj GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Indtast en fuld HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-eksempel",
|
||||
"github_webhook_secret_title": "Tilføj GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Gem den delte secret i",
|
||||
"review_title": "Gennemgå GitHub-opsætning",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Mangler værdi"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen er ikke klar",
|
||||
"summary_prefix": "Din konto er tildelt",
|
||||
"summary_suffix": ", men kanalen mangler den konfiguration, der skal bruges for at indl?se dens release-image.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Tjekker igen om {seconds}s",
|
||||
"refresh_error": "Release-status kunne ikke opdateres. Det n?ste automatiske tjek pr?ver igen.",
|
||||
"ignore": "Ignorer de n?ste 5 minutter",
|
||||
"check_again": "Tjek igen",
|
||||
"logout": "Log ud",
|
||||
"base_image": "Basis-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du er nu p? {channel}",
|
||||
"summary_prefix": "Din konto er blevet tildelt release-kanalen",
|
||||
"summary_suffix": "Denne enhed husker, at du har set denne besked.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-version",
|
||||
"api_version": "API-version",
|
||||
"assigned_channel": "Tildelt kanal",
|
||||
"current_app_image": "Nuv?rende app-image",
|
||||
"current_api": "Nuv?rende API",
|
||||
"base_image": "Basis-image",
|
||||
"continue": "Forts?t"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nej",
|
||||
"off": "fra"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Rediger",
|
||||
"rollback": "Rul tilbage",
|
||||
"update": "Opdater",
|
||||
"create": "Opret",
|
||||
"clear": "Ryd",
|
||||
"assign": "Tildel",
|
||||
"remove": "Fjern",
|
||||
"test_access": "Test adgang",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promover",
|
||||
"enable": "Aktiver",
|
||||
"search": "Søg",
|
||||
"save_target": "Gem mål",
|
||||
"delete": "Slet",
|
||||
"back": "Tilbage",
|
||||
"next": "Næste"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Handlinger",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrolleret",
|
||||
"subject": "Emne",
|
||||
"reason": "Årsag",
|
||||
"expires": "Udløber",
|
||||
"deployment": "Deployment",
|
||||
"source": "Kilde",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "fuldt redigeret"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "bruger",
|
||||
"subuser": "medarbejder",
|
||||
"customer": "kunde",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "fejl",
|
||||
"warning": "advarsel",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployet",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiveret",
|
||||
"queued": "i kø",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "ikke konfigureret",
|
||||
"warning": "advarsel",
|
||||
"failed": "fejlet",
|
||||
"down": "nede",
|
||||
"error": "fejl",
|
||||
"unknown": "ukendt"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Opret og juster gradvise release-kanaler.",
|
||||
"rollout_percent": "{percent}% udrulning",
|
||||
"already_configured": "Allerede konfigureret",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Navn",
|
||||
"name_message": "Vises til operatører, når de gennemgår en kanal.",
|
||||
"name_placeholder": "Navn",
|
||||
"rollout": "Udrulning",
|
||||
"rollout_message": "0-100 procent af ikke-tildelt trafik.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchende release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchende API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
|
||||
"retention_days": "Opbevaringsdage",
|
||||
"retention_days_message": "1-365 dage.",
|
||||
"enabled": "Aktiveret",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiveret",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tildelinger",
|
||||
"subtitle": "Fastgør brugere, medarbejdere eller kunder til release-kanaler.",
|
||||
"subject_type": "Emnetype",
|
||||
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Søg på kundenummer eller navn, og vælg derefter den præcise kunde.",
|
||||
"customer_placeholder": "Søg kunde",
|
||||
"subject_id": "Emne-ID",
|
||||
"subject_id_message": "Eksempel: bruger-id 42",
|
||||
"subject_id_placeholder": "Emne-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Fastgjorte emner springer procentudrulningen over.",
|
||||
"reason": "Årsag",
|
||||
"reason_message": "Eksempel: canary-validering for kundesupport.",
|
||||
"reason_placeholder": "Årsag",
|
||||
"expires": "Udløber",
|
||||
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Start, promover og inspicer release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
|
||||
"select_target_or_manual": "Vælg mål eller manuel",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-valg",
|
||||
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
|
||||
"latest_from_branch": "Seneste fra branch",
|
||||
"specific_commit": "Specifik commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionslabel",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Eksempel: bruger-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalafgrænsning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalafgrænsning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Udløber",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Alvorlighed",
|
||||
"any_severity": "Alle alvorligheder",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub og Coolify",
|
||||
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
|
||||
"choose_repository": "Vælg repository",
|
||||
"add_health_url": "tilføj health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Åbn GitHub token-indstillinger",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domæne",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domæne",
|
||||
"https_domain_for_coolify": "HTTPS-domæne til Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nu",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Opret manglende service",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
|
||||
@@ -2547,6 +2547,412 @@
|
||||
"subtitle": "@:{'words.generated.konfiguration'} @:{'words.generated.der'} @:{'words.generated.xlvask'}-@.capitalize:{'words.generated.integration'}",
|
||||
"title": "@:{'words.generated.xlvask'}-@:{'words.generated.konfiguration'}",
|
||||
"username_desc": "@.capitalize:{'words.generated.der'} @:{'words.generated.benutzername'} @:{'words.generated.wird'} @:{'words.generated.zur'} @:{'words.generated.authentifizierung'} @:{'words.generated.der'} @:{'words.generated.api'}-@:{'words.generated.verbindung'} @:{'words.generated.verwendet'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Schrittweise API- und Front-End-Releases",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release-Manager-Aktionen werden an diese API gesendet. Verwende die Produktions-Kontroll-API, au?er du testest ein Staging-Backend.",
|
||||
"example": "Beispiel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-Endpunkt",
|
||||
"endpoint_message": "?ndere dies nur, wenn Release-Manager-Endpunkte auf der Ziel-API verf?gbar sind.",
|
||||
"endpoint_aria": "Release Manager Kontroll-API-URL",
|
||||
"use_tooltip": "Release-Daten von dieser API laden",
|
||||
"use": "Verwenden",
|
||||
"reset_tooltip": "Zur Standard-Kontroll-API zur?ckkehren",
|
||||
"reset": "Zur?cksetzen",
|
||||
"known_endpoints": "Bekannte Kontroll-API-Endpunkte"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "?bersicht",
|
||||
"description": "Kanalzustand, Einrichtungsfortschritt und aktueller Release-Status."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kan?le",
|
||||
"description": "Stabile, Canary- und Zielkan?le mit Rollout-Grenzen erstellen."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Zuweisungen",
|
||||
"description": "Benutzer, Unterbenutzer oder Kunden einem bestimmten Release-Kanal zuweisen."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Bereitstellungen",
|
||||
"description": "Bereitstellungen starten und erfolgreiche Builds promoten."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Gezielte Erfassung aktivieren und Release-Timeline-Ereignisse durchsuchen."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationen",
|
||||
"description": "GitHub-Repositories, Branches und Coolify-Dienste verbinden."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Einstellungen",
|
||||
"description": "GitHub-Zugriff und Webhook-Einstellungen des Release Managers konfigurieren."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kan?le",
|
||||
"targets": "GitHub- und Coolify-Ziele",
|
||||
"assignments": "Pilot-Zuweisungen",
|
||||
"deployments": "Erste Bereitstellung",
|
||||
"replay": "Replay-Erfassung"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kan?le",
|
||||
"targets": "Ziele",
|
||||
"deployments": "Bereitstellungen",
|
||||
"timeline_events": "Timeline-Ereignisse"
|
||||
},
|
||||
"overview": {
|
||||
"title": "?bersicht",
|
||||
"subtitle": "Aktueller Kanalzustand, Release-Aktivit?t und Modulstatus.",
|
||||
"guided_setup": "Gef?hrte Einrichtung",
|
||||
"next_step": "N?chster Schritt: {step}",
|
||||
"ready": "Release Manager ist f?r den t?glichen Betrieb bereit.",
|
||||
"add_suggested_channel": "Vorgeschlagenen Kanal hinzufügen",
|
||||
"module_health_empty": "Modul-Health-Snapshots erscheinen, nachdem Probes aufgezeichnet wurden."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release-Manager-Einstellungen",
|
||||
"subtitle": "GitHub-Token f?r private Repositories und Branch-Abfragen konfigurieren.",
|
||||
"github_token": "GitHub-Token",
|
||||
"github_api_url": "GitHub-API-URL",
|
||||
"webhook_secret": "Webhook-Secret",
|
||||
"github_webhook_secret": "GitHub-Webhook-Secret",
|
||||
"configured": "Konfiguriert",
|
||||
"not_configured": "Nicht konfiguriert",
|
||||
"loaded_from": "Geladen aus {variable}",
|
||||
"set_below": "{variable} unten festlegen",
|
||||
"private_repositories_prefix": "Private Repositories werden mit der Server-Umgebungsvariable gelesen",
|
||||
"private_repositories_or": "oder der Modul-Konfigurationsvariable",
|
||||
"github_token_message": "Beispiel: github_pat_... mit Zugriff auf die privaten Repositories, die Release Manager bereitstellt.",
|
||||
"github_token_placeholder": "Leer lassen, um das vorhandene Token zu behalten",
|
||||
"github_api_url_message": "Konfiguriert in ReleaseManager.github_api_url. https://api.github.com verwenden, sofern GitHub Enterprise nicht genutzt wird.",
|
||||
"webhook_secret_message": "Optional; leer lassen, um das vorhandene Secret zu behalten.",
|
||||
"webhook_secret_placeholder": "Webhook-HMAC-Secret",
|
||||
"save": "Einstellungen speichern",
|
||||
"back_to_integrations": "Zur?ck zu Integrationen",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-Token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub Webhook Secret",
|
||||
"review": "Prüfung"
|
||||
},
|
||||
"github_token_title": "GitHub-Token hinzufügen",
|
||||
"github_token_description": "Release Manager verwendet diesen Token, um private Repositories, Branches und Commits aufzulisten, bevor ein Deployment-Ziel gespeichert oder getestet wird.",
|
||||
"configure_this_in": "Dies konfigurieren in",
|
||||
"server_environment_variable": "Die Server-Umgebungsvariable",
|
||||
"takes_precedence": "hat Vorrang, wenn sie gesetzt ist.",
|
||||
"recommended_access": "Empfohlener Zugriff",
|
||||
"recommended_access_description": "Fine-grained GitHub-Token mit Contents: read-Zugriff für jedes Release-Repository.",
|
||||
"github_api_url_title": "GitHub API-URL hinzufügen",
|
||||
"github_api_url_description": "GitHub Cloud standardmäßig verwenden oder auf die REST-API-Basis-URL einer GitHub Enterprise Server-Installation zeigen.",
|
||||
"github_api_url_invalid": "Vollständige HTTP- oder HTTPS-URL eingeben.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-Beispiel",
|
||||
"github_webhook_secret_title": "GitHub Webhook Secret hinzufügen",
|
||||
"github_webhook_secret_description": "Dasselbe Secret in GitHub-Repository-Webhooks verwenden, damit Release Manager Deployment-Events vor der Verarbeitung verifizieren kann.",
|
||||
"github_webhook_url": "GitHub Webhook-URL:",
|
||||
"store_shared_secret_in": "Geteiltes Secret speichern in",
|
||||
"review_title": "GitHub-Konfiguration prüfen",
|
||||
"review_description": "Werte speichern und dann unter Integrationen ein privates Repository, einen Branch und einen bestimmten Commit oder den neuesten Commit auswählen, bevor der Zugriff getestet wird.",
|
||||
"ready": "Bereit",
|
||||
"needs_value": "Wert fehlt"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-Kanal ist nicht bereit",
|
||||
"summary_prefix": "Dein Konto ist zugewiesen zu",
|
||||
"summary_suffix": ", aber diesem Kanal fehlt die Konfiguration, um sein Release-Image zu laden.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Erneute Pr?fung in {seconds}s",
|
||||
"refresh_error": "Release-Status konnte nicht aktualisiert werden. Die n?chste automatische Pr?fung versucht es erneut.",
|
||||
"ignore": "N?chste 5 Minuten ignorieren",
|
||||
"check_again": "Erneut pr?fen",
|
||||
"logout": "Abmelden",
|
||||
"base_image": "Basis-Image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du bist jetzt auf {channel}",
|
||||
"summary_prefix": "Dein Konto wurde dem Release-Kanal",
|
||||
"summary_suffix": "zugewiesen. Dieses Ger?t merkt sich, dass du diesen Hinweis gesehen hast.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-Version",
|
||||
"api_version": "API-Version",
|
||||
"assigned_channel": "Zugewiesener Kanal",
|
||||
"current_app_image": "Aktuelles App-Image",
|
||||
"current_api": "Aktuelle API",
|
||||
"base_image": "Basis-Image",
|
||||
"continue": "Fortfahren"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nein",
|
||||
"off": "aus"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Bearbeiten",
|
||||
"rollback": "Zurückrollen",
|
||||
"update": "Aktualisieren",
|
||||
"create": "Erstellen",
|
||||
"clear": "Leeren",
|
||||
"assign": "Zuweisen",
|
||||
"remove": "Entfernen",
|
||||
"test_access": "Zugriff testen",
|
||||
"deploy": "Deployen",
|
||||
"promote": "Promoten",
|
||||
"enable": "Aktivieren",
|
||||
"search": "Suchen",
|
||||
"save_target": "Ziel speichern",
|
||||
"delete": "Löschen",
|
||||
"back": "Zurück",
|
||||
"next": "Weiter"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Rollout",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Aktionen",
|
||||
"module": "Module",
|
||||
"status": "Status",
|
||||
"checked": "Geprüft",
|
||||
"subject": "Subjekt",
|
||||
"reason": "Grund",
|
||||
"expires": "Läuft ab",
|
||||
"deployment": "Deployment",
|
||||
"source": "Quelle",
|
||||
"started": "Gestartet",
|
||||
"target": "Ziel",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "Frontend",
|
||||
"api": "API"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "Metadaten",
|
||||
"full_redacted": "vollständig redigiert"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "Benutzer",
|
||||
"subuser": "Mitarbeiter",
|
||||
"customer": "Kunde",
|
||||
"channel": "Kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "Fehler",
|
||||
"warning": "Warnung",
|
||||
"info": "Info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployt",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiviert",
|
||||
"queued": "in Warteschlange",
|
||||
"deploying": "deploying",
|
||||
"metadata": "Metadaten",
|
||||
"not_configured": "nicht konfiguriert",
|
||||
"warning": "Warnung",
|
||||
"failed": "fehlgeschlagen",
|
||||
"down": "down",
|
||||
"error": "Fehler",
|
||||
"unknown": "unbekannt"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Vorgeschlagener Wert",
|
||||
"suggestion": "Vorschlag",
|
||||
"use_typed_value": "Eingegebenen Wert verwenden",
|
||||
"custom": "Benutzerdefiniert",
|
||||
"api_url": "API URL",
|
||||
"frontend_url": "Frontend URL",
|
||||
"known_release_url": "Bekannte Release-URL",
|
||||
"deploy_here_with_coolify": "{app} hier mit Coolify deployen",
|
||||
"prepare_coolify_target": "Coolify-Ziel mit aktiviertem HTTPS/SSL vorbereiten",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-Repository",
|
||||
"owner_repository": "owner/repository",
|
||||
"git_branch": "Git branch",
|
||||
"deployment_branch": "Deployment branch",
|
||||
"github_commit": "GitHub commit",
|
||||
"git_commit": "Git commit",
|
||||
"use_specific_commit_sha": "Bestimmte Commit-SHA verwenden",
|
||||
"custom_commit": "Benutzerdefinierter Commit",
|
||||
"health_url": "Health URL",
|
||||
"release_health_check": "Release-Health-Check",
|
||||
"coolify_service": "Coolify service",
|
||||
"service_uuid": "Service UUID",
|
||||
"known_coolify_service_uuid": "Known Coolify service UUID",
|
||||
"control_api": "Kontroll-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-Endpunkt",
|
||||
"module": "Module",
|
||||
"timeline_module_key": "Timeline-Modulschlüssel"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "GitHub-Repositories konnten nicht geladen werden.",
|
||||
"github_branches": "GitHub-Branches konnten nicht geladen werden.",
|
||||
"repository_branch_required": "Repository und Branch sind erforderlich, bevor GitHub-Zugriff getestet werden kann.",
|
||||
"github_access_failed": "GitHub-Repository-Zugriffstest ist fehlgeschlagen."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanäle",
|
||||
"subtitle": "Graduale Release-Kanäle erstellen und anpassen.",
|
||||
"rollout_percent": "{percent}% rollout",
|
||||
"already_configured": "Bereits konfiguriert",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Example: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Name",
|
||||
"name_message": "Wird Operatoren bei der Kanalprüfung angezeigt.",
|
||||
"name_placeholder": "Name",
|
||||
"rollout": "Rollout",
|
||||
"rollout_message": "0-100 Prozent des nicht zugewiesenen Traffics.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"frontend_url_message": "Example: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend URL",
|
||||
"no_matching_release_url": "Keine passende Release-URL.",
|
||||
"api_url": "API URL",
|
||||
"api_url_message": "Example: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API URL",
|
||||
"no_matching_api_url": "Keine passende API-URL.",
|
||||
"replay_capture": "Replay-Erfassung",
|
||||
"replay_capture_message": "Metadaten sind für breite Kanäle sicherer.",
|
||||
"retention_days": "Aufbewahrungstage",
|
||||
"retention_days_message": "1-365 Tage.",
|
||||
"enabled": "Aktiviert",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiviert",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Zuweisungen",
|
||||
"subtitle": "Benutzer, Mitarbeiter oder Kunden an Release-Kanäle binden.",
|
||||
"subject_type": "Subjekttyp",
|
||||
"subject_type_message": "Identitätstyp zum Binden auswählen.",
|
||||
"customer": "Customer",
|
||||
"customer_message": "Nach Kundennummer oder Name suchen und dann den genauen Kunden auswählen.",
|
||||
"customer_placeholder": "Kunde suchen",
|
||||
"subject_id": "Subjekt-ID",
|
||||
"subject_id_message": "Beispiel: Benutzer-ID 42",
|
||||
"subject_id_placeholder": "Subjekt-ID",
|
||||
"channel": "Channel",
|
||||
"channel_message": "Gebundene Subjekte umgehen den prozentualen Rollout.",
|
||||
"reason": "Grund",
|
||||
"reason_message": "Beispiel: Canary-Validierung für Kundensupport.",
|
||||
"reason_placeholder": "Grund",
|
||||
"expires": "Läuft ab",
|
||||
"expires_message": "Leer lassen für kein automatisches Ablaufdatum."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Release-Deployments starten, promoten und prüfen.",
|
||||
"manual_deployment_record": "Manueller Deployment-Eintrag",
|
||||
"target": "Ziel",
|
||||
"target_message": "Gespeichertes Ziel wählen oder manuelle Felder editierbar lassen.",
|
||||
"select_target_or_manual": "Ziel oder manuell auswählen",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/front-end-vue",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-Auswahl",
|
||||
"commit_selection_message": "Neueste Version wird mit dem konfigurierten GitHub-Token als Branch-Head aufgelöst.",
|
||||
"latest_from_branch": "Neuester vom Branch",
|
||||
"specific_commit": "Bestimmter Commit",
|
||||
"specific_commit_message": "Example: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionslabel",
|
||||
"version_label_message": "Example: api-2026.05.19",
|
||||
"version_label_placeholder": "Versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Token-Zugriff auf das ausgewählte Repository, den Branch und Commit prüfen."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Gezielte Erfassung aktivieren und Fehler-Timelines Schritt für Schritt prüfen.",
|
||||
"capture_channel": "{channel} erfassen",
|
||||
"target_type": "Zieltyp",
|
||||
"target": "Ziel",
|
||||
"target_message": "Beispiel: Benutzer-ID 42 oder Kanal-Slug canary.",
|
||||
"target_placeholder": "Ziel-ID oder Kanal-Slug",
|
||||
"channel_scope": "Kanalbereich",
|
||||
"channel_scope_message": "Optional; begrenzt Replay-Erfassung auf einen Kanal.",
|
||||
"no_channel_scope": "Kein Kanalbereich",
|
||||
"capture_level": "Erfassungsstufe",
|
||||
"expires": "Läuft ab",
|
||||
"expires_message": "Leer lassen für manuelle Deaktivierung.",
|
||||
"trace_id": "Trace ID",
|
||||
"trace_id_message": "Example: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace id",
|
||||
"channel": "Channel",
|
||||
"channel_placeholder": "Channel",
|
||||
"principal_id": "Principal ID",
|
||||
"principal_id_placeholder": "Principal-ID",
|
||||
"module": "Module",
|
||||
"module_placeholder": "Module",
|
||||
"severity": "Schweregrad",
|
||||
"any_severity": "Beliebiger Schweregrad",
|
||||
"no_timeline_events": "Keine Timeline-Events entsprechen den aktuellen Filtern."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub und Coolify",
|
||||
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
|
||||
"choose_repository": "Repository auswählen",
|
||||
"add_health_url": "Health-URL hinzufügen",
|
||||
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
|
||||
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/backend-php",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Neueste: {commit}",
|
||||
"coolify_instance": "Coolify-Instanz",
|
||||
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
|
||||
"no_coolify_instance": "Keine Coolify-Instanz",
|
||||
"coolify_instance_placeholder": "Coolify-Instanz-ID",
|
||||
"coolify_service_uuid": "Coolify service UUID",
|
||||
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service UUID",
|
||||
"health_url": "Health URL",
|
||||
"health_url_message": "Example: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health URL",
|
||||
"ssl_domain": "SSL-Domain",
|
||||
"ssl_domain_message": "Example: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-Domain",
|
||||
"https_domain_for_coolify": "HTTPS-Domain für Coolify",
|
||||
"auto_deploy": "Auto-Deploy",
|
||||
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
|
||||
"deploy_now": "Jetzt deployen",
|
||||
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
|
||||
"create_missing_service": "Fehlenden Service erstellen",
|
||||
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
|
||||
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
|
||||
@@ -2271,6 +2271,412 @@
|
||||
"subtitle": "@.capitalize:{'words.generated.configuration'} @:{'words.generated.of'} @:{'words.replication.article.host_mention'} @:{'words.generated.xlvask'} @:{'words.generated.integration'}",
|
||||
"title": "@:{'words.generated.xlvask'} @:{'words.generated.configuration'}",
|
||||
"username_desc": "@.capitalize:{'words.replication.article.host_mention'} @:{'words.generated.username'} @:{'words.generated.is'} @:{'words.generated.used'} @:{'words.generated.to'} @:{'words.generated.authenticate'} @:{'words.replication.article.host_mention'} @:{'words.generated.api'} @:{'words.generated.connection'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradual API and Front-End releases",
|
||||
"control_api": {
|
||||
"title": "Control API",
|
||||
"tooltip": "Release Manager actions are sent to this API. Use the production control API unless testing a staged backend.",
|
||||
"example": "Example: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API endpoint",
|
||||
"endpoint_message": "Switch this only when Release Manager endpoints are available on the target API.",
|
||||
"endpoint_aria": "Release Manager control API URL",
|
||||
"use_tooltip": "Load release data from this API",
|
||||
"use": "Use",
|
||||
"reset_tooltip": "Return to the default control API",
|
||||
"reset": "Reset",
|
||||
"known_endpoints": "Known control API endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Overview",
|
||||
"description": "Channel health, setup progress, and the latest release state."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Channels",
|
||||
"description": "Create stable, canary, and targeted channels with rollout limits."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Assignments",
|
||||
"description": "Pin users, subusers, or customers to a specific release channel."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Deployments",
|
||||
"description": "Trigger deployments and promote successful builds."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Enable targeted capture and search release timeline events."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrations",
|
||||
"description": "Connect GitHub repositories, branches, and Coolify services."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Settings",
|
||||
"description": "Configure Release Manager GitHub access and webhook settings."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Channels",
|
||||
"targets": "GitHub and Coolify targets",
|
||||
"assignments": "Pilot assignments",
|
||||
"deployments": "First deployment",
|
||||
"replay": "Replay capture"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Channels",
|
||||
"targets": "Targets",
|
||||
"deployments": "Deployments",
|
||||
"timeline_events": "Timeline events"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Overview",
|
||||
"subtitle": "Current channel health, release activity, and module status.",
|
||||
"guided_setup": "Guided setup",
|
||||
"next_step": "Next: {step}",
|
||||
"ready": "Release Manager is ready for day-to-day operations.",
|
||||
"add_suggested_channel": "Add suggested channel",
|
||||
"module_health_empty": "Module health snapshots appear after probes have been recorded."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager Settings",
|
||||
"subtitle": "Configure the GitHub token used for private repositories and branch lookups.",
|
||||
"github_token": "GitHub token",
|
||||
"github_api_url": "GitHub API URL",
|
||||
"webhook_secret": "Webhook secret",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"configured": "Configured",
|
||||
"not_configured": "Not configured",
|
||||
"loaded_from": "Loaded from {variable}",
|
||||
"set_below": "Set {variable} below",
|
||||
"private_repositories_prefix": "Private repositories are read with the server environment variable",
|
||||
"private_repositories_or": "or the module config variable",
|
||||
"github_token_message": "Example: github_pat_... with access to the private repositories Release Manager deploys.",
|
||||
"github_token_placeholder": "Leave blank to keep the existing token",
|
||||
"github_api_url_message": "Configured in ReleaseManager.github_api_url. Use https://api.github.com unless using GitHub Enterprise.",
|
||||
"webhook_secret_message": "Optional; leave blank to keep the existing secret.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC secret",
|
||||
"save": "Save settings",
|
||||
"back_to_integrations": "Back to integrations",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub token",
|
||||
"github_api_url": "GitHub API URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Review"
|
||||
},
|
||||
"github_token_title": "Add GitHub token",
|
||||
"github_token_description": "Release Manager uses this token to list private repositories, branches, and commits before a deployment target is saved or tested.",
|
||||
"configure_this_in": "Configure this in",
|
||||
"server_environment_variable": "The server environment variable",
|
||||
"takes_precedence": "takes precedence when it is set.",
|
||||
"recommended_access": "Recommended access",
|
||||
"recommended_access_description": "Fine-grained GitHub token with Contents: read access for each release repository.",
|
||||
"github_api_url_title": "Add GitHub API URL",
|
||||
"github_api_url_description": "Use GitHub Cloud by default, or point this at the REST API base URL for a GitHub Enterprise Server installation.",
|
||||
"github_api_url_invalid": "Enter a full HTTP or HTTPS URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise example",
|
||||
"github_webhook_secret_title": "Add GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Use the same secret in GitHub repository webhooks so Release Manager can verify deployment events before handling them.",
|
||||
"github_webhook_url": "GitHub webhook URL:",
|
||||
"store_shared_secret_in": "Store the shared secret in",
|
||||
"review_title": "Review GitHub setup",
|
||||
"review_description": "Save the values, then use Integrations to select a private repository, branch, and specific commit or latest commit before testing access.",
|
||||
"ready": "Ready",
|
||||
"needs_value": "Needs value"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release channel is not ready",
|
||||
"summary_prefix": "Your account is assigned to",
|
||||
"summary_suffix": ", but that channel is missing the configuration needed to load its release image.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"api_url": "API URL",
|
||||
"checking_again": "Checking again in {seconds}s",
|
||||
"refresh_error": "Release status could not be refreshed. The next automatic check will try again.",
|
||||
"ignore": "Ignore next 5 minutes",
|
||||
"check_again": "Check again",
|
||||
"logout": "Logout",
|
||||
"base_image": "Base image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "You are now on {channel}",
|
||||
"summary_prefix": "Your account has been assigned to the",
|
||||
"summary_suffix": "release channel. This device will remember that you have seen this notice.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend version",
|
||||
"api_version": "API version",
|
||||
"assigned_channel": "Assigned channel",
|
||||
"current_app_image": "Current app image",
|
||||
"current_api": "Current API",
|
||||
"base_image": "Base image",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"common": {
|
||||
"yes": "yes",
|
||||
"no": "no",
|
||||
"off": "off"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"rollback": "Rollback",
|
||||
"update": "Update",
|
||||
"create": "Create",
|
||||
"clear": "Clear",
|
||||
"assign": "Assign",
|
||||
"remove": "Remove",
|
||||
"test_access": "Test access",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promote",
|
||||
"enable": "Enable",
|
||||
"search": "Search",
|
||||
"save_target": "Save target",
|
||||
"delete": "Delete",
|
||||
"back": "Back",
|
||||
"next": "Next"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Channel",
|
||||
"rollout": "Rollout",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Actions",
|
||||
"module": "Module",
|
||||
"status": "Status",
|
||||
"checked": "Checked",
|
||||
"subject": "Subject",
|
||||
"reason": "Reason",
|
||||
"expires": "Expires",
|
||||
"deployment": "Deployment",
|
||||
"source": "Source",
|
||||
"started": "Started",
|
||||
"target": "Target",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "full_redacted"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "user",
|
||||
"subuser": "subuser",
|
||||
"customer": "customer",
|
||||
"channel": "channel"
|
||||
},
|
||||
"severity": {
|
||||
"error": "error",
|
||||
"warning": "warning",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "active",
|
||||
"deployed": "deployed",
|
||||
"stable": "stable",
|
||||
"enabled": "enabled",
|
||||
"queued": "queued",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "not configured",
|
||||
"warning": "warning",
|
||||
"failed": "failed",
|
||||
"down": "down",
|
||||
"error": "error",
|
||||
"unknown": "unknown"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Suggested value",
|
||||
"suggestion": "Suggestion",
|
||||
"use_typed_value": "Use typed value",
|
||||
"custom": "Custom",
|
||||
"api_url": "API URL",
|
||||
"frontend_url": "Frontend URL",
|
||||
"known_release_url": "Known release URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} here with Coolify",
|
||||
"prepare_coolify_target": "Prepare a Coolify target with HTTPS/SSL enabled",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub repository",
|
||||
"owner_repository": "owner/repository",
|
||||
"git_branch": "Git branch",
|
||||
"deployment_branch": "Deployment branch",
|
||||
"github_commit": "GitHub commit",
|
||||
"git_commit": "Git commit",
|
||||
"use_specific_commit_sha": "Use specific commit SHA",
|
||||
"custom_commit": "Custom commit",
|
||||
"health_url": "Health URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify service",
|
||||
"service_uuid": "Service UUID",
|
||||
"known_coolify_service_uuid": "Known Coolify service UUID",
|
||||
"control_api": "Control API",
|
||||
"release_manager_api_endpoint": "Release Manager API endpoint",
|
||||
"module": "Module",
|
||||
"timeline_module_key": "Timeline module key"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Could not load GitHub repositories.",
|
||||
"github_branches": "Could not load GitHub branches.",
|
||||
"repository_branch_required": "Repository and branch are required before testing GitHub access.",
|
||||
"github_access_failed": "GitHub repository access test failed."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Channels",
|
||||
"subtitle": "Create and tune gradual release channels.",
|
||||
"rollout_percent": "{percent}% rollout",
|
||||
"already_configured": "Already configured",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Example: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Name",
|
||||
"name_message": "Shown to operators when reviewing a channel.",
|
||||
"name_placeholder": "Name",
|
||||
"rollout": "Rollout",
|
||||
"rollout_message": "0-100 percent of unassigned traffic.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"frontend_url_message": "Example: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend URL",
|
||||
"no_matching_release_url": "No matching release URL.",
|
||||
"api_url": "API URL",
|
||||
"api_url_message": "Example: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API URL",
|
||||
"no_matching_api_url": "No matching API URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata is safer for broad channels.",
|
||||
"retention_days": "Retention days",
|
||||
"retention_days_message": "1-365 days.",
|
||||
"enabled": "Enabled",
|
||||
"default_channel": "Default channel",
|
||||
"replay_enabled": "Replay enabled",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Assignments",
|
||||
"subtitle": "Pin users, subusers, or customers to release channels.",
|
||||
"subject_type": "Subject type",
|
||||
"subject_type_message": "Choose the identity type to pin.",
|
||||
"customer": "Customer",
|
||||
"customer_message": "Search by customer number or name, then select the exact customer.",
|
||||
"customer_placeholder": "Search customer",
|
||||
"subject_id": "Subject ID",
|
||||
"subject_id_message": "Example: user id 42",
|
||||
"subject_id_placeholder": "Subject id",
|
||||
"channel": "Channel",
|
||||
"channel_message": "Pinned subjects bypass percentage rollout.",
|
||||
"reason": "Reason",
|
||||
"reason_message": "Example: canary validation for customer support.",
|
||||
"reason_placeholder": "Reason",
|
||||
"expires": "Expires",
|
||||
"expires_message": "Leave blank for no automatic expiry."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Trigger, promote, and inspect release deployments.",
|
||||
"manual_deployment_record": "Manual deployment record",
|
||||
"target": "Target",
|
||||
"target_message": "Pick a saved target or keep manual fields editable.",
|
||||
"select_target_or_manual": "Select target or manual",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/front-end-vue",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit selection",
|
||||
"commit_selection_message": "Latest resolves to the branch head with the configured GitHub token.",
|
||||
"latest_from_branch": "Latest from branch",
|
||||
"specific_commit": "Specific commit",
|
||||
"specific_commit_message": "Example: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Version label",
|
||||
"version_label_message": "Example: api-2026.05.19",
|
||||
"version_label_placeholder": "version label",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Verify token access to the selected repository, branch, and commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Enable targeted capture and step through failure timelines.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Target type",
|
||||
"target": "Target",
|
||||
"target_message": "Example: user id 42 or channel slug canary.",
|
||||
"target_placeholder": "Target id or channel slug",
|
||||
"channel_scope": "Channel scope",
|
||||
"channel_scope_message": "Optional; limits replay capture to a channel.",
|
||||
"no_channel_scope": "No channel scope",
|
||||
"capture_level": "Capture level",
|
||||
"expires": "Expires",
|
||||
"expires_message": "Leave blank for manual disable.",
|
||||
"trace_id": "Trace ID",
|
||||
"trace_id_message": "Example: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace id",
|
||||
"channel": "Channel",
|
||||
"channel_placeholder": "Channel",
|
||||
"principal_id": "Principal ID",
|
||||
"principal_id_placeholder": "Principal id",
|
||||
"module": "Module",
|
||||
"module_placeholder": "Module",
|
||||
"severity": "Severity",
|
||||
"any_severity": "Any severity",
|
||||
"no_timeline_events": "No timeline events match the current filters."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub and Coolify",
|
||||
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
|
||||
"choose_repository": "Choose repository",
|
||||
"add_health_url": "add health URL",
|
||||
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
|
||||
"open_github_token_settings": "Open GitHub token settings",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/backend-php",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Latest: {commit}",
|
||||
"coolify_instance": "Coolify instance",
|
||||
"coolify_instance_message": "Leave empty for manual deployment records.",
|
||||
"no_coolify_instance": "No Coolify instance",
|
||||
"coolify_instance_placeholder": "Coolify instance id",
|
||||
"coolify_service_uuid": "Coolify service UUID",
|
||||
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service UUID",
|
||||
"health_url": "Health URL",
|
||||
"health_url_message": "Example: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health URL",
|
||||
"ssl_domain": "SSL domain",
|
||||
"ssl_domain_message": "Example: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL domain",
|
||||
"https_domain_for_coolify": "HTTPS domain for Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
|
||||
"deploy_now": "Deploy now",
|
||||
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
|
||||
"create_missing_service": "Create missing service",
|
||||
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
|
||||
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
|
||||
@@ -1496,6 +1496,412 @@
|
||||
"title": "@:{'templates.generated.compat.configuration.xlvask.title'}",
|
||||
"username": "@:auth.username",
|
||||
"username_desc": "@:{'templates.generated.compat.configuration.xlvask.username_desc'}"
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.subtitle'}",
|
||||
"control_api": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.control_api.title'}",
|
||||
"tooltip": "@:{'templates.generated.compat.configuration.release_manager.control_api.tooltip'}",
|
||||
"example": "@:{'templates.generated.compat.configuration.release_manager.control_api.example'}",
|
||||
"endpoint_label": "@:{'templates.generated.compat.configuration.release_manager.control_api.endpoint_label'}",
|
||||
"endpoint_message": "@:{'templates.generated.compat.configuration.release_manager.control_api.endpoint_message'}",
|
||||
"endpoint_aria": "@:{'templates.generated.compat.configuration.release_manager.control_api.endpoint_aria'}",
|
||||
"use_tooltip": "@:{'templates.generated.compat.configuration.release_manager.control_api.use_tooltip'}",
|
||||
"use": "@:{'templates.generated.compat.configuration.release_manager.control_api.use'}",
|
||||
"reset_tooltip": "@:{'templates.generated.compat.configuration.release_manager.control_api.reset_tooltip'}",
|
||||
"reset": "@:{'templates.generated.compat.configuration.release_manager.control_api.reset'}",
|
||||
"known_endpoints": "@:{'templates.generated.compat.configuration.release_manager.control_api.known_endpoints'}"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.overview.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.overview.description'}"
|
||||
},
|
||||
"channels": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.channels.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.channels.description'}"
|
||||
},
|
||||
"assignments": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.assignments.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.assignments.description'}"
|
||||
},
|
||||
"deployments": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.deployments.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.deployments.description'}"
|
||||
},
|
||||
"replay": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.replay.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.replay.description'}"
|
||||
},
|
||||
"integrations": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.integrations.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.integrations.description'}"
|
||||
},
|
||||
"settings": {
|
||||
"label": "@:{'templates.generated.compat.configuration.release_manager.tabs.settings.label'}",
|
||||
"description": "@:{'templates.generated.compat.configuration.release_manager.tabs.settings.description'}"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "@:{'templates.generated.compat.configuration.release_manager.setup.channels'}",
|
||||
"targets": "@:{'templates.generated.compat.configuration.release_manager.setup.targets'}",
|
||||
"assignments": "@:{'templates.generated.compat.configuration.release_manager.setup.assignments'}",
|
||||
"deployments": "@:{'templates.generated.compat.configuration.release_manager.setup.deployments'}",
|
||||
"replay": "@:{'templates.generated.compat.configuration.release_manager.setup.replay'}"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "@:{'templates.generated.compat.configuration.release_manager.stats.channels'}",
|
||||
"targets": "@:{'templates.generated.compat.configuration.release_manager.stats.targets'}",
|
||||
"deployments": "@:{'templates.generated.compat.configuration.release_manager.stats.deployments'}",
|
||||
"timeline_events": "@:{'templates.generated.compat.configuration.release_manager.stats.timeline_events'}"
|
||||
},
|
||||
"overview": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.overview.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.overview.subtitle'}",
|
||||
"guided_setup": "@:{'templates.generated.compat.configuration.release_manager.overview.guided_setup'}",
|
||||
"next_step": "@:{'templates.generated.compat.configuration.release_manager.overview.next_step'}",
|
||||
"ready": "@:{'templates.generated.compat.configuration.release_manager.overview.ready'}",
|
||||
"add_suggested_channel": "@:{'templates.generated.compat.configuration.release_manager.overview.add_suggested_channel'}",
|
||||
"module_health_empty": "@:{'templates.generated.compat.configuration.release_manager.overview.module_health_empty'}"
|
||||
},
|
||||
"settings": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.settings.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.settings.subtitle'}",
|
||||
"github_token": "@:{'templates.generated.compat.configuration.release_manager.settings.github_token'}",
|
||||
"github_api_url": "@:{'templates.generated.compat.configuration.release_manager.settings.github_api_url'}",
|
||||
"webhook_secret": "@:{'templates.generated.compat.configuration.release_manager.settings.webhook_secret'}",
|
||||
"github_webhook_secret": "@:{'templates.generated.compat.configuration.release_manager.settings.github_webhook_secret'}",
|
||||
"configured": "@:{'templates.generated.compat.configuration.release_manager.settings.configured'}",
|
||||
"not_configured": "@:{'templates.generated.compat.configuration.release_manager.settings.not_configured'}",
|
||||
"loaded_from": "@:{'templates.generated.compat.configuration.release_manager.settings.loaded_from'}",
|
||||
"set_below": "@:{'templates.generated.compat.configuration.release_manager.settings.set_below'}",
|
||||
"private_repositories_prefix": "@:{'templates.generated.compat.configuration.release_manager.settings.private_repositories_prefix'}",
|
||||
"private_repositories_or": "@:{'templates.generated.compat.configuration.release_manager.settings.private_repositories_or'}",
|
||||
"github_token_message": "@:{'templates.generated.compat.configuration.release_manager.settings.github_token_message'}",
|
||||
"github_token_placeholder": "@:{'templates.generated.compat.configuration.release_manager.settings.github_token_placeholder'}",
|
||||
"github_api_url_message": "@:{'templates.generated.compat.configuration.release_manager.settings.github_api_url_message'}",
|
||||
"webhook_secret_message": "@:{'templates.generated.compat.configuration.release_manager.settings.webhook_secret_message'}",
|
||||
"webhook_secret_placeholder": "@:{'templates.generated.compat.configuration.release_manager.settings.webhook_secret_placeholder'}",
|
||||
"save": "@:{'templates.generated.compat.configuration.release_manager.settings.save'}",
|
||||
"back_to_integrations": "@:{'templates.generated.compat.configuration.release_manager.settings.back_to_integrations'}",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.steps.github_token'}",
|
||||
"github_api_url": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.steps.github_api_url'}",
|
||||
"github_webhook_secret": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.steps.github_webhook_secret'}",
|
||||
"review": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.steps.review'}"
|
||||
},
|
||||
"github_token_title": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_token_title'}",
|
||||
"github_token_description": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_token_description'}",
|
||||
"configure_this_in": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.configure_this_in'}",
|
||||
"server_environment_variable": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.server_environment_variable'}",
|
||||
"takes_precedence": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.takes_precedence'}",
|
||||
"recommended_access": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.recommended_access'}",
|
||||
"recommended_access_description": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.recommended_access_description'}",
|
||||
"github_api_url_title": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_api_url_title'}",
|
||||
"github_api_url_description": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_api_url_description'}",
|
||||
"github_api_url_invalid": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_api_url_invalid'}",
|
||||
"github_cloud": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_cloud'}",
|
||||
"enterprise_example": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.enterprise_example'}",
|
||||
"github_webhook_secret_title": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_webhook_secret_title'}",
|
||||
"github_webhook_secret_description": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_webhook_secret_description'}",
|
||||
"github_webhook_url": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.github_webhook_url'}",
|
||||
"store_shared_secret_in": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.store_shared_secret_in'}",
|
||||
"review_title": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.review_title'}",
|
||||
"review_description": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.review_description'}",
|
||||
"ready": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.ready'}",
|
||||
"needs_value": "@:{'templates.generated.compat.configuration.release_manager.settings.guide.needs_value'}"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.kicker'}",
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.title'}",
|
||||
"summary_prefix": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.summary_prefix'}",
|
||||
"summary_suffix": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.summary_suffix'}",
|
||||
"frontend_url": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.frontend_url'}",
|
||||
"api_url": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.api_url'}",
|
||||
"checking_again": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.checking_again'}",
|
||||
"refresh_error": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.refresh_error'}",
|
||||
"ignore": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.ignore'}",
|
||||
"check_again": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.check_again'}",
|
||||
"logout": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.logout'}",
|
||||
"base_image": "@:{'templates.generated.compat.configuration.release_manager.channel_unavailable.base_image'}"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.kicker'}",
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.title'}",
|
||||
"summary_prefix": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.summary_prefix'}",
|
||||
"summary_suffix": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.summary_suffix'}",
|
||||
"frontend": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.frontend'}",
|
||||
"api": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.api'}",
|
||||
"frontend_version": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.frontend_version'}",
|
||||
"api_version": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.api_version'}",
|
||||
"assigned_channel": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.assigned_channel'}",
|
||||
"current_app_image": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.current_app_image'}",
|
||||
"current_api": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.current_api'}",
|
||||
"base_image": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.base_image'}",
|
||||
"continue": "@:{'templates.generated.compat.configuration.release_manager.channel_switched.continue'}"
|
||||
},
|
||||
"common": {
|
||||
"yes": "@:{'templates.generated.compat.configuration.release_manager.common.yes'}",
|
||||
"no": "@:{'templates.generated.compat.configuration.release_manager.common.no'}",
|
||||
"off": "@:{'templates.generated.compat.configuration.release_manager.common.off'}"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "@:{'templates.generated.compat.configuration.release_manager.actions.edit'}",
|
||||
"rollback": "@:{'templates.generated.compat.configuration.release_manager.actions.rollback'}",
|
||||
"update": "@:{'templates.generated.compat.configuration.release_manager.actions.update'}",
|
||||
"create": "@:{'templates.generated.compat.configuration.release_manager.actions.create'}",
|
||||
"clear": "@:{'templates.generated.compat.configuration.release_manager.actions.clear'}",
|
||||
"assign": "@:{'templates.generated.compat.configuration.release_manager.actions.assign'}",
|
||||
"remove": "@:{'templates.generated.compat.configuration.release_manager.actions.remove'}",
|
||||
"test_access": "@:{'templates.generated.compat.configuration.release_manager.actions.test_access'}",
|
||||
"deploy": "@:{'templates.generated.compat.configuration.release_manager.actions.deploy'}",
|
||||
"promote": "@:{'templates.generated.compat.configuration.release_manager.actions.promote'}",
|
||||
"enable": "@:{'templates.generated.compat.configuration.release_manager.actions.enable'}",
|
||||
"search": "@:{'templates.generated.compat.configuration.release_manager.actions.search'}",
|
||||
"save_target": "@:{'templates.generated.compat.configuration.release_manager.actions.save_target'}",
|
||||
"delete": "@:{'templates.generated.compat.configuration.release_manager.actions.delete'}",
|
||||
"back": "@:{'templates.generated.compat.configuration.release_manager.actions.back'}",
|
||||
"next": "@:{'templates.generated.compat.configuration.release_manager.actions.next'}"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "@:{'templates.generated.compat.configuration.release_manager.tables.channel'}",
|
||||
"rollout": "@:{'templates.generated.compat.configuration.release_manager.tables.rollout'}",
|
||||
"frontend": "@:{'templates.generated.compat.configuration.release_manager.tables.frontend'}",
|
||||
"api": "@:{'templates.generated.compat.configuration.release_manager.tables.api'}",
|
||||
"replay": "@:{'templates.generated.compat.configuration.release_manager.tables.replay'}",
|
||||
"actions": "@:{'templates.generated.compat.configuration.release_manager.tables.actions'}",
|
||||
"module": "@:{'templates.generated.compat.configuration.release_manager.tables.module'}",
|
||||
"status": "@:{'templates.generated.compat.configuration.release_manager.tables.status'}",
|
||||
"checked": "@:{'templates.generated.compat.configuration.release_manager.tables.checked'}",
|
||||
"subject": "@:{'templates.generated.compat.configuration.release_manager.tables.subject'}",
|
||||
"reason": "@:{'templates.generated.compat.configuration.release_manager.tables.reason'}",
|
||||
"expires": "@:{'templates.generated.compat.configuration.release_manager.tables.expires'}",
|
||||
"deployment": "@:{'templates.generated.compat.configuration.release_manager.tables.deployment'}",
|
||||
"source": "@:{'templates.generated.compat.configuration.release_manager.tables.source'}",
|
||||
"started": "@:{'templates.generated.compat.configuration.release_manager.tables.started'}",
|
||||
"target": "@:{'templates.generated.compat.configuration.release_manager.tables.target'}",
|
||||
"coolify": "@:{'templates.generated.compat.configuration.release_manager.tables.coolify'}",
|
||||
"auto": "@:{'templates.generated.compat.configuration.release_manager.tables.auto'}"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "@:{'templates.generated.compat.configuration.release_manager.values.app.frontend'}",
|
||||
"api": "@:{'templates.generated.compat.configuration.release_manager.values.app.api'}"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "@:{'templates.generated.compat.configuration.release_manager.values.capture_level.metadata'}",
|
||||
"full_redacted": "@:{'templates.generated.compat.configuration.release_manager.values.capture_level.full_redacted'}"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "@:{'templates.generated.compat.configuration.release_manager.values.subject_type.user'}",
|
||||
"subuser": "@:{'templates.generated.compat.configuration.release_manager.values.subject_type.subuser'}",
|
||||
"customer": "@:{'templates.generated.compat.configuration.release_manager.values.subject_type.customer'}",
|
||||
"channel": "@:{'templates.generated.compat.configuration.release_manager.values.subject_type.channel'}"
|
||||
},
|
||||
"severity": {
|
||||
"error": "@:{'templates.generated.compat.configuration.release_manager.values.severity.error'}",
|
||||
"warning": "@:{'templates.generated.compat.configuration.release_manager.values.severity.warning'}",
|
||||
"info": "@:{'templates.generated.compat.configuration.release_manager.values.severity.info'}"
|
||||
},
|
||||
"status": {
|
||||
"ok": "@:{'templates.generated.compat.configuration.release_manager.values.status.ok'}",
|
||||
"active": "@:{'templates.generated.compat.configuration.release_manager.values.status.active'}",
|
||||
"deployed": "@:{'templates.generated.compat.configuration.release_manager.values.status.deployed'}",
|
||||
"stable": "@:{'templates.generated.compat.configuration.release_manager.values.status.stable'}",
|
||||
"enabled": "@:{'templates.generated.compat.configuration.release_manager.values.status.enabled'}",
|
||||
"queued": "@:{'templates.generated.compat.configuration.release_manager.values.status.queued'}",
|
||||
"deploying": "@:{'templates.generated.compat.configuration.release_manager.values.status.deploying'}",
|
||||
"metadata": "@:{'templates.generated.compat.configuration.release_manager.values.status.metadata'}",
|
||||
"not_configured": "@:{'templates.generated.compat.configuration.release_manager.values.status.not_configured'}",
|
||||
"warning": "@:{'templates.generated.compat.configuration.release_manager.values.status.warning'}",
|
||||
"failed": "@:{'templates.generated.compat.configuration.release_manager.values.status.failed'}",
|
||||
"down": "@:{'templates.generated.compat.configuration.release_manager.values.status.down'}",
|
||||
"error": "@:{'templates.generated.compat.configuration.release_manager.values.status.error'}",
|
||||
"unknown": "@:{'templates.generated.compat.configuration.release_manager.values.status.unknown'}"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.suggested_value'}",
|
||||
"suggestion": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.suggestion'}",
|
||||
"use_typed_value": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.use_typed_value'}",
|
||||
"custom": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.custom'}",
|
||||
"api_url": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.api_url'}",
|
||||
"frontend_url": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.frontend_url'}",
|
||||
"known_release_url": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.known_release_url'}",
|
||||
"deploy_here_with_coolify": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.deploy_here_with_coolify'}",
|
||||
"prepare_coolify_target": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.prepare_coolify_target'}",
|
||||
"coolify_ssl": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.coolify_ssl'}",
|
||||
"github_repository": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.github_repository'}",
|
||||
"owner_repository": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.owner_repository'}",
|
||||
"git_branch": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.git_branch'}",
|
||||
"deployment_branch": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.deployment_branch'}",
|
||||
"github_commit": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.github_commit'}",
|
||||
"git_commit": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.git_commit'}",
|
||||
"use_specific_commit_sha": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.use_specific_commit_sha'}",
|
||||
"custom_commit": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.custom_commit'}",
|
||||
"health_url": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.health_url'}",
|
||||
"release_health_check": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.release_health_check'}",
|
||||
"coolify_service": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.coolify_service'}",
|
||||
"service_uuid": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.service_uuid'}",
|
||||
"known_coolify_service_uuid": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.known_coolify_service_uuid'}",
|
||||
"control_api": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.control_api'}",
|
||||
"release_manager_api_endpoint": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.release_manager_api_endpoint'}",
|
||||
"module": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.module'}",
|
||||
"timeline_module_key": "@:{'templates.generated.compat.configuration.release_manager.autocomplete.timeline_module_key'}"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "@:{'templates.generated.compat.configuration.release_manager.errors.github_repositories'}",
|
||||
"github_branches": "@:{'templates.generated.compat.configuration.release_manager.errors.github_branches'}",
|
||||
"repository_branch_required": "@:{'templates.generated.compat.configuration.release_manager.errors.repository_branch_required'}",
|
||||
"github_access_failed": "@:{'templates.generated.compat.configuration.release_manager.errors.github_access_failed'}"
|
||||
},
|
||||
"channels": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.channels.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.channels.subtitle'}",
|
||||
"rollout_percent": "@:{'templates.generated.compat.configuration.release_manager.channels.rollout_percent'}",
|
||||
"already_configured": "@:{'templates.generated.compat.configuration.release_manager.channels.already_configured'}",
|
||||
"slug": "@:{'templates.generated.compat.configuration.release_manager.channels.slug'}",
|
||||
"slug_message": "@:{'templates.generated.compat.configuration.release_manager.channels.slug_message'}",
|
||||
"slug_placeholder": "@:{'templates.generated.compat.configuration.release_manager.channels.slug_placeholder'}",
|
||||
"name": "@:{'templates.generated.compat.configuration.release_manager.channels.name'}",
|
||||
"name_message": "@:{'templates.generated.compat.configuration.release_manager.channels.name_message'}",
|
||||
"name_placeholder": "@:{'templates.generated.compat.configuration.release_manager.channels.name_placeholder'}",
|
||||
"rollout": "@:{'templates.generated.compat.configuration.release_manager.channels.rollout'}",
|
||||
"rollout_message": "@:{'templates.generated.compat.configuration.release_manager.channels.rollout_message'}",
|
||||
"frontend_url": "@:{'templates.generated.compat.configuration.release_manager.channels.frontend_url'}",
|
||||
"frontend_url_message": "@:{'templates.generated.compat.configuration.release_manager.channels.frontend_url_message'}",
|
||||
"frontend_url_placeholder": "@:{'templates.generated.compat.configuration.release_manager.channels.frontend_url_placeholder'}",
|
||||
"no_matching_release_url": "@:{'templates.generated.compat.configuration.release_manager.channels.no_matching_release_url'}",
|
||||
"api_url": "@:{'templates.generated.compat.configuration.release_manager.channels.api_url'}",
|
||||
"api_url_message": "@:{'templates.generated.compat.configuration.release_manager.channels.api_url_message'}",
|
||||
"api_url_placeholder": "@:{'templates.generated.compat.configuration.release_manager.channels.api_url_placeholder'}",
|
||||
"no_matching_api_url": "@:{'templates.generated.compat.configuration.release_manager.channels.no_matching_api_url'}",
|
||||
"replay_capture": "@:{'templates.generated.compat.configuration.release_manager.channels.replay_capture'}",
|
||||
"replay_capture_message": "@:{'templates.generated.compat.configuration.release_manager.channels.replay_capture_message'}",
|
||||
"retention_days": "@:{'templates.generated.compat.configuration.release_manager.channels.retention_days'}",
|
||||
"retention_days_message": "@:{'templates.generated.compat.configuration.release_manager.channels.retention_days_message'}",
|
||||
"enabled": "@:{'templates.generated.compat.configuration.release_manager.channels.enabled'}",
|
||||
"default_channel": "@:{'templates.generated.compat.configuration.release_manager.channels.default_channel'}",
|
||||
"replay_enabled": "@:{'templates.generated.compat.configuration.release_manager.channels.replay_enabled'}",
|
||||
"frontend_chip": "@:{'templates.generated.compat.configuration.release_manager.channels.frontend_chip'}",
|
||||
"api_chip": "@:{'templates.generated.compat.configuration.release_manager.channels.api_chip'}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.assignments.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.assignments.subtitle'}",
|
||||
"subject_type": "@:{'templates.generated.compat.configuration.release_manager.assignments.subject_type'}",
|
||||
"subject_type_message": "@:{'templates.generated.compat.configuration.release_manager.assignments.subject_type_message'}",
|
||||
"customer": "@:{'templates.generated.compat.configuration.release_manager.assignments.customer'}",
|
||||
"customer_message": "@:{'templates.generated.compat.configuration.release_manager.assignments.customer_message'}",
|
||||
"customer_placeholder": "@:{'templates.generated.compat.configuration.release_manager.assignments.customer_placeholder'}",
|
||||
"subject_id": "@:{'templates.generated.compat.configuration.release_manager.assignments.subject_id'}",
|
||||
"subject_id_message": "@:{'templates.generated.compat.configuration.release_manager.assignments.subject_id_message'}",
|
||||
"subject_id_placeholder": "@:{'templates.generated.compat.configuration.release_manager.assignments.subject_id_placeholder'}",
|
||||
"channel": "@:{'templates.generated.compat.configuration.release_manager.assignments.channel'}",
|
||||
"channel_message": "@:{'templates.generated.compat.configuration.release_manager.assignments.channel_message'}",
|
||||
"reason": "@:{'templates.generated.compat.configuration.release_manager.assignments.reason'}",
|
||||
"reason_message": "@:{'templates.generated.compat.configuration.release_manager.assignments.reason_message'}",
|
||||
"reason_placeholder": "@:{'templates.generated.compat.configuration.release_manager.assignments.reason_placeholder'}",
|
||||
"expires": "@:{'templates.generated.compat.configuration.release_manager.assignments.expires'}",
|
||||
"expires_message": "@:{'templates.generated.compat.configuration.release_manager.assignments.expires_message'}"
|
||||
},
|
||||
"deployments": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.deployments.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.deployments.subtitle'}",
|
||||
"manual_deployment_record": "@:{'templates.generated.compat.configuration.release_manager.deployments.manual_deployment_record'}",
|
||||
"target": "@:{'templates.generated.compat.configuration.release_manager.deployments.target'}",
|
||||
"target_message": "@:{'templates.generated.compat.configuration.release_manager.deployments.target_message'}",
|
||||
"select_target_or_manual": "@:{'templates.generated.compat.configuration.release_manager.deployments.select_target_or_manual'}",
|
||||
"channel": "@:{'templates.generated.compat.configuration.release_manager.deployments.channel'}",
|
||||
"app": "@:{'templates.generated.compat.configuration.release_manager.deployments.app'}",
|
||||
"repository": "@:{'templates.generated.compat.configuration.release_manager.deployments.repository'}",
|
||||
"repository_message": "@:{'templates.generated.compat.configuration.release_manager.deployments.repository_message'}",
|
||||
"repository_placeholder": "@:{'templates.generated.compat.configuration.release_manager.deployments.repository_placeholder'}",
|
||||
"branch": "@:{'templates.generated.compat.configuration.release_manager.deployments.branch'}",
|
||||
"branch_message": "@:{'templates.generated.compat.configuration.release_manager.deployments.branch_message'}",
|
||||
"branch_placeholder": "@:{'templates.generated.compat.configuration.release_manager.deployments.branch_placeholder'}",
|
||||
"commit_selection": "@:{'templates.generated.compat.configuration.release_manager.deployments.commit_selection'}",
|
||||
"commit_selection_message": "@:{'templates.generated.compat.configuration.release_manager.deployments.commit_selection_message'}",
|
||||
"latest_from_branch": "@:{'templates.generated.compat.configuration.release_manager.deployments.latest_from_branch'}",
|
||||
"specific_commit": "@:{'templates.generated.compat.configuration.release_manager.deployments.specific_commit'}",
|
||||
"specific_commit_message": "@:{'templates.generated.compat.configuration.release_manager.deployments.specific_commit_message'}",
|
||||
"commit_sha_placeholder": "@:{'templates.generated.compat.configuration.release_manager.deployments.commit_sha_placeholder'}",
|
||||
"version_label": "@:{'templates.generated.compat.configuration.release_manager.deployments.version_label'}",
|
||||
"version_label_message": "@:{'templates.generated.compat.configuration.release_manager.deployments.version_label_message'}",
|
||||
"version_label_placeholder": "@:{'templates.generated.compat.configuration.release_manager.deployments.version_label_placeholder'}",
|
||||
"commit_prefix": "@:{'templates.generated.compat.configuration.release_manager.deployments.commit_prefix'}",
|
||||
"test_access_tooltip": "@:{'templates.generated.compat.configuration.release_manager.deployments.test_access_tooltip'}"
|
||||
},
|
||||
"replay": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.replay.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.replay.subtitle'}",
|
||||
"capture_channel": "@:{'templates.generated.compat.configuration.release_manager.replay.capture_channel'}",
|
||||
"target_type": "@:{'templates.generated.compat.configuration.release_manager.replay.target_type'}",
|
||||
"target": "@:{'templates.generated.compat.configuration.release_manager.replay.target'}",
|
||||
"target_message": "@:{'templates.generated.compat.configuration.release_manager.replay.target_message'}",
|
||||
"target_placeholder": "@:{'templates.generated.compat.configuration.release_manager.replay.target_placeholder'}",
|
||||
"channel_scope": "@:{'templates.generated.compat.configuration.release_manager.replay.channel_scope'}",
|
||||
"channel_scope_message": "@:{'templates.generated.compat.configuration.release_manager.replay.channel_scope_message'}",
|
||||
"no_channel_scope": "@:{'templates.generated.compat.configuration.release_manager.replay.no_channel_scope'}",
|
||||
"capture_level": "@:{'templates.generated.compat.configuration.release_manager.replay.capture_level'}",
|
||||
"expires": "@:{'templates.generated.compat.configuration.release_manager.replay.expires'}",
|
||||
"expires_message": "@:{'templates.generated.compat.configuration.release_manager.replay.expires_message'}",
|
||||
"trace_id": "@:{'templates.generated.compat.configuration.release_manager.replay.trace_id'}",
|
||||
"trace_id_message": "@:{'templates.generated.compat.configuration.release_manager.replay.trace_id_message'}",
|
||||
"trace_id_placeholder": "@:{'templates.generated.compat.configuration.release_manager.replay.trace_id_placeholder'}",
|
||||
"channel": "@:{'templates.generated.compat.configuration.release_manager.replay.channel'}",
|
||||
"channel_placeholder": "@:{'templates.generated.compat.configuration.release_manager.replay.channel_placeholder'}",
|
||||
"principal_id": "@:{'templates.generated.compat.configuration.release_manager.replay.principal_id'}",
|
||||
"principal_id_placeholder": "@:{'templates.generated.compat.configuration.release_manager.replay.principal_id_placeholder'}",
|
||||
"module": "@:{'templates.generated.compat.configuration.release_manager.replay.module'}",
|
||||
"module_placeholder": "@:{'templates.generated.compat.configuration.release_manager.replay.module_placeholder'}",
|
||||
"severity": "@:{'templates.generated.compat.configuration.release_manager.replay.severity'}",
|
||||
"any_severity": "@:{'templates.generated.compat.configuration.release_manager.replay.any_severity'}",
|
||||
"no_timeline_events": "@:{'templates.generated.compat.configuration.release_manager.replay.no_timeline_events'}"
|
||||
},
|
||||
"integrations": {
|
||||
"title": "@:{'templates.generated.compat.configuration.release_manager.integrations.title'}",
|
||||
"subtitle": "@:{'templates.generated.compat.configuration.release_manager.integrations.subtitle'}",
|
||||
"choose_repository": "@:{'templates.generated.compat.configuration.release_manager.integrations.choose_repository'}",
|
||||
"add_health_url": "@:{'templates.generated.compat.configuration.release_manager.integrations.add_health_url'}",
|
||||
"github_token_not_configured": "@:{'templates.generated.compat.configuration.release_manager.integrations.github_token_not_configured'}",
|
||||
"open_github_token_settings": "@:{'templates.generated.compat.configuration.release_manager.integrations.open_github_token_settings'}",
|
||||
"channel": "@:{'templates.generated.compat.configuration.release_manager.integrations.channel'}",
|
||||
"app": "@:{'templates.generated.compat.configuration.release_manager.integrations.app'}",
|
||||
"repository": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository'}",
|
||||
"repository_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository_message'}",
|
||||
"repository_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.repository_placeholder'}",
|
||||
"branch": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch'}",
|
||||
"branch_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch_message'}",
|
||||
"branch_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.branch_placeholder'}",
|
||||
"latest_prefix": "@:{'templates.generated.compat.configuration.release_manager.integrations.latest_prefix'}",
|
||||
"coolify_instance": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance'}",
|
||||
"coolify_instance_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance_message'}",
|
||||
"no_coolify_instance": "@:{'templates.generated.compat.configuration.release_manager.integrations.no_coolify_instance'}",
|
||||
"coolify_instance_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_instance_placeholder'}",
|
||||
"coolify_service_uuid": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid'}",
|
||||
"coolify_service_uuid_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid_message'}",
|
||||
"coolify_service_uuid_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_service_uuid_placeholder'}",
|
||||
"health_url": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url'}",
|
||||
"health_url_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url_message'}",
|
||||
"health_url_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.health_url_placeholder'}",
|
||||
"ssl_domain": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain'}",
|
||||
"ssl_domain_message": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain_message'}",
|
||||
"ssl_domain_placeholder": "@:{'templates.generated.compat.configuration.release_manager.integrations.ssl_domain_placeholder'}",
|
||||
"https_domain_for_coolify": "@:{'templates.generated.compat.configuration.release_manager.integrations.https_domain_for_coolify'}",
|
||||
"auto_deploy": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_deploy'}",
|
||||
"auto_deploy_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.auto_deploy_tooltip'}",
|
||||
"coolify_ssl": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_ssl'}",
|
||||
"coolify_ssl_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.coolify_ssl_tooltip'}",
|
||||
"deploy_now": "@:{'templates.generated.compat.configuration.release_manager.integrations.deploy_now'}",
|
||||
"deploy_now_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.deploy_now_tooltip'}",
|
||||
"create_missing_service": "@:{'templates.generated.compat.configuration.release_manager.integrations.create_missing_service'}",
|
||||
"create_missing_service_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.create_missing_service_tooltip'}",
|
||||
"test_access_tooltip": "@:{'templates.generated.compat.configuration.release_manager.integrations.test_access_tooltip'}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
|
||||
@@ -2548,6 +2548,412 @@
|
||||
"subtitle": "@.capitalize:{'words.generated.konfigurasjon'} @:{'words.generated.av'} @:{'words.generated.xlvask'}-@:{'words.generated.integrasjonen'}",
|
||||
"title": "@:{'words.generated.xlvask'}-@:{'words.generated.konfigurasjon'}",
|
||||
"username_desc": "@:{'words.generated.brukernavnet'} @:{'words.generated.brukes'} @:{'words.generated.til'} @:{'words.generated.a'} @:{'words.generated.autentisere'} @:{'words.generated.api'}-@:{'words.generated.tilkoblingen'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvise API- og Front-End-utgivelser",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release Manager-handlinger sendes til denne API-en. Bruk produksjons-API-en med mindre du tester en staging-backend.",
|
||||
"example": "Eksempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endepunkt",
|
||||
"endpoint_message": "Endre dette bare n?r Release Manager-endepunkter er tilgjengelige p? m?l-API-en.",
|
||||
"endpoint_aria": "Release Manager kontroll-API URL",
|
||||
"use_tooltip": "Last release-data fra denne API-en",
|
||||
"use": "Bruk",
|
||||
"reset_tooltip": "G? tilbake til standard kontroll-API",
|
||||
"reset": "Tilbakestill",
|
||||
"known_endpoints": "Kjente kontroll-API-endepunkter"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Oversikt",
|
||||
"description": "Kanalhelse, oppsettsfremdrift og siste release-status."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Opprett stabile, canary- og m?lrettede kanaler med rollout-grenser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tildelinger",
|
||||
"description": "Knytt brukere, underbrukere eller kunder til en bestemt release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Utrullinger",
|
||||
"description": "Start utrullinger og promoter vellykkede bygg."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktiver m?lrettet innsamling og s?k i release-tidslinjehendelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrasjoner",
|
||||
"description": "Koble GitHub-repositories, branches og Coolify-tjenester."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Innstillinger",
|
||||
"description": "Konfigurer Release Managers GitHub-tilgang og webhook-innstillinger."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- og Coolify-m?l",
|
||||
"assignments": "Pilottildelinger",
|
||||
"deployments": "F?rste utrulling",
|
||||
"replay": "Replay-innsamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Utrullinger",
|
||||
"timeline_events": "Tidslinjehendelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Oversikt",
|
||||
"subtitle": "Aktuell kanalhelse, release-aktivitet og modulstatus.",
|
||||
"guided_setup": "Veiledet oppsett",
|
||||
"next_step": "Neste: {step}",
|
||||
"ready": "Release Manager er klar for daglig drift.",
|
||||
"add_suggested_channel": "Legg til foreslått kanal",
|
||||
"module_health_empty": "Modul-health snapshots vises når probes er registrert."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-innstillinger",
|
||||
"subtitle": "Konfigurer GitHub-tokenet som brukes for private repositories og branch-oppslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemmelighet",
|
||||
"github_webhook_secret": "GitHub webhook-hemmelighet",
|
||||
"configured": "Konfigurert",
|
||||
"not_configured": "Ikke konfigurert",
|
||||
"loaded_from": "Lastet fra {variable}",
|
||||
"set_below": "Angi {variable} nedenfor",
|
||||
"private_repositories_prefix": "Private repositories leses med serverens milj?variabel",
|
||||
"private_repositories_or": "eller modulens konfigurasjonsvariabel",
|
||||
"github_token_message": "Eksempel: github_pat_... med tilgang til de private repositories Release Manager ruller ut.",
|
||||
"github_token_placeholder": "La st? tomt for ? beholde eksisterende token",
|
||||
"github_api_url_message": "Konfigurert i ReleaseManager.github_api_url. Bruk https://api.github.com med mindre GitHub Enterprise brukes.",
|
||||
"webhook_secret_message": "Valgfritt; la st? tomt for ? beholde eksisterende hemmelighet.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemmelighet",
|
||||
"save": "Lagre innstillinger",
|
||||
"back_to_integrations": "Tilbake til integrasjoner",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Legg til GitHub-token",
|
||||
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
|
||||
"configure_this_in": "Konfigurer dette i",
|
||||
"server_environment_variable": "Serverens miljøvariabel",
|
||||
"takes_precedence": "har forrang når den er satt.",
|
||||
"recommended_access": "Anbefalt tilgang",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
|
||||
"github_api_url_title": "Legg til GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Skriv inn en full HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-eksempel",
|
||||
"github_webhook_secret_title": "Legg til GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Lagre den delte secreten i",
|
||||
"review_title": "Gå gjennom GitHub-oppsett",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Mangler værdi"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen er ikke klar",
|
||||
"summary_prefix": "Kontoen din er tildelt",
|
||||
"summary_suffix": ", men kanalen mangler konfigurasjonen som trengs for ? laste release-imaget.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Sjekker igjen om {seconds}s",
|
||||
"refresh_error": "Release-status kunne ikke oppdateres. Neste automatiske sjekk pr?ver igjen.",
|
||||
"ignore": "Ignorer de neste 5 minuttene",
|
||||
"check_again": "Sjekk igjen",
|
||||
"logout": "Logg ut",
|
||||
"base_image": "Basis-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du er n? p? {channel}",
|
||||
"summary_prefix": "Kontoen din er tildelt release-kanalen",
|
||||
"summary_suffix": "Denne enheten husker at du har sett denne meldingen.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-versjon",
|
||||
"api_version": "API-versjon",
|
||||
"assigned_channel": "Tildelt kanal",
|
||||
"current_app_image": "N?v?rende app-image",
|
||||
"current_api": "N?v?rende API",
|
||||
"base_image": "Basis-image",
|
||||
"continue": "Fortsett"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nei",
|
||||
"off": "av"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Rediger",
|
||||
"rollback": "Rull tilbake",
|
||||
"update": "Opdater",
|
||||
"create": "Opprett",
|
||||
"clear": "Tøm",
|
||||
"assign": "Tildel",
|
||||
"remove": "Fjern",
|
||||
"test_access": "Test tilgang",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promoter",
|
||||
"enable": "Aktiver",
|
||||
"search": "Søk",
|
||||
"save_target": "Lagre mål",
|
||||
"delete": "Slett",
|
||||
"back": "Tilbake",
|
||||
"next": "Neste"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Handlinger",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrollert",
|
||||
"subject": "Emne",
|
||||
"reason": "Årsag",
|
||||
"expires": "Utløper",
|
||||
"deployment": "Deployment",
|
||||
"source": "Kilde",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "fullt redigert"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "bruker",
|
||||
"subuser": "medarbeider",
|
||||
"customer": "kunde",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "feil",
|
||||
"warning": "advarsel",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployet",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktivert",
|
||||
"queued": "i kø",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "ikke konfigurert",
|
||||
"warning": "advarsel",
|
||||
"failed": "feilet",
|
||||
"down": "nede",
|
||||
"error": "feil",
|
||||
"unknown": "ukjent"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Opprett og juster gradvise release-kanaler.",
|
||||
"rollout_percent": "{percent}% udrulning",
|
||||
"already_configured": "Allerede konfigurert",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Navn",
|
||||
"name_message": "Vises til operatører når de gjennomgår en kanal.",
|
||||
"name_placeholder": "Navn",
|
||||
"rollout": "Udrulning",
|
||||
"rollout_message": "0-100 prosent av ikke-tildelt trafikk.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchende release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchende API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
|
||||
"retention_days": "Oppbevaringsdager",
|
||||
"retention_days_message": "1-365 dage.",
|
||||
"enabled": "Aktivert",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktivert",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tildelinger",
|
||||
"subtitle": "Fest brukere, medarbeidere eller kunder til release-kanaler.",
|
||||
"subject_type": "Emnetype",
|
||||
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Søk på kundenummer eller navn, og velg deretter nøyaktig kunde.",
|
||||
"customer_placeholder": "Søk kunde",
|
||||
"subject_id": "Emne-ID",
|
||||
"subject_id_message": "Eksempel: bruker-id 42",
|
||||
"subject_id_placeholder": "Emne-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Festede emner hopper over prosentutrullingen.",
|
||||
"reason": "Årsag",
|
||||
"reason_message": "Eksempel: canary-validering for kundesupport.",
|
||||
"reason_placeholder": "Årsag",
|
||||
"expires": "Utløper",
|
||||
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Start, promoter og inspiser release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
|
||||
"select_target_or_manual": "Velg mål eller manuelt",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-valg",
|
||||
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
|
||||
"latest_from_branch": "Siste fra branch",
|
||||
"specific_commit": "Spesifikk commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versjonsetikett",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Eksempel: bruker-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalavgrensning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalavgrensning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Utløper",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Alvorlighet",
|
||||
"any_severity": "Alle alvorligheder",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub og Coolify",
|
||||
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
|
||||
"choose_repository": "Velg repository",
|
||||
"add_health_url": "legg til health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Åpne GitHub token-innstillinger",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domene",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domene",
|
||||
"https_domain_for_coolify": "HTTPS-domæne til Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nå",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Opprett manglende service",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
|
||||
@@ -2598,6 +2598,412 @@
|
||||
"subtitle": "@.capitalize:{'words.generated.konfiguration'} @:{'words.generated.af'} @:{'words.generated.xlvask'} @:{'words.generated.integrationen'}",
|
||||
"title": "@:{'words.generated.xlvask'} @:{'words.generated.konfiguration'}",
|
||||
"username_desc": "Användarnamnet @:{'words.generated.anvands'} @:{'words.generated.for'} @:{'words.generated.att'} @:{'words.generated.autentisera'} @:{'words.generated.api'}-@:{'words.generated.anslutningen'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvisa API- och Front-End-versioner",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release Manager-?tg?rder skickas till detta API. Anv?nd produktions-API:t om du inte testar en staging-backend.",
|
||||
"example": "Exempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endpoint",
|
||||
"endpoint_message": "?ndra detta endast n?r Release Manager-endpoints finns p? m?l-API:t.",
|
||||
"endpoint_aria": "Release Manager kontroll-API URL",
|
||||
"use_tooltip": "L?s in release-data fr?n detta API",
|
||||
"use": "Anv?nd",
|
||||
"reset_tooltip": "G? tillbaka till standard kontroll-API",
|
||||
"reset": "?terst?ll",
|
||||
"known_endpoints": "K?nda kontroll-API-endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "?versikt",
|
||||
"description": "Kanalh?lsa, installationsstatus och senaste release-l?ge."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Skapa stabila, canary- och riktade kanaler med rollout-gr?nser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tilldelningar",
|
||||
"description": "Koppla anv?ndare, underanv?ndare eller kunder till en specifik release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Utrullningar",
|
||||
"description": "Starta utrullningar och promovera lyckade byggen."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktivera riktad insamling och s?k i release-tidslinjeh?ndelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationer",
|
||||
"description": "Anslut GitHub-repositories, branches och Coolify-tj?nster."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Inst?llningar",
|
||||
"description": "Konfigurera Release Managers GitHub-?tkomst och webhook-inst?llningar."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- och Coolify-m?l",
|
||||
"assignments": "Pilottilldelningar",
|
||||
"deployments": "F?rsta utrullningen",
|
||||
"replay": "Replay-insamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Utrullningar",
|
||||
"timeline_events": "Tidslinjeh?ndelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "?versikt",
|
||||
"subtitle": "Aktuell kanalh?lsa, release-aktivitet och modulstatus.",
|
||||
"guided_setup": "Guidad installation",
|
||||
"next_step": "N?sta: {step}",
|
||||
"ready": "Release Manager ?r klar f?r daglig drift.",
|
||||
"add_suggested_channel": "Lägg till föreslagen kanal",
|
||||
"module_health_empty": "Modulhälsosnapshots visas när probes har registrerats."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-inst?llningar",
|
||||
"subtitle": "Konfigurera GitHub-token som anv?nds f?r privata repositories och branch-uppslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemlighet",
|
||||
"github_webhook_secret": "GitHub webhook-hemlighet",
|
||||
"configured": "Konfigurerad",
|
||||
"not_configured": "Inte konfigurerad",
|
||||
"loaded_from": "Inl?st fr?n {variable}",
|
||||
"set_below": "Ange {variable} nedan",
|
||||
"private_repositories_prefix": "Privata repositories l?ses med serverns milj?variabel",
|
||||
"private_repositories_or": "eller modulens konfigurationsvariabel",
|
||||
"github_token_message": "Exempel: github_pat_... med ?tkomst till de privata repositories som Release Manager distribuerar.",
|
||||
"github_token_placeholder": "L?mna tomt f?r att beh?lla befintlig token",
|
||||
"github_api_url_message": "Konfigurerad i ReleaseManager.github_api_url. Anv?nd https://api.github.com om du inte anv?nder GitHub Enterprise.",
|
||||
"webhook_secret_message": "Valfritt; l?mna tomt f?r att beh?lla befintlig hemlighet.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemlighet",
|
||||
"save": "Spara inst?llningar",
|
||||
"back_to_integrations": "Tillbaka till integrationer",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Lägg till GitHub-token",
|
||||
"github_token_description": "Release Manager använder denna token för att lista privata repositories, branches och commits innan ett deployment-mål sparas eller testas.",
|
||||
"configure_this_in": "Konfigurera detta i",
|
||||
"server_environment_variable": "Serverns miljövariabel",
|
||||
"takes_precedence": "har företräde när den är satt.",
|
||||
"recommended_access": "Rekommenderad åtkomst",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-åtkomst till varje release-repository.",
|
||||
"github_api_url_title": "Lägg till GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Ange en fullständig HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-exempel",
|
||||
"github_webhook_secret_title": "Lägg till GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Spara den delade secret i",
|
||||
"review_title": "Granska GitHub-konfiguration",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Saknar värde"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen ?r inte klar",
|
||||
"summary_prefix": "Ditt konto ?r tilldelat",
|
||||
"summary_suffix": ", men kanalen saknar konfigurationen som beh?vs f?r att l?sa in dess release-image.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Kontrollerar igen om {seconds}s",
|
||||
"refresh_error": "Release-status kunde inte uppdateras. N?sta automatiska kontroll f?rs?ker igen.",
|
||||
"ignore": "Ignorera de kommande 5 minuterna",
|
||||
"check_again": "Kontrollera igen",
|
||||
"logout": "Logga ut",
|
||||
"base_image": "Bas-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du ?r nu p? {channel}",
|
||||
"summary_prefix": "Ditt konto har tilldelats release-kanalen",
|
||||
"summary_suffix": "Den h?r enheten kommer ih?g att du har sett detta meddelande.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-version",
|
||||
"api_version": "API-version",
|
||||
"assigned_channel": "Tilldelad kanal",
|
||||
"current_app_image": "Nuvarande app-image",
|
||||
"current_api": "Nuvarande API",
|
||||
"base_image": "Bas-image",
|
||||
"continue": "Forts?tt"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nej",
|
||||
"off": "av"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Redigera",
|
||||
"rollback": "Rulla tillbaka",
|
||||
"update": "Uppdatera",
|
||||
"create": "Skapa",
|
||||
"clear": "Rensa",
|
||||
"assign": "Tilldela",
|
||||
"remove": "Ta bort",
|
||||
"test_access": "Testa åtkomst",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promota",
|
||||
"enable": "Aktivera",
|
||||
"search": "Sök",
|
||||
"save_target": "Spara mål",
|
||||
"delete": "Radera",
|
||||
"back": "Tillbaka",
|
||||
"next": "Nästa"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Åtgärder",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrollerad",
|
||||
"subject": "Emne",
|
||||
"reason": "Orsak",
|
||||
"expires": "Upphör",
|
||||
"deployment": "Deployment",
|
||||
"source": "Källa",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "helt redigerad"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "användare",
|
||||
"subuser": "medarbetare",
|
||||
"customer": "kund",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "fel",
|
||||
"warning": "varning",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployad",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiverad",
|
||||
"queued": "i kö",
|
||||
"deploying": "deployar",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "inte konfigurerad",
|
||||
"warning": "varning",
|
||||
"failed": "misslyckad",
|
||||
"down": "nere",
|
||||
"error": "fel",
|
||||
"unknown": "okänd"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Skapa och justera gradvisa release-kanaler.",
|
||||
"rollout_percent": "{percent}% utrullning",
|
||||
"already_configured": "Redan konfigurerad",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Namn",
|
||||
"name_message": "Visas för operatörer när de granskar en kanal.",
|
||||
"name_placeholder": "Namn",
|
||||
"rollout": "Utrullning",
|
||||
"rollout_message": "0-100 procent av otilldelad trafik.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchande release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchande API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata är säkrare för breda kanaler.",
|
||||
"retention_days": "Lagringsdagar",
|
||||
"retention_days_message": "1-365 dagar.",
|
||||
"enabled": "Aktiverad",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiverad",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tilldelningar",
|
||||
"subtitle": "Fäst användare, medarbetare eller kunder till release-kanaler.",
|
||||
"subject_type": "Ämnestyp",
|
||||
"subject_type_message": "Välj identitetstypen som ska fästas.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Sök på kundnummer eller namn och välj sedan exakt kund.",
|
||||
"customer_placeholder": "Sök kund",
|
||||
"subject_id": "Ämnes-ID",
|
||||
"subject_id_message": "Exempel: användar-id 42",
|
||||
"subject_id_placeholder": "Ämnes-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Fästa ämnen hoppar över procentutrullningen.",
|
||||
"reason": "Orsak",
|
||||
"reason_message": "Exempel: canary-validering för kundsupport.",
|
||||
"reason_placeholder": "Orsak",
|
||||
"expires": "Upphör",
|
||||
"expires_message": "Lämna tomt för ingen automatisk upphörning."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Starta, promota och inspektera release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Välj ett sparat mål eller behåll manuella fält redigerbara.",
|
||||
"select_target_or_manual": "Vælg mål eller manuel",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-val",
|
||||
"commit_selection_message": "Senaste slås upp som branchens head med konfigurerad GitHub-token.",
|
||||
"latest_from_branch": "Senaste från branch",
|
||||
"specific_commit": "Specifik commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionsetikett",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionsetikett",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktivera riktad capture och gå igenom feltimelines steg för steg.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Exempel: användar-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalavgränsning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalavgränsning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Upphör",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Allvarlighetsgrad",
|
||||
"any_severity": "Alla allvarlighetsgrader",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub och Coolify",
|
||||
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
|
||||
"choose_repository": "Välj repository",
|
||||
"add_health_url": "lägg till health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Öppna GitHub token-inställningar",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domän",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domän",
|
||||
"https_domain_for_coolify": "HTTPS-domän för Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nu",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Skapa saknad tjänst",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
|
||||
@@ -1495,6 +1495,412 @@
|
||||
},
|
||||
"company_id": "Company ID",
|
||||
"company_id_desc": "The Workfeed company identifier used for API synchronization."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvise API- og Front-End-udgivelser",
|
||||
"control_api": {
|
||||
"title": "Kontrol-API",
|
||||
"tooltip": "Release Manager-handlinger sendes til denne API. Brug produktions-API'en, medmindre du tester en staging-backend.",
|
||||
"example": "Eksempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endpoint",
|
||||
"endpoint_message": "Skift kun dette, n?r Release Manager-endpoints er tilg?ngelige p? m?l-API'en.",
|
||||
"endpoint_aria": "Release Manager kontrol-API URL",
|
||||
"use_tooltip": "Indl?s release-data fra denne API",
|
||||
"use": "Brug",
|
||||
"reset_tooltip": "Vend tilbage til standard kontrol-API",
|
||||
"reset": "Nulstil",
|
||||
"known_endpoints": "Kendte kontrol-API-endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Oversigt",
|
||||
"description": "Kanalstatus, ops?tningsfremdrift og seneste release-tilstand."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Opret stabile, canary- og m?lrettede kanaler med rollout-gr?nser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tildelinger",
|
||||
"description": "Fastg?r brugere, subbrugere eller kunder til en bestemt release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Udrulninger",
|
||||
"description": "Start udrulninger og promover vellykkede builds."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktiv?r m?lrettet opsamling og s?g i release-tidslinjeh?ndelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationer",
|
||||
"description": "Forbind GitHub-repositories, branches og Coolify-services."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Indstillinger",
|
||||
"description": "Konfigurer Release Managers GitHub-adgang og webhook-indstillinger."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- og Coolify-m?l",
|
||||
"assignments": "Pilot-tildelinger",
|
||||
"deployments": "F?rste udrulning",
|
||||
"replay": "Replay-opsamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Udrulninger",
|
||||
"timeline_events": "Tidslinjeh?ndelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Oversigt",
|
||||
"subtitle": "Aktuel kanalstatus, release-aktivitet og modulstatus.",
|
||||
"guided_setup": "Guidet ops?tning",
|
||||
"next_step": "N?ste: {step}",
|
||||
"ready": "Release Manager er klar til daglig drift.",
|
||||
"add_suggested_channel": "Tilføj foreslået kanal",
|
||||
"module_health_empty": "Modul-health snapshots vises, når probes er blevet registreret."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-indstillinger",
|
||||
"subtitle": "Konfigurer GitHub-tokenet, der bruges til private repositories og branch-opslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemmelighed",
|
||||
"github_webhook_secret": "GitHub webhook-hemmelighed",
|
||||
"configured": "Konfigureret",
|
||||
"not_configured": "Ikke konfigureret",
|
||||
"loaded_from": "Indl?st fra {variable}",
|
||||
"set_below": "Angiv {variable} nedenfor",
|
||||
"private_repositories_prefix": "Private repositories l?ses med serverens milj?variabel",
|
||||
"private_repositories_or": "eller modulets konfigurationsvariabel",
|
||||
"github_token_message": "Eksempel: github_pat_... med adgang til de private repositories, Release Manager udruller.",
|
||||
"github_token_placeholder": "Lad feltet v?re tomt for at beholde det eksisterende token",
|
||||
"github_api_url_message": "Konfigureret i ReleaseManager.github_api_url. Brug https://api.github.com medmindre GitHub Enterprise bruges.",
|
||||
"webhook_secret_message": "Valgfrit; lad feltet v?re tomt for at beholde den eksisterende hemmelighed.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemmelighed",
|
||||
"save": "Gem indstillinger",
|
||||
"back_to_integrations": "Tilbage til integrationer",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Tilføj GitHub-token",
|
||||
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
|
||||
"configure_this_in": "Konfigurer dette i",
|
||||
"server_environment_variable": "Serverens miljøvariabel",
|
||||
"takes_precedence": "har forrang, når den er sat.",
|
||||
"recommended_access": "Anbefalet adgang",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
|
||||
"github_api_url_title": "Tilføj GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Indtast en fuld HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-eksempel",
|
||||
"github_webhook_secret_title": "Tilføj GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Gem den delte secret i",
|
||||
"review_title": "Gennemgå GitHub-opsætning",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Mangler værdi"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen er ikke klar",
|
||||
"summary_prefix": "Din konto er tildelt",
|
||||
"summary_suffix": ", men kanalen mangler den konfiguration, der skal bruges for at indl?se dens release-image.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Tjekker igen om {seconds}s",
|
||||
"refresh_error": "Release-status kunne ikke opdateres. Det n?ste automatiske tjek pr?ver igen.",
|
||||
"ignore": "Ignorer de n?ste 5 minutter",
|
||||
"check_again": "Tjek igen",
|
||||
"logout": "Log ud",
|
||||
"base_image": "Basis-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du er nu p? {channel}",
|
||||
"summary_prefix": "Din konto er blevet tildelt release-kanalen",
|
||||
"summary_suffix": "Denne enhed husker, at du har set denne besked.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-version",
|
||||
"api_version": "API-version",
|
||||
"assigned_channel": "Tildelt kanal",
|
||||
"current_app_image": "Nuv?rende app-image",
|
||||
"current_api": "Nuv?rende API",
|
||||
"base_image": "Basis-image",
|
||||
"continue": "Forts?t"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nej",
|
||||
"off": "fra"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Rediger",
|
||||
"rollback": "Rul tilbage",
|
||||
"update": "Opdater",
|
||||
"create": "Opret",
|
||||
"clear": "Ryd",
|
||||
"assign": "Tildel",
|
||||
"remove": "Fjern",
|
||||
"test_access": "Test adgang",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promover",
|
||||
"enable": "Aktiver",
|
||||
"search": "Søg",
|
||||
"save_target": "Gem mål",
|
||||
"delete": "Slet",
|
||||
"back": "Tilbage",
|
||||
"next": "Næste"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Handlinger",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrolleret",
|
||||
"subject": "Emne",
|
||||
"reason": "Årsag",
|
||||
"expires": "Udløber",
|
||||
"deployment": "Deployment",
|
||||
"source": "Kilde",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "fuldt redigeret"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "bruger",
|
||||
"subuser": "medarbejder",
|
||||
"customer": "kunde",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "fejl",
|
||||
"warning": "advarsel",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployet",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiveret",
|
||||
"queued": "i kø",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "ikke konfigureret",
|
||||
"warning": "advarsel",
|
||||
"failed": "fejlet",
|
||||
"down": "nede",
|
||||
"error": "fejl",
|
||||
"unknown": "ukendt"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Opret og juster gradvise release-kanaler.",
|
||||
"rollout_percent": "{percent}% udrulning",
|
||||
"already_configured": "Allerede konfigureret",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Navn",
|
||||
"name_message": "Vises til operatører, når de gennemgår en kanal.",
|
||||
"name_placeholder": "Navn",
|
||||
"rollout": "Udrulning",
|
||||
"rollout_message": "0-100 procent af ikke-tildelt trafik.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchende release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchende API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
|
||||
"retention_days": "Opbevaringsdage",
|
||||
"retention_days_message": "1-365 dage.",
|
||||
"enabled": "Aktiveret",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiveret",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tildelinger",
|
||||
"subtitle": "Fastgør brugere, medarbejdere eller kunder til release-kanaler.",
|
||||
"subject_type": "Emnetype",
|
||||
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Søg på kundenummer eller navn, og vælg derefter den præcise kunde.",
|
||||
"customer_placeholder": "Søg kunde",
|
||||
"subject_id": "Emne-ID",
|
||||
"subject_id_message": "Eksempel: bruger-id 42",
|
||||
"subject_id_placeholder": "Emne-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Fastgjorte emner springer procentudrulningen over.",
|
||||
"reason": "Årsag",
|
||||
"reason_message": "Eksempel: canary-validering for kundesupport.",
|
||||
"reason_placeholder": "Årsag",
|
||||
"expires": "Udløber",
|
||||
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Start, promover og inspicer release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
|
||||
"select_target_or_manual": "Vælg mål eller manuel",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-valg",
|
||||
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
|
||||
"latest_from_branch": "Seneste fra branch",
|
||||
"specific_commit": "Specifik commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionslabel",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Eksempel: bruger-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalafgrænsning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalafgrænsning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Udløber",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Alvorlighed",
|
||||
"any_severity": "Alle alvorligheder",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub og Coolify",
|
||||
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
|
||||
"choose_repository": "Vælg repository",
|
||||
"add_health_url": "tilføj health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Åbn GitHub token-indstillinger",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domæne",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domæne",
|
||||
"https_domain_for_coolify": "HTTPS-domæne til Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nu",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Opret manglende service",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"customer_creation": {
|
||||
|
||||
@@ -1495,6 +1495,412 @@
|
||||
},
|
||||
"company_id": "Company ID",
|
||||
"company_id_desc": "The Workfeed company identifier used for API synchronization."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Schrittweise API- und Front-End-Releases",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release-Manager-Aktionen werden an diese API gesendet. Verwende die Produktions-Kontroll-API, au?er du testest ein Staging-Backend.",
|
||||
"example": "Beispiel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-Endpunkt",
|
||||
"endpoint_message": "?ndere dies nur, wenn Release-Manager-Endpunkte auf der Ziel-API verf?gbar sind.",
|
||||
"endpoint_aria": "Release Manager Kontroll-API-URL",
|
||||
"use_tooltip": "Release-Daten von dieser API laden",
|
||||
"use": "Verwenden",
|
||||
"reset_tooltip": "Zur Standard-Kontroll-API zur?ckkehren",
|
||||
"reset": "Zur?cksetzen",
|
||||
"known_endpoints": "Bekannte Kontroll-API-Endpunkte"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "?bersicht",
|
||||
"description": "Kanalzustand, Einrichtungsfortschritt und aktueller Release-Status."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kan?le",
|
||||
"description": "Stabile, Canary- und Zielkan?le mit Rollout-Grenzen erstellen."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Zuweisungen",
|
||||
"description": "Benutzer, Unterbenutzer oder Kunden einem bestimmten Release-Kanal zuweisen."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Bereitstellungen",
|
||||
"description": "Bereitstellungen starten und erfolgreiche Builds promoten."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Gezielte Erfassung aktivieren und Release-Timeline-Ereignisse durchsuchen."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationen",
|
||||
"description": "GitHub-Repositories, Branches und Coolify-Dienste verbinden."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Einstellungen",
|
||||
"description": "GitHub-Zugriff und Webhook-Einstellungen des Release Managers konfigurieren."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kan?le",
|
||||
"targets": "GitHub- und Coolify-Ziele",
|
||||
"assignments": "Pilot-Zuweisungen",
|
||||
"deployments": "Erste Bereitstellung",
|
||||
"replay": "Replay-Erfassung"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kan?le",
|
||||
"targets": "Ziele",
|
||||
"deployments": "Bereitstellungen",
|
||||
"timeline_events": "Timeline-Ereignisse"
|
||||
},
|
||||
"overview": {
|
||||
"title": "?bersicht",
|
||||
"subtitle": "Aktueller Kanalzustand, Release-Aktivit?t und Modulstatus.",
|
||||
"guided_setup": "Gef?hrte Einrichtung",
|
||||
"next_step": "N?chster Schritt: {step}",
|
||||
"ready": "Release Manager ist f?r den t?glichen Betrieb bereit.",
|
||||
"add_suggested_channel": "Vorgeschlagenen Kanal hinzufügen",
|
||||
"module_health_empty": "Modul-Health-Snapshots erscheinen, nachdem Probes aufgezeichnet wurden."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release-Manager-Einstellungen",
|
||||
"subtitle": "GitHub-Token f?r private Repositories und Branch-Abfragen konfigurieren.",
|
||||
"github_token": "GitHub-Token",
|
||||
"github_api_url": "GitHub-API-URL",
|
||||
"webhook_secret": "Webhook-Secret",
|
||||
"github_webhook_secret": "GitHub-Webhook-Secret",
|
||||
"configured": "Konfiguriert",
|
||||
"not_configured": "Nicht konfiguriert",
|
||||
"loaded_from": "Geladen aus {variable}",
|
||||
"set_below": "{variable} unten festlegen",
|
||||
"private_repositories_prefix": "Private Repositories werden mit der Server-Umgebungsvariable gelesen",
|
||||
"private_repositories_or": "oder der Modul-Konfigurationsvariable",
|
||||
"github_token_message": "Beispiel: github_pat_... mit Zugriff auf die privaten Repositories, die Release Manager bereitstellt.",
|
||||
"github_token_placeholder": "Leer lassen, um das vorhandene Token zu behalten",
|
||||
"github_api_url_message": "Konfiguriert in ReleaseManager.github_api_url. https://api.github.com verwenden, sofern GitHub Enterprise nicht genutzt wird.",
|
||||
"webhook_secret_message": "Optional; leer lassen, um das vorhandene Secret zu behalten.",
|
||||
"webhook_secret_placeholder": "Webhook-HMAC-Secret",
|
||||
"save": "Einstellungen speichern",
|
||||
"back_to_integrations": "Zur?ck zu Integrationen",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-Token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub Webhook Secret",
|
||||
"review": "Prüfung"
|
||||
},
|
||||
"github_token_title": "GitHub-Token hinzufügen",
|
||||
"github_token_description": "Release Manager verwendet diesen Token, um private Repositories, Branches und Commits aufzulisten, bevor ein Deployment-Ziel gespeichert oder getestet wird.",
|
||||
"configure_this_in": "Dies konfigurieren in",
|
||||
"server_environment_variable": "Die Server-Umgebungsvariable",
|
||||
"takes_precedence": "hat Vorrang, wenn sie gesetzt ist.",
|
||||
"recommended_access": "Empfohlener Zugriff",
|
||||
"recommended_access_description": "Fine-grained GitHub-Token mit Contents: read-Zugriff für jedes Release-Repository.",
|
||||
"github_api_url_title": "GitHub API-URL hinzufügen",
|
||||
"github_api_url_description": "GitHub Cloud standardmäßig verwenden oder auf die REST-API-Basis-URL einer GitHub Enterprise Server-Installation zeigen.",
|
||||
"github_api_url_invalid": "Vollständige HTTP- oder HTTPS-URL eingeben.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-Beispiel",
|
||||
"github_webhook_secret_title": "GitHub Webhook Secret hinzufügen",
|
||||
"github_webhook_secret_description": "Dasselbe Secret in GitHub-Repository-Webhooks verwenden, damit Release Manager Deployment-Events vor der Verarbeitung verifizieren kann.",
|
||||
"github_webhook_url": "GitHub Webhook-URL:",
|
||||
"store_shared_secret_in": "Geteiltes Secret speichern in",
|
||||
"review_title": "GitHub-Konfiguration prüfen",
|
||||
"review_description": "Werte speichern und dann unter Integrationen ein privates Repository, einen Branch und einen bestimmten Commit oder den neuesten Commit auswählen, bevor der Zugriff getestet wird.",
|
||||
"ready": "Bereit",
|
||||
"needs_value": "Wert fehlt"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-Kanal ist nicht bereit",
|
||||
"summary_prefix": "Dein Konto ist zugewiesen zu",
|
||||
"summary_suffix": ", aber diesem Kanal fehlt die Konfiguration, um sein Release-Image zu laden.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Erneute Pr?fung in {seconds}s",
|
||||
"refresh_error": "Release-Status konnte nicht aktualisiert werden. Die n?chste automatische Pr?fung versucht es erneut.",
|
||||
"ignore": "N?chste 5 Minuten ignorieren",
|
||||
"check_again": "Erneut pr?fen",
|
||||
"logout": "Abmelden",
|
||||
"base_image": "Basis-Image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du bist jetzt auf {channel}",
|
||||
"summary_prefix": "Dein Konto wurde dem Release-Kanal",
|
||||
"summary_suffix": "zugewiesen. Dieses Ger?t merkt sich, dass du diesen Hinweis gesehen hast.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-Version",
|
||||
"api_version": "API-Version",
|
||||
"assigned_channel": "Zugewiesener Kanal",
|
||||
"current_app_image": "Aktuelles App-Image",
|
||||
"current_api": "Aktuelle API",
|
||||
"base_image": "Basis-Image",
|
||||
"continue": "Fortfahren"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nein",
|
||||
"off": "aus"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Bearbeiten",
|
||||
"rollback": "Zurückrollen",
|
||||
"update": "Aktualisieren",
|
||||
"create": "Erstellen",
|
||||
"clear": "Leeren",
|
||||
"assign": "Zuweisen",
|
||||
"remove": "Entfernen",
|
||||
"test_access": "Zugriff testen",
|
||||
"deploy": "Deployen",
|
||||
"promote": "Promoten",
|
||||
"enable": "Aktivieren",
|
||||
"search": "Suchen",
|
||||
"save_target": "Ziel speichern",
|
||||
"delete": "Löschen",
|
||||
"back": "Zurück",
|
||||
"next": "Weiter"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Rollout",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Aktionen",
|
||||
"module": "Module",
|
||||
"status": "Status",
|
||||
"checked": "Geprüft",
|
||||
"subject": "Subjekt",
|
||||
"reason": "Grund",
|
||||
"expires": "Läuft ab",
|
||||
"deployment": "Deployment",
|
||||
"source": "Quelle",
|
||||
"started": "Gestartet",
|
||||
"target": "Ziel",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "Frontend",
|
||||
"api": "API"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "Metadaten",
|
||||
"full_redacted": "vollständig redigiert"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "Benutzer",
|
||||
"subuser": "Mitarbeiter",
|
||||
"customer": "Kunde",
|
||||
"channel": "Kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "Fehler",
|
||||
"warning": "Warnung",
|
||||
"info": "Info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployt",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiviert",
|
||||
"queued": "in Warteschlange",
|
||||
"deploying": "deploying",
|
||||
"metadata": "Metadaten",
|
||||
"not_configured": "nicht konfiguriert",
|
||||
"warning": "Warnung",
|
||||
"failed": "fehlgeschlagen",
|
||||
"down": "down",
|
||||
"error": "Fehler",
|
||||
"unknown": "unbekannt"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Vorgeschlagener Wert",
|
||||
"suggestion": "Vorschlag",
|
||||
"use_typed_value": "Eingegebenen Wert verwenden",
|
||||
"custom": "Benutzerdefiniert",
|
||||
"api_url": "API URL",
|
||||
"frontend_url": "Frontend URL",
|
||||
"known_release_url": "Bekannte Release-URL",
|
||||
"deploy_here_with_coolify": "{app} hier mit Coolify deployen",
|
||||
"prepare_coolify_target": "Coolify-Ziel mit aktiviertem HTTPS/SSL vorbereiten",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-Repository",
|
||||
"owner_repository": "owner/repository",
|
||||
"git_branch": "Git branch",
|
||||
"deployment_branch": "Deployment branch",
|
||||
"github_commit": "GitHub commit",
|
||||
"git_commit": "Git commit",
|
||||
"use_specific_commit_sha": "Bestimmte Commit-SHA verwenden",
|
||||
"custom_commit": "Benutzerdefinierter Commit",
|
||||
"health_url": "Health URL",
|
||||
"release_health_check": "Release-Health-Check",
|
||||
"coolify_service": "Coolify service",
|
||||
"service_uuid": "Service UUID",
|
||||
"known_coolify_service_uuid": "Known Coolify service UUID",
|
||||
"control_api": "Kontroll-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-Endpunkt",
|
||||
"module": "Module",
|
||||
"timeline_module_key": "Timeline-Modulschlüssel"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "GitHub-Repositories konnten nicht geladen werden.",
|
||||
"github_branches": "GitHub-Branches konnten nicht geladen werden.",
|
||||
"repository_branch_required": "Repository und Branch sind erforderlich, bevor GitHub-Zugriff getestet werden kann.",
|
||||
"github_access_failed": "GitHub-Repository-Zugriffstest ist fehlgeschlagen."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanäle",
|
||||
"subtitle": "Graduale Release-Kanäle erstellen und anpassen.",
|
||||
"rollout_percent": "{percent}% rollout",
|
||||
"already_configured": "Bereits konfiguriert",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Example: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Name",
|
||||
"name_message": "Wird Operatoren bei der Kanalprüfung angezeigt.",
|
||||
"name_placeholder": "Name",
|
||||
"rollout": "Rollout",
|
||||
"rollout_message": "0-100 Prozent des nicht zugewiesenen Traffics.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"frontend_url_message": "Example: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend URL",
|
||||
"no_matching_release_url": "Keine passende Release-URL.",
|
||||
"api_url": "API URL",
|
||||
"api_url_message": "Example: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API URL",
|
||||
"no_matching_api_url": "Keine passende API-URL.",
|
||||
"replay_capture": "Replay-Erfassung",
|
||||
"replay_capture_message": "Metadaten sind für breite Kanäle sicherer.",
|
||||
"retention_days": "Aufbewahrungstage",
|
||||
"retention_days_message": "1-365 Tage.",
|
||||
"enabled": "Aktiviert",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiviert",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Zuweisungen",
|
||||
"subtitle": "Benutzer, Mitarbeiter oder Kunden an Release-Kanäle binden.",
|
||||
"subject_type": "Subjekttyp",
|
||||
"subject_type_message": "Identitätstyp zum Binden auswählen.",
|
||||
"customer": "Customer",
|
||||
"customer_message": "Nach Kundennummer oder Name suchen und dann den genauen Kunden auswählen.",
|
||||
"customer_placeholder": "Kunde suchen",
|
||||
"subject_id": "Subjekt-ID",
|
||||
"subject_id_message": "Beispiel: Benutzer-ID 42",
|
||||
"subject_id_placeholder": "Subjekt-ID",
|
||||
"channel": "Channel",
|
||||
"channel_message": "Gebundene Subjekte umgehen den prozentualen Rollout.",
|
||||
"reason": "Grund",
|
||||
"reason_message": "Beispiel: Canary-Validierung für Kundensupport.",
|
||||
"reason_placeholder": "Grund",
|
||||
"expires": "Läuft ab",
|
||||
"expires_message": "Leer lassen für kein automatisches Ablaufdatum."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Release-Deployments starten, promoten und prüfen.",
|
||||
"manual_deployment_record": "Manueller Deployment-Eintrag",
|
||||
"target": "Ziel",
|
||||
"target_message": "Gespeichertes Ziel wählen oder manuelle Felder editierbar lassen.",
|
||||
"select_target_or_manual": "Ziel oder manuell auswählen",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/front-end-vue",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-Auswahl",
|
||||
"commit_selection_message": "Neueste Version wird mit dem konfigurierten GitHub-Token als Branch-Head aufgelöst.",
|
||||
"latest_from_branch": "Neuester vom Branch",
|
||||
"specific_commit": "Bestimmter Commit",
|
||||
"specific_commit_message": "Example: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionslabel",
|
||||
"version_label_message": "Example: api-2026.05.19",
|
||||
"version_label_placeholder": "Versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Token-Zugriff auf das ausgewählte Repository, den Branch und Commit prüfen."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Gezielte Erfassung aktivieren und Fehler-Timelines Schritt für Schritt prüfen.",
|
||||
"capture_channel": "{channel} erfassen",
|
||||
"target_type": "Zieltyp",
|
||||
"target": "Ziel",
|
||||
"target_message": "Beispiel: Benutzer-ID 42 oder Kanal-Slug canary.",
|
||||
"target_placeholder": "Ziel-ID oder Kanal-Slug",
|
||||
"channel_scope": "Kanalbereich",
|
||||
"channel_scope_message": "Optional; begrenzt Replay-Erfassung auf einen Kanal.",
|
||||
"no_channel_scope": "Kein Kanalbereich",
|
||||
"capture_level": "Erfassungsstufe",
|
||||
"expires": "Läuft ab",
|
||||
"expires_message": "Leer lassen für manuelle Deaktivierung.",
|
||||
"trace_id": "Trace ID",
|
||||
"trace_id_message": "Example: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace id",
|
||||
"channel": "Channel",
|
||||
"channel_placeholder": "Channel",
|
||||
"principal_id": "Principal ID",
|
||||
"principal_id_placeholder": "Principal-ID",
|
||||
"module": "Module",
|
||||
"module_placeholder": "Module",
|
||||
"severity": "Schweregrad",
|
||||
"any_severity": "Beliebiger Schweregrad",
|
||||
"no_timeline_events": "Keine Timeline-Events entsprechen den aktuellen Filtern."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub und Coolify",
|
||||
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
|
||||
"choose_repository": "Repository auswählen",
|
||||
"add_health_url": "Health-URL hinzufügen",
|
||||
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
|
||||
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/backend-php",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Neueste: {commit}",
|
||||
"coolify_instance": "Coolify-Instanz",
|
||||
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
|
||||
"no_coolify_instance": "Keine Coolify-Instanz",
|
||||
"coolify_instance_placeholder": "Coolify-Instanz-ID",
|
||||
"coolify_service_uuid": "Coolify service UUID",
|
||||
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service UUID",
|
||||
"health_url": "Health URL",
|
||||
"health_url_message": "Example: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health URL",
|
||||
"ssl_domain": "SSL-Domain",
|
||||
"ssl_domain_message": "Example: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-Domain",
|
||||
"https_domain_for_coolify": "HTTPS-Domain für Coolify",
|
||||
"auto_deploy": "Auto-Deploy",
|
||||
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
|
||||
"deploy_now": "Jetzt deployen",
|
||||
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
|
||||
"create_missing_service": "Fehlenden Service erstellen",
|
||||
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
|
||||
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
|
||||
}
|
||||
}
|
||||
},
|
||||
"customer_creation": {
|
||||
|
||||
@@ -1495,6 +1495,412 @@
|
||||
},
|
||||
"company_id": "Company ID",
|
||||
"company_id_desc": "The Workfeed company identifier used for API synchronization."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradual API and Front-End releases",
|
||||
"control_api": {
|
||||
"title": "Control API",
|
||||
"tooltip": "Release Manager actions are sent to this API. Use the production control API unless testing a staged backend.",
|
||||
"example": "Example: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API endpoint",
|
||||
"endpoint_message": "Switch this only when Release Manager endpoints are available on the target API.",
|
||||
"endpoint_aria": "Release Manager control API URL",
|
||||
"use_tooltip": "Load release data from this API",
|
||||
"use": "Use",
|
||||
"reset_tooltip": "Return to the default control API",
|
||||
"reset": "Reset",
|
||||
"known_endpoints": "Known control API endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Overview",
|
||||
"description": "Channel health, setup progress, and the latest release state."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Channels",
|
||||
"description": "Create stable, canary, and targeted channels with rollout limits."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Assignments",
|
||||
"description": "Pin users, subusers, or customers to a specific release channel."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Deployments",
|
||||
"description": "Trigger deployments and promote successful builds."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Enable targeted capture and search release timeline events."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrations",
|
||||
"description": "Connect GitHub repositories, branches, and Coolify services."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Settings",
|
||||
"description": "Configure Release Manager GitHub access and webhook settings."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Channels",
|
||||
"targets": "GitHub and Coolify targets",
|
||||
"assignments": "Pilot assignments",
|
||||
"deployments": "First deployment",
|
||||
"replay": "Replay capture"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Channels",
|
||||
"targets": "Targets",
|
||||
"deployments": "Deployments",
|
||||
"timeline_events": "Timeline events"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Overview",
|
||||
"subtitle": "Current channel health, release activity, and module status.",
|
||||
"guided_setup": "Guided setup",
|
||||
"next_step": "Next: {step}",
|
||||
"ready": "Release Manager is ready for day-to-day operations.",
|
||||
"add_suggested_channel": "Add suggested channel",
|
||||
"module_health_empty": "Module health snapshots appear after probes have been recorded."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager Settings",
|
||||
"subtitle": "Configure the GitHub token used for private repositories and branch lookups.",
|
||||
"github_token": "GitHub token",
|
||||
"github_api_url": "GitHub API URL",
|
||||
"webhook_secret": "Webhook secret",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"configured": "Configured",
|
||||
"not_configured": "Not configured",
|
||||
"loaded_from": "Loaded from {variable}",
|
||||
"set_below": "Set {variable} below",
|
||||
"private_repositories_prefix": "Private repositories are read with the server environment variable",
|
||||
"private_repositories_or": "or the module config variable",
|
||||
"github_token_message": "Example: github_pat_... with access to the private repositories Release Manager deploys.",
|
||||
"github_token_placeholder": "Leave blank to keep the existing token",
|
||||
"github_api_url_message": "Configured in ReleaseManager.github_api_url. Use https://api.github.com unless using GitHub Enterprise.",
|
||||
"webhook_secret_message": "Optional; leave blank to keep the existing secret.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC secret",
|
||||
"save": "Save settings",
|
||||
"back_to_integrations": "Back to integrations",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub token",
|
||||
"github_api_url": "GitHub API URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Review"
|
||||
},
|
||||
"github_token_title": "Add GitHub token",
|
||||
"github_token_description": "Release Manager uses this token to list private repositories, branches, and commits before a deployment target is saved or tested.",
|
||||
"configure_this_in": "Configure this in",
|
||||
"server_environment_variable": "The server environment variable",
|
||||
"takes_precedence": "takes precedence when it is set.",
|
||||
"recommended_access": "Recommended access",
|
||||
"recommended_access_description": "Fine-grained GitHub token with Contents: read access for each release repository.",
|
||||
"github_api_url_title": "Add GitHub API URL",
|
||||
"github_api_url_description": "Use GitHub Cloud by default, or point this at the REST API base URL for a GitHub Enterprise Server installation.",
|
||||
"github_api_url_invalid": "Enter a full HTTP or HTTPS URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise example",
|
||||
"github_webhook_secret_title": "Add GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Use the same secret in GitHub repository webhooks so Release Manager can verify deployment events before handling them.",
|
||||
"github_webhook_url": "GitHub webhook URL:",
|
||||
"store_shared_secret_in": "Store the shared secret in",
|
||||
"review_title": "Review GitHub setup",
|
||||
"review_description": "Save the values, then use Integrations to select a private repository, branch, and specific commit or latest commit before testing access.",
|
||||
"ready": "Ready",
|
||||
"needs_value": "Needs value"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release channel is not ready",
|
||||
"summary_prefix": "Your account is assigned to",
|
||||
"summary_suffix": ", but that channel is missing the configuration needed to load its release image.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"api_url": "API URL",
|
||||
"checking_again": "Checking again in {seconds}s",
|
||||
"refresh_error": "Release status could not be refreshed. The next automatic check will try again.",
|
||||
"ignore": "Ignore next 5 minutes",
|
||||
"check_again": "Check again",
|
||||
"logout": "Logout",
|
||||
"base_image": "Base image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "You are now on {channel}",
|
||||
"summary_prefix": "Your account has been assigned to the",
|
||||
"summary_suffix": "release channel. This device will remember that you have seen this notice.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend version",
|
||||
"api_version": "API version",
|
||||
"assigned_channel": "Assigned channel",
|
||||
"current_app_image": "Current app image",
|
||||
"current_api": "Current API",
|
||||
"base_image": "Base image",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"common": {
|
||||
"yes": "yes",
|
||||
"no": "no",
|
||||
"off": "off"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"rollback": "Rollback",
|
||||
"update": "Update",
|
||||
"create": "Create",
|
||||
"clear": "Clear",
|
||||
"assign": "Assign",
|
||||
"remove": "Remove",
|
||||
"test_access": "Test access",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promote",
|
||||
"enable": "Enable",
|
||||
"search": "Search",
|
||||
"save_target": "Save target",
|
||||
"delete": "Delete",
|
||||
"back": "Back",
|
||||
"next": "Next"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Channel",
|
||||
"rollout": "Rollout",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Actions",
|
||||
"module": "Module",
|
||||
"status": "Status",
|
||||
"checked": "Checked",
|
||||
"subject": "Subject",
|
||||
"reason": "Reason",
|
||||
"expires": "Expires",
|
||||
"deployment": "Deployment",
|
||||
"source": "Source",
|
||||
"started": "Started",
|
||||
"target": "Target",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "full_redacted"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "user",
|
||||
"subuser": "subuser",
|
||||
"customer": "customer",
|
||||
"channel": "channel"
|
||||
},
|
||||
"severity": {
|
||||
"error": "error",
|
||||
"warning": "warning",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "active",
|
||||
"deployed": "deployed",
|
||||
"stable": "stable",
|
||||
"enabled": "enabled",
|
||||
"queued": "queued",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "not configured",
|
||||
"warning": "warning",
|
||||
"failed": "failed",
|
||||
"down": "down",
|
||||
"error": "error",
|
||||
"unknown": "unknown"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Suggested value",
|
||||
"suggestion": "Suggestion",
|
||||
"use_typed_value": "Use typed value",
|
||||
"custom": "Custom",
|
||||
"api_url": "API URL",
|
||||
"frontend_url": "Frontend URL",
|
||||
"known_release_url": "Known release URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} here with Coolify",
|
||||
"prepare_coolify_target": "Prepare a Coolify target with HTTPS/SSL enabled",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub repository",
|
||||
"owner_repository": "owner/repository",
|
||||
"git_branch": "Git branch",
|
||||
"deployment_branch": "Deployment branch",
|
||||
"github_commit": "GitHub commit",
|
||||
"git_commit": "Git commit",
|
||||
"use_specific_commit_sha": "Use specific commit SHA",
|
||||
"custom_commit": "Custom commit",
|
||||
"health_url": "Health URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify service",
|
||||
"service_uuid": "Service UUID",
|
||||
"known_coolify_service_uuid": "Known Coolify service UUID",
|
||||
"control_api": "Control API",
|
||||
"release_manager_api_endpoint": "Release Manager API endpoint",
|
||||
"module": "Module",
|
||||
"timeline_module_key": "Timeline module key"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Could not load GitHub repositories.",
|
||||
"github_branches": "Could not load GitHub branches.",
|
||||
"repository_branch_required": "Repository and branch are required before testing GitHub access.",
|
||||
"github_access_failed": "GitHub repository access test failed."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Channels",
|
||||
"subtitle": "Create and tune gradual release channels.",
|
||||
"rollout_percent": "{percent}% rollout",
|
||||
"already_configured": "Already configured",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Example: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Name",
|
||||
"name_message": "Shown to operators when reviewing a channel.",
|
||||
"name_placeholder": "Name",
|
||||
"rollout": "Rollout",
|
||||
"rollout_message": "0-100 percent of unassigned traffic.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"frontend_url_message": "Example: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend URL",
|
||||
"no_matching_release_url": "No matching release URL.",
|
||||
"api_url": "API URL",
|
||||
"api_url_message": "Example: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API URL",
|
||||
"no_matching_api_url": "No matching API URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata is safer for broad channels.",
|
||||
"retention_days": "Retention days",
|
||||
"retention_days_message": "1-365 days.",
|
||||
"enabled": "Enabled",
|
||||
"default_channel": "Default channel",
|
||||
"replay_enabled": "Replay enabled",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Assignments",
|
||||
"subtitle": "Pin users, subusers, or customers to release channels.",
|
||||
"subject_type": "Subject type",
|
||||
"subject_type_message": "Choose the identity type to pin.",
|
||||
"customer": "Customer",
|
||||
"customer_message": "Search by customer number or name, then select the exact customer.",
|
||||
"customer_placeholder": "Search customer",
|
||||
"subject_id": "Subject ID",
|
||||
"subject_id_message": "Example: user id 42",
|
||||
"subject_id_placeholder": "Subject id",
|
||||
"channel": "Channel",
|
||||
"channel_message": "Pinned subjects bypass percentage rollout.",
|
||||
"reason": "Reason",
|
||||
"reason_message": "Example: canary validation for customer support.",
|
||||
"reason_placeholder": "Reason",
|
||||
"expires": "Expires",
|
||||
"expires_message": "Leave blank for no automatic expiry."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Trigger, promote, and inspect release deployments.",
|
||||
"manual_deployment_record": "Manual deployment record",
|
||||
"target": "Target",
|
||||
"target_message": "Pick a saved target or keep manual fields editable.",
|
||||
"select_target_or_manual": "Select target or manual",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/front-end-vue",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit selection",
|
||||
"commit_selection_message": "Latest resolves to the branch head with the configured GitHub token.",
|
||||
"latest_from_branch": "Latest from branch",
|
||||
"specific_commit": "Specific commit",
|
||||
"specific_commit_message": "Example: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Version label",
|
||||
"version_label_message": "Example: api-2026.05.19",
|
||||
"version_label_placeholder": "version label",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Verify token access to the selected repository, branch, and commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Enable targeted capture and step through failure timelines.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Target type",
|
||||
"target": "Target",
|
||||
"target_message": "Example: user id 42 or channel slug canary.",
|
||||
"target_placeholder": "Target id or channel slug",
|
||||
"channel_scope": "Channel scope",
|
||||
"channel_scope_message": "Optional; limits replay capture to a channel.",
|
||||
"no_channel_scope": "No channel scope",
|
||||
"capture_level": "Capture level",
|
||||
"expires": "Expires",
|
||||
"expires_message": "Leave blank for manual disable.",
|
||||
"trace_id": "Trace ID",
|
||||
"trace_id_message": "Example: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace id",
|
||||
"channel": "Channel",
|
||||
"channel_placeholder": "Channel",
|
||||
"principal_id": "Principal ID",
|
||||
"principal_id_placeholder": "Principal id",
|
||||
"module": "Module",
|
||||
"module_placeholder": "Module",
|
||||
"severity": "Severity",
|
||||
"any_severity": "Any severity",
|
||||
"no_timeline_events": "No timeline events match the current filters."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub and Coolify",
|
||||
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
|
||||
"choose_repository": "Choose repository",
|
||||
"add_health_url": "add health URL",
|
||||
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
|
||||
"open_github_token_settings": "Open GitHub token settings",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/backend-php",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Latest: {commit}",
|
||||
"coolify_instance": "Coolify instance",
|
||||
"coolify_instance_message": "Leave empty for manual deployment records.",
|
||||
"no_coolify_instance": "No Coolify instance",
|
||||
"coolify_instance_placeholder": "Coolify instance id",
|
||||
"coolify_service_uuid": "Coolify service UUID",
|
||||
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service UUID",
|
||||
"health_url": "Health URL",
|
||||
"health_url_message": "Example: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health URL",
|
||||
"ssl_domain": "SSL domain",
|
||||
"ssl_domain_message": "Example: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL domain",
|
||||
"https_domain_for_coolify": "HTTPS domain for Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
|
||||
"deploy_now": "Deploy now",
|
||||
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
|
||||
"create_missing_service": "Create missing service",
|
||||
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
|
||||
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"customer_creation": {
|
||||
|
||||
@@ -1495,6 +1495,412 @@
|
||||
},
|
||||
"company_id": "Company ID",
|
||||
"company_id_desc": "The Workfeed company identifier used for API synchronization."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvise API- og Front-End-utgivelser",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release Manager-handlinger sendes til denne API-en. Bruk produksjons-API-en med mindre du tester en staging-backend.",
|
||||
"example": "Eksempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endepunkt",
|
||||
"endpoint_message": "Endre dette bare n?r Release Manager-endepunkter er tilgjengelige p? m?l-API-en.",
|
||||
"endpoint_aria": "Release Manager kontroll-API URL",
|
||||
"use_tooltip": "Last release-data fra denne API-en",
|
||||
"use": "Bruk",
|
||||
"reset_tooltip": "G? tilbake til standard kontroll-API",
|
||||
"reset": "Tilbakestill",
|
||||
"known_endpoints": "Kjente kontroll-API-endepunkter"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Oversikt",
|
||||
"description": "Kanalhelse, oppsettsfremdrift og siste release-status."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Opprett stabile, canary- og m?lrettede kanaler med rollout-grenser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tildelinger",
|
||||
"description": "Knytt brukere, underbrukere eller kunder til en bestemt release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Utrullinger",
|
||||
"description": "Start utrullinger og promoter vellykkede bygg."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktiver m?lrettet innsamling og s?k i release-tidslinjehendelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrasjoner",
|
||||
"description": "Koble GitHub-repositories, branches og Coolify-tjenester."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Innstillinger",
|
||||
"description": "Konfigurer Release Managers GitHub-tilgang og webhook-innstillinger."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- og Coolify-m?l",
|
||||
"assignments": "Pilottildelinger",
|
||||
"deployments": "F?rste utrulling",
|
||||
"replay": "Replay-innsamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Utrullinger",
|
||||
"timeline_events": "Tidslinjehendelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Oversikt",
|
||||
"subtitle": "Aktuell kanalhelse, release-aktivitet og modulstatus.",
|
||||
"guided_setup": "Veiledet oppsett",
|
||||
"next_step": "Neste: {step}",
|
||||
"ready": "Release Manager er klar for daglig drift.",
|
||||
"add_suggested_channel": "Legg til foreslått kanal",
|
||||
"module_health_empty": "Modul-health snapshots vises når probes er registrert."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-innstillinger",
|
||||
"subtitle": "Konfigurer GitHub-tokenet som brukes for private repositories og branch-oppslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemmelighet",
|
||||
"github_webhook_secret": "GitHub webhook-hemmelighet",
|
||||
"configured": "Konfigurert",
|
||||
"not_configured": "Ikke konfigurert",
|
||||
"loaded_from": "Lastet fra {variable}",
|
||||
"set_below": "Angi {variable} nedenfor",
|
||||
"private_repositories_prefix": "Private repositories leses med serverens milj?variabel",
|
||||
"private_repositories_or": "eller modulens konfigurasjonsvariabel",
|
||||
"github_token_message": "Eksempel: github_pat_... med tilgang til de private repositories Release Manager ruller ut.",
|
||||
"github_token_placeholder": "La st? tomt for ? beholde eksisterende token",
|
||||
"github_api_url_message": "Konfigurert i ReleaseManager.github_api_url. Bruk https://api.github.com med mindre GitHub Enterprise brukes.",
|
||||
"webhook_secret_message": "Valgfritt; la st? tomt for ? beholde eksisterende hemmelighet.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemmelighet",
|
||||
"save": "Lagre innstillinger",
|
||||
"back_to_integrations": "Tilbake til integrasjoner",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Legg til GitHub-token",
|
||||
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
|
||||
"configure_this_in": "Konfigurer dette i",
|
||||
"server_environment_variable": "Serverens miljøvariabel",
|
||||
"takes_precedence": "har forrang når den er satt.",
|
||||
"recommended_access": "Anbefalt tilgang",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
|
||||
"github_api_url_title": "Legg til GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Skriv inn en full HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-eksempel",
|
||||
"github_webhook_secret_title": "Legg til GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Lagre den delte secreten i",
|
||||
"review_title": "Gå gjennom GitHub-oppsett",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Mangler værdi"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen er ikke klar",
|
||||
"summary_prefix": "Kontoen din er tildelt",
|
||||
"summary_suffix": ", men kanalen mangler konfigurasjonen som trengs for ? laste release-imaget.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Sjekker igjen om {seconds}s",
|
||||
"refresh_error": "Release-status kunne ikke oppdateres. Neste automatiske sjekk pr?ver igjen.",
|
||||
"ignore": "Ignorer de neste 5 minuttene",
|
||||
"check_again": "Sjekk igjen",
|
||||
"logout": "Logg ut",
|
||||
"base_image": "Basis-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du er n? p? {channel}",
|
||||
"summary_prefix": "Kontoen din er tildelt release-kanalen",
|
||||
"summary_suffix": "Denne enheten husker at du har sett denne meldingen.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-versjon",
|
||||
"api_version": "API-versjon",
|
||||
"assigned_channel": "Tildelt kanal",
|
||||
"current_app_image": "N?v?rende app-image",
|
||||
"current_api": "N?v?rende API",
|
||||
"base_image": "Basis-image",
|
||||
"continue": "Fortsett"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nei",
|
||||
"off": "av"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Rediger",
|
||||
"rollback": "Rull tilbake",
|
||||
"update": "Opdater",
|
||||
"create": "Opprett",
|
||||
"clear": "Tøm",
|
||||
"assign": "Tildel",
|
||||
"remove": "Fjern",
|
||||
"test_access": "Test tilgang",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promoter",
|
||||
"enable": "Aktiver",
|
||||
"search": "Søk",
|
||||
"save_target": "Lagre mål",
|
||||
"delete": "Slett",
|
||||
"back": "Tilbake",
|
||||
"next": "Neste"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Handlinger",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrollert",
|
||||
"subject": "Emne",
|
||||
"reason": "Årsag",
|
||||
"expires": "Utløper",
|
||||
"deployment": "Deployment",
|
||||
"source": "Kilde",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "fullt redigert"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "bruker",
|
||||
"subuser": "medarbeider",
|
||||
"customer": "kunde",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "feil",
|
||||
"warning": "advarsel",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployet",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktivert",
|
||||
"queued": "i kø",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "ikke konfigurert",
|
||||
"warning": "advarsel",
|
||||
"failed": "feilet",
|
||||
"down": "nede",
|
||||
"error": "feil",
|
||||
"unknown": "ukjent"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Opprett og juster gradvise release-kanaler.",
|
||||
"rollout_percent": "{percent}% udrulning",
|
||||
"already_configured": "Allerede konfigurert",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Navn",
|
||||
"name_message": "Vises til operatører når de gjennomgår en kanal.",
|
||||
"name_placeholder": "Navn",
|
||||
"rollout": "Udrulning",
|
||||
"rollout_message": "0-100 prosent av ikke-tildelt trafikk.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchende release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchende API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
|
||||
"retention_days": "Oppbevaringsdager",
|
||||
"retention_days_message": "1-365 dage.",
|
||||
"enabled": "Aktivert",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktivert",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tildelinger",
|
||||
"subtitle": "Fest brukere, medarbeidere eller kunder til release-kanaler.",
|
||||
"subject_type": "Emnetype",
|
||||
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Søk på kundenummer eller navn, og velg deretter nøyaktig kunde.",
|
||||
"customer_placeholder": "Søk kunde",
|
||||
"subject_id": "Emne-ID",
|
||||
"subject_id_message": "Eksempel: bruker-id 42",
|
||||
"subject_id_placeholder": "Emne-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Festede emner hopper over prosentutrullingen.",
|
||||
"reason": "Årsag",
|
||||
"reason_message": "Eksempel: canary-validering for kundesupport.",
|
||||
"reason_placeholder": "Årsag",
|
||||
"expires": "Utløper",
|
||||
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Start, promoter og inspiser release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
|
||||
"select_target_or_manual": "Velg mål eller manuelt",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-valg",
|
||||
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
|
||||
"latest_from_branch": "Siste fra branch",
|
||||
"specific_commit": "Spesifikk commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versjonsetikett",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Eksempel: bruker-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalavgrensning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalavgrensning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Utløper",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Alvorlighet",
|
||||
"any_severity": "Alle alvorligheder",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub og Coolify",
|
||||
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
|
||||
"choose_repository": "Velg repository",
|
||||
"add_health_url": "legg til health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Åpne GitHub token-innstillinger",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domene",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domene",
|
||||
"https_domain_for_coolify": "HTTPS-domæne til Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nå",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Opprett manglende service",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"customer_creation": {
|
||||
|
||||
@@ -1495,6 +1495,412 @@
|
||||
},
|
||||
"company_id": "Company ID",
|
||||
"company_id_desc": "The Workfeed company identifier used for API synchronization."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvisa API- och Front-End-versioner",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release Manager-?tg?rder skickas till detta API. Anv?nd produktions-API:t om du inte testar en staging-backend.",
|
||||
"example": "Exempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endpoint",
|
||||
"endpoint_message": "?ndra detta endast n?r Release Manager-endpoints finns p? m?l-API:t.",
|
||||
"endpoint_aria": "Release Manager kontroll-API URL",
|
||||
"use_tooltip": "L?s in release-data fr?n detta API",
|
||||
"use": "Anv?nd",
|
||||
"reset_tooltip": "G? tillbaka till standard kontroll-API",
|
||||
"reset": "?terst?ll",
|
||||
"known_endpoints": "K?nda kontroll-API-endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "?versikt",
|
||||
"description": "Kanalh?lsa, installationsstatus och senaste release-l?ge."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Skapa stabila, canary- och riktade kanaler med rollout-gr?nser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tilldelningar",
|
||||
"description": "Koppla anv?ndare, underanv?ndare eller kunder till en specifik release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Utrullningar",
|
||||
"description": "Starta utrullningar och promovera lyckade byggen."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktivera riktad insamling och s?k i release-tidslinjeh?ndelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationer",
|
||||
"description": "Anslut GitHub-repositories, branches och Coolify-tj?nster."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Inst?llningar",
|
||||
"description": "Konfigurera Release Managers GitHub-?tkomst och webhook-inst?llningar."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- och Coolify-m?l",
|
||||
"assignments": "Pilottilldelningar",
|
||||
"deployments": "F?rsta utrullningen",
|
||||
"replay": "Replay-insamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Utrullningar",
|
||||
"timeline_events": "Tidslinjeh?ndelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "?versikt",
|
||||
"subtitle": "Aktuell kanalh?lsa, release-aktivitet och modulstatus.",
|
||||
"guided_setup": "Guidad installation",
|
||||
"next_step": "N?sta: {step}",
|
||||
"ready": "Release Manager ?r klar f?r daglig drift.",
|
||||
"add_suggested_channel": "Lägg till föreslagen kanal",
|
||||
"module_health_empty": "Modulhälsosnapshots visas när probes har registrerats."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-inst?llningar",
|
||||
"subtitle": "Konfigurera GitHub-token som anv?nds f?r privata repositories och branch-uppslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemlighet",
|
||||
"github_webhook_secret": "GitHub webhook-hemlighet",
|
||||
"configured": "Konfigurerad",
|
||||
"not_configured": "Inte konfigurerad",
|
||||
"loaded_from": "Inl?st fr?n {variable}",
|
||||
"set_below": "Ange {variable} nedan",
|
||||
"private_repositories_prefix": "Privata repositories l?ses med serverns milj?variabel",
|
||||
"private_repositories_or": "eller modulens konfigurationsvariabel",
|
||||
"github_token_message": "Exempel: github_pat_... med ?tkomst till de privata repositories som Release Manager distribuerar.",
|
||||
"github_token_placeholder": "L?mna tomt f?r att beh?lla befintlig token",
|
||||
"github_api_url_message": "Konfigurerad i ReleaseManager.github_api_url. Anv?nd https://api.github.com om du inte anv?nder GitHub Enterprise.",
|
||||
"webhook_secret_message": "Valfritt; l?mna tomt f?r att beh?lla befintlig hemlighet.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemlighet",
|
||||
"save": "Spara inst?llningar",
|
||||
"back_to_integrations": "Tillbaka till integrationer",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Lägg till GitHub-token",
|
||||
"github_token_description": "Release Manager använder denna token för att lista privata repositories, branches och commits innan ett deployment-mål sparas eller testas.",
|
||||
"configure_this_in": "Konfigurera detta i",
|
||||
"server_environment_variable": "Serverns miljövariabel",
|
||||
"takes_precedence": "har företräde när den är satt.",
|
||||
"recommended_access": "Rekommenderad åtkomst",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-åtkomst till varje release-repository.",
|
||||
"github_api_url_title": "Lägg till GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Ange en fullständig HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-exempel",
|
||||
"github_webhook_secret_title": "Lägg till GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Spara den delade secret i",
|
||||
"review_title": "Granska GitHub-konfiguration",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Saknar värde"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen ?r inte klar",
|
||||
"summary_prefix": "Ditt konto ?r tilldelat",
|
||||
"summary_suffix": ", men kanalen saknar konfigurationen som beh?vs f?r att l?sa in dess release-image.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Kontrollerar igen om {seconds}s",
|
||||
"refresh_error": "Release-status kunde inte uppdateras. N?sta automatiska kontroll f?rs?ker igen.",
|
||||
"ignore": "Ignorera de kommande 5 minuterna",
|
||||
"check_again": "Kontrollera igen",
|
||||
"logout": "Logga ut",
|
||||
"base_image": "Bas-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du ?r nu p? {channel}",
|
||||
"summary_prefix": "Ditt konto har tilldelats release-kanalen",
|
||||
"summary_suffix": "Den h?r enheten kommer ih?g att du har sett detta meddelande.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-version",
|
||||
"api_version": "API-version",
|
||||
"assigned_channel": "Tilldelad kanal",
|
||||
"current_app_image": "Nuvarande app-image",
|
||||
"current_api": "Nuvarande API",
|
||||
"base_image": "Bas-image",
|
||||
"continue": "Forts?tt"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nej",
|
||||
"off": "av"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Redigera",
|
||||
"rollback": "Rulla tillbaka",
|
||||
"update": "Uppdatera",
|
||||
"create": "Skapa",
|
||||
"clear": "Rensa",
|
||||
"assign": "Tilldela",
|
||||
"remove": "Ta bort",
|
||||
"test_access": "Testa åtkomst",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promota",
|
||||
"enable": "Aktivera",
|
||||
"search": "Sök",
|
||||
"save_target": "Spara mål",
|
||||
"delete": "Radera",
|
||||
"back": "Tillbaka",
|
||||
"next": "Nästa"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Åtgärder",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrollerad",
|
||||
"subject": "Emne",
|
||||
"reason": "Orsak",
|
||||
"expires": "Upphör",
|
||||
"deployment": "Deployment",
|
||||
"source": "Källa",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "helt redigerad"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "användare",
|
||||
"subuser": "medarbetare",
|
||||
"customer": "kund",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "fel",
|
||||
"warning": "varning",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployad",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiverad",
|
||||
"queued": "i kö",
|
||||
"deploying": "deployar",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "inte konfigurerad",
|
||||
"warning": "varning",
|
||||
"failed": "misslyckad",
|
||||
"down": "nere",
|
||||
"error": "fel",
|
||||
"unknown": "okänd"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Skapa och justera gradvisa release-kanaler.",
|
||||
"rollout_percent": "{percent}% utrullning",
|
||||
"already_configured": "Redan konfigurerad",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Namn",
|
||||
"name_message": "Visas för operatörer när de granskar en kanal.",
|
||||
"name_placeholder": "Namn",
|
||||
"rollout": "Utrullning",
|
||||
"rollout_message": "0-100 procent av otilldelad trafik.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchande release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchande API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata är säkrare för breda kanaler.",
|
||||
"retention_days": "Lagringsdagar",
|
||||
"retention_days_message": "1-365 dagar.",
|
||||
"enabled": "Aktiverad",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiverad",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tilldelningar",
|
||||
"subtitle": "Fäst användare, medarbetare eller kunder till release-kanaler.",
|
||||
"subject_type": "Ämnestyp",
|
||||
"subject_type_message": "Välj identitetstypen som ska fästas.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Sök på kundnummer eller namn och välj sedan exakt kund.",
|
||||
"customer_placeholder": "Sök kund",
|
||||
"subject_id": "Ämnes-ID",
|
||||
"subject_id_message": "Exempel: användar-id 42",
|
||||
"subject_id_placeholder": "Ämnes-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Fästa ämnen hoppar över procentutrullningen.",
|
||||
"reason": "Orsak",
|
||||
"reason_message": "Exempel: canary-validering för kundsupport.",
|
||||
"reason_placeholder": "Orsak",
|
||||
"expires": "Upphör",
|
||||
"expires_message": "Lämna tomt för ingen automatisk upphörning."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Starta, promota och inspektera release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Välj ett sparat mål eller behåll manuella fält redigerbara.",
|
||||
"select_target_or_manual": "Vælg mål eller manuel",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-val",
|
||||
"commit_selection_message": "Senaste slås upp som branchens head med konfigurerad GitHub-token.",
|
||||
"latest_from_branch": "Senaste från branch",
|
||||
"specific_commit": "Specifik commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionsetikett",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionsetikett",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktivera riktad capture och gå igenom feltimelines steg för steg.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Exempel: användar-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalavgränsning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalavgränsning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Upphör",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Allvarlighetsgrad",
|
||||
"any_severity": "Alla allvarlighetsgrader",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub och Coolify",
|
||||
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
|
||||
"choose_repository": "Välj repository",
|
||||
"add_health_url": "lägg till health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Öppna GitHub token-inställningar",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domän",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domän",
|
||||
"https_domain_for_coolify": "HTTPS-domän för Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nu",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Skapa saknad tjänst",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
},
|
||||
"customer_creation": {
|
||||
|
||||
@@ -298,6 +298,412 @@
|
||||
"subtitle": "@.capitalize:{'terms.glossary.konfiguration'} @:{'terms.glossary.af'} @:{'terms.glossary.xlvask'} @:{'terms.glossary.integrationen'}",
|
||||
"title": "@:{'terms.glossary.xlvask'} @:{'terms.glossary.konfiguration'}",
|
||||
"username_desc": "@.capitalize:{'terms.glossary.brugernavnet'} @:{'terms.glossary.bruges'} @:{'terms.glossary.til'} @:{'terms.glossary.at'} @:{'terms.glossary.autentificere'} @:{'terms.glossary.api'} @:{'terms.glossary.forbindelsen'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvise API- og Front-End-udgivelser",
|
||||
"control_api": {
|
||||
"title": "Kontrol-API",
|
||||
"tooltip": "Release Manager-handlinger sendes til denne API. Brug produktions-API'en, medmindre du tester en staging-backend.",
|
||||
"example": "Eksempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endpoint",
|
||||
"endpoint_message": "Skift kun dette, n?r Release Manager-endpoints er tilg?ngelige p? m?l-API'en.",
|
||||
"endpoint_aria": "Release Manager kontrol-API URL",
|
||||
"use_tooltip": "Indl?s release-data fra denne API",
|
||||
"use": "Brug",
|
||||
"reset_tooltip": "Vend tilbage til standard kontrol-API",
|
||||
"reset": "Nulstil",
|
||||
"known_endpoints": "Kendte kontrol-API-endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Oversigt",
|
||||
"description": "Kanalstatus, ops?tningsfremdrift og seneste release-tilstand."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Opret stabile, canary- og m?lrettede kanaler med rollout-gr?nser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tildelinger",
|
||||
"description": "Fastg?r brugere, subbrugere eller kunder til en bestemt release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Udrulninger",
|
||||
"description": "Start udrulninger og promover vellykkede builds."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktiv?r m?lrettet opsamling og s?g i release-tidslinjeh?ndelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationer",
|
||||
"description": "Forbind GitHub-repositories, branches og Coolify-services."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Indstillinger",
|
||||
"description": "Konfigurer Release Managers GitHub-adgang og webhook-indstillinger."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- og Coolify-m?l",
|
||||
"assignments": "Pilot-tildelinger",
|
||||
"deployments": "F?rste udrulning",
|
||||
"replay": "Replay-opsamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Udrulninger",
|
||||
"timeline_events": "Tidslinjeh?ndelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Oversigt",
|
||||
"subtitle": "Aktuel kanalstatus, release-aktivitet og modulstatus.",
|
||||
"guided_setup": "Guidet ops?tning",
|
||||
"next_step": "N?ste: {step}",
|
||||
"ready": "Release Manager er klar til daglig drift.",
|
||||
"add_suggested_channel": "Tilføj foreslået kanal",
|
||||
"module_health_empty": "Modul-health snapshots vises, når probes er blevet registreret."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-indstillinger",
|
||||
"subtitle": "Konfigurer GitHub-tokenet, der bruges til private repositories og branch-opslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemmelighed",
|
||||
"github_webhook_secret": "GitHub webhook-hemmelighed",
|
||||
"configured": "Konfigureret",
|
||||
"not_configured": "Ikke konfigureret",
|
||||
"loaded_from": "Indl?st fra {variable}",
|
||||
"set_below": "Angiv {variable} nedenfor",
|
||||
"private_repositories_prefix": "Private repositories l?ses med serverens milj?variabel",
|
||||
"private_repositories_or": "eller modulets konfigurationsvariabel",
|
||||
"github_token_message": "Eksempel: github_pat_... med adgang til de private repositories, Release Manager udruller.",
|
||||
"github_token_placeholder": "Lad feltet v?re tomt for at beholde det eksisterende token",
|
||||
"github_api_url_message": "Konfigureret i ReleaseManager.github_api_url. Brug https://api.github.com medmindre GitHub Enterprise bruges.",
|
||||
"webhook_secret_message": "Valgfrit; lad feltet v?re tomt for at beholde den eksisterende hemmelighed.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemmelighed",
|
||||
"save": "Gem indstillinger",
|
||||
"back_to_integrations": "Tilbage til integrationer",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Tilføj GitHub-token",
|
||||
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
|
||||
"configure_this_in": "Konfigurer dette i",
|
||||
"server_environment_variable": "Serverens miljøvariabel",
|
||||
"takes_precedence": "har forrang, når den er sat.",
|
||||
"recommended_access": "Anbefalet adgang",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
|
||||
"github_api_url_title": "Tilføj GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Indtast en fuld HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-eksempel",
|
||||
"github_webhook_secret_title": "Tilføj GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Gem den delte secret i",
|
||||
"review_title": "Gennemgå GitHub-opsætning",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Mangler værdi"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen er ikke klar",
|
||||
"summary_prefix": "Din konto er tildelt",
|
||||
"summary_suffix": ", men kanalen mangler den konfiguration, der skal bruges for at indl?se dens release-image.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Tjekker igen om {seconds}s",
|
||||
"refresh_error": "Release-status kunne ikke opdateres. Det n?ste automatiske tjek pr?ver igen.",
|
||||
"ignore": "Ignorer de n?ste 5 minutter",
|
||||
"check_again": "Tjek igen",
|
||||
"logout": "Log ud",
|
||||
"base_image": "Basis-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du er nu p? {channel}",
|
||||
"summary_prefix": "Din konto er blevet tildelt release-kanalen",
|
||||
"summary_suffix": "Denne enhed husker, at du har set denne besked.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-version",
|
||||
"api_version": "API-version",
|
||||
"assigned_channel": "Tildelt kanal",
|
||||
"current_app_image": "Nuv?rende app-image",
|
||||
"current_api": "Nuv?rende API",
|
||||
"base_image": "Basis-image",
|
||||
"continue": "Forts?t"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nej",
|
||||
"off": "fra"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Rediger",
|
||||
"rollback": "Rul tilbage",
|
||||
"update": "Opdater",
|
||||
"create": "Opret",
|
||||
"clear": "Ryd",
|
||||
"assign": "Tildel",
|
||||
"remove": "Fjern",
|
||||
"test_access": "Test adgang",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promover",
|
||||
"enable": "Aktiver",
|
||||
"search": "Søg",
|
||||
"save_target": "Gem mål",
|
||||
"delete": "Slet",
|
||||
"back": "Tilbage",
|
||||
"next": "Næste"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Handlinger",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrolleret",
|
||||
"subject": "Emne",
|
||||
"reason": "Årsag",
|
||||
"expires": "Udløber",
|
||||
"deployment": "Deployment",
|
||||
"source": "Kilde",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "fuldt redigeret"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "bruger",
|
||||
"subuser": "medarbejder",
|
||||
"customer": "kunde",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "fejl",
|
||||
"warning": "advarsel",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployet",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiveret",
|
||||
"queued": "i kø",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "ikke konfigureret",
|
||||
"warning": "advarsel",
|
||||
"failed": "fejlet",
|
||||
"down": "nede",
|
||||
"error": "fejl",
|
||||
"unknown": "ukendt"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Opret og juster gradvise release-kanaler.",
|
||||
"rollout_percent": "{percent}% udrulning",
|
||||
"already_configured": "Allerede konfigureret",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Navn",
|
||||
"name_message": "Vises til operatører, når de gennemgår en kanal.",
|
||||
"name_placeholder": "Navn",
|
||||
"rollout": "Udrulning",
|
||||
"rollout_message": "0-100 procent af ikke-tildelt trafik.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchende release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchende API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
|
||||
"retention_days": "Opbevaringsdage",
|
||||
"retention_days_message": "1-365 dage.",
|
||||
"enabled": "Aktiveret",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiveret",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tildelinger",
|
||||
"subtitle": "Fastgør brugere, medarbejdere eller kunder til release-kanaler.",
|
||||
"subject_type": "Emnetype",
|
||||
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Søg på kundenummer eller navn, og vælg derefter den præcise kunde.",
|
||||
"customer_placeholder": "Søg kunde",
|
||||
"subject_id": "Emne-ID",
|
||||
"subject_id_message": "Eksempel: bruger-id 42",
|
||||
"subject_id_placeholder": "Emne-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Fastgjorte emner springer procentudrulningen over.",
|
||||
"reason": "Årsag",
|
||||
"reason_message": "Eksempel: canary-validering for kundesupport.",
|
||||
"reason_placeholder": "Årsag",
|
||||
"expires": "Udløber",
|
||||
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Start, promover og inspicer release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
|
||||
"select_target_or_manual": "Vælg mål eller manuel",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-valg",
|
||||
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
|
||||
"latest_from_branch": "Seneste fra branch",
|
||||
"specific_commit": "Specifik commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionslabel",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Eksempel: bruger-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalafgrænsning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalafgrænsning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Udløber",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Alvorlighed",
|
||||
"any_severity": "Alle alvorligheder",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub og Coolify",
|
||||
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
|
||||
"choose_repository": "Vælg repository",
|
||||
"add_health_url": "tilføj health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Åbn GitHub token-indstillinger",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domæne",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domæne",
|
||||
"https_domain_for_coolify": "HTTPS-domæne til Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nu",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Opret manglende service",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,412 @@
|
||||
"subtitle": "@:{'terms.glossary.konfiguration'} @:{'terms.glossary.der'} @:{'terms.glossary.xlvask'}-@.capitalize:{'terms.glossary.integration'}",
|
||||
"title": "@:{'terms.glossary.xlvask'}-@:{'terms.glossary.konfiguration'}",
|
||||
"username_desc": "@.capitalize:{'terms.glossary.der'} @:{'terms.glossary.benutzername'} @:{'terms.glossary.wird'} @:{'terms.glossary.zur'} @:{'terms.glossary.authentifizierung'} @:{'terms.glossary.der'} @:{'terms.glossary.api'}-@:{'terms.glossary.verbindung'} @:{'terms.glossary.verwendet'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Schrittweise API- und Front-End-Releases",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release-Manager-Aktionen werden an diese API gesendet. Verwende die Produktions-Kontroll-API, au?er du testest ein Staging-Backend.",
|
||||
"example": "Beispiel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-Endpunkt",
|
||||
"endpoint_message": "?ndere dies nur, wenn Release-Manager-Endpunkte auf der Ziel-API verf?gbar sind.",
|
||||
"endpoint_aria": "Release Manager Kontroll-API-URL",
|
||||
"use_tooltip": "Release-Daten von dieser API laden",
|
||||
"use": "Verwenden",
|
||||
"reset_tooltip": "Zur Standard-Kontroll-API zur?ckkehren",
|
||||
"reset": "Zur?cksetzen",
|
||||
"known_endpoints": "Bekannte Kontroll-API-Endpunkte"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "?bersicht",
|
||||
"description": "Kanalzustand, Einrichtungsfortschritt und aktueller Release-Status."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kan?le",
|
||||
"description": "Stabile, Canary- und Zielkan?le mit Rollout-Grenzen erstellen."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Zuweisungen",
|
||||
"description": "Benutzer, Unterbenutzer oder Kunden einem bestimmten Release-Kanal zuweisen."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Bereitstellungen",
|
||||
"description": "Bereitstellungen starten und erfolgreiche Builds promoten."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Gezielte Erfassung aktivieren und Release-Timeline-Ereignisse durchsuchen."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationen",
|
||||
"description": "GitHub-Repositories, Branches und Coolify-Dienste verbinden."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Einstellungen",
|
||||
"description": "GitHub-Zugriff und Webhook-Einstellungen des Release Managers konfigurieren."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kan?le",
|
||||
"targets": "GitHub- und Coolify-Ziele",
|
||||
"assignments": "Pilot-Zuweisungen",
|
||||
"deployments": "Erste Bereitstellung",
|
||||
"replay": "Replay-Erfassung"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kan?le",
|
||||
"targets": "Ziele",
|
||||
"deployments": "Bereitstellungen",
|
||||
"timeline_events": "Timeline-Ereignisse"
|
||||
},
|
||||
"overview": {
|
||||
"title": "?bersicht",
|
||||
"subtitle": "Aktueller Kanalzustand, Release-Aktivit?t und Modulstatus.",
|
||||
"guided_setup": "Gef?hrte Einrichtung",
|
||||
"next_step": "N?chster Schritt: {step}",
|
||||
"ready": "Release Manager ist f?r den t?glichen Betrieb bereit.",
|
||||
"add_suggested_channel": "Vorgeschlagenen Kanal hinzufügen",
|
||||
"module_health_empty": "Modul-Health-Snapshots erscheinen, nachdem Probes aufgezeichnet wurden."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release-Manager-Einstellungen",
|
||||
"subtitle": "GitHub-Token f?r private Repositories und Branch-Abfragen konfigurieren.",
|
||||
"github_token": "GitHub-Token",
|
||||
"github_api_url": "GitHub-API-URL",
|
||||
"webhook_secret": "Webhook-Secret",
|
||||
"github_webhook_secret": "GitHub-Webhook-Secret",
|
||||
"configured": "Konfiguriert",
|
||||
"not_configured": "Nicht konfiguriert",
|
||||
"loaded_from": "Geladen aus {variable}",
|
||||
"set_below": "{variable} unten festlegen",
|
||||
"private_repositories_prefix": "Private Repositories werden mit der Server-Umgebungsvariable gelesen",
|
||||
"private_repositories_or": "oder der Modul-Konfigurationsvariable",
|
||||
"github_token_message": "Beispiel: github_pat_... mit Zugriff auf die privaten Repositories, die Release Manager bereitstellt.",
|
||||
"github_token_placeholder": "Leer lassen, um das vorhandene Token zu behalten",
|
||||
"github_api_url_message": "Konfiguriert in ReleaseManager.github_api_url. https://api.github.com verwenden, sofern GitHub Enterprise nicht genutzt wird.",
|
||||
"webhook_secret_message": "Optional; leer lassen, um das vorhandene Secret zu behalten.",
|
||||
"webhook_secret_placeholder": "Webhook-HMAC-Secret",
|
||||
"save": "Einstellungen speichern",
|
||||
"back_to_integrations": "Zur?ck zu Integrationen",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-Token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub Webhook Secret",
|
||||
"review": "Prüfung"
|
||||
},
|
||||
"github_token_title": "GitHub-Token hinzufügen",
|
||||
"github_token_description": "Release Manager verwendet diesen Token, um private Repositories, Branches und Commits aufzulisten, bevor ein Deployment-Ziel gespeichert oder getestet wird.",
|
||||
"configure_this_in": "Dies konfigurieren in",
|
||||
"server_environment_variable": "Die Server-Umgebungsvariable",
|
||||
"takes_precedence": "hat Vorrang, wenn sie gesetzt ist.",
|
||||
"recommended_access": "Empfohlener Zugriff",
|
||||
"recommended_access_description": "Fine-grained GitHub-Token mit Contents: read-Zugriff für jedes Release-Repository.",
|
||||
"github_api_url_title": "GitHub API-URL hinzufügen",
|
||||
"github_api_url_description": "GitHub Cloud standardmäßig verwenden oder auf die REST-API-Basis-URL einer GitHub Enterprise Server-Installation zeigen.",
|
||||
"github_api_url_invalid": "Vollständige HTTP- oder HTTPS-URL eingeben.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-Beispiel",
|
||||
"github_webhook_secret_title": "GitHub Webhook Secret hinzufügen",
|
||||
"github_webhook_secret_description": "Dasselbe Secret in GitHub-Repository-Webhooks verwenden, damit Release Manager Deployment-Events vor der Verarbeitung verifizieren kann.",
|
||||
"github_webhook_url": "GitHub Webhook-URL:",
|
||||
"store_shared_secret_in": "Geteiltes Secret speichern in",
|
||||
"review_title": "GitHub-Konfiguration prüfen",
|
||||
"review_description": "Werte speichern und dann unter Integrationen ein privates Repository, einen Branch und einen bestimmten Commit oder den neuesten Commit auswählen, bevor der Zugriff getestet wird.",
|
||||
"ready": "Bereit",
|
||||
"needs_value": "Wert fehlt"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-Kanal ist nicht bereit",
|
||||
"summary_prefix": "Dein Konto ist zugewiesen zu",
|
||||
"summary_suffix": ", aber diesem Kanal fehlt die Konfiguration, um sein Release-Image zu laden.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Erneute Pr?fung in {seconds}s",
|
||||
"refresh_error": "Release-Status konnte nicht aktualisiert werden. Die n?chste automatische Pr?fung versucht es erneut.",
|
||||
"ignore": "N?chste 5 Minuten ignorieren",
|
||||
"check_again": "Erneut pr?fen",
|
||||
"logout": "Abmelden",
|
||||
"base_image": "Basis-Image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du bist jetzt auf {channel}",
|
||||
"summary_prefix": "Dein Konto wurde dem Release-Kanal",
|
||||
"summary_suffix": "zugewiesen. Dieses Ger?t merkt sich, dass du diesen Hinweis gesehen hast.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-Version",
|
||||
"api_version": "API-Version",
|
||||
"assigned_channel": "Zugewiesener Kanal",
|
||||
"current_app_image": "Aktuelles App-Image",
|
||||
"current_api": "Aktuelle API",
|
||||
"base_image": "Basis-Image",
|
||||
"continue": "Fortfahren"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nein",
|
||||
"off": "aus"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Bearbeiten",
|
||||
"rollback": "Zurückrollen",
|
||||
"update": "Aktualisieren",
|
||||
"create": "Erstellen",
|
||||
"clear": "Leeren",
|
||||
"assign": "Zuweisen",
|
||||
"remove": "Entfernen",
|
||||
"test_access": "Zugriff testen",
|
||||
"deploy": "Deployen",
|
||||
"promote": "Promoten",
|
||||
"enable": "Aktivieren",
|
||||
"search": "Suchen",
|
||||
"save_target": "Ziel speichern",
|
||||
"delete": "Löschen",
|
||||
"back": "Zurück",
|
||||
"next": "Weiter"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Rollout",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Aktionen",
|
||||
"module": "Module",
|
||||
"status": "Status",
|
||||
"checked": "Geprüft",
|
||||
"subject": "Subjekt",
|
||||
"reason": "Grund",
|
||||
"expires": "Läuft ab",
|
||||
"deployment": "Deployment",
|
||||
"source": "Quelle",
|
||||
"started": "Gestartet",
|
||||
"target": "Ziel",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "Frontend",
|
||||
"api": "API"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "Metadaten",
|
||||
"full_redacted": "vollständig redigiert"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "Benutzer",
|
||||
"subuser": "Mitarbeiter",
|
||||
"customer": "Kunde",
|
||||
"channel": "Kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "Fehler",
|
||||
"warning": "Warnung",
|
||||
"info": "Info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployt",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiviert",
|
||||
"queued": "in Warteschlange",
|
||||
"deploying": "deploying",
|
||||
"metadata": "Metadaten",
|
||||
"not_configured": "nicht konfiguriert",
|
||||
"warning": "Warnung",
|
||||
"failed": "fehlgeschlagen",
|
||||
"down": "down",
|
||||
"error": "Fehler",
|
||||
"unknown": "unbekannt"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Vorgeschlagener Wert",
|
||||
"suggestion": "Vorschlag",
|
||||
"use_typed_value": "Eingegebenen Wert verwenden",
|
||||
"custom": "Benutzerdefiniert",
|
||||
"api_url": "API URL",
|
||||
"frontend_url": "Frontend URL",
|
||||
"known_release_url": "Bekannte Release-URL",
|
||||
"deploy_here_with_coolify": "{app} hier mit Coolify deployen",
|
||||
"prepare_coolify_target": "Coolify-Ziel mit aktiviertem HTTPS/SSL vorbereiten",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-Repository",
|
||||
"owner_repository": "owner/repository",
|
||||
"git_branch": "Git branch",
|
||||
"deployment_branch": "Deployment branch",
|
||||
"github_commit": "GitHub commit",
|
||||
"git_commit": "Git commit",
|
||||
"use_specific_commit_sha": "Bestimmte Commit-SHA verwenden",
|
||||
"custom_commit": "Benutzerdefinierter Commit",
|
||||
"health_url": "Health URL",
|
||||
"release_health_check": "Release-Health-Check",
|
||||
"coolify_service": "Coolify service",
|
||||
"service_uuid": "Service UUID",
|
||||
"known_coolify_service_uuid": "Known Coolify service UUID",
|
||||
"control_api": "Kontroll-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-Endpunkt",
|
||||
"module": "Module",
|
||||
"timeline_module_key": "Timeline-Modulschlüssel"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "GitHub-Repositories konnten nicht geladen werden.",
|
||||
"github_branches": "GitHub-Branches konnten nicht geladen werden.",
|
||||
"repository_branch_required": "Repository und Branch sind erforderlich, bevor GitHub-Zugriff getestet werden kann.",
|
||||
"github_access_failed": "GitHub-Repository-Zugriffstest ist fehlgeschlagen."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanäle",
|
||||
"subtitle": "Graduale Release-Kanäle erstellen und anpassen.",
|
||||
"rollout_percent": "{percent}% rollout",
|
||||
"already_configured": "Bereits konfiguriert",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Example: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Name",
|
||||
"name_message": "Wird Operatoren bei der Kanalprüfung angezeigt.",
|
||||
"name_placeholder": "Name",
|
||||
"rollout": "Rollout",
|
||||
"rollout_message": "0-100 Prozent des nicht zugewiesenen Traffics.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"frontend_url_message": "Example: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend URL",
|
||||
"no_matching_release_url": "Keine passende Release-URL.",
|
||||
"api_url": "API URL",
|
||||
"api_url_message": "Example: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API URL",
|
||||
"no_matching_api_url": "Keine passende API-URL.",
|
||||
"replay_capture": "Replay-Erfassung",
|
||||
"replay_capture_message": "Metadaten sind für breite Kanäle sicherer.",
|
||||
"retention_days": "Aufbewahrungstage",
|
||||
"retention_days_message": "1-365 Tage.",
|
||||
"enabled": "Aktiviert",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiviert",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Zuweisungen",
|
||||
"subtitle": "Benutzer, Mitarbeiter oder Kunden an Release-Kanäle binden.",
|
||||
"subject_type": "Subjekttyp",
|
||||
"subject_type_message": "Identitätstyp zum Binden auswählen.",
|
||||
"customer": "Customer",
|
||||
"customer_message": "Nach Kundennummer oder Name suchen und dann den genauen Kunden auswählen.",
|
||||
"customer_placeholder": "Kunde suchen",
|
||||
"subject_id": "Subjekt-ID",
|
||||
"subject_id_message": "Beispiel: Benutzer-ID 42",
|
||||
"subject_id_placeholder": "Subjekt-ID",
|
||||
"channel": "Channel",
|
||||
"channel_message": "Gebundene Subjekte umgehen den prozentualen Rollout.",
|
||||
"reason": "Grund",
|
||||
"reason_message": "Beispiel: Canary-Validierung für Kundensupport.",
|
||||
"reason_placeholder": "Grund",
|
||||
"expires": "Läuft ab",
|
||||
"expires_message": "Leer lassen für kein automatisches Ablaufdatum."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Release-Deployments starten, promoten und prüfen.",
|
||||
"manual_deployment_record": "Manueller Deployment-Eintrag",
|
||||
"target": "Ziel",
|
||||
"target_message": "Gespeichertes Ziel wählen oder manuelle Felder editierbar lassen.",
|
||||
"select_target_or_manual": "Ziel oder manuell auswählen",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/front-end-vue",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-Auswahl",
|
||||
"commit_selection_message": "Neueste Version wird mit dem konfigurierten GitHub-Token als Branch-Head aufgelöst.",
|
||||
"latest_from_branch": "Neuester vom Branch",
|
||||
"specific_commit": "Bestimmter Commit",
|
||||
"specific_commit_message": "Example: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionslabel",
|
||||
"version_label_message": "Example: api-2026.05.19",
|
||||
"version_label_placeholder": "Versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Token-Zugriff auf das ausgewählte Repository, den Branch und Commit prüfen."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Gezielte Erfassung aktivieren und Fehler-Timelines Schritt für Schritt prüfen.",
|
||||
"capture_channel": "{channel} erfassen",
|
||||
"target_type": "Zieltyp",
|
||||
"target": "Ziel",
|
||||
"target_message": "Beispiel: Benutzer-ID 42 oder Kanal-Slug canary.",
|
||||
"target_placeholder": "Ziel-ID oder Kanal-Slug",
|
||||
"channel_scope": "Kanalbereich",
|
||||
"channel_scope_message": "Optional; begrenzt Replay-Erfassung auf einen Kanal.",
|
||||
"no_channel_scope": "Kein Kanalbereich",
|
||||
"capture_level": "Erfassungsstufe",
|
||||
"expires": "Läuft ab",
|
||||
"expires_message": "Leer lassen für manuelle Deaktivierung.",
|
||||
"trace_id": "Trace ID",
|
||||
"trace_id_message": "Example: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace id",
|
||||
"channel": "Channel",
|
||||
"channel_placeholder": "Channel",
|
||||
"principal_id": "Principal ID",
|
||||
"principal_id_placeholder": "Principal-ID",
|
||||
"module": "Module",
|
||||
"module_placeholder": "Module",
|
||||
"severity": "Schweregrad",
|
||||
"any_severity": "Beliebiger Schweregrad",
|
||||
"no_timeline_events": "Keine Timeline-Events entsprechen den aktuellen Filtern."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub und Coolify",
|
||||
"subtitle": "GitHub-Repositories und Branches kanalspezifischen Coolify-Services zuordnen.",
|
||||
"choose_repository": "Repository auswählen",
|
||||
"add_health_url": "Health-URL hinzufügen",
|
||||
"github_token_not_configured": "GitHub-Token ist nicht konfiguriert; private Repositories können nicht aufgelistet werden.",
|
||||
"open_github_token_settings": "GitHub-Token-Einstellungen öffnen",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/backend-php",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Neueste: {commit}",
|
||||
"coolify_instance": "Coolify-Instanz",
|
||||
"coolify_instance_message": "Leer lassen für manuelle Deployment-Einträge.",
|
||||
"no_coolify_instance": "Keine Coolify-Instanz",
|
||||
"coolify_instance_placeholder": "Coolify-Instanz-ID",
|
||||
"coolify_service_uuid": "Coolify service UUID",
|
||||
"coolify_service_uuid_message": "Bestehenden Service auswählen oder UUID einfügen.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service UUID",
|
||||
"health_url": "Health URL",
|
||||
"health_url_message": "Example: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health URL",
|
||||
"ssl_domain": "SSL-Domain",
|
||||
"ssl_domain_message": "Example: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-Domain",
|
||||
"https_domain_for_coolify": "HTTPS-Domain für Coolify",
|
||||
"auto_deploy": "Auto-Deploy",
|
||||
"auto_deploy_tooltip": "Automatisch Deployments erstellen, wenn sich dieses Ziel ändert.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Coolify anweisen, HTTPS für die konfigurierte Domain bereitzustellen.",
|
||||
"deploy_now": "Jetzt deployen",
|
||||
"deploy_now_tooltip": "Deployment direkt nach dem Speichern des Ziels starten.",
|
||||
"create_missing_service": "Fehlenden Service erstellen",
|
||||
"create_missing_service_tooltip": "Release Manager erlauben, einen fehlenden Coolify-Service zu erstellen.",
|
||||
"test_access_tooltip": "Prüfen, ob der konfigurierte GitHub-Token dieses Repository und den Branch lesen kann."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,412 @@
|
||||
"subtitle": "@.capitalize:{'terms.glossary.configuration'} @:{'terms.glossary.of'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.xlvask'} @:{'terms.glossary.integration'}",
|
||||
"title": "@:{'terms.glossary.xlvask'} @:{'terms.glossary.configuration'}",
|
||||
"username_desc": "@.capitalize:{'terms.replication.article.host_mention'} @:{'terms.glossary.username'} @:{'terms.glossary.is'} @:{'terms.glossary.used'} @:{'terms.glossary.to'} @:{'terms.glossary.authenticate'} @:{'terms.replication.article.host_mention'} @:{'terms.glossary.api'} @:{'terms.glossary.connection'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradual API and Front-End releases",
|
||||
"control_api": {
|
||||
"title": "Control API",
|
||||
"tooltip": "Release Manager actions are sent to this API. Use the production control API unless testing a staged backend.",
|
||||
"example": "Example: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API endpoint",
|
||||
"endpoint_message": "Switch this only when Release Manager endpoints are available on the target API.",
|
||||
"endpoint_aria": "Release Manager control API URL",
|
||||
"use_tooltip": "Load release data from this API",
|
||||
"use": "Use",
|
||||
"reset_tooltip": "Return to the default control API",
|
||||
"reset": "Reset",
|
||||
"known_endpoints": "Known control API endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Overview",
|
||||
"description": "Channel health, setup progress, and the latest release state."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Channels",
|
||||
"description": "Create stable, canary, and targeted channels with rollout limits."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Assignments",
|
||||
"description": "Pin users, subusers, or customers to a specific release channel."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Deployments",
|
||||
"description": "Trigger deployments and promote successful builds."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Enable targeted capture and search release timeline events."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrations",
|
||||
"description": "Connect GitHub repositories, branches, and Coolify services."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Settings",
|
||||
"description": "Configure Release Manager GitHub access and webhook settings."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Channels",
|
||||
"targets": "GitHub and Coolify targets",
|
||||
"assignments": "Pilot assignments",
|
||||
"deployments": "First deployment",
|
||||
"replay": "Replay capture"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Channels",
|
||||
"targets": "Targets",
|
||||
"deployments": "Deployments",
|
||||
"timeline_events": "Timeline events"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Overview",
|
||||
"subtitle": "Current channel health, release activity, and module status.",
|
||||
"guided_setup": "Guided setup",
|
||||
"next_step": "Next: {step}",
|
||||
"ready": "Release Manager is ready for day-to-day operations.",
|
||||
"add_suggested_channel": "Add suggested channel",
|
||||
"module_health_empty": "Module health snapshots appear after probes have been recorded."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager Settings",
|
||||
"subtitle": "Configure the GitHub token used for private repositories and branch lookups.",
|
||||
"github_token": "GitHub token",
|
||||
"github_api_url": "GitHub API URL",
|
||||
"webhook_secret": "Webhook secret",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"configured": "Configured",
|
||||
"not_configured": "Not configured",
|
||||
"loaded_from": "Loaded from {variable}",
|
||||
"set_below": "Set {variable} below",
|
||||
"private_repositories_prefix": "Private repositories are read with the server environment variable",
|
||||
"private_repositories_or": "or the module config variable",
|
||||
"github_token_message": "Example: github_pat_... with access to the private repositories Release Manager deploys.",
|
||||
"github_token_placeholder": "Leave blank to keep the existing token",
|
||||
"github_api_url_message": "Configured in ReleaseManager.github_api_url. Use https://api.github.com unless using GitHub Enterprise.",
|
||||
"webhook_secret_message": "Optional; leave blank to keep the existing secret.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC secret",
|
||||
"save": "Save settings",
|
||||
"back_to_integrations": "Back to integrations",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub token",
|
||||
"github_api_url": "GitHub API URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Review"
|
||||
},
|
||||
"github_token_title": "Add GitHub token",
|
||||
"github_token_description": "Release Manager uses this token to list private repositories, branches, and commits before a deployment target is saved or tested.",
|
||||
"configure_this_in": "Configure this in",
|
||||
"server_environment_variable": "The server environment variable",
|
||||
"takes_precedence": "takes precedence when it is set.",
|
||||
"recommended_access": "Recommended access",
|
||||
"recommended_access_description": "Fine-grained GitHub token with Contents: read access for each release repository.",
|
||||
"github_api_url_title": "Add GitHub API URL",
|
||||
"github_api_url_description": "Use GitHub Cloud by default, or point this at the REST API base URL for a GitHub Enterprise Server installation.",
|
||||
"github_api_url_invalid": "Enter a full HTTP or HTTPS URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise example",
|
||||
"github_webhook_secret_title": "Add GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Use the same secret in GitHub repository webhooks so Release Manager can verify deployment events before handling them.",
|
||||
"github_webhook_url": "GitHub webhook URL:",
|
||||
"store_shared_secret_in": "Store the shared secret in",
|
||||
"review_title": "Review GitHub setup",
|
||||
"review_description": "Save the values, then use Integrations to select a private repository, branch, and specific commit or latest commit before testing access.",
|
||||
"ready": "Ready",
|
||||
"needs_value": "Needs value"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release channel is not ready",
|
||||
"summary_prefix": "Your account is assigned to",
|
||||
"summary_suffix": ", but that channel is missing the configuration needed to load its release image.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"api_url": "API URL",
|
||||
"checking_again": "Checking again in {seconds}s",
|
||||
"refresh_error": "Release status could not be refreshed. The next automatic check will try again.",
|
||||
"ignore": "Ignore next 5 minutes",
|
||||
"check_again": "Check again",
|
||||
"logout": "Logout",
|
||||
"base_image": "Base image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "You are now on {channel}",
|
||||
"summary_prefix": "Your account has been assigned to the",
|
||||
"summary_suffix": "release channel. This device will remember that you have seen this notice.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend version",
|
||||
"api_version": "API version",
|
||||
"assigned_channel": "Assigned channel",
|
||||
"current_app_image": "Current app image",
|
||||
"current_api": "Current API",
|
||||
"base_image": "Base image",
|
||||
"continue": "Continue"
|
||||
},
|
||||
"common": {
|
||||
"yes": "yes",
|
||||
"no": "no",
|
||||
"off": "off"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Edit",
|
||||
"rollback": "Rollback",
|
||||
"update": "Update",
|
||||
"create": "Create",
|
||||
"clear": "Clear",
|
||||
"assign": "Assign",
|
||||
"remove": "Remove",
|
||||
"test_access": "Test access",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promote",
|
||||
"enable": "Enable",
|
||||
"search": "Search",
|
||||
"save_target": "Save target",
|
||||
"delete": "Delete",
|
||||
"back": "Back",
|
||||
"next": "Next"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Channel",
|
||||
"rollout": "Rollout",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Actions",
|
||||
"module": "Module",
|
||||
"status": "Status",
|
||||
"checked": "Checked",
|
||||
"subject": "Subject",
|
||||
"reason": "Reason",
|
||||
"expires": "Expires",
|
||||
"deployment": "Deployment",
|
||||
"source": "Source",
|
||||
"started": "Started",
|
||||
"target": "Target",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "full_redacted"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "user",
|
||||
"subuser": "subuser",
|
||||
"customer": "customer",
|
||||
"channel": "channel"
|
||||
},
|
||||
"severity": {
|
||||
"error": "error",
|
||||
"warning": "warning",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "active",
|
||||
"deployed": "deployed",
|
||||
"stable": "stable",
|
||||
"enabled": "enabled",
|
||||
"queued": "queued",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "not configured",
|
||||
"warning": "warning",
|
||||
"failed": "failed",
|
||||
"down": "down",
|
||||
"error": "error",
|
||||
"unknown": "unknown"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Suggested value",
|
||||
"suggestion": "Suggestion",
|
||||
"use_typed_value": "Use typed value",
|
||||
"custom": "Custom",
|
||||
"api_url": "API URL",
|
||||
"frontend_url": "Frontend URL",
|
||||
"known_release_url": "Known release URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} here with Coolify",
|
||||
"prepare_coolify_target": "Prepare a Coolify target with HTTPS/SSL enabled",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub repository",
|
||||
"owner_repository": "owner/repository",
|
||||
"git_branch": "Git branch",
|
||||
"deployment_branch": "Deployment branch",
|
||||
"github_commit": "GitHub commit",
|
||||
"git_commit": "Git commit",
|
||||
"use_specific_commit_sha": "Use specific commit SHA",
|
||||
"custom_commit": "Custom commit",
|
||||
"health_url": "Health URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify service",
|
||||
"service_uuid": "Service UUID",
|
||||
"known_coolify_service_uuid": "Known Coolify service UUID",
|
||||
"control_api": "Control API",
|
||||
"release_manager_api_endpoint": "Release Manager API endpoint",
|
||||
"module": "Module",
|
||||
"timeline_module_key": "Timeline module key"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Could not load GitHub repositories.",
|
||||
"github_branches": "Could not load GitHub branches.",
|
||||
"repository_branch_required": "Repository and branch are required before testing GitHub access.",
|
||||
"github_access_failed": "GitHub repository access test failed."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Channels",
|
||||
"subtitle": "Create and tune gradual release channels.",
|
||||
"rollout_percent": "{percent}% rollout",
|
||||
"already_configured": "Already configured",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Example: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Name",
|
||||
"name_message": "Shown to operators when reviewing a channel.",
|
||||
"name_placeholder": "Name",
|
||||
"rollout": "Rollout",
|
||||
"rollout_message": "0-100 percent of unassigned traffic.",
|
||||
"frontend_url": "Frontend URL",
|
||||
"frontend_url_message": "Example: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend URL",
|
||||
"no_matching_release_url": "No matching release URL.",
|
||||
"api_url": "API URL",
|
||||
"api_url_message": "Example: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API URL",
|
||||
"no_matching_api_url": "No matching API URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata is safer for broad channels.",
|
||||
"retention_days": "Retention days",
|
||||
"retention_days_message": "1-365 days.",
|
||||
"enabled": "Enabled",
|
||||
"default_channel": "Default channel",
|
||||
"replay_enabled": "Replay enabled",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Assignments",
|
||||
"subtitle": "Pin users, subusers, or customers to release channels.",
|
||||
"subject_type": "Subject type",
|
||||
"subject_type_message": "Choose the identity type to pin.",
|
||||
"customer": "Customer",
|
||||
"customer_message": "Search by customer number or name, then select the exact customer.",
|
||||
"customer_placeholder": "Search customer",
|
||||
"subject_id": "Subject ID",
|
||||
"subject_id_message": "Example: user id 42",
|
||||
"subject_id_placeholder": "Subject id",
|
||||
"channel": "Channel",
|
||||
"channel_message": "Pinned subjects bypass percentage rollout.",
|
||||
"reason": "Reason",
|
||||
"reason_message": "Example: canary validation for customer support.",
|
||||
"reason_placeholder": "Reason",
|
||||
"expires": "Expires",
|
||||
"expires_message": "Leave blank for no automatic expiry."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Trigger, promote, and inspect release deployments.",
|
||||
"manual_deployment_record": "Manual deployment record",
|
||||
"target": "Target",
|
||||
"target_message": "Pick a saved target or keep manual fields editable.",
|
||||
"select_target_or_manual": "Select target or manual",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/front-end-vue",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit selection",
|
||||
"commit_selection_message": "Latest resolves to the branch head with the configured GitHub token.",
|
||||
"latest_from_branch": "Latest from branch",
|
||||
"specific_commit": "Specific commit",
|
||||
"specific_commit_message": "Example: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Version label",
|
||||
"version_label_message": "Example: api-2026.05.19",
|
||||
"version_label_placeholder": "version label",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Verify token access to the selected repository, branch, and commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Enable targeted capture and step through failure timelines.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Target type",
|
||||
"target": "Target",
|
||||
"target_message": "Example: user id 42 or channel slug canary.",
|
||||
"target_placeholder": "Target id or channel slug",
|
||||
"channel_scope": "Channel scope",
|
||||
"channel_scope_message": "Optional; limits replay capture to a channel.",
|
||||
"no_channel_scope": "No channel scope",
|
||||
"capture_level": "Capture level",
|
||||
"expires": "Expires",
|
||||
"expires_message": "Leave blank for manual disable.",
|
||||
"trace_id": "Trace ID",
|
||||
"trace_id_message": "Example: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace id",
|
||||
"channel": "Channel",
|
||||
"channel_placeholder": "Channel",
|
||||
"principal_id": "Principal ID",
|
||||
"principal_id_placeholder": "Principal id",
|
||||
"module": "Module",
|
||||
"module_placeholder": "Module",
|
||||
"severity": "Severity",
|
||||
"any_severity": "Any severity",
|
||||
"no_timeline_events": "No timeline events match the current filters."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub and Coolify",
|
||||
"subtitle": "Map GitHub repositories and branches to channel-specific Coolify services.",
|
||||
"choose_repository": "Choose repository",
|
||||
"add_health_url": "add health URL",
|
||||
"github_token_not_configured": "GitHub token is not configured; private repositories cannot be listed.",
|
||||
"open_github_token_settings": "Open GitHub token settings",
|
||||
"channel": "Channel",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Example: truckwash/backend-php",
|
||||
"repository_placeholder": "owner/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Example: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Latest: {commit}",
|
||||
"coolify_instance": "Coolify instance",
|
||||
"coolify_instance_message": "Leave empty for manual deployment records.",
|
||||
"no_coolify_instance": "No Coolify instance",
|
||||
"coolify_instance_placeholder": "Coolify instance id",
|
||||
"coolify_service_uuid": "Coolify service UUID",
|
||||
"coolify_service_uuid_message": "Select an existing service or paste a UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service UUID",
|
||||
"health_url": "Health URL",
|
||||
"health_url_message": "Example: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health URL",
|
||||
"ssl_domain": "SSL domain",
|
||||
"ssl_domain_message": "Example: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL domain",
|
||||
"https_domain_for_coolify": "HTTPS domain for Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Automatically create deployments when this target changes.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Ask Coolify to provision HTTPS for the configured domain.",
|
||||
"deploy_now": "Deploy now",
|
||||
"deploy_now_tooltip": "Start a deployment immediately after saving the target.",
|
||||
"create_missing_service": "Create missing service",
|
||||
"create_missing_service_tooltip": "Allow Release Manager to create a missing Coolify service.",
|
||||
"test_access_tooltip": "Verify the configured GitHub token can read this repository and branch."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +351,412 @@
|
||||
"title": "@:{'phrases.compat.configuration.xlvask.title'}",
|
||||
"username": "@:auth.username",
|
||||
"username_desc": "@:{'phrases.compat.configuration.xlvask.username_desc'}"
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.subtitle'}",
|
||||
"control_api": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.control_api.title'}",
|
||||
"tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.tooltip'}",
|
||||
"example": "@:{'phrases.compat.configuration.release_manager.control_api.example'}",
|
||||
"endpoint_label": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_label'}",
|
||||
"endpoint_message": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_message'}",
|
||||
"endpoint_aria": "@:{'phrases.compat.configuration.release_manager.control_api.endpoint_aria'}",
|
||||
"use_tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.use_tooltip'}",
|
||||
"use": "@:{'phrases.compat.configuration.release_manager.control_api.use'}",
|
||||
"reset_tooltip": "@:{'phrases.compat.configuration.release_manager.control_api.reset_tooltip'}",
|
||||
"reset": "@:{'phrases.compat.configuration.release_manager.control_api.reset'}",
|
||||
"known_endpoints": "@:{'phrases.compat.configuration.release_manager.control_api.known_endpoints'}"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.overview.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.overview.description'}"
|
||||
},
|
||||
"channels": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.channels.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.channels.description'}"
|
||||
},
|
||||
"assignments": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.assignments.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.assignments.description'}"
|
||||
},
|
||||
"deployments": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.deployments.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.deployments.description'}"
|
||||
},
|
||||
"replay": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.replay.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.replay.description'}"
|
||||
},
|
||||
"integrations": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.integrations.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.integrations.description'}"
|
||||
},
|
||||
"settings": {
|
||||
"label": "@:{'phrases.compat.configuration.release_manager.tabs.settings.label'}",
|
||||
"description": "@:{'phrases.compat.configuration.release_manager.tabs.settings.description'}"
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "@:{'phrases.compat.configuration.release_manager.setup.channels'}",
|
||||
"targets": "@:{'phrases.compat.configuration.release_manager.setup.targets'}",
|
||||
"assignments": "@:{'phrases.compat.configuration.release_manager.setup.assignments'}",
|
||||
"deployments": "@:{'phrases.compat.configuration.release_manager.setup.deployments'}",
|
||||
"replay": "@:{'phrases.compat.configuration.release_manager.setup.replay'}"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "@:{'phrases.compat.configuration.release_manager.stats.channels'}",
|
||||
"targets": "@:{'phrases.compat.configuration.release_manager.stats.targets'}",
|
||||
"deployments": "@:{'phrases.compat.configuration.release_manager.stats.deployments'}",
|
||||
"timeline_events": "@:{'phrases.compat.configuration.release_manager.stats.timeline_events'}"
|
||||
},
|
||||
"overview": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.overview.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.overview.subtitle'}",
|
||||
"guided_setup": "@:{'phrases.compat.configuration.release_manager.overview.guided_setup'}",
|
||||
"next_step": "@:{'phrases.compat.configuration.release_manager.overview.next_step'}",
|
||||
"ready": "@:{'phrases.compat.configuration.release_manager.overview.ready'}",
|
||||
"add_suggested_channel": "@:{'phrases.compat.configuration.release_manager.overview.add_suggested_channel'}",
|
||||
"module_health_empty": "@:{'phrases.compat.configuration.release_manager.overview.module_health_empty'}"
|
||||
},
|
||||
"settings": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.settings.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.settings.subtitle'}",
|
||||
"github_token": "@:{'phrases.compat.configuration.release_manager.settings.github_token'}",
|
||||
"github_api_url": "@:{'phrases.compat.configuration.release_manager.settings.github_api_url'}",
|
||||
"webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret'}",
|
||||
"github_webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.github_webhook_secret'}",
|
||||
"configured": "@:{'phrases.compat.configuration.release_manager.settings.configured'}",
|
||||
"not_configured": "@:{'phrases.compat.configuration.release_manager.settings.not_configured'}",
|
||||
"loaded_from": "@:{'phrases.compat.configuration.release_manager.settings.loaded_from'}",
|
||||
"set_below": "@:{'phrases.compat.configuration.release_manager.settings.set_below'}",
|
||||
"private_repositories_prefix": "@:{'phrases.compat.configuration.release_manager.settings.private_repositories_prefix'}",
|
||||
"private_repositories_or": "@:{'phrases.compat.configuration.release_manager.settings.private_repositories_or'}",
|
||||
"github_token_message": "@:{'phrases.compat.configuration.release_manager.settings.github_token_message'}",
|
||||
"github_token_placeholder": "@:{'phrases.compat.configuration.release_manager.settings.github_token_placeholder'}",
|
||||
"github_api_url_message": "@:{'phrases.compat.configuration.release_manager.settings.github_api_url_message'}",
|
||||
"webhook_secret_message": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret_message'}",
|
||||
"webhook_secret_placeholder": "@:{'phrases.compat.configuration.release_manager.settings.webhook_secret_placeholder'}",
|
||||
"save": "@:{'phrases.compat.configuration.release_manager.settings.save'}",
|
||||
"back_to_integrations": "@:{'phrases.compat.configuration.release_manager.settings.back_to_integrations'}",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_token'}",
|
||||
"github_api_url": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_api_url'}",
|
||||
"github_webhook_secret": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.github_webhook_secret'}",
|
||||
"review": "@:{'phrases.compat.configuration.release_manager.settings.guide.steps.review'}"
|
||||
},
|
||||
"github_token_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_token_title'}",
|
||||
"github_token_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_token_description'}",
|
||||
"configure_this_in": "@:{'phrases.compat.configuration.release_manager.settings.guide.configure_this_in'}",
|
||||
"server_environment_variable": "@:{'phrases.compat.configuration.release_manager.settings.guide.server_environment_variable'}",
|
||||
"takes_precedence": "@:{'phrases.compat.configuration.release_manager.settings.guide.takes_precedence'}",
|
||||
"recommended_access": "@:{'phrases.compat.configuration.release_manager.settings.guide.recommended_access'}",
|
||||
"recommended_access_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.recommended_access_description'}",
|
||||
"github_api_url_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_title'}",
|
||||
"github_api_url_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_description'}",
|
||||
"github_api_url_invalid": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_api_url_invalid'}",
|
||||
"github_cloud": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_cloud'}",
|
||||
"enterprise_example": "@:{'phrases.compat.configuration.release_manager.settings.guide.enterprise_example'}",
|
||||
"github_webhook_secret_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_secret_title'}",
|
||||
"github_webhook_secret_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_secret_description'}",
|
||||
"github_webhook_url": "@:{'phrases.compat.configuration.release_manager.settings.guide.github_webhook_url'}",
|
||||
"store_shared_secret_in": "@:{'phrases.compat.configuration.release_manager.settings.guide.store_shared_secret_in'}",
|
||||
"review_title": "@:{'phrases.compat.configuration.release_manager.settings.guide.review_title'}",
|
||||
"review_description": "@:{'phrases.compat.configuration.release_manager.settings.guide.review_description'}",
|
||||
"ready": "@:{'phrases.compat.configuration.release_manager.settings.guide.ready'}",
|
||||
"needs_value": "@:{'phrases.compat.configuration.release_manager.settings.guide.needs_value'}"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.kicker'}",
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.title'}",
|
||||
"summary_prefix": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.summary_prefix'}",
|
||||
"summary_suffix": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.summary_suffix'}",
|
||||
"frontend_url": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.frontend_url'}",
|
||||
"api_url": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.api_url'}",
|
||||
"checking_again": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.checking_again'}",
|
||||
"refresh_error": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.refresh_error'}",
|
||||
"ignore": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.ignore'}",
|
||||
"check_again": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.check_again'}",
|
||||
"logout": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.logout'}",
|
||||
"base_image": "@:{'phrases.compat.configuration.release_manager.channel_unavailable.base_image'}"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "@:{'phrases.compat.configuration.release_manager.channel_switched.kicker'}",
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.channel_switched.title'}",
|
||||
"summary_prefix": "@:{'phrases.compat.configuration.release_manager.channel_switched.summary_prefix'}",
|
||||
"summary_suffix": "@:{'phrases.compat.configuration.release_manager.channel_switched.summary_suffix'}",
|
||||
"frontend": "@:{'phrases.compat.configuration.release_manager.channel_switched.frontend'}",
|
||||
"api": "@:{'phrases.compat.configuration.release_manager.channel_switched.api'}",
|
||||
"frontend_version": "@:{'phrases.compat.configuration.release_manager.channel_switched.frontend_version'}",
|
||||
"api_version": "@:{'phrases.compat.configuration.release_manager.channel_switched.api_version'}",
|
||||
"assigned_channel": "@:{'phrases.compat.configuration.release_manager.channel_switched.assigned_channel'}",
|
||||
"current_app_image": "@:{'phrases.compat.configuration.release_manager.channel_switched.current_app_image'}",
|
||||
"current_api": "@:{'phrases.compat.configuration.release_manager.channel_switched.current_api'}",
|
||||
"base_image": "@:{'phrases.compat.configuration.release_manager.channel_switched.base_image'}",
|
||||
"continue": "@:{'phrases.compat.configuration.release_manager.channel_switched.continue'}"
|
||||
},
|
||||
"common": {
|
||||
"yes": "@:{'phrases.compat.configuration.release_manager.common.yes'}",
|
||||
"no": "@:{'phrases.compat.configuration.release_manager.common.no'}",
|
||||
"off": "@:{'phrases.compat.configuration.release_manager.common.off'}"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "@:{'phrases.compat.configuration.release_manager.actions.edit'}",
|
||||
"rollback": "@:{'phrases.compat.configuration.release_manager.actions.rollback'}",
|
||||
"update": "@:{'phrases.compat.configuration.release_manager.actions.update'}",
|
||||
"create": "@:{'phrases.compat.configuration.release_manager.actions.create'}",
|
||||
"clear": "@:{'phrases.compat.configuration.release_manager.actions.clear'}",
|
||||
"assign": "@:{'phrases.compat.configuration.release_manager.actions.assign'}",
|
||||
"remove": "@:{'phrases.compat.configuration.release_manager.actions.remove'}",
|
||||
"test_access": "@:{'phrases.compat.configuration.release_manager.actions.test_access'}",
|
||||
"deploy": "@:{'phrases.compat.configuration.release_manager.actions.deploy'}",
|
||||
"promote": "@:{'phrases.compat.configuration.release_manager.actions.promote'}",
|
||||
"enable": "@:{'phrases.compat.configuration.release_manager.actions.enable'}",
|
||||
"search": "@:{'phrases.compat.configuration.release_manager.actions.search'}",
|
||||
"save_target": "@:{'phrases.compat.configuration.release_manager.actions.save_target'}",
|
||||
"delete": "@:{'phrases.compat.configuration.release_manager.actions.delete'}",
|
||||
"back": "@:{'phrases.compat.configuration.release_manager.actions.back'}",
|
||||
"next": "@:{'phrases.compat.configuration.release_manager.actions.next'}"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "@:{'phrases.compat.configuration.release_manager.tables.channel'}",
|
||||
"rollout": "@:{'phrases.compat.configuration.release_manager.tables.rollout'}",
|
||||
"frontend": "@:{'phrases.compat.configuration.release_manager.tables.frontend'}",
|
||||
"api": "@:{'phrases.compat.configuration.release_manager.tables.api'}",
|
||||
"replay": "@:{'phrases.compat.configuration.release_manager.tables.replay'}",
|
||||
"actions": "@:{'phrases.compat.configuration.release_manager.tables.actions'}",
|
||||
"module": "@:{'phrases.compat.configuration.release_manager.tables.module'}",
|
||||
"status": "@:{'phrases.compat.configuration.release_manager.tables.status'}",
|
||||
"checked": "@:{'phrases.compat.configuration.release_manager.tables.checked'}",
|
||||
"subject": "@:{'phrases.compat.configuration.release_manager.tables.subject'}",
|
||||
"reason": "@:{'phrases.compat.configuration.release_manager.tables.reason'}",
|
||||
"expires": "@:{'phrases.compat.configuration.release_manager.tables.expires'}",
|
||||
"deployment": "@:{'phrases.compat.configuration.release_manager.tables.deployment'}",
|
||||
"source": "@:{'phrases.compat.configuration.release_manager.tables.source'}",
|
||||
"started": "@:{'phrases.compat.configuration.release_manager.tables.started'}",
|
||||
"target": "@:{'phrases.compat.configuration.release_manager.tables.target'}",
|
||||
"coolify": "@:{'phrases.compat.configuration.release_manager.tables.coolify'}",
|
||||
"auto": "@:{'phrases.compat.configuration.release_manager.tables.auto'}"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "@:{'phrases.compat.configuration.release_manager.values.app.frontend'}",
|
||||
"api": "@:{'phrases.compat.configuration.release_manager.values.app.api'}"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "@:{'phrases.compat.configuration.release_manager.values.capture_level.metadata'}",
|
||||
"full_redacted": "@:{'phrases.compat.configuration.release_manager.values.capture_level.full_redacted'}"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "@:{'phrases.compat.configuration.release_manager.values.subject_type.user'}",
|
||||
"subuser": "@:{'phrases.compat.configuration.release_manager.values.subject_type.subuser'}",
|
||||
"customer": "@:{'phrases.compat.configuration.release_manager.values.subject_type.customer'}",
|
||||
"channel": "@:{'phrases.compat.configuration.release_manager.values.subject_type.channel'}"
|
||||
},
|
||||
"severity": {
|
||||
"error": "@:{'phrases.compat.configuration.release_manager.values.severity.error'}",
|
||||
"warning": "@:{'phrases.compat.configuration.release_manager.values.severity.warning'}",
|
||||
"info": "@:{'phrases.compat.configuration.release_manager.values.severity.info'}"
|
||||
},
|
||||
"status": {
|
||||
"ok": "@:{'phrases.compat.configuration.release_manager.values.status.ok'}",
|
||||
"active": "@:{'phrases.compat.configuration.release_manager.values.status.active'}",
|
||||
"deployed": "@:{'phrases.compat.configuration.release_manager.values.status.deployed'}",
|
||||
"stable": "@:{'phrases.compat.configuration.release_manager.values.status.stable'}",
|
||||
"enabled": "@:{'phrases.compat.configuration.release_manager.values.status.enabled'}",
|
||||
"queued": "@:{'phrases.compat.configuration.release_manager.values.status.queued'}",
|
||||
"deploying": "@:{'phrases.compat.configuration.release_manager.values.status.deploying'}",
|
||||
"metadata": "@:{'phrases.compat.configuration.release_manager.values.status.metadata'}",
|
||||
"not_configured": "@:{'phrases.compat.configuration.release_manager.values.status.not_configured'}",
|
||||
"warning": "@:{'phrases.compat.configuration.release_manager.values.status.warning'}",
|
||||
"failed": "@:{'phrases.compat.configuration.release_manager.values.status.failed'}",
|
||||
"down": "@:{'phrases.compat.configuration.release_manager.values.status.down'}",
|
||||
"error": "@:{'phrases.compat.configuration.release_manager.values.status.error'}",
|
||||
"unknown": "@:{'phrases.compat.configuration.release_manager.values.status.unknown'}"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "@:{'phrases.compat.configuration.release_manager.autocomplete.suggested_value'}",
|
||||
"suggestion": "@:{'phrases.compat.configuration.release_manager.autocomplete.suggestion'}",
|
||||
"use_typed_value": "@:{'phrases.compat.configuration.release_manager.autocomplete.use_typed_value'}",
|
||||
"custom": "@:{'phrases.compat.configuration.release_manager.autocomplete.custom'}",
|
||||
"api_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.api_url'}",
|
||||
"frontend_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.frontend_url'}",
|
||||
"known_release_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.known_release_url'}",
|
||||
"deploy_here_with_coolify": "@:{'phrases.compat.configuration.release_manager.autocomplete.deploy_here_with_coolify'}",
|
||||
"prepare_coolify_target": "@:{'phrases.compat.configuration.release_manager.autocomplete.prepare_coolify_target'}",
|
||||
"coolify_ssl": "@:{'phrases.compat.configuration.release_manager.autocomplete.coolify_ssl'}",
|
||||
"github_repository": "@:{'phrases.compat.configuration.release_manager.autocomplete.github_repository'}",
|
||||
"owner_repository": "@:{'phrases.compat.configuration.release_manager.autocomplete.owner_repository'}",
|
||||
"git_branch": "@:{'phrases.compat.configuration.release_manager.autocomplete.git_branch'}",
|
||||
"deployment_branch": "@:{'phrases.compat.configuration.release_manager.autocomplete.deployment_branch'}",
|
||||
"github_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.github_commit'}",
|
||||
"git_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.git_commit'}",
|
||||
"use_specific_commit_sha": "@:{'phrases.compat.configuration.release_manager.autocomplete.use_specific_commit_sha'}",
|
||||
"custom_commit": "@:{'phrases.compat.configuration.release_manager.autocomplete.custom_commit'}",
|
||||
"health_url": "@:{'phrases.compat.configuration.release_manager.autocomplete.health_url'}",
|
||||
"release_health_check": "@:{'phrases.compat.configuration.release_manager.autocomplete.release_health_check'}",
|
||||
"coolify_service": "@:{'phrases.compat.configuration.release_manager.autocomplete.coolify_service'}",
|
||||
"service_uuid": "@:{'phrases.compat.configuration.release_manager.autocomplete.service_uuid'}",
|
||||
"known_coolify_service_uuid": "@:{'phrases.compat.configuration.release_manager.autocomplete.known_coolify_service_uuid'}",
|
||||
"control_api": "@:{'phrases.compat.configuration.release_manager.autocomplete.control_api'}",
|
||||
"release_manager_api_endpoint": "@:{'phrases.compat.configuration.release_manager.autocomplete.release_manager_api_endpoint'}",
|
||||
"module": "@:{'phrases.compat.configuration.release_manager.autocomplete.module'}",
|
||||
"timeline_module_key": "@:{'phrases.compat.configuration.release_manager.autocomplete.timeline_module_key'}"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "@:{'phrases.compat.configuration.release_manager.errors.github_repositories'}",
|
||||
"github_branches": "@:{'phrases.compat.configuration.release_manager.errors.github_branches'}",
|
||||
"repository_branch_required": "@:{'phrases.compat.configuration.release_manager.errors.repository_branch_required'}",
|
||||
"github_access_failed": "@:{'phrases.compat.configuration.release_manager.errors.github_access_failed'}"
|
||||
},
|
||||
"channels": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.channels.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.channels.subtitle'}",
|
||||
"rollout_percent": "@:{'phrases.compat.configuration.release_manager.channels.rollout_percent'}",
|
||||
"already_configured": "@:{'phrases.compat.configuration.release_manager.channels.already_configured'}",
|
||||
"slug": "@:{'phrases.compat.configuration.release_manager.channels.slug'}",
|
||||
"slug_message": "@:{'phrases.compat.configuration.release_manager.channels.slug_message'}",
|
||||
"slug_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.slug_placeholder'}",
|
||||
"name": "@:{'phrases.compat.configuration.release_manager.channels.name'}",
|
||||
"name_message": "@:{'phrases.compat.configuration.release_manager.channels.name_message'}",
|
||||
"name_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.name_placeholder'}",
|
||||
"rollout": "@:{'phrases.compat.configuration.release_manager.channels.rollout'}",
|
||||
"rollout_message": "@:{'phrases.compat.configuration.release_manager.channels.rollout_message'}",
|
||||
"frontend_url": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url'}",
|
||||
"frontend_url_message": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url_message'}",
|
||||
"frontend_url_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.frontend_url_placeholder'}",
|
||||
"no_matching_release_url": "@:{'phrases.compat.configuration.release_manager.channels.no_matching_release_url'}",
|
||||
"api_url": "@:{'phrases.compat.configuration.release_manager.channels.api_url'}",
|
||||
"api_url_message": "@:{'phrases.compat.configuration.release_manager.channels.api_url_message'}",
|
||||
"api_url_placeholder": "@:{'phrases.compat.configuration.release_manager.channels.api_url_placeholder'}",
|
||||
"no_matching_api_url": "@:{'phrases.compat.configuration.release_manager.channels.no_matching_api_url'}",
|
||||
"replay_capture": "@:{'phrases.compat.configuration.release_manager.channels.replay_capture'}",
|
||||
"replay_capture_message": "@:{'phrases.compat.configuration.release_manager.channels.replay_capture_message'}",
|
||||
"retention_days": "@:{'phrases.compat.configuration.release_manager.channels.retention_days'}",
|
||||
"retention_days_message": "@:{'phrases.compat.configuration.release_manager.channels.retention_days_message'}",
|
||||
"enabled": "@:{'phrases.compat.configuration.release_manager.channels.enabled'}",
|
||||
"default_channel": "@:{'phrases.compat.configuration.release_manager.channels.default_channel'}",
|
||||
"replay_enabled": "@:{'phrases.compat.configuration.release_manager.channels.replay_enabled'}",
|
||||
"frontend_chip": "@:{'phrases.compat.configuration.release_manager.channels.frontend_chip'}",
|
||||
"api_chip": "@:{'phrases.compat.configuration.release_manager.channels.api_chip'}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.assignments.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.assignments.subtitle'}",
|
||||
"subject_type": "@:{'phrases.compat.configuration.release_manager.assignments.subject_type'}",
|
||||
"subject_type_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_type_message'}",
|
||||
"customer": "@:{'phrases.compat.configuration.release_manager.assignments.customer'}",
|
||||
"customer_message": "@:{'phrases.compat.configuration.release_manager.assignments.customer_message'}",
|
||||
"customer_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.customer_placeholder'}",
|
||||
"subject_id": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id'}",
|
||||
"subject_id_message": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id_message'}",
|
||||
"subject_id_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.subject_id_placeholder'}",
|
||||
"channel": "@:{'phrases.compat.configuration.release_manager.assignments.channel'}",
|
||||
"channel_message": "@:{'phrases.compat.configuration.release_manager.assignments.channel_message'}",
|
||||
"reason": "@:{'phrases.compat.configuration.release_manager.assignments.reason'}",
|
||||
"reason_message": "@:{'phrases.compat.configuration.release_manager.assignments.reason_message'}",
|
||||
"reason_placeholder": "@:{'phrases.compat.configuration.release_manager.assignments.reason_placeholder'}",
|
||||
"expires": "@:{'phrases.compat.configuration.release_manager.assignments.expires'}",
|
||||
"expires_message": "@:{'phrases.compat.configuration.release_manager.assignments.expires_message'}"
|
||||
},
|
||||
"deployments": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.deployments.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.deployments.subtitle'}",
|
||||
"manual_deployment_record": "@:{'phrases.compat.configuration.release_manager.deployments.manual_deployment_record'}",
|
||||
"target": "@:{'phrases.compat.configuration.release_manager.deployments.target'}",
|
||||
"target_message": "@:{'phrases.compat.configuration.release_manager.deployments.target_message'}",
|
||||
"select_target_or_manual": "@:{'phrases.compat.configuration.release_manager.deployments.select_target_or_manual'}",
|
||||
"channel": "@:{'phrases.compat.configuration.release_manager.deployments.channel'}",
|
||||
"app": "@:{'phrases.compat.configuration.release_manager.deployments.app'}",
|
||||
"repository": "@:{'phrases.compat.configuration.release_manager.deployments.repository'}",
|
||||
"repository_message": "@:{'phrases.compat.configuration.release_manager.deployments.repository_message'}",
|
||||
"repository_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.repository_placeholder'}",
|
||||
"branch": "@:{'phrases.compat.configuration.release_manager.deployments.branch'}",
|
||||
"branch_message": "@:{'phrases.compat.configuration.release_manager.deployments.branch_message'}",
|
||||
"branch_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.branch_placeholder'}",
|
||||
"commit_selection": "@:{'phrases.compat.configuration.release_manager.deployments.commit_selection'}",
|
||||
"commit_selection_message": "@:{'phrases.compat.configuration.release_manager.deployments.commit_selection_message'}",
|
||||
"latest_from_branch": "@:{'phrases.compat.configuration.release_manager.deployments.latest_from_branch'}",
|
||||
"specific_commit": "@:{'phrases.compat.configuration.release_manager.deployments.specific_commit'}",
|
||||
"specific_commit_message": "@:{'phrases.compat.configuration.release_manager.deployments.specific_commit_message'}",
|
||||
"commit_sha_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.commit_sha_placeholder'}",
|
||||
"version_label": "@:{'phrases.compat.configuration.release_manager.deployments.version_label'}",
|
||||
"version_label_message": "@:{'phrases.compat.configuration.release_manager.deployments.version_label_message'}",
|
||||
"version_label_placeholder": "@:{'phrases.compat.configuration.release_manager.deployments.version_label_placeholder'}",
|
||||
"commit_prefix": "@:{'phrases.compat.configuration.release_manager.deployments.commit_prefix'}",
|
||||
"test_access_tooltip": "@:{'phrases.compat.configuration.release_manager.deployments.test_access_tooltip'}"
|
||||
},
|
||||
"replay": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.replay.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.replay.subtitle'}",
|
||||
"capture_channel": "@:{'phrases.compat.configuration.release_manager.replay.capture_channel'}",
|
||||
"target_type": "@:{'phrases.compat.configuration.release_manager.replay.target_type'}",
|
||||
"target": "@:{'phrases.compat.configuration.release_manager.replay.target'}",
|
||||
"target_message": "@:{'phrases.compat.configuration.release_manager.replay.target_message'}",
|
||||
"target_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.target_placeholder'}",
|
||||
"channel_scope": "@:{'phrases.compat.configuration.release_manager.replay.channel_scope'}",
|
||||
"channel_scope_message": "@:{'phrases.compat.configuration.release_manager.replay.channel_scope_message'}",
|
||||
"no_channel_scope": "@:{'phrases.compat.configuration.release_manager.replay.no_channel_scope'}",
|
||||
"capture_level": "@:{'phrases.compat.configuration.release_manager.replay.capture_level'}",
|
||||
"expires": "@:{'phrases.compat.configuration.release_manager.replay.expires'}",
|
||||
"expires_message": "@:{'phrases.compat.configuration.release_manager.replay.expires_message'}",
|
||||
"trace_id": "@:{'phrases.compat.configuration.release_manager.replay.trace_id'}",
|
||||
"trace_id_message": "@:{'phrases.compat.configuration.release_manager.replay.trace_id_message'}",
|
||||
"trace_id_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.trace_id_placeholder'}",
|
||||
"channel": "@:{'phrases.compat.configuration.release_manager.replay.channel'}",
|
||||
"channel_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.channel_placeholder'}",
|
||||
"principal_id": "@:{'phrases.compat.configuration.release_manager.replay.principal_id'}",
|
||||
"principal_id_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.principal_id_placeholder'}",
|
||||
"module": "@:{'phrases.compat.configuration.release_manager.replay.module'}",
|
||||
"module_placeholder": "@:{'phrases.compat.configuration.release_manager.replay.module_placeholder'}",
|
||||
"severity": "@:{'phrases.compat.configuration.release_manager.replay.severity'}",
|
||||
"any_severity": "@:{'phrases.compat.configuration.release_manager.replay.any_severity'}",
|
||||
"no_timeline_events": "@:{'phrases.compat.configuration.release_manager.replay.no_timeline_events'}"
|
||||
},
|
||||
"integrations": {
|
||||
"title": "@:{'phrases.compat.configuration.release_manager.integrations.title'}",
|
||||
"subtitle": "@:{'phrases.compat.configuration.release_manager.integrations.subtitle'}",
|
||||
"choose_repository": "@:{'phrases.compat.configuration.release_manager.integrations.choose_repository'}",
|
||||
"add_health_url": "@:{'phrases.compat.configuration.release_manager.integrations.add_health_url'}",
|
||||
"github_token_not_configured": "@:{'phrases.compat.configuration.release_manager.integrations.github_token_not_configured'}",
|
||||
"open_github_token_settings": "@:{'phrases.compat.configuration.release_manager.integrations.open_github_token_settings'}",
|
||||
"channel": "@:{'phrases.compat.configuration.release_manager.integrations.channel'}",
|
||||
"app": "@:{'phrases.compat.configuration.release_manager.integrations.app'}",
|
||||
"repository": "@:{'phrases.compat.configuration.release_manager.integrations.repository'}",
|
||||
"repository_message": "@:{'phrases.compat.configuration.release_manager.integrations.repository_message'}",
|
||||
"repository_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.repository_placeholder'}",
|
||||
"branch": "@:{'phrases.compat.configuration.release_manager.integrations.branch'}",
|
||||
"branch_message": "@:{'phrases.compat.configuration.release_manager.integrations.branch_message'}",
|
||||
"branch_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.branch_placeholder'}",
|
||||
"latest_prefix": "@:{'phrases.compat.configuration.release_manager.integrations.latest_prefix'}",
|
||||
"coolify_instance": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance'}",
|
||||
"coolify_instance_message": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance_message'}",
|
||||
"no_coolify_instance": "@:{'phrases.compat.configuration.release_manager.integrations.no_coolify_instance'}",
|
||||
"coolify_instance_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_instance_placeholder'}",
|
||||
"coolify_service_uuid": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid'}",
|
||||
"coolify_service_uuid_message": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid_message'}",
|
||||
"coolify_service_uuid_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_service_uuid_placeholder'}",
|
||||
"health_url": "@:{'phrases.compat.configuration.release_manager.integrations.health_url'}",
|
||||
"health_url_message": "@:{'phrases.compat.configuration.release_manager.integrations.health_url_message'}",
|
||||
"health_url_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.health_url_placeholder'}",
|
||||
"ssl_domain": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain'}",
|
||||
"ssl_domain_message": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain_message'}",
|
||||
"ssl_domain_placeholder": "@:{'phrases.compat.configuration.release_manager.integrations.ssl_domain_placeholder'}",
|
||||
"https_domain_for_coolify": "@:{'phrases.compat.configuration.release_manager.integrations.https_domain_for_coolify'}",
|
||||
"auto_deploy": "@:{'phrases.compat.configuration.release_manager.integrations.auto_deploy'}",
|
||||
"auto_deploy_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.auto_deploy_tooltip'}",
|
||||
"coolify_ssl": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_ssl'}",
|
||||
"coolify_ssl_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.coolify_ssl_tooltip'}",
|
||||
"deploy_now": "@:{'phrases.compat.configuration.release_manager.integrations.deploy_now'}",
|
||||
"deploy_now_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.deploy_now_tooltip'}",
|
||||
"create_missing_service": "@:{'phrases.compat.configuration.release_manager.integrations.create_missing_service'}",
|
||||
"create_missing_service_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.create_missing_service_tooltip'}",
|
||||
"test_access_tooltip": "@:{'phrases.compat.configuration.release_manager.integrations.test_access_tooltip'}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,412 @@
|
||||
"subtitle": "@.capitalize:{'terms.glossary.konfigurasjon'} @:{'terms.glossary.av'} @:{'terms.glossary.xlvask'}-@:{'terms.glossary.integrasjonen'}",
|
||||
"title": "@:{'terms.glossary.xlvask'}-@:{'terms.glossary.konfigurasjon'}",
|
||||
"username_desc": "@:{'terms.glossary.brukernavnet'} @:{'terms.glossary.brukes'} @:{'terms.glossary.til'} @:{'terms.glossary.a'} @:{'terms.glossary.autentisere'} @:{'terms.glossary.api'}-@:{'terms.glossary.tilkoblingen'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvise API- og Front-End-utgivelser",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release Manager-handlinger sendes til denne API-en. Bruk produksjons-API-en med mindre du tester en staging-backend.",
|
||||
"example": "Eksempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endepunkt",
|
||||
"endpoint_message": "Endre dette bare n?r Release Manager-endepunkter er tilgjengelige p? m?l-API-en.",
|
||||
"endpoint_aria": "Release Manager kontroll-API URL",
|
||||
"use_tooltip": "Last release-data fra denne API-en",
|
||||
"use": "Bruk",
|
||||
"reset_tooltip": "G? tilbake til standard kontroll-API",
|
||||
"reset": "Tilbakestill",
|
||||
"known_endpoints": "Kjente kontroll-API-endepunkter"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "Oversikt",
|
||||
"description": "Kanalhelse, oppsettsfremdrift og siste release-status."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Opprett stabile, canary- og m?lrettede kanaler med rollout-grenser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tildelinger",
|
||||
"description": "Knytt brukere, underbrukere eller kunder til en bestemt release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Utrullinger",
|
||||
"description": "Start utrullinger og promoter vellykkede bygg."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktiver m?lrettet innsamling og s?k i release-tidslinjehendelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrasjoner",
|
||||
"description": "Koble GitHub-repositories, branches og Coolify-tjenester."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Innstillinger",
|
||||
"description": "Konfigurer Release Managers GitHub-tilgang og webhook-innstillinger."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- og Coolify-m?l",
|
||||
"assignments": "Pilottildelinger",
|
||||
"deployments": "F?rste utrulling",
|
||||
"replay": "Replay-innsamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Utrullinger",
|
||||
"timeline_events": "Tidslinjehendelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "Oversikt",
|
||||
"subtitle": "Aktuell kanalhelse, release-aktivitet og modulstatus.",
|
||||
"guided_setup": "Veiledet oppsett",
|
||||
"next_step": "Neste: {step}",
|
||||
"ready": "Release Manager er klar for daglig drift.",
|
||||
"add_suggested_channel": "Legg til foreslått kanal",
|
||||
"module_health_empty": "Modul-health snapshots vises når probes er registrert."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-innstillinger",
|
||||
"subtitle": "Konfigurer GitHub-tokenet som brukes for private repositories og branch-oppslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemmelighet",
|
||||
"github_webhook_secret": "GitHub webhook-hemmelighet",
|
||||
"configured": "Konfigurert",
|
||||
"not_configured": "Ikke konfigurert",
|
||||
"loaded_from": "Lastet fra {variable}",
|
||||
"set_below": "Angi {variable} nedenfor",
|
||||
"private_repositories_prefix": "Private repositories leses med serverens milj?variabel",
|
||||
"private_repositories_or": "eller modulens konfigurasjonsvariabel",
|
||||
"github_token_message": "Eksempel: github_pat_... med tilgang til de private repositories Release Manager ruller ut.",
|
||||
"github_token_placeholder": "La st? tomt for ? beholde eksisterende token",
|
||||
"github_api_url_message": "Konfigurert i ReleaseManager.github_api_url. Bruk https://api.github.com med mindre GitHub Enterprise brukes.",
|
||||
"webhook_secret_message": "Valgfritt; la st? tomt for ? beholde eksisterende hemmelighet.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemmelighet",
|
||||
"save": "Lagre innstillinger",
|
||||
"back_to_integrations": "Tilbake til integrasjoner",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Legg til GitHub-token",
|
||||
"github_token_description": "Release Manager bruger denne token til at vise private repositories, branches og commits, før et deployment-mål gemmes eller testes.",
|
||||
"configure_this_in": "Konfigurer dette i",
|
||||
"server_environment_variable": "Serverens miljøvariabel",
|
||||
"takes_precedence": "har forrang når den er satt.",
|
||||
"recommended_access": "Anbefalt tilgang",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-adgang til hvert release-repository.",
|
||||
"github_api_url_title": "Legg til GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Skriv inn en full HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-eksempel",
|
||||
"github_webhook_secret_title": "Legg til GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Lagre den delte secreten i",
|
||||
"review_title": "Gå gjennom GitHub-oppsett",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Mangler værdi"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen er ikke klar",
|
||||
"summary_prefix": "Kontoen din er tildelt",
|
||||
"summary_suffix": ", men kanalen mangler konfigurasjonen som trengs for ? laste release-imaget.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Sjekker igjen om {seconds}s",
|
||||
"refresh_error": "Release-status kunne ikke oppdateres. Neste automatiske sjekk pr?ver igjen.",
|
||||
"ignore": "Ignorer de neste 5 minuttene",
|
||||
"check_again": "Sjekk igjen",
|
||||
"logout": "Logg ut",
|
||||
"base_image": "Basis-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du er n? p? {channel}",
|
||||
"summary_prefix": "Kontoen din er tildelt release-kanalen",
|
||||
"summary_suffix": "Denne enheten husker at du har sett denne meldingen.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-versjon",
|
||||
"api_version": "API-versjon",
|
||||
"assigned_channel": "Tildelt kanal",
|
||||
"current_app_image": "N?v?rende app-image",
|
||||
"current_api": "N?v?rende API",
|
||||
"base_image": "Basis-image",
|
||||
"continue": "Fortsett"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nei",
|
||||
"off": "av"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Rediger",
|
||||
"rollback": "Rull tilbake",
|
||||
"update": "Opdater",
|
||||
"create": "Opprett",
|
||||
"clear": "Tøm",
|
||||
"assign": "Tildel",
|
||||
"remove": "Fjern",
|
||||
"test_access": "Test tilgang",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promoter",
|
||||
"enable": "Aktiver",
|
||||
"search": "Søk",
|
||||
"save_target": "Lagre mål",
|
||||
"delete": "Slett",
|
||||
"back": "Tilbake",
|
||||
"next": "Neste"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Handlinger",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrollert",
|
||||
"subject": "Emne",
|
||||
"reason": "Årsag",
|
||||
"expires": "Utløper",
|
||||
"deployment": "Deployment",
|
||||
"source": "Kilde",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "fullt redigert"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "bruker",
|
||||
"subuser": "medarbeider",
|
||||
"customer": "kunde",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "feil",
|
||||
"warning": "advarsel",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployet",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktivert",
|
||||
"queued": "i kø",
|
||||
"deploying": "deploying",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "ikke konfigurert",
|
||||
"warning": "advarsel",
|
||||
"failed": "feilet",
|
||||
"down": "nede",
|
||||
"error": "feil",
|
||||
"unknown": "ukjent"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Opprett og juster gradvise release-kanaler.",
|
||||
"rollout_percent": "{percent}% udrulning",
|
||||
"already_configured": "Allerede konfigurert",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Navn",
|
||||
"name_message": "Vises til operatører når de gjennomgår en kanal.",
|
||||
"name_placeholder": "Navn",
|
||||
"rollout": "Udrulning",
|
||||
"rollout_message": "0-100 prosent av ikke-tildelt trafikk.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchende release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchende API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata er sikrere til brede kanaler.",
|
||||
"retention_days": "Oppbevaringsdager",
|
||||
"retention_days_message": "1-365 dage.",
|
||||
"enabled": "Aktivert",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktivert",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tildelinger",
|
||||
"subtitle": "Fest brukere, medarbeidere eller kunder til release-kanaler.",
|
||||
"subject_type": "Emnetype",
|
||||
"subject_type_message": "Vælg identitetstypen, der skal fastgøres.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Søk på kundenummer eller navn, og velg deretter nøyaktig kunde.",
|
||||
"customer_placeholder": "Søk kunde",
|
||||
"subject_id": "Emne-ID",
|
||||
"subject_id_message": "Eksempel: bruker-id 42",
|
||||
"subject_id_placeholder": "Emne-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Festede emner hopper over prosentutrullingen.",
|
||||
"reason": "Årsag",
|
||||
"reason_message": "Eksempel: canary-validering for kundesupport.",
|
||||
"reason_placeholder": "Årsag",
|
||||
"expires": "Utløper",
|
||||
"expires_message": "Lad feltet være tomt for ingen automatisk udløb."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Start, promoter og inspiser release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Vælg et gemt mål, eller behold manuelle felter redigerbare.",
|
||||
"select_target_or_manual": "Velg mål eller manuelt",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-valg",
|
||||
"commit_selection_message": "Seneste slås op som branchens head med den konfigurerede GitHub-token.",
|
||||
"latest_from_branch": "Siste fra branch",
|
||||
"specific_commit": "Spesifikk commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versjonsetikett",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionslabel",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktiver målrettet capture og gennemgå fejltimelines trin for trin.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Eksempel: bruker-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalavgrensning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalavgrensning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Utløper",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Alvorlighet",
|
||||
"any_severity": "Alle alvorligheder",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub og Coolify",
|
||||
"subtitle": "Knyt GitHub-repositories og branches til kanalspecifikke Coolify-services.",
|
||||
"choose_repository": "Velg repository",
|
||||
"add_health_url": "legg til health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigurert; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Åpne GitHub token-innstillinger",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domene",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domene",
|
||||
"https_domain_for_coolify": "HTTPS-domæne til Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nå",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Opprett manglende service",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +298,412 @@
|
||||
"subtitle": "@.capitalize:{'terms.glossary.konfiguration'} @:{'terms.glossary.af'} @:{'terms.glossary.xlvask'} @:{'terms.glossary.integrationen'}",
|
||||
"title": "@:{'terms.glossary.xlvask'} @:{'terms.glossary.konfiguration'}",
|
||||
"username_desc": "Användarnamnet @:{'terms.glossary.anvands'} @:{'terms.glossary.for'} @:{'terms.glossary.att'} @:{'terms.glossary.autentisera'} @:{'terms.glossary.api'}-@:{'terms.glossary.anslutningen'}."
|
||||
},
|
||||
"release_manager": {
|
||||
"title": "Release Manager",
|
||||
"subtitle": "Gradvisa API- och Front-End-versioner",
|
||||
"control_api": {
|
||||
"title": "Kontroll-API",
|
||||
"tooltip": "Release Manager-?tg?rder skickas till detta API. Anv?nd produktions-API:t om du inte testar en staging-backend.",
|
||||
"example": "Exempel: https://api.truckwash.io:4433",
|
||||
"endpoint_label": "API-endpoint",
|
||||
"endpoint_message": "?ndra detta endast n?r Release Manager-endpoints finns p? m?l-API:t.",
|
||||
"endpoint_aria": "Release Manager kontroll-API URL",
|
||||
"use_tooltip": "L?s in release-data fr?n detta API",
|
||||
"use": "Anv?nd",
|
||||
"reset_tooltip": "G? tillbaka till standard kontroll-API",
|
||||
"reset": "?terst?ll",
|
||||
"known_endpoints": "K?nda kontroll-API-endpoints"
|
||||
},
|
||||
"tabs": {
|
||||
"overview": {
|
||||
"label": "?versikt",
|
||||
"description": "Kanalh?lsa, installationsstatus och senaste release-l?ge."
|
||||
},
|
||||
"channels": {
|
||||
"label": "Kanaler",
|
||||
"description": "Skapa stabila, canary- och riktade kanaler med rollout-gr?nser."
|
||||
},
|
||||
"assignments": {
|
||||
"label": "Tilldelningar",
|
||||
"description": "Koppla anv?ndare, underanv?ndare eller kunder till en specifik release-kanal."
|
||||
},
|
||||
"deployments": {
|
||||
"label": "Utrullningar",
|
||||
"description": "Starta utrullningar och promovera lyckade byggen."
|
||||
},
|
||||
"replay": {
|
||||
"label": "Replay",
|
||||
"description": "Aktivera riktad insamling och s?k i release-tidslinjeh?ndelser."
|
||||
},
|
||||
"integrations": {
|
||||
"label": "Integrationer",
|
||||
"description": "Anslut GitHub-repositories, branches och Coolify-tj?nster."
|
||||
},
|
||||
"settings": {
|
||||
"label": "Inst?llningar",
|
||||
"description": "Konfigurera Release Managers GitHub-?tkomst och webhook-inst?llningar."
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "GitHub- och Coolify-m?l",
|
||||
"assignments": "Pilottilldelningar",
|
||||
"deployments": "F?rsta utrullningen",
|
||||
"replay": "Replay-insamling"
|
||||
},
|
||||
"stats": {
|
||||
"channels": "Kanaler",
|
||||
"targets": "M?l",
|
||||
"deployments": "Utrullningar",
|
||||
"timeline_events": "Tidslinjeh?ndelser"
|
||||
},
|
||||
"overview": {
|
||||
"title": "?versikt",
|
||||
"subtitle": "Aktuell kanalh?lsa, release-aktivitet och modulstatus.",
|
||||
"guided_setup": "Guidad installation",
|
||||
"next_step": "N?sta: {step}",
|
||||
"ready": "Release Manager ?r klar f?r daglig drift.",
|
||||
"add_suggested_channel": "Lägg till föreslagen kanal",
|
||||
"module_health_empty": "Modulhälsosnapshots visas när probes har registrerats."
|
||||
},
|
||||
"settings": {
|
||||
"title": "Release Manager-inst?llningar",
|
||||
"subtitle": "Konfigurera GitHub-token som anv?nds f?r privata repositories och branch-uppslag.",
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"webhook_secret": "Webhook-hemlighet",
|
||||
"github_webhook_secret": "GitHub webhook-hemlighet",
|
||||
"configured": "Konfigurerad",
|
||||
"not_configured": "Inte konfigurerad",
|
||||
"loaded_from": "Inl?st fr?n {variable}",
|
||||
"set_below": "Ange {variable} nedan",
|
||||
"private_repositories_prefix": "Privata repositories l?ses med serverns milj?variabel",
|
||||
"private_repositories_or": "eller modulens konfigurationsvariabel",
|
||||
"github_token_message": "Exempel: github_pat_... med ?tkomst till de privata repositories som Release Manager distribuerar.",
|
||||
"github_token_placeholder": "L?mna tomt f?r att beh?lla befintlig token",
|
||||
"github_api_url_message": "Konfigurerad i ReleaseManager.github_api_url. Anv?nd https://api.github.com om du inte anv?nder GitHub Enterprise.",
|
||||
"webhook_secret_message": "Valfritt; l?mna tomt f?r att beh?lla befintlig hemlighet.",
|
||||
"webhook_secret_placeholder": "Webhook HMAC-hemlighet",
|
||||
"save": "Spara inst?llningar",
|
||||
"back_to_integrations": "Tillbaka till integrationer",
|
||||
"guide": {
|
||||
"steps": {
|
||||
"github_token": "GitHub-token",
|
||||
"github_api_url": "GitHub API-URL",
|
||||
"github_webhook_secret": "GitHub webhook secret",
|
||||
"review": "Gennemgang"
|
||||
},
|
||||
"github_token_title": "Lägg till GitHub-token",
|
||||
"github_token_description": "Release Manager använder denna token för att lista privata repositories, branches och commits innan ett deployment-mål sparas eller testas.",
|
||||
"configure_this_in": "Konfigurera detta i",
|
||||
"server_environment_variable": "Serverns miljövariabel",
|
||||
"takes_precedence": "har företräde när den är satt.",
|
||||
"recommended_access": "Rekommenderad åtkomst",
|
||||
"recommended_access_description": "Fine-grained GitHub-token med Contents: read-åtkomst till varje release-repository.",
|
||||
"github_api_url_title": "Lägg till GitHub API-URL",
|
||||
"github_api_url_description": "Brug GitHub Cloud som standard, eller peg på REST API-base-URLen for en GitHub Enterprise Server-installation.",
|
||||
"github_api_url_invalid": "Ange en fullständig HTTP- eller HTTPS-URL.",
|
||||
"github_cloud": "GitHub Cloud",
|
||||
"enterprise_example": "Enterprise-exempel",
|
||||
"github_webhook_secret_title": "Lägg till GitHub webhook secret",
|
||||
"github_webhook_secret_description": "Brug samme secret i GitHub repository-webhooks, så Release Manager kan verificere deployment-events, før de håndteres.",
|
||||
"github_webhook_url": "GitHub webhook-URL:",
|
||||
"store_shared_secret_in": "Spara den delade secret i",
|
||||
"review_title": "Granska GitHub-konfiguration",
|
||||
"review_description": "Gem værdierne, og brug derefter Integrationer til at vælge et privat repository, branch og specifik commit eller seneste commit før adgang testes.",
|
||||
"ready": "Klar",
|
||||
"needs_value": "Saknar värde"
|
||||
}
|
||||
},
|
||||
"channel_unavailable": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Release-kanalen ?r inte klar",
|
||||
"summary_prefix": "Ditt konto ?r tilldelat",
|
||||
"summary_suffix": ", men kanalen saknar konfigurationen som beh?vs f?r att l?sa in dess release-image.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"api_url": "API-URL",
|
||||
"checking_again": "Kontrollerar igen om {seconds}s",
|
||||
"refresh_error": "Release-status kunde inte uppdateras. N?sta automatiska kontroll f?rs?ker igen.",
|
||||
"ignore": "Ignorera de kommande 5 minuterna",
|
||||
"check_again": "Kontrollera igen",
|
||||
"logout": "Logga ut",
|
||||
"base_image": "Bas-image"
|
||||
},
|
||||
"channel_switched": {
|
||||
"kicker": "Release Manager",
|
||||
"title": "Du ?r nu p? {channel}",
|
||||
"summary_prefix": "Ditt konto har tilldelats release-kanalen",
|
||||
"summary_suffix": "Den h?r enheten kommer ih?g att du har sett detta meddelande.",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"frontend_version": "Frontend-version",
|
||||
"api_version": "API-version",
|
||||
"assigned_channel": "Tilldelad kanal",
|
||||
"current_app_image": "Nuvarande app-image",
|
||||
"current_api": "Nuvarande API",
|
||||
"base_image": "Bas-image",
|
||||
"continue": "Forts?tt"
|
||||
},
|
||||
"common": {
|
||||
"yes": "ja",
|
||||
"no": "nej",
|
||||
"off": "av"
|
||||
},
|
||||
"actions": {
|
||||
"edit": "Redigera",
|
||||
"rollback": "Rulla tillbaka",
|
||||
"update": "Uppdatera",
|
||||
"create": "Skapa",
|
||||
"clear": "Rensa",
|
||||
"assign": "Tilldela",
|
||||
"remove": "Ta bort",
|
||||
"test_access": "Testa åtkomst",
|
||||
"deploy": "Deploy",
|
||||
"promote": "Promota",
|
||||
"enable": "Aktivera",
|
||||
"search": "Sök",
|
||||
"save_target": "Spara mål",
|
||||
"delete": "Radera",
|
||||
"back": "Tillbaka",
|
||||
"next": "Nästa"
|
||||
},
|
||||
"tables": {
|
||||
"channel": "Kanal",
|
||||
"rollout": "Udrulning",
|
||||
"frontend": "Frontend",
|
||||
"api": "API",
|
||||
"replay": "Replay",
|
||||
"actions": "Åtgärder",
|
||||
"module": "Modul",
|
||||
"status": "Status",
|
||||
"checked": "Kontrollerad",
|
||||
"subject": "Emne",
|
||||
"reason": "Orsak",
|
||||
"expires": "Upphör",
|
||||
"deployment": "Deployment",
|
||||
"source": "Källa",
|
||||
"started": "Startet",
|
||||
"target": "Mål",
|
||||
"coolify": "Coolify",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"values": {
|
||||
"app": {
|
||||
"frontend": "frontend",
|
||||
"api": "api"
|
||||
},
|
||||
"capture_level": {
|
||||
"metadata": "metadata",
|
||||
"full_redacted": "helt redigerad"
|
||||
},
|
||||
"subject_type": {
|
||||
"user": "användare",
|
||||
"subuser": "medarbetare",
|
||||
"customer": "kund",
|
||||
"channel": "kanal"
|
||||
},
|
||||
"severity": {
|
||||
"error": "fel",
|
||||
"warning": "varning",
|
||||
"info": "info"
|
||||
},
|
||||
"status": {
|
||||
"ok": "ok",
|
||||
"active": "aktiv",
|
||||
"deployed": "deployad",
|
||||
"stable": "stabil",
|
||||
"enabled": "aktiverad",
|
||||
"queued": "i kö",
|
||||
"deploying": "deployar",
|
||||
"metadata": "metadata",
|
||||
"not_configured": "inte konfigurerad",
|
||||
"warning": "varning",
|
||||
"failed": "misslyckad",
|
||||
"down": "nere",
|
||||
"error": "fel",
|
||||
"unknown": "okänd"
|
||||
}
|
||||
},
|
||||
"autocomplete": {
|
||||
"suggested_value": "Foreslået værdi",
|
||||
"suggestion": "Forslag",
|
||||
"use_typed_value": "Brug indtastet værdi",
|
||||
"custom": "Tilpasset",
|
||||
"api_url": "API-URL",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"known_release_url": "Kendt release-URL",
|
||||
"deploy_here_with_coolify": "Deploy {app} her med Coolify",
|
||||
"prepare_coolify_target": "Forbered et Coolify-mål med HTTPS/SSL aktiveret",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"github_repository": "GitHub-repository",
|
||||
"owner_repository": "ejer/repository",
|
||||
"git_branch": "Git-branch",
|
||||
"deployment_branch": "Deployment-branch",
|
||||
"github_commit": "GitHub-commit",
|
||||
"git_commit": "Git-commit",
|
||||
"use_specific_commit_sha": "Brug specifik commit-SHA",
|
||||
"custom_commit": "Tilpasset commit",
|
||||
"health_url": "Health-URL",
|
||||
"release_health_check": "Release health check",
|
||||
"coolify_service": "Coolify-service",
|
||||
"service_uuid": "Service-UUID",
|
||||
"known_coolify_service_uuid": "Kendt Coolify service-UUID",
|
||||
"control_api": "Kontrol-API",
|
||||
"release_manager_api_endpoint": "Release Manager API-endpoint",
|
||||
"module": "Modul",
|
||||
"timeline_module_key": "Timeline-modulnøgle"
|
||||
},
|
||||
"errors": {
|
||||
"github_repositories": "Kunne ikke indlæse GitHub-repositories.",
|
||||
"github_branches": "Kunne ikke indlæse GitHub-branches.",
|
||||
"repository_branch_required": "Repository og branch er påkrævet før GitHub-adgang kan testes.",
|
||||
"github_access_failed": "Test af GitHub repository-adgang fejlede."
|
||||
},
|
||||
"channels": {
|
||||
"title": "Kanaler",
|
||||
"subtitle": "Skapa och justera gradvisa release-kanaler.",
|
||||
"rollout_percent": "{percent}% utrullning",
|
||||
"already_configured": "Redan konfigurerad",
|
||||
"slug": "Slug",
|
||||
"slug_message": "Eksempel: canary",
|
||||
"slug_placeholder": "slug",
|
||||
"name": "Namn",
|
||||
"name_message": "Visas för operatörer när de granskar en kanal.",
|
||||
"name_placeholder": "Namn",
|
||||
"rollout": "Utrullning",
|
||||
"rollout_message": "0-100 procent av otilldelad trafik.",
|
||||
"frontend_url": "Frontend-URL",
|
||||
"frontend_url_message": "Eksempel: https://canary.example.test",
|
||||
"frontend_url_placeholder": "Frontend-URL",
|
||||
"no_matching_release_url": "Ingen matchande release-URL.",
|
||||
"api_url": "API-URL",
|
||||
"api_url_message": "Eksempel: https://api-canary.example.test",
|
||||
"api_url_placeholder": "API-URL",
|
||||
"no_matching_api_url": "Ingen matchande API-URL.",
|
||||
"replay_capture": "Replay capture",
|
||||
"replay_capture_message": "Metadata är säkrare för breda kanaler.",
|
||||
"retention_days": "Lagringsdagar",
|
||||
"retention_days_message": "1-365 dagar.",
|
||||
"enabled": "Aktiverad",
|
||||
"default_channel": "Standardkanal",
|
||||
"replay_enabled": "Replay aktiverad",
|
||||
"frontend_chip": "Frontend: {url}",
|
||||
"api_chip": "API: {url}"
|
||||
},
|
||||
"assignments": {
|
||||
"title": "Tilldelningar",
|
||||
"subtitle": "Fäst användare, medarbetare eller kunder till release-kanaler.",
|
||||
"subject_type": "Ämnestyp",
|
||||
"subject_type_message": "Välj identitetstypen som ska fästas.",
|
||||
"customer": "Kunde",
|
||||
"customer_message": "Sök på kundnummer eller namn och välj sedan exakt kund.",
|
||||
"customer_placeholder": "Sök kund",
|
||||
"subject_id": "Ämnes-ID",
|
||||
"subject_id_message": "Exempel: användar-id 42",
|
||||
"subject_id_placeholder": "Ämnes-id",
|
||||
"channel": "Kanal",
|
||||
"channel_message": "Fästa ämnen hoppar över procentutrullningen.",
|
||||
"reason": "Orsak",
|
||||
"reason_message": "Exempel: canary-validering för kundsupport.",
|
||||
"reason_placeholder": "Orsak",
|
||||
"expires": "Upphör",
|
||||
"expires_message": "Lämna tomt för ingen automatisk upphörning."
|
||||
},
|
||||
"deployments": {
|
||||
"title": "Deployments",
|
||||
"subtitle": "Starta, promota och inspektera release-deployments.",
|
||||
"manual_deployment_record": "Manuel deployment-post",
|
||||
"target": "Mål",
|
||||
"target_message": "Välj ett sparat mål eller behåll manuella fält redigerbara.",
|
||||
"select_target_or_manual": "Vælg mål eller manuel",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/front-end-vue",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: main",
|
||||
"branch_placeholder": "branch",
|
||||
"commit_selection": "Commit-val",
|
||||
"commit_selection_message": "Senaste slås upp som branchens head med konfigurerad GitHub-token.",
|
||||
"latest_from_branch": "Senaste från branch",
|
||||
"specific_commit": "Specifik commit",
|
||||
"specific_commit_message": "Eksempel: feedface1234",
|
||||
"commit_sha_placeholder": "commit sha",
|
||||
"version_label": "Versionsetikett",
|
||||
"version_label_message": "Eksempel: api-2026.05.19",
|
||||
"version_label_placeholder": "versionsetikett",
|
||||
"commit_prefix": "Commit: {commit}",
|
||||
"test_access_tooltip": "Bekræft token-adgang til det valgte repository, branch og commit."
|
||||
},
|
||||
"replay": {
|
||||
"title": "Replay",
|
||||
"subtitle": "Aktivera riktad capture och gå igenom feltimelines steg för steg.",
|
||||
"capture_channel": "Capture {channel}",
|
||||
"target_type": "Måltype",
|
||||
"target": "Mål",
|
||||
"target_message": "Exempel: användar-id 42 eller kanal-slug canary.",
|
||||
"target_placeholder": "Mål-id eller kanal-slug",
|
||||
"channel_scope": "Kanalavgränsning",
|
||||
"channel_scope_message": "Valgfrit; begrænser replay capture til en kanal.",
|
||||
"no_channel_scope": "Ingen kanalavgränsning",
|
||||
"capture_level": "Capture-niveau",
|
||||
"expires": "Upphör",
|
||||
"expires_message": "Lad feltet være tomt for manuel deaktivering.",
|
||||
"trace_id": "Trace-ID",
|
||||
"trace_id_message": "Eksempel: trace-canary-1",
|
||||
"trace_id_placeholder": "Trace-id",
|
||||
"channel": "Kanal",
|
||||
"channel_placeholder": "Kanal",
|
||||
"principal_id": "Principal-ID",
|
||||
"principal_id_placeholder": "Principal-id",
|
||||
"module": "Modul",
|
||||
"module_placeholder": "Modul",
|
||||
"severity": "Allvarlighetsgrad",
|
||||
"any_severity": "Alla allvarlighetsgrader",
|
||||
"no_timeline_events": "Ingen timeline-events matcher de aktuelle filtre."
|
||||
},
|
||||
"integrations": {
|
||||
"title": "GitHub och Coolify",
|
||||
"subtitle": "Koppla GitHub-repositories och branches till kanalspecifika Coolify-tjänster.",
|
||||
"choose_repository": "Välj repository",
|
||||
"add_health_url": "lägg till health-URL",
|
||||
"github_token_not_configured": "GitHub-token er ikke konfigureret; private repositories kan ikke vises.",
|
||||
"open_github_token_settings": "Öppna GitHub token-inställningar",
|
||||
"channel": "Kanal",
|
||||
"app": "App",
|
||||
"repository": "Repository",
|
||||
"repository_message": "Eksempel: truckwash/backend-php",
|
||||
"repository_placeholder": "ejer/repo",
|
||||
"branch": "Branch",
|
||||
"branch_message": "Eksempel: release/canary",
|
||||
"branch_placeholder": "branch",
|
||||
"latest_prefix": "Seneste: {commit}",
|
||||
"coolify_instance": "Coolify-instans",
|
||||
"coolify_instance_message": "Lad feltet være tomt for manuelle deployment-poster.",
|
||||
"no_coolify_instance": "Ingen Coolify-instans",
|
||||
"coolify_instance_placeholder": "Coolify-instans-id",
|
||||
"coolify_service_uuid": "Coolify service-UUID",
|
||||
"coolify_service_uuid_message": "Vælg en eksisterende service eller indsæt en UUID.",
|
||||
"coolify_service_uuid_placeholder": "Coolify service-UUID",
|
||||
"health_url": "Health-URL",
|
||||
"health_url_message": "Eksempel: https://api-canary.example.test/ping",
|
||||
"health_url_placeholder": "Health-URL",
|
||||
"ssl_domain": "SSL-domän",
|
||||
"ssl_domain_message": "Eksempel: canary.example.test",
|
||||
"ssl_domain_placeholder": "SSL-domän",
|
||||
"https_domain_for_coolify": "HTTPS-domän för Coolify",
|
||||
"auto_deploy": "Auto deploy",
|
||||
"auto_deploy_tooltip": "Opret automatisk deployments, når dette mål ændres.",
|
||||
"coolify_ssl": "Coolify SSL",
|
||||
"coolify_ssl_tooltip": "Bed Coolify om at oprette HTTPS for det konfigurerede domæne.",
|
||||
"deploy_now": "Deploy nu",
|
||||
"deploy_now_tooltip": "Start en deployment straks efter målet gemmes.",
|
||||
"create_missing_service": "Skapa saknad tjänst",
|
||||
"create_missing_service_tooltip": "Tillad Release Manager at oprette en manglende Coolify-service.",
|
||||
"test_access_tooltip": "Bekræft, at den konfigurerede GitHub-token kan læse dette repository og branch."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
import { computed, reactive, readonly } from "vue";
|
||||
import { releaseRuntimeState } from "@/services/releaseTimeline.js";
|
||||
|
||||
export const RELEASE_CHANNEL_IGNORE_STORAGE_KEY = "release_channel_unavailable_ignore_until";
|
||||
export const RELEASE_CHANNEL_SWITCH_NOTICE_STORAGE_KEY = "release_channel_switch_notice_seen";
|
||||
export const RELEASE_CHANNEL_IGNORE_MS = 5 * 60 * 1000;
|
||||
export const RELEASE_CHANNEL_CHECK_INTERVAL_MS = 10 * 1000;
|
||||
|
||||
const readIgnoreMap = () => {
|
||||
if (typeof window === "undefined") {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(window.localStorage.getItem(RELEASE_CHANNEL_IGNORE_STORAGE_KEY) || "{}");
|
||||
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const writeIgnoreMap = (map) => {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
window.localStorage.setItem(RELEASE_CHANNEL_IGNORE_STORAGE_KEY, JSON.stringify(map));
|
||||
} catch {
|
||||
// Ignore storage failures; the guard will simply remain active.
|
||||
}
|
||||
};
|
||||
|
||||
const readSwitchNoticeMap = () => {
|
||||
if (typeof window === "undefined") {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(window.localStorage.getItem(RELEASE_CHANNEL_SWITCH_NOTICE_STORAGE_KEY) || "{}");
|
||||
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const writeSwitchNoticeMap = (map) => {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
window.localStorage.setItem(RELEASE_CHANNEL_SWITCH_NOTICE_STORAGE_KEY, JSON.stringify(map));
|
||||
} catch {
|
||||
// Ignore storage failures; the notice can be shown again on the next load.
|
||||
}
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
now: Date.now(),
|
||||
ignoredUntilByChannel: readIgnoreMap(),
|
||||
switchNoticeSeenByKey: readSwitchNoticeMap(),
|
||||
switchNoticePrincipalKey: "",
|
||||
});
|
||||
|
||||
let clockTimer = null;
|
||||
|
||||
export const releaseChannelAvailabilityClock = readonly(state);
|
||||
|
||||
export const setReleaseChannelSwitchNoticePrincipal = (principalKey) => {
|
||||
state.switchNoticePrincipalKey = String(principalKey || "").trim();
|
||||
};
|
||||
|
||||
export const releaseChannelKey = (channel) => {
|
||||
const slug = String(channel?.slug || "").trim();
|
||||
if (slug) {
|
||||
return slug;
|
||||
}
|
||||
const id = String(channel?.id || "").trim();
|
||||
return id ? `id:${id}` : "";
|
||||
};
|
||||
|
||||
export const normalizeReleaseUrl = (value) => {
|
||||
const raw = String(value || "").trim();
|
||||
if (!raw) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(/^https?:\/\//i.test(raw) ? raw : `https://${raw}`);
|
||||
return `${url.protocol}//${url.host}`;
|
||||
} catch {
|
||||
return raw.replace(/\/+$/, "");
|
||||
}
|
||||
};
|
||||
|
||||
const runtimeUrl = (runtime, key) => {
|
||||
const camelKey = key === "frontend_base_url" ? "frontendBaseUrl" : "apiBaseUrl";
|
||||
return normalizeReleaseUrl(runtime?.[camelKey] || runtime?.[key] || runtime?.channel?.[key]);
|
||||
};
|
||||
|
||||
const hasOwn = (value, key) => Boolean(value && Object.prototype.hasOwnProperty.call(value, key));
|
||||
|
||||
const hasExplicitRuntimeTargets = (runtime) => {
|
||||
return (
|
||||
hasOwn(runtime, "frontend_base_url") ||
|
||||
hasOwn(runtime, "frontendBaseUrl") ||
|
||||
hasOwn(runtime, "api_base_url") ||
|
||||
hasOwn(runtime, "apiBaseUrl") ||
|
||||
hasOwn(runtime?.channel, "frontend_base_url") ||
|
||||
hasOwn(runtime?.channel, "api_base_url")
|
||||
);
|
||||
};
|
||||
|
||||
const runtimeAvailability = (runtime, frontendBaseUrl, apiBaseUrl) => {
|
||||
if (runtime?.availability && typeof runtime.availability === "object") {
|
||||
const missing = Array.isArray(runtime.availability.missing) ? runtime.availability.missing : [];
|
||||
const configured =
|
||||
typeof runtime.availability.configured === "boolean"
|
||||
? runtime.availability.configured
|
||||
: missing.length === 0 && runtime.availability.status !== "unconfigured";
|
||||
return {
|
||||
configured,
|
||||
missing,
|
||||
status: runtime.availability.status || (configured ? "ready" : "unconfigured"),
|
||||
};
|
||||
}
|
||||
|
||||
if (!hasExplicitRuntimeTargets(runtime)) {
|
||||
return {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
};
|
||||
}
|
||||
|
||||
const missing = [];
|
||||
if (!frontendBaseUrl) {
|
||||
missing.push("frontend_base_url");
|
||||
}
|
||||
if (!apiBaseUrl) {
|
||||
missing.push("api_base_url");
|
||||
}
|
||||
|
||||
return {
|
||||
configured: missing.length === 0,
|
||||
missing,
|
||||
status: missing.length === 0 ? "ready" : "unconfigured",
|
||||
};
|
||||
};
|
||||
|
||||
export const getReleaseChannelUnavailableStatus = (runtime = {}, now = Date.now(), ignoredUntil = 0) => {
|
||||
const channel = runtime?.channel || null;
|
||||
const channelSlug = String(channel?.slug || "").trim();
|
||||
const channelName = String(channel?.name || channelSlug || "Release channel").trim();
|
||||
const isDefaultChannel =
|
||||
channel?.default_channel === true || channel?.default_channel === 1 || channelSlug === "stable";
|
||||
const frontendBaseUrl = runtimeUrl(runtime, "frontend_base_url");
|
||||
const apiBaseUrl = runtimeUrl(runtime, "api_base_url");
|
||||
const explicitAvailability =
|
||||
runtime?.availability?.explicit === false
|
||||
? false
|
||||
: Boolean(runtime?.availability && typeof runtime.availability === "object") || hasExplicitRuntimeTargets(runtime);
|
||||
const availability = runtimeAvailability(runtime, frontendBaseUrl, apiBaseUrl);
|
||||
const ignored = Number(ignoredUntil || 0) > now;
|
||||
const unavailable = Boolean(channelSlug) && !isDefaultChannel && availability.configured === false;
|
||||
|
||||
return {
|
||||
channel,
|
||||
channelSlug,
|
||||
channelName,
|
||||
defaultChannel: isDefaultChannel,
|
||||
explicitAvailability,
|
||||
description: channel?.description || "",
|
||||
frontendBaseUrl,
|
||||
apiBaseUrl,
|
||||
missing: availability.missing,
|
||||
configured: availability.configured,
|
||||
unavailable,
|
||||
ignored,
|
||||
ignoredUntil: Number(ignoredUntil || 0),
|
||||
shouldBlock: unavailable && !ignored,
|
||||
status: availability.status,
|
||||
versions: runtime?.versions || {},
|
||||
};
|
||||
};
|
||||
|
||||
export const releaseChannelUnavailableStatus = computed(() => {
|
||||
const key = releaseChannelKey(releaseRuntimeState.channel);
|
||||
const ignoredUntil = key ? Number(state.ignoredUntilByChannel[key] || 0) : 0;
|
||||
return getReleaseChannelUnavailableStatus(releaseRuntimeState, state.now, ignoredUntil);
|
||||
});
|
||||
|
||||
export const releaseChannelSwitchNoticeKey = (channel, principalKey = state.switchNoticePrincipalKey) => {
|
||||
const channelKey = releaseChannelKey(channel);
|
||||
const principal = String(principalKey || "").trim();
|
||||
return channelKey && principal ? `${principal}:${channelKey}` : "";
|
||||
};
|
||||
|
||||
export const getReleaseChannelSwitchedStatus = (
|
||||
runtime = {},
|
||||
principalKey = state.switchNoticePrincipalKey,
|
||||
seenMap = state.switchNoticeSeenByKey
|
||||
) => {
|
||||
const unavailableStatus = getReleaseChannelUnavailableStatus(runtime, state.now, 0);
|
||||
const noticeKey = releaseChannelSwitchNoticeKey(unavailableStatus.channel, principalKey);
|
||||
const seen = noticeKey ? Boolean(seenMap[noticeKey]) : false;
|
||||
const shouldShow =
|
||||
Boolean(noticeKey) &&
|
||||
Boolean(unavailableStatus.channelSlug) &&
|
||||
unavailableStatus.defaultChannel === false &&
|
||||
unavailableStatus.explicitAvailability === true &&
|
||||
unavailableStatus.configured === true &&
|
||||
unavailableStatus.unavailable === false &&
|
||||
unavailableStatus.channelSlug !== "stable" &&
|
||||
seen === false;
|
||||
|
||||
return {
|
||||
...unavailableStatus,
|
||||
noticeKey,
|
||||
principalKey: String(principalKey || "").trim(),
|
||||
seen,
|
||||
shouldShow,
|
||||
};
|
||||
};
|
||||
|
||||
export const releaseChannelSwitchedStatus = computed(() => {
|
||||
return getReleaseChannelSwitchedStatus(
|
||||
releaseRuntimeState,
|
||||
state.switchNoticePrincipalKey,
|
||||
state.switchNoticeSeenByKey
|
||||
);
|
||||
});
|
||||
|
||||
export const acknowledgeReleaseChannelSwitch = (
|
||||
channel = releaseChannelSwitchedStatus.value.channel,
|
||||
principalKey = state.switchNoticePrincipalKey
|
||||
) => {
|
||||
const noticeKey = releaseChannelSwitchNoticeKey(channel, principalKey);
|
||||
if (!noticeKey) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const nextMap = {
|
||||
...state.switchNoticeSeenByKey,
|
||||
[noticeKey]: new Date().toISOString(),
|
||||
};
|
||||
state.switchNoticeSeenByKey = nextMap;
|
||||
writeSwitchNoticeMap(nextMap);
|
||||
return noticeKey;
|
||||
};
|
||||
|
||||
export const ignoreUnavailableReleaseChannel = (
|
||||
channel = releaseChannelUnavailableStatus.value.channel,
|
||||
durationMs = RELEASE_CHANNEL_IGNORE_MS
|
||||
) => {
|
||||
const key = releaseChannelKey(channel);
|
||||
if (!key) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ignoredUntil = Date.now() + durationMs;
|
||||
const nextMap = {
|
||||
...state.ignoredUntilByChannel,
|
||||
[key]: ignoredUntil,
|
||||
};
|
||||
state.ignoredUntilByChannel = nextMap;
|
||||
state.now = Date.now();
|
||||
writeIgnoreMap(nextMap);
|
||||
return ignoredUntil;
|
||||
};
|
||||
|
||||
export const clearUnavailableReleaseChannelIgnore = (channel = releaseChannelUnavailableStatus.value.channel) => {
|
||||
const key = releaseChannelKey(channel);
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextMap = { ...state.ignoredUntilByChannel };
|
||||
delete nextMap[key];
|
||||
state.ignoredUntilByChannel = nextMap;
|
||||
state.now = Date.now();
|
||||
writeIgnoreMap(nextMap);
|
||||
};
|
||||
|
||||
export const startReleaseChannelAvailabilityClock = () => {
|
||||
if (typeof window === "undefined" || clockTimer !== null) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
clockTimer = window.setInterval(() => {
|
||||
state.now = Date.now();
|
||||
}, 1000);
|
||||
|
||||
return stopReleaseChannelAvailabilityClock;
|
||||
};
|
||||
|
||||
export const stopReleaseChannelAvailabilityClock = () => {
|
||||
if (typeof window === "undefined" || clockTimer === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.clearInterval(clockTimer);
|
||||
clockTimer = null;
|
||||
};
|
||||
|
||||
export const buildReleaseFrontendRedirectUrl = (runtime = releaseRuntimeState, currentLocation = window.location) => {
|
||||
const frontendBaseUrl = runtimeUrl(runtime, "frontend_base_url");
|
||||
if (!frontendBaseUrl || !currentLocation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const target = new URL(frontendBaseUrl);
|
||||
const currentOrigin = currentLocation.origin || `${currentLocation.protocol}//${currentLocation.host}`;
|
||||
if (target.origin === currentOrigin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
target.pathname = currentLocation.pathname || "/";
|
||||
target.search = currentLocation.search || "";
|
||||
target.hash = currentLocation.hash || "";
|
||||
return target.toString();
|
||||
};
|
||||
|
||||
export const redirectToConfiguredReleaseFrontend = (runtime = releaseRuntimeState) => {
|
||||
const status = getReleaseChannelUnavailableStatus(runtime, Date.now(), 0);
|
||||
if (!status.configured) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const redirectUrl = buildReleaseFrontendRedirectUrl(runtime);
|
||||
if (!redirectUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
window.location.assign(redirectUrl);
|
||||
return true;
|
||||
};
|
||||
|
||||
export const __resetReleaseChannelAvailabilityForTests = () => {
|
||||
state.now = Date.now();
|
||||
state.ignoredUntilByChannel = {};
|
||||
state.switchNoticeSeenByKey = {};
|
||||
state.switchNoticePrincipalKey = "";
|
||||
stopReleaseChannelAvailabilityClock();
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.removeItem(RELEASE_CHANNEL_IGNORE_STORAGE_KEY);
|
||||
window.localStorage.removeItem(RELEASE_CHANNEL_SWITCH_NOTICE_STORAGE_KEY);
|
||||
}
|
||||
};
|
||||
@@ -43,6 +43,14 @@ const releaseRuntimeStateMutable = reactive({
|
||||
frontend: null,
|
||||
api: null,
|
||||
},
|
||||
frontendBaseUrl: null,
|
||||
apiBaseUrl: null,
|
||||
availability: {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
explicit: false,
|
||||
},
|
||||
capturePolicy: {
|
||||
enabled: false,
|
||||
capture_level: "metadata",
|
||||
@@ -59,6 +67,19 @@ let customTransport = null;
|
||||
|
||||
export const releaseRuntimeState = readonly(releaseRuntimeStateMutable);
|
||||
|
||||
const hasOwn = (value, key) => Boolean(value && Object.prototype.hasOwnProperty.call(value, key));
|
||||
|
||||
const hasExplicitRuntimeTargets = (runtime) => {
|
||||
return (
|
||||
hasOwn(runtime, "frontend_base_url") ||
|
||||
hasOwn(runtime, "frontendBaseUrl") ||
|
||||
hasOwn(runtime, "api_base_url") ||
|
||||
hasOwn(runtime, "apiBaseUrl") ||
|
||||
hasOwn(runtime?.channel, "frontend_base_url") ||
|
||||
hasOwn(runtime?.channel, "api_base_url")
|
||||
);
|
||||
};
|
||||
|
||||
export const configureReleaseRuntime = (runtime = {}) => {
|
||||
if (!runtime || typeof runtime !== "object") {
|
||||
return releaseRuntimeStateMutable;
|
||||
@@ -78,6 +99,36 @@ export const configureReleaseRuntime = (runtime = {}) => {
|
||||
frontend: runtime?.versions?.frontend || null,
|
||||
api: runtime?.versions?.api || null,
|
||||
};
|
||||
const frontendBaseUrl =
|
||||
runtime.frontend_base_url || runtime.frontendBaseUrl || runtime?.channel?.frontend_base_url || null;
|
||||
const apiBaseUrl = runtime.api_base_url || runtime.apiBaseUrl || runtime?.channel?.api_base_url || null;
|
||||
const missingRuntimeTargets = [];
|
||||
if (!frontendBaseUrl) {
|
||||
missingRuntimeTargets.push("frontend_base_url");
|
||||
}
|
||||
if (!apiBaseUrl) {
|
||||
missingRuntimeTargets.push("api_base_url");
|
||||
}
|
||||
releaseRuntimeStateMutable.frontendBaseUrl = frontendBaseUrl;
|
||||
releaseRuntimeStateMutable.apiBaseUrl = apiBaseUrl;
|
||||
releaseRuntimeStateMutable.availability = runtime.availability
|
||||
? {
|
||||
...runtime.availability,
|
||||
explicit: true,
|
||||
}
|
||||
: hasExplicitRuntimeTargets(runtime)
|
||||
? {
|
||||
configured: missingRuntimeTargets.length === 0,
|
||||
missing: missingRuntimeTargets,
|
||||
status: missingRuntimeTargets.length === 0 ? "ready" : "unconfigured",
|
||||
explicit: true,
|
||||
}
|
||||
: {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
explicit: false,
|
||||
};
|
||||
releaseRuntimeStateMutable.capturePolicy = {
|
||||
enabled: Boolean(runtime?.capture_policy?.enabled),
|
||||
capture_level: runtime?.capture_policy?.capture_level || "metadata",
|
||||
@@ -164,10 +215,11 @@ export const recordReleaseTimelineEvent = (type, payload = {}, options = {}) =>
|
||||
return true;
|
||||
};
|
||||
|
||||
export const getRecentFrontendFailureEvents = () => recentFrontendFailureEvents.map((event) => ({
|
||||
...event,
|
||||
payload: redactReleasePayload(event.payload),
|
||||
}));
|
||||
export const getRecentFrontendFailureEvents = () =>
|
||||
recentFrontendFailureEvents.map((event) => ({
|
||||
...event,
|
||||
payload: redactReleasePayload(event.payload),
|
||||
}));
|
||||
|
||||
const scheduleReleaseTimelineFlush = () => {
|
||||
if (flushTimer !== null || typeof window === "undefined") {
|
||||
@@ -329,6 +381,14 @@ export const __resetReleaseTimelineForTests = () => {
|
||||
releaseRuntimeStateMutable.traceId = "test-trace";
|
||||
releaseRuntimeStateMutable.channel = null;
|
||||
releaseRuntimeStateMutable.versions = { frontend: null, api: null };
|
||||
releaseRuntimeStateMutable.frontendBaseUrl = null;
|
||||
releaseRuntimeStateMutable.apiBaseUrl = null;
|
||||
releaseRuntimeStateMutable.availability = {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
explicit: false,
|
||||
};
|
||||
releaseRuntimeStateMutable.capturePolicy = {
|
||||
enabled: false,
|
||||
capture_level: "metadata",
|
||||
|
||||
@@ -3,6 +3,21 @@ import { authenticatedRequest } from "@/components/session/authenticatedRequest.
|
||||
export const getCoolifySummary = () =>
|
||||
authenticatedRequest("/superuser/coolify", "GET", {});
|
||||
|
||||
export const getCoolifyLoadBalancer = () =>
|
||||
authenticatedRequest("/superuser/coolify/load-balancer", "GET", {});
|
||||
|
||||
export const reconcileCoolifyLoadBalancer = (payload) =>
|
||||
authenticatedRequest("/superuser/coolify/load-balancer/reconcile", "POST", payload);
|
||||
|
||||
export const listCoolifyGateways = () =>
|
||||
authenticatedRequest("/superuser/coolify/gateways", "GET", {});
|
||||
|
||||
export const saveCoolifyGateway = (payload) =>
|
||||
authenticatedRequest("/superuser/coolify/gateways", "POST", payload);
|
||||
|
||||
export const testCoolifyGateway = (id) =>
|
||||
authenticatedRequest(`/superuser/coolify/gateways/${id}/test`, "POST", {});
|
||||
|
||||
export const createCoolifyInstance = (payload) =>
|
||||
authenticatedRequest("/superuser/coolify/instances", "POST", payload);
|
||||
|
||||
|
||||
@@ -1,49 +1,213 @@
|
||||
import { authenticatedRequest } from "@/components/session/authenticatedRequest.vue";
|
||||
import axios from "axios";
|
||||
import { API_URL, RELEASE_MANAGER_CONTROL_API_FALLBACK_URLS, RELEASE_MANAGER_CONTROL_API_URL } from "@/config.js";
|
||||
import { enqueueRequest } from "@/services/requestQueue.js";
|
||||
|
||||
export const getReleaseSummary = () =>
|
||||
authenticatedRequest("/superuser/releases", "GET", {});
|
||||
export const RELEASE_MANAGER_CONTROL_API_STORAGE_KEY = "release_manager_control_api_url";
|
||||
const RELEASE_MANAGER_LAST_WORKING_CONTROL_API_STORAGE_KEY = "release_manager_last_working_control_api_url";
|
||||
|
||||
export const listReleaseChannels = () =>
|
||||
authenticatedRequest("/superuser/releases/channels", "GET", {});
|
||||
const normalizeApiUrl = (value) => {
|
||||
const url = String(value || "")
|
||||
.trim()
|
||||
.replace(/\/+$/, "");
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
return "";
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
export const createReleaseChannel = (payload) =>
|
||||
authenticatedRequest("/superuser/releases/channels", "POST", payload);
|
||||
const browserStorage = (name) => {
|
||||
if (typeof window === "undefined") {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return window[name] || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getSelectedCustomerNumber = () => {
|
||||
const stored = browserStorage("localStorage")?.getItem("selected_customer_number");
|
||||
return stored ? parseInt(stored, 10) : null;
|
||||
};
|
||||
|
||||
const uniqueUrls = (urls) => {
|
||||
const seen = new Set();
|
||||
return urls
|
||||
.map((url) => normalizeApiUrl(url))
|
||||
.filter((url) => {
|
||||
if (!url || seen.has(url)) {
|
||||
return false;
|
||||
}
|
||||
seen.add(url);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
const releaseManagerLocalStorage = () => browserStorage("localStorage");
|
||||
const releaseManagerSessionStorage = () => browserStorage("sessionStorage");
|
||||
|
||||
export const getReleaseManagerControlApiUrl = () =>
|
||||
uniqueUrls([
|
||||
releaseManagerSessionStorage()?.getItem(RELEASE_MANAGER_LAST_WORKING_CONTROL_API_STORAGE_KEY),
|
||||
releaseManagerLocalStorage()?.getItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY),
|
||||
RELEASE_MANAGER_CONTROL_API_URL,
|
||||
API_URL,
|
||||
])[0] || API_URL;
|
||||
|
||||
export const releaseManagerControlApiCandidates = () =>
|
||||
uniqueUrls([
|
||||
releaseManagerSessionStorage()?.getItem(RELEASE_MANAGER_LAST_WORKING_CONTROL_API_STORAGE_KEY),
|
||||
releaseManagerLocalStorage()?.getItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY),
|
||||
RELEASE_MANAGER_CONTROL_API_URL,
|
||||
...RELEASE_MANAGER_CONTROL_API_FALLBACK_URLS,
|
||||
API_URL,
|
||||
]);
|
||||
|
||||
export const setReleaseManagerControlApiUrl = (url) => {
|
||||
const normalized = normalizeApiUrl(url);
|
||||
if (!normalized) {
|
||||
return "";
|
||||
}
|
||||
releaseManagerLocalStorage()?.setItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, normalized);
|
||||
releaseManagerSessionStorage()?.setItem(RELEASE_MANAGER_LAST_WORKING_CONTROL_API_STORAGE_KEY, normalized);
|
||||
return normalized;
|
||||
};
|
||||
|
||||
export const clearReleaseManagerControlApiUrl = () => {
|
||||
releaseManagerLocalStorage()?.removeItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY);
|
||||
releaseManagerSessionStorage()?.removeItem(RELEASE_MANAGER_LAST_WORKING_CONTROL_API_STORAGE_KEY);
|
||||
};
|
||||
|
||||
const rememberWorkingControlApiUrl = (url) => {
|
||||
const normalized = normalizeApiUrl(url);
|
||||
if (normalized) {
|
||||
releaseManagerSessionStorage()?.setItem(RELEASE_MANAGER_LAST_WORKING_CONTROL_API_STORAGE_KEY, normalized);
|
||||
}
|
||||
};
|
||||
|
||||
const shouldTryNextControlApi = (error, method) => {
|
||||
const status = Number(error?.response?.status || 0);
|
||||
return (
|
||||
status === 0 ||
|
||||
(method === "GET" && status === 404) ||
|
||||
status === 405 ||
|
||||
status === 501 ||
|
||||
error?.code === "ERR_NETWORK"
|
||||
);
|
||||
};
|
||||
|
||||
const buildHeaders = () => {
|
||||
const headers = {};
|
||||
const token = releaseManagerLocalStorage()?.getItem("token");
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
const isSubuser = releaseManagerLocalStorage()?.getItem("is_subuser") === "true";
|
||||
const selectedCustomerNumber = getSelectedCustomerNumber();
|
||||
if (isSubuser && selectedCustomerNumber) {
|
||||
headers["X-Customer-Number"] = selectedCustomerNumber;
|
||||
}
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
const requestReleaseManager = (url, method, data = {}) => {
|
||||
const candidates = releaseManagerControlApiCandidates();
|
||||
const headers = buildHeaders();
|
||||
|
||||
return enqueueRequest(
|
||||
async () => {
|
||||
let lastError = null;
|
||||
for (const baseUrl of candidates) {
|
||||
try {
|
||||
const response = await axios({
|
||||
method,
|
||||
url: `${baseUrl}${url}`,
|
||||
...(method === "GET" ? { params: data } : { data }),
|
||||
__skipRequestQueue: true,
|
||||
headers,
|
||||
});
|
||||
rememberWorkingControlApiUrl(baseUrl);
|
||||
return response;
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (!shouldTryNextControlApi(error, method)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw lastError;
|
||||
},
|
||||
{
|
||||
method,
|
||||
url: `${candidates[0] || API_URL}${url}`,
|
||||
requestData: {
|
||||
params: method === "GET" ? data : null,
|
||||
data: method === "GET" ? null : data,
|
||||
headers,
|
||||
},
|
||||
shouldRetry: () => false,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const getReleaseSummary = () => requestReleaseManager("/superuser/releases", "GET", {});
|
||||
|
||||
export const getReleaseConfig = () => requestReleaseManager("/superuser/releases/config", "GET", {});
|
||||
|
||||
export const updateReleaseConfig = (payload) => requestReleaseManager("/superuser/releases/config", "POST", payload);
|
||||
|
||||
export const listReleaseChannels = () => requestReleaseManager("/superuser/releases/channels", "GET", {});
|
||||
|
||||
export const createReleaseChannel = (payload) => requestReleaseManager("/superuser/releases/channels", "POST", payload);
|
||||
|
||||
export const updateReleaseChannel = (id, payload) =>
|
||||
authenticatedRequest(`/superuser/releases/channels/${id}`, "PATCH", payload);
|
||||
requestReleaseManager(`/superuser/releases/channels/${id}`, "PATCH", payload);
|
||||
|
||||
export const rollbackReleaseChannel = (id) =>
|
||||
authenticatedRequest(`/superuser/releases/channels/${id}/rollback`, "POST", {});
|
||||
requestReleaseManager(`/superuser/releases/channels/${id}/rollback`, "POST", {});
|
||||
|
||||
export const listReleaseAssignments = () =>
|
||||
authenticatedRequest("/superuser/releases/assignments", "GET", {});
|
||||
export const listReleaseAssignments = () => requestReleaseManager("/superuser/releases/assignments", "GET", {});
|
||||
|
||||
export const createReleaseAssignment = (payload) =>
|
||||
authenticatedRequest("/superuser/releases/assignments", "POST", payload);
|
||||
requestReleaseManager("/superuser/releases/assignments", "POST", payload);
|
||||
|
||||
export const deleteReleaseAssignment = (id) =>
|
||||
authenticatedRequest(`/superuser/releases/assignments/${id}`, "DELETE", {});
|
||||
requestReleaseManager(`/superuser/releases/assignments/${id}`, "DELETE", {});
|
||||
|
||||
export const listReleaseDeploymentTargets = () =>
|
||||
authenticatedRequest("/superuser/releases/targets", "GET", {});
|
||||
export const listReleaseDeploymentTargets = () => requestReleaseManager("/superuser/releases/targets", "GET", {});
|
||||
|
||||
export const saveReleaseDeploymentTarget = (payload) =>
|
||||
authenticatedRequest("/superuser/releases/targets", "POST", payload);
|
||||
requestReleaseManager("/superuser/releases/targets", "POST", payload);
|
||||
|
||||
export const deleteReleaseDeploymentTarget = (id) =>
|
||||
authenticatedRequest(`/superuser/releases/targets/${id}`, "DELETE", {});
|
||||
requestReleaseManager(`/superuser/releases/targets/${id}`, "DELETE", {});
|
||||
|
||||
export const listReleaseGithubRepositories = (params = {}) =>
|
||||
requestReleaseManager("/superuser/releases/github/repositories", "GET", params);
|
||||
|
||||
export const listReleaseGithubBranches = (repository) =>
|
||||
requestReleaseManager("/superuser/releases/github/branches", "GET", { repository });
|
||||
|
||||
export const listReleaseGithubCommits = ({ repository, branch, query = "", commit_sha = "" } = {}) =>
|
||||
requestReleaseManager("/superuser/releases/github/commits", "GET", { repository, branch, query, commit_sha });
|
||||
|
||||
export const testReleaseGithubAccess = (payload) =>
|
||||
requestReleaseManager("/superuser/releases/github/test", "POST", payload);
|
||||
|
||||
export const listReleaseDeployments = (limit = 50) =>
|
||||
authenticatedRequest("/superuser/releases/deployments", "GET", { limit });
|
||||
requestReleaseManager("/superuser/releases/deployments", "GET", { limit });
|
||||
|
||||
export const startReleaseDeployment = (payload) =>
|
||||
authenticatedRequest("/superuser/releases/deployments", "POST", payload);
|
||||
requestReleaseManager("/superuser/releases/deployments", "POST", payload);
|
||||
|
||||
export const promoteReleaseDeployment = (id) =>
|
||||
authenticatedRequest(`/superuser/releases/deployments/${id}/promote`, "POST", {});
|
||||
requestReleaseManager(`/superuser/releases/deployments/${id}/promote`, "POST", {});
|
||||
|
||||
export const setReleaseReplayTarget = (payload) =>
|
||||
authenticatedRequest("/superuser/releases/replay-targets", "POST", payload);
|
||||
requestReleaseManager("/superuser/releases/replay-targets", "POST", payload);
|
||||
|
||||
export const searchReleaseTimeline = (filters = {}) =>
|
||||
authenticatedRequest("/superuser/releases/timeline", "GET", filters);
|
||||
requestReleaseManager("/superuser/releases/timeline", "GET", filters);
|
||||
|
||||
@@ -15,9 +15,11 @@ import {
|
||||
failoverCoolifyTarget,
|
||||
getCoolifyConfig,
|
||||
getCoolifySummary,
|
||||
reconcileCoolifyLoadBalancer,
|
||||
reconcileCoolifyTarget,
|
||||
restartCoolifyTarget,
|
||||
setCoolifyConfig,
|
||||
testCoolifyGateway,
|
||||
testCoolifyInstance,
|
||||
} from "@/services/superuserCoolify.js";
|
||||
|
||||
@@ -32,9 +34,21 @@ const instanceForm = reactive({
|
||||
api_token: "",
|
||||
});
|
||||
|
||||
const loadBalancerForm = reactive({
|
||||
lb_automation_enabled: false,
|
||||
lb_automation_mode: "report_only",
|
||||
hetzner_load_balancer_id: "",
|
||||
hetzner_cloud_api_token: "",
|
||||
public_gateway_host: "api-v2.truckwash.io",
|
||||
});
|
||||
|
||||
const canManage = computed(() => SessionUser.canAccessSuperUser() || SessionUser.hasPermission("superuser_coolify_manage"));
|
||||
const instances = computed(() => Array.isArray(summary.value?.instances) ? summary.value.instances : []);
|
||||
const targets = computed(() => Array.isArray(summary.value?.targets) ? summary.value.targets : []);
|
||||
const loadBalancer = computed(() => summary.value?.load_balancer || {});
|
||||
const loadBalancerConfig = computed(() => loadBalancer.value?.config || {});
|
||||
const loadBalancerGateways = computed(() => Array.isArray(loadBalancer.value?.gateways) ? loadBalancer.value.gateways : []);
|
||||
const loadBalancerDriftActions = computed(() => Array.isArray(loadBalancer.value?.drift?.actions) ? loadBalancer.value.drift.actions : []);
|
||||
|
||||
function configValue(variable) {
|
||||
const entry = config.value.find((row) => row.variable === variable);
|
||||
@@ -51,16 +65,57 @@ async function load() {
|
||||
const rows = configResponse?.data?.data ?? [];
|
||||
config.value = Array.isArray(rows) ? rows : Object.values(rows);
|
||||
summary.value = summaryResponse?.data?.data ?? { instances: [], targets: [], availability: {} };
|
||||
hydrateLoadBalancerForm();
|
||||
} catch (error) {
|
||||
errors.value.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
function hydrateLoadBalancerForm() {
|
||||
const automationEnabled = configValue("lb_automation_enabled");
|
||||
loadBalancerForm.lb_automation_enabled = automationEnabled === true || automationEnabled === "true";
|
||||
loadBalancerForm.lb_automation_mode = configValue("lb_automation_mode") || "report_only";
|
||||
loadBalancerForm.hetzner_load_balancer_id = String(configValue("hetzner_load_balancer_id") || "");
|
||||
loadBalancerForm.public_gateway_host = String(configValue("public_gateway_host") || "api-v2.truckwash.io");
|
||||
loadBalancerForm.hetzner_cloud_api_token = "";
|
||||
}
|
||||
|
||||
async function saveEnabled(value) {
|
||||
await setCoolifyConfig({ variable: "enabled", value });
|
||||
await load();
|
||||
}
|
||||
|
||||
async function saveLoadBalancerSettings() {
|
||||
await run("lb:save", async () => {
|
||||
const payload = {
|
||||
lb_automation_enabled: Boolean(loadBalancerForm.lb_automation_enabled),
|
||||
lb_automation_mode: loadBalancerForm.lb_automation_mode,
|
||||
hetzner_load_balancer_id: String(loadBalancerForm.hetzner_load_balancer_id || "").trim(),
|
||||
public_gateway_host: String(loadBalancerForm.public_gateway_host || "").trim() || "api-v2.truckwash.io",
|
||||
};
|
||||
const token = String(loadBalancerForm.hetzner_cloud_api_token || "").trim();
|
||||
if (token !== "") {
|
||||
payload.hetzner_cloud_api_token = token;
|
||||
}
|
||||
await setCoolifyConfig(payload);
|
||||
await load();
|
||||
});
|
||||
}
|
||||
|
||||
async function runLoadBalancerReconcile(dryRun) {
|
||||
await run(dryRun ? "lb:dry-run" : "lb:enforce", async () => {
|
||||
await reconcileCoolifyLoadBalancer({ dry_run: dryRun, enforce: !dryRun });
|
||||
await load();
|
||||
});
|
||||
}
|
||||
|
||||
async function runGatewayTest(gateway) {
|
||||
await run(`gateway:${gateway.id}:test`, async () => {
|
||||
await testCoolifyGateway(gateway.id);
|
||||
await load();
|
||||
});
|
||||
}
|
||||
|
||||
async function submitInstance() {
|
||||
await run("instance:create", async () => {
|
||||
await createCoolifyInstance({ ...instanceForm });
|
||||
@@ -125,12 +180,32 @@ async function run(key, callback) {
|
||||
|
||||
function statusClass(status) {
|
||||
return {
|
||||
"is-ok": ["ok", "protected", "failover_ready", "in_sync", "provisioned"].includes(status),
|
||||
"is-degraded": ["degraded", "pending", "deploying", "restarting", "failover_blocked", "not_configured"].includes(status),
|
||||
"is-ok": ["ok", "protected", "failover_ready", "in_sync", "provisioned", "in_lb"].includes(status),
|
||||
"is-degraded": ["degraded", "pending", "deploying", "restarting", "failover_blocked", "not_configured", "missing", "disabled_present", "unknown"].includes(status),
|
||||
"is-down": ["down", "reconcile_failed", "restart_failed", "provision_blocked", "destructive_action_required"].includes(status),
|
||||
};
|
||||
}
|
||||
|
||||
function serviceLabel(service) {
|
||||
return `${service.protocol || "--"} ${service.listen_port || "--"} -> ${service.destination_port || "--"}`;
|
||||
}
|
||||
|
||||
function driftActionLabel(action) {
|
||||
if (action.type === "add_target") {
|
||||
return `Add target ${action.target_ip}`;
|
||||
}
|
||||
if (action.type === "remove_target") {
|
||||
return `Remove target ${action.target_ip}`;
|
||||
}
|
||||
if (action.type === "skip_remove_target") {
|
||||
return `Keep target ${action.target_ip}`;
|
||||
}
|
||||
if (action.type === "add_service") {
|
||||
return `Add service ${serviceLabel(action)}`;
|
||||
}
|
||||
return action.type || "planned change";
|
||||
}
|
||||
|
||||
onMounted(load);
|
||||
</script>
|
||||
|
||||
@@ -165,6 +240,144 @@ onMounted(load);
|
||||
/>
|
||||
</ConfigurationCategory>
|
||||
|
||||
<ConfigurationCategory
|
||||
class="mt-2"
|
||||
module="Coolify"
|
||||
title="Gateway load balancer"
|
||||
description="Managed public gateway for api-v2.truckwash.io and edge broker traffic."
|
||||
icon="fas fa-route"
|
||||
>
|
||||
<div class="coolify-lb" data-testid="coolify-load-balancer-card">
|
||||
<div class="coolify-lb__summary">
|
||||
<div>
|
||||
<span class="coolify-muted">Public host</span>
|
||||
<strong>{{ loadBalancerConfig.public_gateway_host || loadBalancerForm.public_gateway_host }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span class="coolify-muted">Status</span>
|
||||
<span class="tag" :class="statusClass(loadBalancer.status)">{{ loadBalancer.status || "not_configured" }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="coolify-muted">Token</span>
|
||||
<strong>{{ loadBalancerConfig.token_set ? `set via ${loadBalancerConfig.token_source || "config"}` : "not set" }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span class="coolify-muted">Targets</span>
|
||||
<strong>{{ loadBalancerGateways.length }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="coolify-form mt-3" data-testid="coolify-load-balancer-form" @submit.prevent="saveLoadBalancerSettings">
|
||||
<label>
|
||||
<span>Public gateway host</span>
|
||||
<input v-model="loadBalancerForm.public_gateway_host" class="input" type="text" required />
|
||||
</label>
|
||||
<label>
|
||||
<span>Hetzner LB ID</span>
|
||||
<input v-model="loadBalancerForm.hetzner_load_balancer_id" class="input" type="text" inputmode="numeric" />
|
||||
</label>
|
||||
<label>
|
||||
<span>Mode</span>
|
||||
<span class="select is-fullwidth">
|
||||
<select v-model="loadBalancerForm.lb_automation_mode">
|
||||
<option value="report_only">Report only</option>
|
||||
<option value="enforce">Enforce</option>
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
<label>
|
||||
<span>Hetzner API token</span>
|
||||
<input
|
||||
v-model="loadBalancerForm.hetzner_cloud_api_token"
|
||||
class="input"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
placeholder="Leave blank to keep existing token"
|
||||
/>
|
||||
</label>
|
||||
<label class="coolify-checkbox">
|
||||
<input v-model="loadBalancerForm.lb_automation_enabled" type="checkbox" />
|
||||
<span>Enable cron reconciliation</span>
|
||||
</label>
|
||||
<div class="coolify-form__actions">
|
||||
<button class="button is-dark" type="submit" :class="{ 'is-loading': busy === 'lb:save' }">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="buttons mt-3">
|
||||
<button
|
||||
class="button"
|
||||
type="button"
|
||||
:class="{ 'is-loading': busy === 'lb:dry-run' }"
|
||||
@click="runLoadBalancerReconcile(true)"
|
||||
>
|
||||
Dry-run reconcile
|
||||
</button>
|
||||
<button
|
||||
class="button is-warning"
|
||||
type="button"
|
||||
:class="{ 'is-loading': busy === 'lb:enforce' }"
|
||||
@click="runLoadBalancerReconcile(false)"
|
||||
>
|
||||
Enforce reconcile
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container mt-3">
|
||||
<table class="table is-fullwidth is-hoverable" data-testid="coolify-gateways-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gateway</th>
|
||||
<th>IP</th>
|
||||
<th>Enabled</th>
|
||||
<th>Health</th>
|
||||
<th>LB state</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="gateway in loadBalancerGateways" :key="gateway.id">
|
||||
<td>
|
||||
<strong>{{ gateway.hostname }}</strong>
|
||||
<div class="coolify-muted">Priority {{ gateway.priority }}</div>
|
||||
</td>
|
||||
<td>{{ gateway.target_ip }}</td>
|
||||
<td>{{ gateway.enabled ? "yes" : "no" }}</td>
|
||||
<td><span class="tag" :class="statusClass(gateway.health_state)">{{ gateway.health_state }}</span></td>
|
||||
<td><span class="tag" :class="statusClass(gateway.lb_state)">{{ gateway.lb_state }}</span></td>
|
||||
<td>
|
||||
<button
|
||||
class="button is-small"
|
||||
type="button"
|
||||
:class="{ 'is-loading': busy === `gateway:${gateway.id}:test` }"
|
||||
@click="runGatewayTest(gateway)"
|
||||
>
|
||||
Test
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="loadBalancerGateways.length === 0">
|
||||
<td colspan="6">No gateway targets configured.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="coolify-drift mt-3" data-testid="coolify-load-balancer-drift">
|
||||
<strong>Drift</strong>
|
||||
<span v-if="loadBalancerDriftActions.length === 0" class="coolify-muted">No planned changes.</span>
|
||||
<ul v-else>
|
||||
<li v-for="(action, index) in loadBalancerDriftActions" :key="`${action.type}-${index}`">
|
||||
{{ driftActionLabel(action) }}
|
||||
<span v-if="action.reason" class="coolify-muted">({{ action.reason }})</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigurationCategory>
|
||||
|
||||
<ConfigurationCategory
|
||||
class="mt-2"
|
||||
module="Coolify"
|
||||
@@ -306,6 +519,34 @@ onMounted(load);
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.coolify-checkbox {
|
||||
align-items: center;
|
||||
display: flex !important;
|
||||
gap: 0.5rem !important;
|
||||
padding-top: 1.65rem;
|
||||
}
|
||||
|
||||
.coolify-lb__summary {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
}
|
||||
|
||||
.coolify-lb__summary > div {
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.coolify-drift {
|
||||
display: grid;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.coolify-drift ul {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.coolify-muted {
|
||||
color: #64748b;
|
||||
font-size: 0.86rem;
|
||||
|
||||
+2594
-233
File diff suppressed because it is too large
Load Diff
@@ -81,6 +81,77 @@ function createCoolifyState() {
|
||||
|
||||
return {
|
||||
configEnabled: true,
|
||||
loadBalancerConfig: {
|
||||
lb_automation_enabled: false,
|
||||
lb_automation_mode: "report_only",
|
||||
hetzner_load_balancer_id: "123456",
|
||||
hetzner_cloud_api_token: "[redacted]",
|
||||
public_gateway_host: "api-v2.truckwash.io",
|
||||
token_set: true,
|
||||
},
|
||||
loadBalancer: {
|
||||
configured: true,
|
||||
status: "degraded",
|
||||
config: {
|
||||
automation_enabled: false,
|
||||
automation_mode: "report_only",
|
||||
load_balancer_id: "123456",
|
||||
public_gateway_host: "api-v2.truckwash.io",
|
||||
token_set: true,
|
||||
token_source: "config",
|
||||
required_services: [
|
||||
{ protocol: "http", listen_port: 80, destination_port: 80 },
|
||||
{ protocol: "tcp", listen_port: 443, destination_port: 443 },
|
||||
],
|
||||
},
|
||||
gateways: [
|
||||
{
|
||||
id: 1,
|
||||
hostname: "node1.truckwash.io",
|
||||
target_ip: "94.130.142.41",
|
||||
enabled: true,
|
||||
priority: 10,
|
||||
health_state: "ok",
|
||||
lb_state: "in_lb",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
hostname: "node2.truckwash.io",
|
||||
target_ip: "65.21.214.30",
|
||||
enabled: true,
|
||||
priority: 20,
|
||||
health_state: "ok",
|
||||
lb_state: "missing",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
hostname: "node3.truckwash.io",
|
||||
target_ip: "23.88.23.183",
|
||||
enabled: true,
|
||||
priority: 30,
|
||||
health_state: "ok",
|
||||
lb_state: "in_lb",
|
||||
},
|
||||
],
|
||||
load_balancer: {
|
||||
id: 123456,
|
||||
name: "truckwash-gateway",
|
||||
ipv4: "203.0.113.10",
|
||||
ipv6: null,
|
||||
location: "fsn1",
|
||||
algorithm: "round_robin",
|
||||
targets: ["94.130.142.41", "23.88.23.183"],
|
||||
services: [{ protocol: "http", listen_port: 80, destination_port: 80, proxyprotocol: false }],
|
||||
},
|
||||
drift: {
|
||||
has_drift: true,
|
||||
actions: [
|
||||
{ type: "add_target", target_ip: "65.21.214.30", hostname: "node2.truckwash.io" },
|
||||
{ type: "add_service", protocol: "tcp", listen_port: 443, destination_port: 443 },
|
||||
],
|
||||
},
|
||||
last_error: null,
|
||||
},
|
||||
instances: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -126,6 +197,7 @@ function createCoolifyState() {
|
||||
renamedHosts: [],
|
||||
removedHosts: [],
|
||||
reconciledTargets: [],
|
||||
lbReconciles: [],
|
||||
placement: {
|
||||
generated_at: "2026-05-18T08:00:00.000Z",
|
||||
servers: [
|
||||
@@ -172,30 +244,141 @@ function createCoolifyState() {
|
||||
}
|
||||
|
||||
async function installCoolifyMocks(page, state) {
|
||||
const coolifyConfigRows = () => [
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "enabled",
|
||||
type: "bool",
|
||||
value: state.configEnabled,
|
||||
},
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "lb_automation_enabled",
|
||||
type: "bool",
|
||||
value: state.loadBalancerConfig.lb_automation_enabled,
|
||||
},
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "lb_automation_mode",
|
||||
type: "string",
|
||||
value: state.loadBalancerConfig.lb_automation_mode,
|
||||
},
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "hetzner_load_balancer_id",
|
||||
type: "string",
|
||||
value: state.loadBalancerConfig.hetzner_load_balancer_id,
|
||||
},
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "hetzner_cloud_api_token",
|
||||
type: "string",
|
||||
value: state.loadBalancerConfig.hetzner_cloud_api_token,
|
||||
secret_set: state.loadBalancerConfig.token_set,
|
||||
},
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "public_gateway_host",
|
||||
type: "string",
|
||||
value: state.loadBalancerConfig.public_gateway_host,
|
||||
},
|
||||
];
|
||||
|
||||
await page.route(/\/coolify\/config(?:\?.*)?$/, async (route) => {
|
||||
if (route.request().method() === "POST") {
|
||||
const payload = route.request().postDataJSON?.() || {};
|
||||
if (payload.variable === "enabled") {
|
||||
state.configEnabled = Boolean(payload.value);
|
||||
const updates = Object.prototype.hasOwnProperty.call(payload, "variable")
|
||||
? { [payload.variable]: payload.value }
|
||||
: payload;
|
||||
|
||||
for (const [variable, value] of Object.entries(updates)) {
|
||||
if (variable === "enabled") {
|
||||
state.configEnabled = Boolean(value);
|
||||
} else if (variable === "hetzner_cloud_api_token") {
|
||||
state.loadBalancerConfig.hetzner_cloud_api_token = "[redacted]";
|
||||
state.loadBalancerConfig.token_set = String(value || "").trim() !== "";
|
||||
} else if (Object.prototype.hasOwnProperty.call(state.loadBalancerConfig, variable)) {
|
||||
state.loadBalancerConfig[variable] = value;
|
||||
}
|
||||
}
|
||||
state.loadBalancer.config = {
|
||||
...state.loadBalancer.config,
|
||||
automation_enabled: Boolean(state.loadBalancerConfig.lb_automation_enabled),
|
||||
automation_mode: state.loadBalancerConfig.lb_automation_mode,
|
||||
load_balancer_id: state.loadBalancerConfig.hetzner_load_balancer_id,
|
||||
public_gateway_host: state.loadBalancerConfig.public_gateway_host,
|
||||
token_set: state.loadBalancerConfig.token_set,
|
||||
};
|
||||
await route.fulfill(json({ data: { updated: true } }));
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: [
|
||||
{
|
||||
module: "Coolify",
|
||||
variable: "enabled",
|
||||
type: "bool",
|
||||
value: state.configEnabled,
|
||||
},
|
||||
],
|
||||
data: coolifyConfigRows(),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
await page.route(/\/superuser\/coolify\/load-balancer$/, async (route) => {
|
||||
await route.fulfill(json({ data: state.loadBalancer }));
|
||||
});
|
||||
|
||||
await page.route(/\/superuser\/coolify\/load-balancer\/reconcile$/, async (route) => {
|
||||
const payload = route.request().postDataJSON?.() || {};
|
||||
const dryRun = payload.dry_run !== false;
|
||||
state.lbReconciles.push({ dry_run: dryRun });
|
||||
const planned = [...(state.loadBalancer.drift.actions || [])];
|
||||
|
||||
if (!dryRun) {
|
||||
state.loadBalancer.status = "ok";
|
||||
state.loadBalancer.drift = { has_drift: false, actions: [] };
|
||||
state.loadBalancer.load_balancer.targets = ["94.130.142.41", "65.21.214.30", "23.88.23.183"];
|
||||
state.loadBalancer.load_balancer.services = [
|
||||
{ protocol: "http", listen_port: 80, destination_port: 80, proxyprotocol: false },
|
||||
{ protocol: "tcp", listen_port: 443, destination_port: 443, proxyprotocol: false },
|
||||
];
|
||||
state.loadBalancer.gateways = state.loadBalancer.gateways.map((gateway) => ({
|
||||
...gateway,
|
||||
lb_state: gateway.enabled ? "in_lb" : "disabled_absent",
|
||||
}));
|
||||
}
|
||||
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
ok: true,
|
||||
dry_run: dryRun,
|
||||
mutated: !dryRun,
|
||||
planned,
|
||||
applied: dryRun ? [] : planned,
|
||||
skipped: [],
|
||||
errors: [],
|
||||
...state.loadBalancer,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
await page.route(/\/superuser\/coolify\/gateways$/, async (route) => {
|
||||
await route.fulfill(json({ data: state.loadBalancer.gateways }));
|
||||
});
|
||||
|
||||
await page.route(/\/superuser\/coolify\/gateways\/\d+\/test$/, async (route) => {
|
||||
const id = Number(
|
||||
route
|
||||
.request()
|
||||
.url()
|
||||
.match(/\/gateways\/(\d+)\/test$/)?.[1] || 0
|
||||
);
|
||||
const gateway = state.loadBalancer.gateways.find((entry) => entry.id === id);
|
||||
if (gateway) {
|
||||
gateway.health_state = "ok";
|
||||
gateway.last_probe = { ok: true, status_code: 200, path: "/ping" };
|
||||
}
|
||||
await route.fulfill(json({ data: { gateway, test: { ok: true, status_code: 200 } } }));
|
||||
});
|
||||
|
||||
await page.route(/\/superuser\/coolify\/instances\/\d+\/test$/, async (route) => {
|
||||
const instance = state.instances[0];
|
||||
instance.status = "ok";
|
||||
@@ -445,6 +628,7 @@ async function installCoolifyMocks(page, state) {
|
||||
redis: { status: "not_configured", targets: 0, protected: 0, blocked: 0 },
|
||||
minio: { status: "not_configured", targets: 0, protected: 0, blocked: 0 },
|
||||
},
|
||||
load_balancer: state.loadBalancer,
|
||||
},
|
||||
})
|
||||
);
|
||||
@@ -463,8 +647,18 @@ test.describe("Coolify infrastructure management", () => {
|
||||
|
||||
await expect(page.getByRole("heading", { name: "Coolify configuration" })).toBeVisible({ timeout: 30_000 });
|
||||
await page.locator(".card-header").filter({ hasText: "API connection" }).click();
|
||||
await page.locator(".card-header").filter({ hasText: "Gateway load balancer" }).click();
|
||||
await page.locator(".card-header").filter({ hasText: "Instances" }).click();
|
||||
await page.locator(".card-header").filter({ hasText: "Managed targets" }).click();
|
||||
await expect(page.getByTestId("coolify-load-balancer-card")).toContainText("api-v2.truckwash.io");
|
||||
await expect(page.getByTestId("coolify-gateways-table")).toContainText("65.21.214.30");
|
||||
await expect(page.getByTestId("coolify-load-balancer-drift")).toContainText("Add target 65.21.214.30");
|
||||
await page.getByRole("button", { name: "Dry-run reconcile" }).click();
|
||||
expect(state.lbReconciles.at(-1)).toEqual({ dry_run: true });
|
||||
await page.getByRole("button", { name: "Enforce reconcile" }).click();
|
||||
expect(state.lbReconciles.at(-1)).toEqual({ dry_run: false });
|
||||
await expect(page.getByTestId("coolify-load-balancer-card")).toContainText("ok");
|
||||
await expect(page.getByTestId("coolify-load-balancer-drift")).toContainText("No planned changes.");
|
||||
await expect(page.getByTestId("coolify-instances-table")).toContainText("Production Coolify");
|
||||
await expect(page.getByTestId("coolify-targets-table")).toContainText("failover_ready");
|
||||
|
||||
@@ -481,7 +675,7 @@ test.describe("Coolify infrastructure management", () => {
|
||||
await expect(page.getByTestId("coolify-instances-table")).toContainText("ok");
|
||||
|
||||
await page.getByTestId("coolify-targets-table").getByRole("button", { name: "Reconcile" }).first().click();
|
||||
expect(state.reconciledTargets).toContain(7);
|
||||
await expect.poll(() => state.reconciledTargets).toContain(7);
|
||||
await expect(page.getByTestId("coolify-targets-table")).toContainText("failover_ready");
|
||||
|
||||
page.once("dialog", async (dialog) => {
|
||||
|
||||
@@ -20,7 +20,12 @@ async function mockHtml2Canvas(page) {
|
||||
await route.fulfill({
|
||||
contentType: "text/javascript",
|
||||
body: `
|
||||
let html2canvasAttempts = 0;
|
||||
export default async function html2canvas() {
|
||||
html2canvasAttempts += 1;
|
||||
if (html2canvasAttempts === 1) {
|
||||
throw new Error("Simulated first capture failure");
|
||||
}
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 8;
|
||||
canvas.height = 8;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
||||
|
||||
const baseUrl = process.env.PLAYWRIGHT_BASE_URL || "http://127.0.0.1:5173";
|
||||
|
||||
const availableRuntime = (frontendBaseUrl) => ({
|
||||
generated_at: "2026-05-19T09:30:00.000Z",
|
||||
trace_id: "trace-switched-channel",
|
||||
channel: {
|
||||
id: 2,
|
||||
slug: "canary",
|
||||
name: "Canary",
|
||||
description: "Early production validation before broader rollout.",
|
||||
enabled: true,
|
||||
default_channel: false,
|
||||
frontend_base_url: frontendBaseUrl,
|
||||
api_base_url: "https://api-canary.example.test",
|
||||
},
|
||||
versions: {
|
||||
frontend: { version_label: "frontend-canary", commit_sha: "c0ffee" },
|
||||
api: { version_label: "api-canary", commit_sha: "feedface" },
|
||||
},
|
||||
frontend_base_url: frontendBaseUrl,
|
||||
api_base_url: "https://api-canary.example.test",
|
||||
availability: {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
},
|
||||
capture_policy: {
|
||||
enabled: false,
|
||||
capture_level: "metadata",
|
||||
all_failure_metadata: true,
|
||||
retention_days: 14,
|
||||
},
|
||||
});
|
||||
|
||||
async function boot(page) {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("locale", "en");
|
||||
window.localStorage.removeItem("release_channel_unavailable_ignore_until");
|
||||
window.localStorage.removeItem("release_channel_switch_notice_seen");
|
||||
});
|
||||
|
||||
await mockApi(page, {
|
||||
authenticated: true,
|
||||
permissions: ["user"],
|
||||
sessionData: {
|
||||
id: 77,
|
||||
customer_number: 990077,
|
||||
runtime_config: {
|
||||
release: availableRuntime(baseUrl),
|
||||
},
|
||||
},
|
||||
});
|
||||
await seedAuthenticatedState(page, "release-channel-switched-token");
|
||||
}
|
||||
|
||||
test("users assigned to a configured release channel see the switched notice once on the device", async ({ page }) => {
|
||||
await boot(page);
|
||||
|
||||
await page.goto("/user");
|
||||
const notice = page.getByTestId("release-channel-switched-page");
|
||||
await expect(notice).toBeVisible();
|
||||
await expect(notice).toContainText("You are now on Canary");
|
||||
await expect(notice).toContainText("Early production validation");
|
||||
await expect(page.getByTestId("release-channel-switched-details")).toContainText("frontend-canary");
|
||||
await expect(page.getByTestId("release-channel-switched-details")).toContainText("api-canary");
|
||||
|
||||
await page.getByTestId("release-channel-switched-continue").click();
|
||||
await expect(notice).toHaveCount(0);
|
||||
|
||||
await page.goto("/user");
|
||||
await expect(page.getByTestId("release-channel-switched-page")).toHaveCount(0);
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
||||
|
||||
const json = (body, status = 200) => ({
|
||||
status,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
const unavailableRuntime = {
|
||||
generated_at: "2026-05-19T09:00:00.000Z",
|
||||
trace_id: "trace-unavailable-channel",
|
||||
channel: {
|
||||
id: 2,
|
||||
slug: "canary",
|
||||
name: "Canary",
|
||||
description: "Early production validation before broader rollout.",
|
||||
enabled: true,
|
||||
default_channel: false,
|
||||
frontend_base_url: null,
|
||||
api_base_url: null,
|
||||
},
|
||||
versions: { frontend: null, api: null },
|
||||
frontend_base_url: null,
|
||||
api_base_url: null,
|
||||
availability: {
|
||||
configured: false,
|
||||
missing: ["frontend_base_url", "api_base_url"],
|
||||
status: "unconfigured",
|
||||
},
|
||||
capture_policy: {
|
||||
enabled: false,
|
||||
capture_level: "metadata",
|
||||
all_failure_metadata: true,
|
||||
retention_days: 14,
|
||||
},
|
||||
};
|
||||
|
||||
const availableRuntime = (frontendBaseUrl) => ({
|
||||
...unavailableRuntime,
|
||||
channel: {
|
||||
...unavailableRuntime.channel,
|
||||
frontend_base_url: frontendBaseUrl,
|
||||
api_base_url: "https://api-canary.example.test",
|
||||
},
|
||||
frontend_base_url: frontendBaseUrl,
|
||||
api_base_url: "https://api-canary.example.test",
|
||||
availability: {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
},
|
||||
});
|
||||
|
||||
async function boot(page, runtime = unavailableRuntime) {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("locale", "en");
|
||||
window.localStorage.removeItem("release_channel_unavailable_ignore_until");
|
||||
});
|
||||
|
||||
await mockApi(page, {
|
||||
authenticated: true,
|
||||
permissions: ["user"],
|
||||
sessionData: {
|
||||
runtime_config: {
|
||||
release: runtime,
|
||||
},
|
||||
},
|
||||
});
|
||||
await seedAuthenticatedState(page, "release-channel-token");
|
||||
}
|
||||
|
||||
test("users assigned to an unconfigured release channel can ignore the guard temporarily", async ({ page }) => {
|
||||
await boot(page);
|
||||
await page.route("**/release/runtime", async (route) => {
|
||||
await route.fulfill(json({ data: unavailableRuntime }));
|
||||
});
|
||||
|
||||
await page.goto("/user");
|
||||
const guard = page.getByTestId("release-channel-unavailable-page");
|
||||
await expect(guard).toBeVisible();
|
||||
await expect(guard).toContainText("Release channel is not ready");
|
||||
await expect(guard).toContainText("Canary");
|
||||
await expect(page.getByTestId("release-channel-missing")).toContainText("Frontend URL");
|
||||
await expect(page.getByTestId("release-channel-missing")).toContainText("API URL");
|
||||
await expect(page.getByTestId("release-channel-next-check")).toContainText("Checking again in");
|
||||
|
||||
await page.getByTestId("release-channel-ignore").click();
|
||||
await expect(guard).toHaveCount(0);
|
||||
await page.evaluate(() => window.localStorage.removeItem("release_channel_unavailable_ignore_until"));
|
||||
});
|
||||
|
||||
test("users assigned to an unconfigured release channel can check again when it becomes ready", async ({ page }) => {
|
||||
await boot(page);
|
||||
let releaseRuntime = unavailableRuntime;
|
||||
|
||||
await page.route("**/release/runtime", async (route) => {
|
||||
await route.fulfill(json({ data: releaseRuntime }));
|
||||
});
|
||||
|
||||
await page.goto("/user");
|
||||
const guard = page.getByTestId("release-channel-unavailable-page");
|
||||
await expect(guard).toBeVisible();
|
||||
|
||||
releaseRuntime = availableRuntime(new URL(page.url()).origin);
|
||||
await page.getByTestId("release-channel-check-again").click();
|
||||
await expect(guard).toHaveCount(0);
|
||||
});
|
||||
@@ -142,6 +142,49 @@ function summary(state) {
|
||||
sessions: 1,
|
||||
events: state.timelineEvents.length,
|
||||
},
|
||||
module_keys: ["requestqueue", "frontend", "auth", "coolify"],
|
||||
suggestions: {
|
||||
repositories: ["truckwash/front-end-vue", "truckwash/backend-php"],
|
||||
branches: ["main", "release/canary", "release/beta"],
|
||||
frontend_base_urls: ["https://canary.example.test", "https://app.example.test"],
|
||||
api_base_urls: ["https://api-canary.example.test", "https://api.example.test"],
|
||||
health_urls: ["https://canary.example.test/health", "https://api-canary.example.test/ping"],
|
||||
coolify_instances: [{ id: 3, label: "Production Coolify", status: "ok" }],
|
||||
coolify_services: [
|
||||
{
|
||||
instance_id: 3,
|
||||
instance_label: "Production Coolify",
|
||||
uuid: "api-canary-service",
|
||||
name: "API Canary",
|
||||
status: "running",
|
||||
urls: ["https://api-canary.example.test"],
|
||||
},
|
||||
],
|
||||
coolify_service_uuids: ["service-canary", "api-canary-service"],
|
||||
channel_presets: [
|
||||
{ slug: "stable", name: "Stable", rollout_percent: 100, description: "Default production channel" },
|
||||
{ slug: "canary", name: "Canary", rollout_percent: 5, description: "Early release channel" },
|
||||
{ slug: "internal", name: "Internal", rollout_percent: 0, description: "Staff-only channel" },
|
||||
],
|
||||
target_presets: [
|
||||
{
|
||||
label: "Frontend target",
|
||||
app: "frontend",
|
||||
repository: "truckwash/front-end-vue",
|
||||
branch: "main",
|
||||
health_url: "https://canary.example.test/health",
|
||||
auto_deploy: true,
|
||||
},
|
||||
{
|
||||
label: "API target",
|
||||
app: "api",
|
||||
repository: "truckwash/backend-php",
|
||||
branch: "release/canary",
|
||||
health_url: "https://api-canary.example.test/ping",
|
||||
auto_deploy: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -150,6 +193,9 @@ function channelSlug(state, id) {
|
||||
}
|
||||
|
||||
async function boot(page, state = createReleaseState()) {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("locale", "en");
|
||||
});
|
||||
await mockApi(page, {
|
||||
authenticated: true,
|
||||
permissions,
|
||||
@@ -170,6 +216,24 @@ async function boot(page, state = createReleaseState()) {
|
||||
}
|
||||
|
||||
async function installReleaseMocks(page, state) {
|
||||
await page.route(/\/customers\?search=.*/i, async (route) => {
|
||||
const url = new URL(route.request().url());
|
||||
const query = (url.searchParams.get("search") || "").toLowerCase();
|
||||
const customers = [
|
||||
{ id: 1, customerNumber: 424242, name: "Acme Transport" },
|
||||
{ id: 2, customerNumber: 123456, name: "Nordic Logistics" },
|
||||
].filter((customer) => {
|
||||
return customer.name.toLowerCase().includes(query) || String(customer.customerNumber).includes(query);
|
||||
});
|
||||
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: customers,
|
||||
meta: { pagination: { total: customers.length, count: customers.length, per_page: 10, current_page: 1 } },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
await page.route(/\/superuser\/releases(?:\/.*)?(?:\?.*)?$/i, async (route) => {
|
||||
const request = route.request();
|
||||
const url = new URL(request.url());
|
||||
@@ -181,6 +245,141 @@ async function installReleaseMocks(page, state) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/config") && method === "GET") {
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
github_token_configured: true,
|
||||
github_token_env_configured: false,
|
||||
github_token_module_configured: true,
|
||||
github_token_variable: "ReleaseManager.github_token",
|
||||
github_token_env_variable: "RELEASE_MANAGER_GITHUB_TOKEN",
|
||||
github_api_url: "https://api.github.com",
|
||||
github_api_url_variable: "ReleaseManager.github_api_url",
|
||||
github_webhook_secret_configured: true,
|
||||
github_webhook_secret_variable: "ReleaseManager.github_webhook_secret",
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/config") && method === "POST") {
|
||||
const payload = request.postDataJSON?.() || {};
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
github_token_configured: true,
|
||||
github_token_env_configured: false,
|
||||
github_token_module_configured: true,
|
||||
github_token_variable: "ReleaseManager.github_token",
|
||||
github_token_env_variable: "RELEASE_MANAGER_GITHUB_TOKEN",
|
||||
github_api_url: payload.github_api_url || "https://api.github.com",
|
||||
github_api_url_variable: "ReleaseManager.github_api_url",
|
||||
github_webhook_secret_configured: true,
|
||||
github_webhook_secret_variable: "ReleaseManager.github_webhook_secret",
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/github/repositories") && method === "GET") {
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
token_configured: true,
|
||||
repositories: [
|
||||
{
|
||||
full_name: "truckwash/front-end-vue",
|
||||
name: "front-end-vue",
|
||||
private: true,
|
||||
default_branch: "main",
|
||||
description: "Truckwash dashboard frontend",
|
||||
},
|
||||
{
|
||||
full_name: "truckwash/backend-php",
|
||||
name: "backend-php",
|
||||
private: true,
|
||||
default_branch: "release/canary",
|
||||
description: "Truckwash API",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/github/branches") && method === "GET") {
|
||||
const repository = url.searchParams.get("repository") || "truckwash/front-end-vue";
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
token_configured: true,
|
||||
repository,
|
||||
branches: [
|
||||
{ name: "main", commit_sha: "c0ffee", protected: true },
|
||||
{ name: "release/canary", commit_sha: "feedface", protected: false },
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/github/commits") && method === "GET") {
|
||||
const repository = url.searchParams.get("repository") || "truckwash/front-end-vue";
|
||||
const branch = url.searchParams.get("branch") || "main";
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
token_configured: true,
|
||||
repository,
|
||||
branch,
|
||||
commits: [
|
||||
{
|
||||
sha: "feedface00000000000000000000000000000000",
|
||||
short_sha: "feedface0000",
|
||||
message: "Deploy canary build",
|
||||
author_name: "Release Bot",
|
||||
authored_at: "2026-05-19T08:10:00.000Z",
|
||||
},
|
||||
{
|
||||
sha: "c0ffee0000000000000000000000000000000000",
|
||||
short_sha: "c0ffee000000",
|
||||
message: "Previous release",
|
||||
author_name: "Release Bot",
|
||||
authored_at: "2026-05-19T08:00:00.000Z",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/github/test") && method === "POST") {
|
||||
const payload = request.postDataJSON?.() || {};
|
||||
const latestSha = payload.branch === "release/canary" ? "feedface00000000000000000000000000000000" : "c0ffee";
|
||||
await route.fulfill(
|
||||
json({
|
||||
data: {
|
||||
ok: true,
|
||||
status: "accessible",
|
||||
token_configured: true,
|
||||
message: "Repository and branch are accessible with the configured GitHub token.",
|
||||
repository: payload.repository,
|
||||
branch: payload.branch || "main",
|
||||
commit_mode: payload.commit_mode || "latest",
|
||||
commit_sha: payload.commit_mode === "specific" ? payload.commit_sha : latestSha,
|
||||
latest_commit_sha: latestSha,
|
||||
},
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/superuser/releases/channels") && method === "POST") {
|
||||
const payload = request.postDataJSON?.() || {};
|
||||
const channel = {
|
||||
@@ -263,7 +462,7 @@ async function installReleaseMocks(page, state) {
|
||||
app: payload.app || target?.app || "frontend",
|
||||
repository: payload.repository || target?.repository || "truckwash/front-end-vue",
|
||||
branch: payload.branch || target?.branch || "main",
|
||||
commit_sha: payload.commit_sha || "newcommit",
|
||||
commit_sha: payload.commit_sha || "latestcommit",
|
||||
version_label: payload.version_label || "manual-release",
|
||||
status: "deploying",
|
||||
started_at: "2026-05-19T08:12:00.000Z",
|
||||
@@ -309,9 +508,14 @@ async function installReleaseMocks(page, state) {
|
||||
}
|
||||
|
||||
async function expandActiveReleaseCategory(page) {
|
||||
const category = page.getByTestId("release-manager-page").locator(".card").first();
|
||||
await category.locator(".card-header").click();
|
||||
await expect(category.locator(".card-content")).toBeVisible();
|
||||
await expect(page.getByTestId("release-manager-page")).toBeVisible();
|
||||
}
|
||||
|
||||
async function selectReleaseTab(page, name) {
|
||||
await page
|
||||
.getByTestId("release-section-tabs")
|
||||
.getByRole("tab", { name: new RegExp(name) })
|
||||
.click();
|
||||
}
|
||||
|
||||
test("superusers manage release channels, assignments, deployments, and replay", async ({ page }) => {
|
||||
@@ -321,40 +525,80 @@ test("superusers manage release channels, assignments, deployments, and replay",
|
||||
await expect(page.getByTestId("release-manager-page")).toBeVisible({ timeout: 30_000 });
|
||||
await expect(page.getByRole("link", { name: /Release Manager/i }).first()).toBeVisible();
|
||||
await expect(page.locator(".tabs").getByText("Release Manager", { exact: true })).toBeVisible();
|
||||
await expect(page.getByTestId("release-control-api")).toContainText("https://api.truckwash.io:4433");
|
||||
await expect(page.getByTestId("release-guided-setup")).toContainText("Guided setup");
|
||||
await expect(page.getByTestId("release-guided-setup")).toContainText("Frontend target");
|
||||
await expect(page.getByTestId("release-channels-overview")).toContainText("Canary");
|
||||
|
||||
await page.getByRole("button", { name: "Channels" }).click();
|
||||
await selectReleaseTab(page, "Settings");
|
||||
await expect(page.getByTestId("release-settings-summary")).toContainText("ReleaseManager.github_token");
|
||||
await expect(page.getByTestId("release-settings-guide")).toContainText("GitHub token");
|
||||
const settingsForm = page.getByTestId("release-settings-form");
|
||||
await settingsForm.getByPlaceholder("Leave blank to keep the existing token").fill("github_pat_release_manager_test");
|
||||
await page.getByTestId("release-settings-next").click();
|
||||
await expect(page.getByTestId("release-settings-step-api-url")).toContainText("ReleaseManager.github_api_url");
|
||||
await settingsForm.getByPlaceholder("https://api.github.com").fill("https://api.github.com");
|
||||
await page.getByTestId("release-settings-next").click();
|
||||
await expect(page.getByTestId("release-settings-step-webhook")).toContainText("/release/github/webhook");
|
||||
await settingsForm.getByPlaceholder("Webhook HMAC secret").fill("release-manager-webhook-secret");
|
||||
await page.getByTestId("release-settings-next").click();
|
||||
await expect(page.getByTestId("release-settings-step-review")).toContainText("GitHub webhook secret");
|
||||
await page.getByTestId("release-settings-save").click();
|
||||
await expect(page.getByTestId("release-settings-summary")).toContainText("https://api.github.com");
|
||||
|
||||
await selectReleaseTab(page, "Channels");
|
||||
await expandActiveReleaseCategory(page);
|
||||
await page.getByTestId("release-channel-form").getByPlaceholder("slug").fill("internal");
|
||||
await page.getByTestId("release-channel-form").getByPlaceholder("Name").fill("Internal");
|
||||
await page.getByTestId("release-channel-form").getByPlaceholder("Frontend URL").fill("https://internal.example.test");
|
||||
await page.getByTestId("release-channel-presets").getByText("Internal").click();
|
||||
await expect(page.getByTestId("release-channel-form").getByPlaceholder("slug")).toHaveValue("internal");
|
||||
await page.getByTestId("release-channel-form").getByRole("button", { name: "Create" }).click();
|
||||
|
||||
await page.getByRole("button", { name: "Overview" }).click();
|
||||
await selectReleaseTab(page, "Overview");
|
||||
await expect(page.getByTestId("release-channels-overview")).toContainText("internal");
|
||||
|
||||
await page.getByRole("button", { name: "Assignments" }).click();
|
||||
await selectReleaseTab(page, "Assignments");
|
||||
await expandActiveReleaseCategory(page);
|
||||
await page.getByTestId("release-assignment-form").getByPlaceholder("Subject id").fill("42");
|
||||
await page.getByTestId("release-assignment-subject-type").selectOption("customer");
|
||||
await page.getByTestId("release-assignment-customer-search").fill("Acme");
|
||||
await expect(page.getByTestId("release-assignment-customer-results")).toContainText("Acme Transport");
|
||||
await expect(page.getByTestId("release-assignment-customer-search")).toBeFocused();
|
||||
await page.getByTestId("release-assignment-customer-option-424242").click();
|
||||
await expect(page.getByTestId("release-assignment-customer-search")).toHaveValue("Acme Transport (424242)");
|
||||
await page.getByTestId("release-assignment-form").getByRole("button", { name: "Assign" }).click();
|
||||
await expect(page.getByTestId("release-assignments-table")).toContainText("user:42");
|
||||
await expect(page.getByTestId("release-assignments-table")).toContainText("customer:424242");
|
||||
|
||||
await page.getByRole("button", { name: "Integrations" }).click();
|
||||
await selectReleaseTab(page, "Integrations");
|
||||
await expandActiveReleaseCategory(page);
|
||||
await page.getByTestId("release-target-form").getByPlaceholder("owner/repo").fill("truckwash/backend-php");
|
||||
await page.getByTestId("release-target-form").getByPlaceholder("branch").fill("release/canary");
|
||||
await page.getByTestId("release-target-form").getByPlaceholder("Coolify service UUID").fill("api-canary-service");
|
||||
await page.getByTestId("release-target-presets").getByText("API target").click();
|
||||
await expect(page.getByTestId("release-target-form").getByPlaceholder("owner/repo")).toHaveValue(
|
||||
"truckwash/backend-php"
|
||||
);
|
||||
await page.getByTestId("release-target-form").getByPlaceholder("Coolify service UUID").fill("API");
|
||||
await page.getByText("API Canary").click();
|
||||
await expect(page.getByTestId("release-target-form").getByPlaceholder("Coolify service UUID")).toHaveValue(
|
||||
"api-canary-service"
|
||||
);
|
||||
await expect(page.getByTestId("release-target-form").getByPlaceholder("SSL domain")).toHaveValue(
|
||||
"api-canary.example.test"
|
||||
);
|
||||
await expect(page.getByTestId("release-target-form").getByLabel("Coolify SSL")).toBeChecked();
|
||||
await page.getByTestId("release-target-form").getByRole("button", { name: "Test access" }).click();
|
||||
await expect(page.getByTestId("release-target-form")).toContainText("accessible");
|
||||
await page.getByTestId("release-target-form").getByRole("button", { name: "Save target" }).click();
|
||||
await expect(page.getByTestId("release-targets-table")).toContainText("truckwash/backend-php#release/canary");
|
||||
|
||||
await page.getByRole("button", { name: "Deployments" }).click();
|
||||
await page
|
||||
.getByTestId("release-targets-table")
|
||||
.getByRole("row", { name: /truckwash\/backend-php/i })
|
||||
.getByRole("button", { name: "Deploy" })
|
||||
.click();
|
||||
await expandActiveReleaseCategory(page);
|
||||
await page.getByTestId("release-deployment-form").locator("select").nth(3).selectOption("specific");
|
||||
await page.getByTestId("release-deployment-form").getByPlaceholder("commit sha").fill("feedface");
|
||||
await page.getByTestId("release-deployment-form").getByPlaceholder("version label").fill("api-feedface");
|
||||
await page.getByTestId("release-deployment-form").getByRole("button", { name: "Deploy" }).click();
|
||||
await page.getByTestId("release-deployment-submit").click();
|
||||
await expect(page.getByTestId("release-deployments-table")).toContainText("feedface");
|
||||
|
||||
await page.getByRole("button", { name: "Replay" }).click();
|
||||
await selectReleaseTab(page, "Replay");
|
||||
await expandActiveReleaseCategory(page);
|
||||
await page.getByTestId("release-replay-form").getByPlaceholder("Target id or channel slug").fill("42");
|
||||
await page.getByTestId("release-replay-form").getByRole("button", { name: "Enable" }).click();
|
||||
|
||||
@@ -2105,7 +2105,8 @@ export async function mockMobilePosApi(page, fixture) {
|
||||
json({
|
||||
success: false,
|
||||
data: {
|
||||
message: "License plate extraction failed",
|
||||
message: "No license plate detected.",
|
||||
reason: "no_license_plate_detected",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -53,29 +53,33 @@ describe("authenticatedRequest", () => {
|
||||
localStorage.setItem("token", "token");
|
||||
});
|
||||
|
||||
it("suppresses known scanner lpr extraction failures and resolves with response", async () => {
|
||||
it("resolves scanner lpr no-plate responses without recording a failed request", async () => {
|
||||
const response = {
|
||||
status: 500,
|
||||
status: 200,
|
||||
data: {
|
||||
success: false,
|
||||
data: {
|
||||
message: "Internal server error: License plate extraction failed.",
|
||||
message: "No license plate detected.",
|
||||
reason: "no_license_plate_detected",
|
||||
},
|
||||
meta: [],
|
||||
includes: [],
|
||||
},
|
||||
};
|
||||
|
||||
axiosMock.mockRejectedValueOnce({ response });
|
||||
axiosMock.mockResolvedValueOnce(response);
|
||||
const catchCallable = vi.fn();
|
||||
|
||||
const result = await authenticatedRequest("/modules/scanner/lpr", "POST", { base64_image: "x" }, catchCallable);
|
||||
|
||||
expect(result).toBe(response);
|
||||
expect(catchCallable).not.toHaveBeenCalled();
|
||||
expect(requestQueueState.batchCompleted).toBe(1);
|
||||
expect(requestQueueState.batchFailed).toBe(0);
|
||||
expect(requestQueueState.errorRequests).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("continues to reject non-suppressed scanner errors", async () => {
|
||||
it("continues to reject scanner errors", async () => {
|
||||
const error = {
|
||||
response: {
|
||||
status: 500,
|
||||
@@ -98,24 +102,6 @@ describe("authenticatedRequest", () => {
|
||||
expect(catchCallable).toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
it("continues to reject non-lpr errors", async () => {
|
||||
const error = {
|
||||
response: {
|
||||
status: 500,
|
||||
data: {
|
||||
success: false,
|
||||
data: {
|
||||
message: "Internal server error: License plate extraction failed.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
axiosMock.mockRejectedValueOnce(error);
|
||||
|
||||
await expect(authenticatedRequest("/auth/session", "GET")).rejects.toBe(error);
|
||||
});
|
||||
|
||||
it("queues requests and executes them sequentially", async () => {
|
||||
const first = createDeferred();
|
||||
const second = createDeferred();
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// @vitest-environment jsdom
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
acknowledgeReleaseChannelSwitch,
|
||||
buildReleaseFrontendRedirectUrl,
|
||||
getReleaseChannelSwitchedStatus,
|
||||
getReleaseChannelUnavailableStatus,
|
||||
ignoreUnavailableReleaseChannel,
|
||||
RELEASE_CHANNEL_IGNORE_MS,
|
||||
RELEASE_CHANNEL_IGNORE_STORAGE_KEY,
|
||||
RELEASE_CHANNEL_SWITCH_NOTICE_STORAGE_KEY,
|
||||
__resetReleaseChannelAvailabilityForTests,
|
||||
} from "@/services/releaseChannelAvailability.js";
|
||||
|
||||
const canaryRuntime = {
|
||||
channel: {
|
||||
id: 2,
|
||||
slug: "canary",
|
||||
name: "Canary",
|
||||
description: "Early validation channel",
|
||||
default_channel: false,
|
||||
},
|
||||
frontend_base_url: "",
|
||||
api_base_url: "",
|
||||
availability: {
|
||||
configured: false,
|
||||
missing: ["frontend_base_url", "api_base_url"],
|
||||
status: "unconfigured",
|
||||
},
|
||||
};
|
||||
|
||||
const configuredCanaryRuntime = {
|
||||
...canaryRuntime,
|
||||
frontend_base_url: "https://canary.example.test",
|
||||
api_base_url: "https://api-canary.example.test",
|
||||
availability: {
|
||||
configured: true,
|
||||
missing: [],
|
||||
status: "ready",
|
||||
},
|
||||
};
|
||||
|
||||
describe("release channel availability", () => {
|
||||
afterEach(() => {
|
||||
__resetReleaseChannelAvailabilityForTests();
|
||||
});
|
||||
|
||||
it("blocks a non-default assigned channel when targets are missing", () => {
|
||||
const status = getReleaseChannelUnavailableStatus(canaryRuntime, 1_000, 0);
|
||||
|
||||
expect(status.shouldBlock).toBe(true);
|
||||
expect(status.channelSlug).toBe("canary");
|
||||
expect(status.missing).toEqual(["frontend_base_url", "api_base_url"]);
|
||||
});
|
||||
|
||||
it("does not block the stable default channel even when runtime targets are absent", () => {
|
||||
const status = getReleaseChannelUnavailableStatus({
|
||||
channel: {
|
||||
slug: "stable",
|
||||
name: "Stable",
|
||||
default_channel: true,
|
||||
},
|
||||
availability: {
|
||||
configured: false,
|
||||
missing: ["frontend_base_url"],
|
||||
},
|
||||
});
|
||||
|
||||
expect(status.unavailable).toBe(false);
|
||||
expect(status.shouldBlock).toBe(false);
|
||||
});
|
||||
|
||||
it("does not block legacy runtime payloads that omit availability and target URLs", () => {
|
||||
const runtime = {
|
||||
channel: {
|
||||
slug: "canary",
|
||||
name: "Canary",
|
||||
default_channel: false,
|
||||
},
|
||||
};
|
||||
const status = getReleaseChannelUnavailableStatus(runtime);
|
||||
const switchedStatus = getReleaseChannelSwitchedStatus(runtime, "user:42", {});
|
||||
|
||||
expect(status.configured).toBe(true);
|
||||
expect(status.shouldBlock).toBe(false);
|
||||
expect(switchedStatus.shouldShow).toBe(false);
|
||||
});
|
||||
|
||||
it("supports a five-minute channel-specific ignore window", () => {
|
||||
const ignoredUntil = ignoreUnavailableReleaseChannel(canaryRuntime.channel);
|
||||
const stored = JSON.parse(window.localStorage.getItem(RELEASE_CHANNEL_IGNORE_STORAGE_KEY));
|
||||
const status = getReleaseChannelUnavailableStatus(canaryRuntime, Date.now(), ignoredUntil);
|
||||
|
||||
expect(ignoredUntil).toBeGreaterThan(Date.now() + RELEASE_CHANNEL_IGNORE_MS - 2_000);
|
||||
expect(stored.canary).toBe(ignoredUntil);
|
||||
expect(status.ignored).toBe(true);
|
||||
expect(status.shouldBlock).toBe(false);
|
||||
});
|
||||
|
||||
it("shows a one-time switched-channel notice for configured non-default channels", () => {
|
||||
const status = getReleaseChannelSwitchedStatus(configuredCanaryRuntime, "user:42", {});
|
||||
|
||||
expect(status.shouldShow).toBe(true);
|
||||
expect(status.noticeKey).toBe("user:42:canary");
|
||||
});
|
||||
|
||||
it("stores switched-channel acknowledgement per account and channel", () => {
|
||||
const noticeKey = acknowledgeReleaseChannelSwitch(configuredCanaryRuntime.channel, "user:42");
|
||||
const stored = JSON.parse(window.localStorage.getItem(RELEASE_CHANNEL_SWITCH_NOTICE_STORAGE_KEY));
|
||||
const status = getReleaseChannelSwitchedStatus(configuredCanaryRuntime, "user:42", stored);
|
||||
const otherUserStatus = getReleaseChannelSwitchedStatus(configuredCanaryRuntime, "user:99", stored);
|
||||
|
||||
expect(noticeKey).toBe("user:42:canary");
|
||||
expect(stored["user:42:canary"]).toEqual(expect.any(String));
|
||||
expect(status.shouldShow).toBe(false);
|
||||
expect(otherUserStatus.shouldShow).toBe(true);
|
||||
});
|
||||
|
||||
it("does not show the switched-channel notice for default channels or unknown accounts", () => {
|
||||
const defaultStatus = getReleaseChannelSwitchedStatus(
|
||||
{
|
||||
...configuredCanaryRuntime,
|
||||
channel: {
|
||||
...configuredCanaryRuntime.channel,
|
||||
default_channel: true,
|
||||
},
|
||||
},
|
||||
"user:42",
|
||||
{}
|
||||
);
|
||||
const anonymousStatus = getReleaseChannelSwitchedStatus(configuredCanaryRuntime, "", {});
|
||||
|
||||
expect(defaultStatus.shouldShow).toBe(false);
|
||||
expect(anonymousStatus.shouldShow).toBe(false);
|
||||
});
|
||||
|
||||
it("builds a same-path redirect for a configured channel frontend", () => {
|
||||
const redirectUrl = buildReleaseFrontendRedirectUrl(
|
||||
{
|
||||
channel: { slug: "beta", default_channel: false },
|
||||
frontend_base_url: "https://beta.example.test",
|
||||
api_base_url: "https://api-beta.example.test",
|
||||
},
|
||||
{
|
||||
origin: "https://app.example.test",
|
||||
pathname: "/user/bookings",
|
||||
search: "?page=2",
|
||||
hash: "#next",
|
||||
}
|
||||
);
|
||||
|
||||
expect(redirectUrl).toBe("https://beta.example.test/user/bookings?page=2#next");
|
||||
});
|
||||
});
|
||||
@@ -123,7 +123,7 @@ describe("session token initialization regression contract", () => {
|
||||
it("keeps department id lookup decoupled from router module initialization", () => {
|
||||
expect(sessionUserSource).not.toContain('import router from "@/router.js";');
|
||||
expect(sessionUserSource).not.toContain("router.currentRoute.value.params.departmentId");
|
||||
expect(sessionUserSource).toContain("const path = window.location.pathname || '';");
|
||||
expect(sessionUserSource).toMatch(/const path = window\.location\.pathname \|\| ["']["'];/);
|
||||
expect(sessionUserSource).toContain("const match = path.match(/^\\/admin\\/(\\d+)(?:\\/|$)/);");
|
||||
expect(sessionUserSource).toContain("const departmentId = Number.parseInt(match[1], 10);");
|
||||
expect(sessionUserSource).toContain(
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { axiosMock } = vi.hoisted(() => ({
|
||||
axiosMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("axios", () => ({
|
||||
default: axiosMock,
|
||||
}));
|
||||
|
||||
import {
|
||||
clearReleaseManagerControlApiUrl,
|
||||
getReleaseManagerControlApiUrl,
|
||||
getReleaseSummary,
|
||||
RELEASE_MANAGER_CONTROL_API_STORAGE_KEY,
|
||||
releaseManagerControlApiCandidates,
|
||||
setReleaseManagerControlApiUrl,
|
||||
} from "@/services/superuserReleases.js";
|
||||
import { __configureRequestQueueForTests, __resetRequestQueueForTests } from "@/services/requestQueue.js";
|
||||
|
||||
describe("superuser release manager service", () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.mockReset();
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
localStorage.setItem("token", "release-token");
|
||||
__resetRequestQueueForTests();
|
||||
__configureRequestQueueForTests({
|
||||
maxConcurrentGet: 1,
|
||||
maxConcurrentOther: 1,
|
||||
spacingMs: 0,
|
||||
retryByStatusCode: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps release manager calls on the explicit control API", async () => {
|
||||
setReleaseManagerControlApiUrl("https://control.example.test/");
|
||||
axiosMock.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } });
|
||||
|
||||
await getReleaseSummary();
|
||||
|
||||
expect(getReleaseManagerControlApiUrl()).toBe("https://control.example.test");
|
||||
expect(axiosMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
url: "https://control.example.test/superuser/releases",
|
||||
method: "GET",
|
||||
headers: expect.objectContaining({ Authorization: "Bearer release-token" }),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back when the selected API does not have release manager endpoints", async () => {
|
||||
localStorage.setItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY, "https://api.truckwash.io");
|
||||
axiosMock
|
||||
.mockRejectedValueOnce({ response: { status: 404 } })
|
||||
.mockResolvedValueOnce({ status: 200, data: { data: { channels: [] } } });
|
||||
|
||||
await getReleaseSummary();
|
||||
|
||||
expect(axiosMock.mock.calls[0][0].url).toBe("https://api.truckwash.io/superuser/releases");
|
||||
expect(axiosMock.mock.calls[1][0].url).toBe("https://api.truckwash.io:4433/superuser/releases");
|
||||
expect(releaseManagerControlApiCandidates()[0]).toBe("https://api.truckwash.io:4433");
|
||||
});
|
||||
|
||||
it("can clear the explicit control API override", () => {
|
||||
setReleaseManagerControlApiUrl("https://control.example.test");
|
||||
clearReleaseManagerControlApiUrl();
|
||||
|
||||
expect(localStorage.getItem(RELEASE_MANAGER_CONTROL_API_STORAGE_KEY)).toBeNull();
|
||||
expect(releaseManagerControlApiCandidates()).toContain("https://api.truckwash.io:4433");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user