diff --git a/.github/ISSUE_TEMPLATE/new-gallery-post.md b/.github/ISSUE_TEMPLATE/new-gallery-post.md
new file mode 100644
index 0000000..b0650ef
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/new-gallery-post.md
@@ -0,0 +1,18 @@
+---
+name: New Gallery post
+about: Template with front matter for gallery posts
+title: ''
+labels: Gallery
+assignees: ''
+
+---
+
+*** swap these for dashes for front matter
+title: "post title"
+date: 2022-01-16 00:00:00
+description: "will show underneath title on list page"
+image: upload cover image here
+alt: image alt text
+***
+
+post content goes here, underneath
diff --git a/src/lib/components/MobileMenu.svelte b/src/lib/components/MobileMenu.svelte
index 352915e..04743d0 100644
--- a/src/lib/components/MobileMenu.svelte
+++ b/src/lib/components/MobileMenu.svelte
@@ -12,6 +12,41 @@
}, 300);
}
}
+
+ export const links = [
+ {
+ name: 'Home',
+ href: '/'
+ },
+ {
+ name: 'Posts',
+ href: '/blog'
+ },
+ {
+ name: 'Work',
+ href: '/work'
+ },
+ {
+ name: 'Gallery',
+ href: '/gallery'
+ },
+ {
+ name: 'Blogroll',
+ href: '/blogroll'
+ },
+ {
+ name: 'About',
+ href: '/about'
+ },
+ {
+ name: 'Github',
+ href: 'https://github.com/tjheffner'
+ },
+ {
+ name: 'Tweets',
+ href: 'https://twitter.com/foodpyramids'
+ },
+ ]
@@ -60,73 +95,22 @@
{#if isOpen}
{/if}
diff --git a/src/lib/content.js b/src/lib/content.js
index c79b9c3..1eb4a9d 100644
--- a/src/lib/content.js
+++ b/src/lib/content.js
@@ -1,6 +1,4 @@
-import { compile } from 'mdsvex';
import { dev } from '$app/environment';
-import grayMatter from 'gray-matter';
import fetch from 'node-fetch';
import {
GH_USER_REPO,
@@ -8,53 +6,41 @@ import {
GH_PUBLISHED_TAGS,
REPO_OWNER
} from './siteConfig';
-import { slugify, readingTime } from './utils'
+import { slugify, readingTime, baseIssueContent, formatContent } from './utils'
import parse from 'parse-link-header';
-import { remark } from 'remark';
-import remarkParse from 'remark-parse';
-import remarkStringify from 'remark-stringify';
-import rehypeStringify from 'rehype-stringify';
-import rehypeSlug from 'rehype-slug';
-import rehypeAutoLink from 'rehype-autolink-headings';
-
-const remarkPlugins = undefined;
-const rehypePlugins = [
- rehypeStringify,
- rehypeSlug,
- [
- rehypeAutoLink,
- {
- behavior: 'wrap',
- properties: { class: 'hover:text-yellow-100 no-underline' }
- }
- ]
-];
let allBlogposts = [];
-// let etag = null // todo - implmement etag header
+let allGalleries = [];
+let allPosts = [];
-export async function listContent() {
- // use a diff var so as to not have race conditions while fetching
- // TODO: make sure to handle this better when doing etags or cache restore
-
- /** @type {import('./types').ContentItem[]} */
- let _allBlogposts = [];
+/*
+ * Gets all github issues with a provided label.
+ *
+ * PAGETYPE: 'LABEL'
+ * Blog posts: 'Published'
+ * Gallery pages: 'Gallery'
+ */
+export async function listContentFromIssues(label) {
+ let allContentWithLabel = []
let next = null;
- let limit = 0; // just a failsafe against infinite loop - feel free to remove
+
const authheader = process.env.GH_TOKEN && {
Authorization: `token ${process.env.GH_TOKEN}`
};
+
let url =
`https://api.github.com/repos/${GH_USER_REPO}/issues?` +
new URLSearchParams({
state: 'all',
- labels: GH_PUBLISHED_TAGS.toString(),
+ labels: label,
per_page: '100',
});
+
// pull issues created by owner only if allowed author = repo owner
if (APPROVED_POSTERS_GH_USERNAME.length === 1 && APPROVED_POSTERS_GH_USERNAME[0] === REPO_OWNER) {
url += '&' + new URLSearchParams({ creator: REPO_OWNER });
}
+
do {
const res = await fetch(next?.url ?? url, {
headers: authheader
@@ -63,182 +49,81 @@ export async function listContent() {
const issues = await res.json();
if ('message' in issues && res.status > 400)
throw new Error(res.status + ' ' + res.statusText + '\n' + (issues && issues.message));
- issues.forEach(
- /** @param {import('./types').GithubIssue} issue */
- (issue) => {
- if (
- // labels check not needed anymore as we have set the labels param in github api
- // issue.labels.some((label) => GH_PUBLISHED_TAGS.includes(label.name)) &&
- APPROVED_POSTERS_GH_USERNAME.includes(issue.user.login)
- ) {
- _allBlogposts.push(parseIssue(issue));
- }
+
+ issues.forEach((issue) => {
+ if (APPROVED_POSTERS_GH_USERNAME.includes(issue.user.login)) {
+ allContentWithLabel.push(parseIssue(issue, label))
}
- );
+ });
const headers = parse(res.headers.get('Link'));
next = headers && headers.next;
- } while (next && limit++ < 1000); // just a failsafe against infinite loop - feel free to remove
- _allBlogposts.sort((a, b) => b.date.valueOf() - a.date.valueOf()); // use valueOf to make TS happy https://stackoverflow.com/a/60688789/1106414
- allBlogposts = _allBlogposts;
- return _allBlogposts;
+ } while (next)
+
+ allContentWithLabel.sort((a, b) => b.date.valueOf() - a.date.valueOf()); // use valueOf to make TS happy https://stackoverflow.com/a/60688789/1106414
+ return allContentWithLabel
}
+// searches the list of content returned and matches based on slug
export async function getContent(slug) {
- // get all blogposts if not already done - or in development
- if (dev || allBlogposts.length === 0) {
+ // get all posts if not already done - or in development
+ if (dev || allPosts.length === 0) {
console.log('loading allBlogposts');
- allBlogposts = await listContent();
+ allBlogposts = await listContentFromIssues('Published');
+ allGalleries = await listContentFromIssues('Gallery');
+ allPosts = [...allBlogposts, ...allGalleries];
console.log('loaded ' + allBlogposts.length + ' blogposts');
- if (!allBlogposts.length)
+ console.log('loaded ' + allGalleries.length + ' galleries');
+ console.log('loaded ' + allPosts.length + ' posts from issues');
+
+ if (!allPosts.length)
throw new Error(
- 'failed to load blogposts for some reason. check token' + process.env.GH_TOKEN
+ 'failed to load posts from github issues for some reason. check token' + process.env.GH_TOKEN
);
}
- if (!allBlogposts.length) throw new Error('no blogposts');
- // find the blogpost that matches this slug
- const blogpost = allBlogposts.find((post) => post.slug === slug);
- if (blogpost) {
- const blogbody = blogpost.content
- .replace(/\n{% youtube (.*?) %}/g, (_, x) => {
- // https://stackoverflow.com/a/27728417/1106414
- function youtube_parser(url) {
- var rx =
- /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/;
- return url.match(rx)[1];
- }
- const videoId = x.startsWith('https://') ? youtube_parser(x) : x;
- return ``;
- })
- .replace(/\n{% (tweet|twitter) (.*?) %}/g, (_, _2, x) => {
- const url = x.startsWith('https://twitter.com/') ? x : `https://twitter.com/x/status/${x}`;
- return `
-
-
- `;
- });
-
- // compile it with mdsvex
- const content = (
- await compile(blogbody, {
- remarkPlugins,
- // @ts-ignore
- rehypePlugins
- })
- ).code
- // https://github.com/pngwn/MDsveX/issues/392
- .replace(/>{@html ``}<\/pre>/g, '
');
-
- return { ...blogpost, content };
+ if (!allPosts.length) throw new Error('no posts');
+ // find the issue that matches this slug
+ const post = allPosts.find((p) => p.slug === slug);
+ if (post) {
+ const content = await formatContent(post.content);
+
+ return { ...post, content };
} else {
- throw new Error('Blogpost not found for slug: ' + slug);
+ throw new Error('Issue not found for slug: ' + slug);
}
}
-/**
- * @param {import('./types').GithubIssue} issue
- * @returns {import('./types').ContentItem}
- */
-function parseIssue(issue) {
- const src = issue.body;
- const { content, data } = grayMatter(src);
- let title = data.title ?? issue.title;
- let slug;
- if (data.slug) {
- slug = data.slug;
- } else {
- slug = slugify(title);
+// format github issue into object that page type expects.
+// work pages are loaded using localContent.js for .svx files, not github issues
+function parseIssue(issue, label) {
+ const base = baseIssueContent(issue);
+ const data = base.frontmatter;
+
+ let post;
+
+ switch (label) {
+ case 'Gallery':
+ post = {
+ type: 'gallery',
+ ...base,
+ alt: data.alt,
+ }
+ break;
+ case 'Published':
+ default:
+ let tags = [];
+ if (data.tags) tags = Array.isArray(data.tags) ? data.tags : [data.tags];
+ tags = tags.map((tag) => tag.toLowerCase());
+
+ post = {
+ type: 'blog',
+ ...base,
+ category: data.category?.toLowerCase() || 'note',
+ tags,
+ readingTime: readingTime(base.content),
+ }
+
+ break;
}
- let description = data.description ?? content.trim().split('\n')[0];
- // extract plain text from markdown
- description = remark()
- .use(remarkParse)
- .use(remarkStringify)
- .processSync(description)
- .toString();
- description = description.replace(/\n/g, ' ');
- // strip html
- description = description.replace(/<[^>]*>?/gm, '');
- // strip markdown
- description = description.replace(/[[\]]/gm, '');
- // strip markdown
- description = description.replace(/[[\]]/gm, '');
-
- // you may wish to use a truncation approach like this instead...
- // let description = (data.content.length > 300) ? data.content.slice(0, 300) + '...' : data.content
-
- /** @type {string[]} */
- let tags = [];
- if (data.tags) tags = Array.isArray(data.tags) ? data.tags : [data.tags];
- tags = tags.map((tag) => tag.toLowerCase());
- // console.log(slug, tags);
-
- return {
- type: 'blog', // futureproof in case you want to add other types of content
- issueNumber: issue.number,
- content,
- frontmatter: data,
- title,
- subtitle: data.subtitle,
- description,
- category: data.category?.toLowerCase() || 'blog',
- tags,
- image: data.image ?? data.cover_image,
- canonical: data.canonical, // for canonical URLs of something published elsewhere
- slug: slug.toString().toLowerCase(),
- date: new Date(data.date ?? issue.created_at),
- readingTime: readingTime(content),
- ghMetadata: {
- issueUrl: issue.html_url,
- commentsUrl: issue.comments_url,
- title: issue.title,
- created_at: issue.created_at,
- updated_at: issue.updated_at,
- reactions: issue.reactions
- }
- };
+
+ return post
}
diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts
index 08a30e8..aed263e 100644
--- a/src/lib/types.d.ts
+++ b/src/lib/types.d.ts
@@ -1,5 +1,4 @@
-export type ContentItem = {
- type: 'blog';
+export type BaseContentItem = {
content: string;
frontmatter: {
[key: string]: string;
@@ -7,15 +6,31 @@ export type ContentItem = {
title: string;
subtitle: string;
description: string;
- category: string;
- tags: string[];
- image: string;
canonical: string;
slug: string;
date: Date;
ghMetadata: GHMetadata;
+ image: string;
+}
+
+export type BlogItem = BaseContentItem & {
+ type: 'blog';
+ category: string;
+ tags: string[];
+ readingTime: string;
};
+export type GalleryItem = BaseContentItem & {
+ type: 'gallery';
+ images: GalleryImage[]
+}
+
+export type GalleryImage = {
+ src: string;
+ alt: string;
+ size: string;
+}
+
export type GHMetadata = {
issueUrl: string;
commentsUrl: string;
diff --git a/src/lib/utils.js b/src/lib/utils.js
index 18587f6..72e0c27 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -1,3 +1,25 @@
+import grayMatter from 'gray-matter';
+import { compile } from 'mdsvex';
+import { remark } from 'remark';
+import remarkParse from 'remark-parse';
+import remarkStringify from 'remark-stringify';
+import rehypeStringify from 'rehype-stringify';
+import rehypeSlug from 'rehype-slug';
+import rehypeAutoLink from 'rehype-autolink-headings';
+
+const remarkPlugins = undefined;
+const rehypePlugins = [
+ rehypeStringify,
+ rehypeSlug,
+ [
+ rehypeAutoLink,
+ {
+ behavior: 'wrap',
+ properties: { class: 'hover:text-yellow-100 no-underline' }
+ }
+ ]
+];
+
/**
* @param {string} text
* @returns {string}
@@ -22,3 +44,135 @@ export function slugify(text) {
.replace(/--+/g, '-') // Replace multiple hyphen with single hyphen
.replace(/(^-|-$)/g, ''); // Remove leading or trailing hyphen
}
+
+
+/**
+ * All pages built from github issue should contain this data at minimum
+ *
+ * @param {import('./types').GithubIssue} issue
+ * @returns {import('./types').BaseContentItem}
+ */
+export function baseIssueContent(issue) {
+ const src = issue.body;
+ const { content, data } = grayMatter(src);
+ let title = data.title ?? issue.title;
+ let slug;
+ if (data.slug) {
+ slug = data.slug;
+ } else {
+ slug = slugify(title);
+ }
+
+ let description = data.description ?? content.trim().split('\n')[0];
+ // extract plain text from markdown
+ description = remark()
+ .use(remarkParse)
+ .use(remarkStringify)
+ .processSync(description)
+ .toString();
+ description = description.replace(/\n/g, ' ');
+ // strip html
+ description = description.replace(/<[^>]*>?/gm, '');
+ // strip markdown
+ description = description.replace(/[[\]]/gm, '');
+ // strip markdown
+ description = description.replace(/[[\]]/gm, '');
+
+ return {
+ frontmatter: data,
+ issueNumber: issue.number,
+ slug: slug,
+ title,
+ description,
+ content,
+ image: data.image ?? data.cover_image,
+ date: new Date(data.date ?? issue.created_at),
+ ghMetadata: {
+ issueUrl: issue.html_url,
+ commentsUrl: issue.comments_url,
+ title: issue.title,
+ created_at: issue.created_at,
+ updated_at: issue.updated_at,
+ reactions: issue.reactions
+ }
+ }
+}
+
+export async function formatContent(content) {
+ const formatted = content
+ .replace(/\n{% youtube (.*?) %}/g, (_, x) => {
+ // https://stackoverflow.com/a/27728417/1106414
+ function youtube_parser(url) {
+ var rx =
+ /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|&v(?:i)?=))([^#&?]*).*/;
+ return url.match(rx)[1];
+ }
+ const videoId = x.startsWith('https://') ? youtube_parser(x) : x;
+ return ``;
+ })
+ .replace(/\n{% (tweet|twitter) (.*?) %}/g, (_, _2, x) => {
+ const url = x.startsWith('https://twitter.com/') ? x : `https://twitter.com/x/status/${x}`;
+ return `
+
+
+ `;
+ });
+
+ // compile it with mdsvex
+ const output = (
+ await compile(formatted, {
+ remarkPlugins,
+ // @ts-ignore
+ rehypePlugins
+ })
+ ).code
+ // https://github.com/pngwn/MDsveX/issues/392
+ .replace(/>{@html ``}<\/pre>/g, '
');
+
+ return output
+}
diff --git a/src/routes/(main)/+layout.svelte b/src/routes/(main)/+layout.svelte
index 9b6e865..eb75480 100644
--- a/src/routes/(main)/+layout.svelte
+++ b/src/routes/(main)/+layout.svelte
@@ -53,6 +53,7 @@
Posts
Work
+ Gallery
About
diff --git a/src/routes/(main)/+page.svelte b/src/routes/(main)/+page.svelte
index 3097909..059174c 100644
--- a/src/routes/(main)/+page.svelte
+++ b/src/routes/(main)/+page.svelte
@@ -30,14 +30,14 @@
- welcome to my page
+ welcome to my page 🌊
This site is perpetually under construction but coming along :)
- It's mostly a place for me to post recipes I like, with the occasional technical post or personal blog mixed in. Some work-related stuff can be found here too, but that's not what I want to talk about.
+
It's mostly a place for me to post recipes I like and travel photos , with the occasional technical post or personal blog mixed in. Some work-related stuff can be found here too, but that's not what I want to talk about .
Thanks for stopping by, check out the links that work. ✌️
diff --git a/src/routes/(main)/[slug]/+page.js b/src/routes/(main)/[slug]/+page.js
index d0b3ca5..6a4cda8 100644
--- a/src/routes/(main)/[slug]/+page.js
+++ b/src/routes/(main)/[slug]/+page.js
@@ -10,15 +10,22 @@ export async function load({ params, fetch, setHeaders }) {
}
let res = null;
- res = await fetch(`/api/blog/${slug}.json`);
+ res = await fetch(`/api/getContent/${slug}.json`);
if (res.status > 400) {
throw error(res.status, await res.text());
}
+ const json = await res.json()
+
+ // because [slug] is a catchall, it gets the gallery pages too. redirect them.
+ if (json.type === 'gallery') {
+ throw redirect(308, `/gallery/${json.slug}`)
+ }
+
setHeaders({
'Cache-Control': 'public, max-age=60'
});
return {
- json: await res.json(),
+ json,
slug,
REPO_URL
};
diff --git a/src/routes/(main)/about/+page.svx b/src/routes/(main)/about/+page.svx
index 40801ea..b03b512 100644
--- a/src/routes/(main)/about/+page.svx
+++ b/src/routes/(main)/about/+page.svx
@@ -13,7 +13,7 @@
about
## the site
-this site is the online home of tanner heffner. it's part [blog](/blog), [portfolio](/work), playground, [resume](/resume), and all [digital garden](https://joelhooks.com/digital-garden), using [swyxkit](https://swyxkit.netlify.app/about) as my starting point. [this post](/heffdotdev-technical-details) explains more about the tools i chose and why.
+this site is the online home of tanner heffner. it's part [blog](/blog), [portfolio](/work), [photo gallery](/gallery), [playground](/christmas), [resume](/resume), and all [digital garden](https://joelhooks.com/digital-garden), using [swyxkit](https://swyxkit.netlify.app/about) as my starting point. [this post](/heffdotdev-technical-details) explains more about the tools i chose and why.
you'll find technical snippets & longer blogs, cooking recipes, old projects both professional and personal, and potentially much more. we're just getting started watering this garden :)
@@ -21,27 +21,5 @@ you'll find technical snippets & longer blogs, cooking recipes, old projects bot
i do a lot of different stuff. when i'm not staring at screens for work and fun,
i like to get outside in the pnw.
-here's a list of my favorite disc golf courses around portland, or.
-if you're in the area you might catch me out here!
-
-
- Dabney
- Pier on an empty day
- Ft. Stevens
- Milo
- Blue Lake on a still day
- Stub Stewart
- Horning's
- Champoeg
- Timber
- McCormick
- Rooster Rock
- Pier on a busy day
- Leveritch
- ...
- Blue Lake on a windy day
- Trojan
- Eco Park
-
-
-i'm hoping to play Buxton soon!
+i'm interested in cooking, DIY, sustainability,
+gardening, disc golf, and a whole lot more.
diff --git a/src/routes/(main)/blog/+page.js b/src/routes/(main)/blog/+page.js
index f35f233..adbbc8b 100644
--- a/src/routes/(main)/blog/+page.js
+++ b/src/routes/(main)/blog/+page.js
@@ -2,8 +2,7 @@ import { error } from '@sveltejs/kit';
// export const prerender = true; // turned off so it refreshes quickly
export async function load({ setHeaders, fetch }) {
const res = await fetch(`/api/listContent.json`);
- // alternate strategy https://www.davidwparker.com/posts/how-to-make-an-rss-feed-in-sveltekit
- // Object.entries(import.meta.glob('./*.md')).map(async ([path, page]) => {
+
if (res.status > 400) {
throw error(res.status, await res.text())
}
diff --git a/src/routes/(main)/blogroll/+page.svx b/src/routes/(main)/blogroll/+page.svx
index ff92874..6972d06 100644
--- a/src/routes/(main)/blogroll/+page.svx
+++ b/src/routes/(main)/blogroll/+page.svx
@@ -8,8 +8,10 @@
blogroll
-- [randi bolt](https://www.rbolt.me/)
+here's some other cool blogs
+
- [nug doug](https://darkdell.net)
+- [mike crittenden](https://critter.blog)
- [defector](https://defector.com)
-- [bright side](https://www.brightsideofthesun.com/)
+- [bright side of the sun](https://www.brightsideofthesun.com/)
- [fujichia](https://www.fujichia.com/)
diff --git a/src/routes/(main)/gallery/+page.js b/src/routes/(main)/gallery/+page.js
new file mode 100644
index 0000000..f72efef
--- /dev/null
+++ b/src/routes/(main)/gallery/+page.js
@@ -0,0 +1,16 @@
+import { error } from '@sveltejs/kit';
+// export const prerender = true; // turned off so it refreshes quickly
+export async function load({ setHeaders, fetch }) {
+ const res = await fetch(`/api/listGallery.json`);
+
+ if (res.status > 400) {
+ throw error(res.status, await res.text())
+ }
+
+ /** @type {import('$lib/types').GalleryItem[]} */
+ const items = await res.json();
+ setHeaders({
+ 'Cache-Control': 'public, max-age=60' // 1 minute
+ })
+ return {items}
+}
diff --git a/src/routes/(main)/gallery/+page.svelte b/src/routes/(main)/gallery/+page.svelte
new file mode 100644
index 0000000..d7ef233
--- /dev/null
+++ b/src/routes/(main)/gallery/+page.svelte
@@ -0,0 +1,32 @@
+
+
+
+ heffner.dev | adventures
+
+
+
+
+
+
+
+
+ Gallery
+
+ details, photos, ephemera from past adventures
+
+
+
+ {#each items as trip}
+
+
+ {trip.description}
+
+
+ {/each}
+
+
diff --git a/src/routes/(main)/gallery/[slug]/+page.js b/src/routes/(main)/gallery/[slug]/+page.js
new file mode 100644
index 0000000..e68b476
--- /dev/null
+++ b/src/routes/(main)/gallery/[slug]/+page.js
@@ -0,0 +1,21 @@
+import { error, redirect } from '@sveltejs/kit';
+import { REPO_URL } from '$lib/siteConfig';
+
+export const csr = true; // https://github.com/sveltejs/kit/pull/6446
+export async function load({ params, url, fetch, setHeaders }) {
+ const slug = params.slug;
+
+ let res = null;
+ res = await fetch(`/api/gallery/${slug}.json`);
+ if (res.status > 400) {
+ throw error(res.status, await res.text());
+ }
+ setHeaders({
+ 'Cache-Control': 'public, max-age=60'
+ });
+ return {
+ json: await res.json(),
+ slug,
+ REPO_URL
+ };
+}
diff --git a/src/routes/(main)/gallery/[slug]/+page.svelte b/src/routes/(main)/gallery/[slug]/+page.svelte
new file mode 100644
index 0000000..4f66430
--- /dev/null
+++ b/src/routes/(main)/gallery/[slug]/+page.svelte
@@ -0,0 +1,86 @@
+
+
+
+ {json.title}
+
+
+
+
+
+
+
+
+
+
+
+ {#if json.image}
+
+
+
+ {:else}
+
+
+
+ {/if}
+
+
+ Back
+
+
+
+ {json.title}
+
+
+
+
+
+
+
+
+ {@html json.content}
+
+
+
+
diff --git a/src/routes/(main)/resume/+page.svelte b/src/routes/(main)/resume/+page.svelte
index 001ebcd..577f687 100644
--- a/src/routes/(main)/resume/+page.svelte
+++ b/src/routes/(main)/resume/+page.svelte
@@ -1,5 +1,4 @@
diff --git a/src/routes/api/blog/[slug].json/+server.js b/src/routes/api/gallery/[slug].json/+server.js
similarity index 100%
rename from src/routes/api/blog/[slug].json/+server.js
rename to src/routes/api/gallery/[slug].json/+server.js
diff --git a/src/routes/api/getContent/[slug].json/+server.js b/src/routes/api/getContent/[slug].json/+server.js
new file mode 100644
index 0000000..b071857
--- /dev/null
+++ b/src/routes/api/getContent/[slug].json/+server.js
@@ -0,0 +1,20 @@
+import { getContent } from '$lib/content';
+import { error } from '@sveltejs/kit';
+
+/**
+ * @type {import('@sveltejs/kit').RequestHandler}
+ */
+export async function GET({ params }) {
+ const { slug } = params;
+ let data;
+ try {
+ data = await getContent(slug);
+ return new Response(JSON.stringify(data), {
+ headers: {
+ 'Cache-Control': `max-age=0, s-maxage=${60}` // 1 minute.. for now
+ }
+ });
+ } catch (err) {
+ throw error(404, err.message);
+ }
+}
diff --git a/src/routes/api/listContent.json/+server.js b/src/routes/api/listContent.json/+server.js
index 38b4d66..f855d3f 100644
--- a/src/routes/api/listContent.json/+server.js
+++ b/src/routes/api/listContent.json/+server.js
@@ -1,11 +1,10 @@
-// import { json } from '@sveltejs/kit';
-import { listContent } from '$lib/content';
+import { listContentFromIssues } from '$lib/content';
/**
* @type {import('./$types').RequestHandler}
*/
export async function GET({ setHeaders }) {
- const list = await listContent();
+ const list = await listContentFromIssues('Published');
setHeaders({
'Cache-Control': `max-age=0, s-maxage=${60}` // 1 minute.. for now
});
diff --git a/src/routes/api/listGallery.json/+server.js b/src/routes/api/listGallery.json/+server.js
new file mode 100644
index 0000000..5d99523
--- /dev/null
+++ b/src/routes/api/listGallery.json/+server.js
@@ -0,0 +1,44 @@
+import { listContentFromIssues } from '$lib/content';
+
+/**
+ * @type {import('./$types').RequestHandler}
+ */
+export async function GET({ setHeaders }) {
+ const list = await listContentFromIssues('Gallery');
+
+ // const list = [
+ // {
+ // name: 'Japan',
+ // date: 'April 2023',
+ // description: 'traveled all over the country for a month with randi',
+ // slug: '/gallery/japan',
+ // image: 'http://placekitten.com/400/400',
+ // alt: 'japan alt'
+ // },
+ // {
+ // name: 'Morocco',
+ // date: 'September 2018',
+ // description: 'visited my sister during her peace corps mission',
+ // slug: '/gallery/morocco',
+ // image: 'http://placekitten.com/400/400',
+ // alt: 'morocco alt'
+ // },
+ // {
+ // name: 'Costa Rica',
+ // date: 'December 2017',
+ // description: 'two weeks of desayuno tipica with the boys',
+ // slug: '/gallery/costa-rica',
+ // image: 'http://placekitten.com/400/400',
+ // alt: 'cr alt'
+ // },
+ // ];
+
+ setHeaders({
+ 'Cache-Control': `max-age=0, s-maxage=${60}` // 1 minute.. for now
+ });
+ return new Response(JSON.stringify(list), {
+ headers: {
+ 'content-type': 'application/json; charset=utf-8'
+ }
+ });
+}
diff --git a/src/routes/rss.xml/+server.js b/src/routes/rss.xml/+server.js
index 05aa0e6..aa9c818 100644
--- a/src/routes/rss.xml/+server.js
+++ b/src/routes/rss.xml/+server.js
@@ -2,7 +2,7 @@ import RSS from 'rss';
import { SITE_TITLE, SITE_URL } from '$lib/siteConfig';
import { remark } from 'remark';
import remarkHTML from 'remark-html';
-import { listContent } from '$lib/content';
+import { listContentFromIssues } from '$lib/content';
// Reference: https://github.com/sveltejs/kit/blob/master/examples/hn.svelte.dev/src/routes/%5Blist%5D/rss.js
/** @type {import('@sveltejs/kit').RequestHandler} */
@@ -13,7 +13,7 @@ export async function GET({ fetch }) {
feed_url: SITE_URL + '/rss.xml'
});
- const allBlogs = await listContent(fetch);
+ const allBlogs = await listContentFromIssues('Published');
allBlogs.forEach((post) => {
// extract HTML from markdown
const htmlDescription = remark()
diff --git a/src/routes/sitemap.xml/+server.js b/src/routes/sitemap.xml/+server.js
index d289513..0ee4b56 100644
--- a/src/routes/sitemap.xml/+server.js
+++ b/src/routes/sitemap.xml/+server.js
@@ -1,12 +1,13 @@
import { SITE_URL } from '$lib/siteConfig';
-import { listContent } from '$lib/content';
+import { listContentFromIssues } from '$lib/content';
import { fetchMarkdownPosts } from '$lib/localContent'
/** @type {import('@sveltejs/kit').RequestHandler} */
export async function GET({ fetch }) {
- const posts = await listContent(fetch);
+ const posts = await listContentFromIssues('Published');
+ const galleries = await listContentFromIssues('Gallery');
const projects = await fetchMarkdownPosts()
- const pages = [`about`, 'resume', 'blogroll', 'christmas', 'blog', 'work'];
+ const pages = ['about', 'resume', 'blogroll', 'christmas', 'blog', 'work'];
const body = sitemap(posts, projects, pages);
return new Response(body, {
@@ -47,6 +48,17 @@ const sitemap = (posts, projects, pages) => `
`
).join('')}
+ ${galleries
+ .map((gallery) =>
+ post.isPrivate
+ ? null
+ : `
+
+ ${SITE_URL}/gallery/${gallery.slug}
+ ${gallery.ghMetadata.updated_at ? gallery.ghMetadata.updated_at.substring(0, 10) : gallery.ghMetadata.created_at.substring(0, 10)}
+
+ `
+ ).join('')}
${projects
.map((project) =>
project.isPrivate