mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-18 21:38:57 +08:00
feat: Add MySQL character set and collation configuration during application installation (#11062)
This commit is contained in:
parent
d6b00967ca
commit
48e2e01a57
10 changed files with 76 additions and 27 deletions
|
|
@ -12,6 +12,8 @@ type AppDatabase struct {
|
||||||
DbUser string `json:"PANEL_DB_USER"`
|
DbUser string `json:"PANEL_DB_USER"`
|
||||||
Password string `json:"PANEL_DB_USER_PASSWORD"`
|
Password string `json:"PANEL_DB_USER_PASSWORD"`
|
||||||
DatabaseName string `json:"DATABASE_NAME"`
|
DatabaseName string `json:"DATABASE_NAME"`
|
||||||
|
Format string `json:"format"`
|
||||||
|
Collation string `json:"collation"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthParam struct {
|
type AuthParam struct {
|
||||||
|
|
|
||||||
|
|
@ -300,10 +300,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
||||||
createMysql.Name = dbConfig.DbName
|
createMysql.Name = dbConfig.DbName
|
||||||
createMysql.Username = dbConfig.DbUser
|
createMysql.Username = dbConfig.DbUser
|
||||||
createMysql.Database = database.Name
|
createMysql.Database = database.Name
|
||||||
createMysql.Format = "utf8mb4"
|
createMysql.Format = dbConfig.Format
|
||||||
createMysql.Permission = "%"
|
createMysql.Permission = "%"
|
||||||
createMysql.Password = dbConfig.Password
|
createMysql.Password = dbConfig.Password
|
||||||
createMysql.From = database.From
|
createMysql.From = database.From
|
||||||
|
createMysql.Collation = dbConfig.Collation
|
||||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -730,6 +730,6 @@ var AddGPUMonitor = &gormigrate.Migration{
|
||||||
var UpdateDatabaseMysql = &gormigrate.Migration{
|
var UpdateDatabaseMysql = &gormigrate.Migration{
|
||||||
ID: "20251125-update-database-mysql",
|
ID: "20251125-update-database-mysql",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
return tx.AutoMigrate(&model.Database{})
|
return tx.AutoMigrate(&model.DatabaseMysql{})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-html": "^3.2.2",
|
"vite-plugin-html": "^3.2.2",
|
||||||
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||||
"vite-svg-loader": "^5.1.0",
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vue-tsc": "^0.29.8"
|
"vue-tsc": "^0.29.8"
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,7 @@ const formRules = ref<FormRules>({
|
||||||
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
||||||
specifyIP: [Rules.ipv4orV6],
|
specifyIP: [Rules.ipv4orV6],
|
||||||
restartPolicy: [Rules.requiredSelect],
|
restartPolicy: [Rules.requiredSelect],
|
||||||
|
format: [Rules.requiredInput],
|
||||||
});
|
});
|
||||||
|
|
||||||
const initFormData = () => ({
|
const initFormData = () => ({
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ const formData = ref({
|
||||||
taskID: '',
|
taskID: '',
|
||||||
gpuConfig: false,
|
gpuConfig: false,
|
||||||
specifyIP: '',
|
specifyIP: '',
|
||||||
|
format: 'utf8mb4',
|
||||||
|
collation: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,17 @@
|
||||||
</div>
|
</div>
|
||||||
<span class="input-help" v-if="p.description">{{ getDescription(p) }}</span>
|
<span class="input-help" v-if="p.description">{{ getDescription(p) }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form[p.envKey] == 'mysql'" :label="$t('database.format')" prop="format">
|
||||||
|
<el-select filterable v-model="form.format" @change="loadCollations()">
|
||||||
|
<el-option v-for="item of formatOptions" :key="item.format" :label="item.format" :value="item.format" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form[p.envKey] == 'mysql'" :label="$t('database.collation')" prop="collation">
|
||||||
|
<el-select filterable v-model="form.collation">
|
||||||
|
<el-option v-for="item of collationOptions" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
<span class="input-help">{{ $t('database.collationHelper', [form.format]) }}</span>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -128,6 +139,7 @@ import { Rules } from '@/global/form-rules';
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { getDBName, getLabel, getDescription } from '@/utils/util';
|
import { getDBName, getLabel, getDescription } from '@/utils/util';
|
||||||
import { getPathByType } from '@/api/modules/files';
|
import { getPathByType } from '@/api/modules/files';
|
||||||
|
import { loadFormatCollations } from '@/api/modules/database';
|
||||||
|
|
||||||
interface ParamObj extends App.FromField {
|
interface ParamObj extends App.FromField {
|
||||||
services: App.AppService[];
|
services: App.AppService[];
|
||||||
|
|
@ -163,7 +175,9 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const form = reactive({});
|
const form = reactive({
|
||||||
|
format: '',
|
||||||
|
});
|
||||||
let rules = reactive({});
|
let rules = reactive({});
|
||||||
const params = computed({
|
const params = computed({
|
||||||
get() {
|
get() {
|
||||||
|
|
@ -241,6 +255,10 @@ const handleParams = () => {
|
||||||
|
|
||||||
const getServices = async (childKey: string, key: string | undefined, pObj: ParamObj | undefined) => {
|
const getServices = async (childKey: string, key: string | undefined, pObj: ParamObj | undefined) => {
|
||||||
pObj.services = [];
|
pObj.services = [];
|
||||||
|
appKey.value = key || '';
|
||||||
|
if (appKey.value == 'mysql') {
|
||||||
|
form.format = 'utf8mb4';
|
||||||
|
}
|
||||||
await getAppService(key).then((res) => {
|
await getAppService(key).then((res) => {
|
||||||
pObj.services = res.data || [];
|
pObj.services = res.data || [];
|
||||||
form[childKey] = '';
|
form[childKey] = '';
|
||||||
|
|
@ -268,6 +286,9 @@ const changeService = (value: string, services: App.AppService[]) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (appKey.value == 'mysql') {
|
||||||
|
loadOptions(value);
|
||||||
|
}
|
||||||
updateParam();
|
updateParam();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -275,6 +296,22 @@ const toPage = (key: string) => {
|
||||||
window.location.href = '/apps/all?install=' + key;
|
window.location.href = '/apps/all?install=' + key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatOptions = ref();
|
||||||
|
const collationOptions = ref();
|
||||||
|
const appKey = ref('');
|
||||||
|
|
||||||
|
const loadOptions = async (database: string) => {
|
||||||
|
const defaultOptions = [{ format: 'utf8mb4' }, { format: 'utf8mb3' }, { format: 'gbk' }, { format: 'big5' }];
|
||||||
|
await loadFormatCollations(database).then((res) => {
|
||||||
|
formatOptions.value = res.data || defaultOptions;
|
||||||
|
loadCollations();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadCollations = async () => {
|
||||||
|
collationOptions.value = formatOptions.value?.find((item) => item.format === form.format)?.collations || [];
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
handleParams();
|
handleParams();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -417,10 +417,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { searchAppInstalled, installedOp, appInstalledDeleteCheck, getAppIconUrl } from '@/api/modules/app';
|
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
|
||||||
import i18n from '@/lang';
|
|
||||||
import { ElMessageBox } from 'element-plus';
|
|
||||||
import Backups from '@/components/backup/index.vue';
|
import Backups from '@/components/backup/index.vue';
|
||||||
import Uploads from '@/components/upload/index.vue';
|
import Uploads from '@/components/upload/index.vue';
|
||||||
import PortJumpDialog from '@/components/port-jump/index.vue';
|
import PortJumpDialog from '@/components/port-jump/index.vue';
|
||||||
|
|
@ -429,20 +425,25 @@ import AppDelete from './delete/index.vue';
|
||||||
import AppParams from './detail/index.vue';
|
import AppParams from './detail/index.vue';
|
||||||
import AppUpgrade from './upgrade/index.vue';
|
import AppUpgrade from './upgrade/index.vue';
|
||||||
import AppIgnore from './ignore/index.vue';
|
import AppIgnore from './ignore/index.vue';
|
||||||
import ComposeLogs from '@/components/log/compose/index.vue';
|
|
||||||
import TerminalDialog from '@/views/container/container/terminal/index.vue';
|
|
||||||
import { App } from '@/api/interface/app';
|
|
||||||
import Status from '@/components/status/index.vue';
|
import Status from '@/components/status/index.vue';
|
||||||
import { getAge, jumpToPath, toLink } from '@/utils/util';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { MsgSuccess } from '@/utils/message';
|
|
||||||
import TaskLog from '@/components/log/task/index.vue';
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
import Detail from '@/views/app-store/detail/index.vue';
|
import Detail from '@/views/app-store/detail/index.vue';
|
||||||
import IgnoreApp from '@/views/app-store/installed/ignore/create/index.vue';
|
|
||||||
import { getAgentSettingByKey } from '@/api/modules/setting';
|
|
||||||
import Tags from '@/views/app-store/components/tag.vue';
|
import Tags from '@/views/app-store/components/tag.vue';
|
||||||
import SvgIcon from '@/components/svg-icon/svg-icon.vue';
|
import SvgIcon from '@/components/svg-icon/svg-icon.vue';
|
||||||
import MainDiv from '@/components/main-div/index.vue';
|
import MainDiv from '@/components/main-div/index.vue';
|
||||||
|
import ComposeLogs from '@/components/log/compose/index.vue';
|
||||||
|
import IgnoreApp from '@/views/app-store/installed/ignore/create/index.vue';
|
||||||
|
import TerminalDialog from '@/views/container/container/terminal/index.vue';
|
||||||
|
|
||||||
|
import { searchAppInstalled, installedOp, appInstalledDeleteCheck, getAppIconUrl } from '@/api/modules/app';
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElMessageBox } from 'element-plus';
|
||||||
|
import { App } from '@/api/interface/app';
|
||||||
|
import { getAge, jumpToPath, toLink } from '@/utils/util';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import { getAgentSettingByKey } from '@/api/modules/setting';
|
||||||
import { routerToFileWithPath, routerToNameWithQuery } from '@/utils/router';
|
import { routerToFileWithPath, routerToNameWithQuery } from '@/utils/router';
|
||||||
import { useGlobalStore } from '@/composables/useGlobalStore';
|
import { useGlobalStore } from '@/composables/useGlobalStore';
|
||||||
const { currentNode, isMaster, currentNodeAddr } = useGlobalStore();
|
const { currentNode, isMaster, currentNodeAddr } = useGlobalStore();
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@
|
||||||
<TaskLog ref="taskLogRef" />
|
<TaskLog ref="taskLogRef" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import Diff from './diff/index.vue';
|
||||||
|
import TaskLog from '@/components/log/task/index.vue';
|
||||||
|
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||||
|
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { getAppUpdateVersions, ignoreUpgrade, installedOp } from '@/api/modules/app';
|
import { getAppUpdateVersions, ignoreUpgrade, installedOp } from '@/api/modules/app';
|
||||||
import { getAppStoreConfig } from '@/api/modules/setting';
|
import { getAppStoreConfig } from '@/api/modules/setting';
|
||||||
|
|
@ -87,10 +91,7 @@ import { ElMessageBox, FormInstance } from 'element-plus';
|
||||||
import { reactive, ref, onBeforeUnmount } from 'vue';
|
import { reactive, ref, onBeforeUnmount } from 'vue';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
import Diff from './diff/index.vue';
|
|
||||||
import bus from '@/global/bus';
|
import bus from '@/global/bus';
|
||||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
|
||||||
import TaskLog from '@/components/log/task/index.vue';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
const composeDiffRef = ref();
|
const composeDiffRef = ref();
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import Components from 'unplugin-vue-components/vite';
|
||||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
||||||
import svgLoader from 'vite-svg-loader';
|
import svgLoader from 'vite-svg-loader';
|
||||||
|
|
||||||
const prefix = `monaco-editor/esm/vs`;
|
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
|
||||||
|
|
||||||
const { dependencies, devDependencies, name, version } = pkg;
|
const { dependencies, devDependencies, name, version } = pkg;
|
||||||
const __APP_INFO__ = {
|
const __APP_INFO__ = {
|
||||||
|
|
@ -44,7 +44,6 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
scss: {
|
scss: {
|
||||||
additionalData: `@use "@/styles/var.scss" as *;`,
|
additionalData: `@use "@/styles/var.scss" as *;`,
|
||||||
silenceDeprecations: ['legacy-js-api'],
|
silenceDeprecations: ['legacy-js-api'],
|
||||||
api: 'modern',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -52,6 +51,9 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
port: viteEnv.VITE_PORT,
|
port: viteEnv.VITE_PORT,
|
||||||
open: viteEnv.VITE_OPEN,
|
open: viteEnv.VITE_OPEN,
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
|
sourcemapIgnoreList: (sourcePath) => {
|
||||||
|
return sourcePath.includes('node_modules');
|
||||||
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api/v2': {
|
'/api/v2': {
|
||||||
target: 'http://localhost:9999/',
|
target: 'http://localhost:9999/',
|
||||||
|
|
@ -95,12 +97,16 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
svgLoader({
|
svgLoader({
|
||||||
defaultImport: 'url',
|
defaultImport: 'url',
|
||||||
}),
|
}),
|
||||||
|
monacoEditorPlugin({
|
||||||
|
languageWorkers: ['editorWorkerService', 'typescript', 'json', 'html', 'css'],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
esbuild: {
|
esbuild: {
|
||||||
pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log'] : [],
|
pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log'] : [],
|
||||||
drop: viteEnv.VITE_DROP_CONSOLE && process.env.NODE_ENV === 'production' ? ['debugger'] : [],
|
drop: viteEnv.VITE_DROP_CONSOLE && process.env.NODE_ENV === 'production' ? ['debugger'] : [],
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
sourcemap: false,
|
||||||
outDir: '../core/cmd/server/web',
|
outDir: '../core/cmd/server/web',
|
||||||
minify: 'esbuild',
|
minify: 'esbuild',
|
||||||
target: 'esnext',
|
target: 'esnext',
|
||||||
|
|
@ -110,15 +116,12 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
chunkFileNames: 'assets/js/[name]-[hash].js',
|
chunkFileNames: 'assets/js/[name]-[hash].js',
|
||||||
entryFileNames: 'assets/js/[name]-[hash].js',
|
entryFileNames: 'assets/js/[name]-[hash].js',
|
||||||
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
|
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
|
||||||
manualChunks: {
|
|
||||||
jsonWorker: [`${prefix}/language/json/json.worker`],
|
|
||||||
cssWorker: [`${prefix}/language/css/css.worker`],
|
|
||||||
htmlWorker: [`${prefix}/language/html/html.worker`],
|
|
||||||
tsWorker: [`${prefix}/language/typescript/ts.worker`],
|
|
||||||
editorWorker: [`${prefix}/editor/editor.worker`],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: ['monaco-editor/esm/vs/editor/editor.api'],
|
||||||
|
exclude: ['monaco-editor'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue