You've already forked AstralRinth
forked from didirus/AstralRinth
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:
@@ -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,
|
||||
() => {
|
||||
|
||||
Reference in New Issue
Block a user