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:
Jeppe Bundgaard
2026-05-19 12:05:25 +02:00
parent 4f1973d048
commit d25726fafd
41 changed files with 12399 additions and 689 deletions
+45 -24
View File
@@ -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>
+102 -5
View File
@@ -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
View File
@@ -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 = [
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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": {
+406
View File
@@ -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."
}
}
}
}
+350
View File
@@ -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);
}
};
+64 -4
View File
@@ -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",
+15
View File
@@ -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);
+186 -22
View File
@@ -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;
File diff suppressed because it is too large Load Diff
+205 -11
View File
@@ -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) => {
+5
View File
@@ -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);
});
+263 -19
View File
@@ -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();
+2 -1
View File
@@ -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",
},
})
);
+9 -23
View File
@@ -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(
+74
View File
@@ -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");
});
});