mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-17 12:58:51 +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"`
|
||||
Password string `json:"PANEL_DB_USER_PASSWORD"`
|
||||
DatabaseName string `json:"DATABASE_NAME"`
|
||||
Format string `json:"format"`
|
||||
Collation string `json:"collation"`
|
||||
}
|
||||
|
||||
type AuthParam struct {
|
||||
|
|
|
|||
|
|
@ -300,10 +300,11 @@ func createLink(ctx context.Context, installTask *task.Task, app model.App, appI
|
|||
createMysql.Name = dbConfig.DbName
|
||||
createMysql.Username = dbConfig.DbUser
|
||||
createMysql.Database = database.Name
|
||||
createMysql.Format = "utf8mb4"
|
||||
createMysql.Format = dbConfig.Format
|
||||
createMysql.Permission = "%"
|
||||
createMysql.Password = dbConfig.Password
|
||||
createMysql.From = database.From
|
||||
createMysql.Collation = dbConfig.Collation
|
||||
mysqldb, err := NewIMysqlService().Create(ctx, createMysql)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -730,6 +730,6 @@ var AddGPUMonitor = &gormigrate.Migration{
|
|||
var UpdateDatabaseMysql = &gormigrate.Migration{
|
||||
ID: "20251125-update-database-mysql",
|
||||
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-eslint": "^1.8.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-tsc": "^0.29.8"
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ const formRules = ref<FormRules>({
|
|||
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
||||
specifyIP: [Rules.ipv4orV6],
|
||||
restartPolicy: [Rules.requiredSelect],
|
||||
format: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
const initFormData = () => ({
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ const formData = ref({
|
|||
taskID: '',
|
||||
gpuConfig: false,
|
||||
specifyIP: '',
|
||||
format: 'utf8mb4',
|
||||
collation: '',
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
|
|
|
|||
|
|
@ -118,6 +118,17 @@
|
|||
</div>
|
||||
<span class="input-help" v-if="p.description">{{ getDescription(p) }}</span>
|
||||
</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>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
|
@ -128,6 +139,7 @@ import { Rules } from '@/global/form-rules';
|
|||
import { App } from '@/api/interface/app';
|
||||
import { getDBName, getLabel, getDescription } from '@/utils/util';
|
||||
import { getPathByType } from '@/api/modules/files';
|
||||
import { loadFormatCollations } from '@/api/modules/database';
|
||||
|
||||
interface ParamObj extends App.FromField {
|
||||
services: App.AppService[];
|
||||
|
|
@ -163,7 +175,9 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const form = reactive({});
|
||||
const form = reactive({
|
||||
format: '',
|
||||
});
|
||||
let rules = reactive({});
|
||||
const params = computed({
|
||||
get() {
|
||||
|
|
@ -241,6 +255,10 @@ const handleParams = () => {
|
|||
|
||||
const getServices = async (childKey: string, key: string | undefined, pObj: ParamObj | undefined) => {
|
||||
pObj.services = [];
|
||||
appKey.value = key || '';
|
||||
if (appKey.value == 'mysql') {
|
||||
form.format = 'utf8mb4';
|
||||
}
|
||||
await getAppService(key).then((res) => {
|
||||
pObj.services = res.data || [];
|
||||
form[childKey] = '';
|
||||
|
|
@ -268,6 +286,9 @@ const changeService = (value: string, services: App.AppService[]) => {
|
|||
});
|
||||
}
|
||||
});
|
||||
if (appKey.value == 'mysql') {
|
||||
loadOptions(value);
|
||||
}
|
||||
updateParam();
|
||||
};
|
||||
|
||||
|
|
@ -275,6 +296,22 @@ const toPage = (key: string) => {
|
|||
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(() => {
|
||||
handleParams();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -417,10 +417,6 @@
|
|||
</template>
|
||||
|
||||
<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 Uploads from '@/components/upload/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 AppUpgrade from './upgrade/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 { 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 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 SvgIcon from '@/components/svg-icon/svg-icon.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 { useGlobalStore } from '@/composables/useGlobalStore';
|
||||
const { currentNode, isMaster, currentNodeAddr } = useGlobalStore();
|
||||
|
|
|
|||
|
|
@ -79,6 +79,10 @@
|
|||
<TaskLog ref="taskLogRef" />
|
||||
</template>
|
||||
<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 { getAppUpdateVersions, ignoreUpgrade, installedOp } from '@/api/modules/app';
|
||||
import { getAppStoreConfig } from '@/api/modules/setting';
|
||||
|
|
@ -87,10 +91,7 @@ import { ElMessageBox, FormInstance } from 'element-plus';
|
|||
import { reactive, ref, onBeforeUnmount } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import Diff from './diff/index.vue';
|
||||
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';
|
||||
|
||||
const composeDiffRef = ref();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import Components from 'unplugin-vue-components/vite';
|
|||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
|
||||
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 __APP_INFO__ = {
|
||||
|
|
@ -44,7 +44,6 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
scss: {
|
||||
additionalData: `@use "@/styles/var.scss" as *;`,
|
||||
silenceDeprecations: ['legacy-js-api'],
|
||||
api: 'modern',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -52,6 +51,9 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
port: viteEnv.VITE_PORT,
|
||||
open: viteEnv.VITE_OPEN,
|
||||
host: '0.0.0.0',
|
||||
sourcemapIgnoreList: (sourcePath) => {
|
||||
return sourcePath.includes('node_modules');
|
||||
},
|
||||
proxy: {
|
||||
'/api/v2': {
|
||||
target: 'http://localhost:9999/',
|
||||
|
|
@ -95,12 +97,16 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
svgLoader({
|
||||
defaultImport: 'url',
|
||||
}),
|
||||
monacoEditorPlugin({
|
||||
languageWorkers: ['editorWorkerService', 'typescript', 'json', 'html', 'css'],
|
||||
}),
|
||||
],
|
||||
esbuild: {
|
||||
pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log'] : [],
|
||||
drop: viteEnv.VITE_DROP_CONSOLE && process.env.NODE_ENV === 'production' ? ['debugger'] : [],
|
||||
},
|
||||
build: {
|
||||
sourcemap: false,
|
||||
outDir: '../core/cmd/server/web',
|
||||
minify: 'esbuild',
|
||||
target: 'esnext',
|
||||
|
|
@ -110,15 +116,12 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
chunkFileNames: 'assets/js/[name]-[hash].js',
|
||||
entryFileNames: 'assets/js/[name]-[hash].js',
|
||||
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