mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 21:22:43 +00:00
build(docs): add Astro and content collection configuration
This commit is contained in:
1 parent
fadfa7ff9f
commit
7f8461e692
2 files changed
+164
No files matched your search
@@ -0,0 +1,137 @@
|
|||||||
|
// Written by `blume eject`. This is your Astro config now: edit it freely.
|
||||||
|
import { availableParallelism } from "node:os";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { defineConfig, fontProviders } from "astro/config";
|
||||||
|
import mdx from "@astrojs/mdx";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import { blumeMarkdownProcessor, blumeMdxProcessor, blumeShikiTransformers, blumeTwoslashTransformer } from "blume/markdown";
|
||||||
|
import react from "@astrojs/react";
|
||||||
|
import { blumeIntegration, includeHmrPlugin, mdxSourceErrorsPlugin, prerenderDepsPlugin } from "blume/astro";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
root: ".",
|
||||||
|
srcDir: "./src",
|
||||||
|
outDir: "./dist",
|
||||||
|
publicDir: "./public",
|
||||||
|
output: "static",
|
||||||
|
site: "https://lc.prd.lk",
|
||||||
|
redirects: {"/1":{"destination":"/hash-table/two-sum","status":301},"/1.md":{"destination":"/hash-table/two-sum.md","status":301},"/1.mdx":{"destination":"/hash-table/two-sum.mdx","status":301},"/2":{"destination":"/hybrid-structures/add-two-numbers","status":301},"/2.md":{"destination":"/hybrid-structures/add-two-numbers.md","status":301},"/2.mdx":{"destination":"/hybrid-structures/add-two-numbers.mdx","status":301},"/3":{"destination":"/sliding-window/longest-substring-without-repeating-characters","status":301},"/3.md":{"destination":"/sliding-window/longest-substring-without-repeating-characters.md","status":301},"/3.mdx":{"destination":"/sliding-window/longest-substring-without-repeating-characters.mdx","status":301},"/4":{"destination":"/binary-search/median-of-two-sorted-arrays","status":301},"/4.md":{"destination":"/binary-search/median-of-two-sorted-arrays.md","status":301},"/4.mdx":{"destination":"/binary-search/median-of-two-sorted-arrays.mdx","status":301},"/11":{"destination":"/two-pointers/container-with-most-water","status":301},"/11.md":{"destination":"/two-pointers/container-with-most-water.md","status":301},"/11.mdx":{"destination":"/two-pointers/container-with-most-water.mdx","status":301},"/15":{"destination":"/two-pointers/3sum","status":301},"/15.md":{"destination":"/two-pointers/3sum.md","status":301},"/15.mdx":{"destination":"/two-pointers/3sum.mdx","status":301},"/19":{"destination":"/linked-list/remove-nth-node-from-end-of-list","status":301},"/19.md":{"destination":"/linked-list/remove-nth-node-from-end-of-list.md","status":301},"/19.mdx":{"destination":"/linked-list/remove-nth-node-from-end-of-list.mdx","status":301},"/20":{"destination":"/stack/valid-parentheses","status":301},"/20.md":{"destination":"/stack/valid-parentheses.md","status":301},"/20.mdx":{"destination":"/stack/valid-parentheses.mdx","status":301},"/21":{"destination":"/linked-list/merge-two-sorted-lists","status":301},"/21.md":{"destination":"/linked-list/merge-two-sorted-lists.md","status":301},"/21.mdx":{"destination":"/linked-list/merge-two-sorted-lists.mdx","status":301},"/23":{"destination":"/heap-priority-queue/merge-k-sorted-lists","status":301},"/23.md":{"destination":"/heap-priority-queue/merge-k-sorted-lists.md","status":301},"/23.mdx":{"destination":"/heap-priority-queue/merge-k-sorted-lists.mdx","status":301},"/33":{"destination":"/binary-search/search-in-rotated-sorted-array","status":301},"/33.md":{"destination":"/binary-search/search-in-rotated-sorted-array.md","status":301},"/33.mdx":{"destination":"/binary-search/search-in-rotated-sorted-array.mdx","status":301},"/34":{"destination":"/binary-search/find-first-and-last-position-of-element-in-sorted-array","status":301},"/34.md":{"destination":"/binary-search/find-first-and-last-position-of-element-in-sorted-array.md","status":301},"/34.mdx":{"destination":"/binary-search/find-first-and-last-position-of-element-in-sorted-array.mdx","status":301},"/35":{"destination":"/binary-search/search-insert-position","status":301},"/35.md":{"destination":"/binary-search/search-insert-position.md","status":301},"/35.mdx":{"destination":"/binary-search/search-insert-position.mdx","status":301},"/36":{"destination":"/matrix-index-math/valid-sudoku","status":301},"/36.md":{"destination":"/matrix-index-math/valid-sudoku.md","status":301},"/36.mdx":{"destination":"/matrix-index-math/valid-sudoku.mdx","status":301},"/39":{"destination":"/backtracking/combination-sum","status":301},"/39.md":{"destination":"/backtracking/combination-sum.md","status":301},"/39.mdx":{"destination":"/backtracking/combination-sum.mdx","status":301},"/42":{"destination":"/two-pointers/trapping-rain-water","status":301},"/42.md":{"destination":"/two-pointers/trapping-rain-water.md","status":301},"/42.mdx":{"destination":"/two-pointers/trapping-rain-water.mdx","status":301},"/46":{"destination":"/backtracking/permutations","status":301},"/46.md":{"destination":"/backtracking/permutations.md","status":301},"/46.mdx":{"destination":"/backtracking/permutations.mdx","status":301},"/48":{"destination":"/matrix-index-math/rotate-image","status":301},"/48.md":{"destination":"/matrix-index-math/rotate-image.md","status":301},"/48.mdx":{"destination":"/matrix-index-math/rotate-image.mdx","status":301},"/49":{"destination":"/hash-table/group-anagrams","status":301},"/49.md":{"destination":"/hash-table/group-anagrams.md","status":301},"/49.mdx":{"destination":"/hash-table/group-anagrams.mdx","status":301},"/53":{"destination":"/greedy/maximum-subarray","status":301},"/53.md":{"destination":"/greedy/maximum-subarray.md","status":301},"/53.mdx":{"destination":"/greedy/maximum-subarray.mdx","status":301},"/54":{"destination":"/matrix-index-math/spiral-matrix","status":301},"/54.md":{"destination":"/matrix-index-math/spiral-matrix.md","status":301},"/54.mdx":{"destination":"/matrix-index-math/spiral-matrix.mdx","status":301},"/55":{"destination":"/greedy/jump-game","status":301},"/55.md":{"destination":"Line truncated
|
||||||
|
fonts: [{ provider: fontProviders.local(), name: "Inter", cssVariable: "--blume-ff-inter", fallbacks: ["ui-sans-serif","system-ui","sans-serif"], options: { variants: [{"weight":"100..900","src":["node_modules/@fontsource-variable/inter/files/inter-latin-wght-normal.woff2"]}] } }, { provider: fontProviders.local(), name: "IBM Plex Mono", cssVariable: "--blume-ff-ibm-plex-mono", fallbacks: ["ui-monospace","SF Mono","Menlo","monospace"], options: { variants: [{"weight":400,"src":["node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-400-normal.woff2"]},{"weight":500,"src":["node_modules/@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2"]}] } }],
|
||||||
|
integrations: [mdx({ processor: blumeMdxProcessor({ ...{"basePath":"","codeThemes":{"dark":"github-dark","light":"github-light"},"contentRoot":"content","deployBase":"","externalLinks":false,"headingAnchors":true,"variables":{}}, dataFile: fileURLToPath(new URL("./src/generated/data.json", import.meta.url)) }) }), react({ exclude: [/\/\.cache\/vite\//] }), blumeIntegration({"pages":[{"entrypoint":"pages/appendix.astro","pattern":"/appendix"},{"entrypoint":"pages/changelog.astro","pattern":"/changelog"},{"entrypoint":"pages/progress.astro","pattern":"/progress"}],"poweredBy":true,"redirects":[["^/1/?$","/hash-table/two-sum",301],["^/1\\.md/?$","/hash-table/two-sum.md",301],["^/1\\.mdx/?$","/hash-table/two-sum.mdx",301],["^/2/?$","/hybrid-structures/add-two-numbers",301],["^/2\\.md/?$","/hybrid-structures/add-two-numbers.md",301],["^/2\\.mdx/?$","/hybrid-structures/add-two-numbers.mdx",301],["^/3/?$","/sliding-window/longest-substring-without-repeating-characters",301],["^/3\\.md/?$","/sliding-window/longest-substring-without-repeating-characters.md",301],["^/3\\.mdx/?$","/sliding-window/longest-substring-without-repeating-characters.mdx",301],["^/4/?$","/binary-search/median-of-two-sorted-arrays",301],["^/4\\.md/?$","/binary-search/median-of-two-sorted-arrays.md",301],["^/4\\.mdx/?$","/binary-search/median-of-two-sorted-arrays.mdx",301],["^/11/?$","/two-pointers/container-with-most-water",301],["^/11\\.md/?$","/two-pointers/container-with-most-water.md",301],["^/11\\.mdx/?$","/two-pointers/container-with-most-water.mdx",301],["^/15/?$","/two-pointers/3sum",301],["^/15\\.md/?$","/two-pointers/3sum.md",301],["^/15\\.mdx/?$","/two-pointers/3sum.mdx",301],["^/19/?$","/linked-list/remove-nth-node-from-end-of-list",301],["^/19\\.md/?$","/linked-list/remove-nth-node-from-end-of-list.md",301],["^/19\\.mdx/?$","/linked-list/remove-nth-node-from-end-of-list.mdx",301],["^/20/?$","/stack/valid-parentheses",301],["^/20\\.md/?$","/stack/valid-parentheses.md",301],["^/20\\.mdx/?$","/stack/valid-parentheses.mdx",301],["^/21/?$","/linked-list/merge-two-sorted-lists",301],["^/21\\.md/?$","/linked-list/merge-two-sorted-lists.md",301],["^/21\\.mdx/?$","/linked-list/merge-two-sorted-lists.mdx",301],["^/23/?$","/heap-priority-queue/merge-k-sorted-lists",301],["^/23\\.md/?$","/heap-priority-queue/merge-k-sorted-lists.md",301],["^/23\\.mdx/?$","/heap-priority-queue/merge-k-sorted-lists.mdx",301],["^/33/?$","/binary-search/search-in-rotated-sorted-array",301],["^/33\\.md/?$","/binary-search/search-in-rotated-sorted-array.md",301],["^/33\\.mdx/?$","/binary-search/search-in-rotated-sorted-array.mdx",301],["^/34/?$","/binary-search/find-first-and-last-position-of-element-in-sorted-array",301],["^/34\\.md/?$","/binary-search/find-first-and-last-position-of-element-in-sorted-array.md",301],["^/34\\.mdx/?$","/binary-search/find-first-and-last-position-of-element-in-sorted-array.mdx",301],["^/35/?$","/binary-search/search-insert-position",301],["^/35\\.md/?$","/binary-search/search-insert-position.md",301],["^/35\\.mdx/?$","/binary-search/search-insert-position.mdx",301],["^/36/?$","/matrix-index-math/valid-sudoku",301],["^/36\\.md/?$","/matrix-index-math/valid-sudoku.md",301],["^/36\\.mdx/?$","/matrix-index-math/valid-sudoku.mdx",301],["^/39/?$","/backtracking/combination-sum",301],["^/39\\.md/?$","/backtracking/combination-sum.md",301],["^/39\\.mdx/?$","/backtracking/combination-sum.mdx",301],["^/42/?$","/two-pointers/trapping-rain-water",301],["^/42\\.md/?$","/two-pointers/trapping-rain-water.md",301],["^/42\\.mdx/?$","/two-pointers/trapping-rain-water.mdx",301],["^/46/?$","/backtracking/permutations",301],["^/46\\.md/?$","/backtracking/permutations.md",301],["^/46\\.mdx/?$","/backtracking/permutations.mdx",301],["^/48/?$","/matrix-index-math/rotate-image",301],["^/48\\.md/?$","/matrix-index-math/rotate-image.md",301],["^/48\\.mdx/?$","/matrix-index-math/rotate-image.mdx",301],["^/49/?$","/hash-table/group-anagrams",301],["^/49\\.md/?$","/hash-table/group-anagrams.md",301],["^/49\\.mdx/?$","/hash-table/group-anagrams.mdx",301],["^/53/?$","/greedy/maximum-subarray",301],["^/53\\.md/?$","/greedy/maximum-subarray.md",301],["^/53\\.mdx/?$","/greedy/maximum-subarray.mdx",301],["^/54/?$","/matrix-index-math/spiral-matrix",301],["^/54\\.md/?$","/matrix-index-math/spiral-matrix.md",301],["^/54\\.mdx/?$","/matrix-index-math/spiral-matrix.mdx",301],["^/55/?$","/greedy/jump-game",301],["^/55\\.md/?$","/greedy/jump-game.md",301],["^/55\\.mdx/?$","/greedy/jump-game.mdx",301],["^/56/?$","/intervals/merge-intervals",301],["^/56\\.md/?$","/intervals/merge-intervals.md",301],["^/56\\.mdx/?$","/intervals/merge-intervals.mdx",301],["^/57/?$","/intervals/insert-interval",301],["^/57\\.md/?$","/intervals/insert-interval.md",301],["^/57\\.mdx/?$","/intervals/insert-interval.mdx",301],["^/62/?$","/multi-d-grid-dp/unique-paths",301],["^/62\\.md/?$","/multi-d-grid-dp/unique-paths.md",301],["^/62\\.mdx/?$","/multi-d-grid-dp/unique-paths.mdx",301],["^/64/?$","Line truncated
|
||||||
|
markdown: {
|
||||||
|
processor: blumeMarkdownProcessor({ ...{"basePath":"","codeThemes":{"dark":"github-dark","light":"github-light"},"contentRoot":"content","deployBase":"","externalLinks":false,"headingAnchors":true,"variables":{}}, dataFile: fileURLToPath(new URL("./src/generated/data.json", import.meta.url)) }),
|
||||||
|
shikiConfig: {
|
||||||
|
themes: {
|
||||||
|
light: "github-light",
|
||||||
|
dark: "github-dark",
|
||||||
|
},
|
||||||
|
defaultColor: false,
|
||||||
|
transformers: [blumeTwoslashTransformer(), ...blumeShikiTransformers({"icons":true})],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
devToolbar: { enabled: false },
|
||||||
|
// One canonical URL per page: canonicals, the sitemap, and hreflang all use
|
||||||
|
// the slashless form, so the slashed spelling is not a second address. Astro
|
||||||
|
// applies this itself — an on-demand route redirects, and the integration
|
||||||
|
// has the dev server redirect too, ahead of Astro's own 404 — and the Vercel
|
||||||
|
// adapter turns it into the platform's 308 route, so the Build Output config
|
||||||
|
// needs no hand-spliced redirect (see deploy/vercel-negotiation.ts). Static hosts
|
||||||
|
// serve the `index.html` directory layout as they always did.
|
||||||
|
trailingSlash: "never",
|
||||||
|
// The layouts render Astro's <ClientRouter />, and its in-place swaps read
|
||||||
|
// from the prefetch cache — fetching every link on hover/viewport hides the
|
||||||
|
// request latency behind the user's intent, so most navigations swap
|
||||||
|
// instantly.
|
||||||
|
prefetch: { prefetchAll: true },
|
||||||
|
// Prerender several pages at once. Rendering is single-threaded, but a
|
||||||
|
// page's OG card renders on a native thread and its HTML is written
|
||||||
|
// asynchronously, so the main thread would otherwise idle behind each
|
||||||
|
// page's off-thread work. Capped at 8: the gain flattens there and beyond it
|
||||||
|
// the overlap only adds memory.
|
||||||
|
build: { concurrency: Math.min(8, availableParallelism()) },
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss(), includeHmrPlugin(fileURLToPath(new URL("./src/generated/includes.json", import.meta.url))), mdxSourceErrorsPlugin(), prerenderDepsPlugin()],
|
||||||
|
build: {
|
||||||
|
chunkSizeWarningLimit: 2048,
|
||||||
|
rolldownOptions: {
|
||||||
|
onLog(level, log, handler) {
|
||||||
|
if (log.code === "MODULE_LEVEL_DIRECTIVE" && String(log.message).includes("use astro:head-inject")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handler(level, log);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Everything hydration can reach must be part of the dev dep optimizer's
|
||||||
|
// FIRST run. The Vite root is the generated runtime, so user pages,
|
||||||
|
// islands, and aliased components live outside it and are only crawled
|
||||||
|
// when first requested — and `react/compiler-runtime` is injected by the
|
||||||
|
// React Compiler, so no source scan can ever see it. A dependency discovered after
|
||||||
|
// hydration begins triggers a mid-session re-optimization whose new
|
||||||
|
// generation imports React through new `?v=` URLs; the browser then
|
||||||
|
// evaluates a second React copy and every island tears down with
|
||||||
|
// "Invalid hook call" (#157). `entries` points the startup scanner at
|
||||||
|
// the user's files (the scanner follows their imports, so their deps land
|
||||||
|
// in the initial optimization); the compiler runtime rides the include
|
||||||
|
// list because only the transform pipeline knows it exists.
|
||||||
|
//
|
||||||
|
// The mermaid/epub includes fix CJS interop instead: both lazy client-side
|
||||||
|
// imports land on CJS/UMD files (mermaid statically imports dayjs as CJS,
|
||||||
|
// epub-gen-memory's browser bundle is a browserified UMD) that break when
|
||||||
|
// served as raw ESM — mermaid throws on load and the EPUB export throws
|
||||||
|
// `epub is not a function`. They resolve through the `blume` package
|
||||||
|
// (they aren't direct deps of the generated project), so the nested
|
||||||
|
// `blume > x` form is required, and epub-gen-memory must name the
|
||||||
|
// `/bundle` subpath that is actually imported: optimizing the package
|
||||||
|
// root leaves that entry out. Production (Rollup) already handles the
|
||||||
|
// interop, so all of this only affects dev.
|
||||||
|
//
|
||||||
|
// The search adapter's client library rides the list for the same reason
|
||||||
|
// as the compiler runtime: the search dialog lazy-imports it on first
|
||||||
|
// open, and discovering it then re-optimizes and reloads the page.
|
||||||
|
optimizeDeps: {
|
||||||
|
entries: ["./pages/**/*.astro","./islands/**/*.{jsx,svelte,tsx,vue}"],
|
||||||
|
include: ["blume > mermaid","blume > @orama/orama"],
|
||||||
|
},
|
||||||
|
// Blume's render-time deps are forced external on both build environments so
|
||||||
|
// native bindings resolve at runtime and isolated linkers don't bundle
|
||||||
|
// symlinked store copies (which would surface their children as unresolvable
|
||||||
|
// imports). See RENDER_EXTERNAL_DEPS / prerenderDepsPlugin.
|
||||||
|
//
|
||||||
|
// The SSR externals go through the legacy `ssr.external` key rather than
|
||||||
|
// `environments.ssr`: defining a user-owned `environments.ssr` block
|
||||||
|
// collides with the internal environment Astro 7 builds the server under and
|
||||||
|
// detaches the adapter's server entrypoint from the rolldown input, so the
|
||||||
|
// SSR entry is emitted as `index.mjs` instead of the `entry.mjs` the
|
||||||
|
// Vercel adapter's `astro:build:done` hook then fails to find. `prerender`
|
||||||
|
// is Astro-only and has no legacy equivalent, so it stays under `environments`.
|
||||||
|
ssr: { external: ["@astrojs/markdown-satteri","@pierre/diffs","@shikijs/transformers","@takumi-rs/core","@takumi-rs/helpers","@takumi-rs/wasm","github-slugger","katex","shiki","simple-icons","takumi-js","takumi-js/helpers","zod"] },
|
||||||
|
environments: {
|
||||||
|
prerender: { resolve: { external: ["@astrojs/markdown-satteri","@pierre/diffs","@shikijs/transformers","@takumi-rs/core","@takumi-rs/helpers","@takumi-rs/wasm","github-slugger","katex","shiki","simple-icons","takumi-js","takumi-js/helpers","zod"] } },
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"blume:ask": fileURLToPath(new URL("./src/generated/Ask.astro", import.meta.url)),
|
||||||
|
"blume:consent-client": fileURLToPath(new URL("./src/generated/consent-client.ts", import.meta.url)),
|
||||||
|
"blume:examples": fileURLToPath(new URL("./src/generated/examples.ts", import.meta.url)),
|
||||||
|
"blume:examples-theme": fileURLToPath(new URL("./src/generated/examples.css", import.meta.url)),
|
||||||
|
"blume:features": fileURLToPath(new URL("./src/generated/features.ts", import.meta.url)),
|
||||||
|
"blume:search-client": fileURLToPath(new URL("./src/generated/search-client.ts", import.meta.url)),
|
||||||
|
"blume:theme": fileURLToPath(new URL("./src/generated/app.css", import.meta.url)),
|
||||||
|
"blume:ask-data": fileURLToPath(new URL("./src/generated/ask-data.json", import.meta.url)),
|
||||||
|
"blume:content-assets": fileURLToPath(new URL("./src/generated/content-assets.json", import.meta.url)),
|
||||||
|
"blume:data": fileURLToPath(new URL("./src/generated/data.json", import.meta.url)),
|
||||||
|
"blume:mcp-data": fileURLToPath(new URL("./src/generated/mcp-data.json", import.meta.url)),
|
||||||
|
"blume:openapi": fileURLToPath(new URL("./src/generated/openapi.json", import.meta.url)),
|
||||||
|
"blume:raw-markdown": fileURLToPath(new URL("./src/generated/raw-markdown.json", import.meta.url)),
|
||||||
|
"blume:rss": fileURLToPath(new URL("./src/generated/rss.json", import.meta.url)),
|
||||||
|
"blume:search-index": fileURLToPath(new URL("./src/generated/search.json", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
fs: {
|
||||||
|
allow: ["."],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Generated by Blume. Do not edit.
|
||||||
|
import { defineCollection } from "astro:content";
|
||||||
|
import { glob } from "astro/loaders";
|
||||||
|
import { withIncludeRefresh } from "blume/astro";
|
||||||
|
import { pageCollectionSchema } from "blume/core/schema.ts";
|
||||||
|
|
||||||
|
// withIncludeRefresh keeps <include>-bearing pages fresh: plain .md entries
|
||||||
|
// are rendered at sync time and digest-cached on the page file alone, so a
|
||||||
|
// partial edit (or a warm-cache rebuild after one) would serve stale HTML.
|
||||||
|
//
|
||||||
|
// pageCollectionSchema is the scan's page front-matter schema, so entry.data
|
||||||
|
// is typed and normalized the same way; it passes custom keys through and
|
||||||
|
// falls back to defaults for a page the scan already dropped as invalid.
|
||||||
|
//
|
||||||
|
// Astro's content store re-parses an entry only when its file or this config
|
||||||
|
// changes, so naming the Blume version (2.2.4) here re-parses
|
||||||
|
// every entry after an upgrade: none keeps what an older schema produced.
|
||||||
|
const docs = defineCollection({
|
||||||
|
loader: withIncludeRefresh(glob({
|
||||||
|
pattern: ["**/*.{md,mdx}","!**/_*","!**/.*","!**/.blume-verify/**","!**/.cache/**","!**/.git/**","!**/.next/**","!**/.turbo/**","!**/.vercel/**","!**/dist/**","!**/node_modules/**"],
|
||||||
|
base: "content",
|
||||||
|
generateId: ({ entry }) => entry,
|
||||||
|
}), "./src/generated/includes.json"),
|
||||||
|
schema: pageCollectionSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const collections = { docs };
|
||||||
Reference in new issue
Block a user