feat: add region switch proxy (#5532)

* fix: launchpad tcp value & switch region

update swithch region

update

add cf worker

update

update

update .lintstagedrc

* delete log

* update

* update name

* update launchpad

* update page
This commit is contained in:
jingyang
2025-04-17 10:45:35 +08:00
committed by GitHub
parent e33104d21b
commit 4c4e112b9e
11 changed files with 244 additions and 8 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
{
"extends": "next/core-web-vitals"
}
}
@@ -12,10 +12,12 @@ import { useRouter } from 'next/router';
import { useMemo } from 'react';
import { DesktopExchangeIcon, ZoneIcon } from '../icons';
import { I18nCloudProvidersKey } from '@/types/i18next';
import { useConfigStore } from '@/stores/config';
export default function RegionToggle() {
const disclosure = useDisclosure();
const { setWorkSpaceId, session } = useSessionStore();
const { cloudConfig } = useConfigStore();
const { t, i18n } = useTranslation();
const router = useRouter();
const { data } = useQuery(['regionlist'], getRegionList, {
@@ -36,11 +38,17 @@ export default function RegionToggle() {
const handleCick = async (region: Region) => {
setWorkSpaceId(session?.user?.ns_uid || '');
const target = new URL(`https://${region.domain}/switchRegion`);
const target = new URL(
cloudConfig?.proxyDomain
? `https://${cloudConfig?.proxyDomain}/switchRegion`
: `https://${region.domain}/switchRegion`
);
const res = await request.get<any, ApiResp<{ token: string }>>('/api/auth/globalToken');
const token = res?.data?.token;
if (!token) return;
target.searchParams.append('token', token);
target.searchParams.append('regionId', region.uid);
target.searchParams.append('regionDomain', region.domain);
await router.replace(target);
};
@@ -16,7 +16,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
function genResCloudConfig(conf: CloudConfigType): CloudConfigType {
return {
domain: conf.domain,
port: conf.port
port: conf.port,
proxyDomain: conf.proxyDomain
} as CloudConfigType;
}
+3 -1
View File
@@ -5,6 +5,7 @@ export type CloudConfigType = {
port: string;
regionUID: string;
certSecretName: string;
proxyDomain: string;
};
export type CommonConfigType = {
@@ -254,7 +255,8 @@ export const DefaultCloudConfig: CloudConfigType = {
domain: 'cloud.sealos.io',
port: '443',
regionUID: 'sealos-cloud',
certSecretName: 'wildcard-cert'
certSecretName: 'wildcard-cert',
proxyDomain: 'cloud.sealos.io'
};
export const DefaultLayoutConfig: LayoutConfigType = {
@@ -45,7 +45,7 @@ const AppMainInfo = ({ app = MOCK_APP_DETAIL }: { app: AppDetailType }) => {
inline: `${protocol?.inline}${app.appName}.${getUserNamespace()}.svc.cluster.local:${
network.port
}`,
public: `${protocol?.label}${network.domain}${
public: `${protocol?.label}${protocol?.value.toLowerCase()}.${network.domain}${
network?.nodePort ? `:${network.nodePort}` : ''
}`,
showReadyStatus: false
@@ -97,6 +97,7 @@ const Form = ({
control,
name: 'networks'
});
const { fields: envs, replace: replaceEnvs } = useFieldArray({
control,
name: 'envs'
@@ -970,8 +971,12 @@ const Form = ({
? network.customDomain
: network.openNodePort
? network?.nodePort
? `${network.domain}:${network.nodePort}`
: `${network.domain}:${t('pending_to_allocated')}`
? `${network.protocol.toLowerCase()}.${network.domain}:${
network.nodePort
}`
: `${network.protocol.toLowerCase()}.${network.domain}:${t(
'pending_to_allocated'
)}`
: `${network.publicDomain}.${network.domain}`}
</Box>
@@ -0,0 +1,4 @@
FROM nginx:alpine
COPY ./index.html /usr/share/nginx/html/index.html
COPY ./nginx.conf /etc/nginx/nginx.conf
@@ -0,0 +1,87 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: desktop-switch-region
namespace: sealos
spec:
replicas: 1
revisionHistoryLimit: 1
selector:
matchLabels:
app: desktop-switch-region
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
labels:
app: desktop-switch-region
spec:
automountServiceAccountToken: false
containers:
- name: desktop-switch-region
image: ghcr.io/labring/sealos-desktop-switch-region:latest
env: []
resources:
requests:
cpu: 20m
memory: 25Mi
limits:
cpu: 200m
memory: 256Mi
ports:
- containerPort: 80
name: pwihfnbsxdbv
imagePullPolicy: Always
volumeMounts: []
volumes: []
---
apiVersion: v1
kind: Service
metadata:
name: desktop-switch-region
namespace: sealos
spec:
ports:
- port: 80
targetPort: 80
name: pwihfnbsxdbv
protocol: TCP
selector:
app: desktop-switch-region
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: desktop-switch-region
namespace: sealos
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/proxy-body-size: 32m
nginx.ingress.kubernetes.io/ssl-redirect: 'false'
nginx.ingress.kubernetes.io/backend-protocol: HTTP
nginx.ingress.kubernetes.io/client-body-buffer-size: 64k
nginx.ingress.kubernetes.io/proxy-buffer-size: 64k
nginx.ingress.kubernetes.io/proxy-send-timeout: '300'
nginx.ingress.kubernetes.io/proxy-read-timeout: '300'
nginx.ingress.kubernetes.io/server-snippet: |
client_header_buffer_size 64k;
large_client_header_buffers 4 128k;
spec:
rules:
- host: cloud.sealos.run
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: desktop-switch-region
port:
number: 80
tls:
- hosts:
- cloud.sealos.run
secretName: wildcard-cert
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Region Switching</title>
</head>
<body>
<script>
(function () {
const DEFAULT_REGION = 'hzh.sealos.run'
const REGION_MAP = {
xxx: 'xxx'
}
function getUrlParam (name) {
const url = new URL(window.location.href)
return url.searchParams.get(name)
}
function setLocalStorage (name, value) {
localStorage.setItem(name, value)
}
function getLocalStorage (name) {
return localStorage.getItem(name)
}
function validateRegion (regionId, regionDomain) {
if (!regionId || !regionDomain) return false
return REGION_MAP[regionId] === regionDomain
}
function buildTargetUrl (targetRegion, pathname, url) {
let targetUrl = `https://${targetRegion}${pathname}`
const params = url.search
if (params) {
targetUrl += params
}
return targetUrl
}
function redirect () {
const url = new URL(window.location.href)
const regionId = getUrlParam('regionId')
const regionDomain = getUrlParam('regionDomain')
const isValid = validateRegion(regionId, regionDomain)
if (isValid) {
setLocalStorage('latest_region', regionDomain)
setLocalStorage('latest_region_id', regionId)
const targetRegion = regionDomain
const targetUrl = buildTargetUrl(targetRegion, url.pathname, url)
window.location.href = targetUrl
} else {
const savedRegionId = getLocalStorage('latest_region_id')
const savedRegion = getLocalStorage('latest_region')
const isSavedValid = savedRegionId && savedRegion && REGION_MAP[savedRegionId] === savedRegion
const targetRegion = isSavedValid ? savedRegion : DEFAULT_REGION
const targetUrl = buildTargetUrl(targetRegion, url.pathname, url)
window.location.href = targetUrl
}
}
redirect()
})();
</script>
</body>
</html>
@@ -0,0 +1,49 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires max;
add_header Cache-Control "public, max-age=31536000";
log_not_found off;
}
}
}
@@ -46,7 +46,7 @@ function Terminal({ url, site }: { url: string; site: string }) {
);
iframeRef.current?.contentWindow?.postMessage(
{
command: `echo -e "\\e[A\\e[K 👉 Switched to namespace \\e[1;4;32m${nsid}\\e[0m" && history -c`
command: `echo -e "This is a temporary terminal for debugging and testing. The session will automatically end 30 minutes after you leave.\\n\\nYou are now working in: namespace ${nsid}" && history -c`
},
url
);