refactor: restore symlink-based xpack integration (#12473)

This commit is contained in:
ssongliu
2026-04-13 10:34:59 +08:00
committed by GitHub
parent 6ea07421ac
commit 4d06f444ac
33 changed files with 420 additions and 136 deletions

12
.gitignore vendored
View File

@@ -41,20 +41,10 @@ core/cmd/server/web/index.html
frontend/auto-imports.d.ts
frontend/components.d.ts
frontend/src/xpack
frontend/src/xpack-ee
agent/xpack
agent/router/entry_xpack.go
agent/server/init_xpack.go
agent/utils/xpack/xpack.go
agent/utils/xpack/xpack_xpack.go
core/xpack
core/router/entry_xpack.go
core/server/init_xpack.go
core/utils/xpack/xpack.go
core/utils/xpack/xpack_xpack.go
core/xpack-ee
core/router/entry_xpackee.go
core/server/init_xpackee.go
core/utils/xpack/xpack_xpackee.go
.history/
dist/

View File

@@ -1,4 +1,4 @@
//go:build !xpack
//go:build !xpack && !xpackee
package router

View File

@@ -0,0 +1,19 @@
//go:build xpack
package router
import (
xpackRouter "github.com/1Panel-dev/1Panel/agent/xpack/router"
)
func RouterGroups() []CommonRouter {
baseRouter := commonGroups()
for _, ro := range xpackRouter.XpackGroups() {
if val, ok := ro.(CommonRouter); ok {
baseRouter = append(baseRouter, val)
}
}
return baseRouter
}
var RouterGroupApp = RouterGroups()

View File

@@ -1,4 +1,4 @@
//go:build !xpack
//go:build !xpack && !xpackee
package server

View File

@@ -0,0 +1,11 @@
//go:build xpack
package server
import (
xpack "github.com/1Panel-dev/1Panel/agent/xpack"
)
func InitOthers() {
xpack.Init()
}

View File

@@ -1,4 +1,4 @@
//go:build !xpack
//go:build !xpack && !xpackee
package xpack

View File

@@ -0,0 +1,74 @@
//go:build xpack
package xpack
import (
"net/http"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
edition "github.com/1Panel-dev/1Panel/agent/xpack/edition"
"github.com/gin-gonic/gin"
)
func RemoveTamper(website string) {
edition.RemoveTamper(website)
}
func StartClam(startClam *model.Clam, isUpdate bool) (int, error) {
return edition.StartClam(startClam, isUpdate)
}
func LoadNodeInfo(isBase bool) (model.NodeInfo, error) {
return edition.LoadNodeInfo(isBase)
}
func GetImagePrefix() string {
return edition.GetImagePrefix()
}
func IsUseCustomApp() bool {
return edition.IsUseCustomApp()
}
func IsXpack() bool {
return edition.IsXpack()
}
func CreateTaskScanSMSAlertLog(info dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string) error {
return edition.CreateTaskScanSMSAlertLog(info, alertType, create, pushAlert, method)
}
func CreateSMSAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string) error {
return edition.CreateSMSAlertLog(alertType, info, create, project, params, method)
}
func CreateTaskScanWebhookAlertLog(alert dto.AlertDTO, alertType string, create dto.AlertLogCreate, pushAlert dto.PushAlert, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
return edition.CreateTaskScanWebhookAlertLog(alert, alertType, create, pushAlert, method, transport, agentInfo)
}
func CreateWebhookAlertLog(alertType string, info dto.AlertDTO, create dto.AlertLogCreate, project string, params []dto.Param, method string, transport *http.Transport, agentInfo *dto.AgentInfo) error {
return edition.CreateWebhookAlertLog(alertType, info, create, project, params, method, transport, agentInfo)
}
func GetLicenseErrorAlert() (uint, error) {
return edition.GetLicenseErrorAlert()
}
func GetNodeErrorAlert() (uint, error) {
return edition.GetNodeErrorAlert()
}
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
func ValidateCertificate(c *gin.Context) bool {
return edition.ValidateCertificate(c)
}
func PushSSLToNode(websiteSSL *model.WebsiteSSL) error {
return edition.PushSSLToNode(websiteSSL)
}
func GetAgentInfo() (*dto.AgentInfo, error) {
return edition.GetAgentInfo()
}

View File

@@ -1,4 +1,4 @@
//go:build !xpack
//go:build !xpack && !xpackee
package router

View File

@@ -0,0 +1,19 @@
//go:build xpack
package router
import (
xpackRouter "github.com/1Panel-dev/1Panel/core/xpack/router"
)
func RouterGroups() []CommonRouter {
baseRouter := commonGroups()
for _, ro := range xpackRouter.XpackGroups() {
if val, ok := ro.(CommonRouter); ok {
baseRouter = append(baseRouter, val)
}
}
return baseRouter
}
var RouterGroupApp = RouterGroups()

View File

@@ -0,0 +1,25 @@
//go:build xpackee
package router
import (
xpackEERouter "github.com/1Panel-dev/1Panel/core/xpack-ee/router"
xpackRouter "github.com/1Panel-dev/1Panel/core/xpack/router"
)
func RouterGroups() []CommonRouter {
baseRouter := commonGroups()
for _, ro := range xpackRouter.XpackGroups() {
if val, ok := ro.(CommonRouter); ok {
baseRouter = append(baseRouter, val)
}
}
for _, ro := range xpackEERouter.XpackEEGroups() {
if val, ok := ro.(CommonRouter); ok {
baseRouter = append(baseRouter, val)
}
}
return baseRouter
}
var RouterGroupApp = RouterGroups()

View File

@@ -1,4 +1,4 @@
//go:build !xpack
//go:build !xpack && !xpackee
package server

11
core/server/init_xpack.go Normal file
View File

@@ -0,0 +1,11 @@
//go:build xpack
package server
import (
xpack "github.com/1Panel-dev/1Panel/core/xpack"
)
func InitOthers() {
xpack.Init()
}

View File

@@ -0,0 +1,13 @@
//go:build xpackee
package server
import (
xpack "github.com/1Panel-dev/1Panel/core/xpack"
xpackEE "github.com/1Panel-dev/1Panel/core/xpack-ee"
)
func InitOthers() {
xpack.Init()
xpackEE.Init()
}

View File

@@ -1,4 +1,4 @@
//go:build !xpack
//go:build !xpack && !xpackee
package xpack

35
core/utils/xpack/xpack.go Normal file
View File

@@ -0,0 +1,35 @@
//go:build xpack
package xpack
import (
"net/http"
"github.com/1Panel-dev/1Panel/core/utils/ssh"
edition "github.com/1Panel-dev/1Panel/core/xpack/edition"
"github.com/gin-gonic/gin"
)
func Proxy(c *gin.Context, currentNode string) {
edition.Proxy(c, currentNode)
}
func ProxyDocker(proxyURL string) error { return edition.ProxyDocker(proxyURL) }
func UpdateGroup(name string, group, newGroup uint) error {
return edition.UpdateGroup(name, group, newGroup)
}
func CheckBackupUsed(name string) error {
return edition.CheckBackupUsed(name)
}
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
func LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {
return edition.LoadNodeInfo(currentNode)
}
func Sync(dataType string) error { return edition.Sync(dataType) }
func AutoUpgradeWithMaster() { edition.AutoUpgradeWithMaster() }

View File

@@ -0,0 +1,44 @@
//go:build xpackee
package xpack
import (
"net/http"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/utils/ssh"
edition "github.com/1Panel-dev/1Panel/core/xpack-ee/edition"
"github.com/gin-gonic/gin"
)
func Proxy(c *gin.Context, currentNode string) {
edition.Proxy(c, currentNode)
}
func ProxyDocker(proxyURL string) error { return edition.ProxyDocker(proxyURL) }
func UpdateGroup(name string, group, newGroup uint) error {
return edition.UpdateGroup(name, group, newGroup)
}
func CheckBackupUsed(name string) error {
return edition.CheckBackupUsed(name)
}
func LoadRequestTransport() *http.Transport { return edition.LoadRequestTransport() }
func LoadNodeInfo(currentNode string) (*ssh.ConnInfo, string, error) {
return edition.LoadNodeInfo(currentNode)
}
func Sync(dataType string) error { return edition.Sync(dataType) }
func AutoUpgradeWithMaster() { edition.AutoUpgradeWithMaster() }
func Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, string, error) {
return edition.Login(c, info, entrance)
}
func MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, string, error) {
return edition.MFALogin(c, info, entrance)
}

View File

@@ -191,18 +191,12 @@ import TaskLog from '@/components/log/task/index.vue';
import { routerToFileWithPath } from '@/utils/router';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { mysqlArgs } from '@/views/cronjob/cronjob/helper';
import { loadOptionalComponent } from '@/extensions/optional';
const { currentNode } = useGlobalStore();
const emit = defineEmits(['close']);
const PushApp = defineAsyncComponent(async () => {
const modules = import.meta.glob('@/xpack/views/appstore/push-app/index.vue');
const loader = modules['/src/xpack/views/appstore/push-app/index.vue'];
if (loader) {
return ((await loader()) as any).default;
}
return { template: '<div></div>' };
});
const PushApp = defineAsyncComponent(() => loadOptionalComponent('/src/xpack/views/appstore/push-app/index.vue'));
const selects = ref<any>([]);
const args = ref([]);

View File

@@ -25,6 +25,7 @@
<script lang="ts" setup>
import { getSettingInfo } from '@/api/modules/setting';
import { searchXpackSetting } from '@/extensions/xpack';
const open = ref(false);
const restart = ref(true);
@@ -58,21 +59,16 @@ const acceptParams = async (props: DialogProps): Promise<void> => {
return;
}
let searchXSetting;
const xpackModules = import.meta.glob('../../xpack/api/modules/setting.ts', { eager: true });
if (xpackModules['../../xpack/api/modules/setting.ts']) {
searchXSetting = xpackModules['../../xpack/api/modules/setting.ts']['searchXSetting'] || {};
const res = await searchXSetting();
if (!res) {
emit();
return;
}
if (res.data.proxyDocker === '') {
emit();
return;
}
open.value = true;
const res = await searchXpackSetting();
if (!res) {
emit();
return;
}
if (res.data.proxyDocker === '') {
emit();
return;
}
open.value = true;
};
const onConfirm = async () => {

View File

@@ -11,6 +11,7 @@
<script lang="ts" setup>
import { getSettingInfo } from '@/api/modules/setting';
import { searchXpackSetting } from '@/extensions/xpack';
const showOption = ref(false);
const restart = ref(false);
@@ -36,21 +37,16 @@ const loadStatus = async () => {
em('update:withDockerRestart', false);
return;
});
let searchXSetting;
const xpackModules = import.meta.glob('../../xpack/api/modules/setting.ts', { eager: true });
if (xpackModules['../../xpack/api/modules/setting.ts']) {
searchXSetting = xpackModules['../../xpack/api/modules/setting.ts']['searchXSetting'] || {};
const res = await searchXSetting();
if (!res) {
em('update:withDockerRestart', false);
return;
}
if (res.data.proxyDocker === '') {
em('update:withDockerRestart', false);
return;
}
showOption.value = true;
const res = await searchXpackSetting();
if (!res) {
em('update:withDockerRestart', false);
return;
}
if (res.data.proxyDocker === '') {
em('update:withDockerRestart', false);
return;
}
showOption.value = true;
};
const changeRestart = () => {

View File

@@ -0,0 +1,21 @@
type VueComponent = { template?: string } | Record<string, any>;
const EmptyComponent: VueComponent = { template: '<div></div>' };
const xpackViewModules = import.meta.glob('@/xpack/views/**/*.vue');
function findLoader(suffix: string) {
for (const path in xpackViewModules) {
if (path.endsWith(suffix)) {
return xpackViewModules[path];
}
}
return null;
}
export async function loadOptionalComponent(key: string): Promise<VueComponent> {
const loader = findLoader(key);
if (!loader) {
return EmptyComponent;
}
return ((await loader()) as { default?: VueComponent }).default || EmptyComponent;
}

View File

@@ -0,0 +1,5 @@
import type { RouteRecordRaw } from 'vue-router';
export function getXpackRoutes(): Record<string, RouteRecordRaw> {
return import.meta.glob('@/xpack/routers/*.ts', { eager: true }) as Record<string, RouteRecordRaw>;
}

View File

@@ -0,0 +1,27 @@
type XpackThemeModule = {
setPrimaryColor?: (color: string) => any;
};
function findModule<T>(modules: Record<string, T>, suffix: string): T | null {
for (const path in modules) {
if (path.endsWith(suffix)) {
return modules[path];
}
}
return null;
}
export function loadXpackStyles() {
const modules = import.meta.glob('@/xpack/styles/index.scss');
const loader = findModule(modules, '/styles/index.scss');
loader?.();
}
export function setXpackPrimaryColor(color: string) {
const modules = import.meta.glob('@/xpack/utils/theme/tool.ts', { eager: true }) as Record<
string,
XpackThemeModule
>;
const module = findModule(modules, '/utils/theme/tool.ts');
return module?.setPrimaryColor?.(color);
}

View File

@@ -0,0 +1,37 @@
type XpackSettingModule = {
searchXSetting?: () => Promise<any>;
updateXSettingByKey?: (key: string, value: string) => Promise<any>;
};
function findModule<T>(modules: Record<string, T>, suffix: string): T | null {
for (const path in modules) {
if (path.endsWith(suffix)) {
return modules[path];
}
}
return null;
}
function getXpackSettingModule(): XpackSettingModule | null {
const modules = import.meta.glob('@/xpack/api/modules/setting.ts', { eager: true }) as Record<
string,
XpackSettingModule
>;
return findModule(modules, '/api/modules/setting.ts');
}
export async function searchXpackSetting() {
const module = getXpackSettingModule();
if (!module?.searchXSetting) {
return null;
}
return module.searchXSetting();
}
export async function updateXpackSettingByKey(key: string, value: string) {
const module = getXpackSettingModule();
if (!module?.updateXSettingByKey) {
return null;
}
return module.updateXSettingByKey(key, value);
}

View File

@@ -6,11 +6,9 @@ import '@/styles/common.scss';
import '@/assets/iconfont/iconfont.css';
import '@/assets/iconfont/iconfont.js';
import '@/styles/style.css';
import { loadXpackStyles } from '@/extensions/theme';
const styleModule = import.meta.glob('xpack/styles/index.scss');
for (const path in styleModule) {
styleModule[path]?.();
}
loadXpackStyles();
import router from '@/routers/index';
import i18n, { ensureFallbackLocale, loadLocaleMessages } from '@/lang/index';

View File

@@ -1,8 +1,9 @@
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import { getXpackRoutes } from '@/extensions/routes';
import { Layout } from '@/routers/constant';
let modules: Record<string, RouteRecordRaw> = import.meta.glob('./modules/*.ts', { eager: true });
const xpackModules: Record<string, RouteRecordRaw> = import.meta.glob('../xpack/routers/*.ts', { eager: true });
const xpackModules: Record<string, RouteRecordRaw> = getXpackRoutes();
modules = { ...modules, ...xpackModules };
const homeRouter: RouteRecordRaw = {

View File

@@ -1,8 +1,5 @@
import { setXpackPrimaryColor } from '@/extensions/theme';
export function setPrimaryColor(color: string) {
let setPrimaryColor: (arg0: string) => any;
const xpackModules = import.meta.glob('../xpack/utils/theme/tool.ts', { eager: true });
if (xpackModules['../xpack/utils/theme/tool.ts']) {
setPrimaryColor = xpackModules['../xpack/utils/theme/tool.ts']['setPrimaryColor'] || {};
return setPrimaryColor(color);
}
return setXpackPrimaryColor(color);
}

View File

@@ -1,5 +1,9 @@
import { getLicenseStatus, getMasterLicenseStatus, getSettingInfo } from '@/api/modules/setting';
import { useTheme } from '@/global/use-theme';
import {
searchXpackSetting,
updateXpackSettingByKey as updateXpackSettingByKeyFromExtension,
} from '@/extensions/xpack';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const { switchTheme } = useTheme();
@@ -51,19 +55,14 @@ export async function initFavicon() {
}
export async function getXpackSetting() {
let searchXSetting;
const xpackModules = import.meta.glob('../xpack/api/modules/setting.ts', { eager: true });
if (xpackModules['../xpack/api/modules/setting.ts']) {
searchXSetting = xpackModules['../xpack/api/modules/setting.ts']['searchXSetting'] || {};
const res = await searchXSetting();
if (!res) {
initFavicon();
resetXSetting();
return;
}
const res = await searchXpackSetting();
if (!res) {
initFavicon();
return res;
resetXSetting();
return;
}
initFavicon();
return res;
}
const loadDataFromDB = async () => {
@@ -98,44 +97,34 @@ export async function loadMasterProductProFromDB() {
}
export async function getXpackSettingForTheme() {
let searchXSetting;
const xpackModules = import.meta.glob('../xpack/api/modules/setting.ts', { eager: true });
if (xpackModules['../xpack/api/modules/setting.ts']) {
searchXSetting = xpackModules['../xpack/api/modules/setting.ts']['searchXSetting'] || {};
const res2 = await searchXSetting();
if (res2) {
globalStore.themeConfig.title = res2.data?.title;
globalStore.themeConfig.logo = res2.data?.logo;
globalStore.themeConfig.logoWithText = res2.data?.logoWithText;
globalStore.themeConfig.favicon = res2.data?.favicon;
globalStore.themeConfig.loginImage = res2.data?.loginImage;
globalStore.themeConfig.loginBgType = res2.data?.loginBgType;
globalStore.themeConfig.loginBackground = res2.data?.loginBackground;
globalStore.themeConfig.loginBtnLinkColor = res2.data?.loginBtnLinkColor;
globalStore.themeConfig.themeColor = res2.data?.themeColor;
globalStore.masterAlias = res2.data.masterAlias;
if (res2.data?.theme) {
globalStore.themeConfig.theme = res2.data.theme;
}
globalStore.watermarkShow = res2.data.watermarkShow === 'Enable';
try {
globalStore.watermark = JSON.parse(res2.data.watermark);
} catch {
globalStore.watermark = null;
}
} else {
resetXSetting();
const res2 = await searchXpackSetting();
if (res2) {
globalStore.themeConfig.title = res2.data?.title;
globalStore.themeConfig.logo = res2.data?.logo;
globalStore.themeConfig.logoWithText = res2.data?.logoWithText;
globalStore.themeConfig.favicon = res2.data?.favicon;
globalStore.themeConfig.loginImage = res2.data?.loginImage;
globalStore.themeConfig.loginBgType = res2.data?.loginBgType;
globalStore.themeConfig.loginBackground = res2.data?.loginBackground;
globalStore.themeConfig.loginBtnLinkColor = res2.data?.loginBtnLinkColor;
globalStore.themeConfig.themeColor = res2.data?.themeColor;
globalStore.masterAlias = res2.data.masterAlias;
if (res2.data?.theme) {
globalStore.themeConfig.theme = res2.data.theme;
}
globalStore.watermarkShow = res2.data.watermarkShow === 'Enable';
try {
globalStore.watermark = JSON.parse(res2.data.watermark);
} catch {
globalStore.watermark = null;
}
} else {
resetXSetting();
}
switchTheme();
initFavicon();
}
export async function updateXpackSettingByKey(key: string, value: string) {
let updateXSettingByKey;
const xpackModules = import.meta.glob('../xpack/api/modules/setting.ts', { eager: true });
if (xpackModules['../xpack/api/modules/setting.ts']) {
updateXSettingByKey = xpackModules['../xpack/api/modules/setting.ts']['updateXSettingByKey'] || {};
return updateXSettingByKey(key, value);
}
return updateXpackSettingByKeyFromExtension(key, value);
}

View File

@@ -23,11 +23,13 @@
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { computed, defineAsyncComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import OllamaView from '@/views/ai/model/ollama/index.vue';
import TensorRTView from '@/views/ai/model/tensorrt/index.vue';
import VllmView from '@/xpack/views/vllm/index.vue';
import { loadOptionalComponent } from '@/extensions/optional';
const VllmView = defineAsyncComponent(() => loadOptionalComponent('/src/xpack/views/vllm/index.vue'));
type LocalTab = 'ollama' | 'vllm' | 'tensorrt';

View File

@@ -157,16 +157,10 @@ import CodemirrorPro from '@/components/codemirror-pro/index.vue';
import { computeSizeFromMB } from '@/utils/util';
import { loadResourceLimit } from '@/api/modules/container';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { loadOptionalComponent } from '@/extensions/optional';
const { isOffLine, isMasterProductPro, isMaster } = useGlobalStore();
const PushtoNode = defineAsyncComponent(async () => {
const modules = import.meta.glob('@/xpack/views/ssl/index.vue');
const loader = modules['/src/xpack/views/ssl/index.vue'];
if (loader) {
return ((await loader()) as any).default;
}
return { template: '<div></div>' };
});
const PushtoNode = defineAsyncComponent(() => loadOptionalComponent('/src/xpack/views/ssl/index.vue'));
interface ClusterProps {
key: string;

View File

@@ -71,16 +71,10 @@ import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
import { defineAsyncComponent } from 'vue';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { loadOptionalComponent } from '@/extensions/optional';
const { isProductPro, isMasterProductPro } = useGlobalStore();
const CustomSetting = defineAsyncComponent(async () => {
const modules = import.meta.glob('@/xpack/views/appstore/index.vue');
const loader = modules['/src/xpack/views/appstore/index.vue'];
if (loader) {
return ((await loader()) as any).default;
}
return { template: '<div></div>' };
});
const CustomSetting = defineAsyncComponent(() => loadOptionalComponent('/src/xpack/views/appstore/index.vue'));
const rules = ref<FormRules>({});
const config = ref({

View File

@@ -180,16 +180,10 @@ import { KeyTypes } from '@/global/mimetype';
import { getDNSName, getAccountName } from '@/utils/util';
import { defineAsyncComponent } from 'vue';
import { useGlobalStore } from '@/composables/useGlobalStore';
import { loadOptionalComponent } from '@/extensions/optional';
const { isMasterProductPro, isMaster } = useGlobalStore();
const PushtoNode = defineAsyncComponent(async () => {
const modules = import.meta.glob('@/xpack/views/ssl/index.vue');
const loader = modules['/src/xpack/views/ssl/index.vue'];
if (loader) {
return ((await loader()) as any).default;
}
return { template: '<div></div>' };
});
const PushtoNode = defineAsyncComponent(() => loadOptionalComponent('/src/xpack/views/ssl/index.vue'));
const props = defineProps({
id: {

Submodule frontend/src/xpack-ee deleted from 329f2b121b

View File

@@ -44,7 +44,6 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
alias: {
'@': resolve(__dirname, './src'),
'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
xpack: resolve(__dirname, './src/xpack'),
},
},
define: {