Migrate to Astro V5 (#22)
* fix video url * update deploy script for using rsync * remove delete from script * translate resources page * fix images dimensions and favicon * add css optimization plugin and improve images * update project dependencies * refactor lang * post translated to spanish * fix metadata * update dependencies * update dependencies * update dependencies * update to Astro 5.x * format index.astro * Migrate content layer to Astro V5
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout title="Not found" description="Error 404: Not found.">
|
||||
<div class="prose prose-invert">
|
||||
<h1 class="">Error 404: Not found</h1>
|
||||
<Layout
|
||||
title='Not found'
|
||||
description='Error 404: Not found.'
|
||||
>
|
||||
<div class='prose prose-invert'>
|
||||
<h1 class=''>Error 404: Not found</h1>
|
||||
</div>
|
||||
</Layout>
|
||||
|
@@ -1,28 +1,38 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
interface Props {
|
||||
page: CollectionEntry<"pages">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allPages = await getCollection("pages");
|
||||
const allPages = await getCollection('pages');
|
||||
const filterEnPages = allPages.map((page) => {
|
||||
const [lang, id] = page.id.split('/');
|
||||
|
||||
return allPages.map((page: CollectionEntry<"pages">) => ({
|
||||
params: { slug: page.slug },
|
||||
props: { page },
|
||||
if (lang === 'en')
|
||||
return {
|
||||
...page,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
|
||||
return filterEnPages.map((page) => ({
|
||||
params: { slug: page?.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
const { Content } = await page.render();
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const project = await getEntry('pages', `${lang}/${slug}`)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(project);
|
||||
---
|
||||
|
||||
<Layout {...page.data}>
|
||||
<article class="prose prose-invert">
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<Content components={{ ...components }} />
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,51 +1,48 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import formatDate from "@/utils/format-date";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
|
||||
interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import formatDate from '@/utils/format-date';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allBlogPosts = await getCollection(
|
||||
"blog",
|
||||
({ data }) => data.draft !== true,
|
||||
);
|
||||
const filterEnPosts = allBlogPosts.map((post) => {
|
||||
const [lang, ...slug] = post.slug.split("/");
|
||||
const allBlogPosts = await getCollection(
|
||||
'blog',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const filterEnPosts = allBlogPosts.map((post) => {
|
||||
const [lang, id] = post.id.split('/');
|
||||
|
||||
if (lang === "en")
|
||||
return {
|
||||
...post,
|
||||
slug: slug.toString(),
|
||||
};
|
||||
else null;
|
||||
});
|
||||
if (lang === 'en')
|
||||
return {
|
||||
...post,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
|
||||
return filterEnPosts.map((post) => ({
|
||||
params: { slug: post?.slug },
|
||||
props: { post },
|
||||
}));
|
||||
return filterEnPosts.map((post) => ({
|
||||
params: { slug: post?.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
const { Content } = await post.render();
|
||||
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const post = await getEntry('blog', `${lang}/${slug}`)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(post);
|
||||
---
|
||||
|
||||
<Layout title={post.data.title} description={post.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{post.data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Posted: </strong>
|
||||
{post.data.date && formatDate(new Date(post.data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<h1>{data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Posted: </strong>
|
||||
{data.date && formatDate(new Date(data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,25 +1,25 @@
|
||||
---
|
||||
import PostItem from "@/components/post-item";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getCollection } from "astro:content";
|
||||
import PostItem from '@/components/post-item';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pageData = {
|
||||
title: "Blog",
|
||||
description: "Long format about thoughts and other topics.",
|
||||
title: 'Blog',
|
||||
description: 'Long format about thoughts and other topics.',
|
||||
};
|
||||
|
||||
const allPosts = await getCollection("blog", ({ data }) => data.draft !== true);
|
||||
const allPosts = await getCollection('blog', ({ data }) => data.draft !== true);
|
||||
const filterEnPosts = allPosts.map((post) => {
|
||||
const [lang, ...slug] = post.slug.split("/");
|
||||
const [lang, id] = post.id.split('/');
|
||||
|
||||
if (lang === "en")
|
||||
return {
|
||||
...post,
|
||||
slug: slug.toString(),
|
||||
};
|
||||
else null;
|
||||
if (lang === 'en')
|
||||
return {
|
||||
...post,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
sortContentByDate(filterEnPosts);
|
||||
|
||||
@@ -27,26 +27,26 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class="mt-4 flex flex-col gap-4">
|
||||
{
|
||||
filterEnPosts.map(
|
||||
(blogpost) =>
|
||||
blogpost && (
|
||||
<li>
|
||||
<PostItem
|
||||
type="blog"
|
||||
lang={lang}
|
||||
slug={blogpost.slug}
|
||||
date={blogpost.data.date!}
|
||||
title={blogpost.data.title!}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<section class='prose prose-invert'>
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class='mt-4 flex flex-col gap-4'>
|
||||
{
|
||||
filterEnPosts.map(
|
||||
(blogpost) =>
|
||||
blogpost && (
|
||||
<li>
|
||||
<PostItem
|
||||
type='blog'
|
||||
lang={lang}
|
||||
id={blogpost.id}
|
||||
date={blogpost.data.date!}
|
||||
title={blogpost.data.title!}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</Layout>
|
||||
|
@@ -1,28 +1,38 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
interface Props {
|
||||
page: CollectionEntry<"pages">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allPages = await getCollection("pages");
|
||||
const allPages = await getCollection('pages');
|
||||
const filterEsPages = allPages.map((page) => {
|
||||
const [lang, id] = page.id.split('/');
|
||||
|
||||
return allPages.map((page: CollectionEntry<"pages">) => ({
|
||||
params: { slug: page.slug },
|
||||
props: { page },
|
||||
if (lang === 'es')
|
||||
return {
|
||||
...page,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
|
||||
return filterEsPages.map((page) => ({
|
||||
params: { slug: page?.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
const { Content } = await page.render();
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const page = await getEntry('pages', `${lang}/${slug}`)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(page);
|
||||
---
|
||||
|
||||
<Layout {...page.data}>
|
||||
<article class="prose prose-invert">
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<Content components={{ ...components }} />
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,51 +1,48 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import formatDate from "@/utils/format-date";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
|
||||
interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import formatDate from '@/utils/format-date';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allBlogPosts = await getCollection(
|
||||
"blog",
|
||||
({ data }) => data.draft !== true,
|
||||
'blog',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const filterEsPosts = allBlogPosts.map((post) => {
|
||||
const [lang, ...slug] = post.slug.split("/");
|
||||
const [lang, id] = post.id.split('/');
|
||||
|
||||
if (lang === "es")
|
||||
if (lang === 'es')
|
||||
return {
|
||||
...post,
|
||||
slug: slug.toString(),
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
|
||||
return filterEsPosts.map((post) => ({
|
||||
params: { slug: post?.slug },
|
||||
props: { post },
|
||||
params: { slug: post?.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
const { Content } = await post.render();
|
||||
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const blog = await getEntry('blog', `${lang}/${slug}`)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(blog);
|
||||
---
|
||||
|
||||
<Layout title={post.data.title} description={post.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{post.data.title}</h1>
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<h1>{data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Publicado: </strong>
|
||||
{post.data.date && formatDate(new Date(post.data.date), lang)}
|
||||
{data.date && formatDate(new Date(data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,25 +1,25 @@
|
||||
---
|
||||
import PostItem from "@/components/post-item";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getCollection } from "astro:content";
|
||||
import PostItem from '@/components/post-item';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pageData = {
|
||||
title: "Blog",
|
||||
description: "Formato largo sobre pensamientos y otros temas.",
|
||||
title: 'Blog',
|
||||
description: 'Formato largo sobre pensamientos y otros temas.',
|
||||
};
|
||||
|
||||
const allPosts = await getCollection("blog", ({ data }) => data.draft !== true);
|
||||
const allPosts = await getCollection('blog', ({ data }) => data.draft !== true);
|
||||
const filterEsPosts = allPosts.map((post) => {
|
||||
const [lang, ...slug] = post.slug.split("/");
|
||||
const [lang, id] = post.id.split('/');
|
||||
|
||||
if (lang === "es")
|
||||
return {
|
||||
...post,
|
||||
slug: slug.toString(),
|
||||
};
|
||||
else null;
|
||||
if (lang === 'es')
|
||||
return {
|
||||
...post,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
sortContentByDate(filterEsPosts);
|
||||
|
||||
@@ -27,26 +27,26 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class="mt-4 flex flex-col gap-4">
|
||||
{
|
||||
filterEsPosts.map(
|
||||
(post) =>
|
||||
post && (
|
||||
<li>
|
||||
<PostItem
|
||||
type="blog"
|
||||
lang={lang}
|
||||
slug={post?.slug}
|
||||
date={post?.data.date!}
|
||||
title={post?.data.title!}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<section class='prose prose-invert'>
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class='mt-4 flex flex-col gap-4'>
|
||||
{
|
||||
filterEsPosts.map(
|
||||
(post) =>
|
||||
post && (
|
||||
<li>
|
||||
<PostItem
|
||||
type='blog'
|
||||
lang={lang}
|
||||
id={post.id}
|
||||
date={post.data.date}
|
||||
title={post.data.title}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</Layout>
|
||||
|
@@ -1,54 +1,54 @@
|
||||
import rss from "@astrojs/rss";
|
||||
import type { RSSFeedItem } from "@astrojs/rss";
|
||||
import { getCollection } from "astro:content";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { parse as htmlParser } from "node-html-parser";
|
||||
import { getImage } from "astro:assets";
|
||||
import type { ImageMetadata } from "astro";
|
||||
import rss from '@astrojs/rss';
|
||||
import type { RSSFeedItem } from '@astrojs/rss';
|
||||
import { getCollection } from 'astro:content';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import { parse as htmlParser } from 'node-html-parser';
|
||||
import { getImage } from 'astro:assets';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
|
||||
const markdownParser = new MarkdownIt();
|
||||
|
||||
const imagesBlog = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}"
|
||||
'/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}'
|
||||
);
|
||||
const imagesPortfolio = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}"
|
||||
'/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}'
|
||||
);
|
||||
|
||||
export async function GET(context: any) {
|
||||
const items: RSSFeedItem[] = [];
|
||||
|
||||
const blog = await getCollection(
|
||||
"blog",
|
||||
'blog',
|
||||
({ data }) => data.draft !== true && data.rss === true
|
||||
);
|
||||
const filterBlog = blog.filter((post) => {
|
||||
const [lang] = post.slug.split("/");
|
||||
const [lang] = post.id.split('/');
|
||||
|
||||
return lang === "es" && post;
|
||||
return lang === 'es' && post;
|
||||
});
|
||||
|
||||
const portfolio = await getCollection(
|
||||
"portfolio",
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true && data.rss === true
|
||||
);
|
||||
const filterPortfolio = portfolio.filter((project) => {
|
||||
const [lang] = project.slug.split("/");
|
||||
const [lang] = project.id.split('/');
|
||||
|
||||
return lang === "es" && project;
|
||||
return lang === 'es' && project;
|
||||
});
|
||||
|
||||
for await (const post of filterBlog) {
|
||||
const body = markdownParser.render(post.body);
|
||||
const body = markdownParser.render(post.body!);
|
||||
const html = htmlParser.parse(body);
|
||||
const images = html.querySelectorAll("img");
|
||||
const images = html.querySelectorAll('img');
|
||||
|
||||
for await (const img of images) {
|
||||
const src = img.getAttribute("src")!;
|
||||
const src = img.getAttribute('src')!;
|
||||
|
||||
if (src.startsWith("@/")) {
|
||||
const prefixRemoved = src.replace("@/", "");
|
||||
if (src.startsWith('@/')) {
|
||||
const prefixRemoved = src.replace('@/', '');
|
||||
const imagePathPrefix = `/src/${prefixRemoved}`;
|
||||
const imagePath = await imagesBlog[imagePathPrefix]?.()?.then(
|
||||
(res: any) => res.default
|
||||
@@ -57,14 +57,14 @@ export async function GET(context: any) {
|
||||
if (imagePath) {
|
||||
const optimizedImg = await getImage({ src: imagePath });
|
||||
img.setAttribute(
|
||||
"src",
|
||||
context.site + optimizedImg.src.replace("/", "")
|
||||
'src',
|
||||
context.site + optimizedImg.src.replace('/', '')
|
||||
);
|
||||
}
|
||||
} else if (src.startsWith("/images")) {
|
||||
img.setAttribute("src", context.site + src.replace("/", ""));
|
||||
} else if (src.startsWith('/images')) {
|
||||
img.setAttribute('src', context.site + src.replace('/', ''));
|
||||
} else {
|
||||
throw Error("src unknown");
|
||||
throw Error('src unknown');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,23 +72,23 @@ export async function GET(context: any) {
|
||||
title: post.data.title,
|
||||
pubDate: post.data.date,
|
||||
description: post.data.description,
|
||||
link: `/blog/${post.slug}/`,
|
||||
link: `/blog/${post.id.split('.')[0]}/`,
|
||||
content: sanitizeHtml(html.toString(), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
for await (const project of filterPortfolio) {
|
||||
const body = markdownParser.render(project.body);
|
||||
const body = markdownParser.render(project.body!);
|
||||
const html = htmlParser.parse(body);
|
||||
const images = html.querySelectorAll("img");
|
||||
const images = html.querySelectorAll('img');
|
||||
|
||||
for await (const img of images) {
|
||||
const src = img.getAttribute("src")!;
|
||||
const src = img.getAttribute('src')!;
|
||||
|
||||
if (src.startsWith("@/")) {
|
||||
const prefixRemoved = src.replace("@/", "");
|
||||
if (src.startsWith('@/')) {
|
||||
const prefixRemoved = src.replace('@/', '');
|
||||
const imagePathPrefix = `/src/${prefixRemoved}`;
|
||||
const imagePath = await imagesPortfolio[imagePathPrefix]?.()?.then(
|
||||
(res: any) => res.default
|
||||
@@ -97,15 +97,15 @@ export async function GET(context: any) {
|
||||
if (imagePath) {
|
||||
const optimizedImg = await getImage({ src: imagePath });
|
||||
img.setAttribute(
|
||||
"src",
|
||||
context.site + optimizedImg.src.replace("/", "")
|
||||
'src',
|
||||
context.site + optimizedImg.src.replace('/', '')
|
||||
);
|
||||
}
|
||||
} else if (src.startsWith("/images")) {
|
||||
} else if (src.startsWith('/images')) {
|
||||
// images starting with `/images/` is the public dir
|
||||
img.setAttribute("src", context.site + src.replace("/", ""));
|
||||
img.setAttribute('src', context.site + src.replace('/', ''));
|
||||
} else {
|
||||
throw Error("src unknown");
|
||||
throw Error('src unknown');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,27 +113,27 @@ export async function GET(context: any) {
|
||||
title: project.data.title,
|
||||
pubDate: project.data.date,
|
||||
description: project.data.description,
|
||||
link: `/portfolio/${project.slug}/`,
|
||||
link: `/portfolio/${project.id.split('.')[0]}/`,
|
||||
content: sanitizeHtml(html.toString(), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return rss({
|
||||
xmlns: { atom: "http://www.w3.org/2005/Atom" },
|
||||
title: "juancmandev",
|
||||
description: "Bienvenido a mi dominio, extraño.",
|
||||
xmlns: { atom: 'http://www.w3.org/2005/Atom' },
|
||||
title: 'juancmandev',
|
||||
description: 'Bienvenido a mi dominio, extraño.',
|
||||
site: `${context.site}es/`,
|
||||
customData: [
|
||||
"<language>es-mx</language>",
|
||||
'<language>es-mx</language>',
|
||||
`<image>
|
||||
<url>https://juancman.dev/logo.png</url>
|
||||
<title>juancmandev</title>
|
||||
<link>https://juancman.dev</link>
|
||||
</image>`,
|
||||
`<atom:link href="${context.site}es/feed.xml" rel="self" type="application/rss+xml"/>`,
|
||||
].join(""),
|
||||
].join(''),
|
||||
items,
|
||||
trailingSlash: false,
|
||||
});
|
||||
|
@@ -1,25 +1,25 @@
|
||||
---
|
||||
import LinkButton from "@/components/link-button";
|
||||
import PostItem from "@/components/post-item";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getCollection } from "astro:content";
|
||||
import LinkButton from '@/components/link-button';
|
||||
import PostItem from '@/components/post-item';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pageData = {
|
||||
title: "juancmandev",
|
||||
title: 'juancmandev',
|
||||
description:
|
||||
"Bienvenido a mi dominio, extraño. Soy juancmandev; Desarrollador Web, entusiasta de Linux, y defensor de la privacidad.",
|
||||
'Bienvenido a mi dominio, extraño. Soy juancmandev; Desarrollador Web, entusiasta de Linux, y defensor de la privacidad.',
|
||||
};
|
||||
|
||||
const allPosts = await getCollection("blog", ({ data }) => data.draft !== true);
|
||||
const allPosts = await getCollection('blog', ({ data }) => data.draft !== true);
|
||||
const allEsPosts = allPosts.map((post) => {
|
||||
const [lang, ...slug] = post.slug.split("/");
|
||||
const [lang, id] = post.id.split('/');
|
||||
|
||||
if (lang === "es")
|
||||
if (lang !== 'en')
|
||||
return {
|
||||
...post,
|
||||
slug: slug.toString(),
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
@@ -27,16 +27,16 @@ sortContentByDate(allEsPosts);
|
||||
const last3Blogs = allEsPosts.slice(0, 3);
|
||||
|
||||
const allProjects = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true,
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const allEnProjects = allProjects.map((project) => {
|
||||
const [lang, ...slug] = project.slug.split("/");
|
||||
const [lang, id] = project.id.split('/');
|
||||
|
||||
if (lang === "es")
|
||||
if (lang !== 'en')
|
||||
return {
|
||||
...project,
|
||||
slug: slug.toString(),
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
@@ -47,10 +47,10 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<div class="prose prose-invert">
|
||||
<h1 class="text-primary">Bienvenido a mi dominio, extraño.</h1>
|
||||
<div class='prose prose-invert'>
|
||||
<h1 class='text-primary'>Bienvenido a mi dominio, extraño.</h1>
|
||||
<p>
|
||||
Soy <strong class="text-primary">juancmandev</strong>; <strong
|
||||
Soy <strong class='text-primary'>juancmandev</strong>; <strong
|
||||
>Desarrollador Web</strong
|
||||
>, entusiasta de <strong>Linux</strong> y defensor de la <strong
|
||||
>privacidad.</strong
|
||||
@@ -63,52 +63,56 @@ const lang = getLangFromUrl(Astro.url);
|
||||
</p>
|
||||
<section>
|
||||
<h2>Últimos posts</h2>
|
||||
<ul class="mt-0 p-0 list-none">
|
||||
<ul class='mt-0 p-0 list-none'>
|
||||
{
|
||||
last3Blogs.map(
|
||||
(blogpost) =>
|
||||
blogpost && (
|
||||
<li class="p-0">
|
||||
<li class='p-0'>
|
||||
<PostItem
|
||||
type="blog"
|
||||
type='blog'
|
||||
lang={lang}
|
||||
slug={blogpost?.slug}
|
||||
id={blogpost.id}
|
||||
date={blogpost.data.date}
|
||||
title={blogpost.data.title}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<LinkButton variant="secondary" href="/es/blog" className="no-underline"
|
||||
<LinkButton
|
||||
variant='secondary'
|
||||
href='/es/blog'
|
||||
className='no-underline'
|
||||
>Más posts</LinkButton
|
||||
>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Últimos proyectos</h2>
|
||||
<ul class="mt-0 p-0 list-none">
|
||||
<ul class='mt-0 p-0 list-none'>
|
||||
{
|
||||
last3Projects.map(
|
||||
(project) =>
|
||||
project && (
|
||||
<li class="p-0">
|
||||
<li class='p-0'>
|
||||
<PostItem
|
||||
lang={lang}
|
||||
type="portfolio"
|
||||
slug={project.slug}
|
||||
type='portfolio'
|
||||
id={project.id}
|
||||
date={project.data.date!}
|
||||
title={project.data.title!}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<LinkButton
|
||||
variant="secondary"
|
||||
href="/es/portfolio"
|
||||
className="no-underline">Más proyectos</LinkButton
|
||||
variant='secondary'
|
||||
href='/es/portfolio'
|
||||
className='no-underline'
|
||||
>Más proyectos</LinkButton
|
||||
>
|
||||
</section>
|
||||
</div>
|
||||
|
@@ -1,51 +1,48 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import formatDate from "@/utils/format-date";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
|
||||
interface Props {
|
||||
project: CollectionEntry<"portfolio">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import formatDate from '@/utils/format-date';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allProjects = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true,
|
||||
);
|
||||
const filterEnProjects = allProjects.map((project) => {
|
||||
const [lang, ...slug] = project.slug.split("/");
|
||||
const allProjects = await getCollection(
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const filterEnProjects = allProjects.map((project) => {
|
||||
const [lang, id] = project.id.split('/');
|
||||
|
||||
if (lang === "es")
|
||||
return {
|
||||
...project,
|
||||
slug: slug.toString(),
|
||||
};
|
||||
else null;
|
||||
});
|
||||
if (lang === 'es')
|
||||
return {
|
||||
...project,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
|
||||
return filterEnProjects.map((project) => ({
|
||||
params: { slug: project?.slug },
|
||||
props: { project },
|
||||
}));
|
||||
return filterEnProjects.map((project) => ({
|
||||
params: { slug: project?.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { project } = Astro.props;
|
||||
const { Content } = await project.render();
|
||||
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const project = await getEntry('portfolio', `${lang}/${slug}`)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(project);
|
||||
---
|
||||
|
||||
<Layout title={project.data.title} description={project.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{project.data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Publicado: </strong>
|
||||
{project.data.date && formatDate(new Date(project.data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<h1>{data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Publicado: </strong>
|
||||
{data.date && formatDate(new Date(data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,28 +1,28 @@
|
||||
---
|
||||
import PostItem from "@/components/post-item";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getCollection } from "astro:content";
|
||||
import PostItem from '@/components/post-item';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pageData = {
|
||||
title: "Portfolio",
|
||||
description: "Revisa mis proyectos.",
|
||||
title: 'Portfolio',
|
||||
description: 'Revisa mis proyectos.',
|
||||
};
|
||||
|
||||
const allProjects = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true,
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const allEsProjects = allProjects.map((project) => {
|
||||
const [lang, ...slug] = project.slug.split("/");
|
||||
const [lang, id] = project.id.split('/');
|
||||
|
||||
if (lang === "es")
|
||||
return {
|
||||
...project,
|
||||
slug: slug.toString(),
|
||||
};
|
||||
else null;
|
||||
if (lang === 'es')
|
||||
return {
|
||||
...project,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
sortContentByDate(allEsProjects);
|
||||
|
||||
@@ -30,26 +30,26 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class="mt-4 flex flex-col gap-4">
|
||||
{
|
||||
allEsProjects.map(
|
||||
(project) =>
|
||||
project && (
|
||||
<li>
|
||||
<PostItem
|
||||
lang={lang}
|
||||
type="portfolio"
|
||||
slug={project.slug}
|
||||
date={project.data.date!}
|
||||
title={project.data.title!}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<section class='prose prose-invert'>
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class='mt-4 flex flex-col gap-4'>
|
||||
{
|
||||
allEsProjects.map(
|
||||
(project) =>
|
||||
project && (
|
||||
<li>
|
||||
<PostItem
|
||||
lang={lang}
|
||||
type='portfolio'
|
||||
id={project.id}
|
||||
date={project.data.date!}
|
||||
title={project.data.title!}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</Layout>
|
||||
|
@@ -1,41 +1,46 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import formatDate from "@/utils/format-date";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
|
||||
interface Props {
|
||||
project: CollectionEntry<"videos">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import formatDate from '@/utils/format-date';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allProjects = await getCollection(
|
||||
"videos",
|
||||
({ data }) => data.draft !== true,
|
||||
const allVideos = await getCollection(
|
||||
'videos',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const formatVideos = allVideos.map((video) => {
|
||||
const [id] = video.id.split('/');
|
||||
|
||||
return allProjects.map((project) => ({
|
||||
params: { slug: project.slug },
|
||||
props: { project },
|
||||
return {
|
||||
...video,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
});
|
||||
|
||||
return formatVideos.map((video) => ({
|
||||
params: { slug: video.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { project } = Astro.props;
|
||||
const { Content } = await project.render();
|
||||
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const video = await getEntry('videos', slug)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(video);
|
||||
---
|
||||
|
||||
<Layout title={project.data.title} description={project.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{project.data.title}</h1>
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<h1>{data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Posted: </strong>
|
||||
{project.data.date && formatDate(new Date(project.data.date), lang)}
|
||||
{data.date && formatDate(new Date(data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,18 +1,18 @@
|
||||
---
|
||||
import PostItem from "@/components/post-item";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getCollection } from "astro:content";
|
||||
import PostItem from '@/components/post-item';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pageData = {
|
||||
title: "Videos",
|
||||
description: "Guiones de los videos de mi canal de YouTube.",
|
||||
title: 'Videos',
|
||||
description: 'Guiones de los videos de mi canal de YouTube.',
|
||||
};
|
||||
|
||||
const allVideos = await getCollection(
|
||||
"videos",
|
||||
({ data }) => data.draft !== true,
|
||||
'videos',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
sortContentByDate(allVideos);
|
||||
|
||||
@@ -20,18 +20,18 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<section class='prose prose-invert'>
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class="mt-4 flex flex-col gap-4">
|
||||
<ul class='mt-4 flex flex-col gap-4'>
|
||||
{
|
||||
allVideos.map((video: any) => (
|
||||
<li>
|
||||
<PostItem
|
||||
lang={lang}
|
||||
type="videos"
|
||||
slug={video.slug}
|
||||
type='videos'
|
||||
id={video.id.split('.')[0]}
|
||||
date={video.data.date!}
|
||||
title={video.data.title!}
|
||||
/>
|
||||
|
@@ -1,54 +1,54 @@
|
||||
import rss from "@astrojs/rss";
|
||||
import type { RSSFeedItem } from "@astrojs/rss";
|
||||
import { getCollection } from "astro:content";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { parse as htmlParser } from "node-html-parser";
|
||||
import { getImage } from "astro:assets";
|
||||
import type { ImageMetadata } from "astro";
|
||||
import rss from '@astrojs/rss';
|
||||
import type { RSSFeedItem } from '@astrojs/rss';
|
||||
import { getCollection } from 'astro:content';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import { parse as htmlParser } from 'node-html-parser';
|
||||
import { getImage } from 'astro:assets';
|
||||
import type { ImageMetadata } from 'astro';
|
||||
|
||||
const markdownParser = new MarkdownIt();
|
||||
|
||||
const imagesBlog = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}"
|
||||
'/src/assets/blog/**/**/*.{jpeg,jpg,png,gif,webp}'
|
||||
);
|
||||
const imagesPortfolio = import.meta.glob<{ default: ImageMetadata }>(
|
||||
"/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}"
|
||||
'/src/assets/portfolio/**/**/*.{jpeg,jpg,png,gif,webp}'
|
||||
);
|
||||
|
||||
export async function GET(context: any) {
|
||||
const items: RSSFeedItem[] = [];
|
||||
|
||||
const blog = await getCollection(
|
||||
"blog",
|
||||
'blog',
|
||||
({ data }) => data.draft !== true && data.rss === true
|
||||
);
|
||||
const filterBlog = blog.filter((post) => {
|
||||
const [lang] = post.slug.split("/");
|
||||
const [lang] = post.id.split('/');
|
||||
|
||||
return lang !== "es" && post;
|
||||
return lang !== 'es' && post;
|
||||
});
|
||||
|
||||
const portfolio = await getCollection(
|
||||
"portfolio",
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true && data.rss === true
|
||||
);
|
||||
const filterPortfolio = portfolio.filter((project) => {
|
||||
const [lang] = project.slug.split("/");
|
||||
const [lang] = project.id.split('/');
|
||||
|
||||
return lang !== "es" && project;
|
||||
return lang !== 'es' && project;
|
||||
});
|
||||
|
||||
for await (const post of filterBlog) {
|
||||
const body = markdownParser.render(post.body);
|
||||
const body = markdownParser.render(post.body!);
|
||||
const html = htmlParser.parse(body);
|
||||
const images = html.querySelectorAll("img");
|
||||
const images = html.querySelectorAll('img');
|
||||
|
||||
for await (const img of images) {
|
||||
const src = img.getAttribute("src")!;
|
||||
const src = img.getAttribute('src')!;
|
||||
|
||||
if (src.startsWith("@/")) {
|
||||
const prefixRemoved = src.replace("@/", "");
|
||||
if (src.startsWith('@/')) {
|
||||
const prefixRemoved = src.replace('@/', '');
|
||||
const imagePathPrefix = `/src/${prefixRemoved}`;
|
||||
const imagePath = await imagesBlog[imagePathPrefix]?.()?.then(
|
||||
(res: any) => res.default
|
||||
@@ -57,14 +57,14 @@ export async function GET(context: any) {
|
||||
if (imagePath) {
|
||||
const optimizedImg = await getImage({ src: imagePath });
|
||||
img.setAttribute(
|
||||
"src",
|
||||
context.site + optimizedImg.src.replace("/", "")
|
||||
'src',
|
||||
context.site + optimizedImg.src.replace('/', '')
|
||||
);
|
||||
}
|
||||
} else if (src.startsWith("/images")) {
|
||||
img.setAttribute("src", context.site + src.replace("/", ""));
|
||||
} else if (src.startsWith('/images')) {
|
||||
img.setAttribute('src', context.site + src.replace('/', ''));
|
||||
} else {
|
||||
throw Error("src unknown");
|
||||
throw Error('src unknown');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,23 +72,23 @@ export async function GET(context: any) {
|
||||
title: post.data.title,
|
||||
pubDate: post.data.date,
|
||||
description: post.data.description,
|
||||
link: `/blog/${post.slug}/`,
|
||||
link: `/blog/${post.id.split('.')[0]}/`,
|
||||
content: sanitizeHtml(html.toString(), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
for await (const project of filterPortfolio) {
|
||||
const body = markdownParser.render(project.body);
|
||||
const body = markdownParser.render(project.body!);
|
||||
const html = htmlParser.parse(body);
|
||||
const images = html.querySelectorAll("img");
|
||||
const images = html.querySelectorAll('img');
|
||||
|
||||
for await (const img of images) {
|
||||
const src = img.getAttribute("src")!;
|
||||
const src = img.getAttribute('src')!;
|
||||
|
||||
if (src.startsWith("@/")) {
|
||||
const prefixRemoved = src.replace("@/", "");
|
||||
if (src.startsWith('@/')) {
|
||||
const prefixRemoved = src.replace('@/', '');
|
||||
const imagePathPrefix = `/src/${prefixRemoved}`;
|
||||
const imagePath = await imagesPortfolio[imagePathPrefix]?.()?.then(
|
||||
(res: any) => res.default
|
||||
@@ -97,15 +97,15 @@ export async function GET(context: any) {
|
||||
if (imagePath) {
|
||||
const optimizedImg = await getImage({ src: imagePath });
|
||||
img.setAttribute(
|
||||
"src",
|
||||
context.site + optimizedImg.src.replace("/", "")
|
||||
'src',
|
||||
context.site + optimizedImg.src.replace('/', '')
|
||||
);
|
||||
}
|
||||
} else if (src.startsWith("/images")) {
|
||||
} else if (src.startsWith('/images')) {
|
||||
// images starting with `/images/` is the public dir
|
||||
img.setAttribute("src", context.site + src.replace("/", ""));
|
||||
img.setAttribute('src', context.site + src.replace('/', ''));
|
||||
} else {
|
||||
throw Error("src unknown");
|
||||
throw Error('src unknown');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,27 +113,27 @@ export async function GET(context: any) {
|
||||
title: project.data.title,
|
||||
pubDate: project.data.date,
|
||||
description: project.data.description,
|
||||
link: `/portfolio/${project.slug}/`,
|
||||
link: `/portfolio/${project.id.split('.')[0]}/`,
|
||||
content: sanitizeHtml(html.toString(), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(["img"]),
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return rss({
|
||||
xmlns: { atom: "http://www.w3.org/2005/Atom" },
|
||||
title: "juancmandev",
|
||||
description: "Welcome to my domain, stranger.",
|
||||
xmlns: { atom: 'http://www.w3.org/2005/Atom' },
|
||||
title: 'juancmandev',
|
||||
description: 'Welcome to my domain, stranger.',
|
||||
site: context.site,
|
||||
customData: [
|
||||
"<language>en-us</language>",
|
||||
'<language>en-us</language>',
|
||||
`<image>
|
||||
<url>https://juancman.dev/logo.png</url>
|
||||
<title>juancmandev</title>
|
||||
<link>https://juancman.dev</link>
|
||||
</image>`,
|
||||
`<atom:link href="${context.site}feed.xml" rel="self" type="application/rss+xml"/>`,
|
||||
].join(""),
|
||||
].join(''),
|
||||
items,
|
||||
trailingSlash: false,
|
||||
});
|
||||
|
@@ -1,25 +1,25 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import LinkButton from "@/components/link-button";
|
||||
import { getCollection } from "astro:content";
|
||||
import PostItem from "@/components/post-item";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import LinkButton from '@/components/link-button';
|
||||
import { getCollection } from 'astro:content';
|
||||
import PostItem from '@/components/post-item';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
|
||||
const pageData = {
|
||||
title: "juancmandev",
|
||||
title: 'juancmandev',
|
||||
description:
|
||||
"Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender.",
|
||||
'Welcome to my domain, stranger. I am juancmandev; Web Developer, Linux enthusiast, and privacy defender.',
|
||||
};
|
||||
|
||||
const allPosts = await getCollection("blog", ({ data }) => data.draft !== true);
|
||||
const allPosts = await getCollection('blog', ({ data }) => data.draft !== true);
|
||||
const allEnPosts = allPosts.map((post) => {
|
||||
const [lang, ...slug] = post.slug.split("/");
|
||||
const [lang, id] = post.id.split('/');
|
||||
|
||||
if (lang !== "es")
|
||||
if (lang !== 'es')
|
||||
return {
|
||||
...post,
|
||||
slug: slug.toString(),
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
@@ -27,16 +27,16 @@ sortContentByDate(allEnPosts);
|
||||
const last3Blogs = allEnPosts.slice(0, 3);
|
||||
|
||||
const allProjects = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true,
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const allEnProjects = allProjects.map((project) => {
|
||||
const [lang, ...slug] = project.slug.split("/");
|
||||
const [lang, id] = project.id.split('/');
|
||||
|
||||
if (lang !== "es")
|
||||
if (lang !== 'es')
|
||||
return {
|
||||
...project,
|
||||
slug: slug.toString(),
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
@@ -47,10 +47,10 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<div class="prose prose-invert">
|
||||
<h1 class="text-primary">Welcome to my domain, stranger.</h1>
|
||||
<div class='prose prose-invert'>
|
||||
<h1 class='text-primary'>Welcome to my domain, stranger.</h1>
|
||||
<p>
|
||||
I am <strong class="text-primary">juancmandev</strong>; <strong
|
||||
I am <strong class='text-primary'>juancmandev</strong>; <strong
|
||||
>Web Developer</strong
|
||||
>, <strong>Linux</strong> enthusiast, and <strong>privacy</strong> defender.
|
||||
</p>
|
||||
@@ -61,14 +61,14 @@ const lang = getLangFromUrl(Astro.url);
|
||||
</p>
|
||||
<section>
|
||||
<h2>Latest posts</h2>
|
||||
<ul class="mt-0 p-0 list-none">
|
||||
<ul class='mt-0 p-0 list-none'>
|
||||
{
|
||||
last3Blogs.map((blogpost: any) => (
|
||||
<li class="p-0">
|
||||
<li class='p-0'>
|
||||
<PostItem
|
||||
type="blog"
|
||||
type='blog'
|
||||
lang={lang}
|
||||
slug={blogpost.slug}
|
||||
id={blogpost.id}
|
||||
date={blogpost.data.date!}
|
||||
title={blogpost.data.title!}
|
||||
/>
|
||||
@@ -76,31 +76,37 @@ const lang = getLangFromUrl(Astro.url);
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<LinkButton variant="secondary" href="/blog" className="no-underline"
|
||||
<LinkButton
|
||||
variant='secondary'
|
||||
href='/blog'
|
||||
className='no-underline'
|
||||
>More posts</LinkButton
|
||||
>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Latest projects</h2>
|
||||
<ul class="mt-0 p-0 list-none">
|
||||
<ul class='mt-0 p-0 list-none'>
|
||||
{
|
||||
last3Projects.map(
|
||||
(project) =>
|
||||
project && (
|
||||
<li class="p-0">
|
||||
<li class='p-0'>
|
||||
<PostItem
|
||||
lang={lang}
|
||||
type="portfolio"
|
||||
slug={project.slug}
|
||||
type='portfolio'
|
||||
id={project.id}
|
||||
date={project.data.date!}
|
||||
title={project.data.title!}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<LinkButton variant="secondary" href="/portfolio" className="no-underline"
|
||||
<LinkButton
|
||||
variant='secondary'
|
||||
href='/portfolio'
|
||||
className='no-underline'
|
||||
>More projects</LinkButton
|
||||
>
|
||||
</section>
|
||||
|
@@ -1,24 +1,24 @@
|
||||
---
|
||||
import MicroblogItem from "@/components/microblog-item.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { createServerClient } from "@/utils/pocketbase";
|
||||
import MicroblogItem from '@/components/microblog-item.astro';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { createServerClient } from '@/utils/pocketbase';
|
||||
|
||||
const pb = createServerClient(import.meta.env.SECRET_POCKETBASE_API_URL);
|
||||
const data = await pb.collection("microblogs").getFullList({
|
||||
expand: "tags",
|
||||
sort: "-published",
|
||||
const data = await pb.collection('microblogs').getFullList({
|
||||
expand: 'tags',
|
||||
sort: '-published',
|
||||
});
|
||||
---
|
||||
|
||||
<Layout
|
||||
title="Microblog"
|
||||
description="Short-format writing. Instead of using shitty social media."
|
||||
title='Microblog'
|
||||
description='Short-format writing. Instead of using shitty social media.'
|
||||
>
|
||||
<div class="prose prose-invert">
|
||||
<div class='prose prose-invert'>
|
||||
<h1>Microblog</h1>
|
||||
<p>Short-format writing.</p>
|
||||
<p>Instead of using shitty social media.</p>
|
||||
<ul class="mx-auto p-0 mt-10 flex flex-col gap-10 list-none">
|
||||
<ul class='mx-auto p-0 mt-10 flex flex-col gap-10 list-none'>
|
||||
{
|
||||
data.map((item: any) => (
|
||||
<li>
|
||||
|
@@ -1,51 +1,48 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import components from "@/components/mdx/wrapper";
|
||||
import formatDate from "@/utils/format-date";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
|
||||
interface Props {
|
||||
project: CollectionEntry<"portfolio">;
|
||||
}
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import components from '@/components/mdx/wrapper';
|
||||
import formatDate from '@/utils/format-date';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import { getCollection, getEntry, render } from 'astro:content';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const allProjects = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true,
|
||||
);
|
||||
const filterEnProjects = allProjects.map((project) => {
|
||||
const [lang, ...slug] = project.slug.split("/");
|
||||
const allProjects = await getCollection(
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const filterEnProjects = allProjects.map((project) => {
|
||||
const [lang, id] = project.id.split('/');
|
||||
|
||||
if (lang === "en")
|
||||
return {
|
||||
...project,
|
||||
slug: slug.toString(),
|
||||
};
|
||||
else null;
|
||||
});
|
||||
if (lang === 'en')
|
||||
return {
|
||||
...project,
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
|
||||
return filterEnProjects.map((project) => ({
|
||||
params: { slug: project?.slug },
|
||||
props: { project },
|
||||
}));
|
||||
return filterEnProjects.map((project) => ({
|
||||
params: { slug: project?.id },
|
||||
}));
|
||||
}
|
||||
|
||||
const { project } = Astro.props;
|
||||
const { Content } = await project.render();
|
||||
|
||||
const lang = getLangFromUrl(Astro.url);
|
||||
const { slug } = Astro.params;
|
||||
const project = await getEntry('portfolio', `${lang}/${slug}`)!;
|
||||
const { Content, remarkPluginFrontmatter: data } = await render(project);
|
||||
---
|
||||
|
||||
<Layout title={project.data.title} description={project.data.description}>
|
||||
<article class="prose prose-invert">
|
||||
<h1>{project.data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Posted: </strong>
|
||||
{project.data.date && formatDate(new Date(project.data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
<Layout
|
||||
title={data.title}
|
||||
description={data.description}
|
||||
>
|
||||
<article class='prose prose-invert'>
|
||||
<h1>{data.title}</h1>
|
||||
<Content components={{ ...components }} />
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Posted: </strong>
|
||||
{data.date && formatDate(new Date(data.date), lang)}
|
||||
</p>
|
||||
</article>
|
||||
</Layout>
|
||||
|
@@ -1,26 +1,26 @@
|
||||
---
|
||||
import PostItem from "@/components/post-item";
|
||||
import { getLangFromUrl } from "@/i18n/utils";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { sortContentByDate } from "@/utils/sorts";
|
||||
import { getCollection } from "astro:content";
|
||||
import PostItem from '@/components/post-item';
|
||||
import { getLangFromUrl } from '@/i18n/utils';
|
||||
import Layout from '@/layouts/Layout.astro';
|
||||
import { sortContentByDate } from '@/utils/sorts';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const pageData = {
|
||||
title: "Portfolio",
|
||||
description: "Check my projects.",
|
||||
title: 'Portfolio',
|
||||
description: 'Check my projects.',
|
||||
};
|
||||
|
||||
const allProjects = await getCollection(
|
||||
"portfolio",
|
||||
({ data }) => data.draft !== true,
|
||||
'portfolio',
|
||||
({ data }) => data.draft !== true
|
||||
);
|
||||
const allEnProjects = allProjects.map((project) => {
|
||||
const [lang, ...slug] = project.slug.split("/");
|
||||
const [lang, id] = project.id.split('/');
|
||||
|
||||
if (lang === "en")
|
||||
if (lang === 'en')
|
||||
return {
|
||||
...project,
|
||||
slug: slug.toString(),
|
||||
id: id.split('.')[0],
|
||||
};
|
||||
else null;
|
||||
});
|
||||
@@ -30,11 +30,11 @@ const lang = getLangFromUrl(Astro.url);
|
||||
---
|
||||
|
||||
<Layout {...pageData}>
|
||||
<section class="prose prose-invert">
|
||||
<section class='prose prose-invert'>
|
||||
<h1>{pageData.title}</h1>
|
||||
<p>{pageData.description}</p>
|
||||
</section>
|
||||
<ul class="mt-4 flex flex-col gap-4">
|
||||
<ul class='mt-4 flex flex-col gap-4'>
|
||||
{
|
||||
allEnProjects.map(
|
||||
(project) =>
|
||||
@@ -42,13 +42,13 @@ const lang = getLangFromUrl(Astro.url);
|
||||
<li>
|
||||
<PostItem
|
||||
lang={lang}
|
||||
type="portfolio"
|
||||
slug={project.slug}
|
||||
type='portfolio'
|
||||
id={project.id}
|
||||
date={project.data.date!}
|
||||
title={project.data.title!}
|
||||
/>
|
||||
</li>
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
const robotsTxt = `
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: ${new URL("sitemap-index.xml", import.meta.env.SITE).href}
|
||||
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}
|
||||
`.trim();
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
return new Response(robotsTxt, {
|
||||
headers: {
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
Reference in New Issue
Block a user