rename com.imyeyu.server to com.imyeyu.api

This commit is contained in:
Timi
2025-07-22 15:26:14 +08:00
parent e816b885b2
commit 323e038e86
340 changed files with 1174 additions and 1175 deletions
@@ -0,0 +1,158 @@
package com.imyeyu.api.modules.mirror;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.TimiServerAPI;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import com.imyeyu.utils.Time;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.core5.http.HttpHost;
import org.springframework.transaction.annotation.Transactional;
/**
* 抽象镜像同步
*
* @author 夜雨
* @version 2024-05-23 14:21
*/
@Slf4j
abstract class AbstractMirror {
/**
* 镜像同步状态
*
* @author 夜雨
* @version 2024-05-23 14:27
*/
public enum Status {
/** 空闲 */
IDLE,
/** 正在同步 */
SYNCING,
/** 成功 */
SUCCESSFUL,
/** 同步失败 */
FAIL
}
/** 上一次同步时间 */
long lastSyncAt = -1;
/** 同步状态 */
Status status = Status.IDLE;
HttpHost proxy = null;
/**
* 初始化
*
* @param mirror 镜像
*/
final void initialize(Mirror mirror) {
status = Status.IDLE;
}
/** @return true 为已初始化 */
final boolean isInitialized() {
return status != null;
}
/**
* 触发同步,由 {@link MirrorSyncTask} 调用
*
* @param mirror 镜像
*/
@Transactional(TimiServerDBConfig.ROLLBACKER)
final void sync0(Mirror mirror) {
long startAt = lastSyncAt = Time.now();
status = Status.SYNCING;
try {
// 预备同步
beforeSync(mirror);
// 同步
sync(mirror);
// 成功
status = Status.SUCCESSFUL;
MirrorService service = TimiServerAPI.applicationContext.getBean(MirrorService.class);
Mirror dbMirror = service.get(mirror.getId());
dbMirror.setLastSyncAt(Time.now());
service.update(dbMirror);
// 同步成功
status = Status.SUCCESSFUL;
onSuccessful(mirror);
} catch (Exception e) {
status = Status.FAIL;
onException(mirror, e);
if (e instanceof TimiException te) {
log.warn("[%s] Fail: %s".formatted(mirror.getBean(), te.getMsg()));
} else {
log.error("[%s] Error".formatted(mirror.getBean()), e);
}
} finally {
onFinally(mirror);
String usedTime = Time.Media.toString(Time.now() - startAt);
log.info("[{}] synced {} in {}", mirror.getBean(), status, usedTime);
if (status == Status.SUCCESSFUL) {
status = Status.IDLE;
}
}
}
/**
* 预备同步
*
* @param mirror 镜像
* @throws Exception 准备异常
*/
protected void beforeSync(Mirror mirror) throws Exception {
// 子类实现
}
/**
* 同步,子类实现
*
* @param mirror 镜像
* @throws Exception 同步异常
*/
protected abstract void sync(Mirror mirror) throws Exception;
/**
* 同步成功
*
* @param mirror 镜像
*/
protected void onSuccessful(Mirror mirror) {
// 子类实现
}
/**
* 同步失败
*
* @param mirror 镜像
* @param e 异常
*/
protected void onException(Mirror mirror, Exception e) {
// 子类实现
}
/**
* 最终执行,触发同步后无论同步结果如何都触发此方法
*
* @param mirror 镜像
*/
protected void onFinally(Mirror mirror) {
// 子类实现
}
}
@@ -0,0 +1,88 @@
package com.imyeyu.api.modules.mirror;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import com.imyeyu.api.TimiServerAPI;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import java.util.List;
/**
* 附件镜像,镜像同步器储存镜像文件时使用此抽象镜像
*
* @author 夜雨
* @version 2024-06-07 23:35
*/
@Slf4j
@Getter
public abstract class AttachmentMirror extends AbstractMirror {
/** 同步结果:新增数量 */
protected int syncAdded = 0;
/** 同步结果:移除数量 */
protected int syncRemoved = 0;
/** 为 true 时同步产生新增或移除更新 */
protected boolean hasUpdated = false;
@Override
protected void beforeSync(Mirror mirror) throws Exception {
syncAdded = syncRemoved = 0;
hasUpdated = false;
}
/**
* 差分新增附件
*
* @param dbFiles 当前数据库附件
* @return 新增附件
* @throws Exception 处理异常
*/
protected abstract List<AttachmentRequest> diffAdd(List<Attachment> dbFiles) throws Exception;
/**
* 差分移除附件
*
* @param dbFiles 当前数据库附件
* @return 移除附件
* @throws Exception 处理异常
*/
protected abstract List<Attachment> diffRemove(List<Attachment> dbFiles) throws Exception;
/**
* 同步,<span style="color: #F30" >子类复现务必继续调用父类方法,保留 super.sync(mirror)</span>,否则附件差分同步无效
*
* @param mirror 镜像
* @throws Exception 同步异常
*/
@Override
protected void sync(Mirror mirror) throws Exception {
AttachmentService attachmentService = TimiServerAPI.applicationContext.getBean(AttachmentService.class);
List<Attachment> dbFiles = attachmentService.listByBizId(Attachment.BizType.MIRROR, mirror.getId());
List<Attachment> diffRemoveList = diffRemove(dbFiles);
syncRemoved = diffRemoveList.size();
for (int i = 0; i < diffRemoveList.size(); i++) {
attachmentService.destroy(diffRemoveList.get(i).getId());
}
List<AttachmentRequest> diffAddList = diffAdd(dbFiles);
syncAdded = diffAddList.size();
for (int i = 0; i < diffAddList.size(); i++) {
AttachmentRequest request = diffAddList.get(i);
request.setBizType(Attachment.BizType.MIRROR);
request.setBizId(mirror.getId());
attachmentService.create(request);
}
hasUpdated = syncAdded != 0 || syncRemoved != 0;
}
@Override
protected void onSuccessful(Mirror mirror) {
log.info("[{}] attachment synced, added: {}, removed: {}", mirror.getBean(), syncAdded, syncRemoved);
}
}
@@ -0,0 +1,150 @@
package com.imyeyu.api.modules.mirror;
import com.google.gson.Gson;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
import com.imyeyu.api.modules.mirror.bean.AttachType;
import com.imyeyu.api.modules.mirror.data.FabricAPI;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.fluent.Request;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Minecraft FabricApi 模组镜像
*
* @author 夜雨
* @version 2024-05-23 14:41
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FabricAPIMirror extends AttachmentMirror {
/** 主域名 */
private static final String FABRIC_DOMAIN = "https://maven.fabricmc.net/";
/** 模组列表 */
private static final String API_LIST = FABRIC_DOMAIN + "/net/fabricmc/fabric-api/fabric-api/maven-metadata.xml";
/** 下载地址 */
private static final String API_DOWNLOAD = "https://github.com/FabricMC/fabric/releases/download/%s/fabric-api-%s.jar";
/** 版本匹配正则 */
private static final Pattern versionRegex = Pattern.compile("^(\\d+\\.){1,2}(\\*|\\d+)$");
private final Gson gson;
private final MirrorService service;
private final AttachmentService attachmentService;
/** 游戏版本: Fabric 版本 */
private final Map<String, String> versionMap = new HashMap<>();
@Override
protected void beforeSync(Mirror mirror) throws Exception {
super.beforeSync(mirror);
Document dom = new SAXReader().read(API_LIST);
Element root = dom.getRootElement();
Element versioning = root.element("versioning");
Element versions = versioning.element("versions");
List<?> versionList = versions.elements("version");
versionMap.clear();
for (int i = 0; i < versionList.size(); i++) {
if (versionList.get(i) instanceof Element el) {
String[] args = el.getTextTrim().split("\\+");
if (versionRegex.matcher(args[1]).matches()) {
versionMap.put(args[1], args[0]);
}
}
}
}
@Override
protected void sync(Mirror mirror) throws Exception {
super.sync(mirror);
if (!hasUpdated) {
return;
}
List<FabricAPI> result = new ArrayList<>();
{
List<Attachment> attachmentList = attachmentService.listByBizId(Attachment.BizType.MIRROR, mirror.getId(), AttachType.FABRIC_API);
for (int i = 0; i < attachmentList.size(); i++) {
Attachment attachment = attachmentList.get(i);
// 附件名:fabric-api-(fabricVer)+(minecraftVer).jar
String[] args = attachment.getName().split("-")[2].replaceAll("\\.jar", "").split("\\+");
FabricAPI item = new FabricAPI();
item.setName(attachment.getName());
item.setFabricVer(args[0]);
item.setMinecraftVer(args[1]);
item.setMongoId(attachment.getMongoId());
result.add(item);
}
}
mirror.setData(gson.toJsonTree(result));
service.update(mirror);
}
@Override
protected List<AttachmentRequest> diffAdd(List<Attachment> dbFiles) throws Exception {
Set<String> dbNameSet = dbFiles.stream().map(Attachment::getName).collect(Collectors.toSet());
List<AttachmentRequest> result = new ArrayList<>();
for (Map.Entry<String, String> item : versionMap.entrySet()) {
String version = "%s+%s".formatted(item.getValue(), item.getKey());
String name = "fabric-api-%s.jar".formatted(version);
if (!dbNameSet.contains(name)) {
String url = API_DOWNLOAD.formatted(version, version);
log.info("Syncing a new fabric-api from {}", url);
byte[] bytes = Request.get(url).viaProxy(proxy).execute().returnContent().asBytes();
AttachmentRequest attachment = new AttachmentRequest();
attachment.setAttachTypeValue(AttachType.FABRIC_API);
attachment.setName(name);
attachment.setSize((long) bytes.length);
attachment.setInputStream(new ByteArrayInputStream(bytes));
result.add(attachment);
}
}
return result;
}
@Override
protected List<Attachment> diffRemove(List<Attachment> dbFiles) {
List<String> versionList = new ArrayList<>();
for (Map.Entry<String, String> item : versionMap.entrySet()) {
String version = "%s+%s".formatted(item.getValue(), item.getKey());
versionList.add("fabric-api-%s.jar".formatted(version));
}
List<Attachment> result = new ArrayList<>();
for (int i = 0; i < dbFiles.size(); i++) {
Attachment attachment = dbFiles.get(i);
if (!versionList.contains(attachment.getName())) {
log.info("Syncing a miss fabric-api for {}", attachment.getName());
result.add(attachment);
}
}
return result;
}
}
@@ -0,0 +1,70 @@
package com.imyeyu.api.modules.mirror;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.imyeyu.java.TimiJava;
import com.imyeyu.api.TimiServerAPI;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import com.imyeyu.utils.Time;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 镜像同步任务触发器
* <p>触发器周期为每分钟轮询一次,镜像同步周期由 {@link Mirror#setPeriod(int)} 决定
*
* @author 夜雨
* @version 2024-05-23 14:22
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MirrorSyncTask implements SchedulingConfigurer, TimiJava {
private final MirrorService service;
private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> {
List<Mirror> mirrors = service.listAll();
long now = Time.now();
for (int i = 0; i < mirrors.size(); i++) {
Mirror dbMirror = mirrors.get(i);
if (!dbMirror.isEnable()) {
continue;
}
try {
Object bean = TimiServerAPI.applicationContext.getBean(Class.forName(dbMirror.getBean()));
if (bean instanceof AbstractMirror mirror) {
if (!mirror.isInitialized()) {
mirror.initialize(dbMirror);
}
if (now - mirror.lastSyncAt < dbMirror.getPeriod() * Time.M) {
continue;
}
switch (mirror.status) {
case IDLE -> {
log.info("[{}] Starting..", dbMirror.getBean());
threadPoolTaskExecutor.execute(() -> mirror.sync0(dbMirror));
}
case SYNCING -> log.warn("[%s] is syncing, skipped in this period. This task maybe need more time to finish work");
case FAIL -> {
log.info("[{}] Retrying..", dbMirror.getBean());
threadPoolTaskExecutor.execute(() -> mirror.sync0(dbMirror));
}
}
}
} catch (ClassNotFoundException e) {
log.error("[%s] Initialization fail".formatted(dbMirror.getBean()), e);
}
}
}, tc -> new CronTrigger("0 * * * * ?").nextExecution(tc));
}
}
@@ -0,0 +1,119 @@
package com.imyeyu.api.modules.mirror;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.api.modules.mirror.data.OpenJDK;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import com.imyeyu.utils.OS;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.util.Timeout;
import org.springframework.stereotype.Service;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Github JDK 镜像,仅同步下载链接等信息,不储存文件
*
* @author 夜雨
* @version 2024-06-10 10:51
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OpenJDKGithubMirror extends AbstractMirror {
/** 版本发布列表接口,插值 {@link #REPOS_MAP} 的值 */
private static final String API_RELEASE = "https://api.github.com/repos/adoptium/%s/releases?page=1";
/** 版本仓库映射,key 为版本,value 为对应仓库,只读 */
private static final Map<String, String> REPOS_MAP = Collections.unmodifiableMap(new HashMap<>() {{
put("8", "temurin8-binaries");
put("11", "temurin11-binaries");
put("17", "temurin17-binaries");
put("21", "temurin21-binaries");
}});
private final Gson gson;
private final MirrorService service;
@Override
protected void sync(Mirror mirror) throws Exception {
mirror.setData(gson.toJsonTree(fetch()));
service.update(mirror);
}
/**
* 获取 {@link OpenJDK} 列表,{@link OpenJDKMirror} 也会使用此接口
*
* @return jdk 列表
* @throws Exception 获取异常
*/
final List<OpenJDK> fetch() throws Exception {
List<OpenJDK> result = new ArrayList<>();
for (Map.Entry<String, String> repo : REPOS_MAP.entrySet()) {
String respText = Request.get(API_RELEASE.formatted(repo.getValue())).connectTimeout(Timeout.ofSeconds(60)).execute().returnContent().asString();
JsonArray root = JsonParser.parseString(respText).getAsJsonArray();
JsonObject itemRel = null;
for (JsonElement el : root) {
itemRel = el.getAsJsonObject();
if (itemRel.get("prerelease").getAsBoolean() || itemRel.get("draft").getAsBoolean()) {
// 忽略草稿或预发布版本
itemRel = null;
} else {
break;
}
}
if (itemRel == null) {
throw new TimiException(TimiCode.ERROR, "not found release item for " + repo.getValue());
}
JsonArray assets = itemRel.get("assets").getAsJsonArray();
for (JsonElement asset : assets) {
JsonObject itemAsset = asset.getAsJsonObject();
// OpenJDK21U-jdk_x64_windows_hotspot_21.0.3_9.zip
String name = itemAsset.get("name").getAsString();
if (!name.contains("-") || !name.contains("_")) {
continue;
}
if (!name.contains("x64")) {
continue;
}
if (!name.endsWith(".zip") && !name.endsWith(".tar.gz")) {
continue;
}
String[] split = name.split("-")[1].split("_");
if (split.length < 3) {
continue;
}
OS.Platform platform = Ref.toType(OS.Platform.class, split[2].toUpperCase());
OpenJDK.Type type = Ref.toType(OpenJDK.Type.class, split[0].toUpperCase());
if (platform != null && type != null) {
OpenJDK jdk = new OpenJDK();
jdk.setPlatform(platform);
jdk.setType(type);
jdk.setName(name);
jdk.setVersion(repo.getKey());
jdk.setData(URLDecoder.decode(itemAsset.get("browser_download_url").getAsString(), StandardCharsets.UTF_8));
result.add(jdk);
}
}
}
return result;
}
}
@@ -0,0 +1,110 @@
package com.imyeyu.api.modules.mirror;
import com.google.gson.Gson;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
import com.imyeyu.api.modules.mirror.bean.AttachType;
import com.imyeyu.api.modules.mirror.data.OpenJDK;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.fluent.Request;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 本地 JDK 镜像,使用 {@link OpenJDKGithubMirror} 镜像同步
*
* @author 夜雨
* @version 2024-06-11 10:15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OpenJDKMirror extends AttachmentMirror {
private final Gson gson;
private final MirrorService service;
private final AttachmentService attachmentService;
private final OpenJDKGithubMirror githubMirror;
/** Github 镜像结果 */
private List<OpenJDK> githubMirrorResult;
@Override
protected void beforeSync(Mirror mirror) throws Exception {
super.beforeSync(mirror);
// 本地镜像来自 Github
githubMirrorResult = githubMirror.fetch();
}
@Override
protected void sync(Mirror mirror) throws Exception {
super.sync(mirror);
if (!hasUpdated) {
return;
}
Map<String, OpenJDK> githubNameMap = githubMirrorResult.stream().collect(Collectors.toMap(OpenJDK::getName, item -> item));
List<OpenJDK> result = new ArrayList<>();
{
List<Attachment> attachmentList = attachmentService.listByBizId(Attachment.BizType.MIRROR, mirror.getId(), AttachType.OPEN_JDK);
for (int i = 0; i < attachmentList.size(); i++) {
Attachment attachment = attachmentList.get(i);
OpenJDK jdk = githubNameMap.get(attachment.getName());
jdk.setData(attachment.getMongoId());
result.add(jdk);
}
}
mirror.setData(gson.toJsonTree(result));
service.update(mirror);
}
@Override
protected List<AttachmentRequest> diffAdd(List<Attachment> dbFiles) throws Exception {
Map<String, OpenJDK> githubNameMap = githubMirrorResult.stream().collect(Collectors.toMap(OpenJDK::getName, item -> item));
Set<String> dbNameSet = dbFiles.stream().map(Attachment::getName).collect(Collectors.toSet());
List<AttachmentRequest> result = new ArrayList<>();
for (Map.Entry<String, OpenJDK> item : githubNameMap.entrySet()) {
if (!dbNameSet.contains(item.getKey())) {
String url = item.getValue().getData();
log.info("Syncing a new open-jdk from {}", url);
byte[] bytes = Request.get(url).viaProxy(proxy).execute().returnContent().asBytes();
AttachmentRequest attachment = new AttachmentRequest();
attachment.setAttachTypeValue(AttachType.OPEN_JDK);
attachment.setName(item.getKey());
attachment.setSize((long) bytes.length);
attachment.setInputStream(new ByteArrayInputStream(bytes));
result.add(attachment);
}
}
return result;
}
@Override
protected List<Attachment> diffRemove(List<Attachment> dbFiles) {
Set<String> githubNameSet = githubMirrorResult.stream().map(OpenJDK::getName).collect(Collectors.toSet());
List<Attachment> result = new ArrayList<>();
for (int i = 0; i < dbFiles.size(); i++) {
Attachment attachment = dbFiles.get(i);
if (!githubNameSet.contains(attachment.getName())) {
log.info("Syncing a miss open-jdk for {}", attachment.getName());
result.add(attachment);
}
}
return result;
}
}
@@ -0,0 +1,83 @@
package com.imyeyu.api.modules.mirror;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.imyeyu.api.modules.mirror.data.OpenJDK;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import com.imyeyu.utils.OS;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* OpenJDK 清华大学源镜像,仅同步下载链接,不储存文件
*
* @author 夜雨
* @version 2024-06-11 10:48
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class OpenJDKTunaMirror extends AbstractMirror {
/**
* 页面地址模板,插值参数(小写)
* <ol>
* <li>{@link #VERSIONS}</li>
* <li>{@link OpenJDK.Type}</li>
* <li>{@link com.imyeyu.utils.OS.Platform}</li>
* </ol>
*
*/
private static final String PAGE_URL_TEMPLATE = "https://mirrors.tuna.tsinghua.edu.cn/Adoptium/%s/%s/x64/%s/";
/** 版本列表 */
private static final String[] VERSIONS = {"8", "11", "17", "21"};
private final Gson gson;
private final MirrorService service;
@Override
protected void sync(Mirror mirror) throws Exception {
List<OpenJDK> result = new ArrayList<>();
for (int i = 0; i < VERSIONS.length; i++) {
OpenJDK.Type[] types = OpenJDK.Type.values();
for (int j = 0; j < types.length; j++) {
OS.Platform[] platforms = OS.Platform.values();
for (int k = 0; k < platforms.length; k++) {
String url = PAGE_URL_TEMPLATE.formatted(VERSIONS[i], types[j].toString().toLowerCase(), platforms[k].toString().toLowerCase());
Document document = Jsoup.connect(url).get();
Element fileList = document.getElementById("list");
Elements linkTDList = fileList.getElementsByClass("link");
for (Element element : linkTDList) {
Elements linkA = element.getElementsByTag("a");
for (Element a : linkA) {
String href = a.attr("href");
if (!href.endsWith(".zip") && !href.endsWith(".tar.gz")) {
continue;
}
OpenJDK jdk = new OpenJDK();
jdk.setPlatform(platforms[k]);
jdk.setType(types[j]);
jdk.setName(href);
jdk.setVersion(VERSIONS[i]);
jdk.setData(url + href);
result.add(jdk);
break;
}
}
}
}
}
mirror.setData(gson.toJsonTree(result));
service.update(mirror);
}
}
@@ -0,0 +1,12 @@
package com.imyeyu.api.modules.mirror.bean;
/**
* @author 夜雨
* @version 2024-05-23 16:54
*/
public enum AttachType {
FABRIC_API,
OPEN_JDK,
}
@@ -0,0 +1,64 @@
package com.imyeyu.api.modules.mirror.controller;
import com.google.gson.JsonElement;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import com.imyeyu.api.modules.mirror.vo.MirrorView;
import com.imyeyu.spring.annotation.RequestRateLimit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* 镜像接口
*
* @author 夜雨
* @version 2024-06-11 12:53
*/
@Slf4j
@RestController
@RequestMapping("/mirror")
@RequiredArgsConstructor
public class MirrorController {
private final MirrorService service;
/**
* 获取镜像信息
*
* @param mirrorName 镜像名称
* @return 镜像信息
*/
@RequestRateLimit
@GetMapping("/{mirrorName}")
public JsonElement get(@PathVariable String mirrorName) {
return service.getByName(mirrorName).getData();
}
/**
* 获取镜像列表
*
* @return 镜像列表
*/
@RequestRateLimit
@GetMapping("/list")
public List<MirrorView> list() {
List<MirrorView> result = new ArrayList<>();
List<Mirror> mirrors = service.listAll();
for (int i = 0; i < mirrors.size(); i++) {
MirrorView target = new MirrorView();
BeanUtils.copyProperties(mirrors.get(i), target);
target.setBean(null);
target.setData(null);
result.add(target);
}
return result;
}
}
@@ -0,0 +1,25 @@
package com.imyeyu.api.modules.mirror.data;
import lombok.Data;
/**
* FabricAPI
*
* @author 夜雨
* @version 2024-06-08 00:03
*/
@Data
public class FabricAPI {
/** 名称 */
private String name;
/** FabricAPI 版本 */
private String fabricVer;
/** Minecraft 版本 */
private String minecraftVer;
/** 映射文件 mongoId */
private String mongoId;
}
@@ -0,0 +1,44 @@
package com.imyeyu.api.modules.mirror.data;
import lombok.Data;
import com.imyeyu.utils.OS;
/**
* OpenJDK
*
* @author 夜雨
* @version 2024-06-10 10:35
*/
@Data
public class OpenJDK {
/**
* 类型
*
* @author 夜雨
* @version 2024-06-10 10:35
*/
public enum Type {
/** 集成开发工具 */
JDK,
/** 运行时 */
JRE
}
/** 类型 */
private Type type;
/** 平台 */
private OS.Platform platform;
/** 版本 */
private String version;
/** 名称 */
private String name;
/** 数据(可能是下载链接,可能是 mongoId) */
private String data;
}
@@ -0,0 +1,35 @@
package com.imyeyu.api.modules.mirror.entity;
import com.google.gson.JsonElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.imyeyu.spring.entity.Entity;
/**
* 镜像
*
* @author 夜雨
* @version 2024-05-23 15:22
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Mirror extends Entity {
/** 执行 JavaBean */
protected String bean;
/** 名称 */
protected String name;
/** 同步数据 */
protected JsonElement data;
/** 周期(分钟) */
protected int period;
/** 上一次同步时间 */
protected Long lastSyncAt;
/** true 为启用同步 */
protected boolean isEnable;
}
@@ -0,0 +1,29 @@
package com.imyeyu.api.modules.mirror.mapper;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* 镜像
*
* @author 夜雨
* @version 2024-05-23 15:21
*/
public interface MirrorMapper extends BaseMapper<Mirror, Long> {
@Select("SELECT * FROM mirror WHERE id = #{id} LIMIT 1")
Mirror select(Long id);
@Update("UPDATE mirror SET data = #{data}, last_sync_at = #{lastSyncAt} WHERE id = #{id} LIMIT 1")
void update(Mirror mirror);
@Select("SELECT * FROM mirror WHERE name = #{name} LIMIT 1")
Mirror queryByName(String name);
@Select("SELECT * FROM mirror WHERE deleted_at IS NULL")
List<Mirror> listAll();
}
@@ -0,0 +1,34 @@
package com.imyeyu.api.modules.mirror.service;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.spring.service.GettableService;
import com.imyeyu.spring.service.UpdatableService;
import java.util.List;
/**
* 镜像服务
*
* @author 夜雨
* @version 2024-05-23 15:21
*/
public interface MirrorService extends GettableService<Mirror, Long>, UpdatableService<Mirror> {
/**
* 根据名称获取
*
* @param name 名称
* @return 镜像
* @throws TimiException 服务异常
*/
Mirror getByName(String name) throws TimiException;
/**
* 获取所有镜像
*
* @return 镜像列表
* @throws TimiException 服务异常
*/
List<Mirror> listAll() throws TimiException;
}
@@ -0,0 +1,40 @@
package com.imyeyu.api.modules.mirror.service.implement;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.modules.mirror.entity.Mirror;
import com.imyeyu.api.modules.mirror.mapper.MirrorMapper;
import com.imyeyu.api.modules.mirror.service.MirrorService;
import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 镜像服务
*
* @author 夜雨
* @version 2024-05-23 15:22
*/
@Service
@RequiredArgsConstructor
public class MirrorServiceImplement extends AbstractEntityService<Mirror, Long> implements MirrorService {
private final MirrorMapper mapper;
@Override
protected BaseMapper<Mirror, Long> mapper() {
return mapper;
}
@Override
public Mirror getByName(String name) throws TimiException {
return mapper.queryByName(name);
}
@Override
public List<Mirror> listAll() throws TimiException {
return mapper.listAll();
}
}
@@ -0,0 +1,12 @@
package com.imyeyu.api.modules.mirror.vo;
import com.imyeyu.api.modules.mirror.entity.Mirror;
/**
* 镜像视图
*
* @author 夜雨
* @version 2024-06-12 10:20
*/
public class MirrorView extends Mirror {
}