mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
feat(web): use Markdown helper class to interact with markdown
This commit is contained in:
@@ -10,18 +10,19 @@ import {
|
||||
import { useMutation } from '@vue/apollo-composable';
|
||||
import type { NotificationFragmentFragment } from '~/composables/gql/graphql';
|
||||
import { NotificationType } from '~/composables/gql/graphql';
|
||||
import { safeParseMarkdown } from '~/helpers/markdown';
|
||||
import {
|
||||
archiveNotification as archiveMutation,
|
||||
deleteNotification as deleteMutation,
|
||||
} from './graphql/notification.query';
|
||||
import { Markdown } from '@/helpers/markdown';
|
||||
|
||||
const props = defineProps<NotificationFragmentFragment>();
|
||||
|
||||
const descriptionMarkup = computedAsync(async () => {
|
||||
try {
|
||||
return await safeParseMarkdown(props.description);
|
||||
return await Markdown.parse(props.description);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return props.description;
|
||||
}
|
||||
}, '');
|
||||
|
||||
@@ -1,13 +1,41 @@
|
||||
import DOMPurify from 'dompurify';
|
||||
import { marked } from 'marked';
|
||||
import { Marked, type MarkedExtension } from 'marked';
|
||||
|
||||
const defaultMarkedExtension: MarkedExtension = {
|
||||
hooks: {
|
||||
// must define as a function (instead of a lambda) to preserve/reflect bindings downstream
|
||||
postprocess(html) {
|
||||
return DOMPurify.sanitize(html);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses arbitrary markdown content as sanitized html. May throw if parsing fails.
|
||||
*
|
||||
* @param markdownContent string of markdown content
|
||||
* @returns safe, sanitized html content
|
||||
* Helper class to build or conveniently use a markdown parser.
|
||||
*/
|
||||
export async function safeParseMarkdown(markdownContent: string) {
|
||||
const parsed = await marked.parse(markdownContent);
|
||||
return DOMPurify.sanitize(parsed);
|
||||
export class Markdown {
|
||||
private static instance = Markdown.create();
|
||||
|
||||
/**
|
||||
* Creates a `Marked` instance with default MarkedExtension's already added.
|
||||
*
|
||||
* Default behaviors:
|
||||
* - Sanitizes html after parsing
|
||||
*
|
||||
* @param args any number of Marked Extensions
|
||||
* @returns Marked parser instance
|
||||
*/
|
||||
static create(...args: Parameters<Marked['use']>) {
|
||||
return new Marked(defaultMarkedExtension, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses arbitrary markdown content as sanitized html. May throw if parsing fails.
|
||||
*
|
||||
* @param markdownContent string of markdown content
|
||||
* @returns safe, sanitized html content
|
||||
*/
|
||||
static async parse(markdownContent: string): Promise<string> {
|
||||
return Markdown.instance.parse(markdownContent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { marked } from 'marked';
|
||||
import { Markdown } from '@/helpers/markdown';
|
||||
import { request } from '~/composables/services/request';
|
||||
import { DOCS_RELEASE_NOTES } from '~/helpers/urls';
|
||||
import { useCallbackStore } from '~/store/callbackActions';
|
||||
// import { useServerStore } from '~/store/server';
|
||||
import type { ServerUpdateOsResponse } from '~/types/server';
|
||||
import { Marked } from 'marked';
|
||||
import { baseUrl } from 'marked-base-url';
|
||||
import { defineStore } from 'pinia';
|
||||
import prerelease from 'semver/functions/prerelease';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { DOCS_RELEASE_NOTES } from '~/helpers/urls';
|
||||
import { request } from '~/composables/services/request';
|
||||
import { useCallbackStore } from '~/store/callbackActions';
|
||||
// import { useServerStore } from '~/store/server';
|
||||
import type { ServerUpdateOsResponse } from '~/types/server';
|
||||
import { safeParseMarkdown } from '~/helpers/markdown';
|
||||
|
||||
export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () => {
|
||||
const callbackStore = useCallbackStore();
|
||||
// const serverStore = useServerStore();
|
||||
@@ -30,10 +29,15 @@ export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () =>
|
||||
if (!releaseForUpdate.value || !releaseForUpdate.value?.changelog) {
|
||||
return '';
|
||||
}
|
||||
return releaseForUpdate.value?.changelog ?? `https://raw.githubusercontent.com/unraid/docs/main/docs/unraid-os/release-notes/${releaseForUpdate.value.version}.md`;
|
||||
return (
|
||||
releaseForUpdate.value?.changelog ??
|
||||
`https://raw.githubusercontent.com/unraid/docs/main/docs/unraid-os/release-notes/${releaseForUpdate.value.version}.md`
|
||||
);
|
||||
});
|
||||
|
||||
const isReleaseForUpdateStable = computed(() => releaseForUpdate.value ? prerelease(releaseForUpdate.value.version) === null : false);
|
||||
const isReleaseForUpdateStable = computed(() =>
|
||||
releaseForUpdate.value ? prerelease(releaseForUpdate.value.version) === null : false
|
||||
);
|
||||
const parsedChangelog = ref<string>('');
|
||||
const parseChangelogFailed = ref<string>('');
|
||||
// used to remove the first <h1></h1> and it's contents from the parsedChangelog
|
||||
@@ -49,7 +53,10 @@ export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () =>
|
||||
return parseChangelogFailed.value;
|
||||
}
|
||||
if (parsedChangelog.value) {
|
||||
return parsedChangelog.value.match(/<h1>(.*?)<\/h1>/)?.[1] ?? `Version ${releaseForUpdate.value?.version} ${releaseForUpdate.value?.date}`;
|
||||
return (
|
||||
parsedChangelog.value.match(/<h1>(.*?)<\/h1>/)?.[1] ??
|
||||
`Version ${releaseForUpdate.value?.version} ${releaseForUpdate.value?.date}`
|
||||
);
|
||||
}
|
||||
return '';
|
||||
});
|
||||
@@ -72,7 +79,7 @@ export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () =>
|
||||
.text();
|
||||
|
||||
// set base url for relative links
|
||||
marked.use(baseUrl(DOCS_RELEASE_NOTES.toString()));
|
||||
const marked = Markdown.create(baseUrl(DOCS_RELEASE_NOTES.toString()));
|
||||
|
||||
// open links in new tab & replace .md from links
|
||||
const renderer = new marked.Renderer();
|
||||
@@ -80,20 +87,20 @@ export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () =>
|
||||
options: {
|
||||
sanitize: true,
|
||||
},
|
||||
render: marked.Renderer.prototype.link
|
||||
render: marked.Renderer.prototype.link,
|
||||
};
|
||||
renderer.link = function (href, title, text) {
|
||||
const anchor = anchorRender.render(href, title, text);
|
||||
return anchor
|
||||
.replace('<a', '<a target=\'_blank\' ') // open links in new tab
|
||||
.replace('<a', "<a target='_blank' ") // open links in new tab
|
||||
.replace('.md', ''); // remove .md from links
|
||||
};
|
||||
|
||||
marked.setOptions({
|
||||
renderer
|
||||
renderer,
|
||||
});
|
||||
|
||||
parsedChangelog.value = await safeParseMarkdown(changelogMarkdownRaw);
|
||||
parsedChangelog.value = await marked.parse(changelogMarkdownRaw);
|
||||
} catch (error: unknown) {
|
||||
const caughtError = error as Error;
|
||||
parseChangelogFailed.value =
|
||||
@@ -106,12 +113,14 @@ export const useUpdateOsChangelogStore = defineStore('updateOsChangelog', () =>
|
||||
const fetchAndConfirmInstall = (sha256: string) => {
|
||||
callbackStore.send(
|
||||
window.location.href,
|
||||
[{
|
||||
sha256,
|
||||
type: 'updateOs',
|
||||
}],
|
||||
[
|
||||
{
|
||||
sha256,
|
||||
type: 'updateOs',
|
||||
},
|
||||
],
|
||||
undefined,
|
||||
'forUpc',
|
||||
'forUpc'
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user