chore: update Go server version handling and enhance Docker build process

This commit is contained in:
UNITRONIX
2026-06-11 20:31:21 +02:00
parent 246f37a583
commit 5403a04783
47 changed files with 302 additions and 56 deletions
+2
View File
@@ -117,6 +117,7 @@ jobs:
build-args: |
BETTERDESK_COMMIT_SHA=${{ github.sha }}
BETTERDESK_IMAGE_VERSION=${{ needs.read-version.outputs.version }}
BETTERDESK_PRODUCT_VERSION=${{ needs.read-version.outputs.version }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
@@ -222,6 +223,7 @@ jobs:
build-args: |
BETTERDESK_COMMIT_SHA=${{ github.sha }}
BETTERDESK_IMAGE_VERSION=${{ needs.read-version.outputs.version }}
BETTERDESK_PRODUCT_VERSION=${{ needs.read-version.outputs.version }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
+1 -3
View File
@@ -41,9 +41,7 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add VERSION web-nodejs/package.json betterdesk.sh betterdesk.ps1 betterdesk-docker.sh \
CHANGELOG.md README.md Dockerfile Dockerfile.server Dockerfile.console \
docker-compose.quick.yml docker/entrypoint.sh docker-entrypoint.sh
git add $(node scripts/bump-version.js --list-paths)
if git diff --cached --quiet; then
echo "No version files changed — skipping commit"
exit 0
+1 -3
View File
@@ -46,9 +46,7 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add VERSION web-nodejs/package.json betterdesk.sh betterdesk.ps1 betterdesk-docker.sh \
CHANGELOG.md README.md Dockerfile Dockerfile.server Dockerfile.console \
docker-compose.quick.yml docker/entrypoint.sh docker-entrypoint.sh
git add $(node scripts/bump-version.js --list-paths)
BRANCH="release/v${NEW_VERSION}"
git checkout -b "$BRANCH"
git commit -m "chore: release version ${NEW_VERSION} [version-bump]"
+4
View File
@@ -5,6 +5,8 @@ on:
branches: [main, dev]
paths:
- 'VERSION'
- 'betterdesk-server/VERSION'
- 'betterdesk-server/internal/productversion/VERSION'
- 'web-nodejs/package.json'
- 'betterdesk.sh'
- 'betterdesk.ps1'
@@ -21,6 +23,8 @@ on:
branches: [main, dev]
paths:
- 'VERSION'
- 'betterdesk-server/VERSION'
- 'betterdesk-server/internal/productversion/VERSION'
- 'web-nodejs/package.json'
- 'betterdesk.sh'
- 'betterdesk.ps1'
+4 -1
View File
@@ -27,8 +27,11 @@ COPY betterdesk-server/go.mod betterdesk-server/go.sum ./
RUN go mod download
COPY betterdesk-server/ .
ARG BETTERDESK_PRODUCT_VERSION=dev
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w" \
-ldflags="-s -w -X main.Version=${BETTERDESK_PRODUCT_VERSION}" \
-tags "netgo osusergo" \
-o /betterdesk-server .
+3 -1
View File
@@ -21,9 +21,11 @@ RUN go mod download
# Copy full source
COPY betterdesk-server/ .
ARG BETTERDESK_PRODUCT_VERSION=dev
# Build static binary (modernc.org/sqlite is pure Go — no CGO needed)
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w" \
-ldflags="-s -w -X main.Version=${BETTERDESK_PRODUCT_VERSION}" \
-tags "netgo osusergo" \
-o /betterdesk-server .
+1
View File
@@ -0,0 +1 @@
3.2.15
@@ -0,0 +1 @@
3.2.15
@@ -0,0 +1,19 @@
// Package productversion exposes the embedded BetterDesk product semver.
package productversion
import (
_ "embed"
"strings"
)
//go:embed VERSION
var embedded []byte
// Product returns the embedded product version (X.Y.Z) or "dev" when unset.
func Product() string {
v := strings.TrimSpace(string(embedded))
if v == "" {
return "dev"
}
return v
}
@@ -0,0 +1,10 @@
package productversion
import "testing"
func TestProduct_notEmpty(t *testing.T) {
v := Product()
if v == "" {
t.Fatal("Product() returned empty string")
}
}
+9
View File
@@ -26,6 +26,7 @@ import (
"github.com/unitronix/betterdesk-server/config"
"github.com/unitronix/betterdesk-server/crypto"
"github.com/unitronix/betterdesk-server/db"
"github.com/unitronix/betterdesk-server/internal/productversion"
"github.com/unitronix/betterdesk-server/logging"
"github.com/unitronix/betterdesk-server/metrics"
"github.com/unitronix/betterdesk-server/ratelimit"
@@ -41,6 +42,14 @@ var (
BuildDate = "unknown"
)
func init() {
if Version == "dev" {
if v := productversion.Product(); v != "" && v != "dev" {
Version = v
}
}
}
func main() {
cfg := parseFlags()
+1 -1
View File
@@ -1419,7 +1419,7 @@ compile_go_server() {
go mod download
# Build with optimizations
CGO_ENABLED=0 go build -ldflags="-s -w" -o "$output_name" .
CGO_ENABLED=0 go build -ldflags="-s -w -X main.Version=${VERSION}" -o "$output_name" .
if [ -f "$output_name" ]; then
chmod +x "$output_name"
+5 -5
View File
@@ -28,13 +28,13 @@
# (issue #186): use service_started (not service_healthy), pin image tags, and
# set DB_PATH=/app/data/db_v2.sqlite3 plus AUTH_DB_PATH on the server.
#
# Image tag (aligned with CHANGELOG / git tag v3.2.14):
# Default: 3.2.14 | Rolling: BETTERDESK_IMAGE_TAG=latest
# Image tag (aligned with CHANGELOG / git tag v3.2.15):
# Default: 3.2.15 | Rolling: BETTERDESK_IMAGE_TAG=latest
# =============================================================================
services:
server:
image: ghcr.io/unitronix/betterdesk-server:${BETTERDESK_IMAGE_TAG:-3.2.14}
image: ghcr.io/unitronix/betterdesk-server:${BETTERDESK_IMAGE_TAG:-3.2.15}
container_name: betterdesk-server
hostname: betterdesk-server
command: ["/usr/local/bin/betterdesk-server", "-mode", "all", "-api-port", "21114", "-key-file", "/opt/rustdesk/id_ed25519"]
@@ -70,7 +70,7 @@ services:
start_period: 60s
console:
image: ghcr.io/unitronix/betterdesk-console:${BETTERDESK_IMAGE_TAG:-3.2.14}
image: ghcr.io/unitronix/betterdesk-console:${BETTERDESK_IMAGE_TAG:-3.2.15}
container_name: betterdesk-console
# Shares server network stack — panel and RustDesk ports use MACVLAN_IPV4.
network_mode: service:server
@@ -97,7 +97,7 @@ services:
- WS_HBBR_PORT=21117
- DOCKER=true
- BETTERDESK_UPDATE_MODE=image
- BETTERDESK_IMAGE_TAG=${BETTERDESK_IMAGE_TAG:-3.2.14}
- BETTERDESK_IMAGE_TAG=${BETTERDESK_IMAGE_TAG:-3.2.15}
- TZ=${TZ:-UTC}
depends_on:
# service_started (not healthy): avoids deadlock with auth.db on first boot (#138, #186).
+8 -2
View File
@@ -39,7 +39,8 @@ Every automated bump updates these together:
**Tier 1**
- `VERSION`
- `web-nodejs/package.json`
- `web-nodejs/package.json` (npm mirror; panel reads root `VERSION` via `web-nodejs/lib/productVersion.js`)
- `betterdesk-server/VERSION` and `betterdesk-server/internal/productversion/VERSION` (Go `go:embed` + builds)
- `betterdesk.sh`, `betterdesk.ps1`, `betterdesk-docker.sh`
- `CHANGELOG.md` (moves `[Unreleased]` → new section)
- `README.md` (version badge)
@@ -47,9 +48,11 @@ Every automated bump updates these together:
**Tier 2 (Docker)**
- `Dockerfile`, `Dockerfile.server`, `Dockerfile.console`
- `docker-compose.quick.yml`
- `docker-compose.quick.yml`, `docker-compose.quick.macvlan.yml`
- `docker/entrypoint.sh`, `docker-entrypoint.sh`
The web console and Go server should report the **same product semver** after a normal install, panel update, or release build. Settings → Server Information shows both the product version and the live Go server version from `/api/health`.
Sub-project versions (`betterdesk-mgmt`, `betterdesk-agent-client`, SDKs) are **not** bumped with the main product unless explicitly requested.
### Maintainer commands
@@ -58,6 +61,9 @@ Sub-project versions (`betterdesk-mgmt`, `betterdesk-agent-client`, SDKs) are **
# Verify all Tier 1/2 files match VERSION (CI runs this too)
node scripts/bump-version.js --verify
# List every path touched by automated bumps (used by CI git add)
node scripts/bump-version.js --list-paths
# Manual bump (normally CI handles this)
node scripts/bump-version.js --patch # +0.0.1
node scripts/bump-version.js --minor # +0.1.0
+33 -1
View File
@@ -152,8 +152,30 @@ const FILE_RULES = [
`\${BETTERDESK_IMAGE_VERSION:-${version}}`
),
},
{
id: 'betterdesk-server-version',
path: 'betterdesk-server/VERSION',
extract: (content) => content.trim(),
apply: (_content, version) => `${version}\n`,
},
{
id: 'betterdesk-server-productversion-embed',
path: 'betterdesk-server/internal/productversion/VERSION',
extract: (content) => content.trim(),
apply: (_content, version) => `${version}\n`,
},
];
const CHANGELOG_PATH = 'CHANGELOG.md';
function getManagedPaths() {
const paths = FILE_RULES.map((rule) => rule.path);
if (!paths.includes(CHANGELOG_PATH)) {
paths.push(CHANGELOG_PATH);
}
return paths;
}
function escapeRegExp(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
@@ -308,12 +330,13 @@ function verifyVersions() {
}
function printUsage() {
console.log(`Usage: node scripts/bump-version.js [--patch | --minor | --set X.Y.Z] [--dry-run] [--verify]
console.log(`Usage: node scripts/bump-version.js [--patch | --minor | --set X.Y.Z] [--dry-run] [--verify] [--list-paths]
--patch Bump patch (+0.0.1)
--minor Bump minor (+0.1.0, reset patch)
--set X.Y.Z Set explicit version
--verify Exit 1 if Tier 1/2 files disagree with VERSION
--list-paths Print managed file paths (one per line) for CI git add
--dry-run Print planned version without writing files`);
}
@@ -321,6 +344,14 @@ function main() {
const args = process.argv.slice(2);
const dryRun = args.includes('--dry-run');
const verify = args.includes('--verify');
const listPaths = args.includes('--list-paths');
if (listPaths) {
for (const filePath of getManagedPaths()) {
console.log(filePath);
}
return;
}
if (verify) {
verifyVersions();
@@ -365,6 +396,7 @@ if (require.main === module) {
module.exports = {
FILE_RULES,
getManagedPaths,
readCanonicalVersion,
bumpPatch,
bumpMinor,
+2 -6
View File
@@ -30,12 +30,8 @@ if (fs.existsSync(_envFile)) {
} catch (_e) { /* .env read failed — continue with existing env vars */ }
}
// Read version from package.json
let pkgVersion = '2.0.0';
try {
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
pkgVersion = pkg.version || pkgVersion;
} catch (e) { /* use default */ }
const { readProductVersion } = require('../lib/productVersion');
const pkgVersion = readProductVersion({ consoleDir: path.join(__dirname, '..') });
// Environment / platform detection
const NODE_ENV = process.env.NODE_ENV || 'development';
+3 -1
View File
@@ -582,7 +582,9 @@
"enrollment_tag_picker": "اختر علامات من علامات الأجهزة الموجودة",
"enrollment_tag_picker_hint": "يتيح للمشغلين اختيار علامات من القائمة المستخدمة في صفحة الأجهزة بدلًا من الكتابة فقط.",
"enrollment_open_requests": "فتح طلبات التسجيل",
"enrollment_pending_count": "{count} طلب تسجيل معلق"
"enrollment_pending_count": "{count} طلب تسجيل معلق",
"go_server_version": "خادم Go",
"go_server_version_dev_hint": "الثنائي Go يبلغ عن بناء تطويري — أعد البناء أو حدّث الخادم ليطابق إصدار المنتج"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Wybor tagow z listy istniejacych",
"enrollment_tag_picker_hint": "Umozliwia wybor tagow z listy uzywanej na stronie Urzadzenia zamiast samego wpisywania.",
"enrollment_open_requests": "Otworz zadania rejestracji",
"enrollment_pending_count": "{count} oczekujacych zadan rejestracji"
"enrollment_pending_count": "{count} oczekujacych zadan rejestracji",
"go_server_version": "Go server",
"go_server_version_dev_hint": "Binární Go server hlásí vývojovou sestavu — přestavte nebo aktualizujte server, aby odpovídal verzi produktu"
},
"audit": {
"time": "Čas",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (supervisor-konfiguration)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (orkestrering)",
"advanced_restart_none": "Für diese Fil ist kein Tjenesteneustart erforderlich."
"advanced_restart_none": "Für diese Fil ist kein Tjenesteneustart erforderlich.",
"go_server_version": "Go-server",
"go_server_version_dev_hint": "Go-binær rapporterer udviklingsbuild — genopbyg eller opdater serveren for at matche produktversionen"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Tags aus vorhandenen Geräte-Tags auswählen",
"enrollment_tag_picker_hint": "Ermöglicht Operatoren, Tags aus der auf der Geräteseite verwendeten Liste auszuwählen, anstatt nur zu tippen.",
"enrollment_open_requests": "Offene Registrierungsanfragen",
"enrollment_pending_count": "{count} ausstehende Registrierungsanfrage(n)"
"enrollment_pending_count": "{count} ausstehende Registrierungsanfrage(n)",
"go_server_version": "Go-Server",
"go_server_version_dev_hint": "Go-Binary meldet Entwicklungsbuild — Server neu bauen oder aktualisieren, um die Produktversion anzugleichen"
},
"audit": {
"time": "Zeit",
+3 -1
View File
@@ -575,7 +575,9 @@
"totp_recovery_warning": "Save these recovery codes in a safe place. Each code can only be used once. If you lose access to your authenticator app, you can use these codes to log in.",
"totp_done": "Done",
"totp_disable_warning": "Disabling two-factor authentication will make your account less secure. Enter your password to confirm.",
"totp_disabled_success": "Two-factor authentication has been disabled."
"totp_disabled_success": "Two-factor authentication has been disabled.",
"go_server_version": "Go server",
"go_server_version_dev_hint": "Go binary reports development build — rebuild or update the server to match the product version"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Elegir etiquetas de las etiquetas de dispositivos existentes",
"enrollment_tag_picker_hint": "Permite a los operadores seleccionar etiquetas de la lista usada en la página de Dispositivos en lugar de solo escribirlas.",
"enrollment_open_requests": "Solicitudes de inscripción abiertas",
"enrollment_pending_count": "{count} solicitud(es) de inscripción pendiente(s)"
"enrollment_pending_count": "{count} solicitud(es) de inscripción pendiente(s)",
"go_server_version": "Servidor Go",
"go_server_version_dev_hint": "El binario Go informa compilación de desarrollo — recompile o actualice el servidor para igualar la versión del producto"
},
"audit": {
"time": "Hora",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (supervisor-asetukset)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (orkestrointi)",
"advanced_restart_none": "Für diese Tiedosto ist kein Palveluneustart erforderlich."
"advanced_restart_none": "Für diese Tiedosto ist kein Palveluneustart erforderlich.",
"go_server_version": "Go-palvelin",
"go_server_version_dev_hint": "Go-binääri raportoi kehitysversion — rakenna palvelin uudelleen tai päivitä se vastaamaan tuoteversiota"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Sélectionner des tags parmi les tags d'appareils existants",
"enrollment_tag_picker_hint": "Permet aux opérateurs de sélectionner des tags dans la liste utilisée sur la page Appareils plutôt que de les saisir uniquement.",
"enrollment_open_requests": "Demandes d'inscription ouvertes",
"enrollment_pending_count": "{count} demande(s) d'inscription en attente"
"enrollment_pending_count": "{count} demande(s) d'inscription en attente",
"go_server_version": "Serveur Go",
"go_server_version_dev_hint": "Le binaire Go signale une build de développement — reconstruisez ou mettez à jour le serveur pour correspondre à la version produit"
},
"audit": {
"time": "Heure",
+3 -1
View File
@@ -581,7 +581,9 @@
"enrollment_tag_picker": "मौजूदा डिवाइस टैग से टैग चुनें",
"enrollment_tag_picker_hint": "ऑपरेटरों को केवल टाइप करने की बजाय डिवाइस पेज पर उपयोग की गई टैग सूची से चुनने देता है।",
"enrollment_open_requests": "नामांकन अनुरोध खोलें",
"enrollment_pending_count": "{count} लंबित नामांकन अनुरोध"
"enrollment_pending_count": "{count} लंबित नामांकन अनुरोध",
"go_server_version": "Go सर्वर",
"go_server_version_dev_hint": "Go बाइनरी विकास बिल्ड की सूचना देता है — उत्पाद संस्करण से मेल खाने के लिए सर्वर को पुनर्निर्माण या अपडेट करें"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (supervisor konfiguráció)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (orchestráció)",
"advanced_restart_none": "Für diese Fájl ist kein Szolgáltatásneustart erforderlich."
"advanced_restart_none": "Für diese Fájl ist kein Szolgáltatásneustart erforderlich.",
"go_server_version": "Go szerver",
"go_server_version_dev_hint": "A Go bináris fejlesztői buildet jelez — építse újra vagy frissítse a szervert a termékverzióhoz"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"enrollment_tag_picker": "Pilih tag dari tag perangkat yang ada",
"enrollment_tag_picker_hint": "Memungkinkan operator memilih tag dari daftar di halaman Perangkat alih-alih hanya mengetik.",
"enrollment_open_requests": "Buka permintaan pendaftaran",
"enrollment_pending_count": "{count} permintaan pendaftaran tertunda"
"enrollment_pending_count": "{count} permintaan pendaftaran tertunda",
"go_server_version": "Server Go",
"go_server_version_dev_hint": "Biner Go melaporkan build pengembangan — bangun ulang atau perbarui server agar sesuai versi produk"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Sèlectionner des tags parmi les tags d'dispositivi existants",
"enrollment_tag_picker_hint": "Permet aux opèrateurs de sèlectionner des tags dall'elenco utilisèe sur la page Dispositivi plutôt que de les saisir uniquement.",
"enrollment_open_requests": "Demandes d'inscription ouvertes",
"enrollment_pending_count": "{count} demande(s) d'inscription en attesa"
"enrollment_pending_count": "{count} demande(s) d'inscription en attesa",
"go_server_version": "Server Go",
"go_server_version_dev_hint": "Il binario Go segnala build di sviluppo — ricompilare o aggiornare il server per allineare la versione prodotto"
},
"audit": {
"time": "Ora",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "既存のデバイスタグからタグを選択",
"enrollment_tag_picker_hint": "オペレーターがデバイスページで使用されるタグリストから選択できます(手動入力のみに限定されません)。",
"enrollment_open_requests": "登録リクエストを開く",
"enrollment_pending_count": "{count} 件の保留中の登録リクエスト"
"enrollment_pending_count": "{count} 件の保留中の登録リクエスト",
"go_server_version": "Goサーバー",
"go_server_version_dev_hint": "Goバイナリが開発ビルドを報告しています — 製品バージョンに合わせてサーバーを再ビルドまたは更新してください"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "기존 기기 태그에서 태그 선택",
"enrollment_tag_picker_hint": "운영자가 기기 페이지에서 사용되는 태그 목록에서 선택할 수 있습니다(입력만으로 제한되지 않음).",
"enrollment_open_requests": "등록 요청 열기",
"enrollment_pending_count": "대기 중인 등록 요청 {count}건"
"enrollment_pending_count": "대기 중인 등록 요청 {count}건",
"go_server_version": "Go 서버",
"go_server_version_dev_hint": "Go 바이너리가 개발 빌드를 보고합니다 — 제품 버전과 일치하도록 서버를 다시 빌드하거나 업데이트하세요"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (supervisor-konfigurasjon)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (orkestrering)",
"advanced_restart_none": "Für diese Fil ist kein Tjenesteneustart erforderlich."
"advanced_restart_none": "Für diese Fil ist kein Tjenesteneustart erforderlich.",
"go_server_version": "Go-server",
"go_server_version_dev_hint": "Go-binæren rapporterer utviklingsbuild — bygg på nytt eller oppdater serveren for å matche produktversjonen"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Tags aus vorhandenen Gerate-Tags auswahlen",
"enrollment_tag_picker_hint": "Ermoglicht Operatoren, Tags aus der auf der Gerateseite verwendeten Liste auszuwahlen, anstatt nur zu tippen.",
"enrollment_open_requests": "Offene Registrierungsanfragen",
"enrollment_pending_count": "{count} ausstehende Registrierungsanfrage(n)"
"enrollment_pending_count": "{count} ausstehende Registrierungsanfrage(n)",
"go_server_version": "Go-server",
"go_server_version_dev_hint": "Go-binary meldt ontwikkelbuild — herbouw of werk de server bij om de productversie te matchen"
},
"audit": {
"time": "Tijd",
+3 -1
View File
@@ -575,7 +575,9 @@
"totp_recovery_warning": "Zapisz te kody odzyskiwania w bezpiecznym miejscu. Każdy kod może być użyty tylko raz. Jeśli stracisz dostęp do aplikacji uwierzytelniającej, możesz użyć tych kodów do zalogowania.",
"totp_done": "Gotowe",
"totp_disable_warning": "Wyłączenie uwierzytelniania dwuskładnikowego zmniejszy bezpieczeństwo konta. Wprowadź hasło, aby potwierdzić.",
"totp_disabled_success": "Uwierzytelnianie dwuskładnikowe zostało wyłączone."
"totp_disabled_success": "Uwierzytelnianie dwuskładnikowe zostało wyłączone.",
"go_server_version": "Serwer Go",
"go_server_version_dev_hint": "Binarny serwer Go zgłasza build deweloperski — przebuduj lub zaktualizuj serwer, aby dopasować wersję produktu"
},
"audit": {
"time": "Czas",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "Escolher etiquetas de las etiquetas de dispositivos existentes",
"enrollment_tag_picker_hint": "Permite aos operadores selecionar etiquetas de la lista usada en la página de Dispositivos em vez de solo escribirlas.",
"enrollment_open_requests": "Pedidos de registo abiertas",
"enrollment_pending_count": "{count} solicitud(es) de registo pendente(s)"
"enrollment_pending_count": "{count} solicitud(es) de registo pendente(s)",
"go_server_version": "Servidor Go",
"go_server_version_dev_hint": "O binário Go reporta build de desenvolvimento — recompile ou atualize o servidor para corresponder à versão do produto"
},
"audit": {
"time": "Hora",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (configurare supervisord)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (orchestrare)",
"advanced_restart_none": "Für diese Fișier ist kein Serviciuneustart erforderlich."
"advanced_restart_none": "Für diese Fișier ist kein Serviciuneustart erforderlich.",
"go_server_version": "Server Go",
"go_server_version_dev_hint": "Binarul Go raportează build de dezvoltare — reconstruiți sau actualizați serverul pentru a corespunde versiunii produsului"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (supervisor-konfiguration)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (orkestrering)",
"advanced_restart_none": "Für diese Fil ist kein Tjänstneustart erforderlich."
"advanced_restart_none": "Für diese Fil ist kein Tjänstneustart erforderlich.",
"go_server_version": "Go-server",
"go_server_version_dev_hint": "Go-binären rapporterar utvecklingsbuild — bygg om eller uppdatera servern för att matcha produktversionen"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"enrollment_tag_picker": "เลือกแท็กจากแท็กอุปกรณ์ที่มีอยู่",
"enrollment_tag_picker_hint": "ให้ผู้ดำเนินการเลือกแท็กจากรายการในหน้าอุปกรณ์แทนการพิมพ์เท่านั้น",
"enrollment_open_requests": "เปิดคำขอลงทะเบียน",
"enrollment_pending_count": "คำขอลงทะเบียนที่รอดำเนินการ {count} รายการ"
"enrollment_pending_count": "คำขอลงทะเบียนที่รอดำเนินการ {count} รายการ",
"go_server_version": "เซิร์ฟเวอร์ Go",
"go_server_version_dev_hint": "ไบนารี Go รายงานว่าเป็น build พัฒนา — สร้างใหม่หรืออัปเดตเซิร์ฟเวอร์ให้ตรงกับเวอร์ชันผลิตภัณฑ์"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"enrollment_tag_picker": "Mevcut cihaz etiketlerinden etiket seç",
"enrollment_tag_picker_hint": "Operatörlerin yalnızca yazmak yerine Cihazlar sayfasındaki etiket listesinden seçmesine olanak tanır.",
"enrollment_open_requests": "Kayıt isteklerini aç",
"enrollment_pending_count": "{count} bekleyen kayıt isteği"
"enrollment_pending_count": "{count} bekleyen kayıt isteği",
"go_server_version": "Go sunucusu",
"go_server_version_dev_hint": "Go ikilisi geliştirme derlemesi bildiriyor — ürün sürümüyle eşleşmesi için sunucuyu yeniden derleyin veya güncelleyin"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"advanced_file_build-env": "Agent-Build-Umgebung (/etc/betterdesk/build.env)",
"advanced_file_docker-supervisord": "Docker: supervisord.conf (конфігурація supervisord)",
"advanced_file_docker-compose": "Docker: docker-compose.yml (оркестрація)",
"advanced_restart_none": "Für diese Файл ist kein Службаneustart erforderlich."
"advanced_restart_none": "Für diese Файл ist kein Службаneustart erforderlich.",
"go_server_version": "Go-сервер",
"go_server_version_dev_hint": "Go-бінарник повідомляє про dev-збірку — перезберіть або оновіть сервер, щоб відповідати версії продукту"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -581,7 +581,9 @@
"enrollment_tag_picker": "Chọn thẻ từ thẻ thiết bị hiện có",
"enrollment_tag_picker_hint": "Cho phép người vận hành chọn thẻ từ danh sách trên trang Thiết bị thay vì chỉ gõ tay.",
"enrollment_open_requests": "Mở yêu cầu đăng ký",
"enrollment_pending_count": "{count} yêu cầu đăng ký đang chờ"
"enrollment_pending_count": "{count} yêu cầu đăng ký đang chờ",
"go_server_version": "Máy chủ Go",
"go_server_version_dev_hint": "Nhị phân Go báo bản dựng phát triển — build lại hoặc cập nhật máy chủ để khớp phiên bản sản phẩm"
},
"audit": {
"time": "Time",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "從現有裝置標籤中選擇標籤",
"enrollment_tag_picker_hint": "允許操作員從裝置頁面的標籤清單中選擇,而不僅限於手動輸入。",
"enrollment_open_requests": "開啟註冊請求",
"enrollment_pending_count": "{count} 個待處理的註冊請求"
"enrollment_pending_count": "{count} 個待處理的註冊請求",
"go_server_version": "Go 伺服器",
"go_server_version_dev_hint": "Go 二進位報告為開發建置 — 請重新建置或更新伺服器以符合產品版本"
},
"audit": {
"time": "時間",
+3 -1
View File
@@ -575,7 +575,9 @@
"enrollment_tag_picker": "从现有设备标签中选择标签",
"enrollment_tag_picker_hint": "允许操作员从设备页面标签列表中选择,而不仅限于手动输入。",
"enrollment_open_requests": "打开注册请求",
"enrollment_pending_count": "{count} 个待处理注册请求"
"enrollment_pending_count": "{count} 个待处理注册请求",
"go_server_version": "Go 服务器",
"go_server_version_dev_hint": "Go 二进制报告为开发构建 — 请重新构建或更新服务器以匹配产品版本"
},
"audit": {
"time": "时间",
+49
View File
@@ -0,0 +1,49 @@
'use strict';
const fs = require('fs');
const path = require('path');
const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
function getProjectRoot() {
return path.join(__dirname, '..', '..');
}
function readPackageJsonVersion(consoleDir) {
try {
const pkgPath = path.join(consoleDir || path.join(__dirname, '..'), 'package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
const v = (pkg.version || '').trim();
return SEMVER_RE.test(v) ? v : null;
} catch (_e) {
return null;
}
}
/**
* Read canonical BetterDesk product version.
* Prefers repo-root VERSION, then web-nodejs/package.json, then fallback.
*/
function readProductVersion({ rootDir, consoleDir, fallback = '0.0.0' } = {}) {
const projectRoot = rootDir || getProjectRoot();
const versionFile = path.join(projectRoot, 'VERSION');
try {
if (fs.existsSync(versionFile)) {
const v = fs.readFileSync(versionFile, 'utf8').trim();
if (SEMVER_RE.test(v)) return v;
}
} catch (_e) { /* fall through */ }
const fromPkg = readPackageJsonVersion(consoleDir || path.join(projectRoot, 'web-nodejs'));
if (fromPkg) return fromPkg;
return fallback;
}
module.exports = {
SEMVER_RE,
getProjectRoot,
readProductVersion,
readPackageJsonVersion,
};
+11
View File
@@ -193,6 +193,17 @@
document.getElementById('db-path').textContent = data.paths?.database || '-';
document.getElementById('uptime').textContent = formatUptime(data.server?.uptime);
const goVerEl = document.getElementById('go-server-version');
if (goVerEl) {
const goVer = data.goServer?.version;
goVerEl.textContent = goVer ? `v${goVer}` : '-';
if (goVer && goVer === 'dev' && data.app?.version && data.app.version !== 'dev') {
goVerEl.title = typeof _ === 'function' ? _('settings.go_server_version_dev_hint') : '';
} else {
goVerEl.title = '';
}
}
} catch (error) {
console.error('Failed to load server info:', error);
+5 -7
View File
@@ -26,6 +26,7 @@ const path = require('path');
const https = require('https');
const { execSync, execFileSync } = require('child_process');
const config = require('../config/config');
const { readProductVersion } = require('../lib/productVersion');
const { createConsoleDeployGraph } = require('../lib/consoleDeployGraph');
const { resolveChildPath, resolvePathUnderRoot, existsConfinedChild, removeConfinedChild } = require('../lib/safePath');
const { runConsoleNpmInstall } = require('../lib/consoleNpmInstall');
@@ -483,12 +484,7 @@ async function getRemoteHeadSHA() {
}
function getLocalVersion() {
const versionFile = path.join(PROJECT_ROOT, 'VERSION');
if (fs.existsSync(versionFile)) {
const v = fs.readFileSync(versionFile, 'utf8').trim();
if (v) return v;
}
return config.appVersion;
return readProductVersion({ rootDir: PROJECT_ROOT }) || config.appVersion;
}
// ======================== Classify ======================================
@@ -1155,9 +1151,11 @@ async function buildGoServer(preferredGoBinPath = null) {
env: buildEnv
});
const productVersion = readProductVersion({ rootDir: PROJECT_ROOT });
const ldflags = `-s -w -X main.Version=${productVersion}`;
await spawnPromise(
goBin,
['build', '-trimpath', '-ldflags=-s -w', '-o', binaryName, '.'],
['build', '-trimpath', `-ldflags=${ldflags}`, '-o', binaryName, '.'],
{ cwd: serverDir, timeout: 600000, env: buildEnv }
);
+48
View File
@@ -0,0 +1,48 @@
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const { readProductVersion } = require('../lib/productVersion');
describe('productVersion', () => {
let tmpRoot;
beforeEach(() => {
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'bd-version-'));
});
afterEach(() => {
fs.rmSync(tmpRoot, { recursive: true, force: true });
});
test('reads semver from repo-root VERSION', () => {
fs.writeFileSync(path.join(tmpRoot, 'VERSION'), '4.1.2\n');
expect(readProductVersion({ rootDir: tmpRoot })).toBe('4.1.2');
});
test('falls back to package.json when VERSION is missing', () => {
const consoleDir = path.join(tmpRoot, 'web-nodejs');
fs.mkdirSync(consoleDir, { recursive: true });
fs.writeFileSync(
path.join(consoleDir, 'package.json'),
JSON.stringify({ version: '3.9.8' })
);
expect(readProductVersion({ rootDir: tmpRoot, consoleDir })).toBe('3.9.8');
});
test('prefers VERSION over package.json', () => {
const consoleDir = path.join(tmpRoot, 'web-nodejs');
fs.mkdirSync(consoleDir, { recursive: true });
fs.writeFileSync(path.join(tmpRoot, 'VERSION'), '2.0.1\n');
fs.writeFileSync(
path.join(consoleDir, 'package.json'),
JSON.stringify({ version: '9.9.9' })
);
expect(readProductVersion({ rootDir: tmpRoot, consoleDir })).toBe('2.0.1');
});
test('returns fallback when no sources available', () => {
expect(readProductVersion({ rootDir: tmpRoot, fallback: '0.0.0' })).toBe('0.0.0');
});
});
+7
View File
@@ -100,6 +100,13 @@
</span>
<span class="server-info-value">v${appVersion}</span>
</div>
<div class="server-info-item">
<span class="server-info-label">
<span class="material-icons">dns</span>
${_('settings.go_server_version')}
</span>
<span class="server-info-value" id="go-server-version">-</span>
</div>
<div class="server-info-item">
<span class="server-info-label">
<span class="material-icons">storage</span>