Modrinth Servers February Release: Bug Fix Round 1 (#3267)

* chore(pyroservers): attempt better error propogation

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore(pyroservers): introduce deferred modules

* fix(pyroservers): synchronize server icon processing

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor: server action buttons

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: bring back skeleton

* fix(startup): populate values on refresh

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: properly refresh network

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: do not open backup settings modal if fetch failed

* fix(platform): only clear selected loader version if selecting a different loader

Signed-off-by: Evan Song <theevansong@gmail.com>

* feat: parse links in console log

* fix: attempt to mitigate power button state flash

Signed-off-by: Evan Song <theevansong@gmail.com>

* Revert "fix: attempt to mitigate power button state flash"

This reverts commit 3ba5c0b4f7f5bacf1576aba5efe42785696a5aed.

* refactor: error accumulation builder in PyroServersFetch

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: sentence case

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix(files): await deferred fs

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: startup border

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: prevent suspended server errors from being overwritten

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: add server id copy button to suspended server listing

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: refresh behavior

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: behavior of server icon in options

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: prevent error inspector failures from destroying the page

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: clean

Signed-off-by: Evan Song <theevansong@gmail.com>

* chore: remove nexttick wrapper

Signed-off-by: Evan Song <theevansong@gmail.com>

* fix: ensure file edit gets initted due to deferred module

Signed-off-by: Evan Song <theevansong@gmail.com>

* refactor: prevent module errors from breaking the layout

* chore: clean

Signed-off-by: Evan Song <theevansong@gmail.com>

---------

Signed-off-by: Evan Song <theevansong@gmail.com>
This commit is contained in:
Evan Song
2025-02-18 15:17:50 -07:00
committed by GitHub
parent 6c4548a303
commit a88593fec5
19 changed files with 1170 additions and 612 deletions

View File

@@ -260,7 +260,25 @@
</div>
<NewModal ref="viewLogModal" class="z-[9999]" header="Viewing selected logs">
<div class="text-contrast">
<pre class="select-text overflow-x-auto whitespace-pre font-mono">{{ selectedLog }}</pre>
<pre
class="select-text overflow-x-auto whitespace-pre rounded-lg bg-bg font-mono"
v-html="processedLogWithLinks"
></pre>
<div v-if="detectedLinks.length" class="border-contrast/20 mt-4 border-t pt-4">
<h2>Detected Links</h2>
<ul class="flex flex-col gap-2">
<li v-for="(link, index) in detectedLinks" :key="index">
<a
:href="link"
target="_blank"
rel="noopener noreferrer"
class="text-sm text-blue hover:underline"
>
{{ link }}
</a>
</li>
</ul>
</div>
</div>
</NewModal>
</div>
@@ -272,6 +290,7 @@ import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue";
import { useDebounceFn } from "@vueuse/core";
import { NewModal } from "@modrinth/ui";
import ButtonStyled from "@modrinth/ui/src/components/base/ButtonStyled.vue";
import DOMPurify from "dompurify";
import { usePyroConsole } from "~/store/console.ts";
const { $cosmetics } = useNuxtApp();
@@ -984,6 +1003,38 @@ const jumpToLine = (line: string, event?: MouseEvent) => {
});
};
const sanitizeUrl = (url: string): string => {
try {
const parsed = new URL(url);
if (!["http:", "https:"].includes(parsed.protocol)) {
return "#";
}
return parsed.toString();
} catch {
return "#";
}
};
const detectedLinks = computed(() => {
const urlRegex = /(https?:\/\/[^\s,<]+(?=[,\s<]|$))/g;
const matches = [...selectedLog.value.matchAll(urlRegex)].map((match) => match[0]);
return matches.filter((url) => sanitizeUrl(url) !== "#");
});
const processedLogWithLinks = computed(() => {
const urlRegex = /(https?:\/\/[^\s,<]+(?=[,\s<]|$))/g;
const sanitizedLog = DOMPurify.sanitize(selectedLog.value, {
ALLOWED_TAGS: [],
ALLOWED_ATTR: [],
});
return sanitizedLog.replace(urlRegex, (url) => {
const safeUrl = sanitizeUrl(url);
if (safeUrl === "#") return url;
return `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer nofollow" class="text-blue hover:underline">${url}</a>`;
});
});
watch(
() => pyroConsole.filteredOutput.value,
() => {