[FA-misc] Should be good
Some checks failed
CI / build-backend (pull_request) Successful in 1m49s
CI / build-frontend (pull_request) Failing after 16s

This commit is contained in:
gamer147
2025-12-01 07:26:38 -05:00
parent b2f4548807
commit c60aaf2bdb
5 changed files with 1598 additions and 8 deletions

View File

@@ -0,0 +1,35 @@
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import svelte from 'eslint-plugin-svelte';
import astro from 'eslint-plugin-astro';
import globals from 'globals';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
...svelte.configs['flat/recommended'],
...astro.configs.recommended,
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ['**/*.svelte'],
languageOptions: {
parserOptions: {
parser: tseslint.parser
}
},
rules: {
// Disabled because we sanitize HTML with DOMPurify before rendering
'svelte/no-at-html-tags': 'off'
}
},
{
ignores: ['node_modules/', 'dist/', '.astro/', 'src/lib/graphql/__generated__/']
}
);

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,9 @@
"build": "astro build", "build": "astro build",
"preview": "astro preview", "preview": "astro preview",
"astro": "astro", "astro": "astro",
"codegen": "graphql-codegen --config codegen.ts -r dotenv/config --use-system-ca" "codegen": "graphql-codegen --config codegen.ts -r dotenv/config --use-system-ca",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}, },
"dependencies": { "dependencies": {
"@astrojs/node": "^9.5.1", "@astrojs/node": "^9.5.1",
@@ -19,6 +21,7 @@
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"dompurify": "^3.3.0",
"graphql": "^16.12.0", "graphql": "^16.12.0",
"oidc-client-ts": "^3.4.1", "oidc-client-ts": "^3.4.1",
"svelte": "^5.45.2", "svelte": "^5.45.2",
@@ -27,15 +30,22 @@
"typescript": "^5.9.3" "typescript": "^5.9.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.1",
"@graphql-codegen/cli": "^6.1.0", "@graphql-codegen/cli": "^6.1.0",
"@graphql-codegen/typed-document-node": "^6.1.3", "@graphql-codegen/typed-document-node": "^6.1.3",
"@graphql-codegen/typescript": "^5.0.5", "@graphql-codegen/typescript": "^5.0.5",
"@graphql-codegen/typescript-operations": "^5.0.5", "@graphql-codegen/typescript-operations": "^5.0.5",
"@internationalized/date": "^3.10.0", "@internationalized/date": "^3.10.0",
"@lucide/svelte": "^0.544.0", "@lucide/svelte": "^0.544.0",
"@types/dompurify": "^3.0.5",
"bits-ui": "^2.14.4", "bits-ui": "^2.14.4",
"dotenv": "^16.6.1", "dotenv": "^16.6.1",
"eslint": "^9.39.1",
"eslint-plugin-astro": "^1.5.0",
"eslint-plugin-svelte": "^3.13.0",
"globals": "^16.5.0",
"tailwind-variants": "^3.2.2", "tailwind-variants": "^3.2.2",
"tw-animate-css": "^1.4.0" "tw-animate-css": "^1.4.0",
"typescript-eslint": "^8.48.0"
} }
} }

View File

@@ -34,6 +34,7 @@
TooltipProvider TooltipProvider
} from '$lib/components/ui/tooltip'; } from '$lib/components/ui/tooltip';
import { formatRelativeTime, formatAbsoluteTime } from '$lib/utils/time'; import { formatRelativeTime, formatAbsoluteTime } from '$lib/utils/time';
import { sanitizeHtml } from '$lib/utils/sanitize';
let { novel }: NovelCardProps = $props(); let { novel }: NovelCardProps = $props();
@@ -44,7 +45,8 @@
} }
const title = $derived(pickText(novel.name)); const title = $derived(pickText(novel.name));
const description = $derived(pickText(novel.description)); const descriptionRaw = $derived(pickText(novel.description));
const descriptionHtml = $derived(sanitizeHtml(descriptionRaw));
const coverSrc = $derived(novel.coverImage?.newPath ?? novel.coverImage?.originalPath); const coverSrc = $derived(novel.coverImage?.newPath ?? novel.coverImage?.originalPath);
const latestChapter = $derived( const latestChapter = $derived(
@@ -87,9 +89,9 @@
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent class="pt-0 pb-4 space-y-3"> <CardContent class="pt-0 pb-4 space-y-3">
<p class="line-clamp-3 text-sm text-muted-foreground" title={description}> <div class="line-clamp-3 text-sm text-muted-foreground" title={descriptionRaw}>
{description} {@html descriptionHtml}
</p> </div>
{#if chapterDisplay || relativeTime} {#if chapterDisplay || relativeTime}
<div class="flex items-center gap-1 text-xs text-muted-foreground/80"> <div class="flex items-center gap-1 text-xs text-muted-foreground/80">
{#if chapterDisplay} {#if chapterDisplay}

View File

@@ -0,0 +1,12 @@
import DOMPurify from 'dompurify';
/**
* Sanitizes HTML content, allowing only safe inline formatting elements.
* Removes scripts, event handlers, iframes, and other risky elements.
*/
export function sanitizeHtml(html: string): string {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'br', 'p', 'span'],
ALLOWED_ATTR: []
});
}