diff --git a/.env.example b/.env.example
index 4104fdd..eb00a90 100644
--- a/.env.example
+++ b/.env.example
@@ -3,5 +3,10 @@ DATABASE_URL=
# github
GITHUB_AUTH_TOKEN=
+# https://github.com/settings/developers
+# To have an Oauth configuration for dev and prod environments, you'll need to register a second Oauth app
OAUTH_CLIENT_ID=
OAUTH_CLIENT_SECRET=
+
+# A random value that encrypts tokens and email verification hashes. https://authjs.dev/getting-started/installation?framework=next.js#setup-environment
+AUTH_SECRET=
diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml
index cbe48f4..4f98a08 100644
--- a/.github/workflows/lighthouse.yml
+++ b/.github/workflows/lighthouse.yml
@@ -56,6 +56,7 @@ jobs:
${{ steps.vercel_preview_url.outputs.url }}
${{ steps.vercel_preview_url.outputs.url }}/blog
${{ steps.vercel_preview_url.outputs.url }}/projects
+ ${{ steps.vercel_preview_url.outputs.url }}/guestbook
${{ steps.vercel_preview_url.outputs.url }}/about
uploadArtifacts: true # save results as a action artifacts
temporaryPublicStorage: true # upload lighthouse report to the temporary storage
diff --git a/messages/en.json b/messages/en.json
index 418e386..63180a1 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -14,7 +14,8 @@
"backToHome": "Back to Home",
"postViews": " views",
"notFound": "Sorry, the page you are looking for does not exist.",
- "chooseLanguage": "chooseLanguage"
+ "chooseLanguage": "chooseLanguage",
+ "guestbook": "Guestbook"
},
"homePage": {
"description": "👋 Hi, I'm Tom Jin, a frontend developer based in Taiwan who specializes in React and Next.js. I'm here to share learning and projects. Please feel free to read and discover.",
diff --git a/messages/zh-TW.json b/messages/zh-TW.json
index 2b7cfdc..830f853 100644
--- a/messages/zh-TW.json
+++ b/messages/zh-TW.json
@@ -14,7 +14,8 @@
"backToHome": "返回首頁",
"postViews": "瀏覽次數: 次",
"notFound": "對不起,您正在尋找的頁面不存在。",
- "chooseLanguage": "選擇語言"
+ "chooseLanguage": "選擇語言",
+ "guestbook": "留言板"
},
"homePage": {
"description": "👋 嗨,我是Tom Jin,我是一位前端工程師。專注於使用 React 和 Next.js,我在這裡分享我的學習筆記和專案實作,歡迎閱讀與探索。",
diff --git a/next.config.mjs b/next.config.mjs
index 229820c..7a6eac6 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -4,6 +4,13 @@ import withNextIntl from 'next-intl/plugin';
const nextConfig = {
swcMinify: true,
i18n: null,
+ images: {
+ remotePatterns: [
+ {
+ hostname: 'avatars.githubusercontent.com',
+ },
+ ],
+ },
};
export default withNextIntl('./src/i18n.ts')(nextConfig);
diff --git a/package.json b/package.json
index 216f099..853144e 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"clsx": "^1.2.1",
"framer-motion": "^10.12.12",
"next": "^14.0.1",
+ "next-auth": "5.0.0-beta.19",
"next-intl": "^3.2.4",
"octokit": "^3.1.2",
"react": "18.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b1e0d8..e609e68 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -35,6 +35,9 @@ importers:
next:
specifier: ^14.0.1
version: 14.0.1(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ next-auth:
+ specifier: 5.0.0-beta.19
+ version: 5.0.0-beta.19(next@14.0.1(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
next-intl:
specifier: ^3.2.4
version: 3.9.5(next@14.0.1(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
@@ -162,6 +165,20 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
+ '@auth/core@0.32.0':
+ resolution: {integrity: sha512-3+ssTScBd+1fd0/fscAyQN1tSygXzuhysuVVzB942ggU4mdfiTbv36P0ccVnExKWYJKvu3E2r3/zxXCCAmTOrg==}
+ peerDependencies:
+ '@simplewebauthn/browser': ^9.0.1
+ '@simplewebauthn/server': ^9.0.2
+ nodemailer: ^6.8.0
+ peerDependenciesMeta:
+ '@simplewebauthn/browser':
+ optional: true
+ '@simplewebauthn/server':
+ optional: true
+ nodemailer:
+ optional: true
+
'@babel/code-frame@7.24.2':
resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
engines: {node: '>=6.9.0'}
@@ -993,6 +1010,9 @@ packages:
resolution: {integrity: sha512-+nGS3ReCByF6m+nbNB59x7Aa3CNjCCGuBLFzfkiJP1O3uVKKuJbkP4uO4t46YqH26nlugmOhqjT7nx5D0VPtdA==}
engines: {node: '>= 18'}
+ '@panva/hkdf@1.2.1':
+ resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
+
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -1378,6 +1398,9 @@ packages:
'@types/btoa-lite@1.0.2':
resolution: {integrity: sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==}
+ '@types/cookie@0.6.0':
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -1888,6 +1911,10 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie@0.6.0:
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+ engines: {node: '>= 0.6'}
+
create-jest@29.7.0:
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -2934,6 +2961,9 @@ packages:
resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
hasBin: true
+ jose@5.6.3:
+ resolution: {integrity: sha512-1Jh//hEEwMhNYPDDLwXHa2ePWgWiFNNUadVmguAAw2IJ6sj9mNxV5tGXJNqlMkJAybF6Lgw1mISDxTePP/187g==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -3418,6 +3448,22 @@ packages:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
+ next-auth@5.0.0-beta.19:
+ resolution: {integrity: sha512-YHu1igcAxZPh8ZB7GIM93dqgY6gcAzq66FOhQFheAdOx1raxNcApt05nNyNCSB6NegSiyJ4XOPsaNow4pfDmsg==}
+ peerDependencies:
+ '@simplewebauthn/browser': ^9.0.1
+ '@simplewebauthn/server': ^9.0.2
+ next: ^14 || ^15.0.0-0
+ nodemailer: ^6.6.5
+ react: ^18.2.0 || ^19.0.0-0
+ peerDependenciesMeta:
+ '@simplewebauthn/browser':
+ optional: true
+ '@simplewebauthn/server':
+ optional: true
+ nodemailer:
+ optional: true
+
next-intl@3.9.5:
resolution: {integrity: sha512-tsp4N433WgTAbbyZdMlcsLGHFM88wv2a7ZpF/od8X9+qAlO1TrajZrNrGBpIg6nA9EGZyMbQPzZD7XZrqYIv7g==}
peerDependencies:
@@ -3467,6 +3513,9 @@ packages:
nwsapi@2.2.10:
resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
+ oauth4webapi@2.11.1:
+ resolution: {integrity: sha512-aNzOnL98bL6izG97zgnZs1PFEyO4WDVRhz2Pd066NPak44w5ESLRCYmJIyey8avSBPOMtBjhF3ZDDm7bIb7UOg==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -3654,6 +3703,14 @@ packages:
resolution: {integrity: sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==}
engines: {node: ^10 || ^12 || >=14}
+ preact-render-to-string@5.2.3:
+ resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
+ peerDependencies:
+ preact: '>=10'
+
+ preact@10.11.3:
+ resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
+
prebuild-install@7.1.2:
resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==}
engines: {node: '>=10'}
@@ -3676,6 +3733,9 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ pretty-format@3.8.0:
+ resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
+
prisma@4.16.2:
resolution: {integrity: sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==}
engines: {node: '>=14.17'}
@@ -4491,6 +4551,16 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
+ '@auth/core@0.32.0':
+ dependencies:
+ '@panva/hkdf': 1.2.1
+ '@types/cookie': 0.6.0
+ cookie: 0.6.0
+ jose: 5.6.3
+ oauth4webapi: 2.11.1
+ preact: 10.11.3
+ preact-render-to-string: 5.2.3(preact@10.11.3)
+
'@babel/code-frame@7.24.2':
dependencies:
'@babel/highlight': 7.24.2
@@ -5438,6 +5508,8 @@ snapshots:
'@octokit/webhooks-types': 7.3.2
aggregate-error: 3.1.0
+ '@panva/hkdf@1.2.1': {}
+
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -5826,6 +5898,8 @@ snapshots:
'@types/btoa-lite@1.0.2': {}
+ '@types/cookie@0.6.0': {}
+
'@types/debug@4.1.12':
dependencies:
'@types/ms': 0.7.34
@@ -6392,6 +6466,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie@0.6.0: {}
+
create-jest@29.7.0(@types/node@20.2.1):
dependencies:
'@jest/types': 29.6.3
@@ -6753,8 +6829,8 @@ snapshots:
'@typescript-eslint/parser': 5.62.0(eslint@8.41.0)(typescript@5.0.4)
eslint: 8.41.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.41.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.41.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0))(eslint@8.41.0)
eslint-plugin-jsx-a11y: 6.8.0(eslint@8.41.0)
eslint-plugin-react: 7.34.1(eslint@8.41.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.41.0)
@@ -6772,13 +6848,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.41.0):
+ eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0):
dependencies:
debug: 4.3.4
enhanced-resolve: 5.16.0
eslint: 8.41.0
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.41.0))(eslint@8.41.0)
- eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.41.0)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0))(eslint@8.41.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0))(eslint@8.41.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.3
is-core-module: 2.13.1
@@ -6789,18 +6865,18 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.41.0))(eslint@8.41.0):
+ eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0))(eslint@8.41.0):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 5.62.0(eslint@8.41.0)(typescript@5.0.4)
eslint: 8.41.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.41.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0)
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.41.0):
+ eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0))(eslint@8.41.0):
dependencies:
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.4
@@ -6810,7 +6886,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.41.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.41.0))(eslint@8.41.0)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.41.0)(typescript@5.0.4))(eslint@8.41.0))(eslint@8.41.0))(eslint@8.41.0)
hasown: 2.0.2
is-core-module: 2.13.1
is-glob: 4.0.3
@@ -7885,6 +7961,8 @@ snapshots:
jiti@1.21.0: {}
+ jose@5.6.3: {}
+
js-tokens@4.0.0: {}
js-yaml@3.14.1:
@@ -8716,6 +8794,12 @@ snapshots:
negotiator@0.6.3: {}
+ next-auth@5.0.0-beta.19(next@14.0.1(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
+ dependencies:
+ '@auth/core': 0.32.0
+ next: 14.0.1(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ react: 18.2.0
+
next-intl@3.9.5(next@14.0.1(@babel/core@7.24.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
dependencies:
'@formatjs/intl-localematcher': 0.2.32
@@ -8769,6 +8853,8 @@ snapshots:
nwsapi@2.2.10: {}
+ oauth4webapi@2.11.1: {}
+
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@@ -8974,6 +9060,13 @@ snapshots:
picocolors: 1.0.0
source-map-js: 1.0.2
+ preact-render-to-string@5.2.3(preact@10.11.3):
+ dependencies:
+ preact: 10.11.3
+ pretty-format: 3.8.0
+
+ preact@10.11.3: {}
+
prebuild-install@7.1.2:
dependencies:
detect-libc: 2.0.2
@@ -9005,6 +9098,8 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
+ pretty-format@3.8.0: {}
+
prisma@4.16.2:
dependencies:
'@prisma/engines': 4.16.2
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 2913aa4..180b4f1 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -11,3 +11,10 @@ model Post {
slug String @unique
views Int @default(0)
}
+
+model Guestbook {
+ id Int @id @default(autoincrement())
+ email String @db.VarChar(255)
+ body String
+ createdAt DateTime @db.Timestamp(0)
+}
diff --git a/src/app/[locale]/guestbook/page.tsx b/src/app/[locale]/guestbook/page.tsx
new file mode 100644
index 0000000..72a3ff6
--- /dev/null
+++ b/src/app/[locale]/guestbook/page.tsx
@@ -0,0 +1,39 @@
+import { signIn, signOut , auth } from '@/lib/auth';
+import Image from 'next/image';
+
+const GuestbookPage = async () => {
+ const session = await auth();
+
+ if (!session?.user) {
+ return (
+ <>
+
+ >
+ );
+ }
+
+ return (
+ <>
+ Welcome {session.user.name}
+
+
+
+ >
+ );
+};
+
+export default GuestbookPage;
diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000..5ef28c1
--- /dev/null
+++ b/src/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,3 @@
+import { handlers } from '@/lib/auth';
+
+export const { GET, POST } = handlers;
diff --git a/src/app/api/guestbook/route.ts b/src/app/api/guestbook/route.ts
new file mode 100644
index 0000000..b0d598c
--- /dev/null
+++ b/src/app/api/guestbook/route.ts
@@ -0,0 +1,5 @@
+import { NextResponse } from 'next/server';
+
+export const GET = async () => {
+ return NextResponse.json({});
+};
diff --git a/src/components/header.tsx b/src/components/header.tsx
index f1c8f21..584d404 100644
--- a/src/components/header.tsx
+++ b/src/components/header.tsx
@@ -47,11 +47,11 @@ const Header = () => {
return (
-
+
{
{navLinks.map((link) => (
diff --git a/src/config/nav-links.tsx b/src/config/nav-links.tsx
index 5dcaf4e..3b097aa 100644
--- a/src/config/nav-links.tsx
+++ b/src/config/nav-links.tsx
@@ -1,4 +1,4 @@
-import { RiGitRepositoryFill, RiQuillPenFill, RiUser3Fill } from 'react-icons/ri';
+import { RiGitRepositoryFill, RiQuillPenFill, RiUser3Fill, RiUser6Line } from 'react-icons/ri';
const navLinks = [
{
@@ -16,6 +16,11 @@ const navLinks = [
href: '/about',
icon: ,
},
+ {
+ title: 'guestbook',
+ href: '/guestbook',
+ icon: ,
+ },
];
export default navLinks;
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
new file mode 100644
index 0000000..1b521a1
--- /dev/null
+++ b/src/lib/auth.ts
@@ -0,0 +1,11 @@
+import NextAuth from 'next-auth';
+import Github from 'next-auth/providers/github';
+
+export const { handlers, signIn, signOut, auth } = NextAuth({
+ providers: [
+ Github({
+ clientId: process.env.OAUTH_CLIENT_ID,
+ clientSecret: process.env.OAUTH_CLIENT_SECRET,
+ }),
+ ],
+});