mirror of
https://github.com/HangarMC/Hangar.git
synced 2024-12-27 07:03:26 +08:00
Fixup health page
This commit is contained in:
parent
c16ca10f36
commit
89b9b6ad51
@ -144,12 +144,10 @@ public class AdminController extends HangarComponent {
|
||||
@PermissionRequired(NamedPermission.VIEW_HEALTH)
|
||||
@GetMapping(path = "/health", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public HealthReport getHealthReport() {
|
||||
final List<UnhealthyProject> noTopicProjects = this.healthService.getProjectsWithoutTopic();
|
||||
final List<UnhealthyProject> staleProjects = this.healthService.getStaleProjects();
|
||||
final List<UnhealthyProject> nonPublicProjects = this.healthService.getNonPublicProjects();
|
||||
final List<MissingFileCheck> missingFiles = this.healthService.getVersionsWithMissingFiles();
|
||||
final List<JobTable> erroredJobs = this.jobService.getErroredJobs();
|
||||
return new HealthReport(noTopicProjects, staleProjects, nonPublicProjects, missingFiles, erroredJobs);
|
||||
return new HealthReport(staleProjects, missingFiles, erroredJobs);
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
|
@ -14,19 +14,6 @@ import org.springframework.stereotype.Repository;
|
||||
@RegisterConstructorMapper(UnhealthyProject.class)
|
||||
public interface HealthDAO {
|
||||
|
||||
@SqlQuery(" SELECT p.owner_name \"owner\"," +
|
||||
" p.slug," +
|
||||
" p.topic_id," +
|
||||
" p.post_id," +
|
||||
" coalesce(hp.last_updated, p.created_at) AS last_updated," +
|
||||
" p.visibility" +
|
||||
" FROM projects p" +
|
||||
" JOIN home_projects hp ON p.id = hp.id" +
|
||||
" WHERE p.topic_id IS NULL OR " +
|
||||
" p.post_id IS NULL" +
|
||||
" ORDER BY p.created_at DESC")
|
||||
List<UnhealthyProject> getProjectsWithoutTopic();
|
||||
|
||||
@SqlQuery(" SELECT p.owner_name \"owner\"," +
|
||||
" p.slug," +
|
||||
" p.topic_id," +
|
||||
@ -55,18 +42,20 @@ public interface HealthDAO {
|
||||
@RegisterConstructorMapper(MissingFileCheck.class)
|
||||
@SqlQuery("""
|
||||
SELECT pv.version_string,
|
||||
pvd.file_name,
|
||||
p.owner_name "owner",
|
||||
p.slug,
|
||||
p.name,
|
||||
pq.platform
|
||||
array_agg(pvpd.file_name) as fileNames,
|
||||
array_agg(DISTINCT pvpd.platform) AS platforms
|
||||
FROM project_versions pv
|
||||
JOIN projects p ON pv.project_id = p.id
|
||||
JOIN (SELECT DISTINCT plv.platform, pvpd.version_id
|
||||
FROM project_version_platform_dependencies pvpd
|
||||
JOIN platform_versions plv ON pvpd.platform_version_id = plv.id) pq ON pv.id = pq.version_id
|
||||
JOIN project_version_downloads pvd ON pvd.version_id = pq.version_id
|
||||
WHERE pvd.file_name IS NOT NULL
|
||||
ORDER BY pv.created_at DESC""")
|
||||
JOIN projects p ON p.id = pv.project_id
|
||||
LEFT JOIN (
|
||||
SELECT DISTINCT ON (pvpd.download_id) pvpd.id, pvpd.version_id, pvpd.platform, pvd.file_name
|
||||
FROM project_version_platform_downloads pvpd
|
||||
LEFT JOIN project_version_downloads pvd ON pvd.id = pvpd.download_id AND pvd.file_name IS NOT NULL
|
||||
WHERE pvd.id IS NOT NULL
|
||||
ORDER BY pvpd.download_id
|
||||
) pvpd ON pvpd.version_id = pv.id
|
||||
WHERE pvpd.id IS NOT NULL
|
||||
GROUP BY p.id, pv.id""")
|
||||
List<MissingFileCheck> getVersionsForMissingFiles();
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ package io.papermc.hangar.model.internal.admin.health;
|
||||
|
||||
import io.papermc.hangar.model.api.project.ProjectNamespace;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import java.util.List;
|
||||
import org.jdbi.v3.core.mapper.Nested;
|
||||
|
||||
public record MissingFileCheck(Platform platform, String versionString, String fileName, @Nested ProjectNamespace namespace, String name) {
|
||||
public record MissingFileCheck(@Nested ProjectNamespace namespace, String versionString, List<Platform> platforms, List<String> fileNames) {
|
||||
}
|
||||
|
@ -5,9 +5,7 @@ import io.papermc.hangar.model.internal.admin.health.MissingFileCheck;
|
||||
import io.papermc.hangar.model.internal.admin.health.UnhealthyProject;
|
||||
import java.util.List;
|
||||
|
||||
public record HealthReport(List<UnhealthyProject> noTopicProjects,
|
||||
List<UnhealthyProject> staleProjects,
|
||||
List<UnhealthyProject> nonPublicProjects,
|
||||
public record HealthReport(List<UnhealthyProject> staleProjects,
|
||||
List<MissingFileCheck> missingFiles,
|
||||
List<JobTable> erroredJobs) {
|
||||
}
|
||||
|
@ -2,10 +2,12 @@ package io.papermc.hangar.service.internal.admin;
|
||||
|
||||
import io.papermc.hangar.HangarComponent;
|
||||
import io.papermc.hangar.db.dao.internal.HealthDAO;
|
||||
import io.papermc.hangar.model.common.Platform;
|
||||
import io.papermc.hangar.model.internal.admin.health.MissingFileCheck;
|
||||
import io.papermc.hangar.model.internal.admin.health.UnhealthyProject;
|
||||
import io.papermc.hangar.service.internal.file.FileService;
|
||||
import io.papermc.hangar.service.internal.uploads.ProjectFiles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -24,10 +26,6 @@ public class HealthService extends HangarComponent {
|
||||
this.fileService = fileService;
|
||||
}
|
||||
|
||||
public List<UnhealthyProject> getProjectsWithoutTopic() {
|
||||
return this.healthDAO.getProjectsWithoutTopic();
|
||||
}
|
||||
|
||||
public List<UnhealthyProject> getStaleProjects() {
|
||||
return this.healthDAO.getStaleProjects("'" + this.config.projects.staleAge().toSeconds() + " SECONDS'");
|
||||
}
|
||||
@ -38,9 +36,23 @@ public class HealthService extends HangarComponent {
|
||||
|
||||
public List<MissingFileCheck> getVersionsWithMissingFiles() {
|
||||
final List<MissingFileCheck> missingFileChecks = this.healthDAO.getVersionsForMissingFiles();
|
||||
return missingFileChecks.stream().filter(mfc -> {
|
||||
final String path = this.projectFiles.getVersionDir(mfc.namespace().getOwner(), mfc.name(), mfc.versionString(), mfc.platform());
|
||||
return !this.fileService.exists(this.fileService.resolve(path, mfc.fileName()));
|
||||
return missingFileChecks.stream().filter(missingFileCheck -> {
|
||||
final List<Platform> platforms = missingFileCheck.platforms();
|
||||
final List<Platform> missingFilePlatforms = new ArrayList<>();
|
||||
final List<String> fileNames = missingFileCheck.fileNames();
|
||||
for (int i = 0; i < platforms.size(); i++) {
|
||||
final Platform platform = platforms.get(i);
|
||||
final String path = this.projectFiles.getVersionDir(missingFileCheck.namespace().getOwner(), missingFileCheck.namespace().getSlug(), missingFileCheck.versionString(), platform);
|
||||
if (!this.fileService.exists(this.fileService.resolve(path, fileNames.get(i)))) {
|
||||
missingFilePlatforms.add(platform);
|
||||
}
|
||||
}
|
||||
|
||||
if (!missingFilePlatforms.isEmpty()) {
|
||||
platforms.retainAll(missingFilePlatforms);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
@ -23,34 +23,22 @@ useHead(useSeo(i18n.t("health.title"), null, route, null));
|
||||
<template>
|
||||
<div>
|
||||
<PageTitle>{{ i18n.t("health.title") }}</PageTitle>
|
||||
<div class="grid gap-8 grid-cols-1 md:grid-cols-2">
|
||||
<Card v-if="healthReport">
|
||||
<template #header> {{ i18n.t("health.noTopicProject") }}</template>
|
||||
<div v-if="healthReport" class="grid gap-8 grid-cols-1 md:grid-cols-2">
|
||||
<Card>
|
||||
<template #header> {{ i18n.t("health.missingFileProjects") }}</template>
|
||||
|
||||
<ul class="max-h-xs overflow-auto">
|
||||
<li v-for="project in healthReport.noTopicProjects" :key="project.namespace.slug + project.namespace.owner">
|
||||
<Link :to="'/' + project.namespace.owner + '/' + project.namespace.slug">
|
||||
{{ project.namespace.owner + "/" + project.namespace.slug }}
|
||||
<li v-for="project in healthReport.missingFiles" :key="project.namespace.slug + project.namespace.owner">
|
||||
<Link :to="'/' + project.namespace.owner + '/' + project.namespace.slug + '/' + project.versionString">
|
||||
{{ project.namespace.owner + "/" + project.namespace.slug + "/" + project.versionString + " (" + project.platforms + ")" }}
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="!healthReport.noTopicProjects || healthReport.noTopicProjects.length === 0">
|
||||
<li v-if="!healthReport.missingFiles || healthReport.missingFiles.length === 0">
|
||||
{{ i18n.t("health.empty") }}
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card v-if="healthReport">
|
||||
<template #header> {{ i18n.t("health.erroredJobs") }}</template>
|
||||
|
||||
<ul class="max-h-xs overflow-auto">
|
||||
<li v-for="job in healthReport.erroredJobs" :key="job.jobType + new Date(job.lastUpdated).toISOString()">
|
||||
{{ i18n.t("health.jobText", [job.jobType, job.lastErrorDescriptor, i18n.d(job.lastUpdated, "time")]) }}
|
||||
</li>
|
||||
<li v-if="!healthReport.erroredJobs || healthReport.erroredJobs.length === 0">
|
||||
{{ i18n.t("health.empty") }}
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card v-if="healthReport">
|
||||
<Card>
|
||||
<template #header> {{ i18n.t("health.staleProjects") }}</template>
|
||||
|
||||
<ul class="max-h-xs overflow-auto">
|
||||
@ -64,40 +52,14 @@ useHead(useSeo(i18n.t("health.title"), null, route, null));
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card v-if="healthReport">
|
||||
<template #header> {{ i18n.t("health.notPublicProjects") }}</template>
|
||||
|
||||
<ul class="max-h-xs overflow-auto">
|
||||
<li v-for="project in healthReport.nonPublicProjects" :key="project.namespace.slug + project.namespace.owner">
|
||||
<Link :to="'/' + project.namespace.owner + '/' + project.namespace.slug">
|
||||
<strong>{{ project.namespace.owner + "/" + project.namespace.slug }}</strong>
|
||||
<small class="ml-1">{{ i18n.t("visibility.name." + project.visibility) }}</small>
|
||||
</Link>
|
||||
</li>
|
||||
<li v-if="!healthReport.nonPublicProjects || healthReport.nonPublicProjects.length === 0">
|
||||
{{ i18n.t("health.empty") }}
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card>
|
||||
<template #header>{{ i18n.t("health.noPlatform") }}</template>
|
||||
|
||||
<ul>
|
||||
<li>TODO: Implementation</li>
|
||||
<!--TODO idek what this is for?-->
|
||||
<!--<li v-if="!healthReport.noPlatform || healthReport.noPlatform.length === 0">{{ i18n.t('health.empty') }}</li>-->
|
||||
</ul>
|
||||
</Card>
|
||||
<Card v-if="healthReport">
|
||||
<template #header> {{ i18n.t("health.missingFileProjects") }}</template>
|
||||
<template #header> {{ i18n.t("health.erroredJobs") }}</template>
|
||||
|
||||
<ul class="max-h-xs overflow-auto">
|
||||
<li v-for="project in healthReport.missingFiles" :key="project.namespace.slug + project.namespace.owner">
|
||||
<Link :to="'/' + project.namespace.owner + '/' + project.namespace.slug">
|
||||
{{ project.namespace.owner + "/" + project.namespace.slug }}
|
||||
</Link>
|
||||
<li v-for="job in healthReport.erroredJobs" :key="job.jobType + new Date(job.lastUpdated).toISOString()">
|
||||
{{ i18n.t("health.jobText", [job.jobType, job.lastErrorDescriptor, i18n.d(job.lastUpdated, "time")]) }}
|
||||
</li>
|
||||
<li v-if="!healthReport.missingFiles || healthReport.missingFiles.length === 0">
|
||||
<li v-if="!healthReport.erroredJobs || healthReport.erroredJobs.length === 0">
|
||||
{{ i18n.t("health.empty") }}
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -91,18 +91,6 @@ async function saveRoles() {
|
||||
handleRequestError(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateHashes() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const errors = await useInternalApi("admin/updateHashes", "post", undefined, { timeout: 240000 });
|
||||
console.log(errors);
|
||||
notification.success("Updated hashes!");
|
||||
} catch (e: any) {
|
||||
loading.value = false;
|
||||
handleRequestError(e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -171,11 +159,5 @@ async function updateHashes() {
|
||||
</span>
|
||||
</template>
|
||||
</Card>
|
||||
<Card class="mt-5">
|
||||
<PageTitle>Update file hashes</PageTitle>
|
||||
Update file hashes stored in db from md5 to sha256.
|
||||
<br />
|
||||
<Button button-type="red" :disabled="loading" @click="updateHashes">Run</Button>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
6
frontend/src/types/internal/admin.d.ts
vendored
6
frontend/src/types/internal/admin.d.ts
vendored
@ -56,11 +56,9 @@ declare module "hangar-internal" {
|
||||
}
|
||||
|
||||
interface MissingFile {
|
||||
platform: Platform;
|
||||
versionString: string;
|
||||
fileName: string;
|
||||
namespace: ProjectNamespace;
|
||||
name: string;
|
||||
versionString: string;
|
||||
platforms: Platform[];
|
||||
}
|
||||
|
||||
interface UnhealthyProject {
|
||||
|
Loading…
Reference in New Issue
Block a user