mirror of
https://github.com/gl-inet/glkvm-cloud.git
synced 2026-10-04 12:41:42 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e9e5bb93a | |||
| 3c6c214479 |
@@ -35,6 +35,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -44,7 +45,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/valyala/bytebufferpool"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
type HttpProxySession struct {
|
||||
@@ -346,13 +346,8 @@ func httpProxyRedirect(srv *RttyServer, c *gin.Context, group string) {
|
||||
location = fmt.Sprintf("https://%s%s?sid=%s", hostname, cfg.AddrHttpProxy, sid)
|
||||
log.Info().Msgf("Using IP redirect: %s", location)
|
||||
} else {
|
||||
// 域名访问,拼接 devid 子域名
|
||||
eTLDPlusOne, err := publicsuffix.EffectiveTLDPlusOne(hostname)
|
||||
if err != nil {
|
||||
log.Info().Msgf("Error parsing domain: %v", err)
|
||||
eTLDPlusOne = hostname // fallback
|
||||
}
|
||||
location = fmt.Sprintf("https://%s.%s%s?sid=%s", devid, eTLDPlusOne, cfg.AddrHttpProxy, sid)
|
||||
redirHost := buildRedirectHost(hostname, devid)
|
||||
location = fmt.Sprintf("https://%s%s?sid=%s", redirHost, cfg.AddrHttpProxy, sid)
|
||||
log.Info().Msgf("Using domain redirect: %s", location)
|
||||
}
|
||||
|
||||
@@ -624,3 +619,40 @@ func Write302WithCookie(conn net.Conn, location, cookieName, cookieValue string)
|
||||
)
|
||||
_, _ = conn.Write([]byte(response))
|
||||
}
|
||||
|
||||
// buildRedirectHost removes the first label of the hostname and prepends devid.
|
||||
// Rules:
|
||||
// - "www.example.com" -> "devid.example.com"
|
||||
// - "www.l1.example.com" -> "devid.l1.example.com"
|
||||
// - "www.l1.l2.example.com" -> "devid.l1.l2.example.com"
|
||||
// - Two-level domain "example.com" -> "devid.example.com"
|
||||
// - Single label / abnormal cases -> "devid." + hostname (fallback)
|
||||
//
|
||||
// The input hostname must be a pure hostname without port.
|
||||
func buildRedirectHost(hostname, devid string) string {
|
||||
// Allow FQDN with trailing dot like "example.com."
|
||||
hostname = strings.TrimSuffix(hostname, ".")
|
||||
|
||||
// Split into labels
|
||||
labels := strings.Split(hostname, ".")
|
||||
// Remove empty labels (in case of consecutive dots)
|
||||
compact := make([]string, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
if l != "" {
|
||||
compact = append(compact, l)
|
||||
}
|
||||
}
|
||||
labels = compact
|
||||
|
||||
switch len(labels) {
|
||||
case 0:
|
||||
return devid // extreme case: just return devid
|
||||
case 1:
|
||||
// Single label (e.g., "localhost") — keep original as suffix
|
||||
return devid + "." + labels[0]
|
||||
default:
|
||||
// >=2: drop the leftmost label
|
||||
suffix := strings.Join(labels[1:], ".")
|
||||
return devid + "." + suffix
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import type { BaseButton, BaseText } from '@gl/main/components'
|
||||
import type { BaseButton, BaseText } from 'gl-web-main/components'
|
||||
import type BaseSvg from './src/components/base/baseSvg.vue'
|
||||
|
||||
declare module 'vue' {
|
||||
|
||||
+1
-1
@@ -18,7 +18,6 @@
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gl/main": "0.0.50",
|
||||
"@vue/eslint-config-typescript": "12.0.0",
|
||||
"@xterm/addon-fit": "0.10.0",
|
||||
"@xterm/addon-web-links": "0.11.0",
|
||||
@@ -26,6 +25,7 @@
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"axios": "^1.9.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"gl-web-main": "^1.0.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"pinia": "^3.0.2",
|
||||
|
||||
+2
-2
@@ -27,8 +27,8 @@ import { computed } from 'vue'
|
||||
import { useAppStore } from './stores/modules/app'
|
||||
import { RouterView } from 'vue-router'
|
||||
import type { ThemeConfig } from 'ant-design-vue/es/config-provider/context'
|
||||
import { ConfigProvider as GlConfigProvider } from '@gl/main/components'
|
||||
import { Languages } from '@gl/main'
|
||||
import { ConfigProvider as GlConfigProvider } from 'gl-web-main/components'
|
||||
import { Languages } from 'gl-web-main'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { NotNeedHandledRequestErrorCodeList, RequestErrorCodeEnum } from '@/models/request'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import { showErrorMessage } from './requestError'
|
||||
import { BaseResponse, HttpService } from '@gl/main'
|
||||
import { BaseResponse, HttpService } from 'gl-web-main'
|
||||
import type { AxiosResponse } from 'axios'
|
||||
|
||||
export const httpService = new HttpService(
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" generic="T">
|
||||
import type { SelectOptions } from '@gl/main'
|
||||
import { BaseDivider, BaseDropdownSelect } from '@gl/main/components'
|
||||
import type { SelectOptions } from 'gl-web-main'
|
||||
import { BaseDivider, BaseDropdownSelect } from 'gl-web-main/components'
|
||||
import type { Trigger } from 'ant-design-vue/es/dropdown/props'
|
||||
import { computed } from 'vue'
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
|
||||
<script setup lang="ts" generic="T extends AnyObject">
|
||||
import type { DropdownGroupItem } from '@/models/component'
|
||||
import type { AnyObject } from '@gl/main'
|
||||
import { BaseDivider } from '@gl/main/components'
|
||||
import type { AnyObject } from 'gl-web-main'
|
||||
import { BaseDivider } from 'gl-web-main/components'
|
||||
import { Dropdown, Menu, Radio, RadioGroup } from 'ant-design-vue'
|
||||
import type { Trigger } from 'ant-design-vue/es/dropdown/props'
|
||||
import { computed, reactive, watch } from 'vue'
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { debounce } from '@gl/main'
|
||||
import { debounce } from 'gl-web-main'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<script setup lang="ts" generic="T extends BaseData" >
|
||||
import type { BaseTableProps } from '@/models/component'
|
||||
import { isEmpty, type BaseData } from '@gl/main'
|
||||
import { isEmpty, type BaseData } from 'gl-web-main'
|
||||
import { Table } from 'ant-design-vue'
|
||||
import { useSlots } from 'vue'
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ViewType } from '@gl/main'
|
||||
import { ViewType } from 'gl-web-main'
|
||||
import { Tooltip } from 'ant-design-vue'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
||||
@@ -53,11 +53,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import useLanguage from '@/hooks/useLanguage'
|
||||
import { languageOptions, Languages } from '@gl/main'
|
||||
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/main/components'
|
||||
import { BaseDropdownSelect } from 'gl-web-main/components'
|
||||
import { isForeignEnv } from '@/utils'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @FilePath: \kvm-cloud-frontend\src\hooks\useDeviceQueue.ts
|
||||
* @Description: 自动发现设备队列
|
||||
*/
|
||||
import { AnyObject, deepClone, isEmpty } from '@gl/main'
|
||||
import { AnyObject, deepClone, isEmpty } from 'gl-web-main'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
const DEFAULT_QUEUE_INTERVAL = 400
|
||||
|
||||
@@ -11,7 +11,7 @@ import i18n from '@/lang'
|
||||
import { languageLabelMap } from '@/models/setting'
|
||||
import { computed } from 'vue'
|
||||
import { LocalStorageKeys, useLocalStorage } from './useLocalStorage'
|
||||
import { Languages } from '@gl/main'
|
||||
import { Languages } from 'gl-web-main'
|
||||
/** 语言hook */
|
||||
export default function useLanguage () {
|
||||
// @ts-ignore
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @FilePath: \gl-cloud-frontend\src\hooks\usePageLink.js
|
||||
* @Description: 用于处理分页相关的逻辑
|
||||
*/
|
||||
import type { AnyObject, TableDataResponse } from '@gl/main'
|
||||
import type { AnyObject, TableDataResponse } from 'gl-web-main'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export const GLOBAL_PAGE_SIZE = 'GLOBAL_PAGE_SIZE'
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
import { computed } from 'vue'
|
||||
import useLanguage from './useLanguage'
|
||||
import type { SelectOptions } from '@gl/main'
|
||||
import type { SelectOptions } from 'gl-web-main'
|
||||
|
||||
export const useTranslatedOptions = <T>(options: SelectOptions<T>[]) => {
|
||||
const { t } = useLanguage()
|
||||
|
||||
@@ -10,7 +10,7 @@ import { createI18n } from 'vue-i18n'
|
||||
import zh from './locales/zh.json'
|
||||
import en from './locales/en.json'
|
||||
import useLanguage from '@/hooks/useLanguage'
|
||||
import { Languages } from '@gl/main'
|
||||
import { Languages } from 'gl-web-main'
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import '@gl/main/style.css'
|
||||
import 'gl-web-main/style.css'
|
||||
import '@/styles/index.scss'
|
||||
import App from './App.vue'
|
||||
import projectInitialize from './projectInitialize'
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @FilePath: \kvm-cloud-frontend\src\models\component.ts
|
||||
* @Description: 组件有关的models
|
||||
*/
|
||||
import type { SelectOptions } from '@gl/main'
|
||||
import type { SelectOptions } from 'gl-web-main'
|
||||
import type { TableProps } from 'ant-design-vue'
|
||||
|
||||
export interface DropdownGroupItem<T = any> {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @Description: 设置相关类型声明
|
||||
*/
|
||||
|
||||
import { Languages, SelectOptions } from '@gl/main'
|
||||
import { Languages, SelectOptions } from 'gl-web-main'
|
||||
|
||||
/** 语言对应的label映射 */
|
||||
export const languageLabelMap = new Map<Languages, string>([
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { App } from 'vue'
|
||||
|
||||
import '@/assets/iconfont/iconfont.js'
|
||||
import BaseSvg from '@/components/base/baseSvg.vue'
|
||||
import { BaseButton, BaseText } from '@gl/main/components'
|
||||
import { BaseButton, BaseText } from 'gl-web-main/components'
|
||||
|
||||
/** 全局注册自定义组件 */
|
||||
const installComponent = function (app: App) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @Description: app相关状态存储
|
||||
*/
|
||||
import { LocalStorageKeys, useLocalStorage } from '@/hooks/useLocalStorage'
|
||||
import { baseTheme, createStyleInsert, darkTheme, replaceAntTheme, ThemeMode } from '@gl/main'
|
||||
import { baseTheme, createStyleInsert, darkTheme, replaceAntTheme, ThemeMode } from 'gl-web-main'
|
||||
import type { ThemeConfig } from 'ant-design-vue/es/config-provider/context'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive } from 'vue'
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
import { getDeviceListApi } from '@/api/device'
|
||||
import { type DeviceInfo, type DeviceQuery } from '@/models/device'
|
||||
import { PageLink } from '@gl/main'
|
||||
import { PageLink } from 'gl-web-main'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { t } from '@/hooks/useLanguage'
|
||||
import { glConfirm, type BaseConfirmProps } from '@gl/main'
|
||||
import { glConfirm, type BaseConfirmProps } from 'gl-web-main'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'
|
||||
import { AxiosError } from 'axios'
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch } from 'vue'
|
||||
import { BaseModal } from '@gl/main/components'
|
||||
import { BaseModal } from 'gl-web-main/components'
|
||||
import { getAddDeviceScriptInfoApi } from '@/api/device'
|
||||
import { copyText } from '@gl/main'
|
||||
import { copyText } from 'gl-web-main'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { t } from '@/hooks/useLanguage'
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { BaseModal } from '@gl/main/components'
|
||||
import { BaseModal } from 'gl-web-main/components'
|
||||
|
||||
const props = defineProps<{ open: boolean, type: string, res: any }>()
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ import { reqExecuteCommand } from '@/api/device'
|
||||
import BaseTable from '@/components/base/baseTable.vue'
|
||||
import { t } from '@/hooks/useLanguage'
|
||||
import { DeviceInfo, ExecuteCommandFormData } from '@/models/device'
|
||||
import { useFakeUpgradeProgress } from '@gl/main'
|
||||
import { BaseModal } from '@gl/main/components'
|
||||
import { useFakeUpgradeProgress } from 'gl-web-main'
|
||||
import { BaseModal } from 'gl-web-main/components'
|
||||
import { TableColumnType } from 'ant-design-vue'
|
||||
import { computed, reactive, watch } from 'vue'
|
||||
import CmdResDetailDialog from './cmdResDetailDialog.vue'
|
||||
|
||||
@@ -64,8 +64,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { BaseModal } from '@gl/main/components'
|
||||
import { FormRules, OnBeforeOk, useValidateInfo } from '@gl/main'
|
||||
import { BaseModal } from 'gl-web-main/components'
|
||||
import { FormRules, OnBeforeOk, useValidateInfo } from 'gl-web-main'
|
||||
import { t } from '@/hooks/useLanguage'
|
||||
import { DeviceInfo, ExecuteCommandFormData } from '@/models/device'
|
||||
import { FormInstance } from 'ant-design-vue'
|
||||
|
||||
@@ -44,7 +44,7 @@ import '@xterm/xterm/css/xterm.css'
|
||||
import OverlayAddon from './components/xterm-addon-overlay'
|
||||
import ContextMenu from './components/contextMenu.vue'
|
||||
import RttyKeyboard from './components/rttyKeyboard.vue'
|
||||
import { BaseModal } from '@gl/main/components'
|
||||
import { BaseModal } from 'gl-web-main/components'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import { t } from '@/hooks/useLanguage'
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import NoDevicePage from './components/noDevicePage.vue'
|
||||
import { useDeviceStore } from '@/stores/modules/device'
|
||||
import DeviceListView from './components/deviceListView.vue'
|
||||
import { reactive, onBeforeUnmount, onMounted } from 'vue'
|
||||
import { BaseLoading } from '@gl/main/components'
|
||||
import { BaseLoading } from 'gl-web-main/components'
|
||||
import AddDeviceDialog from './components/addDeviceDialog.vue'
|
||||
|
||||
const deviceStore = useDeviceStore()
|
||||
|
||||
@@ -44,8 +44,8 @@ import LoginBox from './components/loginBox.vue'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { t } from '@/hooks/useLanguage'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import { useValidateInfo, type FormRules } from '@gl/main'
|
||||
import { GlPassword } from '@gl/main/components'
|
||||
import { useValidateInfo, type FormRules } from 'gl-web-main'
|
||||
import { GlPassword } from 'gl-web-main/components'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { LoginParams } from '@/models/user'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
+8
-8
@@ -23,35 +23,35 @@ export default defineConfig(({ mode }) => {
|
||||
port: 3011,
|
||||
proxy: {
|
||||
'/devs': {
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
secure: false,
|
||||
},
|
||||
'/signin': {
|
||||
target: 'https://49.7.174.146:1443/',
|
||||
target: 'https://106.55.158.199',
|
||||
secure: false,
|
||||
},
|
||||
'/signout': {
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
secure: false,
|
||||
},
|
||||
'/alive': {
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
secure: false,
|
||||
},
|
||||
'/get': {
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
secure: false,
|
||||
},
|
||||
'^/cmd/.*': {
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
secure: false,
|
||||
},
|
||||
'^/connect/.*': {
|
||||
ws: true,
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
},
|
||||
'^/web/*': {
|
||||
target: 'https://49.7.174.146:1443',
|
||||
target: 'https://106.55.158.199',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+553
-529
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user