perf: 支持ftp上传

pull/148/head
xiaojunnuo 2024-09-01 04:49:26 +08:00
parent ee617095ef
commit b9bddbfabb
8 changed files with 105 additions and 48 deletions

View File

@ -0,0 +1,14 @@
version: '3.3'
services:
ftp:
# 镜像 # ↓↓↓↓↓ --- 1、 镜像版本号,建议改成固定版本号【可选】
image: gists/pure-ftpd
container_name: ftp # 容器名
restart: unless-stopped # 自动重启
volumes:
- /data/ftp2/:/home/ftpuser
ports: # 端口映射
- "21:21"
- "30000-30009:30000-30009"
environment: # 环境变量
- TZ=Asia/Shanghai

View File

@ -1,20 +1,13 @@
<template> <template>
<div class="layout-vip isPlus"> <div class="layout-vip isPlus" @click="openUpgrade">
<contextHolder /> <contextHolder />
<fs-icon icon="mingcute:vip-1-line"></fs-icon> <fs-icon icon="mingcute:vip-1-line" :title="text.title" />
<div class="text">
<template v-if="userStore.isPlus"> <div v-if="mode !== 'icon'" class="text">
<a-tooltip> <a-tooltip>
<template #title> 到期时间{{ expireTime }} </template> <template #title> {{ text.title }}</template>
<span @click="openUpgrade">{{ texts.plus }}</span> <span>{{ text.name }}</span>
</a-tooltip> </a-tooltip>
</template>
<template v-else>
<a-tooltip>
<template #title> 升级专业版享受更多VIP特权 </template>
<span @click="openUpgrade"> {{ texts.free }} {{ expiredDays }} </span>
</a-tooltip>
</template>
</div> </div>
</div> </div>
</template> </template>
@ -26,24 +19,53 @@ import { message, Modal } from "ant-design-vue";
import * as api from "./api"; import * as api from "./api";
import { useSettingStore } from "/@/store/modules/settings"; import { useSettingStore } from "/@/store/modules/settings";
const props = defineProps<{ const props = withDefaults(
mode?: "button" | "nav"; defineProps<{
}>(); mode?: "button" | "nav" | "icon";
type Texts = { }>(),
plus: string; {
free: string; mode: "button"
}
);
type Text = {
name: string;
title?: string;
}; };
const texts = computed<Texts>(() => { const text = computed<Text>(() => {
if (props.mode === "button") { const map = {
return { isPlus: {
plus: "专业版已开通", button: {
free: "此为专业版功能" name: "专业版已开通",
title: "到期时间:" + expireTime.value
},
icon: {
name: "",
title: "专业版已开通"
},
nav: {
name: "专业版",
title: "到期时间:" + expireTime.value
}
},
free: {
button: {
name: "此为专业版功能",
title: "升级专业版享受更多VIP特权"
},
icon: {
name: "",
title: "此为专业版功能"
},
nav: {
name: "免费版",
title: "升级专业版享受更多VIP特权"
}
}
}; };
if (userStore.isPlus) {
return map.isPlus[props.mode];
} else { } else {
return { return map.free[props.mode];
plus: "专业版",
free: "免费版"
};
} }
}); });
@ -86,6 +108,7 @@ async function doActive() {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const computedSiteId = computed(() => settingStore.installInfo?.siteId); const computedSiteId = computed(() => settingStore.installInfo?.siteId);
const [modal, contextHolder] = Modal.useModal(); const [modal, contextHolder] = Modal.useModal();
function openUpgrade() { function openUpgrade() {
const placeholder = "请输入激活码"; const placeholder = "请输入激活码";
modal.confirm({ modal.confirm({
@ -137,6 +160,7 @@ function openUpgrade() {
justify-content: center; justify-content: center;
height: 100%; height: 100%;
cursor: pointer; cursor: pointer;
&.isPlus { &.isPlus {
color: #c5913f; color: #c5913f;
} }

View File

@ -18,7 +18,7 @@
<MenuFoldOutlined v-else /> <MenuFoldOutlined v-else />
</div> </div>
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="frameworkMenus" /> <fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="frameworkMenus" />
<vip-button class="flex-center header-btn"></vip-button> <vip-button class="flex-center header-btn" mode="nav" />
</div> </div>
<div class="header-right header-buttons"> <div class="header-right header-buttons">
<!-- <button--> <!-- <button-->

View File

@ -16,7 +16,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
} }
}; };
function buildDefineFields(define: any) { function buildDefineFields(define: any, form: any) {
const formWrapperRef = crudExpose.getFormWrapperRef(); const formWrapperRef = crudExpose.getFormWrapperRef();
const columnsRef = toRef(formWrapperRef.formOptions, "columns"); const columnsRef = toRef(formWrapperRef.formOptions, "columns");
@ -32,7 +32,12 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
...value, ...value,
key key
}; };
columnsRef.value[key] = _.merge({ title: key }, defaultPluginConfig, field); const column = _.merge({ title: key }, defaultPluginConfig, field);
if (column.value != null && _.get(form, key) == null) {
//设置默认值
_.set(form, key, column.value);
}
columnsRef.value[key] = column;
console.log("form", columnsRef.value); console.log("form", columnsRef.value);
}); });
} }
@ -55,13 +60,16 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
rules: [{ required: true, message: "请选择类型" }], rules: [{ required: true, message: "请选择类型" }],
valueChange: { valueChange: {
immediate: true, immediate: true,
async handle({ value, mode, form }) { async handle({ value, mode, form, immediate }) {
if (value == null) { if (value == null) {
return; return;
} }
const define = await api.GetProviderDefine(value); const define = await api.GetProviderDefine(value);
console.log("define", define); console.log("define", define);
buildDefineFields(define); if (!immediate) {
form.access = {};
}
buildDefineFields(define, form);
} }
} }
}, },

View File

@ -25,6 +25,7 @@
<template #title> <template #title>
<a-avatar :src="item.icon || '/images/plugin.png'" /> <a-avatar :src="item.icon || '/images/plugin.png'" />
<span class="title">{{ item.title }}</span> <span class="title">{{ item.title }}</span>
<vip-button v-if="item.needPlus" mode="icon" />
</template> </template>
<template #description> <template #description>
<span :title="item.desc">{{ item.desc }}</span> <span :title="item.desc">{{ item.desc }}</span>
@ -81,6 +82,7 @@ import _ from "lodash-es";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { CopyOutlined } from "@ant-design/icons-vue"; import { CopyOutlined } from "@ant-design/icons-vue";
import { PluginGroups } from "/@/views/certd/pipeline/pipeline/type"; import { PluginGroups } from "/@/views/certd/pipeline/pipeline/type";
import { useUserStore } from "/@/store/modules/user";
export default { export default {
name: "PiStepForm", name: "PiStepForm",
@ -98,6 +100,7 @@ export default {
* @returns * @returns
*/ */
function useStepForm() { function useStepForm() {
const useStore = useUserStore();
const getPluginGroups: any = inject("getPluginGroups"); const getPluginGroups: any = inject("getPluginGroups");
const pluginGroups: PluginGroups = getPluginGroups(); const pluginGroups: PluginGroups = getPluginGroups();
const mode: Ref = ref("add"); const mode: Ref = ref("add");
@ -117,6 +120,10 @@ export default {
}); });
const stepTypeSelected = (item: any) => { const stepTypeSelected = (item: any) => {
if (item.needPlus && !useStore.isPlus) {
message.warn("此插件需要开通专业版才能使用");
throw new Error("此插件需要开通专业版才能使用");
}
currentStep.value.type = item.name; currentStep.value.type = item.name;
currentStep.value.title = item.title; currentStep.value.title = item.title;
console.log("currentStepTypeChanged:", currentStep.value); console.log("currentStepTypeChanged:", currentStep.value);
@ -128,6 +135,7 @@ export default {
message.warn("请先选择类型"); message.warn("请先选择类型");
return; return;
} }
// stepinput // stepinput
changeCurrentPlugin(currentStep.value); changeCurrentPlugin(currentStep.value);
@ -347,7 +355,7 @@ export default {
overflow-y: hidden; overflow-y: hidden;
.ant-card-meta-description { .ant-card-meta-description {
font-size: 10px; font-size: 12px;
line-height: 20px; line-height: 20px;
height: 40px; height: 40px;
color: #7f7f7f; color: #7f7f7f;

View File

@ -29,7 +29,7 @@
"@certd/pipeline": "^1.24.0", "@certd/pipeline": "^1.24.0",
"@certd/plugin-cert": "^1.24.0", "@certd/plugin-cert": "^1.24.0",
"@certd/plugin-plus": "^1.24.0", "@certd/plugin-plus": "^1.24.0",
"@koa/cors": "^3.4.3", "@koa/cors": "^5.0.0",
"@midwayjs/bootstrap": "^3.16.2", "@midwayjs/bootstrap": "^3.16.2",
"@midwayjs/cache": "^3.14.0", "@midwayjs/cache": "^3.14.0",
"@midwayjs/core": "^3.16.2", "@midwayjs/core": "^3.16.2",
@ -41,6 +41,7 @@
"@midwayjs/typeorm": "^3.16.4", "@midwayjs/typeorm": "^3.16.4",
"@midwayjs/validate": "^3.16.4", "@midwayjs/validate": "^3.16.4",
"axios": "^1.7.2", "axios": "^1.7.2",
"basic-ftp": "^5.0.5",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"better-sqlite3": "^11.1.2", "better-sqlite3": "^11.1.2",
"cache-manager": "^3.6.3", "cache-manager": "^3.6.3",
@ -50,7 +51,7 @@
"https-proxy-agent": "^7.0.4", "https-proxy-agent": "^7.0.4",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^9.0.0",
"koa-send": "^5.0.1", "koa-send": "^5.0.1",
"kubernetes-client": "^9.0.0", "kubernetes-client": "^9.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
@ -65,7 +66,7 @@
"ssh2": "^1.15.0", "ssh2": "^1.15.0",
"svg-captcha": "^1.4.0", "svg-captcha": "^1.4.0",
"tencentcloud-sdk-nodejs": "^4.0.44", "tencentcloud-sdk-nodejs": "^4.0.44",
"typeorm": "^0.3.11" "typeorm": "0.3.15"
}, },
"devDependencies": { "devDependencies": {
"@midwayjs/mock": "^3.16.4", "@midwayjs/mock": "^3.16.4",

View File

@ -75,7 +75,8 @@ export class AccessService extends BaseService<AccessEntity> implements IAccessS
//星号保护 //星号保护
const length = value.length; const length = value.length;
const subIndex = Math.min(2, length); const subIndex = Math.min(2, length);
const starLength = length - subIndex * 2; let starLength = length - subIndex * 2;
starLength = Math.max(2, starLength);
const starString = '*'.repeat(starLength); const starString = '*'.repeat(starLength);
json[key] = value.substring(0, subIndex) + starString + value.substring(value.length - subIndex); json[key] = value.substring(0, subIndex) + starString + value.substring(value.length - subIndex);
encryptSetting[key] = { encryptSetting[key] = {

View File

@ -74,14 +74,15 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
async getSetting<T>(type: any): Promise<T> { async getSetting<T>(type: any): Promise<T> {
const key = type.__key__; const key = type.__key__;
const cacheKey = type.getCacheKey(); const cacheKey = type.getCacheKey();
let settings: T = await this.cache.get(cacheKey); const settings: T = await this.cache.get(cacheKey);
let settingInstance: T = new type(); if (settings) {
if (settings == null) { return settings;
settings = await this.getSettingByKey(key);
settingInstance = _.merge(settingInstance, settings);
await this.cache.set(key, settingInstance);
} }
return settingInstance; let newSetting: T = new type();
const savedSettings = await this.getSettingByKey(key);
newSetting = _.merge(newSetting, savedSettings);
await this.cache.set(cacheKey, newSetting);
return newSetting;
} }
async saveSetting<T extends BaseSettings>(bean: T) { async saveSetting<T extends BaseSettings>(bean: T) {