7 Commits

Author SHA1 Message Date
GL.iNet-Yongping.Xie 1c473458cf Merge remote-tracking branch 'origin/dev-ui-0105' into feature/version 2026-01-05 18:41:05 -08:00
GL.iNet-Yongping.Xie c6c09dbae1 feat: show version in GUI
Display the current application version in the GUI.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2026-01-05 18:40:03 -08:00
GL.iNet-Yongping.Xie 9258aa5f43 feat: show version in GUI
Display the current application version in the GUI.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2026-01-05 18:36:16 -08:00
pengyu.lu 8994cccb25 feat: Add the display of version numbers 2026-01-05 14:36:34 +08:00
GL.iNet-Yongping.Xie 60994b9513 fix: update documentation formatting
Update documentation formatting.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-12-29 00:22:03 -08:00
GL.iNet-Yongping.Xie c99b96ca01 Merge branch 'arm64' 2025-12-29 00:06:08 -08:00
GL.iNet-Yongping.Xie 27792291ed feat: add arm64 platform support
Add support for the arm64 platform and update the documentation
to reflect arm64-specific installation and configuration steps.

Signed-off-by: GL.iNet-Yongping.Xie <yongping.xie@gl-inet.com>
2025-12-29 00:05:49 -08:00
15 changed files with 248 additions and 51 deletions
+4 -1
View File
@@ -1,4 +1,7 @@
FROM alpine:latest
WORKDIR /home
COPY ./rttys /usr/bin/rttys
ARG TARGETARCH
COPY ./dist/rttys-linux-${TARGETARCH} /usr/bin/rttys
ENTRYPOINT ["/usr/bin/rttys"]
+84 -17
View File
@@ -1,29 +1,54 @@
# Makefile
# Go binary name
BINARY_NAME = rttys
# ---------------- Project ----------------
BINARY_NAME ?= rttys
UI_DIR ?= ui
CONF_FILE ?= ./rttys.conf
# Go build flags
BUILD_FLAGS := -ldflags "-s -w"
BUILD_FLAGS ?= -ldflags "-s -w"
# Go build command
# Output dir for cross builds
DIST_DIR ?= dist
# Image name
IMAGE_NAME ?= glkvm-cloud
IMAGE_TAG ?= build
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
GOOS ?= $(shell go env GOOS)
GOARCH ?= $(shell go env GOARCH)
# Map uname -m -> goarch
ifeq ($(UNAME_M),x86_64)
HOST_GOARCH := amd64
else ifeq ($(UNAME_M),aarch64)
HOST_GOARCH := arm64
else ifeq ($(UNAME_M),arm64)
HOST_GOARCH := arm64
else
HOST_GOARCH := $(GOARCH)
endif
# ---------------- Commands ----------------
GO_BUILD_CMD = go build $(BUILD_FLAGS) -o $(BINARY_NAME)
# Paths
UI_DIR = ui
CONF_FILE = ./rttys.conf
.PHONY: all ui build run build-all build-run full-run \
build-linux-amd64 build-linux-arm64 build-linux-all \
docker-build docker-fullbuild docker-buildx docker-buildx-full
.PHONY: all ui build run build-run full-run
all: build
# Build frontend files only
ui:
cd $(UI_DIR) && npm install && npm run build
# Build Go binary only
# Build for current env (native)
build:
CGO_ENABLED=0 $(GO_BUILD_CMD)
CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GO_BUILD_CMD)
# Run Go program only
# Run Go program only (native binary)
run:
./$(BINARY_NAME) -c $(CONF_FILE)
@@ -36,10 +61,52 @@ build-run: build run
# Build frontend, build Go binary, and run
full-run: ui build run
# Build Docker image without updating ui
docker-build: build
docker build -t glkvm-cloud:build .
# ---------------- Cross compile (Linux) ----------------
# Produce: dist/rttys-linux-amd64 , dist/rttys-linux-arm64
build-linux-amd64:
@mkdir -p $(DIST_DIR)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build $(BUILD_FLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-linux-amd64
# Full Build Docker image
build-linux-arm64:
@mkdir -p $(DIST_DIR)
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \
go build $(BUILD_FLAGS) -o $(DIST_DIR)/$(BINARY_NAME)-linux-arm64
build-linux-all: build-linux-amd64 build-linux-arm64
# ---------------- Docker (single-arch) ----------------
# Build Docker image using current host arch
docker-build: build
docker build -t $(IMAGE_NAME):$(IMAGE_TAG) .
# Full build Docker image
docker-fullbuild: ui build
docker build -t glkvm-cloud:build .
docker build -t $(IMAGE_NAME):$(IMAGE_TAG) .
# ---------------- Docker Buildx ----------------
# Multi-arch build
# Usage:
# make docker-buildx GOARCH=amd64 IMAGE_TAG=build-amd64
# make docker-buildx GOARCH=arm64 IMAGE_TAG=build-arm64
PLATFORMS ?= linux/amd64,linux/arm64
REGISTRY ?=
# If REGISTRY is set, tag becomes: REGISTRY/IMAGE_NAME:IMAGE_TAG
ifdef REGISTRY
IMAGE_REF := $(REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG)
else
IMAGE_REF := $(IMAGE_NAME):$(IMAGE_TAG)
endif
docker-buildx:
@docker buildx version >/dev/null 2>&1 || (echo "docker buildx not available" && exit 1)
@echo "==> buildx (load local image): $(IMAGE_REF) [linux/$(GOARCH)]"
docker buildx build \
--platform linux/$(GOARCH) \
-t $(IMAGE_REF) \
--load .
docker-buildx-full: ui
@$(MAKE) docker-buildx
+8 -1
View File
@@ -17,6 +17,9 @@ Self-Deployed Lightweight Cloud is a lightweight KVM remote cloud platform tailo
- **Lightweight Design** - Optimized for small businesses and individual users
- **Enterprise Authentication** - Supports both **LDAP** and **OIDC** login methods for enterprise users.
- **Deployment** - Supports both **internal network** and **public internet** deployments
- **Platform Compatibility** - Supports both **x86_64** and **arm64** platforms
## Self-Hosting Guide
The following mainstream operating systems have been tested and verified
@@ -61,9 +64,11 @@ If your server provider uses a **cloud security group** (e.g., AWS, Aliyun, etc.
We provide **two** ways to install GLKVM Cloud:
#### A) One-line installer (recommended)
#### A) One-line installer (recommended, x86_64/amd64)
> **Note:** The one-line installer is **Docker-based**. It automates Docker/Compose setup, pulls images, renders configs from templates, and starts services for you.
>
> **Platform:** currently supports **x86_64 (amd64)** only.
Run **as root**:
@@ -74,6 +79,8 @@ Run **as root**:
#### B) Docker manual install
> Full reference: see [`docker-compose/README.md`](https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/README.md)
>
> **Platform:** supports both **x86_64 (amd64)** and **arm64 (AArch64)**.
### 🌐 Platform Access
+8 -1
View File
@@ -18,6 +18,9 @@
* **轻量设计** - 专为小型企业和个人优化
* **企业级认证** - 同时支持 **LDAP** 和 **OIDC** 登录方式,适用于企业用户。
- **部署方式** - 同时支持 **内网部署** 和 **公网部署**
- **平台兼容性** - 同时支持 **x86_64** 和 **arm64** 平台
## 自部署指南
以下主流操作系统已通过测试验证:
@@ -61,9 +64,11 @@
我们提供 **两种** 安装 GLKVM Cloud 的方式:
#### A) 一键安装脚本(推荐)
#### A) 一键安装脚本(推荐,仅支持 x86_64 / amd64)
> **注意:** 一键安装脚本基于 **Docker**。它会自动完成 Docker / Docker Compose 的安装、拉取镜像、根据模板渲染配置文件,并启动所有服务。
>
> **平台支持:** 当前仅支持 **x86_64(amd64)** 平台。
使用 **root 权限** 运行以下命令安装 GLKVM 轻量云:
@@ -74,6 +79,8 @@
#### B) 使用 Docker 手动安装
> 完整参考文档请查看:[`docker-compose/README-CN.md`](https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/README-CN.md)
>
> 平台支持: 同时支持 x86_64(amd64) 与 arm64(AArch64) 平台。
### 🌐 平台访问
+4 -3
View File
@@ -432,9 +432,10 @@ func (srv *RttyServer) ListenAPI() error {
r.GET("/auth-config", func(c *gin.Context) {
authConfig := gin.H{
"ldapEnabled": cfg.LdapEnabled,
"legacyPassword": cfg.Password != "",
"oidcEnabled": cfg.OIDCEnabled,
"ldapEnabled": cfg.LdapEnabled,
"legacyPassword": cfg.Password != "",
"oidcEnabled": cfg.OIDCEnabled,
"kvmCloudVersion": KVMCloudVersion,
}
c.JSON(http.StatusOK, authConfig)
})
+83
View File
@@ -0,0 +1,83 @@
# Images
GLKVM_IMAGE=glzhitong/glkvm-cloud:latest-arm64
COTURN_IMAGE=coturn/coturn:edge-alpine-arm64v8
# Enable reverse proxy mode (e.g. Nginx in front of GLKVM Cloud).
# When enabled, TLS is handled by the proxy and GLKVM Cloud runs in plain HTTP.
#
# Note:
# In reverse-proxy mode, remote device access depends on the correct forwarded headers
# from the front-end proxy. If these headers are missing or incorrect, GLKVM Cloud may
# generate redirect URLs with the internal port (e.g. :10443).
#
# Please make sure your Nginx config includes:
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-Host $host;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Port $server_port;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#
# Reference (verified working example):
# https://github.com/gl-inet/glkvm-cloud/blob/main/docker-compose/nginx-reverse-proxy-example.conf
REVERSE_PROXY_ENABLED=false
# GLKVM access IP seen by devices/users.
# Leave empty to auto-detect at container start.
GLKVM_ACCESS_IP=
# rttys
RTTYS_TOKEN=DeviceTokenYouCanChangeMe
RTTYS_PASS=StrongP@ssw0rd
RTTYS_DEVICE_PORT=5912
RTTYS_WEBUI_PORT=443
RTTYS_HTTP_PROXY_PORT=10443
# TURN
TURN_PORT=3478
TURN_USER=glkvmcloudwebrtcuser
TURN_PASS=AnotherS3cret
# LDAP Authentication (Optional)
LDAP_ENABLED=false
LDAP_SERVER=your-ldap-server.com
LDAP_PORT=389
LDAP_USE_TLS=false
LDAP_BIND_DN=cn=service-account,ou=users,dc=company,dc=com
LDAP_BIND_PASSWORD=service-password
LDAP_BASE_DN=ou=users,dc=company,dc=com
# User filter examples for different LDAP implementations:
# Active Directory: (&(objectClass=person)(sAMAccountName=%s))
# OpenLDAP: (&(objectClass=inetOrgPerson)(uid=%s))
# FreeIPA: (&(objectClass=person)(uid=%s))
# Generic LDAP: (uid=%s)
LDAP_USER_FILTER=(uid=%s)
LDAP_ALLOWED_GROUPS=admins,operators
LDAP_ALLOWED_USERS=user1,user2
# OIDC Authentication (Optional, generic OIDC provider)
OIDC_ENABLED=false
OIDC_ISSUER=
OIDC_CLIENT_ID=
OIDC_CLIENT_SECRET=
OIDC_AUTH_URL=
OIDC_TOKEN_URL=
# Redirect URL registered in your OIDC provider.
# The path part (/auth/oidc/callback) is fixed by GLKVM Cloud and must not be changed.
# Example:
# OIDC_REDIRECT_URL=https://your-domain.example.com/auth/oidc/callback
OIDC_REDIRECT_URL=
OIDC_SCOPES="openid profile email"
# Email-based whitelist (exact email or domain like @example.com)
OIDC_ALLOWED_USERS=
# Subject (sub) whitelist (stable user IDs)
OIDC_ALLOWED_SUBS=
# Username whitelist (preferred_username or name)
OIDC_ALLOWED_USERNAMES=
# Groups whitelist (e.g. admin, devops)
OIDC_ALLOWED_GROUPS=
+11 -1
View File
@@ -7,9 +7,19 @@
```bash
git clone https://github.com/gl-inet/glkvm-cloud.git
cd glkvm-cloud/docker-compose/
cp .env.example .env
```
* **x86_64(amd64)平台**:
```bash
cp .env.example .env
```
* **arm64(AArch64)平台**:
```bash
cp .env.arm64.example .env
```
### 2. **配置环境变量**
编辑 `.env` 文件,并根据需求更新关键参数:
+9 -1
View File
@@ -7,8 +7,16 @@
```bash
git clone https://github.com/gl-inet/glkvm-cloud.git
cd glkvm-cloud/docker-compose/
cp .env.example .env
```
* For **x86_64 (amd64)**:
```bash
cp .env.example .env
```
* For **arm64 (AArch64)**:
```bash
cp .env.arm64.example .env
```
2. **Configure environment variables**
+3 -8
View File
@@ -5,6 +5,7 @@ services:
image: ${GLKVM_IMAGE:-glzhitong/glkvm-cloud:latest}
container_name: glkvm_cloud
restart: always
network_mode: "host"
environment:
# Preferred: set GLKVM_ACCESS_IP explicitly; if empty, entrypoint will auto-detect once.
GLKVM_ACCESS_IP: ${GLKVM_ACCESS_IP:-}
@@ -60,15 +61,12 @@ services:
- ./database:/home/database:rw
entrypoint: ["/bin/sh", "/docker-entrypoint.sh"]
command: ["rttys"]
ports:
- "${RTTYS_WEBUI_PORT:-443}:${RTTYS_WEBUI_PORT:-443}"
- "${RTTYS_HTTP_PROXY_PORT:-10443}:${RTTYS_HTTP_PROXY_PORT:-10443}"
- "${RTTYS_DEVICE_PORT:-5912}:${RTTYS_DEVICE_PORT:-5912}"
coturn:
image: ${COTURN_IMAGE:-coturn/coturn:edge-alpine}
container_name: glkvm_coturn
restart: always
network_mode: "host"
environment:
# Same semantics as above: prefer explicit value, else auto-detect
GLKVM_ACCESS_IP: ${GLKVM_ACCESS_IP:-}
@@ -79,7 +77,4 @@ services:
command: ["coturn"]
volumes:
- ./templates/turnserver.conf.template:/tpl/turnserver.conf.tmpl:ro
- ./scripts/docker-entrypoint.sh:/docker-entrypoint.sh:ro
ports:
- "${TURN_PORT:-3478}:3478/tcp"
- "${TURN_PORT:-3478}:3478/udp"
- ./scripts/docker-entrypoint.sh:/docker-entrypoint.sh:ro
+1
View File
@@ -41,6 +41,7 @@ import (
)
const RttysVersion = "5.2.0"
const KVMCloudVersion = "v1.6.0"
var (
GitCommit = ""
+10 -9
View File
@@ -2,8 +2,8 @@
* @Author: LPY
* @Date: 2025-06-09 09:29:48
* @LastEditors: LPY
* @LastEditTime: 2025-07-21 10:04:25
* @FilePath: /kvm-cloud-frontend/src/components/base/baseWhitePage.vue
* @LastEditTime: 2026-01-05 14:30:56
* @FilePath: \glkvm-cloud\ui\src\components\base\baseWhitePage.vue
* @Description: 基础白名单页。
-->
<template>
@@ -13,6 +13,7 @@
<img src="@/assets/svg/logo.svg" height="20">
</div>
<div class="base-white-page-header-right">
<BaseText style="margin-right: 24px;">{{ appStore.state.version || '--' }}</BaseText>
<BaseDropdownSelect :value="currentLang" :options="languageOptions" @update:value="changeLang">
<div class="language-box flex">
<BaseSvg name="gl-icon-language-regular" style="margin-right: 8px;font-size: 16px;"></BaseSvg>
@@ -25,13 +26,13 @@
<div class="base-white-page-footer">
<!-- 步骤条 -->
<div v-if="route.query.bindToken" class="base-white-page-step">
<!-- <div v-if="route.query.bindToken" class="base-white-page-step">
<BaseStep
v-model:value="useUserStore().bindingStep"
:items="[{title: $t('login.accountSetup')}, {title: $t('login.deviceSetup')}]"
titlePosition="bottom"
/>
</div>
</div> -->
<div class="base-white-page-content">
<slot></slot>
@@ -54,19 +55,17 @@
<script setup lang="ts">
import useLanguage from '@/hooks/useLanguage'
import { languageOptions, Languages } from 'gl-web-main'
import BaseStep from './baseStep.vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/stores/modules/user'
import { BaseDropdownSelect } from 'gl-web-main/components'
import { isForeignEnv } from '@/utils'
const route = useRoute()
import { useAppStore } from '@/stores/modules/app'
const { currentLang, currentLangLabel } = useLanguage()
const changeLang = (key: Languages) => {
useLanguage().setLanguage(key)
}
const appStore = useAppStore()
</script>
<style scoped lang="scss">
@@ -85,6 +84,8 @@ const changeLang = (key: Languages) => {
border-bottom: 1px solid var(--gl-color-line-divider1);
.base-white-page-header-right {
display: flex;
align-items: center;
.language-box {
color: var(--gl-color-text-level2);
user-select: none;
+4 -2
View File
@@ -2,8 +2,8 @@
* @Author: LPY
* @Date: 2025-05-30 10:18:18
* @LastEditors: LPY
* @LastEditTime: 2025-06-25 10:03:27
* @FilePath: /kvm-cloud-frontend/src/hooks/useLocalStorage.ts
* @LastEditTime: 2026-01-05 14:22:12
* @FilePath: \glkvm-cloud\ui\src\hooks\useLocalStorage.ts
* @Description: 存储hook
*/
import { ref } from 'vue'
@@ -18,6 +18,8 @@ export enum LocalStorageKeys {
TWO_FACTOR_INFO_KEY = 'two-factor-info',
/** 侧边栏手动控制展开收缩状态 */
SIDEBAR_MANUAL_CONTROL_KEY = 'sidebar-manual-control',
/** 版本号 */
VERSION = 'version',
}
/**
+10 -3
View File
@@ -2,8 +2,8 @@
* @Author: LPY
* @Date: 2025-05-30 09:37:06
* @LastEditors: LPY
* @LastEditTime: 2025-06-13 15:55:51
* @FilePath: /kvm-cloud-frontend/src/stores/modules/app.ts
* @LastEditTime: 2026-01-05 14:35:02
* @FilePath: \glkvm-cloud\ui\src\stores\modules\app.ts
* @Description: app相关状态存储
*/
import { LocalStorageKeys, useLocalStorage } from '@/hooks/useLocalStorage'
@@ -24,6 +24,8 @@ export const useAppStore = defineStore('appGlobal', () => {
const state = reactive({
/** 当前的主题模式 */
themeMode: getThemeFromStorage(),
/** 版本号 */
version: (useLocalStorage(LocalStorageKeys.VERSION).getValue() as string)?.toUpperCase() || 'V1.0.0',
})
/** 获取主题模式 */
@@ -114,6 +116,11 @@ export const useAppStore = defineStore('appGlobal', () => {
sidebar.manualSetting = false
}
/** 设置版本号 */
const setVersion = (version: string) =>{
state.version = version.toUpperCase()
useLocalStorage(LocalStorageKeys.VERSION).setValue(version)
}
return { antdTheme, setThemeMode, state, sidebar, isCollapse, manualToggleSidebar, autoCloseSidebar, autoOpenSidebar, resetManualSetting }
return { antdTheme, setThemeMode, state, sidebar, isCollapse, manualToggleSidebar, autoCloseSidebar, autoOpenSidebar, resetManualSetting, setVersion }
})
+6 -2
View File
@@ -2,8 +2,8 @@
* @Author: LPY
* @Date: 2025-05-30 15:21:14
* @LastEditors: LPY
* @LastEditTime: 2025-08-26 17:57:09
* @FilePath: \glkvm-cloud\web-ui\src\views\layout\layHeader\layHeader.vue
* @LastEditTime: 2026-01-05 14:32:56
* @FilePath: \glkvm-cloud\ui\src\views\layout\layHeader\layHeader.vue
* @Description: 顶部集成页
-->
<template>
@@ -12,6 +12,8 @@
<img src="@/assets/svg/logo.svg" height="20">
</div>
<div class="lay-header-right">
<!-- version -->
<BaseText style="margin-right: 24px;">{{ appStore.state.version || '--' }}</BaseText>
<!-- github -->
<ATooltip>
<template #title>{{ githubLink }}</template>
@@ -38,9 +40,11 @@
</template>
<script setup lang="ts">
import { useAppStore } from '@/stores/modules/app'
import { useUserStore } from '@/stores/modules/user'
const userStore = useUserStore()
const appStore = useAppStore()
// github链接
const githubLink = 'https://github.com/gl-inet/glkvm-cloud'
+3 -2
View File
@@ -2,7 +2,7 @@
* @Author: LPY
* @Date: 2025-05-30 10:48:43
* @LastEditors: LPY
* @LastEditTime: 2025-11-12 17:01:59
* @LastEditTime: 2026-01-05 14:25:45
* @FilePath: \glkvm-cloud\ui\src\views\login\loginPage.vue
* @Description: 登录页面
-->
@@ -103,6 +103,7 @@ import { useRouter } from 'vue-router'
import { LoginParams, AuthConfig } from '@/models/user'
import { message, Input, Form } from 'ant-design-vue'
import { reqAuthConfig } from '@/api/user'
import { useAppStore } from '@/stores/modules/app'
const AInput = Input
const AForm = Form
@@ -153,7 +154,7 @@ onMounted(async () => {
// 提取配置数据 (Extract config data)
const configData = response?.info || response?.data?.info || response?.data || response
authConfig.value = configData
useAppStore().setVersion(configData.kvmCloudVersion)
} catch (error) {
console.error('Failed to load auth config:', error)
// 回退 - 无LDAP可用 (Fallback - no LDAP available)