2 Commits
main ... next15

Author SHA1 Message Date
6716ac6907 Initial commit from Create Next App 2024-12-03 11:30:25 +11:00
b0ca2bbeb3 clean slate 2024-12-03 11:30:06 +11:00
40 changed files with 2878 additions and 2878 deletions

View File

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

9
.gitignore vendored
View File

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

5
.idea/.gitignore generated vendored
View File

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

View File

@@ -1,6 +0,0 @@
<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>

View File

@@ -1,6 +0,0 @@
<?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
View File

@@ -1,8 +0,0 @@
<?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
View File

@@ -1,12 +0,0 @@
<?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
View File

@@ -1,7 +0,0 @@
<?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
View File

@@ -1,6 +0,0 @@
<?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://github.com/vercel/next.js/tree/canary/packages/create-next-app).
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).
## 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.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
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.
## 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.
- [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
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/deployment) for more details.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

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

View File

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

View File

@@ -1,21 +0,0 @@
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>
)
}

View File

@@ -1,23 +0,0 @@
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",
}
)

BIN
app/fonts/GeistMonoVF.woff Normal file

Binary file not shown.

BIN
app/fonts/GeistVF.woff Normal file

Binary file not shown.

View File

@@ -1,3 +1,21 @@
@tailwind base;
@tailwind components;
@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;
}

View File

@@ -1,27 +0,0 @@
@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,11 +1,17 @@
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";
import localFont from "next/font/local";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
const geistSans = localFont({
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 = {
title: "Create Next App",
@@ -14,17 +20,15 @@ export const metadata: Metadata = {
export default function RootLayout({
children,
}: {
}: Readonly<{
children: React.ReactNode;
}) {
}>) {
return (
<html lang="en">
<body className={inter.className}>
<div className={`flex flex-col bg-white`}>
<BlogHeader />
{children}
<BlogFooter />
</div>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);

View File

@@ -1,69 +1,101 @@
import Image from "next/image";
import Link from "next/link";
import { raleway, syne, questrial } from "@/app/fonts";
export default function Home() {
return (
<div className={`flex flex-col`}>
<div className={`bg-cover bg-center flex flex-col-reverse bg-blog-cover`}>
<div className={`bg-neutral-100 bg-opacity-30 flex flex-col py-10`}>
<p className={`${raleway.className} text-white text-center text-7xl font-thin mb-6`}>
SUYONO
</p>
<p className={`${raleway.className} text-white text-center font-thin text-xl mb-10`}>
A Tech Archive
</p>
</div>
<div className={`h-64`}></div> {/* spacer */}
<div className={`h-64`}></div> {/* spacer */}
</div>
<div className={`flex flex-row justify-center my-8`}>
<div className={`border border-slate-100 flex flex-col`}>
<Link
href="/post/nginx-ssl-client-certificate-verification-manage-access-to-a-site"
className={`flex flex-row max-w-4xl items-center`}
<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)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
app/page.tsx
</code>
.
</li>
<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
src="/assets/pthumb.webp"
alt="post thumbnail"
width={454}
height={341}
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
<div className={`flex flex-col mx-10`}>
<p className={`${syne.className} text-2xl`}>
Nginx + SSL Client Certificate Verification: Manage Access to a
site
</p>
<p className={`${questrial.className} line-clamp-3 mt-4`}>
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>
</div>
</Link>
Deploy now
</a>
<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"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</div>
<div className={`flex flex-row bg-teal-50 justify-center`}>
<div className={`max-w-4xl py-28 px-10`}>
<p className={`text-3xl ${raleway.className}`}>Hi There</p>
<p className={`text-base ${raleway.className} my-4`}>
a new take on experience is the best teacher
</p>
<p className={`${raleway.className} text-sm`}>
I started this blog as an archive of my experiences and knowledge.
By writing them out, I hope it will help me unlearn and relearn the
various knowledge and skills I&apos;ve accumulated. I hope the
articles, source code examples, and server config examples I wrote
will help you somehow. Read on and enjoy!
</p>
</div>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</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>
);
}

View File

@@ -1,82 +0,0 @@
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>;
}

View File

@@ -1,20 +0,0 @@
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()

View File

@@ -1,13 +0,0 @@
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
}
}

View File

@@ -1,13 +0,0 @@
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>
);
}

View File

@@ -1,25 +0,0 @@
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>
)
}

View File

@@ -1,78 +0,0 @@
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>
)
}

View File

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

7
next.config.ts Normal file
View File

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

View File

@@ -1,5 +1,5 @@
{
"name": "nextts",
"name": "blogts2",
"version": "0.1.0",
"private": true,
"scripts": {
@@ -9,27 +9,19 @@
"lint": "next lint"
},
"dependencies": {
"@types/dompurify": "^3.0.3",
"@types/jsdom": "^21.1.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"
"react": "19.0.0-rc-66855b96-20241106",
"react-dom": "19.0.0-rc-66855b96-20241106",
"next": "15.0.3"
},
"devDependencies": {
"prettier": "3.0.3"
}
"typescript": "^5",
"@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"
}

5076
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

8
postcss.config.mjs Normal file
View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

1
public/file.svg Normal file
View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 391 B

1
public/globe.svg Normal file
View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1 +1 @@
<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>
<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>

Before

Width:  |  Height:  |  Size: 629 B

After

Width:  |  Height:  |  Size: 128 B

1
public/window.svg Normal file
View File

@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 385 B

View File

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

View File

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