Merge pull request 'upstream' (#1) from upstream into main
Some checks failed
Build and Deploy docker container / build (push) Failing after 2m25s

Reviewed-on: #1
This commit is contained in:
jblu 2024-11-04 22:35:56 -06:00
commit ee6e9789bb
24 changed files with 1019 additions and 746 deletions

33
.env.example Normal file
View File

@ -0,0 +1,33 @@
# visit https://giscus.app to get your Giscus ids
NEXT_PUBLIC_GISCUS_REPO=
NEXT_PUBLIC_GISCUS_REPOSITORY_ID=
NEXT_PUBLIC_GISCUS_CATEGORY=
NEXT_PUBLIC_GISCUS_CATEGORY_ID=
NEXT_PUBLIC_UTTERANCES_REPO=
NEXT_PUBLIC_DISQUS_SHORTNAME=
MAILCHIMP_API_KEY=
MAILCHIMP_API_SERVER=
MAILCHIMP_AUDIENCE_ID=
BUTTONDOWN_API_KEY=
CONVERTKIT_API_KEY=
# curl https://api.convertkit.com/v3/forms?api_key=<your_public_api_key> to get your form ID
CONVERTKIT_FORM_ID=
KLAVIYO_API_KEY=
KLAVIYO_LIST_ID=
REVUE_API_KEY=
# Create EmailOctopus API key at https://emailoctopus.com/api-documentation
EMAILOCTOPUS_API_KEY=
# List ID can be found in the URL as a UUID after clicking a list on https://emailoctopus.com/lists
# or the settings page of your list https://emailoctopus.com/lists/{UUID}/settings
EMAILOCTOPUS_LIST_ID=
# Create Beehive API key at https://developers.beehiiv.com/docs/v2/bktd9a7mxo67n-create-an-api-key
BEEHIVE_API_KEY=
BEEHIVE_PUBLICATION_ID=

View File

@ -44,8 +44,8 @@ Internationalization support - [Template with i18n](https://tailwind-nextjs-star
- [thetalhatahir.com](https://www.thetalhatahir.com) - Talha Tahir's personal blog. Added article thumbnails, linkedIn card, Beautiful hero content, technology emoticons.
- [homing.so](https://homing.so) - Homing's personal blog about the stuff he's learning ([source code](https://github.com/hominsu/blog))
- [zS1m's Blog](https://contrails.space) - zS1m's personal blog for recording and sharing daily learning technical content ([source code](https://github.com/zS1m/nextjs-contrails))
- [dariuszwozniak.net](https://dariuszwozniak.net/) - Software development blog
- [Terminals.run](https://terminals.run) - Blog site for some thoughts and records for life and technology.
- [dariuszwozniak.net](https://dariuszwozniak.net/) - Software development blog ([source code](https://github.com/dariusz-wozniak/dariuszwozniak.net-v2))
- [dreams.plus](https://dreams.plus) - Blog site for some thoughts and records for life and technology.
- [francisaguilar.co blog](https://francisaguilar.co) - Francis Aguilar's personal blog that talks about tech, fitness, and personal development.
- [Min71 Dev Blog](https://min71.dev) - Personal blog about Blockchain, Development and etc. ([source code](https://github.com/mingi3442/blog))
- [Bryce Yu's Blog](https://earayu.github.io/) - Bryce Yu's personal Blog about distributed system, database, and web development. ([source code](https://github.com/earayu/earayu.github.io))
@ -70,6 +70,9 @@ Internationalization support - [Template with i18n](https://tailwind-nextjs-star
- [LyricsDecode.com](https://lyricsdecode.com) - A song lyrics website offering original lyrics, Romanisation, and English translations with customisable viewing options.
- [bmacharia.com](https://bmacharia.com/) - Benson Macharia's technical blog about Cybersecurity and IT Risk Management.
- [armujahid.me](https://armujahid.me/) - Abdul Rauf's personal blog about tech and random stuff.
- [leohuynh.dev](leohuynh.dev) - 🇻🇳 Leo's dev blog stories, insights, and ideas. Add `/snippets`, `/books` pages, add `ProfileCard`, `CareerTimeline` components and many more.
- [OpenSats Blog](https://opensats.org/blog) - A 501(c)(3) public charity which aims to sustainably fund free and open-source projects. ([source code](https://github.com/OpenSats/website))
- [Schedles Blog](https://schedles.com/blog) - Social media scheduling tips, strategies, and product updates for content creators. ([Project Link](https://schedles.com))
Using the template? Feel free to create a PR and add your blog to this list.
@ -82,7 +85,6 @@ Thanks to the community of users and contributors to the template! We are no lon
- [Aloisdg's cookbook](https://tambouille.vercel.app/) - with pictures and recipes!
- [GautierArcin's demo with next translate](https://tailwind-nextjs-starter-blog-seven.vercel.app/) - includes translation of mdx posts, [source code](https://github.com/GautierArcin/tailwind-nextjs-starter-blog/tree/demo/next-translate)
- [David Levai's digital garden](https://davidlevai.com/) - customized design and added email subscriptions
- [Leo's Blog](https://leohuynh.dev) - Tuan Anh Huynh's personal site. Add Snippets Page, Author Profile Card, Image Lightbox, ...
- [thvu.dev](https://thvu.dev) - Added `mdx-embed`, view count, reading minutes and more.
- [irvin.dev](https://www.irvin.dev/) - Irvin Lin's personal site. Added YouTube embedding.
- [KirillSo.com](https://www.kirillso.com/) - Personal blog & website.

View File

@ -1,6 +1,8 @@
import { NewsletterAPI } from 'pliny/newsletter'
import siteMetadata from '@/data/siteMetadata'
export const dynamic = 'force-static'
const handler = NewsletterAPI({
// @ts-ignore
provider: siteMetadata.newsletter.provider,

View File

@ -21,11 +21,10 @@ const layouts = {
PostBanner,
}
export async function generateMetadata({
params,
}: {
params: { slug: string[] }
export async function generateMetadata(props: {
params: Promise<{ slug: string[] }>
}): Promise<Metadata | undefined> {
const params = await props.params
const slug = decodeURI(params.slug.join('/'))
const post = allBlogs.find((p) => p.slug === slug)
const authorList = post?.authors || ['default']
@ -78,7 +77,8 @@ export const generateStaticParams = async () => {
return allBlogs.map((p) => ({ slug: p.slug.split('/').map((name) => decodeURI(name)) }))
}
export default async function Page({ params }: { params: { slug: string[] } }) {
export default async function Page(props: { params: Promise<{ slug: string[] }> }) {
const params = await props.params
const slug = decodeURI(params.slug.join('/'))
// Filter out drafts in production
const sortedCoreContents = allCoreContent(sortPosts(allBlogs))

View File

@ -11,7 +11,8 @@ export const generateStaticParams = async () => {
return paths
}
export default function Page({ params }: { params: { page: string } }) {
export default async function Page(props: { params: Promise<{ page: string }> }) {
const params = await props.params
const posts = allCoreContent(sortPosts(allBlogs))
const pageNumber = parseInt(params.page as string)
const initialDisplayPosts = posts.slice(

View File

@ -1,6 +1,8 @@
import { MetadataRoute } from 'next'
import siteMetadata from '@/data/siteMetadata'
export const dynamic = 'force-static'
export default function robots(): MetadataRoute.Robots {
return {
rules: {

View File

@ -2,6 +2,8 @@ import { MetadataRoute } from 'next'
import { allBlogs } from 'contentlayer/generated'
import siteMetadata from '@/data/siteMetadata'
export const dynamic = 'force-static'
export default function sitemap(): MetadataRoute.Sitemap {
const siteUrl = siteMetadata.siteUrl

View File

@ -1 +1 @@
{"python":1,"projects":1,"code":1,"cygnus":1,"self-hosted":1,"server":1}
{"python":1,"projects":1,"code":1,"cygnus":1,"self-hosted":1,"server":1}

View File

@ -8,7 +8,10 @@ import { genPageMetadata } from 'app/seo'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
export async function generateMetadata({ params }: { params: { tag: string } }): Promise<Metadata> {
export async function generateMetadata(props: {
params: Promise<{ tag: string }>
}): Promise<Metadata> {
const params = await props.params
const tag = decodeURI(params.tag)
return genPageMetadata({
title: tag,
@ -31,7 +34,8 @@ export const generateStaticParams = async () => {
return paths
}
export default function TagPage({ params }: { params: { tag: string } }) {
export default async function TagPage(props: { params: Promise<{ tag: string }> }) {
const params = await props.params
const tag = decodeURI(params.tag)
// Capitalize first letter and convert space to dash
const title = tag[0].toUpperCase() + tag.split(' ').join('-').slice(1)

View File

@ -16,6 +16,7 @@ export default function Footer() {
<SocialIcon kind="x" href={siteMetadata.x} size={6} />
<SocialIcon kind="instagram" href={siteMetadata.instagram} size={6} />
<SocialIcon kind="threads" href={siteMetadata.threads} size={6} />
<SocialIcon kind="medium" href={siteMetadata.medium} size={6} />
</div>
<div className="mb-2 flex space-x-2 text-sm text-gray-500 dark:text-gray-400">
<div>{siteMetadata.author}</div>

View File

@ -28,10 +28,8 @@ const Header = () => {
)}
</div>
</Link>
{/* Changed this so I can get all of my links in the header 10-19-2024. Backup Config */}
<div className="flex items-center space-x-4 leading-5 sm:space-x-6">
<div className="no-scrollbar max-w-50 hidden items-center space-x-4 overflow-x-auto sm:flex md:flex lg:flex">
{/* <div className="no-scrollbar hidden max-w-50 items-center space-x-4 overflow-x-auto sm:flex sm:space-x-6 md:max-w-72 lg:max-w-96"> */}
<div className="no-scrollbar hidden max-w-40 items-center space-x-4 overflow-x-auto sm:flex sm:space-x-6 md:max-w-72 lg:max-w-96">
{headerNavLinks
.filter((link) => link.href !== '/')
.map((link) => (

View File

@ -93,3 +93,12 @@ export function Instagram(svgProps: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function Medium(svgProps: SVGProps<SVGSVGElement>) {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...svgProps}>
<title>Medium</title>
<path d="M13.54 12a6.8 6.8 0 01-6.77 6.82A6.8 6.8 0 010 12a6.8 6.8 0 016.77-6.82A6.8 6.8 0 0113.54 12zM20.96 12c0 3.54-1.51 6.42-3.38 6.42-1.87 0-3.39-2.88-3.39-6.42s1.52-6.42 3.39-6.42 3.38 2.88 3.38 6.42M24 12c0 3.17-.53 5.75-1.19 5.75-.66 0-1.19-2.58-1.19-5.75s.53-5.75 1.19-5.75C23.47 6.25 24 8.83 24 12z" />
</svg>
)
}

View File

@ -9,6 +9,7 @@ import {
Mastodon,
Threads,
Instagram,
Medium,
} from './icons'
const components = {
@ -22,6 +23,7 @@ const components = {
mastodon: Mastodon,
threads: Threads,
instagram: Instagram,
medium: Medium,
}
type SocialIconProps = {

View File

@ -18,6 +18,7 @@ import {
import rehypeSlug from 'rehype-slug'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypeKatex from 'rehype-katex'
import rehypeKatexNoTranslate from 'rehype-katex-notranslate'
import rehypeCitation from 'rehype-citation'
import rehypePrismPlus from 'rehype-prism-plus'
import rehypePresetMinify from 'rehype-preset-minify'
@ -169,6 +170,7 @@ export default makeSource({
},
],
rehypeKatex,
rehypeKatexNoTranslate,
[rehypeCitation, { path: path.join(root, 'data') }],
[rehypePrismPlus, { defaultLanguage: 'js', ignoreMissing: true }],
rehypePresetMinify,

View File

@ -80,7 +80,7 @@
}
.token.boolean {
color: rgb(138, 21, 40);
color: rgb(255, 88, 116);
}
.token.number {

View File

@ -13,4 +13,4 @@ Jonathan Branan is a Software Engineer at Fortra. He is mostly self-taught howev
He has worked for Geek Squad repairing computers, GlobalScape as a Lead of the Client Services department and as a Product Owner of MFT applications at Fortra.
He currently lives in San Antonio, Texas with his wife and two dogs. He enjoys Basketball, Video games, cooking, camping(backpacking, glamping), watching movies,
making mixed drinks and building Lego's. Jonathan and his wife like to frequently travel.
making mixed drinks and building Lego's. Jonathan and his wife like to frequently travel.

View File

@ -20,6 +20,7 @@ const siteMetadata = {
linkedin: 'https://www.linkedin.com/in/jonathanbranan/',
// threads: 'https://www.threads.net',
// instagram: 'https://www.instagram.com',
// medium: 'https://medium.com',
locale: 'en-US',
// set to true if you want a navbar fixed to the top
stickyNav: false,

View File

@ -66,7 +66,7 @@ function createSearchIndex(allBlogs) {
) {
writeFileSync(
`public/${siteMetadata.search.kbarConfig.searchDocumentsPath}`,
JSON.stringify((sortPosts(allBlogs)))
JSON.stringify(sortPosts(allBlogs))
)
console.log('Local search index generated...')
}

20
faq/deploy-with-docker.md Normal file
View File

@ -0,0 +1,20 @@
# Deploy with Docker
Follow the [official Next.js repo docker build example and instructions](https://github.com/vercel/next.js/tree/canary/examples/with-docker) to deploy with docker. Copy the [`Dockerfile`](https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile) into the root of the project and modify the `next.config.js` file:
```js
// next.config.js
module.exports = {
// ... rest of the configuration.
output: 'standalone',
}
```
You can now build the docker image and run it:
```bash
docker build -t nextjs-docker .
docker run -p 3000:3000 nextjs-docker
```
Alternatively, to use docker compose, refer to the [docker compose repo](https://github.com/vercel/next.js/tree/canary/examples/with-docker-compose).

View File

@ -100,7 +100,7 @@ export default function PostLayout({ content, authorDetails, next, prev, childre
Discuss on Twitter
</Link>
{``}
<Link href={editUrl(filePath)}>View on gitea</Link>
<Link href={editUrl(filePath)}>View on Gitea</Link>
</div>
{siteMetadata.comments && (
<div

View File

@ -1,6 +1,6 @@
{
"name": "tailwind-nextjs-starter-blog",
"version": "2.2.0",
"version": "2.3.0",
"private": true,
"scripts": {
"start": "next dev",
@ -12,29 +12,30 @@
"prepare": "husky"
},
"dependencies": {
"@headlessui/react": "1.7.19",
"@next/bundle-analyzer": "14.2.3",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.12",
"@headlessui/react": "2.2.0",
"@next/bundle-analyzer": "15.0.2",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"autoprefixer": "^10.4.13",
"body-scroll-lock": "^4.0.0-beta.0",
"contentlayer2": "0.5.1",
"contentlayer2": "0.5.3",
"esbuild": "0.20.2",
"github-slugger": "^2.0.0",
"gray-matter": "^4.0.2",
"hast-util-from-html-isomorphic": "^2.0.0",
"image-size": "1.0.0",
"next": "14.2.3",
"next-contentlayer2": "0.5.1",
"next": "15.0.2",
"next-contentlayer2": "0.5.3",
"next-themes": "^0.3.0",
"pliny": "0.2.1",
"pliny": "0.4.0",
"postcss": "^8.4.24",
"react": "18.3.1",
"react-dom": "18.3.1",
"react": "rc",
"react-dom": "rc",
"reading-time": "1.5.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-citation": "^2.0.0",
"rehype-katex": "^7.0.0",
"rehype-katex-notranslate": "^1.1.4",
"rehype-preset-minify": "7.0.0",
"rehype-prism-plus": "^2.0.0",
"rehype-slug": "^6.0.0",
@ -42,20 +43,20 @@
"remark-gfm": "^4.0.0",
"remark-github-blockquote-alert": "^1.2.1",
"remark-math": "^6.0.0",
"tailwindcss": "^3.4.3",
"tailwindcss": "^3.4.14",
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@svgr/webpack": "^8.0.1",
"@types/mdx": "^2.0.12",
"@types/react": "^18.2.73",
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@typescript-eslint/eslint-plugin": "^8.12.0",
"@typescript-eslint/parser": "^8.12.0",
"cross-env": "^7.0.3",
"eslint": "^8.45.0",
"eslint-config-next": "14.2.3",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint": "^9.14.0",
"eslint-config-next": "15.0.2",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.0",
"husky": "^9.0.0",
"lint-staged": "^13.0.0",
"prettier": "^3.0.0",

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#000000</TileColor>
</tile>
</msapplication>
</browserconfig>

View File

@ -3,10 +3,12 @@ import path from 'path'
import { slug } from 'github-slugger'
import { escape } from 'pliny/utils/htmlEscaper.js'
import siteMetadata from '../data/siteMetadata.js'
import tagData from '../app/tag-data.json' with { type: 'json' }
import tagData from '../app/tag-data.json' assert { type: 'json' }
import { allBlogs } from '../.contentlayer/generated/index.mjs'
import { sortPosts } from 'pliny/utils/contentlayer.js'
const outputFolder = process.env.EXPORT ? 'out' : 'public'
const generateRssItem = (config, post) => `
<item>
<guid>${config.siteUrl}/blog/${post.slug}</guid>
@ -40,14 +42,14 @@ async function generateRSS(config, allBlogs, page = 'feed.xml') {
// RSS for blog post
if (publishPosts.length > 0) {
const rss = generateRss(config, sortPosts(publishPosts))
writeFileSync(`./public/${page}`, rss)
writeFileSync(`./${outputFolder}/${page}`, rss)
}
if (publishPosts.length > 0) {
for (const tag of Object.keys(tagData)) {
const filteredPosts = allBlogs.filter((post) => post.tags.map((t) => slug(t)).includes(tag))
const rss = generateRss(config, filteredPosts, `tags/${tag}/${page}`)
const rssPath = path.join('public', 'tags', tag)
const rssPath = path.join(outputFolder, 'tags', tag)
mkdirSync(rssPath, { recursive: true })
writeFileSync(path.join(rssPath, page), rss)
}

1594
yarn.lock

File diff suppressed because it is too large Load Diff