mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-08-30 05:22:01 +00:00
225 lines
11 KiB
Java
225 lines
11 KiB
Java
package cn.qaiu.lz;
|
|
|
|
import cn.qaiu.WebClientVertxInit;
|
|
import cn.qaiu.db.pool.JDBCPoolInit;
|
|
import cn.qaiu.lz.common.cache.CacheConfigLoader;
|
|
import cn.qaiu.lz.common.cache.CacheManager;
|
|
import cn.qaiu.lz.common.interceptorImpl.RateLimiter;
|
|
import cn.qaiu.lz.web.config.PlaygroundConfig;
|
|
import cn.qaiu.lz.web.service.DbService;
|
|
import cn.qaiu.lz.web.service.impl.ShoutServiceImpl;
|
|
import cn.qaiu.parser.custom.CustomParserConfig;
|
|
import cn.qaiu.parser.custom.CustomParserRegistry;
|
|
import cn.qaiu.parser.customjs.JsScriptMetadataParser;
|
|
import cn.qaiu.vx.core.Deploy;
|
|
import cn.qaiu.vx.core.util.AsyncServiceUtil;
|
|
import cn.qaiu.vx.core.util.ConfigConstant;
|
|
import cn.qaiu.vx.core.util.VertxHolder;
|
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
|
import io.vertx.core.json.JsonArray;
|
|
import io.vertx.core.json.JsonObject;
|
|
import io.vertx.core.json.jackson.DatabindCodec;
|
|
import io.vertx.core.shareddata.LocalMap;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.Date;
|
|
|
|
import static cn.qaiu.vx.core.util.ConfigConstant.LOCAL;
|
|
|
|
|
|
/**
|
|
* vertx程序入口
|
|
*
|
|
* <br>Create date 2021-05-08 13:00:01
|
|
* @author qaiu yyzy
|
|
*/
|
|
@Slf4j
|
|
public class AppMain {
|
|
|
|
public static void main(String[] args) {
|
|
applyRuntimeLogLevelOverride();
|
|
Deploy deploy = Deploy.instance();
|
|
// 先阻断应用级定时任务,再让 Vert.x 停入口和 verticle。
|
|
deploy.addPreShutdownTask(CacheManager::cancelPeriodicCleanup);
|
|
deploy.addPreShutdownTask(ShoutServiceImpl::cancelCleanup);
|
|
// Vert.x 停完后再关数据库和解析器共享资源,避免请求还在路上就先关底层 client。
|
|
deploy.addPostShutdownTask(() -> JDBCPoolInit.instance().close());
|
|
deploy.addPostShutdownTask(cn.qaiu.parser.customjs.JsParserExecutor::shutdownExecutor);
|
|
deploy.addPostShutdownTask(cn.qaiu.parser.customjs.JsPlaygroundExecutor::shutdownPools);
|
|
deploy.addPostShutdownTask(cn.qaiu.parser.customjs.JsHttpClient::shutdownSharedClient);
|
|
deploy.addPostShutdownTask(cn.qaiu.parser.PanBase::shutdownSharedClients);
|
|
deploy.addPostShutdownTask(cn.qaiu.parser.IPanTool::shutdownCloseAfterScheduler);
|
|
deploy.addPostShutdownTask(cn.qaiu.parser.impl.PodTool::shutdownWorkerExecutor);
|
|
// start
|
|
deploy.start(args, AppMain::exec);
|
|
}
|
|
|
|
private static void applyRuntimeLogLevelOverride() {
|
|
String levelName = System.getProperty("NFD_LOG_LEVEL");
|
|
if (levelName == null || levelName.isBlank()) {
|
|
levelName = System.getenv("NFD_LOG_LEVEL");
|
|
}
|
|
if (levelName == null || levelName.isBlank()) {
|
|
return;
|
|
}
|
|
try {
|
|
var level = ch.qos.logback.classic.Level.toLevel(levelName, null);
|
|
if (level == null) {
|
|
log.warn("忽略无效的 NFD_LOG_LEVEL: {}", levelName);
|
|
return;
|
|
}
|
|
var logger = LoggerFactory.getLogger("cn.qaiu");
|
|
if (logger instanceof ch.qos.logback.classic.Logger logbackLogger) {
|
|
logbackLogger.setLevel(level);
|
|
log.info("cn.qaiu 日志级别已覆盖为 {}", level);
|
|
}
|
|
} catch (Exception e) {
|
|
log.warn("覆盖 cn.qaiu 日志级别失败: {}", e.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 框架回调方法
|
|
* 初始化数据库/缓存等
|
|
*
|
|
* @param jsonObject 配置
|
|
*/
|
|
private static void exec(JsonObject jsonObject) {
|
|
WebClientVertxInit.init(VertxHolder.getVertxInstance());
|
|
DatabindCodec.mapper().registerModule(new JavaTimeModule());
|
|
// 演练场配置要先加载,后续启动流程才能按开关决定是否注册动态解析器。
|
|
PlaygroundConfig.loadFromJson(jsonObject);
|
|
// 限流
|
|
if (jsonObject.containsKey("rateLimit")) {
|
|
JsonObject rateLimit = jsonObject.getJsonObject("rateLimit");
|
|
RateLimiter.init(rateLimit);
|
|
}
|
|
// 数据库
|
|
if (jsonObject.getJsonObject(ConfigConstant.SERVER).getBoolean("enableDatabase")) {
|
|
JDBCPoolInit.builder().config(jsonObject.getJsonObject("dataSource"))
|
|
.build()
|
|
.initPool().onSuccess(PreparedStatement -> {
|
|
VertxHolder.getVertxInstance().setTimer(1000, id -> {
|
|
System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"));
|
|
System.out.println("数据库连接成功");
|
|
|
|
// 加载演练场解析器
|
|
String addr = jsonObject.getJsonObject(ConfigConstant.SERVER).getString("domainName");
|
|
if (addr == null || addr.isBlank()) {
|
|
addr = "http://127.0.0.1:" + jsonObject.getJsonObject(ConfigConstant.SERVER).getInteger("port", 6400);
|
|
}
|
|
// 读取代理配置获取前端页面端口(同步读取小文件,仅启动时执行一次)
|
|
String proxyConfName = jsonObject.getString("proxyConf", "server-proxy");
|
|
String pageAddr = addr;
|
|
try {
|
|
String configFile = proxyConfName + ".yml";
|
|
// 与 Deploy 保持一致:优先当前目录,其次 resources/
|
|
Path configPath = Path.of(configFile);
|
|
if (!Files.exists(configPath)) {
|
|
configPath = Path.of("resources", configFile);
|
|
}
|
|
if (Files.exists(configPath)) {
|
|
String yamlContent = Files.readString(configPath);
|
|
// 匹配项目约定的 YAML 格式: "- listen: 8080"
|
|
java.util.regex.Matcher m = java.util.regex.Pattern
|
|
.compile("^\\s*-\\s+listen:\\s*(\\d+)", java.util.regex.Pattern.MULTILINE)
|
|
.matcher(yamlContent);
|
|
if (m.find()) {
|
|
int pagePort = Integer.parseInt(m.group(1));
|
|
pageAddr = "http://127.0.0.1:" + pagePort;
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
log.warn("读取代理配置失败,使用默认页面地址: {}", e.getMessage());
|
|
}
|
|
if (PlaygroundConfig.getInstance().isEnabled()) {
|
|
loadPlaygroundParsers(pageAddr);
|
|
} else {
|
|
log.info("演练场功能已禁用,跳过加载演练场解析器");
|
|
log.info("服务已启动,可通过 {} 访问页面", pageAddr);
|
|
}
|
|
System.out.println("启动成功: \n本地服务地址: " + addr);
|
|
});
|
|
});
|
|
}
|
|
// 缓存
|
|
if (jsonObject.containsKey(ConfigConstant.CACHE)) {
|
|
CacheConfigLoader.init(jsonObject.getJsonObject(ConfigConstant.CACHE));
|
|
}
|
|
|
|
LocalMap<Object, Object> localMap = VertxHolder.getVertxInstance().sharedData().getLocalMap(LOCAL);
|
|
// 代理
|
|
if (jsonObject.containsKey(ConfigConstant.PROXY)) {
|
|
JsonArray proxyJsonArray = jsonObject.getJsonArray(ConfigConstant.PROXY);
|
|
if (proxyJsonArray != null) {
|
|
JsonObject jsonObject1 = new JsonObject();
|
|
proxyJsonArray.forEach(proxyJson -> {
|
|
String panTypes = ((JsonObject)proxyJson).getString("panTypes");
|
|
|
|
if (!panTypes.isEmpty()) {
|
|
for (String s : panTypes.split(",")) {
|
|
jsonObject1.put(s, proxyJson);
|
|
}
|
|
}
|
|
});
|
|
localMap.put("proxy", jsonObject1);
|
|
}
|
|
}
|
|
|
|
// 认证
|
|
if (jsonObject.containsKey(ConfigConstant.AUTHS)) {
|
|
JsonObject auths = jsonObject.getJsonObject(ConfigConstant.AUTHS);
|
|
localMap.put(ConfigConstant.AUTHS, auths);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 在启动时加载所有已发布的演练场解析器
|
|
*/
|
|
private static void loadPlaygroundParsers(String accessAddr) {
|
|
DbService dbService = AsyncServiceUtil.getAsyncServiceInstance(DbService.class);
|
|
|
|
dbService.getEnabledPlaygroundParsersForLoad().onSuccess(result -> {
|
|
JsonArray parsers = result.getJsonArray("data");
|
|
if (parsers != null) {
|
|
int loadedCount = 0;
|
|
for (int i = 0; i < parsers.size(); i++) {
|
|
JsonObject parser = parsers.getJsonObject(i);
|
|
|
|
try {
|
|
String jsCode = parser.getString("jsCode");
|
|
if (jsCode == null || jsCode.trim().isEmpty()) {
|
|
log.error("加载演练场解析器失败: {} - JavaScript代码为空", parser.getString("name"));
|
|
continue;
|
|
}
|
|
CustomParserConfig config = JsScriptMetadataParser.parseScript(jsCode);
|
|
CustomParserRegistry.register(config);
|
|
loadedCount++;
|
|
log.info("已加载演练场解析器: {} ({})",
|
|
config.getDisplayName(), config.getType());
|
|
} catch (Exception e) {
|
|
String parserName = parser.getString("name");
|
|
String errorMsg = e.getMessage();
|
|
log.error("加载演练场解析器失败: {} - {}", parserName, errorMsg, e);
|
|
// 如果是require相关错误,提供更详细的提示
|
|
if (errorMsg != null && errorMsg.contains("require")) {
|
|
log.error("提示:演练场解析器不支持CommonJS模块系统(require),请确保代码使用ES5.1语法");
|
|
}
|
|
}
|
|
}
|
|
log.info("演练场解析器加载完成,共加载 {} 个解析器", loadedCount);
|
|
} else {
|
|
log.info("未找到已发布的演练场解析器");
|
|
}
|
|
}).onFailure(e -> {
|
|
log.error("加载演练场解析器列表失败", e);
|
|
}).onComplete(ar -> {
|
|
log.info("服务已启动,可通过 {} 访问页面", accessAddr);
|
|
});
|
|
}
|
|
}
|