mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
fixes
This commit is contained in:
parent
d40d8bb730
commit
35806aa60d
43
directus.ts
43
directus.ts
@ -1,9 +1,50 @@
|
|||||||
import { createDirectus, graphql, staticToken } from "@directus/sdk";
|
import { createDirectus, graphql, staticToken } from "@directus/sdk";
|
||||||
import { DIRECTUS_TOKEN, DIRECTUS_URL } from "astro:env/server";
|
import { DIRECTUS_TOKEN, DIRECTUS_URL } from "astro:env/server";
|
||||||
import type { Schema } from "./directus-schema";
|
import type { ArticleTranslation, Schema } from "./directus-schema";
|
||||||
|
|
||||||
const directus = createDirectus<Schema>(DIRECTUS_URL)
|
const directus = createDirectus<Schema>(DIRECTUS_URL)
|
||||||
.with(staticToken(DIRECTUS_TOKEN))
|
.with(staticToken(DIRECTUS_TOKEN))
|
||||||
.with(graphql());
|
.with(graphql());
|
||||||
|
|
||||||
|
export async function fetchArticleBySlug(slug: string): Promise<ArticleTranslation | null> {
|
||||||
|
const data = await directus.query<{ article_translations: ArticleTranslation[] }>(`
|
||||||
|
query Article_translations {
|
||||||
|
article_translations(
|
||||||
|
filter: {
|
||||||
|
slug: {
|
||||||
|
_eq: "${slug}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
title
|
||||||
|
tags {
|
||||||
|
tag_id {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
description
|
||||||
|
slug
|
||||||
|
content
|
||||||
|
author {
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
pub_date
|
||||||
|
views
|
||||||
|
languages_code {
|
||||||
|
code
|
||||||
|
}
|
||||||
|
cover_image {
|
||||||
|
title
|
||||||
|
filename_disk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (data.article_translations.length === 0) return null;
|
||||||
|
|
||||||
|
return data.article_translations[0];
|
||||||
|
}
|
||||||
|
|
||||||
export default directus;
|
export default directus;
|
||||||
|
|||||||
2226
package-lock.json
generated
2226
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,49 +19,51 @@ export type Props = {
|
|||||||
const { article, availableOnLangs } = Astro.props as Props;
|
const { article, availableOnLangs } = Astro.props as Props;
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||||
const { id, cover_image, title, author, description, tags, pub_date, slug } = article;
|
const { cover_image, title, author, description, tags, pub_date, slug, views } = article;
|
||||||
|
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
---
|
---
|
||||||
|
|
||||||
<article itemscope itemtype="http://schema.org/BlogPosting" class="article">
|
<article itemscope itemtype='http://schema.org/BlogPosting' class='article'>
|
||||||
<header>
|
<header>
|
||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
tags && (
|
tags && (
|
||||||
<div class="tags">
|
<div class='tags'>
|
||||||
<ul itemprop="keywords">
|
<ul itemprop='keywords'>
|
||||||
{tags.map((tag) => (
|
{tags.map(
|
||||||
<li>
|
(tag) =>
|
||||||
<TranslatedLink rel="tag" to={`/articles/?tags=${tag}`}>
|
typeof tag === "object" && (
|
||||||
{tag}
|
<li>
|
||||||
</TranslatedLink>
|
<TranslatedLink rel='tag' to={`/articles/?tags=${tag.tag_id.title}`}>
|
||||||
</li>
|
{tag.tag_id.title}
|
||||||
))}
|
</TranslatedLink>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div class="views">
|
<div class='views'>
|
||||||
<Icon name="eye" width="21" height="21" />
|
<Icon name='eye' width='21' height='21' />
|
||||||
<span><span id="views">---</span> {t("article.views")}</span>
|
<span><span>{views}</span> {t("article.views")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="heading">
|
<div class='heading'>
|
||||||
<h1 itemprop="headline">{title}</h1>
|
<h1 itemprop='headline'>{title}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="published">
|
<div class='published'>
|
||||||
<time itemprop="datePublished" datetime={pub_date}>
|
<time itemprop='datePublished' datetime={pub_date}>
|
||||||
{createDateFormatter(pageLang).format(new Date(pub_date))}
|
{createDateFormatter(pageLang).format(new Date(pub_date))}
|
||||||
</time>
|
</time>
|
||||||
<span itemprop="author">{author}</span>
|
<span itemprop='author'>{`${author.first_name} ${author.last_name}`}</span>
|
||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
cover_image && (
|
cover_image && (
|
||||||
<ArticleCover src={`${DIRECTUS_URL}/assets/${cover_image.filename_disk}`} alt={title} />
|
<ArticleCover src={`${DIRECTUS_URL}/assets/${cover_image.filename_disk}`} alt={title} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div class="description">
|
<div class='description'>
|
||||||
<p>
|
<p>
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
@ -72,19 +74,7 @@ const t = useTranslation(Astro);
|
|||||||
<ArticleComments url={canonicalUrl.href} id={slug} />
|
<ArticleComments url={canonicalUrl.href} id={slug} />
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<script define:vars={{ slug, IS_DEV: import.meta.env.DEV }}>
|
<style lang='scss' is:global>
|
||||||
(async () => {
|
|
||||||
if (IS_DEV) return;
|
|
||||||
const response = await fetch(`/api/views?slug=${slug}`, { method: "POST" });
|
|
||||||
if (!response.ok) return;
|
|
||||||
const views = await response.text();
|
|
||||||
const $views = document.getElementById("views");
|
|
||||||
if (isNaN(parseInt(views, 10))) return;
|
|
||||||
if ($views) $views.textContent = views;
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" is:global>
|
|
||||||
.article {
|
.article {
|
||||||
& header {
|
& header {
|
||||||
& div:nth-child(1) {
|
& div:nth-child(1) {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||||
const { lang = DEFAULT_LANGUAGE, article, headings, availableOnLangs } = Astro.props as Props;
|
const { lang = DEFAULT_LANGUAGE, article, availableOnLangs } = Astro.props as Props;
|
||||||
const { title, description, cover_image, content } = article;
|
const { title, description, cover_image, content } = article;
|
||||||
const htmlContent = await utils.markdownToHTML(content);
|
const htmlContent = await utils.markdownToHTML(content);
|
||||||
---
|
---
|
||||||
@ -30,19 +30,18 @@ const htmlContent = await utils.markdownToHTML(content);
|
|||||||
title={title}
|
title={title}
|
||||||
siteName={SITE_METADATA.name}
|
siteName={SITE_METADATA.name}
|
||||||
description={description}
|
description={description}
|
||||||
type="article"
|
type='article'
|
||||||
url={canonicalUrl.href}
|
url={canonicalUrl.href}
|
||||||
image={cover_image
|
image={cover_image
|
||||||
? { path: cover_image.filename_disk, width: "700", height: "400", type: cover_image.type }
|
? { path: cover_image.filename_disk, width: "700", height: "400", type: cover_image.type }
|
||||||
: undefined}
|
: undefined}
|
||||||
/>
|
/>
|
||||||
<script is:inline defer src="/assets/js/load-code-highlight.js"></script>
|
<script is:inline defer src='/assets/js/load-code-highlight.js'></script>
|
||||||
</BaseHead>
|
</BaseHead>
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class="container">
|
<div class='container'>
|
||||||
<ArticlePost lang={lang} article={article} availableOnLangs={availableOnLangs}>
|
<ArticlePost lang={lang} article={article} availableOnLangs={availableOnLangs}>
|
||||||
<!-- <TableOfContents headings={headings} /> -->
|
|
||||||
<ArticleContent set:html={htmlContent} />
|
<ArticleContent set:html={htmlContent} />
|
||||||
</ArticlePost>
|
</ArticlePost>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
15
src/pages/[blog]/[...path].astro
Normal file
15
src/pages/[blog]/[...path].astro
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
import ArticleLayout from "@root/layouts/article.astro";
|
||||||
|
import { fetchArticleBySlug } from "@/directus";
|
||||||
|
|
||||||
|
const { path } = Astro.params as { blog: string; path: string };
|
||||||
|
const slug = path.split("/").at(-1);
|
||||||
|
|
||||||
|
if (!slug) return Astro.redirect("/404");
|
||||||
|
|
||||||
|
const article = await fetchArticleBySlug(slug);
|
||||||
|
|
||||||
|
if (!article) return Astro.redirect("/404");
|
||||||
|
---
|
||||||
|
|
||||||
|
<ArticleLayout lang={article.languages_code.code} article={article} />
|
||||||
@ -1,91 +0,0 @@
|
|||||||
---
|
|
||||||
import type { CollectionEntry } from "astro:content";
|
|
||||||
import { getCollection, getEntry, render } from "astro:content";
|
|
||||||
import slugify from "@sindresorhus/slugify";
|
|
||||||
import ArticleLayout from "@root/layouts/article.astro";
|
|
||||||
import { createRelativeArticleUrl, removeLanguageCodeFromPath } from "@root/utils";
|
|
||||||
import directus from "@/directus";
|
|
||||||
import type { ArticleTranslation } from "@/directus-schema";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate articles pages from `blog` collection.
|
|
||||||
* The final url will be `http://website.com/blog/2023-01-01/my-article` with default language
|
|
||||||
* and `http://website.com/{language-code}/blog/2023-01-01/my-article` with other languages.
|
|
||||||
*/
|
|
||||||
// export async function getStaticPaths() {
|
|
||||||
// const collection = await getCollection("blog", (e) => !e.data.draft);
|
|
||||||
// return collection.map((article) => {
|
|
||||||
// const {
|
|
||||||
// id,
|
|
||||||
// data: { title, pubDate },
|
|
||||||
// } = article;
|
|
||||||
// const url = createRelativeArticleUrl({ id, title, pubDate });
|
|
||||||
// return {
|
|
||||||
// params: { blog: url.slice(1) },
|
|
||||||
// props: {
|
|
||||||
// article,
|
|
||||||
// availableOnLangs: collection.filter((e) =>
|
|
||||||
// e.id.includes(removeLanguageCodeFromPath(article.id))
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
type Props = { article: CollectionEntry<"blog">; availableOnLangs: CollectionEntry<"blog">[] };
|
|
||||||
|
|
||||||
const { slug } = Astro.params as { blog: string; date: string; slug: string };
|
|
||||||
|
|
||||||
const { article_translations: data } = await directus.query<{
|
|
||||||
article_translations: ArticleTranslation[];
|
|
||||||
}>(`
|
|
||||||
query Article_translations {
|
|
||||||
article_translations(
|
|
||||||
filter: {
|
|
||||||
slug: {
|
|
||||||
_eq: "${slug}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
title
|
|
||||||
tags {
|
|
||||||
tag_id {
|
|
||||||
title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
description
|
|
||||||
slug
|
|
||||||
content
|
|
||||||
author {
|
|
||||||
first_name
|
|
||||||
last_name
|
|
||||||
}
|
|
||||||
pub_date
|
|
||||||
views
|
|
||||||
languages_code {
|
|
||||||
code
|
|
||||||
}
|
|
||||||
cover_image {
|
|
||||||
title
|
|
||||||
filename_disk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
// const { blog } = Astro.params as { blog: string };
|
|
||||||
// const collection = await getCollection("blog", (e) => !e.data.draft);
|
|
||||||
// const slug = blog.split("/").at(-1);
|
|
||||||
// const article = collection.find(
|
|
||||||
// (a) => slugify(a.slug.split("/").at(-1) || "", { lowercase: true }) === slug
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (data.length === 0) return Astro.redirect("/404");
|
|
||||||
|
|
||||||
const article = data[0];
|
|
||||||
|
|
||||||
// const { Content, headings } = await render(article);
|
|
||||||
// const { article, availableOnLangs } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<ArticleLayout lang={article.languages_code.code} article={article} />
|
|
||||||
@ -65,10 +65,6 @@ export function formatToArticleBlocks(articles: CollectionEntry<"blog">[]) {
|
|||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUniqueTags(articles: CollectionEntry<"blog">[]): string[] {
|
|
||||||
return Array.from(new Set(articles.map((a) => a.data.tags).flat()));
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MarkdownHeadingToc = MarkdownHeading & { subheadings?: MarkdownHeading[] };
|
export type MarkdownHeadingToc = MarkdownHeading & { subheadings?: MarkdownHeading[] };
|
||||||
|
|
||||||
export function buildToc(headings: MarkdownHeading[], maxDepth: number = 2) {
|
export function buildToc(headings: MarkdownHeading[], maxDepth: number = 2) {
|
||||||
@ -96,8 +92,8 @@ export async function markdownToHTML(content: string): Promise<string> {
|
|||||||
|
|
||||||
const file = await unified()
|
const file = await unified()
|
||||||
.use(remarkParse)
|
.use(remarkParse)
|
||||||
.use(remarkPrism)
|
.use(remarkToc, { heading: "(table[ -]of[ -])?contents?|toc|содержание", maxDepth: 2 })
|
||||||
.use(remarkToc)
|
.use(remarkPrism as any)
|
||||||
.use(remarkRehype)
|
.use(remarkRehype)
|
||||||
.use(rehypeSlug)
|
.use(rehypeSlug)
|
||||||
.use(rehypeAutolinkHeadings, {
|
.use(rehypeAutolinkHeadings, {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user