mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-29 18:38:32 +08:00
fix 异步线程支持 i18n,前端常量 i18n 修复
This commit is contained in:
parent
6e1a6fd711
commit
1bfd687119
@ -10,6 +10,7 @@
|
||||
|
||||
### ⚠️ 注意
|
||||
|
||||
- 前端国际化翻译程度:90%
|
||||
- 后端已翻译语言可以度:80%(部分异步执行日志等目前未支持)
|
||||
|
||||
后端日志国际化需要新增或者修改 `jpom.system.lang` 配置项
|
||||
|
@ -29,6 +29,7 @@ import cn.keepbx.jpom.plugins.IPlugin;
|
||||
import lombok.Lombok;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.ProjectConfig;
|
||||
import org.dromara.jpom.configuration.ProjectLogConfig;
|
||||
import org.dromara.jpom.exception.IllegalArgument2Exception;
|
||||
@ -158,7 +159,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
|
||||
}
|
||||
Map<String, String> env = projectInfoService.getEnv(nodeProjectInfoModel.getWorkspaceId());
|
||||
// 执行命令
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
File file = projectInfoService.resolveLibFile(nodeProjectInfoModel);
|
||||
if (SystemUtil.getOsInfo().isWindows()) {
|
||||
@ -288,7 +289,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
|
||||
// webhook 通知
|
||||
Opt.ofBlankAble(nodeProjectInfoModel.token())
|
||||
.ifPresent(s ->
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
String result = this.webHooks(s, nodeProjectInfoModel, type, other);
|
||||
Optional.ofNullable(result).ifPresent(s1 -> log.debug(I18nMessageUtil.get("i18n.trigger_result.364e"), nodeProjectInfoModel.getId(), type, s1));
|
||||
@ -307,7 +308,7 @@ public abstract class AbstractProjectCommander implements ProjectCommander {
|
||||
Boolean fileChangeReload = run.getFileChangeReload();
|
||||
if (fileChangeReload != null && fileChangeReload) {
|
||||
// 需要执行重载事件
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
CommandOpResult reload = this.reload(nodeProjectInfoModel, originalModel);
|
||||
log.info(I18nMessageUtil.get("i18n.trigger_project_reload_event.a7dc"), reload);
|
||||
|
@ -23,6 +23,7 @@ import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.JpomApplication;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.AgentConfig;
|
||||
import org.dromara.jpom.configuration.ProjectConfig;
|
||||
import org.dromara.jpom.model.data.DslYmlDto;
|
||||
@ -187,7 +188,7 @@ public class ProjectFileBackupService {
|
||||
File projectPath = projectInfoService.resolveLibFile(infoModel);
|
||||
DslYmlDto dslYmlDto = infoModel.dslConfig();
|
||||
// 考虑到大文件对比,比较耗时。需要异步对比文件
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
//String useBackupPath = resolveBackupPath(dslYmlDto);
|
||||
File backupItemPath = this.pathProjectBackup(infoModel, backupId);
|
||||
|
@ -12,7 +12,6 @@ package org.dromara.jpom.socket;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.keepbx.jpom.model.JsonMessage;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
@ -21,6 +20,7 @@ import org.dromara.jpom.common.Const;
|
||||
import org.dromara.jpom.common.commander.CommandOpResult;
|
||||
import org.dromara.jpom.common.commander.ProjectCommander;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.AgentConfig;
|
||||
import org.dromara.jpom.configuration.ProjectLogConfig;
|
||||
import org.dromara.jpom.model.RunMode;
|
||||
@ -246,7 +246,7 @@ public class AgentWebSocketConsoleHandle extends BaseAgentWebSocketHandle {
|
||||
if (!FileUtil.isFile(file)) {
|
||||
return new JsonMessage<>(404, I18nMessageUtil.get("i18n.file_not_found.d952"));
|
||||
}
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
boolean first = Convert.toBool(reqJson.getString("first"), false);
|
||||
int head = reqJson.getIntValue("head");
|
||||
|
@ -225,7 +225,7 @@ public class JpomApplicationEvent implements ApplicationListener<ApplicationEven
|
||||
cronMap.forEach((name, iCron) -> {
|
||||
int startCron = iCron.startCron();
|
||||
if (startCron > 0) {
|
||||
log.debug("{} scheduling has been started:{}", name, startCron);
|
||||
log.debug("{} 定时任务已经自动启动:{}", name, startCron);
|
||||
}
|
||||
});
|
||||
Map<String, IAsyncLoad> asyncLoadMap = SpringUtil.getApplicationContext().getBeansOfType(IAsyncLoad.class);
|
||||
|
@ -22,7 +22,6 @@ public class LocaleConfig {
|
||||
|
||||
@Bean
|
||||
public ResourceBundleMessageSource messageSource() {
|
||||
//Locale.setDefault(Locale.CHINA);
|
||||
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
|
||||
//设置国际化文件存储路径 resources目录下
|
||||
source.setBasenames("i18n/messages");
|
||||
|
@ -50,8 +50,17 @@ public class I18nMessageUtil {
|
||||
LANGUAGE_OBTAIN.add(LANGUAGE::get);
|
||||
// http 请求获取
|
||||
LANGUAGE_OBTAIN.add(I18nMessageUtil::getLanguageByRequest);
|
||||
// 系统配置获取
|
||||
// Jpom 配置获取
|
||||
LANGUAGE_OBTAIN.add(() -> SystemUtil.get("JPOM_LANG"));
|
||||
// 系统语言
|
||||
LANGUAGE_OBTAIN.add(() -> {
|
||||
Locale locale = Locale.getDefault();
|
||||
String country = locale.getCountry();
|
||||
if (StrUtil.equals("zh", country)) {
|
||||
return "zh-CN";
|
||||
}
|
||||
return "en-US";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,18 +93,6 @@ public class I18nMessageUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取语言
|
||||
*
|
||||
* @param request 请求
|
||||
* @return 合法的语言
|
||||
*/
|
||||
public static String parseLanguage(HttpServletRequest request) {
|
||||
String language = ServletUtil.getHeader(request, Header.ACCEPT_LANGUAGE.getValue(), CharsetUtil.CHARSET_UTF_8);
|
||||
language = StrUtil.emptyToDefault(language, "zh-cn");
|
||||
return normalLanguage(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* 语言格式化
|
||||
*
|
||||
@ -115,6 +112,22 @@ public class I18nMessageUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取语言
|
||||
*
|
||||
* @return 语言
|
||||
*/
|
||||
public static String tryGetLanguage() {
|
||||
String language = null;
|
||||
for (Supplier<String> supplier : LANGUAGE_OBTAIN) {
|
||||
language = supplier.get();
|
||||
if (language != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key信息获取对应语言的内容
|
||||
*
|
||||
@ -125,13 +138,7 @@ public class I18nMessageUtil {
|
||||
if (StrUtil.isEmpty(key)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
String language = null;
|
||||
for (Supplier<String> supplier : LANGUAGE_OBTAIN) {
|
||||
language = supplier.get();
|
||||
if (language != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
String language = tryGetLanguage();
|
||||
language = normalLanguage(language);
|
||||
Locale locale;
|
||||
switch (language) {
|
||||
|
@ -0,0 +1,27 @@
|
||||
package org.dromara.jpom.common.i18n;
|
||||
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
|
||||
/**
|
||||
* @author bwcx_jzy1
|
||||
* @since 2024/6/15
|
||||
*/
|
||||
public class I18nThreadUtil {
|
||||
|
||||
/**
|
||||
* 线程执行(获取父级线程语言)
|
||||
*
|
||||
* @param runnable runnable
|
||||
*/
|
||||
public static void execute(Runnable runnable) {
|
||||
String language = I18nMessageUtil.tryGetLanguage();
|
||||
ThreadUtil.execute(() -> {
|
||||
try {
|
||||
I18nMessageUtil.setLanguage(language);
|
||||
runnable.run();
|
||||
} finally {
|
||||
I18nMessageUtil.clearLanguage();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ import org.dromara.jpom.JpomApplication;
|
||||
import org.dromara.jpom.common.BaseServerController;
|
||||
import org.dromara.jpom.common.ServerConst;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.BuildExtConfig;
|
||||
import org.dromara.jpom.exception.LogRecorderCloseException;
|
||||
import org.dromara.jpom.func.assets.server.MachineDockerServer;
|
||||
@ -905,7 +906,7 @@ public class BuildExecuteManage implements Runnable {
|
||||
|
||||
Opt.ofBlankAble(buildInfoModel.getWebhook())
|
||||
.ifPresent(s ->
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
IPlugin plugin = PluginFactory.getPlugin("webhook");
|
||||
map.put("JPOM_WEBHOOK_EVENT", DefaultWebhookPluginImpl.WebhookEvent.BUILD);
|
||||
|
@ -286,7 +286,7 @@ public class IndexControl extends BaseServerController {
|
||||
UserModel userModel = getUserModel();
|
||||
String workspaceId = nodeService.getCheckUserWorkspace(request);
|
||||
JSONObject config = systemParametersServer.getConfigDefNewInstance(StrUtil.format("menus_config_{}", workspaceId), JSONObject.class);
|
||||
String language = I18nMessageUtil.parseLanguage(request);
|
||||
String language = I18nMessageUtil.tryGetLanguage();
|
||||
// 菜单
|
||||
InputStream inputStream = ResourceUtil.getStream("classpath:/menus/" + language + "/index.json");
|
||||
JSONArray showArray = config.getJSONArray("serverMenuKeys");
|
||||
@ -328,7 +328,7 @@ public class IndexControl extends BaseServerController {
|
||||
@SystemPermission
|
||||
public IJsonMessage<List<Object>> systemMenusData(HttpServletRequest request) {
|
||||
UserModel userModel = getUserModel();
|
||||
String language = I18nMessageUtil.parseLanguage(request);
|
||||
String language = I18nMessageUtil.tryGetLanguage();
|
||||
// 菜单
|
||||
InputStream inputStream = ResourceUtil.getStream("classpath:/menus/" + language + "/system.json");
|
||||
String json = IoUtil.read(inputStream, CharsetUtil.CHARSET_UTF_8);
|
||||
|
@ -22,6 +22,7 @@ import cn.keepbx.jpom.plugins.IPlugin;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.common.validator.ValidatorItem;
|
||||
import org.dromara.jpom.common.validator.ValidatorRule;
|
||||
import org.dromara.jpom.permission.Feature;
|
||||
@ -136,7 +137,7 @@ public abstract class BaseDockerImagesController extends BaseDockerController {
|
||||
logRecorder.system("start pull {}", repository);
|
||||
Consumer<String> logConsumer = logRecorder::info;
|
||||
parameter.put("logConsumer", logConsumer);
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
plugin.execute("pullImage", parameter);
|
||||
logRecorder.system("pull end");
|
||||
|
@ -26,6 +26,7 @@ import cn.keepbx.jpom.plugins.IPlugin;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.common.validator.ValidatorItem;
|
||||
import org.dromara.jpom.common.validator.ValidatorRule;
|
||||
import org.dromara.jpom.permission.Feature;
|
||||
@ -167,7 +168,7 @@ public abstract class BaseDockerSwarmServiceController extends BaseDockerControl
|
||||
parameter.put("timestamps", timestamps);
|
||||
// 操作id
|
||||
parameter.put("uuid", uuid);
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
plugin.execute(StrUtil.equalsIgnoreCase(type, "service") ? "logService" : "logTask", parameter);
|
||||
logRecorder.system("pull end");
|
||||
|
@ -19,6 +19,7 @@ import cn.keepbx.jpom.model.JsonMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.BaseServerController;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.func.files.service.StaticFileStorageService;
|
||||
import org.dromara.jpom.model.data.AgentWhitelist;
|
||||
import org.dromara.jpom.model.data.ServerWhitelist;
|
||||
@ -140,7 +141,7 @@ public class OutGivingWhitelistController extends BaseServerController {
|
||||
|
||||
String resultData = AgentWhitelist.convertToLine(list);
|
||||
// 重新检查静态目录任务状态
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
staticFileStorageService.startLoad();
|
||||
} catch (Exception e) {
|
||||
|
@ -279,7 +279,7 @@ public class WorkspaceController extends BaseServerController {
|
||||
JSONObject config = systemParametersServer.getConfigDefNewInstance(StrUtil.format("menus_config_{}", workspaceId), JSONObject.class);
|
||||
//"classpath:/menus/index.json"
|
||||
//"classpath:/menus/node-index.json"
|
||||
String language = I18nMessageUtil.parseLanguage(request);
|
||||
String language = I18nMessageUtil.tryGetLanguage();
|
||||
config.put("serverMenus", this.readMenusJson("classpath:/menus/" + language + "/index.json"));
|
||||
return JsonMessage.success("", config);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import org.dromara.jpom.common.ServerConst;
|
||||
import org.dromara.jpom.common.forward.NodeForward;
|
||||
import org.dromara.jpom.common.forward.NodeUrl;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.BuildExtConfig;
|
||||
import org.dromara.jpom.func.assets.model.MachineSshModel;
|
||||
import org.dromara.jpom.func.files.model.FileReleaseTaskLogModel;
|
||||
@ -272,7 +273,7 @@ public class FileReleaseTaskService extends BaseWorkspaceService<FileReleaseTask
|
||||
} else {
|
||||
throw new IllegalArgumentException(I18nMessageUtil.get("i18n.unsupported_method.a1de"));
|
||||
}
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
strictSyncFinisher.start();
|
||||
if (cancelTag.containsKey(taskId)) {
|
||||
|
@ -16,7 +16,6 @@ import cn.hutool.core.date.SystemClock;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.io.unit.DataSize;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@ -28,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.JpomApplication;
|
||||
import org.dromara.jpom.common.ServerConst;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.BuildExtConfig;
|
||||
import org.dromara.jpom.func.files.model.FileStorageModel;
|
||||
import org.dromara.jpom.service.IStatusRecover;
|
||||
@ -99,7 +99,7 @@ public class FileStorageService extends BaseGlobalOrWorkspaceService<FileStorage
|
||||
this.insert(fileStorageModel);
|
||||
}
|
||||
// 异步下载
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
File tempPath = configBean.getTempPath();
|
||||
File file = FileUtil.file(tempPath, "file-storage-download", uuid);
|
||||
|
@ -28,6 +28,7 @@ import org.dromara.jpom.common.BaseServerController;
|
||||
import org.dromara.jpom.common.JpomManifest;
|
||||
import org.dromara.jpom.common.forward.NodeUrl;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.common.validator.ValidatorItem;
|
||||
import org.dromara.jpom.common.validator.ValidatorRule;
|
||||
import org.dromara.jpom.configuration.ClusterConfig;
|
||||
@ -197,7 +198,7 @@ public class CacheManageController extends BaseServerController implements ICach
|
||||
@GetMapping(value = "async-refresh-cache", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public IJsonMessage<String> refresh() {
|
||||
Assert.state(!this.refreshCacheIng, I18nMessageUtil.get("i18n.refreshing_cache.c969"));
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
this.refreshCacheIng = true;
|
||||
this.executeTask();
|
||||
|
@ -31,6 +31,7 @@ import org.dromara.jpom.common.Const;
|
||||
import org.dromara.jpom.common.forward.NodeForward;
|
||||
import org.dromara.jpom.common.forward.NodeUrl;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.model.AfterOpt;
|
||||
import org.dromara.jpom.model.data.NodeModel;
|
||||
import org.dromara.jpom.model.log.OutGivingLog;
|
||||
@ -343,7 +344,7 @@ public class OutGivingRun {
|
||||
Opt.ofBlankAble(outGivingModel2.getWebhook())
|
||||
.orElse(null))
|
||||
.ifPresent(webhook ->
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
// outGivingId、outGivingName、status、statusMsg、executeTime
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
map.put("outGivingId", outGivingId);
|
||||
|
@ -19,6 +19,7 @@ import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.model.PageResultDto;
|
||||
import org.dromara.jpom.model.data.MonitorModel;
|
||||
import org.dromara.jpom.model.data.MonitorUserOptModel;
|
||||
@ -203,7 +204,7 @@ public class DbUserOperateLogService extends BaseWorkspaceService<UserOperateLog
|
||||
String email = item.getEmail();
|
||||
if (StrUtil.isNotEmpty(email)) {
|
||||
MonitorModel.Notify notify1 = new MonitorModel.Notify(MonitorModel.NotifyType.mail, email);
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
NotifyUtil.send(notify1, I18nMessageUtil.get("i18n.user_operation_alarm.15b9"), context);
|
||||
} catch (Exception e) {
|
||||
@ -216,7 +217,7 @@ public class DbUserOperateLogService extends BaseWorkspaceService<UserOperateLog
|
||||
String dingDing = item.getDingDing();
|
||||
if (StrUtil.isNotEmpty(dingDing)) {
|
||||
MonitorModel.Notify notify1 = new MonitorModel.Notify(MonitorModel.NotifyType.dingding, dingDing);
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
NotifyUtil.send(notify1, I18nMessageUtil.get("i18n.user_operation_alarm.15b9"), context);
|
||||
} catch (Exception e) {
|
||||
@ -228,7 +229,7 @@ public class DbUserOperateLogService extends BaseWorkspaceService<UserOperateLog
|
||||
String workWx = item.getWorkWx();
|
||||
if (StrUtil.isNotEmpty(workWx)) {
|
||||
MonitorModel.Notify notify1 = new MonitorModel.Notify(MonitorModel.NotifyType.workWx, workWx);
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
NotifyUtil.send(notify1, I18nMessageUtil.get("i18n.user_operation_alarm.15b9"), context);
|
||||
} catch (Exception e) {
|
||||
@ -249,7 +250,7 @@ public class DbUserOperateLogService extends BaseWorkspaceService<UserOperateLog
|
||||
*/
|
||||
public void insert(UserOperateLogV1 userOperateLogV1, OperateLogController.CacheInfo cacheInfo) {
|
||||
super.insert(userOperateLogV1);
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
// 更新用户名和工作空间名
|
||||
try {
|
||||
UserOperateLogV1 update = new UserOperateLogV1();
|
||||
|
@ -21,6 +21,7 @@ import com.jcraft.jsch.Session;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.BaseServerController;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.cron.CronUtils;
|
||||
import org.dromara.jpom.func.assets.model.MachineSshModel;
|
||||
import org.dromara.jpom.model.EnvironmentMapBuilder;
|
||||
@ -197,7 +198,7 @@ public class SshCommandService extends BaseWorkspaceService<CommandModel> implem
|
||||
for (String sshId : sshIds) {
|
||||
this.executeItem(syncFinisher, commandModel, params, sshId, batchId, triggerExecType, envMap);
|
||||
}
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
syncFinisher.start();
|
||||
} catch (Exception e) {
|
||||
|
@ -23,6 +23,7 @@ import cn.keepbx.jpom.plugins.IPlugin;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.func.assets.model.MachineDockerModel;
|
||||
import org.dromara.jpom.func.assets.server.MachineDockerServer;
|
||||
import org.dromara.jpom.model.user.UserModel;
|
||||
@ -113,7 +114,7 @@ public class DockerLogHandler extends BaseProxyHandler {
|
||||
map.put("charset", CharsetUtil.CHARSET_UTF_8);
|
||||
map.put("consumer", consumer);
|
||||
map.put("timestamps", json.getBoolean("timestamps"));
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
attributes.put("thread", Thread.currentThread());
|
||||
IPlugin plugin = PluginFactory.getPlugin(DockerInfoService.DOCKER_PLUGIN_NAME);
|
||||
try {
|
||||
|
@ -26,6 +26,7 @@ import org.dromara.jpom.common.JpomManifest;
|
||||
import org.dromara.jpom.common.forward.NodeForward;
|
||||
import org.dromara.jpom.common.forward.NodeUrl;
|
||||
import org.dromara.jpom.common.i18n.I18nMessageUtil;
|
||||
import org.dromara.jpom.common.i18n.I18nThreadUtil;
|
||||
import org.dromara.jpom.configuration.NodeConfig;
|
||||
import org.dromara.jpom.func.assets.model.MachineNodeModel;
|
||||
import org.dromara.jpom.func.assets.server.MachineNodeServer;
|
||||
@ -117,7 +118,7 @@ public class NodeUpdateHandler extends BaseProxyHandler {
|
||||
return proxySession;
|
||||
});
|
||||
// 连接节点
|
||||
ThreadUtil.execute(() -> {
|
||||
I18nThreadUtil.execute(() -> {
|
||||
try {
|
||||
if (!nodeClient.isConnected()) {
|
||||
nodeClient.reconnectBlocking();
|
||||
|
@ -62,15 +62,13 @@
|
||||
<a-tooltip>
|
||||
<template #title>
|
||||
<ul>
|
||||
<li>Ctrl-F / Cmd-F Start searching</li>
|
||||
<li>Ctrl-G / Cmd-G Find next</li>
|
||||
<li>Shift-Ctrl-G / Shift-Cmd-G Find previous</li>
|
||||
<li>Shift-Ctrl-F / Cmd-Option-F Replace</li>
|
||||
<li>Shift-Ctrl-R / Shift-Cmd-Option-F Replace all</li>
|
||||
<li>
|
||||
Alt-F Persistent search (dialog doesn't autoclose, enter to find next, Shift-Enter to find previous)
|
||||
</li>
|
||||
<li>Alt-G Jump to line</li>
|
||||
<li><b>Ctrl-F / Cmd-F</b> {{ $t('components.codeEditor.index.77c1e63d') }}</li>
|
||||
<li><b>Ctrl-G / Cmd-G</b> {{ $t('components.codeEditor.index.4f85e42e') }}</li>
|
||||
<li><b>Shift-Ctrl-G / Shift-Cmd-G</b> {{ $t('components.codeEditor.index.e9f2ef9a') }}</li>
|
||||
<li><b>Shift-Ctrl-F / Cmd-Option-F</b> {{ $t('components.codeEditor.index.6a91de60') }}</li>
|
||||
<li><b>Shift-Ctrl-R / Shift-Cmd-Option-F</b> {{ $t('components.codeEditor.index.d720ba75') }}</li>
|
||||
<li><b>Alt-F</b> {{ $t('components.codeEditor.index.6bcfa0bc') }}</li>
|
||||
<li><b>Alt-G</b> {{ $t('components.codeEditor.index.e2621574') }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
<QuestionCircleOutlined />
|
||||
|
2
web-vue/src/d.ts/components.d.ts
vendored
2
web-vue/src/d.ts/components.d.ts
vendored
@ -92,6 +92,7 @@ declare module 'vue' {
|
||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||
BarsOutlined: typeof import('@ant-design/icons-vue')['BarsOutlined']
|
||||
BlockOutlined: typeof import('@ant-design/icons-vue')['BlockOutlined']
|
||||
CheckCircleFilled: typeof import('@ant-design/icons-vue')['CheckCircleFilled']
|
||||
CheckCircleOutlined: typeof import('@ant-design/icons-vue')['CheckCircleOutlined']
|
||||
CloudDownloadOutlined: typeof import('@ant-design/icons-vue')['CloudDownloadOutlined']
|
||||
CloudOutlined: typeof import('@ant-design/icons-vue')['CloudOutlined']
|
||||
@ -160,6 +161,7 @@ declare module 'vue' {
|
||||
SolutionOutlined: typeof import('@ant-design/icons-vue')['SolutionOutlined']
|
||||
SortAscendingOutlined: typeof import('@ant-design/icons-vue')['SortAscendingOutlined']
|
||||
SortDescendingOutlined: typeof import('@ant-design/icons-vue')['SortDescendingOutlined']
|
||||
StopFilled: typeof import('@ant-design/icons-vue')['StopFilled']
|
||||
StopOutlined: typeof import('@ant-design/icons-vue')['StopOutlined']
|
||||
SwapOutlined: typeof import('@ant-design/icons-vue')['SwapOutlined']
|
||||
SwitcherOutlined: typeof import('@ant-design/icons-vue')['SwitcherOutlined']
|
||||
|
@ -1913,7 +1913,8 @@
|
||||
"4fc329c6": "Please enter an email address",
|
||||
"b29d1268": "The cluster address has not been configured yet, and the cluster cannot be switched",
|
||||
"ee23f800": "A verification code is required. It will take effect after confirmation of binding",
|
||||
"b34c9126": "Are you sure you want to disable two-step verification? Disabling it will affect the security of your account, and existing mfa keys will be invalidated"
|
||||
"b34c9126": "Are you sure you want to disable two-step verification? Disabling it will affect the security of your account, and existing mfa keys will be invalidated",
|
||||
"8263b5e7": "After switching languages, the page will automatically refresh, and any opened tab menus (tabs) will be cleared together"
|
||||
},
|
||||
"user-log": {
|
||||
"3ce54760": "Function",
|
||||
@ -4599,7 +4600,14 @@
|
||||
"5cb430c4": "Light Eclipse",
|
||||
"e26f389a": "Dark 2 Blackboard",
|
||||
"10adc4a3": "Light color idea",
|
||||
"4ea93630": "Please enter the content"
|
||||
"4ea93630": "Please enter the content",
|
||||
"77c1e63d": "Start Search",
|
||||
"4f85e42e": "Find Next",
|
||||
"e9f2ef9a": "Find Previous",
|
||||
"6a91de60": "replace",
|
||||
"d720ba75": "replace all",
|
||||
"6bcfa0bc": "Continuous search (dialog box will not automatically close, press Enter to find next, press Shift Enter to find previous)",
|
||||
"e2621574": "Jump to row"
|
||||
}
|
||||
},
|
||||
"compositionTransfer": {
|
||||
@ -4920,6 +4928,37 @@
|
||||
"e7ba3a8": "Parsing files, preparing to upload",
|
||||
"997a109d": "File upload ID generation failed:",
|
||||
"b6c41759": "Parsing file failed:"
|
||||
},
|
||||
"const-i18n": {
|
||||
"1397b7fa": "Cancel timing and no longer execute on schedule (supports! Prefix disabling scheduled execution, such as:! 0 0/1 * *?)",
|
||||
"f397fdb9": "Minute level",
|
||||
"19a1647f": "1 minute",
|
||||
"edee406c": "5 minutes",
|
||||
"eff3c3f": "10 minutes",
|
||||
"4cda3b42": "30 minutes",
|
||||
"5057c1d0": "Hour level",
|
||||
"f00f01ca": "Every hour",
|
||||
"f661cf9a": "Day level",
|
||||
"597995d3": "At midnight and noon",
|
||||
"c9219cc4": "At midnight",
|
||||
"c2d566f2": "Second level (seconds level is not enabled by default and needs to be modified in the configuration file: [system. timerMatchSecond])",
|
||||
"a5ef245b": "Once every 5 seconds",
|
||||
"222fa259": "Once every 10 seconds",
|
||||
"7a431b98": "Once every 30 seconds",
|
||||
"3d7f1632": "<strong>[Recommendation] WeChat Mini Program Search for Number Shield OTP</strong>",
|
||||
"ee0d1cb6": "<strong>[Recommended] Tencent Authentication Code</strong>Simple and user-friendly<a href=_##_ https://a.app.qq.com/o/simple.jsp?pkgname=com.tencent.authenticator Android</a>",
|
||||
"316edf4e": "<strong>Authy</strong>Rich features specially designed for two-step verification codes<a href=_##_ https://authy.com/download/ \"IOS/Android/Windows/Mac/Linux</a> <a=\" https://chrome.google.com/webstore/detail/authy/gaedmjdfmmahhbjefcbgaolhhanlaolb?hl=cn Chrome extension</a>",
|
||||
"571efa83": "<strong>Google Authenticator</strong>Simple and easy to use, but does not support key export backup<a href=_##_ https://apps.apple.com/us/app/google-authenticator/id388497605 \"IOS</a><a=\" https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2& ; Hl=cn>Android</a>",
|
||||
"4c20714f": "<strong>Microsoft Authenticator</strong>Recommendation for using Microsoft Family Bucket<a href=_##_ https://www.microsoft.com/zh-cn/account/authenticator IOS/Android</a>",
|
||||
"c0f69a6e": "<strong>1Password</strong>Powerful and secure password management paid application<a href=_##_ https://1password.com/zh-cn/downloads/ IOS/Android/Windows/Mac/Linux/ChromeOS</a>",
|
||||
"ba767044": "#ScriptId can be the script file name in the project path or the script template ID in the system",
|
||||
"42cca3ee": "#Execute in the specified directory:/ The project directory/root/specific directory defaults to {'${jpom-agent_data_path} '}/script_run_cache",
|
||||
"5b2ef0bb": "#Number of backup files retained",
|
||||
"c4682b3f": "#Restrict backup of specified file suffixes (supports regularization)",
|
||||
"1f958da6": "#Project file backup path",
|
||||
"ae41f11b": "#Is the log backup function enabled",
|
||||
"95d754a0": "#ScriptId can reference scripts in the script library (G {'@'} xxx), where xxx is the script tag in the script library, provided that the corresponding script is extracted and synchronized to the corresponding machine node",
|
||||
"9daba997": "test"
|
||||
}
|
||||
},
|
||||
"App": {
|
||||
|
@ -1913,7 +1913,8 @@
|
||||
"4fc329c6": "请输入邮箱地址",
|
||||
"b29d1268": "还未配置集群地址,不能切换集群",
|
||||
"ee23f800": "需要输入验证码,确认绑定后才生效奥",
|
||||
"b34c9126": "确定要关闭两步验证吗?关闭后账号安全性将受到影响,关闭后已经存在的 mfa key 将失效"
|
||||
"b34c9126": "确定要关闭两步验证吗?关闭后账号安全性将受到影响,关闭后已经存在的 mfa key 将失效",
|
||||
"8263b5e7": "切换语言后页面将自动刷新,已经打开的标签菜单(选项卡)将一并清空"
|
||||
},
|
||||
"user-log": {
|
||||
"3ce54760": "操作功能",
|
||||
@ -4599,7 +4600,14 @@
|
||||
"5cb430c4": "浅色 eclipse",
|
||||
"e26f389a": "深色2 blackboard",
|
||||
"10adc4a3": "浅色 idea",
|
||||
"4ea93630": "请输入内容"
|
||||
"4ea93630": "请输入内容",
|
||||
"77c1e63d": " 开始搜索",
|
||||
"4f85e42e": " 查找下一个",
|
||||
"e9f2ef9a": " 查找上一个",
|
||||
"6a91de60": " 替换",
|
||||
"d720ba75": " 全部替换",
|
||||
"6bcfa0bc": " 持续搜索(对话框不会自动关闭,按 Enter 查找下一个,按 Shift-Enter 查找上一个)",
|
||||
"e2621574": " 跳至行"
|
||||
}
|
||||
},
|
||||
"compositionTransfer": {
|
||||
@ -4914,6 +4922,37 @@
|
||||
"acabc771": "秒",
|
||||
"4a28d11c": "毫秒"
|
||||
},
|
||||
"const-i18n": {
|
||||
"1397b7fa": "取消定时,不再定时执行(支持 ! 前缀禁用定时执行,如:!0 0/1 * * * ?)",
|
||||
"f397fdb9": "分钟级别",
|
||||
"19a1647f": "1分钟",
|
||||
"edee406c": "5分钟",
|
||||
"eff3c3f": "10分钟",
|
||||
"4cda3b42": "30分钟",
|
||||
"5057c1d0": "小时级别",
|
||||
"f00f01ca": "每小时",
|
||||
"f661cf9a": "天级别",
|
||||
"597995d3": "凌晨0点和中午12点",
|
||||
"c9219cc4": "凌晨0点",
|
||||
"c2d566f2": "秒级别(默认未开启秒级别,需要去修改配置文件中:[system.timerMatchSecond])",
|
||||
"a5ef245b": "5秒一次",
|
||||
"222fa259": "10秒一次",
|
||||
"7a431b98": "30秒一次",
|
||||
"3d7f1632": "<strong>【推荐】微信小程序搜索 数盾OTP</strong>",
|
||||
"ee0d1cb6": "<strong>【推荐】腾讯身份验证码</strong> 简单好用 <a href=_##_https://a.app.qq.com/o/simple.jsp?pkgname=com.tencent.authenticator\">Android</a>",
|
||||
"316edf4e": "<strong>Authy</strong> 功能丰富 专为两步验证码 <a href=_##_https://authy.com/download/\">iOS/Android/Windows/Mac/Linux</a> <a href=\"https://chrome.google.com/webstore/detail/authy/gaedmjdfmmahhbjefcbgaolhhanlaolb?hl=cn\">Chrome 扩展</a>",
|
||||
"571efa83": "<strong>Google Authenticator</strong> 简单易用,但不支持密钥导出备份 <a href=_##_https://apps.apple.com/us/app/google-authenticator/id388497605\">iOS</a> <a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=cn\">Android</a>",
|
||||
"4c20714f": "<strong>Microsoft Authenticator</strong> 使用微软全家桶的推荐 <a href=_##_https://www.microsoft.com/zh-cn/account/authenticator\">iOS/Android</a>",
|
||||
"c0f69a6e": "<strong>1Password</strong> 强大安全的密码管理付费应用<a href=_##_https://1password.com/zh-cn/downloads/\">iOS/Android/Windows/Mac/Linux/ChromeOS</a>",
|
||||
"ba767044": "# scriptId 可以是项目路径下脚本文件名或者系统中的脚本模版ID",
|
||||
"42cca3ee": "# 在指定目录执行: ./ 项目目录 /root/ 特定目录 默认在 {'${jpom_agent_data_path}'}/script_run_cache ",
|
||||
"5b2ef0bb": "# 备份文件保留个数",
|
||||
"c4682b3f": "# 限制备份指定文件后缀(支持正则)",
|
||||
"1f958da6": "# 项目文件备份路径",
|
||||
"ae41f11b": "# 是否开启日志备份功能",
|
||||
"95d754a0": "# scriptId 可以引用脚本库中的脚本(G{'@'}xxx)其中 xxx 为脚本库中的脚本标记,前提需要提取将对应脚本同步至对应机器节点",
|
||||
"9daba997": "测试"
|
||||
},
|
||||
"upload-pieces": {
|
||||
"ea2d7f82": "文件不能为空",
|
||||
"f3b7ae3a": "您的浏览器版本太低,不支持该功能",
|
||||
|
@ -483,11 +483,12 @@
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('pages.layout.user-header.ee091a03')">
|
||||
<a-space>
|
||||
<a-select v-model:value="locale">
|
||||
<a-select-option v-for="item in supportLang" :key="item.value">{{ item.label }}</a-select-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
<template #help>{{ $t('pages.layout.user-header.8263b5e7') }}</template>
|
||||
<a-radio-group v-model:value="locale" button-style="solid">
|
||||
<a-radio-button v-for="item in supportLang" :key="item.value" :value="item.value">{{
|
||||
item.label
|
||||
}}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
@ -4,23 +4,24 @@ import { t } from '@/i18n'
|
||||
* mfa app 应用举例
|
||||
*/
|
||||
export const MFA_APP_TIP_ARRAY = [
|
||||
t('utils.const.3d7f1632'),
|
||||
t('utils.const.ee0d1cb6'),
|
||||
t('utils.const.316edf4e'),
|
||||
t('utils.const.571efa83'),
|
||||
t('utils.const.4c20714f'),
|
||||
t('utils.const.c0f69a6e')
|
||||
t('utils.const-i18n.3d7f1632'),
|
||||
t('utils.const-i18n.ee0d1cb6'),
|
||||
t('utils.const-i18n.316edf4e'),
|
||||
t('utils.const-i18n.571efa83'),
|
||||
t('utils.const-i18n.4c20714f'),
|
||||
t('utils.const-i18n.c0f69a6e')
|
||||
]
|
||||
|
||||
/**
|
||||
* 项目 DSL 示例
|
||||
*/
|
||||
export const PROJECT_DSL_DEFATUL =
|
||||
t('utils.const.ba767044') +
|
||||
t('utils.const-i18n.ba767044') +
|
||||
'\n' +
|
||||
t('utils.const.95d754a0') +
|
||||
t('utils.const-i18n.95d754a0') +
|
||||
'\n' +
|
||||
t('utils.const.59e55b4') +
|
||||
'description:' +
|
||||
t('utils.const-i18n.9daba997') +
|
||||
'\n' +
|
||||
'run:\r\n' +
|
||||
' start:\r\n' +
|
||||
@ -55,17 +56,17 @@ export const PROJECT_DSL_DEFATUL =
|
||||
'# scriptEnv:\r\n' +
|
||||
'# "boot_active": test\r\n' +
|
||||
'# fileChangeReload: true\r\n' +
|
||||
t('utils.const.42cca3ee') +
|
||||
t('utils.const-i18n.42cca3ee') +
|
||||
'# execPath: ./\r\n' +
|
||||
'file:\r\n' +
|
||||
t('utils.const.5b2ef0bb') +
|
||||
t('utils.const-i18n.5b2ef0bb') +
|
||||
'# backupCount: 5\r\n' +
|
||||
t('utils.const.c4682b3f') +
|
||||
t('utils.const-i18n.c4682b3f') +
|
||||
"# backupSuffix: [ '.jar','.html','^.+\\.(?i)(txt)$' ]\r\n" +
|
||||
t('utils.const.1f958da6') +
|
||||
t('utils.const-i18n.1f958da6') +
|
||||
'# backupPath: /data/jpom_backup\r\n' +
|
||||
'config:\r\n' +
|
||||
t('utils.const.ae41f11b') +
|
||||
t('utils.const-i18n.ae41f11b') +
|
||||
'# autoBackToFile: true\r\n' +
|
||||
'\r\n'
|
||||
|
||||
@ -76,7 +77,7 @@ export const PROJECT_DSL_DEFATUL =
|
||||
*/
|
||||
export const CRON_DATA_SOURCE = [
|
||||
{
|
||||
title: t('utils.const.1397b7fa'),
|
||||
title: t('utils.const-i18n.1397b7fa'),
|
||||
options: [
|
||||
{
|
||||
title: '',
|
||||
@ -85,61 +86,61 @@ export const CRON_DATA_SOURCE = [
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('utils.const.f397fdb9'),
|
||||
title: t('utils.const-i18n.f397fdb9'),
|
||||
options: [
|
||||
{
|
||||
title: t('utils.const.19a1647f'),
|
||||
title: t('utils.const-i18n.19a1647f'),
|
||||
value: '0 0/1 * * * ?'
|
||||
},
|
||||
{
|
||||
title: t('utils.const.edee406c'),
|
||||
title: t('utils.const-i18n.edee406c'),
|
||||
value: '0 0/5 * * * ?'
|
||||
},
|
||||
{
|
||||
title: t('utils.const.eff3c3f'),
|
||||
title: t('utils.const-i18n.eff3c3f'),
|
||||
value: '0 0/10 * * * ?'
|
||||
},
|
||||
{
|
||||
title: t('utils.const.4cda3b42'),
|
||||
title: t('utils.const-i18n.4cda3b42'),
|
||||
value: '0 0/30 * * * ?'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('utils.const.5057c1d0'),
|
||||
title: t('utils.const-i18n.5057c1d0'),
|
||||
options: [
|
||||
{
|
||||
title: t('utils.const.f00f01ca'),
|
||||
title: t('utils.const-i18n.f00f01ca'),
|
||||
value: '0 0 0/1 * * ?'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('utils.const.f661cf9a'),
|
||||
title: t('utils.const-i18n.f661cf9a'),
|
||||
options: [
|
||||
{
|
||||
title: t('utils.const.597995d3'),
|
||||
title: t('utils.const-i18n.597995d3'),
|
||||
value: '0 0 0,12 * * ?'
|
||||
},
|
||||
{
|
||||
title: t('utils.const.c9219cc4'),
|
||||
title: t('utils.const-i18n.c9219cc4'),
|
||||
value: '0 0 0 * * ?'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: t('utils.const.c2d566f2'),
|
||||
title: t('utils.const-i18n.c2d566f2'),
|
||||
options: [
|
||||
{
|
||||
title: t('utils.const.a5ef245b'),
|
||||
title: t('utils.const-i18n.a5ef245b'),
|
||||
value: '0/5 * * * * ?'
|
||||
},
|
||||
{
|
||||
title: t('utils.const.222fa259'),
|
||||
title: t('utils.const-i18n.222fa259'),
|
||||
value: '0/10 * * * * ?'
|
||||
},
|
||||
{
|
||||
title: t('utils.const.7a431b98'),
|
||||
title: t('utils.const-i18n.7a431b98'),
|
||||
value: '0/30 * * * * ?'
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user