7 Commits
next15 ... main

Author SHA1 Message Date
cf0579023e wip: test versions of pnpm and nodejs 2023-10-22 15:24:59 +11:00
6d3dab582a wip: move workstation 2023-10-21 10:17:10 +11:00
08db8ef124 updated font reference and clean up 2023-09-28 20:54:59 +10:00
fca3d8bcec initialized structure 2023-09-26 19:48:47 +10:00
7e6a2b0300 upper body finished 2023-09-26 18:29:37 +10:00
2dbf116ddf upper body finished 2023-09-25 22:17:46 +10:00
bfed20c1c1 migrated 2023-09-25 20:43:46 +10:00
40 changed files with 2892 additions and 2892 deletions

View File

@@ -1,3 +1,3 @@
{ {
"extends": ["next/core-web-vitals", "next/typescript"] "extends": "next/core-web-vitals"
} }

9
.gitignore vendored
View File

@@ -3,12 +3,7 @@
# dependencies # dependencies
/node_modules /node_modules
/.pnp /.pnp
.pnp.* .pnp.js
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing # testing
/coverage /coverage
@@ -29,7 +24,7 @@ npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
# env files (can opt-in for committing if needed) # local env files
.env* .env*
# vercel # vercel

5
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/nextts.iml" filepath="$PROJECT_DIR$/.idea/nextts.iml" />
</modules>
</component>
</project>

12
.idea/nextts.iml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

7
.idea/prettier.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="MANUAL" />
<option name="myRunOnReformat" value="true" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -1,4 +1,4 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started ## Getting Started
@@ -18,7 +18,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More ## Learn More
@@ -27,10 +27,10 @@ To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel ## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

7
app/about/page.tsx Normal file
View File

@@ -0,0 +1,7 @@
export default function About() {
return(
<div className={`flex flex-col`}>
<p>About</p>
</div>
)
}

7
app/blog/page.tsx Normal file
View File

@@ -0,0 +1,7 @@
export default function Blog() {
return(
<div className={`flex flex-col`}>
<p>Blog Post List</p>
</div>
)
}

21
app/dbcheck/page.tsx Normal file
View File

@@ -0,0 +1,21 @@
import { promisePool } from "@/backend/db";
import {RowDataPacket} from "mysql2";
async function query() {
try {
const [rows, fields] = await promisePool.query<RowDataPacket[]>('select slug from post limit 1;')
return(rows[0]['slug'] as string)
} catch (e) {
console.log(e)
return('something went wrong')
}
}
export default async function DbCheck() {
return(
<div className={`flex flex-col`}>
<p>Env: { process.env.MYSQL_HOST }</p>
<p>Result: { await query() }</p>
</div>
)
}

23
app/fonts.ts Normal file
View File

@@ -0,0 +1,23 @@
import { Raleway, Syne, Questrial, Nunito_Sans } from "next/font/google";
export const raleway = Raleway({
subsets: ['latin'],
display: "swap",
})
export const syne = Syne({
subsets: ['latin'],
display: "swap",
})
export const questrial = Questrial({
subsets: ['latin'],
display: "swap",
weight: ['400'],
})
export const nunito_sans = Nunito_Sans({
subsets: ['latin'],
display: "swap",
}
)

Binary file not shown.

Binary file not shown.

View File

@@ -1,21 +1,3 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}

27
app/globals.css.orig Normal file
View File

@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}

View File

@@ -1,17 +1,11 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css"; import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import BlogHeader from "@/components/blogHeader";
import BlogFooter from "@/components/blogFooter";
import React from "react";
const geistSans = localFont({ const inter = Inter({ subsets: ["latin"] });
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Create Next App", title: "Create Next App",
@@ -20,15 +14,17 @@ export const metadata: Metadata = {
export default function RootLayout({ export default function RootLayout({
children, children,
}: Readonly<{ }: {
children: React.ReactNode; children: React.ReactNode;
}>) { }) {
return ( return (
<html lang="en"> <html lang="en">
<body <body className={inter.className}>
className={`${geistSans.variable} ${geistMono.variable} antialiased`} <div className={`flex flex-col bg-white`}>
> <BlogHeader />
{children} {children}
<BlogFooter />
</div>
</body> </body>
</html> </html>
); );

View File

@@ -1,101 +1,69 @@
import Image from "next/image"; import Image from "next/image";
import Link from "next/link";
import { raleway, syne, questrial } from "@/app/fonts";
export default function Home() { export default function Home() {
return ( return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <div className={`flex flex-col`}>
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start"> <div className={`bg-cover bg-center flex flex-col-reverse bg-blog-cover`}>
<Image <div className={`bg-neutral-100 bg-opacity-30 flex flex-col py-10`}>
className="dark:invert" <p className={`${raleway.className} text-white text-center text-7xl font-thin mb-6`}>
src="/next.svg" SUYONO
alt="Next.js logo" </p>
width={180} <p className={`${raleway.className} text-white text-center font-thin text-xl mb-10`}>
height={38} A Tech Archive
priority </p>
/> </div>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]"> <div className={`h-64`}></div> {/* spacer */}
<li className="mb-2"> <div className={`h-64`}></div> {/* spacer */}
Get started by editing{" "} </div>
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold"> <div className={`flex flex-row justify-center my-8`}>
app/page.tsx <div className={`border border-slate-100 flex flex-col`}>
</code> <Link
. href="/post/nginx-ssl-client-certificate-verification-manage-access-to-a-site"
</li> className={`flex flex-row max-w-4xl items-center`}
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
> >
<Image <Image
className="dark:invert" src="/assets/pthumb.webp"
src="/vercel.svg" alt="post thumbnail"
alt="Vercel logomark" width={454}
width={20} height={341}
height={20}
/> />
Deploy now <div className={`flex flex-col mx-10`}>
</a> <p className={`${syne.className} text-2xl`}>
<a Nginx + SSL Client Certificate Verification: Manage Access to a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44" site
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" </p>
target="_blank" <p className={`${questrial.className} line-clamp-3 mt-4`}>
rel="noopener noreferrer" Access control is a fundamental part of security. Most entities
> rely on the combination of username and password, sometimes with
Read our docs additional multi-factor authentication to improve security. Some
</a> entities also use the SSL client certificate verification to
manage access to specific resources. One of the use cases where
SSL client certificate verification fits perfectly is managing
access to internet-facing development or staging servers. In
this post, I&apos;ll share how to set up the certificates and
configure nginx to verify users based on their certificates.
</p>
</div>
</Link>
</div> </div>
</main> </div>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center"> <div className={`flex flex-row bg-teal-50 justify-center`}>
<a <div className={`max-w-4xl py-28 px-10`}>
className="flex items-center gap-2 hover:underline hover:underline-offset-4" <p className={`text-3xl ${raleway.className}`}>Hi There</p>
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app" <p className={`text-base ${raleway.className} my-4`}>
target="_blank" a new take on experience is the best teacher
rel="noopener noreferrer" </p>
> <p className={`${raleway.className} text-sm`}>
<Image I started this blog as an archive of my experiences and knowledge.
aria-hidden By writing them out, I hope it will help me unlearn and relearn the
src="/file.svg" various knowledge and skills I&apos;ve accumulated. I hope the
alt="File icon" articles, source code examples, and server config examples I wrote
width={16} will help you somehow. Read on and enjoy!
height={16} </p>
/> </div>
Learn </div>
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div> </div>
); );
} }

82
app/post/[slug]/page.tsx Normal file
View File

@@ -0,0 +1,82 @@
import { getPost } from "@/backend/post";
import DOMPurify from "dompurify";
import { JSDOM } from "jsdom";
import { nunito_sans, raleway } from "@/app/fonts";
import parse, {
domToReact,
Element,
HTMLReactParserOptions,
} from "html-react-parser";
import { DummyPostSlug, DummyPostString } from "@/components/dummyPost";
const options: HTMLReactParserOptions = {
replace: (domNode) => {
if (domNode instanceof Element && domNode.attribs) {
// console.log(domNode.attribs)
if (domNode.name === "h1") {
if (domNode.attribs.class === "title") {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-4xl`}>
{domToReact(domNode.children)}
</h1>
);
} else {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-3xl`}>
{domToReact(domNode.children)}
</h1>
);
}
} else if (domNode.name === "h2") {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-2xl`}>
{domToReact(domNode.children)}
</h1>
);
} else if (domNode.name === "h3") {
return (
<h1 className={`${raleway.className} mx-auto w-224 text-xl`}>
{domToReact(domNode.children)}
</h1>
);
} else if (domNode.name === "p") {
if (domNode.attribs.class === "paragraph") {
return (
<h1 className={`${nunito_sans.className} mx-auto w-224`}>
{domToReact(domNode.children)}
</h1>
);
} else {
const classes = domNode.attribs.class.split(" ");
if (classes.includes("code")) {
if (classes.includes("shell")) {
} else if (classes.includes("go") || classes.includes("golang")) {
} else {
}
}
}
}
}
},
};
export default async function Post({ params }: { params: { slug: string } }) {
let content = "";
const dummySlug = await DummyPostSlug();
if (dummySlug === params.slug) {
content = await DummyPostString();
console.log(content);
} else {
content = await getPost(params.slug);
}
content = DOMPurify(new JSDOM("<!DOCTYPE html>").window).sanitize(content);
// console.log(content)
const elem = parse(content, options);
return <div className={`flex flex-col`}>{elem}</div>;
}

20
backend/db.ts Normal file
View File

@@ -0,0 +1,20 @@
import mysql, { PoolOptions } from "mysql2";
const access: PoolOptions = {
host: process.env.MYSQL_HOST,
port: 'MYSQL_PORT' in process.env && typeof process.env.MYSQL_PORT === 'string' ? parseInt(process.env.MYSQL_PORT) : 3306,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: 'MYSQL_DATABASE' in process.env ? process.env.MYSQL_DATABASE : 'blog',
waitForConnections: true,
connectionLimit: 10,
maxIdle: 10,
idleTimeout: 60000,
queueLimit: 0,
enableKeepAlive: true,
keepAliveInitialDelay: 0,
}
export const pool = mysql.createPool(access)
export const promisePool = pool.promise()

13
backend/post.ts Normal file
View File

@@ -0,0 +1,13 @@
import { RowDataPacket } from "mysql2";
import { promisePool } from "@/backend/db";
export async function getPost(slug: string): Promise<string> {
try {
const [rows, fields] = await promisePool.query<RowDataPacket[]>(
'select content from post where slug = ?', [slug])
return rows[0]['content']
} catch (e) {
console.log(e)
throw e
}
}

13
components/blogFooter.tsx Normal file
View File

@@ -0,0 +1,13 @@
import {raleway} from "@/app/fonts";
export default function BlogFooter() {
return (
<div>
<p className={`${raleway.className} text-center text-xl my-10`}>Suyono</p>
<p className={`${raleway.className} text-center`}>suyono3484@gmail.com</p>
<p className={`${raleway.className} text-center mt-20 mb-10`}>
&copy;2023 by Suyono. Built using Next.js
</p>
</div>
);
}

25
components/blogHeader.tsx Normal file
View File

@@ -0,0 +1,25 @@
import Link from "next/link";
import { raleway }from "@/app/fonts";
export default function BlogHeader() {
return(
<div>
<div className="ml-20 py-8">
<p className={`${raleway.className} text-2xl font-thin`}>SUYONO</p>
</div>
<div className="bg-gray-100">
<div className="flex flex-row ml-20">
<Link href="/" className={`${raleway.className} m-2 font-thin text-sm`}>
Home
</Link>
<Link href="/about" className={`${raleway.className} m-2 font-thin text-sm`}>
About
</Link>
<Link href="/blog" className={`${raleway.className} m-2 font-thin text-sm`}>
Blog
</Link>
</div>
</div>
</div>
)
}

78
components/dummyPost.tsx Normal file
View File

@@ -0,0 +1,78 @@
export async function DummyPostString() {
const ReactDOMServer = (await import('react-dom/server')).default
const component = await DummyPost()
return ReactDOMServer.renderToStaticMarkup(component)
}
export async function DummyPostSlug() {
return "dummy-post"
}
export async function DummyPost() {
return(
<div>
<h1 className="title">Nginx + SSL Client Certificate Verification: Manage access to a site</h1>
<p className="paragraph">Access control is a fundamental part of security. Most entities rely on
the combination of username and password, sometimes with additional multi-factor authentication
to improve security. Some entities also use the SSL client certificate verification to manage access
to specific resources. One of the use cases where SSL client certificate verification fits perfectly is
managing access to internet-facing development or staging servers. In this post, I&apos;ll share how
to set up the certificates and configure nginx to verify users based on their certificates.</p>
<h1>Preparing the certificates</h1>
<p className="paragraph">There are two certificates we are going to create. The first one is the root
certificate. It will be placed in the Nginx server. The second one is the client certificate. It will
be installed in the client machine/browsers.</p>
<h2>Root CA</h2>
<p className="paragraph">For generating a root CA, execute these two steps:</p>
<h3>Generate RSA Key</h3>
<p className="code">openssl genrsa -aes256 -out ca.key 4096</p>
<h3>Create Root CA crt file.</h3>
<p className="code">openssl req -new -x509 -days 3650 -key ca.key -out ca.crt</p>
<h2>Setup CA configuration</h2>
<p className="paragraph">This is an optional step, but if you want to be able to revoke access you
previously granted, you need to do this step.</p>
<p className="paragraph">Create a file named ca.cnf in the same directory as the ca.key and ca.crt.</p>
<p className="code">[ ca ]
default_ca = gca
[ crl_ext ]
authorityKeyIdentifier=keyid:always
[ gca ]
dir = ./
new_certs_dir = $dir
unique_subject = no
certificate = $dir/ca.crt
database = $dir/certindex
private_key = $dir/ca.key
serial = $dir/certserial
default_days = 365
default_md = sha256
policy = gca_policy
x509_extensions = gca_extensions
crlnumber = $dir/crlnumber
default_crl_days = 365
[ gca_policy ]
commonName = supplied
stateOrProvinceName = supplied
countryName = optional
emailAddress = optional
organizationName = supplied
organizationUnitName = optional
[ gca_extensions ]
basicConstraints = CA:false
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
keyUsage = digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
crlDistributionPoints = URI:http://example.com/root.crl
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = example.com
DNS.2 = *.example.com</p>
</div>
)
}

9
next.config.js Normal file
View File

@@ -0,0 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
config.externals = [...config.externals, "jsdom"];
return config;
}
}
module.exports = nextConfig

View File

@@ -1,7 +0,0 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

View File

@@ -1,5 +1,5 @@
{ {
"name": "blogts2", "name": "nextts",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
@@ -9,19 +9,27 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"react": "19.0.0-rc-66855b96-20241106", "@types/dompurify": "^3.0.3",
"react-dom": "19.0.0-rc-66855b96-20241106", "@types/jsdom": "^21.1.3",
"next": "15.0.3" "@types/node": "20.6.5",
"@types/react": "18.2.22",
"@types/react-dom": "18.2.7",
"autoprefixer": "10.4.16",
"dompurify": "^3.0.6",
"eslint": "8.50.0",
"eslint-config-next": "13.5.2",
"html-react-parser": "^4.2.2",
"jsdom": "^22.1.0",
"mysql2": "^3.6.1",
"next": "13.5.2",
"postcss": "8.4.30",
"react": "18.2.0",
"react-dom": "18.2.0",
"redis": "^4.6.10",
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5", "prettier": "3.0.3"
"@types/node": "^20", }
"@types/react": "^18",
"@types/react-dom": "^18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "15.0.3"
},
"packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab"
} }

5104
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,8 +0,0 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
public/assets/pthumb.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

View File

@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 629 B

View File

@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

View File

@@ -1,18 +1,24 @@
import type { Config } from "tailwindcss"; import type { Config } from 'tailwindcss'
export default { const config: Config = {
content: [ content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}", './pages/**/*.{js,ts,jsx,tsx,mdx}',
"./components/**/*.{js,ts,jsx,tsx,mdx}", './components/**/*.{js,ts,jsx,tsx,mdx}',
"./app/**/*.{js,ts,jsx,tsx,mdx}", './app/**/*.{js,ts,jsx,tsx,mdx}',
], ],
theme: { theme: {
extend: { extend: {
colors: { backgroundImage: {
background: "var(--background)", 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
foreground: "var(--foreground)", 'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
'blog-cover': "url('/assets/placeholder.webp')"
}, },
width: {
'224': '56rem',
}
}, },
}, },
plugins: [], plugins: [],
} satisfies Config; }
export default config

View File

@@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "es5",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,